1 /*************************************************************************
2 * TinyFugue - programmable mud client
3 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2002, 2003, 2004, 2005, 2006-2007 Ken Keys
4 *
5 * TinyFugue (aka "tf") is protected under the terms of the GNU
6 * General Public License. See the file "COPYING" for details.
7 ************************************************************************/
8
9 /*****************************************************************
10 * Fugue output handling
11 *
12 * Written by Ken Keys (Hawkeye).
13 * Handles all screen-related phenomena.
14 *****************************************************************/
15
16 #define TERM_vt100 1
17 #define TERM_vt220 2
18 #define TERM_ansi 3
19
20 #include "tfconfig.h"
21 #include "port.h"
22 #include "tf.h"
23 #include "util.h"
24 #include "pattern.h"
25 #include "search.h"
26 #include "tfio.h"
27 #include "socket.h" /* fgprompt(), fgname() */
28 #include "output.h"
29 #include "attr.h"
30 #include "macro.h" /* add_ibind(), rebind_key_macros() */
31 #include "tty.h" /* init_tty(), get_window_wize() */
32 #include "variable.h"
33 #include "expand.h" /* current_command */
34 #include "parse.h" /* expr_value_safe() */
35 #include "keyboard.h" /* keyboard_pos */
36 #include "cmdlist.h"
37
38 #if WIDECHAR
39 #include <wchar.h>
40 #include <unicode/uchar.h>
41 #include <unicode/utext.h>
42 #include <unicode/ubrk.h>
43 #endif
44
45 extern struct World *world_decl; /* declares struct World */
46
47 #ifdef EMXANSI
48 # define INCL_VIO
49 # include <os2.h>
50 #endif
51
52 #if HAVE_SETLOCALE
53 static char *lang = NULL;
54 #endif
55
56 /* Terminal codes and capabilities.
57 * Visual mode requires at least clear_screen and cursor_address. The other
58 * codes are good to have, but are not strictly necessary.
59 * Scrolling in visual mode requires set_scroll_region (preferred), or
60 * insert_line and delete_line (may appear jumpy), or EMXANSI.
61 * Fast insert in visual mode requires insert_start and insert_end (preferred),
62 * or insert_char.
63 * Fast delete in visual mode requires delete_char.
64 * Attributes require the appropriate attribute code and attr_off; but if
65 * attr_off is not defined, underline and standout (replacement for bold)
66 * can still be used if underline_off and standout_off are defined.
67 */
68
69 #define DEFAULT_LINES 24
70 #define DEFAULT_COLUMNS 80
71
72 #if HARDCODE
73 # define origin 1 /* top left corner is (1,1) */
74 # if HARDCODE == TERM_vt100
75 # define TERMCODE(id, vt100, vt220, ansi) static const char *(id) = (vt100);
76 # elif HARDCODE == TERM_vt220
77 # define TERMCODE(id, vt100, vt220, ansi) static const char *(id) = (vt220);
78 # elif HARDCODE == TERM_ansi
79 # define TERMCODE(id, vt100, vt220, ansi) static const char *(id) = (ansi);
80 # endif
81 #else /* !HARDCODE */
82 # define origin 0 /* top left corner is (0,0) */
83 # define TERMCODE(id, vt100, vt220, ansi) static const char *(id) = NULL;
84 #endif /* HARDCODE */
85
86 /* vt100 vt220 ansi */
87 /* ----- ----- ---- */
88 #ifdef __CYGWIN32__ /* "\033[J" is broken in CYGWIN32 b18. */
89 TERMCODE (clear_screen, NULL, NULL, NULL)
90 TERMCODE (clear_to_eos, NULL, NULL, NULL)
91 #else
92 TERMCODE (clear_screen, "\033[H\033[J", "\033[H\033[J", "\033[H\033[J")
93 TERMCODE (clear_to_eos, "\033[J", "\033[J", "\033[J")
94 #endif
95 TERMCODE (clear_to_eol, "\033[K", "\033[K", "\033[K")
96 TERMCODE (cursor_address, "\033[%d;%dH", "\033[%d;%dH", "\033[%d;%dH")
97 TERMCODE (enter_ca_mode, NULL, NULL, NULL)
98 TERMCODE (exit_ca_mode, NULL, NULL, NULL)
99 TERMCODE (set_scroll_region, "\033[%d;%dr", "\033[%d;%dr", NULL)
100 TERMCODE (insert_line, NULL, "\033[L", "\033[L")
101 TERMCODE (delete_line, NULL, "\033[M", "\033[M")
102 TERMCODE (delete_char, NULL, "\033[P", "\033[P")
103 #ifdef __CYGWIN32__ /* "\033[@" is broken in CYGWIN32 b18. */
104 TERMCODE (insert_char, NULL, NULL, NULL)
105 #else
106 TERMCODE (insert_char, NULL, "\033[@", "\033[@")
107 #endif
108 TERMCODE (insert_start, NULL, NULL, "\033[4h")
109 TERMCODE (insert_end, NULL, NULL, "\033[4l")
110 TERMCODE (keypad_on, "\033[?1h\033=",NULL, NULL)
111 TERMCODE (keypad_off, "\033[?1l\033>",NULL, NULL)
112 TERMCODE (bell, "\007", "\007", "\007")
113 TERMCODE (underline, "\033[4m", "\033[4m", "\033[4m")
114 TERMCODE (reverse, "\033[7m", "\033[7m", "\033[7m")
115 TERMCODE (flash, "\033[5m", "\033[5m", "\033[5m")
116 TERMCODE (dim, NULL, NULL, NULL)
117 TERMCODE (bold, "\033[1m", "\033[1m", "\033[1m")
118 TERMCODE (italic, "\033[3m", "\033[3m", "\033[3m")
119 TERMCODE (attr_off, "\033[m", "\033[m", "\033[m")
120 TERMCODE (attr_on, NULL, NULL, NULL)
121 /* these are only used if others are missing */
122 TERMCODE (underline_off, NULL, NULL, NULL)
123 TERMCODE (standout, NULL, NULL, NULL)
124 TERMCODE (standout_off, NULL, NULL, NULL)
125
126 /* end HARDCODE section */
127
128 /* If var==NULL and internal<0, the status is a constant string */
129 typedef struct statusfield {
130 char *name;
131 stat_id_t internal; /* index of internal status being watched */
132 Var *var; /* variable being watched */
133 Var *fmtvar; /* variable containing format expression */
134 Var *attrvar; /* variable containing attribute string */
135 int width;
136 int rightjust;
137 int column;
138 int row;
139 attr_t attrs; /* attibutes from status_fields */
140 attr_t vattrs; /* attibutes from status_attr_{int,var}_<name> */
141 } StatusField;
142
143 static Var bogusvar; /* placeholder for StatusField->var */
144
145 #define true_status_pad (status_pad && *status_pad ? *status_pad : ' ')
146 #define sidescroll \
147 (intvar(VAR_sidescroll) <= Wrap/2 ? intvar(VAR_sidescroll) : Wrap/2)
148
149 static void init_term(void);
150 static int fbufputc(int c);
151 static void bufflush(void);
152 static void tbufputs(const char *str);
153 static void tdirectputs(const char *str);
154 static void xy(int x, int y);
155 static void clr(void);
156 static void clear_line(void);
157 static void clear_input_line(void);
158 static void clear_lines(int start, int end);
159 static void clear_input_window(void);
160 static void setscroll(int top, int bottom);
161 static void scroll_input(int n);
162 static void ictrl_put(const char *s, int n);
163 static int ioutputs(const char *str, int len);
164 static void ioutall(int kpos);
165 static void attributes_off(attr_t attrs);
166 static void attributes_on(attr_t attrs);
167 static void color_on(const char *prefix, long color);
168 static void hwrite(conString *line, int start, int len, int indent);
169 static int check_more(Screen *screen);
170 static int next_physline(Screen *screen);
171 static void output_novisual(PhysLine *pl);
172 #ifdef SCREEN
173 static void output_noscroll(PhysLine *pl);
174 static void output_scroll(PhysLine *pl);
175 #endif
176
177 static void (*tp)(const char *str);
178
179 #if TERMCAP
180 #define tpgoto(seq,x,y) tp(tgoto(seq, (x)-1+origin, (y)-1+origin))
181 #else
182 #define tpgoto(seq,x,y) Sappendf(outbuf,seq,(y)-1+origin,(x)-1+origin)
183 #endif
184
185 #define ipos() xy(ix, iy)
186
187 /* Buffered output */
188
189 #define bufputStr(Str) SStringcat(outbuf, Str)
190 #define bufputs(s) Stringcat(outbuf, s)
191 #define bufputns(s, n) Stringncat(outbuf, s, n)
192 #define bufputc(c) Stringadd(outbuf, c)
193 #define bufputnc(c, n) Stringnadd(outbuf, c, n)
194
195 #ifdef EMXANSI /* OS2 */
196 static void crnl(int n);
197 #else
198 # if USE_SGTTY /* CRMOD is off (tty.c) */
199 # define crnl(count) do { bufputc('\r'); bufputnc('\n', count); } while (0)
200 # else /* ONLCR is on (tty.c) */
201 # define crnl(count) bufputnc('\n', count)
202 # endif
203 #endif
204
205
206 /* Others */
207
208 #define Wrap (wrapsize ? wrapsize : columns)
209 #define moremin 1
210 #define morewait 50
211
212 String status_line[][1] = { /* formatted status lines, without alert */
213 STATIC_BUFFER_INIT, STATIC_BUFFER_INIT, STATIC_BUFFER_INIT,
214 STATIC_BUFFER_INIT, STATIC_BUFFER_INIT, STATIC_BUFFER_INIT };
215 #define max_status_height (sizeof(status_line)/sizeof(String))
216 static StatusField *variable_width_field[max_status_height];
217
218 STATIC_BUFFER(outbuf); /* output buffer */
219 static int top_margin = -1, bottom_margin = -1; /* scroll region */
220 static int cx = -1, cy = -1; /* Real cursor ((-1,-1)==unknown) */
221 static int ox = 1, oy = 1; /* Output cursor */
222 static int ix, iy; /* Input cursor */
223 static int old_ix = -1; /* original ix before output clobbered it */
224 static int in_top, in_bot; /* top & bottom line # of input window */
225 #define out_top 1 /* top line # of output window */
226 static int out_bot; /* bottom line # of output window */
227 static int stat_top, stat_bot; /* top & bottom line # of status area */
228 static int istarty, iendy, iendx; /* start/end of current input line */
229 static conString *prompt; /* current prompt */
230 static attr_t have_attr = 0; /* available attributes */
231 static int screen_mode = -1; /* -1=unset, 0=nonvisual, 1=visual */
232 static int output_disabled = 1; /* is it safe to oflush()? */
233 static int can_have_visual = FALSE;
234 static int can_have_expnonvis = FALSE;
235 static List statusfield_list[max_status_height][1];
236 static int status_left[max_status_height]; /* size of status line left part */
237 static int status_right[max_status_height]; /* size of status line right part */
238 static int alert_row = 0; /* row of status area where alert appears */
239 static int alert_pos = 0; /* column where alert appears */
240 static int alert_len = 0; /* length of current alert message */
241
242 STATIC_STRING(moreprompt, "--More--", F_BOLD | F_REVERSE); /* pager prompt */
243
244 #ifndef EMXANSI
245 # define has_scroll_region (set_scroll_region != NULL)
246 #else
247 # define has_scroll_region (1)
248 #endif
249
250
251 #if HARDCODE
252 # if HARDCODE == TERM_vt100
253 # define KEYCODE(vt100, vt220, ansi) (vt100)
254 # else
255 # if HARDCODE == TERM_vt220
256 # define KEYCODE(vt100, vt220, ansi) (vt220)
257 # else
258 # if HARDCODE == TERM_ansi
259 # define KEYCODE(vt100, vt220, ansi) (ansi)
260 # endif
261 # endif
262 # endif
263 #else
264 # define KEYCODE(vt100, vt220, ansi) NULL
265 #endif
266
267 typedef struct Keycode {
268 const char *name, *capname, *code;
269 } Keycode;
270
271 static Keycode keycodes[] = { /* this list is sorted by tolower(name)! */
272 /* vt100 vt220 ansi */
273 /* ----- ----- ---- */
274 /* { "Back Tab", "kB", KEYCODE( NULL, NULL, NULL ) }, */
275 { "Backspace", "kb", KEYCODE( "\010", "\010", "\010" ) },
276 /* { "Clear All Tabs", "ka", KEYCODE( NULL, NULL, NULL ) }, */
277 { "Clear EOL", "kE", KEYCODE( NULL, NULL, NULL ) },
278 { "Clear EOS", "kS", KEYCODE( NULL, NULL, NULL ) },
279 { "Clear Screen", "kC", KEYCODE( NULL, NULL, NULL ) },
280 /* { "Clear Tab", "kt", KEYCODE( NULL, NULL, NULL ) }, */
281 { "Delete", "kD", KEYCODE( NULL, "\033[3~", NULL ) },
282 { "Delete Line", "kL", KEYCODE( NULL, NULL, NULL ) },
283 { "Down", "kd", KEYCODE( "\033OB", "\033[B", "\033[B" ) },
284 /* { "End Insert", "kM", KEYCODE( NULL, NULL, NULL ) }, */
285 { "F0", "k0", KEYCODE( "\033Oy", NULL, NULL ) },
286 { "F1", "k1", KEYCODE( "\033OP", "\033OP", "\033[M" ) },
287 { "F10", "k;", KEYCODE( NULL, NULL, NULL ) },
288 { "F11", "F1", KEYCODE( NULL, NULL, NULL ) },
289 { "F12", "F2", KEYCODE( NULL, NULL, NULL ) },
290 { "F13", "F3", KEYCODE( NULL, NULL, NULL ) },
291 { "F14", "F4", KEYCODE( NULL, NULL, NULL ) },
292 { "F15", "F5", KEYCODE( NULL, NULL, NULL ) },
293 { "F16", "F6", KEYCODE( NULL, NULL, NULL ) },
294 { "F17", "F7", KEYCODE( NULL, NULL, NULL ) },
295 { "F18", "F8", KEYCODE( NULL, NULL, NULL ) },
296 { "F19", "F9", KEYCODE( NULL, NULL, NULL ) },
297 { "F2", "k2", KEYCODE( "\033OQ", "\033OQ", "\033[N" ) },
298 { "F3", "k3", KEYCODE( "\033OR", "\033OR", "\033[O" ) },
299 { "F4", "k4", KEYCODE( "\033OS", "\033OS", "\033[P" ) },
300 { "F5", "k5", KEYCODE( "\033Ot", "\033[17~", "\033[Q" ) },
301 { "F6", "k6", KEYCODE( "\033Ou", "\033[18~", "\033[R" ) },
302 { "F7", "k7", KEYCODE( "\033Ov", "\033[19~", "\033[S" ) },
303 { "F8", "k8", KEYCODE( "\033Ol", "\033[20~", "\033[T" ) },
304 { "F9", "k9", KEYCODE( "\033Ow", "\033[21~", "\033[U" ) },
305 { "Home", "kh", KEYCODE( NULL, "\033[H", "\033[H" ) },
306 { "Home Down", "kH", KEYCODE( NULL, NULL, NULL ) },
307 { "Insert", "kI", KEYCODE( NULL, "\033[2~", "\033[L" ) },
308 { "Insert Line", "kA", KEYCODE( NULL, NULL, NULL ) },
309 { "KP1", "K1", KEYCODE( "\033Oq", NULL, NULL ) },
310 { "KP2", "K2", KEYCODE( "\033Or", NULL, NULL ) },
311 { "KP3", "K3", KEYCODE( "\033Os", NULL, NULL ) },
312 { "KP4", "K4", KEYCODE( "\033Op", NULL, NULL ) },
313 { "KP5", "K5", KEYCODE( "\033Oq", NULL, NULL ) },
314 { "Left", "kl", KEYCODE( "\033OD", "\033[D", "\033[D" ) },
315 { "PgDn", "kN", KEYCODE( NULL, "\033[6~", NULL ) },
316 { "PgUp", "kP", KEYCODE( NULL, "\033[5~", NULL ) },
317 { "Right", "kr", KEYCODE( "\033OC", "\033[C", "\033[C" ) },
318 { "Scroll Down", "kF", KEYCODE( NULL, NULL, NULL ) },
319 { "Scroll Up", "kR", KEYCODE( NULL, NULL, NULL ) },
320 /* { "Set Tab", "kT", KEYCODE( NULL, NULL, NULL ) }, */
321 { "Up", "ku", KEYCODE( "\033OA", "\033[A", "\033[A" ) }
322 };
323
324 #define N_KEYCODES (sizeof(keycodes)/sizeof(Keycode))
325
326 int lines = DEFAULT_LINES;
327 int columns = DEFAULT_COLUMNS;
328 ref_type_t need_refresh = 0; /* does input need refresh? */
329 int need_more_refresh = 0; /* does visual more prompt need refresh? */
330 struct timeval alert_timeout = { 0, 0 }; /* when to clear alert */
331 unsigned long alert_id = 0;
332 struct timeval clock_update ={0,0}; /* when clock needs to be updated */
333 PhysLine *plpool = NULL; /* freelist of PhysLines */
334
335 #if TERMCAP
336 extern int tgetent(char *buf, const char *name);
337 extern int tgetnum(const char *id);
338 extern char *tgetstr(const char *id, char **area);
339 extern char *tgoto(const char *code, int destcol, int destline);
340 extern char *tparm(const char *code, ...);
341 extern int tputs(const char *cp, int affcnt, int (*outc)(int));
342 static int func_putchar(int c);
343 #endif
344
345 /****************************
346 * BUFFERED OUTPUT ROUTINES *
347 ****************************/
348
bufflush(void)349 static void bufflush(void)
350 {
351 int written = 0, result, n;
352 while (written < outbuf->len) {
353 n = outbuf->len - written;
354 if (n > 2048)
355 n = 2048;
356 result = write(STDOUT_FILENO, outbuf->data + written, n);
357 if (result < 0) break;
358 written += result;
359 }
360 Stringtrunc(outbuf, 0);
361 }
362
fbufputc(int c)363 static int fbufputc(int c)
364 {
365 Stringadd(outbuf, c);
366 return c;
367 }
368
369 #ifdef EMXANSI
crnl(int n)370 void crnl(int n)
371 {
372 int off = (cy + n) - bottom_margin;
373 if (off < 0 || !visual) off = 0;
374 bufputnc('\n', n - off);
375 if (off) {
376 bufflush();
377 VioScrollUp(top_margin-1, 0, bottom_margin-1, columns, off, " \x07", 0);
378 bufputc('\r');
379 }
380 }
381 #endif
382
dobell(int n)383 void dobell(int n)
384 {
385 if (beep) {
386 while (n--) bufputs(bell);
387 bufflush();
388 }
389 }
390
391 /********************/
392
change_term(Var * var)393 int change_term(Var *var)
394 {
395 fix_screen();
396 init_term();
397 redraw();
398 rebind_key_macros();
399 return 1;
400 }
401
init_line_numbers(void)402 static void init_line_numbers(void)
403 {
404 /* out_top = 1; */
405 out_bot = lines - isize - status_height;
406 stat_top = out_bot + 1;
407 stat_bot = out_bot + status_height;
408 in_top = stat_bot + 1;
409 in_bot = stat_bot + isize;
410 }
411
412 /* Initialize output data. */
init_output(void)413 void init_output(void)
414 {
415 const char *str;
416 int i;
417
418 tp = tbufputs;
419 init_tty();
420
421 /* Window size: clear defaults, then try:
422 * environment, ioctl TIOCGWINSZ, termcap, defaults.
423 */
424 lines = ((str = getvar("LINES"))) ? atoi(str) : 0;
425 columns = ((str = getvar("COLUMNS"))) ? atoi(str) : 0;
426 if (lines <= 0 || columns <= 0) get_window_size();
427 init_line_numbers();
428 top_margin = 1;
429 bottom_margin = lines;
430
431 prompt = fgprompt();
432 old_ix = -1;
433
434 init_term();
435 for (i = 0; i < max_status_height; i++) {
436 init_list(statusfield_list[i]);
437 Stringninit(status_line[i], columns);
438 check_charattrs(status_line[i], columns, 0, __FILE__, __LINE__);
439 }
440
441 redraw();
442 output_disabled = 0;
443 }
444
445 /********************
446 * TERMCAP ROUTINES *
447 ********************/
448
init_term(void)449 static void init_term(void)
450 {
451 #if TERMCAP
452 int i;
453 /* Termcap entries are supposed to fit in 1024 bytes. But, if a 'tc'
454 * field is present, some termcap libraries will just append the second
455 * entry to the original. Also, some overzealous versions of tset will
456 * also expand 'tc', so there might be *2* entries appended to the
457 * original. And, newer termcap databases have additional fields (e.g.,
458 * linux adds 'li' and 'co'), so it could get even longer. To top it all
459 * off, some termcap libraries don't do any length checking in tgetent().
460 * We should be safe with 4096.
461 */
462 char termcap_entry[4096];
463 /* termcap_buffer will hold at most 1 copy of any field; 1024 is enough. */
464 static char termcap_buffer[1024];
465 char *area = termcap_buffer;
466
467 have_attr = 0;
468 can_have_visual = FALSE;
469 can_have_expnonvis = FALSE;
470 clear_screen = clear_to_eos = clear_to_eol = NULL;
471 set_scroll_region = insert_line = delete_line = NULL;
472 delete_char = insert_char = insert_start = insert_end = NULL;
473 enter_ca_mode = exit_ca_mode = cursor_address = NULL;
474 keypad_on = keypad_off = NULL;
475 standout = underline = reverse = flash = dim = bold = italic = bell = NULL;
476 standout_off = underline_off = attr_off = attr_on = NULL;
477
478 {
479 /* Sanity check: a valid termcap entry should end in ':'. In
480 * particular, csh and tcsh put a limit of 1023 bytes on the TERMCAP
481 * variable, which may cause libpcap to give us garbage. (I really
482 * hate it when people blame tf for a bug in another program,
483 * especially when it's the evil csh or tcsh.)
484 */
485 const char *str, *shell;
486 int len, is_csh;
487 if ((str = getvar("TERMCAP")) && (len = strlen(str)) > 0) {
488 is_csh = ((shell = getenv("SHELL")) && smatch("*csh", shell) == 0);
489 if (str[len-1] != ':') {
490 tf_wprintf("unsetting invalid TERMCAP variable%s.",
491 is_csh ? ", which appears to have been corrupted by your "
492 "broken *csh shell" : "");
493 unsetvar(ffindglobalvar("TERMCAP"));
494 } else if (len == 1023 && (!getenv("TF_FORCE_TERMCAP")) && is_csh) {
495 tf_wprintf("unsetting the TERMCAP environment variable because "
496 "it looks like it has been truncated by your broken *csh "
497 "shell. To force TF to use TERMCAP, restart TF with the "
498 "TF_FORCE_TERMCAP environment variable set.");
499 unsetvar(ffindglobalvar("TERMCAP"));
500 }
501 }
502 }
503
504 if (!TERM || !*TERM) {
505 tf_wprintf("TERM undefined.");
506 } else if (tgetent(termcap_entry, TERM) <= 0) {
507 tf_wprintf("\"%s\" terminal unsupported.", TERM);
508 } else {
509 if (columns <= 0) columns = tgetnum("co");
510 if (lines <= 0) lines = tgetnum("li");
511
512 clear_screen = tgetstr("cl", &area);
513 clear_to_eol = tgetstr("ce", &area);
514 clear_to_eos = tgetstr("cd", &area);
515 enter_ca_mode = tgetstr("ti", &area);
516 exit_ca_mode = tgetstr("te", &area);
517 cursor_address = tgetstr("cm", &area);
518 set_scroll_region = tgetstr("cs", &area);
519 delete_char = tgetstr("dc", &area);
520 insert_char = tgetstr("ic", &area);
521 insert_start = tgetstr("im", &area);
522 insert_end = tgetstr("ei", &area);
523 insert_line = tgetstr("al", &area);
524 delete_line = tgetstr("dl", &area);
525 keypad_on = tgetstr("ks", &area);
526 keypad_off = tgetstr("ke", &area);
527
528 bell = tgetstr("bl", &area);
529 underline = tgetstr("us", &area);
530 reverse = tgetstr("mr", &area);
531 flash = tgetstr("mb", &area);
532 dim = tgetstr("mh", &area);
533 bold = tgetstr("md", &area);
534 italic = tgetstr("ZH", &area);
535 standout = tgetstr("so", &area);
536 underline_off = tgetstr("ue", &area);
537 standout_off = tgetstr("se", &area);
538 attr_off = tgetstr("me", &area);
539 attr_on = tgetstr("sa", &area);
540
541 if (!attr_off) {
542 /* can't exit all attrs, but maybe can exit underline/standout */
543 reverse = flash = dim = bold = italic = NULL;
544 if (!underline_off) underline = NULL;
545 if (!standout_off) standout = NULL;
546 }
547
548 for (i = 0; i < N_KEYCODES; i++) {
549 keycodes[i].code = tgetstr(keycodes[i].capname, &area);
550 #if 0
551 fprintf(stderr, "(%2s) %-12s = %s\n",
552 keycodes[i].capname, keycodes[i].name,
553 keycodes[i].code ? ascii_to_print(keycodes[i].code)->data : "NULL");
554 #endif
555 }
556
557 if (!keypad_off) keypad_on = NULL;
558
559 if (strcmp(TERM, "xterm") == 0) {
560 #if 0 /* Now that tf has virtual screens, the secondary buffer is ok. */
561 enter_ca_mode = exit_ca_mode = NULL; /* Avoid secondary buffer. */
562 #endif
563 /* Many old "xterm" termcaps mistakenly omit "cs". */
564 if (!set_scroll_region)
565 set_scroll_region = "\033[%i%d;%dr";
566 }
567 if (strcmp(TERM, "linux") == 0) {
568 /* Many "linux" termcaps mistakenly omit "ks" and "ke". */
569 if (!keypad_on && !keypad_off) {
570 keypad_on = "\033[?1h\033=";
571 keypad_off = "\033[?1l\033>";
572 }
573 }
574 }
575
576 if (!bell) bell = "\007";
577
578 #ifdef EMXANSI
579 VioSetAnsi(1,0); /* ensure ansi-mode */
580 #endif /* EMXANSI */
581
582 #endif /* TERMCAP */
583
584 #if 1
585 /* The insert_start code in iput() is apparently buggy. Until it is
586 * fixed, we ignore the insert_start capability. */
587 insert_start = NULL;
588 #endif
589 if (!insert_end) insert_start = NULL;
590
591 if (columns <= 0) columns = DEFAULT_COLUMNS;
592 if (lines <= 0) lines = DEFAULT_LINES;
593 set_var_by_id(VAR_wrapsize, columns - 1);
594 ix = 1;
595 can_have_visual = (clear_screen || clear_to_eol) && cursor_address;
596 can_have_expnonvis = delete_char && cursor_address;
597 set_var_by_id(VAR_scroll, has_scroll_region||(insert_line&&delete_line));
598 set_var_by_id(VAR_keypad, !!keypad_on);
599 have_attr = F_BELL;
600 if (underline) have_attr |= F_UNDERLINE;
601 if (reverse) have_attr |= F_REVERSE;
602 if (flash) have_attr |= F_FLASH;
603 if (dim) have_attr |= F_DIM;
604 if (bold) have_attr |= F_BOLD;
605 if (italic) have_attr |= F_ITALIC;
606 if (standout) have_attr |= F_BOLD;
607 }
608
setscroll(int top,int bottom)609 static void setscroll(int top, int bottom)
610 {
611 if (top_margin == top && bottom_margin == bottom) return; /* optimization */
612 #ifdef EMXANSI
613 bufflush();
614 #else
615 if (!set_scroll_region) return;
616 tpgoto(set_scroll_region, (bottom), (top));
617 cx = cy = -1; /* cursor position is undefined */
618 #endif
619 bottom_margin = bottom;
620 top_margin = top;
621 if (top != 1 || bottom != lines) set_refresh_pending(REF_PHYSICAL);
622 }
623
xy(int x,int y)624 static void xy(int x, int y)
625 {
626 if (x == cx && y == cy) return; /* already there */
627 if (cy < 0 || cx < 0 || cx > columns) { /* direct movement */
628 tpgoto(cursor_address, x, y);
629 } else if (x == 1 && y == cy) { /* optimization */
630 bufputc('\r');
631 } else if (x == 1 && y > cy && y < cy + 5 && /* optimization... */
632 cy >= top_margin && y <= bottom_margin) /* if '\n' is safe */
633 {
634 /* Some broken emulators (including CYGWIN32 b18) lose
635 * attributes when \r\n is printed, so we print \n\r instead.
636 */
637 bufputnc('\n', y - cy);
638 if (cx != 1) bufputc('\r');
639 } else if (y == cy && x < cx && x > cx - 7) { /* optimization */
640 bufputnc('\010', cx - x);
641 } else { /* direct movement */
642 tpgoto(cursor_address, x, y);
643 }
644 cx = x; cy = y;
645 }
646
clr(void)647 static void clr(void)
648 {
649 if (clear_screen)
650 tp(clear_screen);
651 else {
652 clear_lines(1, lines);
653 xy(1, 1);
654 }
655 cx = 1; cy = 1;
656 }
657
clear_line(void)658 static void clear_line(void)
659 {
660 if (cx != 1) bufputc('\r');
661 cx = 1;
662 if (clear_to_eol) {
663 tp(clear_to_eol);
664 } else {
665 bufputnc(' ', Wrap);
666 bufputc('\r');
667 }
668 }
669
670 #if TERMCAP
671 /* in case broken lib defines a putchar macro but not a putchar function */
672 /* Note: WATCOM compiler (QNX) has a function named fputchar. */
func_putchar(int c)673 static int func_putchar(int c)
674 {
675 return putchar(c);
676 }
677 #endif
678
tdirectputs(const char * str)679 static void tdirectputs(const char *str)
680 {
681 if (str)
682 #if TERMCAP
683 tputs(str, 1, func_putchar);
684 #else
685 puts(str);
686 #endif
687 }
688
tbufputs(const char * str)689 static void tbufputs(const char *str)
690 {
691 if (str)
692 #if TERMCAP
693 tputs(str, 1, fbufputc);
694 #else
695 bufputs(str);
696 #endif
697 }
698
get_keycode(const char * name)699 const char *get_keycode(const char *name)
700 {
701 Keycode *keycode;
702
703 keycode = (Keycode *)bsearch(name, keycodes, N_KEYCODES,
704 sizeof(Keycode), cstrstructcmp);
705 return !keycode ? NULL : keycode->code ? keycode->code : "";
706 }
707
708 /*******************
709 * WINDOW HANDLING *
710 *******************/
711
setup_screen(void)712 void setup_screen(void)
713 {
714 setscroll(1, lines);
715 output_disabled++;
716
717 if (visual && (!can_have_visual)) {
718 eprintf("Visual mode is not supported on this terminal.");
719 set_var_by_id(VAR_visual, 0); /* XXX recursion problem?? */
720 } else if (visual && lines < 1/*input*/ + 1/*status*/ + 2/*output*/) {
721 eprintf("Screen is too small for visual mode.");
722 set_var_by_id(VAR_visual, 0); /* XXX recursion problem?? */
723 }
724 screen_mode = visual;
725
726 if (!visual) {
727 prompt = display_screen->paused ? moreprompt : fgprompt();
728 old_ix = -1;
729
730 #ifdef SCREEN
731 } else {
732 prompt = fgprompt();
733 if (isize + status_height + 2 > lines) {
734 if ((1 + status_height + 2) <= lines) {
735 set_var_by_id(VAR_isize, lines - (status_height + 2));
736 } else {
737 set_var_by_id(VAR_isize, 1);
738 set_var_by_id(VAR_stat_height, lines - (isize + 2));
739 }
740 }
741 init_line_numbers();
742 #if 0
743 outcount = out_bot - out_top + 1;
744 #endif
745 if (enter_ca_mode) tp(enter_ca_mode);
746
747 if (scroll && !(has_scroll_region || (insert_line && delete_line))) {
748 set_var_by_id(VAR_scroll, 0);
749 }
750 update_status_line(NULL);
751 ix = iendx = oy = 1;
752 iy = iendy = istarty = in_top;
753 ipos();
754 #endif
755 }
756
757 if (keypad && keypad_on)
758 tp(keypad_on);
759
760 set_refresh_pending(REF_LOGICAL);
761 output_disabled--;
762 }
763
764 #define interesting(pl) ((pl)->attrs & F_HWRITE || (pl)->charattrs)
765
766 /* returns true if str passes filter */
screen_filter(Screen * screen,conString * str)767 static int screen_filter(Screen *screen, conString *str)
768 {
769 if (str == textdiv_str) return visual;
770 return (!screen->selflush || interesting(str))
771 &&
772 (!screen->filter_enabled ||
773 (screen->filter_attr ? interesting(str) :
774 patmatch(&screen->filter_pat, str, NULL) == screen->filter_sense));
775 }
776
777 #define purge_old_lines(screen) \
778 do { \
779 if (screen->nlline > screen->maxlline) \
780 f_purge_old_lines(screen); \
781 } while (0) \
782
f_purge_old_lines(Screen * screen)783 static void f_purge_old_lines(Screen *screen)
784 {
785 ListEntry *node;
786 PhysLine *pl;
787
788 /* Free old lines if over maximum and they're off the top of the screen.
789 * (Freeing them when they fall out of history doesn't work: a long-lived
790 * line from a different history could trap lines from the history
791 * corresponding to this screen.) */
792 node = screen->pline.head;
793 while (screen->nlline > screen->maxlline && node != screen->top) {
794 node = node->next;
795 pl = node->datum;
796 if (pl->start == 0) { /* beginning of a new lline */
797 /* free all plines (corresponding to a single lline) above node */
798 screen->nlline--;
799 while (screen->pline.head != node) {
800 pl = unlist(screen->pline.head, &screen->pline);
801 conStringfree(pl->str);
802 pfree(pl, plpool, str);
803 }
804 }
805 }
806 }
807
808 /* Add a line to the bottom. bot does not move if nothing below it matches
809 * filter. */
nextbot(Screen * screen)810 static int nextbot(Screen *screen)
811 {
812 PhysLine *pl;
813 ListEntry *bot;
814 int nback = screen->nback;
815 int passed_maxbot;
816
817 if (screen->bot) {
818 passed_maxbot = (screen->bot == screen->maxbot);
819 bot = screen->bot->next;
820 } else {
821 bot = screen->pline.head;
822 passed_maxbot = 1;
823 }
824 while (bot) {
825 pl = bot->datum;
826 nback--;
827 /* shouldn't need to recalculate visible, but there's a bug somewhere
828 * in maintaining visible flags in (bot,tail] after /unlimit. */
829 if ((pl->visible = screen_filter(screen, pl->str))) {
830 screen->bot = bot;
831 screen->nback_filtered--;
832 screen->nback = nback;
833 if (passed_maxbot) {
834 screen->maxbot = bot;
835 screen->nnew_filtered--;
836 screen->nnew = nback;
837 }
838 if (!screen->top)
839 screen->top = screen->bot;
840 screen->viewsize++;
841 if (screen->viewsize >= winlines())
842 screen->partialview = 0;
843 return 1;
844 }
845 if (bot == screen->maxbot)
846 passed_maxbot = 1;
847 bot = bot->next;
848 }
849 return 0;
850 }
851
852 /* Add a line to the top. top does not move if nothing above it matches
853 * filter. */
prevtop(Screen * screen)854 static int prevtop(Screen *screen)
855 {
856 PhysLine *pl;
857 ListEntry *prev;
858
859 /* (top == NULL && bot != NULL) happens after clear_display_screen() */
860 if (!screen->bot) return 0;
861 prev = screen->top ? screen->top->prev : screen->bot;
862 while (prev) {
863 pl = prev->datum;
864 /* visible flags are not maintained above top, must recalculate */
865 if ((pl->visible = screen_filter(screen, pl->str))) {
866 screen->top = prev;
867 screen->viewsize++;
868 if (screen->viewsize >= winlines())
869 screen->partialview = 0;
870 return 1;
871 }
872 /* XXX optimize for pl's that share same ll */
873 prev = prev->prev;
874 }
875 return 0;
876 }
877
878 /* Remove a line from the bottom. bot always moves. */
prevbot(Screen * screen)879 static int prevbot(Screen *screen)
880 {
881 ListEntry *node;
882 PhysLine *pl;
883 int viewsize_changed = 0;
884
885 if (!screen->bot) return 0;
886 while (screen->bot->prev) {
887 pl = (node = screen->bot)->datum;
888 /* visible flags are maintained in [top,bot], we needn't recalculate */
889 if (pl->visible) {
890 /* line being knocked off was visible */
891 screen->viewsize--;
892 screen->nback_filtered += !pl->tmp;
893 viewsize_changed = 1;
894 }
895 screen->bot = screen->bot->prev;
896 screen->nback += !pl->tmp;
897
898 if (pl->tmp) {
899 /* line being knocked off was temporary */
900 if (screen->maxbot == node)
901 screen->maxbot = screen->maxbot->prev;
902 unlist(node, &screen->pline);
903 conStringfree(pl->str);
904 pfree(pl, plpool, str);
905 }
906
907 /* stop if the viewsize has changed and we've found a new visible bot */
908 pl = screen->bot->datum;
909 if (viewsize_changed && pl->visible)
910 break;
911 }
912 return viewsize_changed;
913 }
914
915 /* Remove a line from the top. top always moves. */
nexttop(Screen * screen)916 static int nexttop(Screen *screen)
917 {
918 PhysLine *pl;
919 ListEntry *newtop;
920 int viewsize_changed = 0;
921
922 if (!screen->top) return 0;
923 while (screen->top->next) {
924 pl = screen->top->datum;
925 newtop = screen->top->next;
926 /* visible flags are maintained in [top,bot], we needn't recalculate */
927 if (pl->visible) {
928 /* line being knocked off was visible */
929 screen->viewsize--;
930 viewsize_changed = 1;
931 }
932 if (pl->tmp) {
933 /* line being knocked off was temporary */
934 unlist(screen->top, &screen->pline);
935 conStringfree(pl->str);
936 pfree(pl, plpool, str);
937 }
938 screen->top = newtop;
939
940 /* stop if the viewsize has changed and we've found a new visible top */
941 pl = screen->top->datum;
942 if (viewsize_changed && pl->visible)
943 break;
944 }
945
946 purge_old_lines(screen);
947 return viewsize_changed;
948 }
949
950 /* recalculate counters and visible flags in (bot, tail] */
screen_refilter_bottom(Screen * screen)951 static void screen_refilter_bottom(Screen *screen)
952 {
953 PhysLine *pl;
954 ListEntry *node;
955
956 if (screen_has_filter(screen)) {
957 screen->nback_filtered = 0;
958 screen->nnew_filtered = 0;
959 node = screen->pline.tail;
960 for ( ; node != screen->maxbot; node = node->prev) {
961 pl = node->datum;
962 if ((pl->visible = screen_filter(screen, pl->str)))
963 screen->nnew_filtered++;
964 }
965 screen->nback_filtered = screen->nnew_filtered;
966 for ( ; node != screen->bot; node = node->prev) {
967 pl = node->datum;
968 if ((pl->visible = screen_filter(screen, pl->str)))
969 screen->nback_filtered++;
970 }
971 } else {
972 screen->nback_filtered = screen->nback;
973 screen->nnew_filtered = screen->nnew;
974 }
975 }
976
screen_refilter(Screen * screen)977 static int screen_refilter(Screen *screen)
978 {
979 PhysLine *pl;
980 int want;
981 Screen oldscreen;
982 ListEntry *lowestvisible;
983
984 screen->needs_refilter = 0;
985 if (!screen->bot)
986 if (!(screen->bot = screen->pline.tail))
987 return 0;
988 oldscreen = *screen;
989 /* NB: screen->bot should stay in the same place, for when the filter
990 * is removed or changed. */
991 lowestvisible = screen->bot;
992 while (!screen_filter(screen, (pl = lowestvisible->datum)->str)) {
993 pl->visible = 0;
994 lowestvisible = lowestvisible->prev;
995 if (!lowestvisible) {
996 if (screen_has_filter(screen)) {
997 *screen = oldscreen; /* restore original state */
998 clear_screen_filter(screen);
999 screen_refilter(screen);
1000 }
1001 return 0;
1002 }
1003 }
1004 (pl = lowestvisible->datum)->visible = 1;
1005 /* recalculate top: start at bot and move top up until view is full */
1006 screen->viewsize = 1;
1007 screen->partialview = 0;
1008 screen->top = lowestvisible;
1009 want = winlines();
1010 while ((screen->viewsize < want) && prevtop(screen))
1011 /* empty loop */;
1012
1013 screen_refilter_bottom(screen);
1014
1015 return screen->viewsize;
1016 }
1017
screen_has_filter(Screen * screen)1018 int screen_has_filter(Screen *screen)
1019 {
1020 return screen->filter_enabled || screen->selflush;
1021 }
1022
clear_screen_filter(Screen * screen)1023 void clear_screen_filter(Screen *screen)
1024 {
1025 screen->filter_enabled = 0;
1026 screen->selflush = 0;
1027 screen->needs_refilter = 1;
1028 }
1029
set_screen_filter(Screen * screen,Pattern * pat,attr_t attr_flag,int sense)1030 void set_screen_filter(Screen *screen, Pattern *pat, attr_t attr_flag,
1031 int sense)
1032 {
1033 if (screen->filter_pat.str)
1034 free_pattern(&screen->filter_pat);
1035 if (pat) screen->filter_pat = *pat;
1036 screen->filter_attr = attr_flag;
1037 screen->filter_sense = sense;
1038 screen->filter_enabled = 1;
1039 screen->selflush = 0;
1040 screen->needs_refilter = 1;
1041 }
1042
enable_screen_filter(Screen * screen)1043 int enable_screen_filter(Screen *screen)
1044 {
1045 if (!screen->filter_pat.str && !screen->filter_attr)
1046 return 0;
1047 screen->filter_enabled = 1;
1048 screen->selflush = 0;
1049 screen->needs_refilter = 1;
1050 return 1;
1051 }
1052
winlines(void)1053 int winlines(void)
1054 {
1055 return visual ? out_bot - out_top + 1 : lines - 1;
1056 }
1057
1058 /* wraplines
1059 * Split logical line <ll> into physical lines and append them to <plines>.
1060 */
wraplines(conString * ll,List * plines,int visible)1061 static int wraplines(conString *ll, List *plines, int visible)
1062 {
1063 PhysLine *pl;
1064 int offset = 0, n = 0;
1065
1066 do {
1067 palloc(pl, PhysLine, plpool, str, __FILE__, __LINE__);
1068 pl->visible = visible;
1069 pl->tmp = 0;
1070 (pl->str = ll)->links++;
1071 pl->start = offset;
1072 pl->indent = wrapflag && pl->start && wrapspace < Wrap ? wrapspace : 0;
1073 pl->len = wraplen(ll->data + offset, ll->len - offset, pl->indent);
1074 offset += pl->len;
1075 inlist(pl, plines, plines->tail);
1076 n++;
1077 } while (offset < ll->len);
1078 return n;
1079 }
1080
rewrap(Screen * screen)1081 static void rewrap(Screen *screen)
1082 {
1083 PhysLine *pl;
1084 ListEntry *node;
1085 List old_pline;
1086 int wrapped, old_bot_visible, old_maxbot_visible;
1087
1088 if (!screen->bot) return;
1089 hide_screen(screen); /* delete temp lines */
1090
1091 old_pline.head = screen->pline.head;
1092 old_pline.tail = screen->pline.tail;
1093 screen->pline.head = screen->pline.tail = NULL;
1094 screen->nnew = screen->nnew_filtered = 0;
1095 screen->nback = screen->nback_filtered = 0;
1096
1097 pl = screen->bot->datum;
1098 old_bot_visible = pl->start + pl->len;
1099 pl = screen->maxbot->datum;
1100 old_maxbot_visible = pl->start + pl->len;
1101
1102 /* XXX possible optimization: don't rewrap [head, top) until needed.
1103 * [top, bot] is needed for display, and (bot, tail] is needed for
1104 * nback and nnew. */
1105
1106 /* rewrap llines corresponding to [head, bot] */
1107 do {
1108 node = old_pline.head;
1109 pl = unlist(old_pline.head, &old_pline);
1110 if (pl->start == 0) {
1111 wrapped = wraplines(pl->str, &screen->pline, pl->visible);
1112 screen->npline += wrapped;
1113 }
1114 conStringfree(pl->str);
1115 pfree(pl, plpool, str);
1116 } while (node != screen->bot);
1117
1118 /* recalculate bot within last lline */
1119 screen->bot = screen->pline.tail;
1120 while (screen->bot->prev) {
1121 pl = screen->bot->datum;
1122 if (pl->start == 0 || pl->start < old_bot_visible) break;
1123 screen->bot = screen->bot->prev;
1124 screen->nback++;
1125 }
1126
1127 /* rewrap llines corresponding to (bot, maxbot] */
1128 while (node != screen->maxbot) {
1129 node = old_pline.head;
1130 pl = unlist(old_pline.head, &old_pline);
1131 if (pl->start == 0) {
1132 wrapped = wraplines(pl->str, &screen->pline, pl->visible);
1133 screen->npline += wrapped;
1134 screen->nback += wrapped;
1135 }
1136 conStringfree(pl->str);
1137 pfree(pl, plpool, str);
1138 }
1139
1140 /* recalculate maxbot within last lline */
1141 screen->maxbot = screen->pline.tail;
1142 while (screen->maxbot->prev) {
1143 pl = screen->maxbot->datum;
1144 if (pl->start == 0 || pl->start < old_maxbot_visible) break;
1145 screen->maxbot = screen->maxbot->prev;
1146 screen->nnew++;
1147 }
1148
1149 /* rewrap llines corresponding to (maxbot, tail] */
1150 while (old_pline.head) {
1151 pl = unlist(old_pline.head, &old_pline);
1152 if (pl->start == 0) {
1153 wrapped = wraplines(pl->str, &screen->pline, pl->visible);
1154 screen->npline += wrapped;
1155 screen->nback += wrapped;
1156 screen->nnew += wrapped;
1157 }
1158 conStringfree(pl->str);
1159 pfree(pl, plpool, str);
1160 }
1161
1162 /* recalculate nback_filtered, nnew_filtered, viewsize, top */
1163 /* XXX TODO: should honor screen->partialview */
1164 screen_refilter(screen);
1165
1166 screen->scr_wrapflag = wrapflag;
1167 screen->scr_wrapsize = Wrap;
1168 screen->scr_wrapspace = wrapspace;
1169 screen->scr_wrappunct = wrappunct;
1170 }
1171
redraw_window(Screen * screen,int already_clear)1172 int redraw_window(Screen *screen, int already_clear)
1173 {
1174 if (screen->needs_refilter && !screen_refilter(screen))
1175 return 0;
1176
1177 if (!already_clear)
1178 clear_lines(1, visual ? out_bot : lines);
1179
1180 if (screen->scr_wrapflag != wrapflag ||
1181 screen->scr_wrapsize != Wrap ||
1182 screen->scr_wrapspace != wrapspace ||
1183 screen->scr_wrappunct != wrappunct)
1184 {
1185 rewrap(screen);
1186 } else {
1187 /* if terminal height decreased or textdiv was inserted */
1188 while (screen->viewsize > winlines())
1189 nexttop(screen);
1190 /* if terminal height increased */
1191 if (!screen->partialview) {
1192 while (screen->viewsize < winlines())
1193 if (!prevtop(screen)) break;
1194 }
1195 }
1196
1197 if (!visual) xy(1, lines);
1198
1199 /* viewsize may be 0 after clear_display_screen(), even if bot != NULL
1200 * or nplines > 0 */
1201 if (screen->viewsize) {
1202 PhysLine *pl;
1203 ListEntry *node;
1204 int first;
1205
1206 if (visual) xy(1, out_bot - (screen->viewsize - 1));
1207
1208 node = screen->top;
1209 first = 1;
1210 while (1) {
1211 pl = node->datum;
1212 if (pl->visible) {
1213 if (!first) crnl(1);
1214 first = 0;
1215 hwrite(pl->str, pl->start,
1216 pl->len < Wrap - pl->indent ? pl->len : Wrap - pl->indent,
1217 pl->indent);
1218 }
1219 if (node == screen->bot)
1220 break;
1221 node = node->next;
1222 }
1223 if (visual) {
1224 cx = 1; cy = out_bot;
1225 } else {
1226 crnl(1);
1227 cx = 1; cy = lines;
1228 }
1229 }
1230
1231 bufflush();
1232 old_ix = -1;
1233 set_refresh_pending(REF_PHYSICAL);
1234 ox = cx;
1235 oy = cy;
1236
1237 return 1;
1238 }
1239
redraw(void)1240 int redraw(void)
1241 {
1242 alert_timeout = tvzero;
1243 alert_pos = 0;
1244 alert_len = 0;
1245 if (visual) {
1246 top_margin = bottom_margin = -1; /* force scroll region reset */
1247 clr();
1248 } else {
1249 bufputnc('\n', lines); /* scroll region impossible */
1250 }
1251 setup_screen();
1252
1253 if (virtscreen) {
1254 hide_screen(display_screen);
1255 unhide_screen(display_screen);
1256 }
1257 return redraw_window(display_screen, 1);
1258 }
1259
clear_screen_view(Screen * screen)1260 static void clear_screen_view(Screen *screen)
1261 {
1262 if (screen->bot) {
1263 screen->top = screen->bot->next;
1264 screen->viewsize = 0;
1265 screen->partialview = 1;
1266 reset_outcount(screen);
1267 }
1268 }
1269
clear_display_screen(void)1270 int clear_display_screen(void)
1271 {
1272 if (!display_screen->bot) return 0;
1273 clear_screen_view(display_screen);
1274 return redraw_window(display_screen, 0);
1275 }
1276
parse_statusfield(StatusField * field,const char * s)1277 static const char *parse_statusfield(StatusField *field, const char *s)
1278 {
1279 const char *t;
1280
1281 memset(field, 0, sizeof(*field));
1282 field->internal = -1;
1283
1284 if (is_quote(*s)) { /* string literal */
1285 STATIC_BUFFER(buffer);
1286 if (!stringliteral(buffer, &s)) {
1287 eprintf("%S", buffer);
1288 return NULL;
1289 }
1290 field->name = STRDUP(buffer->data);
1291 } else if (*s == '@') { /* internal */
1292 for (t = ++s; is_alnum(*s) || *s == '_'; s++);
1293 field->name = strncpy(XMALLOC(s - t + 1), t, s - t);
1294 field->name[s-t] = '\0';
1295 field->internal = enum2int(field->name, 0, enum_status,
1296 "status_fields internal status");
1297 FREE(field->name);
1298 field->name = NULL;
1299 if (field->internal < 0)
1300 return NULL;
1301 } else if (is_alnum(*s) || *s == '_') { /* variable */
1302 for (t = s++; is_alnum(*s) || *s == '_'; s++);
1303 field->name = strncpy(XMALLOC(s - t + 1), t, s - t);
1304 field->name[s-t] = '\0';
1305 field->var = &bogusvar;
1306 } else { /* blank */
1307 field->name = NULL;
1308 }
1309
1310 if (*s == ':') {
1311 while (*++s == '-')
1312 field->rightjust = !field->rightjust;
1313 field->width = strtoint(s, &s);
1314 }
1315
1316 if (*s == ':') {
1317 for (t = s + 1; *s && !is_space(*s); s++);
1318 if (!parse_attrs(t, &field->attrs, ' '))
1319 return NULL;
1320 }
1321
1322 if (*s && !is_space(*s)) {
1323 eprintf("garbage in status field: %.8s", s);
1324 return NULL;
1325 }
1326 return s;
1327 }
1328
status_var(const char * type,const char * suffix)1329 static Var *status_var(const char *type, const char *suffix)
1330 {
1331 STATIC_BUFFER(name);
1332 Stringcat(Stringcat(Stringcpy(name, "status_"), type), suffix);
1333 return findorcreateglobalvar(name->data);
1334 }
1335
status_field_text(int row)1336 conString *status_field_text(int row)
1337 {
1338 ListEntry *node;
1339 StatusField *f;
1340 String *buf = Stringnew(NULL, columns, 0);
1341 int width;
1342
1343 if (row < 0 || row >= max_status_height)
1344 return CS(buf);
1345 for (node = statusfield_list[row]->head; node; node = node->next) {
1346 if (node != statusfield_list[row]->head)
1347 Stringadd(buf, ' ');
1348 f = (StatusField*)node->datum;
1349 width = f->width;
1350 if (f->internal >= 0) {
1351 Stringadd(buf, '@');
1352 SStringcat(buf, &enum_status[f->internal]);
1353 } else if (f->var) {
1354 Stringcat(buf, f->var->val.name);
1355 } else if (f->name) {
1356 Sappendf(buf, "\"%q\"", '"', f->name);
1357 if (width == strlen(f->name)) width = 0;
1358 }
1359 if (!width && !f->attrs && !f->rightjust)
1360 continue;
1361 Stringadd(buf, ':');
1362 if (width || f->rightjust)
1363 Sappendf(buf, "%s%d", f->rightjust ? "-" : "", width);
1364 if (!f->attrs)
1365 continue;
1366 Stringadd(buf, ':');
1367 attr2str(buf, f->attrs);
1368 }
1369 return CS(buf);
1370 }
1371
regen_status_fields(int row)1372 static void regen_status_fields(int row)
1373 {
1374 ListEntry *node;
1375 StatusField *f;
1376 int column;
1377
1378 /* finish calculations on status_fields_list */
1379 for (node = statusfield_list[row]->head; node; node = node->next) {
1380 f = (StatusField*)node->datum;
1381 if (f->var) { /* f->var == &bogusvar */
1382 if (f->var == &bogusvar) {
1383 f->var = findorcreateglobalvar(f->name);
1384 FREE(f->name);
1385 f->name = NULL;
1386 }
1387 f->var->statuses++;
1388 f->fmtvar = status_var("var_", f->var->val.name);
1389 f->attrvar = status_var("attr_var_", f->var->val.name);
1390 } else if (f->internal >= 0) {
1391 f->fmtvar = status_var("int_", enum_status[f->internal].data);
1392 f->attrvar = status_var("attr_int_", enum_status[f->internal].data);
1393 } else {
1394 continue;
1395 }
1396 f->fmtvar->statusfmts++;
1397 f->attrvar->statusattrs++;
1398 if (ch_attr(f->attrvar))
1399 f->vattrs = f->attrvar->val.u.attr;
1400 }
1401
1402 clock_update.tv_sec = 0;
1403 variable_width_field[row] = NULL;
1404 status_left[row] = status_right[row] = 0;
1405 column = 0;
1406 for (node = statusfield_list[row]->head; node; node = node->next) {
1407 f = (StatusField*)node->datum;
1408 status_left[row] = f->column = column;
1409 if (f->width == 0) {
1410 variable_width_field[row] = f;
1411 break;
1412 }
1413 column += f->width;
1414 }
1415 column = 0;
1416 if (node) {
1417 for (node = statusfield_list[row]->tail; node; node = node->prev) {
1418 f = (StatusField*)node->datum;
1419 if (f->width == 0) break;
1420 status_right[row] = -(f->column = (column -= f->width));
1421 }
1422 }
1423
1424 if (row == 0) {
1425 /* regenerate the value of %status_fields for backward compatibility */
1426 conString *buf = status_field_text(row);
1427 /* set_str_var_direct avoids error checking and recursion */
1428 set_str_var_direct(&special_var[VAR_stat_fields], TYPE_STR, buf);
1429 }
1430
1431 update_status_line(NULL);
1432 }
1433
find_statusfield_by_template(StatusField * target,int full_comparison,const char * label)1434 static ListEntry *find_statusfield_by_template(StatusField *target,
1435 int full_comparison, const char *label)
1436 {
1437 ListEntry *node;
1438 StatusField *f;
1439 int row;
1440
1441 for (row = 0; row < status_height; row++) {
1442 if (target->row >= 0 && row != target->row) continue;
1443 for (node = statusfield_list[row]->head; node; node = node->next) {
1444 f = (StatusField*)node->datum;
1445 if (target->internal >= 0) {
1446 if (target->internal != f->internal) continue;
1447 } else if (target->var) {
1448 if (!f->var || strcmp(target->name, f->var->val.name) != 0)
1449 continue;
1450 } else if (target->name) {
1451 if (!f->name || cstrcmp(target->name, f->name) != 0)
1452 continue;
1453 }
1454 if (full_comparison) {
1455 if (target->width && target->width != f->width) continue;
1456 if (target->attrs && target->attrs != f->attrs) continue;
1457 }
1458 return node;
1459 }
1460 }
1461 if (label) {
1462 eprintf(
1463 target->row<0 ? "%s: no such field" : "%s: no such field in row %d",
1464 label, target->row);
1465 }
1466 return NULL;
1467 }
1468
find_statusfield_by_name(int row,const char * spec)1469 static ListEntry *find_statusfield_by_name(int row, const char *spec)
1470 {
1471 ListEntry *result = NULL;
1472 StatusField target;
1473 if (parse_statusfield(&target, spec)) {
1474 target.row = row;
1475 result = find_statusfield_by_template(&target, TRUE, spec);
1476 }
1477 if (target.name) FREE(target.name);
1478 return result;
1479 }
1480
ch_status_int(Var * var)1481 int ch_status_int(Var *var)
1482 {
1483 if (warn_status)
1484 tf_wprintf("the default value of %s has "
1485 "changed between tf version 4 and 5.", var->val.name);
1486 return 1;
1487 }
1488
free_statusfield(StatusField * field)1489 static void free_statusfield(StatusField *field)
1490 {
1491 if (field->var && field->var != &bogusvar && !--field->var->statuses)
1492 freevar(field->var);
1493 if (field->fmtvar && !--field->fmtvar->statuses)
1494 freevar(field->fmtvar);
1495 if (field->attrvar && !--field->attrvar->statuses)
1496 freevar(field->attrvar);
1497 if (field->name)
1498 FREE(field->name);
1499 FREE(field);
1500 }
1501
status_add(int reset,int nodup,int row,ListEntry * where,const char * s,long spacer)1502 static int status_add(int reset, int nodup, int row, ListEntry *where,
1503 const char *s,
1504 long spacer) /* spacer>0 goes after new field, spacer<0 goes before */
1505 {
1506 int totwidth = 0, vwf_found = 0;
1507 List newlist[1];
1508 StatusField *field;
1509
1510 init_list(newlist);
1511
1512 if (!reset) {
1513 vwf_found = !!variable_width_field[row];
1514 totwidth = status_left[row] + status_right[row];
1515 }
1516
1517 /* validate and insert new fields */
1518 while (1) {
1519 while(is_space(*s)) s++;
1520 if (!*s) break;
1521 field = XMALLOC(sizeof(*field));
1522 if (!(s = parse_statusfield(field, s)))
1523 goto status_add_error;
1524 field->row = row;
1525
1526 if (!field->width && !field->var && field->internal < 0 && field->name)
1527 field->width = strlen(field->name);
1528
1529 if (nodup && find_statusfield_by_template(field, FALSE, NULL)) {
1530 free_statusfield(field);
1531 continue;
1532 }
1533
1534 if (field->width != 0) {
1535 totwidth += field->width;
1536 } else {
1537 if (vwf_found) {
1538 eprintf("Only one variable width status field is allowed.");
1539 goto status_add_error;
1540 }
1541 vwf_found = 1;
1542 }
1543
1544 inlist(field, newlist, newlist->tail);
1545 }
1546
1547 if (spacer && newlist->head && !reset && statusfield_list[row]->head) {
1548 field = XCALLOC(sizeof(*field));
1549 field->internal = -1;
1550 field->row = row;
1551 totwidth += (field->width = spacer < 0 ? -spacer : spacer);
1552 inlist(field, newlist, spacer >= 0 ? NULL : newlist->tail);
1553 }
1554
1555 if (totwidth > columns) {
1556 tf_wprintf("total status width (%d) is wider than screen (%d)",
1557 totwidth, columns);
1558 }
1559
1560 if (reset) {
1561 /* delete old fields and clean up referents */
1562 while (statusfield_list[row]->head)
1563 free_statusfield(unlist(statusfield_list[row]->head,
1564 statusfield_list[row]));
1565 *statusfield_list[row] = *newlist;
1566 } else {
1567 /* insert new fields into list */
1568 /* (We could be faster with ptr swapping, but this isn't critical) */
1569 while (newlist->head) {
1570 field = (StatusField*)unlist(newlist->head, newlist);
1571 where = inlist(field, statusfield_list[row], where);
1572 }
1573 }
1574
1575 regen_status_fields(row);
1576 return 1;
1577
1578 status_add_error:
1579 free_statusfield(field);
1580 while (newlist->head)
1581 free_statusfield(unlist(newlist->head, newlist));
1582 return 0;
1583 }
1584
handle_status_add_command(String * args,int offset)1585 struct Value *handle_status_add_command(String *args, int offset)
1586 {
1587 int opt, nodup = FALSE, row = -1, reset = 0;
1588 ValueUnion uval;
1589 long spacer = 1;
1590 const char *ptr, *before = NULL, *after = NULL;
1591 ListEntry *where;
1592
1593 startopt(CS(args), "A:B:s#r#xc");
1594 while ((opt = nextopt(&ptr, &uval, NULL, &offset))) {
1595 switch (opt) {
1596 case 'A': after = STRDUP(ptr); before = NULL; break;
1597 case 'B': before = STRDUP(ptr); after = NULL; break;
1598 case 's': spacer = uval.ival; break;
1599 case 'r': row = uval.ival; break;
1600 case 'x': nodup = TRUE; break;
1601 case 'c': reset = 1; break;
1602 default: return shareval(val_zero);
1603 }
1604 }
1605
1606 if (after) {
1607 if (*after) {
1608 where = find_statusfield_by_name(row, after);
1609 if (!where) return shareval(val_zero);
1610 row = ((StatusField*)(where->datum))->row;
1611 } else {
1612 if (row < 0) row = 0;
1613 where = statusfield_list[row]->tail;
1614 }
1615 FREE(after);
1616 } else if (before) {
1617 spacer = -spacer;
1618 if (*before) {
1619 where = find_statusfield_by_name(row, before);
1620 if (!where) return shareval(val_zero);
1621 row = ((StatusField*)(where->datum))->row;
1622 where = where->prev;
1623 } else {
1624 if (row < 0) row = 0;
1625 where = NULL;
1626 }
1627 FREE(before);
1628 } else {
1629 if (row < 0) row = 0;
1630 where = statusfield_list[row]->tail;
1631 }
1632
1633 return shareval(
1634 status_add(reset, nodup, row, where, args->data + offset, spacer) ?
1635 val_one : val_zero);
1636 }
1637
ch_status_fields(Var * var)1638 int ch_status_fields(Var *var)
1639 {
1640 if (warn_status) {
1641 tf_wprintf("setting status_fields directly is deprecated, "
1642 "and may clobber useful new features introduced in version 5. "
1643 "The recommended way to change "
1644 "status fields is with /status_add, /status_rm, or /status_edit. "
1645 "(This warning can be disabled with \"/set warn_status=off\".)");
1646 }
1647 return status_add(TRUE, FALSE, 0, NULL,
1648 status_fields ? status_fields : "", 0);
1649 }
1650
getspacer(ListEntry * node)1651 static int getspacer(ListEntry *node)
1652 {
1653 StatusField *f;
1654 if (!node) return 0;
1655 f = node->datum;
1656 return (f->var || f->internal >= 0 || f->name) ? 0 : f->width;
1657 }
1658
handle_status_rm_command(String * args,int offset)1659 struct Value *handle_status_rm_command(String *args, int offset)
1660 {
1661 ValueUnion uval;
1662 long row = -1;
1663 int opt;
1664 ListEntry *node;
1665
1666 startopt(CS(args), "r#");
1667 while ((opt = nextopt(NULL, &uval, NULL, &offset))) {
1668 switch (opt) {
1669 case 'r': row = uval.ival; break;
1670 default: return shareval(val_zero);
1671 }
1672 }
1673
1674 node = find_statusfield_by_name(row, args->data + offset);
1675 if (!node) return shareval(val_zero);
1676 row = ((StatusField*)(node->datum))->row;
1677 if (variable_width_field[row] == node->datum)
1678 variable_width_field[row] = NULL;
1679 if (!node->prev && getspacer(node->next)) {
1680 unlist(node->next, statusfield_list[row]); /* remove leading spacer */
1681 } else if (!node->next && getspacer(node->prev)) {
1682 unlist(node->prev, statusfield_list[row]); /* remove trailing spacer */
1683 } else if (getspacer(node->prev) && getspacer(node->next)) {
1684 /* remove smaller of two neighboring spacers */
1685 if (getspacer(node->prev) < getspacer(node->next))
1686 unlist(node->prev, statusfield_list[row]);
1687 else
1688 unlist(node->next, statusfield_list[row]);
1689 }
1690 unlist(node, statusfield_list[row]);
1691 regen_status_fields(row);
1692 return shareval(val_one);
1693 }
1694
handle_status_edit_command(String * args,int offset)1695 struct Value *handle_status_edit_command(String *args, int offset)
1696 {
1697 ValueUnion uval;
1698 long row = -1;
1699 int opt;
1700 StatusField *field;
1701 ListEntry *node;
1702
1703 startopt(CS(args), "r#");
1704 while ((opt = nextopt(NULL, &uval, NULL, &offset))) {
1705 switch (opt) {
1706 case 'r': row = uval.ival; break;
1707 default: return shareval(val_zero);
1708 }
1709 }
1710
1711 field = XMALLOC(sizeof(*field));
1712 if (!parse_statusfield(field, args->data + offset)) {
1713 free_statusfield(field);
1714 return shareval(val_zero);
1715 }
1716 field->row = row;
1717 node = find_statusfield_by_template(field, FALSE, args->data + offset);
1718 if (!node) {
1719 free_statusfield(field);
1720 return shareval(val_zero);
1721 }
1722 row = field->row = ((StatusField*)(node->datum))->row;
1723 if (!field->width && variable_width_field[row] &&
1724 node->datum != variable_width_field[row])
1725 {
1726 eprintf("Only one variable width status field per row is allowed.");
1727 free_statusfield(field);
1728 return shareval(val_zero);
1729 }
1730 free_statusfield(node->datum);
1731 node->datum = field;
1732 regen_status_fields(field->row);
1733 return shareval(val_one);
1734 }
1735
1736 /* returns column of field, or -1 if field is obscured by alert */
statusfield_column(StatusField * field)1737 static inline int statusfield_column(StatusField *field)
1738 {
1739 int column;
1740 if (field->column >= 0)
1741 return (field->column > columns) ? columns : field->column;
1742 column = field->column +
1743 ((status_left[field->row] + status_right[field->row] > columns) ?
1744 status_left[field->row] + status_right[field->row] : columns);
1745 return (column > columns) ? columns : column;
1746 }
1747
status_width(StatusField * field,int start)1748 static int status_width(StatusField *field, int start)
1749 {
1750 int width;
1751 if (start >= columns) return 0;
1752 width = (field->width == 0) ?
1753 columns - status_right[field->row] - status_left[field->row] :
1754 (field->width > 0) ? field->width : -field->width;
1755 if (width > columns - start)
1756 width = columns - start;
1757 if (width < 0)
1758 width = 0;
1759 return width;
1760 }
1761
handle_status_width_func(const char * name)1762 int handle_status_width_func(const char *name)
1763 {
1764 /* XXX need to be able to specify row */
1765 StatusField *field;
1766 field = (StatusField*)find_statusfield_by_name(-1, name)->datum;
1767 return field ? status_width(field, statusfield_column(field)) : 0;
1768 }
1769
format_statusfield(StatusField * field)1770 static int format_statusfield(StatusField *field)
1771 {
1772 STATIC_BUFFER(scratch);
1773 const char *old_command;
1774 Value *fmtval, *val = NULL;
1775 Program *prog;
1776 int width, x, i;
1777
1778 output_disabled++;
1779 Stringtrunc(scratch, 0);
1780 if (field->internal >= 0 || field->var) {
1781 fmtval = getvarval(field->fmtvar);
1782 old_command = current_command;
1783 if (fmtval) {
1784 current_command = fmtval->name;
1785 if (fmtval->type & TYPE_EXPR) {
1786 prog = fmtval->u.prog;
1787 } else if (fmtval->type == TYPE_STR) {
1788 prog = compile_tf(valstr(fmtval), 0, -1, 1, 2);
1789 if ((fmtval->u.prog = prog))
1790 fmtval->type |= TYPE_EXPR;
1791 } else {
1792 prog = compile_tf(valstr(fmtval), 0, -1, 1, 0);
1793 }
1794 if (prog) {
1795 val = expr_value_safe(prog);
1796 if (!(fmtval->type & TYPE_EXPR))
1797 prog_free(prog);
1798 }
1799 if (val) {
1800 SStringcpy(scratch, valstr(val));
1801 freeval(val);
1802 } else {
1803 SStringcpy(scratch, blankline);
1804 }
1805 } else if (field->var) {
1806 current_command = field->var->val.name;
1807 val = getvarval(field->var);
1808 SStringcpy(scratch, val ? valstr(val) : blankline);
1809 }
1810 current_command = old_command;
1811 } else if (field->name) { /* string literal */
1812 Stringcpy(scratch, field->name);
1813 }
1814
1815 x = statusfield_column(field);
1816 width = status_width(field, x);
1817 if (scratch->len > width)
1818 Stringtrunc(scratch, width);
1819
1820 if (field->rightjust && scratch->len < width) { /* left pad */
1821 for (i = 0; i < width - scratch->len; i++, x++) {
1822 status_line[field->row]->data[x] = true_status_pad;
1823 status_line[field->row]->charattrs[x] = status_attr;
1824 }
1825 }
1826
1827 if (scratch->len) { /* value */
1828 attr_t attrs = scratch->attrs;
1829 attrs = adj_attr(attrs, status_attr);
1830 attrs = adj_attr(attrs, field->attrs);
1831 attrs = adj_attr(attrs, field->vattrs);
1832 for (i = 0; i < scratch->len; i++, x++) {
1833 status_line[field->row]->data[x] = scratch->data[i];
1834 status_line[field->row]->charattrs[x] = scratch->charattrs ?
1835 adj_attr(attrs, scratch->charattrs[i]) : attrs;
1836 }
1837 }
1838
1839 if (!field->rightjust && scratch->len < width) { /* right pad */
1840 for (i = 0; i < width - scratch->len; i++, x++) {
1841 status_line[field->row]->data[x] = true_status_pad;
1842 status_line[field->row]->charattrs[x] = status_attr;
1843 }
1844 }
1845
1846 if (field->internal == STAT_MORE)
1847 need_more_refresh = 0;
1848 if (field->internal == STAT_CLOCK) {
1849 struct tm *local;
1850 time_t sec = time(NULL);
1851 /* note: localtime()->tm_sec won't compile if prototype is missing */
1852 local = localtime(&sec);
1853 clock_update.tv_sec = sec + 60 - local->tm_sec;
1854 }
1855
1856 #if 0
1857 if (field->var && !field->var->status) /* var was unset */
1858 field->var = NULL;
1859 #endif
1860 output_disabled--;
1861 return width;
1862 }
1863
display_status_segment(int row,int start,int width)1864 static void display_status_segment(int row, int start, int width)
1865 {
1866 if (!alert_len || row != alert_row ||
1867 start + width <= alert_pos || start >= alert_pos + alert_len)
1868 {
1869 /* no overlap with alert */
1870 xy(start + 1, stat_top + row);
1871 hwrite(CS(status_line[row]), start, width, 0);
1872 } else {
1873 if (start < alert_pos) {
1874 /* segment starts left of alert */
1875 xy(start + 1, stat_top + row);
1876 hwrite(CS(status_line[row]), start, alert_pos - start, 0);
1877 }
1878 if (start + width >= alert_pos) {
1879 /* segment ends right of alert */
1880 xy(alert_pos + alert_len + 1, stat_top + row);
1881 hwrite(CS(status_line[row]), alert_pos + alert_len,
1882 start + width - (alert_pos + alert_len), 0);
1883 }
1884 }
1885 }
1886
update_status_field(Var * var,stat_id_t internal)1887 void update_status_field(Var *var, stat_id_t internal)
1888 {
1889 ListEntry *node;
1890 StatusField *field;
1891 int row, column, width;
1892 int count = 0;
1893
1894 if (screen_mode < 1) return;
1895
1896 if (var && var->statusattrs) {
1897 if (!ch_attr(var))
1898 return; /* error */
1899 }
1900
1901 for (row = 0; row < status_height; row++) {
1902 for (node = statusfield_list[row]->head; node; node = node->next) {
1903 field = (StatusField*)node->datum;
1904 if (var) {
1905 if (field->var == var)
1906 /* do nothing */;
1907 else if (field->fmtvar == var)
1908 /* do nothing */;
1909 else if (field->attrvar == var)
1910 field->vattrs = var->val.u.attr;
1911 else
1912 continue;
1913 }
1914 if (internal >= 0 && field->internal != internal) continue;
1915 column = statusfield_column(field);
1916 if (column >= columns) /* doesn't fit, nor will any later fields */
1917 break;
1918 count++;
1919 width = format_statusfield(field);
1920 display_status_segment(row, column, width);
1921 }
1922 }
1923
1924 if (count) {
1925 bufflush();
1926 set_refresh_pending(REF_PHYSICAL);
1927 }
1928 }
1929
format_status_line(void)1930 void format_status_line(void)
1931 {
1932 ListEntry *node;
1933 StatusField *field;
1934 int row, column, width;
1935
1936 for (row = 0; row < status_height; row++) {
1937 column = 0;
1938 width = 0;
1939 for (node = statusfield_list[row]->head; node; node = node->next) {
1940 field = (StatusField*)node->datum;
1941
1942 if ((column = statusfield_column(field)) >= columns)
1943 break;
1944 width = format_statusfield(field);
1945 }
1946
1947 for (column += width; column < columns; column++) {
1948 status_line[row]->data[column] = true_status_pad;
1949 status_line[row]->charattrs[column] = status_attr;
1950 }
1951 }
1952 }
1953
display_status_line(void)1954 int display_status_line(void)
1955 {
1956 int row;
1957
1958 if (screen_mode < 1) return 0;
1959
1960 for (row = 0; row < status_height; row++) {
1961 display_status_segment(row, 0, columns);
1962 }
1963
1964 bufflush();
1965 set_refresh_pending(REF_PHYSICAL);
1966 return 1;
1967 }
1968
update_status_line(Var * var)1969 int update_status_line(Var *var)
1970 {
1971 /* XXX optimization: some code that calls update_status_line() without
1972 * any change in status_line could call display_status_line() directly,
1973 * avoiding reformatting (in particular, status_{int,var}_* execution). */
1974 format_status_line();
1975 display_status_line();
1976 return 1; /* for variable func */
1977 }
1978
alert(conString * msg)1979 void alert(conString *msg)
1980 {
1981 int row = 0;
1982 int new_pos, new_len;
1983 ListEntry *node;
1984 StatusField *field;
1985 attr_t orig_attrs;
1986
1987 if (msg->attrs & F_GAG && gag)
1988 return;
1989 alert_id++;
1990 msg->links++; /* some callers pass msg with links==0, so we ++ and free */
1991 if (!visual) {
1992 tfputline(msg, tferr);
1993 } else {
1994 /* default to position 0 */
1995 new_pos = 0;
1996 new_len = msg->len > Wrap ? Wrap : msg->len;
1997 if (msg->len < Wrap) {
1998 /* if there's a field after @world, and msg fits there, use it */
1999 for (node = statusfield_list[row]->head; node; node = node->next) {
2000 field = (StatusField*)node->datum;
2001 if (field->internal == STAT_WORLD && node->next) {
2002 field = (StatusField*)node->next->datum;
2003 break;
2004 }
2005 }
2006 if (node) {
2007 new_pos = (field->column < 0 ? columns : 0) + field->column;
2008 if (new_pos + new_len > Wrap)
2009 new_pos = 0;
2010 }
2011 }
2012
2013 if (alert_len &&
2014 (alert_pos < new_pos || alert_pos + alert_len > new_pos + new_len))
2015 {
2016 /* part of old alert would still be visible under new alert */
2017 /* XXX this could be optimized */
2018 clear_alert();
2019 }
2020
2021 alert_len = new_len;
2022 alert_pos = new_pos;
2023
2024 gettime(&alert_timeout);
2025 tvadd(&alert_timeout, &alert_timeout, &alert_time);
2026
2027 xy(alert_pos + 1, stat_top + alert_row);
2028 orig_attrs = msg->attrs;
2029 msg->attrs = adj_attr(msg->attrs, alert_attr);
2030 hwrite(msg, 0, alert_len, 0);
2031 msg->attrs = orig_attrs;
2032
2033 bufflush();
2034 set_refresh_pending(REF_PHYSICAL);
2035 }
2036 conStringfree(msg);
2037 }
2038
clear_alert(void)2039 void clear_alert(void)
2040 {
2041 if (!alert_len) return;
2042 xy(alert_pos + 1, stat_top + alert_row);
2043 hwrite(CS(status_line[alert_row]), alert_pos, alert_len, 0);
2044 bufflush();
2045 set_refresh_pending(REF_PHYSICAL);
2046 alert_timeout = tvzero;
2047 alert_pos = 0;
2048 alert_len = 0;
2049 alert_id = 0;
2050 }
2051
2052 /* used by %{visual}, %{isize}, SIGWINCH */
ch_visual(Var * var)2053 int ch_visual(Var *var)
2054 {
2055 int need_redraw = 0, resized = 0, row;
2056
2057 for (row = 0; row < max_status_height; row++) {
2058 if (status_line[row]->len < columns)
2059 Stringnadd(status_line[row], '?', columns - status_line[row]->len);
2060 Stringtrunc(status_line[row], columns);
2061 }
2062
2063 if (screen_mode < 0) { /* e.g., called by init_variables() */
2064 return 1;
2065 } else if (var == &special_var[VAR_visual]) { /* %visual changed */
2066 need_redraw = resized = 1;
2067 alert_timeout = tvzero;
2068 alert_pos = 0;
2069 alert_len = 0;
2070 } else if (var == &special_var[VAR_isize]) { /* %isize changed */
2071 need_redraw = resized = visual;
2072 #ifdef SCREEN
2073 } else { /* SIGWINCH */
2074 need_redraw = 1;
2075 resized = 1;
2076 cx = cy = -1; /* unknown */
2077 top_margin = bottom_margin = -1; /* unknown */
2078 #endif
2079 }
2080
2081 if (need_redraw)
2082 redraw();
2083 if (resized)
2084 transmit_window_size();
2085 return 1;
2086 }
2087
ch_status_height(Var * var)2088 int ch_status_height(Var *var)
2089 {
2090 if (status_height > max_status_height) {
2091 eprintf("%s must be <= %d", var->val.name, max_status_height);
2092 return 0;
2093 }
2094 if (visual) redraw();
2095 transmit_window_size();
2096 return 1;
2097 }
2098
ch_expnonvis(Var * var)2099 int ch_expnonvis(Var *var)
2100 {
2101 if (!can_have_expnonvis && expnonvis) {
2102 eprintf("expnonvis mode is not supported on this terminal.");
2103 return 0;
2104 }
2105 if (!visual) {
2106 old_ix = -1;
2107 redraw();
2108 }
2109 return 1;
2110 }
2111
2112 /* used by %{wrap}, %{wrappunct}, %{wrapsize}, %{wrapspace} */
ch_wrap(Var * var)2113 int ch_wrap(Var *var)
2114 {
2115 if (screen_mode < 0) /* e.g., called by init_variables() */
2116 return 1;
2117
2118 redraw_window(display_screen, 0);
2119 transmit_window_size();
2120 return 1;
2121 }
2122
fix_screen(void)2123 void fix_screen(void)
2124 {
2125 oflush();
2126 if (keypad && keypad_off)
2127 tp(keypad_off);
2128 if (screen_mode <= 0) {
2129 clear_line();
2130 #ifdef SCREEN
2131 } else {
2132 top_margin = bottom_margin = -1; /* force scroll region reset */
2133 setscroll(1, lines);
2134 clear_lines(stat_top, lines);
2135 xy(1, stat_top);
2136 if (exit_ca_mode) tp(exit_ca_mode);
2137 #endif
2138 }
2139 cx = cy = -1;
2140 bufflush();
2141 screen_mode = -1;
2142 }
2143
2144 /* minimal_fix_screen() avoids use of possibly corrupted structures. */
minimal_fix_screen(void)2145 void minimal_fix_screen(void)
2146 {
2147 tp = tdirectputs;
2148 fg_screen = default_screen;
2149 default_screen->pline.head = default_screen->pline.tail = NULL;
2150 default_screen->top = default_screen->bot = NULL;
2151 outbuf->data = NULL;
2152 outbuf->len = 0;
2153 outbuf->size = 0;
2154 output_disabled++;
2155 fix_screen();
2156 fflush(stdout);
2157 }
2158
clear_lines(int start,int end)2159 static void clear_lines(int start, int end)
2160 {
2161 if (start > end) return;
2162 xy(1, start);
2163 if (end >= lines && clear_to_eos) {
2164 tp(clear_to_eos); /* cx,cy were set by xy() */
2165 } else {
2166 clear_line();
2167 while (start++ < end) {
2168 bufputc('\n');
2169 clear_line();
2170 }
2171 cy = end;
2172 }
2173 }
2174
2175 /* clear entire input window */
clear_input_window(void)2176 static void clear_input_window(void)
2177 {
2178 /* only called in visual mode */
2179 clear_lines(in_top, lines);
2180 ix = iendx = 1;
2181 iy = iendy = istarty = in_top;
2182 ipos();
2183 }
2184
2185 /* clear logical input line */
clear_input_line(void)2186 static void clear_input_line(void)
2187 {
2188 if (!visual) clear_line();
2189 else clear_lines(istarty, iendy);
2190 ix = iendx = 1;
2191 iy = iendy = istarty;
2192 if (visual) ipos();
2193 }
2194
2195 /* affects iendx, iendy, istarty. No effect on ix, iy. */
scroll_input(int n)2196 static void scroll_input(int n)
2197 {
2198 if (n > isize) {
2199 clear_lines(in_top, lines);
2200 iendy = in_top;
2201 } else if (delete_line) {
2202 xy(1, in_top);
2203 for (iendy = lines + 1; iendy > lines - n + 1; iendy--)
2204 tp(delete_line);
2205 } else if (has_scroll_region) {
2206 setscroll(in_top, lines);
2207 xy(1, lines);
2208 crnl(n); /* DON'T: cy += n; */
2209 iendy = lines - n + 1;
2210 }
2211 xy(iendx = 1, iendy);
2212 }
2213
2214
2215 /***********************************************************************
2216 * *
2217 * INPUT WINDOW HANDLING *
2218 * *
2219 ***********************************************************************/
2220
2221 /* ictrl_put
2222 * display n characters of s, with control characters converted to printable
2223 * bold reverse (so the physical width is also n).
2224 */
ictrl_put(const char * s,int n)2225 static void ictrl_put(const char *s, int n)
2226 {
2227 int attrflag;
2228 char c;
2229
2230 for (attrflag = 0; n > 0; s++, n--) {
2231 c = unmapchar(localize(*s));
2232 if (is_cntrl(c)) {
2233 if (!attrflag)
2234 attributes_on(F_BOLD | F_REVERSE), attrflag = 1;
2235 bufputc(CTRL(c));
2236 } else {
2237 if (attrflag)
2238 attributes_off(F_BOLD | F_REVERSE), attrflag = 0;
2239 bufputc(c);
2240 }
2241 }
2242 if (attrflag) attributes_off(F_BOLD | F_REVERSE);
2243 }
2244
2245 /* ioutputs
2246 * Print string within bounds of input window. len is the number of
2247 * characters to print; return value is the number actually printed,
2248 * which may be less than len if string doesn't fit in the input window.
2249 * precondition: iendx,iendy and real cursor are at the output position.
2250 */
ioutputs(const char * str,int len)2251 static int ioutputs(const char *str, int len)
2252 {
2253 int space, written;
2254
2255 for (written = 0; len > 0; len -= space) {
2256 if ((space = Wrap - iendx + 1) <= 0) {
2257 if (!visual || iendy == lines) break; /* at end of window */
2258 if (visual) xy(iendx = 1, ++iendy);
2259 space = Wrap;
2260 }
2261 if (space > len) space = len;
2262 ictrl_put(str, space); cx += space;
2263 str += space;
2264 written += space;
2265 iendx += space;
2266 }
2267 return written;
2268 }
2269
2270 /* ioutall
2271 * Performs ioutputs() for input buffer starting at kpos.
2272 * A negative kpos means to display that much of the end of the prompt.
2273 */
ioutall(int kpos)2274 static void ioutall(int kpos)
2275 {
2276 int ppos;
2277
2278 if (kpos < 0) { /* posible only if there's a prompt */
2279 kpos = -(-kpos % Wrap);
2280 ppos = prompt->len + kpos;
2281 if (ppos < 0) ppos = 0;
2282 hwrite(prompt, ppos, prompt->len - ppos, 0);
2283 iendx = -kpos + 1;
2284 kpos = 0;
2285 }
2286 if (sockecho())
2287 ioutputs(keybuf->data + kpos, keybuf->len - kpos);
2288 }
2289
iput(int len)2290 void iput(int len)
2291 {
2292 const char *s;
2293 int count, scrolled = 0, oiex = iendx, oiey = iendy;
2294
2295 s = keybuf->data + keyboard_pos - len;
2296
2297 if (!sockecho()) return;
2298 if (visual) physical_refresh();
2299
2300 if (keybuf->len - keyboard_pos > 8 && /* faster than redisplay? */
2301 visual && insert && clear_to_eol &&
2302 (insert_char || insert_start) && /* can insert */
2303 cy + (cx + len) / Wrap <= lines && /* new text will fit in window */
2304 Wrap - len > 8) /* faster than redisplay? */
2305 {
2306 /* fast insert */
2307 iy = iy + (ix - 1 + len) / Wrap;
2308 ix = (ix - 1 + len) % Wrap + 1;
2309 iendy = iendy + (iendx - 1 + len) / Wrap;
2310 iendx = (iendx - 1 + len) % Wrap + 1;
2311 if (iendy > lines) { iendy = lines; iendx = Wrap + 1; }
2312 if (cx + len <= Wrap) {
2313 if (insert_start) tp(insert_start);
2314 else for (count = len; count; count--) tp(insert_char);
2315 ictrl_put(s, len);
2316 s += Wrap - (cx - 1);
2317 cx += len;
2318 } else {
2319 ictrl_put(s, Wrap - (cx - 1));
2320 s += Wrap - (cx - 1);
2321 cx = Wrap + 1;
2322 if (insert_start) tp(insert_start);
2323 }
2324 while (s < keybuf->data + keybuf->len) {
2325 if (Wrap < columns && cx <= Wrap) {
2326 xy(Wrap + 1, cy);
2327 tp(clear_to_eol);
2328 }
2329 if (cy == lines) break;
2330 xy(1, cy + 1);
2331 if (!insert_start)
2332 for (count = len; count; count--) tp(insert_char);
2333 ictrl_put(s, len); cx += len;
2334 s += Wrap;
2335 }
2336 if (insert_start) tp(insert_end);
2337 ipos();
2338 bufflush();
2339 return;
2340 }
2341
2342 iendx = ix;
2343 iendy = iy;
2344
2345 /* Display as much as possible. If it doesn't fit, scroll and repeat
2346 * until the whole string has been displayed.
2347 */
2348 while (count = ioutputs(s, len), s += count, (len -= count) > 0) {
2349 scrolled++;
2350 if (!visual) {
2351 if (expnonvis) {
2352 int i;
2353 int prompt_len = 0;
2354 bufputc('\r');
2355 if (prompt) {
2356 prompt_len = prompt->len % Wrap;
2357 hwrite(prompt, prompt->len - prompt_len, prompt_len, 0);
2358 }
2359 for (i = 0; i < sidescroll; i++)
2360 tp(delete_char);
2361 iendx -= i;
2362 cx = cy = -1;
2363 xy(iendx, lines);
2364 } else {
2365 crnl(1); cx = 1;
2366 iendx = ix = 1;
2367 }
2368 } else if (scroll && !clearfull) {
2369 scroll_input(1);
2370 if (istarty > in_top) istarty--;
2371 } else {
2372 clear_input_window();
2373 }
2374 }
2375 ix = iendx;
2376 iy = iendy;
2377
2378 if (insert || scrolled) {
2379 /* we must (re)display tail */
2380 ioutputs(keybuf->data + keyboard_pos, keybuf->len - keyboard_pos);
2381 if (visual) ipos();
2382 else { bufputnc('\010', iendx - ix); cx -= (iendx - ix); }
2383
2384 } else if ((iendy - oiey) * Wrap + iendx - oiex < 0) {
2385 /* if we didn't write past old iendx/iendy, restore them */
2386 iendx = oiex;
2387 iendy = oiey;
2388 }
2389
2390 bufflush();
2391 }
2392
inewline(void)2393 void inewline(void)
2394 {
2395 ix = iendx = 1;
2396 if (!visual) {
2397 if (expnonvis) {
2398 clear_input_line();
2399 } else {
2400 crnl(1); cx = 1; cy++;
2401 }
2402 if (prompt) set_refresh_pending(REF_PHYSICAL);
2403
2404 } else {
2405 if (cleardone) {
2406 clear_input_window();
2407 } else {
2408 iy = iendy + 1;
2409 if (iy > lines) {
2410 if (scroll && !clearfull) {
2411 scroll_input(1);
2412 iy--;
2413 } else {
2414 clear_input_window();
2415 }
2416 }
2417 }
2418 istarty = iendy = iy;
2419 set_refresh_pending(prompt ? REF_LOGICAL : REF_PHYSICAL);
2420 }
2421
2422 bufflush();
2423 }
2424
2425 /* idel() assumes place is in bounds and != keyboard_pos. */
idel(int place)2426 void idel(int place)
2427 {
2428 int len;
2429 int oiey = iendy;
2430
2431 if ((len = place - keyboard_pos) < 0) keyboard_pos = place;
2432 if (!sockecho()) return;
2433 if (len < 0) ix += len;
2434
2435 if (!visual) {
2436 int prompt_len = prompt ? prompt->len % Wrap : 0;
2437 if (ix < prompt_len + 1 || need_refresh) {
2438 physical_refresh();
2439 return;
2440 }
2441 if (expnonvis && ix == prompt_len + 1 && keyboard_pos == keybuf->len) {
2442 /* there would be nothing left; slide the window so there is */
2443 physical_refresh();
2444 return;
2445 }
2446 if (len < 0) { bufputnc('\010', -len); cx += len; }
2447
2448 } else {
2449 /* visual */
2450 if (ix < 1) {
2451 iy -= ((-ix) / Wrap) + 1;
2452 ix = Wrap - ((-ix) % Wrap);
2453 }
2454 if (iy < in_top) {
2455 logical_refresh();
2456 return;
2457 }
2458 physical_refresh();
2459 }
2460
2461 if (len < 0) len = -len;
2462
2463 if (visual && delete_char &&
2464 keybuf->len - keyboard_pos > 3 && len < Wrap/3)
2465 {
2466 /* hardware method */
2467 int i, space, pos;
2468
2469 iendy = iy;
2470 if (ix + len <= Wrap) {
2471 for (i = len; i; i--) tp(delete_char);
2472 iendx = Wrap + 1 - len;
2473 } else {
2474 iendx = ix;
2475 }
2476 pos = keyboard_pos - ix + iendx;
2477
2478 while (pos < keybuf->len) {
2479 if ((space = Wrap - iendx + 1) <= 0) {
2480 if (iendy == lines) break; /* at end of window */
2481 xy(iendx = 1, ++iendy);
2482 for (i = len; i; i--) tp(delete_char);
2483 space = Wrap - len;
2484 if (space > keybuf->len - pos) space = keybuf->len - pos;
2485 } else {
2486 xy(iendx, iendy);
2487 if (space > keybuf->len - pos) space = keybuf->len - pos;
2488 ictrl_put(keybuf->data + pos, space); cx += space;
2489 }
2490 iendx += space;
2491 pos += space;
2492 }
2493
2494 /* erase tail */
2495 if (iendy < oiey) {
2496 crnl(1); cx = 1; cy++;
2497 clear_line();
2498 }
2499
2500 } else {
2501 /* redisplay method */
2502 iendx = ix;
2503 iendy = iy;
2504 ioutputs(keybuf->data + keyboard_pos, keybuf->len - keyboard_pos);
2505
2506 /* erase tail */
2507 if (len > Wrap - cx + 1) len = Wrap - cx + 1;
2508 if (visual && clear_to_eos && (len > 2 || cy < oiey)) {
2509 tp(clear_to_eos);
2510 } else if (clear_to_eol && len > 2) {
2511 tp(clear_to_eol);
2512 if (visual && cy < oiey) clear_lines(cy + 1, oiey);
2513 } else {
2514 bufputnc(' ', len); cx += len;
2515 if (visual && cy < oiey) clear_lines(cy + 1, oiey);
2516 }
2517 }
2518
2519 /* restore cursor */
2520 if (visual) ipos();
2521 else { bufputnc('\010', cx - ix); cx = ix; }
2522
2523 bufflush();
2524 }
2525
igoto(int place)2526 int igoto(int place)
2527 {
2528 int diff;
2529
2530 if (place < 0)
2531 place = 0;
2532 if (place > keybuf->len)
2533 place = keybuf->len;
2534 diff = place - keyboard_pos;
2535 keyboard_pos = place;
2536
2537 if (!diff) {
2538 /* no physical change */
2539 dobell(1);
2540
2541 } else if (!sockecho()) {
2542 /* no physical change */
2543
2544 } else if (!visual) {
2545 int prompt_len = prompt ? prompt->len % Wrap : 0;
2546 ix += diff;
2547 if (ix-1 < prompt_len) { /* off left edge of screen/prompt */
2548 if (expnonvis && insert_char && prompt_len - (ix-1) <= Wrap/2) {
2549 /* can scroll, and amount of scroll needed is <= half screen */
2550 int i, n;
2551 bufputc('\r');
2552 if (prompt)
2553 hwrite(prompt, prompt->len - prompt_len, prompt_len, 0);
2554 n = prompt_len - (ix-1); /* get ix onto screen */
2555 if (sidescroll > n) n = sidescroll; /* bring up to minimum */
2556 if (n > keyboard_pos-diff) n = keyboard_pos-diff; /* too far? */
2557 for (i = 0; i < n; i++)
2558 tp(insert_char);
2559 ix += i;
2560 ictrl_put(keybuf->data + keyboard_pos + prompt_len - (ix-1), i);
2561 bufputnc('\010', (prompt_len + i) - (ix - 1));
2562 cx = ix;
2563 cy = lines;
2564 } else {
2565 physical_refresh();
2566 }
2567 } else if (ix > Wrap) { /* off right edge of screen */
2568 if (expnonvis) {
2569 if (ix - Wrap > Wrap/2) {
2570 physical_refresh();
2571 } else {
2572 /* amount of scroll needed is <= half screen */
2573 int offset = place - ix + iendx;
2574 int i;
2575 bufputc('\r');
2576 if (prompt)
2577 hwrite(prompt, prompt->len - prompt_len, prompt_len, 0);
2578 for (i = 0; i < sidescroll || i < ix - Wrap; i++)
2579 tp(delete_char);
2580 iendx -= i;
2581 ix -= i;
2582 cx = prompt_len + 1;
2583 cy = lines;
2584 xy(iendx, lines);
2585 ioutputs(keybuf->data + offset, keybuf->len - offset);
2586 diff -= i;
2587 offset += i;
2588 xy(ix, lines);
2589 }
2590 } else {
2591 crnl(1); cx = 1; /* old text scrolls up, for continutity */
2592 physical_refresh();
2593 }
2594 } else { /* on screen */
2595 cx += diff;
2596 if (diff < 0)
2597 bufputnc('\010', -diff);
2598 else
2599 ictrl_put(keybuf->data + place - diff, diff);
2600 }
2601
2602 /* visual */
2603 } else {
2604 int new = (ix - 1) + diff;
2605 iy += ndiv(new, Wrap);
2606 ix = nmod(new, Wrap) + 1;
2607
2608 if ((iy > lines) && (iy - lines < isize) && scroll) {
2609 scroll_input(iy - lines);
2610 ioutall(place - (ix - 1) - (iy - lines - 1) * Wrap);
2611 iy = lines;
2612 ipos();
2613 } else if ((iy < in_top) || (iy > lines)) {
2614 logical_refresh();
2615 } else {
2616 ipos();
2617 }
2618 }
2619
2620 bufflush();
2621 return keyboard_pos;
2622 }
2623
do_refresh(void)2624 void do_refresh(void)
2625 {
2626 if (visual && need_more_refresh) update_status_field(NULL, STAT_MORE);
2627 if (need_refresh >= REF_LOGICAL) logical_refresh();
2628 else if (need_refresh >= REF_PHYSICAL) physical_refresh();
2629 }
2630
physical_refresh(void)2631 void physical_refresh(void)
2632 {
2633 if (visual) {
2634 setscroll(1, lines);
2635 ipos();
2636 } else {
2637 int start;
2638 int prompt_len = 0;
2639 clear_input_line();
2640 if (prompt) {
2641 prompt_len = (prompt->len + 1) % Wrap - 1;
2642 hwrite(prompt, prompt->len - prompt_len, prompt_len, 0);
2643 iendx = prompt_len + 1;
2644 }
2645 ix = (sockecho()?keyboard_pos:0) % (Wrap - prompt_len) + 1 + prompt_len;
2646 start = (sockecho()?keyboard_pos:0) - (ix - 1) + prompt_len;
2647 if (start == keybuf->len && keybuf->len > 0) { /* would print nothing */
2648 /* slide window so something is visible */
2649 if (start > Wrap - prompt_len) {
2650 ix += Wrap - prompt_len;
2651 start -= Wrap - prompt_len;
2652 } else {
2653 ix += start;
2654 start = 0;
2655 }
2656 }
2657 ioutall(start);
2658 bufputnc('\010', iendx - ix); cx -= (iendx - ix);
2659 }
2660 bufflush();
2661 if (need_refresh <= REF_PHYSICAL) need_refresh = 0;
2662 old_ix = -1; /* invalid */
2663 }
2664
logical_refresh(void)2665 void logical_refresh(void)
2666 {
2667 int kpos, nix, niy;
2668
2669 if (!visual)
2670 oflush(); /* no sense refreshing if there's going to be output after */
2671
2672 kpos = prompt ? -(prompt->len % Wrap) : 0;
2673 nix = ((sockecho() ? keyboard_pos : 0) - kpos) % Wrap + 1;
2674
2675 if (visual) {
2676 setscroll(1, lines);
2677 niy = istarty + (keyboard_pos - kpos) / Wrap;
2678 if (niy <= lines) {
2679 clear_input_line();
2680 } else {
2681 clear_input_window();
2682 kpos += (niy - lines) * Wrap;
2683 niy = lines;
2684 }
2685 ioutall(kpos);
2686 ix = nix;
2687 iy = niy;
2688 ipos();
2689 } else {
2690 clear_input_line();
2691 if (expnonvis) {
2692 int plen = 0;
2693 if (prompt) {
2694 plen = (prompt->len + 1) % Wrap - 1;
2695 hwrite(prompt, prompt->len - plen, plen, 0);
2696 iendx = plen + 1;
2697 }
2698 ix = (old_ix >= iendx && old_ix <= Wrap) ? old_ix :
2699 (sockecho()?keyboard_pos:0) % (Wrap - plen) + 1 + plen;
2700 old_ix = -1; /* invalid */
2701 kpos = (sockecho()?keyboard_pos:0) - (ix - 1) + plen;
2702 if (kpos == keybuf->len && keybuf->len > 0) {
2703 /* would print nothing; slide window so something is visible */
2704 if (kpos > Wrap/2) {
2705 ix += Wrap/2;
2706 kpos -= Wrap/2;
2707 } else {
2708 ix += kpos;
2709 kpos -= kpos;
2710 }
2711 }
2712 ioutall(kpos);
2713 } else {
2714 ioutall(kpos);
2715 kpos += Wrap;
2716 while ((sockecho() && kpos <= keyboard_pos) || kpos < 0) {
2717 crnl(1); cx = 1;
2718 iendx = 1;
2719 ioutall(kpos);
2720 kpos += Wrap;
2721 }
2722 ix = nix;
2723 }
2724 bufputnc('\010', iendx - ix); cx -= (iendx - ix);
2725 }
2726 bufflush();
2727 if (need_refresh <= REF_LOGICAL) need_refresh = 0;
2728 }
2729
update_prompt(conString * newprompt,int display)2730 void update_prompt(conString *newprompt, int display)
2731 {
2732 conString *oldprompt = prompt;
2733
2734 if (oldprompt == moreprompt) return;
2735 prompt = newprompt;
2736 old_ix = -1;
2737 if ((oldprompt || prompt) && display)
2738 set_refresh_pending(REF_LOGICAL);
2739 }
2740
2741
2742 /*****************************************************
2743 * *
2744 * OUTPUT HANDLING *
2745 * *
2746 *****************************************************/
2747
2748 /*************
2749 * Utilities *
2750 *************/
2751
attributes_off(attr_t attrs)2752 static void attributes_off(attr_t attrs)
2753 {
2754 const char *ctlseq;
2755
2756 if (attrs & F_HILITE) attrs |= hiliteattr;
2757 if (have_attr & attrs & F_SIMPLE) {
2758 if (attr_off) tp(attr_off);
2759 else {
2760 if (have_attr & attrs & F_UNDERLINE) tp(underline_off);
2761 if (have_attr & attrs & F_BOLD ) tp(standout_off);
2762 }
2763 }
2764 if ((attrs & F_COLORS) && (ctlseq = getvar("end_color"))) {
2765 print_to_ascii(outbuf, ctlseq);
2766 }
2767 }
2768
attributes_on(attr_t attrs)2769 static void attributes_on(attr_t attrs)
2770 {
2771 if (attrs & F_HILITE)
2772 attrs |= hiliteattr;
2773
2774 #if 0
2775 if (attr_on) {
2776 /* standout, underline, reverse, blink, dim, bold, blank, prot., ACS */
2777 tp(tparm(attr_on,
2778 (have_attr & attrs & F_BOLD && !bold),
2779 (have_attr & attrs & F_UNDERLINE),
2780 (have_attr & attrs & F_REVERSE),
2781 (have_attr & attrs & F_FLASH),
2782 (have_attr & attrs & F_DIM),
2783 (have_attr & attrs & F_BOLD && bold),
2784 (have_attr & attrs & F_ITALIC),
2785 0, 0, 0));
2786 } else
2787 #endif
2788 {
2789 /* Some emulators only show the last, so we do most important last. */
2790 if (have_attr & attrs & F_DIM) tp(dim);
2791 if (have_attr & attrs & F_BOLD) tp(bold ? bold : standout);
2792 if (have_attr & attrs & F_UNDERLINE) tp(underline);
2793 if (have_attr & attrs & F_ITALIC) tp(italic);
2794 if (have_attr & attrs & F_REVERSE) tp(reverse);
2795 if (have_attr & attrs & F_FLASH) tp(flash);
2796 }
2797
2798 if (attrs & F_FGCOLOR) color_on("", attr2fgcolor(attrs));
2799 if (attrs & F_BGCOLOR) color_on("bg", attr2bgcolor(attrs));
2800 }
2801
color_on(const char * prefix,long color)2802 static void color_on(const char *prefix, long color)
2803 {
2804 const char *ctlseq;
2805 smallstr buf;
2806
2807 sprintf(buf, "start_color_%s%s", prefix, enum_color[color].data);
2808 if ((ctlseq = getvar(buf))) {
2809 print_to_ascii(outbuf, ctlseq);
2810 } else {
2811 sprintf(buf, "start_color_%s%ld", prefix, color);
2812 if ((ctlseq = getvar(buf)))
2813 print_to_ascii(outbuf, ctlseq);
2814 }
2815 }
2816
hwrite(conString * line,int start,int len,int indent)2817 static void hwrite(conString *line, int start, int len, int indent)
2818 {
2819 attr_t attrs = line->attrs & F_HWRITE;
2820 attr_t current = 0;
2821 attr_t new;
2822 int i, ctrl;
2823 int col = 0;
2824 char c;
2825 #if WIDECHAR
2826 size_t ret;
2827 mbstate_t is, os;
2828
2829 memset(&is, 0, sizeof(is));
2830 memset(&os, 0, sizeof(os));
2831 #endif
2832
2833 if (line->attrs & F_BELL && start == 0) {
2834 dobell(1);
2835 }
2836
2837 if (indent) {
2838 bufputnc(' ', indent);
2839 cx += indent;
2840 }
2841
2842 cx += len;
2843
2844 if (!line->charattrs && hilite && attrs)
2845 attributes_on(current = attrs);
2846
2847 for (i = start; i < start + len; ++i) {
2848 new = line->charattrs ? adj_attr(attrs, line->charattrs[i]) : attrs;
2849 c = unmapchar(localize(line->data[i]));
2850 ctrl = (emulation > EMUL_RAW && is_cntrl(c) && c != '\t');
2851 if (ctrl)
2852 new |= F_BOLD | F_REVERSE;
2853 if (new != current) {
2854 if (current) attributes_off(current);
2855 current = new;
2856 if (current) attributes_on(current);
2857 }
2858 if (c == '\t') {
2859 bufputnc(' ', tabsize - col % tabsize);
2860 col += tabsize - col % tabsize;
2861 } else {
2862 #if WIDECHAR
2863 ret = mbrtowc(NULL, (char *)line->data+i, len - i, &is);
2864 if (ret >= (size_t) -2) {
2865 /* Invalid character. Punt. */
2866 bufputc(ctrl ? CTRL(c) : c);
2867 } else {
2868 int j = 1;
2869 bufputc(c);
2870 while (j++ < ret) {
2871 c = line->data[++i];
2872 bufputc(c);
2873 }
2874 }
2875 #else
2876 bufputc(ctrl ? CTRL(c) : c);
2877 #endif
2878 col++;
2879 }
2880 }
2881 if (current) attributes_off(current);
2882 }
2883
reset_outcount(Screen * screen)2884 void reset_outcount(Screen *screen)
2885 {
2886 if (!screen) screen = display_screen;
2887 screen->outcount = visual ?
2888 (scroll ? (out_bot - out_top + 1) : screen->outcount) :
2889 lines - 1;
2890 }
2891
2892 /* return TRUE if okay to print */
check_more(Screen * screen)2893 static int check_more(Screen *screen)
2894 {
2895 if (!screen->paused && more && tfinteractive && screen->outcount-- <= 0)
2896 {
2897 /* status bar is updated in oflush() to avoid scroll region problems */
2898 screen->paused = 1;
2899 do_hook(H_MORE, NULL, "");
2900 }
2901 return !screen->paused;
2902 }
2903
pause_screen(void)2904 int pause_screen(void)
2905 {
2906 if (display_screen->paused)
2907 return 0;
2908 display_screen->outcount = 0;
2909 display_screen->paused = 1;
2910 do_hook(H_MORE, NULL, "");
2911 update_status_field(NULL, STAT_MORE);
2912 return 1;
2913 }
2914
clear_more(int new)2915 int clear_more(int new)
2916 {
2917 PhysLine *pl;
2918 int use_insert, need_redraw = 0, scrolled = 0;
2919
2920 if (new < 0) {
2921 if (!visual /* XXX || !can_scrollback */) return 0;
2922 use_insert = insert_line && -new < winlines();
2923 setscroll(1, out_bot);
2924 while (scrolled > new && prevtop(display_screen)) {
2925 pl = display_screen->top->datum;
2926 if (display_screen->viewsize <= winlines()) {
2927 /* visible area is not full: add to the top of visible area */
2928 xy(1, winlines() - display_screen->viewsize + 1);
2929 hwrite(pl->str, pl->start,
2930 pl->len < Wrap - pl->indent ? pl->len : Wrap - pl->indent,
2931 pl->indent);
2932 } else {
2933 /* visible area is full: insert at top and push bottom off */
2934 display_screen->paused = 1;
2935 display_screen->outcount = 0;
2936 if (use_insert) {
2937 xy(1, 1);
2938 tp(insert_line);
2939 hwrite(pl->str, pl->start,
2940 pl->len < Wrap-pl->indent ? pl->len : Wrap-pl->indent,
2941 pl->indent);
2942 } else {
2943 need_redraw++;
2944 }
2945 prevbot(display_screen);
2946 }
2947 scrolled--;
2948 }
2949 while (scrolled > new && display_screen->viewsize > 1) {
2950 /* no more lines in list to scroll on to top. insert blanks. */
2951 display_screen->paused = 1;
2952 display_screen->outcount = 0;
2953 if (use_insert) {
2954 xy(1, 1);
2955 tp(insert_line);
2956 } else {
2957 need_redraw++;
2958 }
2959 prevbot(display_screen);
2960 scrolled--;
2961 }
2962 if (need_redraw) {
2963 redraw();
2964 return scrolled;
2965 }
2966 update_status_field(NULL, STAT_MORE);
2967 } else { /* new >= 0 */
2968 if (!display_screen->paused) return 0;
2969 if (display_screen->nback_filtered) {
2970 if (visual) {
2971 setscroll(1, out_bot);
2972 if (cy != out_bot)
2973 xy(columns, out_bot);
2974 } else {
2975 if (!need_refresh)
2976 old_ix = ix; /* physical_refresh() will restore ix */
2977 clear_input_line();
2978 }
2979 while (scrolled < new && nextbot(display_screen)) {
2980 pl = display_screen->bot->datum;
2981 if (visual) output_scroll(pl); else output_novisual(pl);
2982 scrolled++;
2983 }
2984 }
2985 while (display_screen->viewsize > winlines())
2986 nexttop(display_screen);
2987 if (scrolled < new) {
2988 display_screen->paused = 0;
2989 display_screen->outcount = new - scrolled;
2990 if (visual) {
2991 update_status_field(NULL, STAT_MORE);
2992 if (!scroll) display_screen->outcount = out_bot;
2993 } else {
2994 prompt = fgprompt();
2995 old_ix = -1;
2996 clear_input_line();
2997 }
2998 }
2999 }
3000 set_refresh_pending(REF_PHYSICAL);
3001 return scrolled;
3002 }
3003
tog_more(Var * var)3004 int tog_more(Var *var)
3005 {
3006 if (!more) clear_more(display_screen->outcount);
3007 else reset_outcount(display_screen);
3008 return 1;
3009 }
3010
tog_keypad(Var * var)3011 int tog_keypad(Var *var)
3012 {
3013 if (!keypad_on) {
3014 if (keypad)
3015 eprintf("don't know how to enable keypad on %s terminal", TERM);
3016 return 0;
3017 }
3018 tp(keypad ? keypad_on : keypad_off);
3019 return 1;
3020 }
3021
screen_end(int need_redraw)3022 int screen_end(int need_redraw)
3023 {
3024 Screen *screen = display_screen;
3025 int oldmore = more;
3026
3027 hide_screen(screen);
3028 if (screen->nback_filtered) {
3029 screen->nback_filtered = screen->nback = 0;
3030 screen->nnew_filtered = screen->nnew = 0;
3031 special_var[VAR_more].val.u.ival = 0;
3032
3033 /* XXX optimize if (jump < screenful) (but what about tmp lines?) */
3034 need_redraw = 1;
3035 screen->maxbot = screen->bot = screen->pline.tail;
3036 screen_refilter(screen);
3037
3038 special_var[VAR_more].val.u.ival = oldmore;
3039 }
3040
3041 screen->paused = 0;
3042 reset_outcount(screen);
3043 if (need_redraw) {
3044 redraw_window(screen, 0);
3045 if (visual) {
3046 update_status_field(NULL, STAT_MORE);
3047 } else {
3048 prompt = fgprompt();
3049 }
3050 }
3051 return 1;
3052 }
3053
selflush(void)3054 int selflush(void)
3055 {
3056 display_screen->selflush = 1;
3057 screen_refilter_bottom(display_screen);
3058 clear_more(winlines());
3059 return 1;
3060 }
3061
3062
3063 /* next_physline
3064 * Increment bottom of screen. Checks for More and SELFLUSH termination.
3065 * Returns 1 if there is a displayable line, 0 if not.
3066 */
next_physline(Screen * screen)3067 static int next_physline(Screen *screen)
3068 {
3069 if (screen->paused)
3070 return 0;
3071 if (!nextbot(screen)) {
3072 if (screen->selflush) {
3073 if (screen->selflush == 1) {
3074 screen->selflush++;
3075 screen->paused = 1;
3076 update_status_field(NULL, STAT_MORE);
3077 } else {
3078 screen->selflush = 0;
3079 clear_screen_filter(screen);
3080 screen_end(1);
3081 }
3082 }
3083 return 0;
3084 }
3085 if (!check_more(screen)) {
3086 /* undo the nextbot() */
3087 if (screen->maxbot == screen->bot) {
3088 screen->maxbot = screen->maxbot->prev;
3089 screen->nnew++;
3090 screen->nnew_filtered++;
3091 }
3092 screen->bot = screen->bot->prev;
3093 screen->nback_filtered++;
3094 screen->nback++;
3095 screen->viewsize--;
3096 return 0;
3097 }
3098 if (display_screen->viewsize > winlines())
3099 nexttop(screen);
3100 return 1;
3101 }
3102
wraplen(const char * str,int len,int indent)3103 int wraplen(const char *str, int len, int indent)
3104 {
3105 int total, max, visible;
3106 #if WIDECHAR
3107 UText *ut = NULL;
3108 UBreakIterator *lineBI = NULL;
3109 UBreakIterator *charBI = NULL;
3110 UErrorCode icuerr = U_ZERO_ERROR;
3111 UChar32 c;
3112 UEastAsianWidth ea;
3113 int nativeIndex = 0;
3114 #endif
3115
3116 if (emulation == EMUL_RAW) return len;
3117
3118 max = Wrap - indent;
3119
3120 #if WIDECHAR
3121 ut = utext_openUTF8(ut, str, len, &icuerr);
3122 if (!U_SUCCESS(icuerr))
3123 return len;
3124
3125 c = UTEXT_NEXT32(ut);
3126 for (visible = 0; visible < max && c != U_SENTINEL; c = UTEXT_NEXT32(ut)) {
3127 if (c == '\t')
3128 visible += tabsize - visible % tabsize;
3129 else {
3130 ea = (UEastAsianWidth)u_getIntPropertyValue(c,
3131 UCHAR_EAST_ASIAN_WIDTH);
3132 switch (ea) {
3133 case U_EA_NEUTRAL:
3134 case U_EA_AMBIGUOUS:
3135 case U_EA_HALFWIDTH:
3136 ++visible;
3137 break;
3138 case U_EA_FULLWIDTH:
3139 visible += 2;
3140 break;
3141 case U_EA_NARROW:
3142 ++visible;
3143 break;
3144 case U_EA_WIDE:
3145 visible += 2;
3146 break;
3147 default:
3148 ++visible;
3149 }
3150 }
3151 }
3152
3153 if (c == U_SENTINEL) {
3154 utext_close(ut);
3155 return len;
3156 }
3157
3158 /* If we had a full width character as the last UChar32, go
3159 * back one to fit within our max length.
3160 */
3161 if (visible >= max)
3162 UTEXT_PREVIOUS32(ut);
3163
3164 lineBI = ubrk_open(UBRK_LINE, lang, NULL, 0, &icuerr);
3165 if (!U_SUCCESS(icuerr)) {
3166 utext_close(ut);
3167 return len;
3168 }
3169
3170 ubrk_setUText(lineBI, ut, &icuerr);
3171 if (!U_SUCCESS(icuerr)) {
3172 nativeIndex = utext_getNativeIndex(ut);
3173 ubrk_close(lineBI);
3174 utext_close(ut);
3175 return nativeIndex;
3176 }
3177
3178 total = ubrk_preceding(lineBI, utext_getNativeIndex(ut));
3179
3180 /* If we can't break at an acceptable "line break" point.
3181 * Break at the previous glyph.
3182 */
3183 if (total == 0) {
3184 charBI = ubrk_open(UBRK_CHARACTER, lang, NULL, 0, &icuerr);
3185 if (!U_SUCCESS(icuerr)) {
3186 nativeIndex = utext_getNativeIndex(ut);
3187 ubrk_close(lineBI);
3188 utext_close(ut);
3189 return nativeIndex;
3190 }
3191
3192 ubrk_setUText(charBI, ut, &icuerr);
3193 if (!U_SUCCESS(icuerr)) {
3194 nativeIndex = utext_getNativeIndex(ut);
3195 ubrk_close(charBI);
3196 ubrk_close(lineBI);
3197 utext_close(ut);
3198 return nativeIndex;
3199 }
3200
3201 total = ubrk_preceding(charBI, utext_getNativeIndex(ut));
3202
3203 /* Return the position we're at if there's no good break. */
3204 if (total == 0)
3205 total = utext_getNativeIndex(ut);
3206 ubrk_close(charBI);
3207 }
3208
3209 ubrk_close(lineBI);
3210 utext_close(ut);
3211 return total;
3212 #else
3213 for (visible = total = 0; total < len && visible < max; total++) {
3214 if (str[total] == '\t')
3215 visible += tabsize - visible % tabsize;
3216 else
3217 visible++;
3218 }
3219
3220 if (total == len) return len;
3221 len = total;
3222 if (wrapflag) {
3223 while (len && !is_space(str[len-1]))
3224
3225 --len;
3226 if (wrappunct > 0 && len < total - wrappunct) {
3227 len = total;
3228 while (len && !is_space(str[len-1]) && !is_punct(str[len-1]))
3229 --len;
3230 }
3231 }
3232 return len ? len : total;
3233 #endif /* WIDECHAR */
3234 }
3235
3236 /****************
3237 * Main drivers *
3238 ****************/
3239
moresize(Screen * screen)3240 int moresize(Screen *screen) {
3241 if (!screen) screen = display_screen;
3242 return screen->nback_filtered;
3243 }
3244
3245 /* write to display_screen (no history) */
screenout(conString * line)3246 void screenout(conString *line)
3247 {
3248 enscreen(display_screen, line);
3249 oflush();
3250 }
3251
enscreen(Screen * screen,conString * line)3252 void enscreen(Screen *screen, conString *line)
3253 {
3254 int wrapped, visible;
3255
3256 if (!hilite)
3257 line->attrs &= ~F_HWRITE;
3258 if (line->attrs & F_GAG && gag)
3259 return;
3260
3261 if (!screen->pline.head) { /* initialize wrap state */
3262 screen->scr_wrapflag = wrapflag;
3263 screen->scr_wrapsize = Wrap;
3264 screen->scr_wrapspace = wrapspace;
3265 screen->scr_wrappunct = wrappunct;
3266 }
3267 visible = screen_filter(screen, line);
3268 wrapped = wraplines(line, &screen->pline, visible);
3269 screen->nlline++;
3270 screen->npline += wrapped;
3271 screen->nback += wrapped;
3272 screen->nnew += wrapped;
3273 if (visible) {
3274 screen->nback_filtered += wrapped;
3275 screen->nnew_filtered += wrapped;
3276 }
3277 purge_old_lines(screen);
3278 }
3279
oflush(void)3280 void oflush(void)
3281 {
3282 static int lastsize;
3283 int waspaused, count = 0;
3284 PhysLine *pl;
3285 Screen *screen = display_screen;
3286
3287 if (output_disabled) return;
3288
3289 if (!(waspaused = screen->paused)) {
3290 lastsize = 0;
3291 while (next_physline(screen)) {
3292 pl = screen->bot->datum;
3293 if (count++ == 0) { /* first iteration? */
3294 if (screen_mode < 1) {
3295 if (!need_refresh)
3296 old_ix = ix; /* physical_refresh() will restore ix */
3297 clear_input_line();
3298 } else if (scroll && has_scroll_region) {
3299 setscroll(1, out_bot);
3300 if (cy != out_bot)
3301 xy(columns, out_bot);
3302 }
3303 }
3304 if (screen_mode < 1) output_novisual(pl);
3305 #ifdef SCREEN
3306 else if (scroll) output_scroll(pl);
3307 else output_noscroll(pl);
3308 #endif
3309 set_refresh_pending(REF_PHYSICAL);
3310 bufflush();
3311 }
3312 }
3313
3314 if (screen->paused) {
3315 if (!visual) {
3316 if (!waspaused) {
3317 prompt = moreprompt;
3318 old_ix = -1;
3319 set_refresh_pending(REF_LOGICAL);
3320 }
3321 } else if (!waspaused ||
3322 moresize(screen) / morewait > lastsize / morewait)
3323 {
3324 update_status_field(NULL, STAT_MORE);
3325 } else if (lastsize != moresize(screen)) {
3326 need_more_refresh = 1;
3327 }
3328 lastsize = moresize(screen);
3329 }
3330 }
3331
output_novisual(PhysLine * pl)3332 static void output_novisual(PhysLine *pl)
3333 {
3334 hwrite(pl->str, pl->start, pl->len, pl->indent);
3335 crnl(1); cx = 1; cy++;
3336 }
3337
3338 #ifdef SCREEN
output_noscroll(PhysLine * pl)3339 static void output_noscroll(PhysLine *pl)
3340 {
3341 setscroll(1, lines); /* needed after scroll_input(), etc. */
3342 xy(1, (oy + 1) % (out_bot) + 1);
3343 clear_line();
3344 xy(ox, oy);
3345 hwrite(pl->str, pl->start, pl->len, pl->indent);
3346 oy = oy % (out_bot) + 1;
3347 }
3348
output_scroll(PhysLine * pl)3349 static void output_scroll(PhysLine *pl)
3350 {
3351 if (has_scroll_region) {
3352 crnl(1);
3353 /* Some brain damaged emulators lose attributes under cursor
3354 * when that '\n' is printed. Too bad. */
3355 } else {
3356 xy(1, 1);
3357 tp(delete_line);
3358 xy(1, out_bot);
3359 tp(insert_line);
3360 }
3361 hwrite(pl->str, pl->start, pl->len, pl->indent);
3362 }
3363 #endif
3364
hide_screen(Screen * screen)3365 void hide_screen(Screen *screen)
3366 {
3367 ListEntry *node, *next;
3368 PhysLine *pl;
3369
3370 if (!screen) screen = fg_screen;
3371 if (screen->viewsize > 0) {
3372 /* delete any temp lines in [top,bot] */
3373 int done;
3374 for (done = 0, node = screen->top; !done; node = next) {
3375 done = (node == screen->bot);
3376 next = node->next;
3377 pl = node->datum;
3378 if (pl->tmp) {
3379 if (screen->top == node)
3380 screen->top = node->next;
3381 if (screen->bot == node)
3382 screen->bot = node->prev;
3383 if (screen->maxbot == node)
3384 screen->maxbot = node->prev;
3385 unlist(node, &screen->pline);
3386 conStringfree(pl->str);
3387 pfree(pl, plpool, str);
3388 screen->viewsize--;
3389 screen->npline--;
3390 screen->nlline--;
3391 }
3392 }
3393 }
3394 }
3395
unhide_screen(Screen * screen)3396 void unhide_screen(Screen *screen)
3397 {
3398 PhysLine *pl;
3399
3400 if (!virtscreen || !textdiv || !visual) {
3401 return;
3402
3403 } else if (textdiv == TEXTDIV_CLEAR) {
3404 clear_screen_view(screen);
3405
3406 } else if (textdiv_str && fg_screen->maxbot &&
3407 ((PhysLine*)(fg_screen->maxbot->datum))->str != textdiv_str &&
3408 (textdiv == TEXTDIV_ALWAYS ||
3409 fg_screen->maxbot != fg_screen->bot || fg_screen->maxbot->next))
3410 /* If textdiv is enabled and there's no divider at maxbot already... */
3411 {
3412 /* insert divider at maxbot */
3413 palloc(pl, PhysLine, plpool, str, __FILE__, __LINE__);
3414 pl->visible = 1;
3415 pl->tmp = 1;
3416 (pl->str = textdiv_str)->links++;
3417 pl->start = 0;
3418 pl->indent = 0;
3419 pl->len = wraplen(textdiv_str->data, textdiv_str->len, 0);
3420 inlist(pl, &fg_screen->pline, fg_screen->maxbot);
3421 if (fg_screen->bot == fg_screen->maxbot) {
3422 /* insert ABOVE bot, so it doesn't look like new activity */
3423 fg_screen->bot = fg_screen->maxbot->next;
3424 if (fg_screen->viewsize == 0)
3425 fg_screen->top = fg_screen->bot;
3426 fg_screen->viewsize++;
3427 if (fg_screen->viewsize >= winlines())
3428 fg_screen->partialview = 0;
3429 } else {
3430 /* inserting BELOW bot; we must increment nback* */
3431 fg_screen->nback++;
3432 fg_screen->nback_filtered++;
3433 }
3434 fg_screen->maxbot = fg_screen->maxbot->next;
3435 fg_screen->npline++;
3436 fg_screen->nlline++;
3437 }
3438 }
3439
3440 /*
3441 * Switch to new fg_screen.
3442 */
switch_screen(int quiet)3443 void switch_screen(int quiet)
3444 {
3445 if (fg_screen != display_screen) { /* !virtscreen */
3446 /* move lines from fg_screen to display_screen */
3447 /* XXX optimize when no filter */
3448 PhysLine *pl;
3449 List *dest = &display_screen->pline;
3450 List *src = &fg_screen->pline;
3451 while (src->head) {
3452 dest->tail->next = src->head;
3453 dest->tail = dest->tail->next;
3454 src->head = src->head->next;
3455 display_screen->nnew++;
3456 display_screen->nback++;
3457 pl = dest->tail->datum;
3458 if (screen_filter(display_screen, pl->str)) {
3459 display_screen->nnew_filtered++;
3460 display_screen->nback_filtered++;
3461 }
3462 }
3463 src->head = src->tail = NULL;
3464 fg_screen->nback_filtered = fg_screen->nback = 0;
3465 fg_screen->nnew_filtered = fg_screen->nnew = 0;
3466 fg_screen->npline = 0;
3467 fg_screen->nlline = 0;
3468 fg_screen->maxbot = fg_screen->bot = fg_screen->top = NULL;
3469 }
3470 update_status_field(NULL, STAT_WORLD);
3471 if (quiet) {
3472 /* jump to end */
3473 screen_end(1);
3474 } else {
3475 if (virtscreen) {
3476 redraw_window(display_screen, 0);
3477 }
3478 /* display new lines */
3479 oflush();
3480 update_status_field(NULL, STAT_MORE);
3481 }
3482 }
3483
3484 #if USE_DMALLOC
free_output(void)3485 void free_output(void)
3486 {
3487 int row;
3488
3489 tfclose(tfscreen);
3490 tfclose(tfalert);
3491 free_screen(default_screen);
3492 fg_screen = default_screen = NULL;
3493 Stringfree(outbuf);
3494 for (row = 0; row < max_status_height; row++) {
3495 Stringfree(status_line[row]);
3496 while (statusfield_list[row]->head)
3497 free_statusfield(unlist(statusfield_list[row]->head,
3498 statusfield_list[row]));
3499 }
3500
3501 pfreepool(PhysLine, plpool, str);
3502 }
3503 #endif
3504
3505