1 /* $EPIC: screen.c,v 1.75 2013/02/27 23:38:57 jnelson Exp $ */
2 /*
3 * screen.c
4 *
5 * Copyright (c) 1993-1996 Matthew Green.
6 * Copyright � 1998 J. Kean Johnston, used with permission
7 * Copyright � 1997, 2003 EPIC Software Labs.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notices, the above paragraph (the one permitting redistribution),
17 * this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. The names of the author(s) may not be used to endorse or promote
20 * products derived from this software without specific prior written
21 * permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #define __need_putchar_x__
37 #include "irc.h"
38 #include "alias.h"
39 #include "clock.h"
40 #include "exec.h"
41 #include "screen.h"
42 #include "window.h"
43 #include "output.h"
44 #include "vars.h"
45 #include "server.h"
46 #include "list.h"
47 #include "termx.h"
48 #include "names.h"
49 #include "ircaux.h"
50 #include "input.h"
51 #include "log.h"
52 #include "hook.h"
53 #include "dcc.h"
54 #include "status.h"
55 #include "commands.h"
56 #include "parse.h"
57 #include "newio.h"
58
59 #define CURRENT_WSERV_VERSION 4
60
61 /*
62 * When some code wants to override the default lastlog level, and needs
63 * to have some output go into some explicit window (such as for /xecho -w),
64 * then while to_window is set to some window, *ALL* output goes to that
65 * window. Dont forget to reset it to NULL when youre done! ;-)
66 */
67 Window *to_window;
68
69 /*
70 * When all else fails, this is the screen that is attached to the controlling
71 * terminal, and we know *that* screen will always be there.
72 */
73 Screen *main_screen;
74
75 /*
76 * This is the screen in which we last handled an input event. This takes
77 * care of the input duties that "current_screen" used to handle.
78 */
79 Screen *last_input_screen;
80
81 /*
82 * This is used to set the default output device for tputs_x(). This takes
83 * care of the output duties that "current_screen" used to handle. Since
84 * the input screen and output screen are independant, it isnt impossible
85 * for a command in one screen to cause output in another.
86 */
87 Screen *output_screen;
88
89 /*
90 * The list of all the screens we're handling. Under most cases, there's
91 * only one screen on the list, "main_screen".
92 */
93 Screen *screen_list = NULL;
94
95 /*
96 * Ugh. Dont ask.
97 */
98 int normalize_never_xlate = 0;
99 int normalize_permit_all_attributes = 0;
100
101 /*
102 * This file includes major work contributed by FireClown, and I am indebted
103 * to him for the work he has graciously donated to the project. The major
104 * highlights of his work include:
105 *
106 * -- ^C codes have been changed to mIRC-order. This is the order that
107 * BitchX uses as well, so those scripts that use ^CXX should work without
108 * changes now between epic and bitchx.
109 * -- The old "ansi-order" ^C codes have been preserved, but in a different
110 * way. If you do ^C30 through ^C37, you will set the foreground color
111 * (directly corresponding to the ansi codes for 30-37), and if you do
112 * ^C40 through ^C47, you will set the background. ^C50 through ^C57
113 * are reserved for bold-foreground, and blink-background.
114 * -- $cparse() still outputs the "right" colors, so if you use $cparse(),
115 * then these changes wont affect you (much).
116 * -- Colors and ansi codes are either graciously handled, or completely
117 * filtered out. Anything that cannot be handled is removed, so there
118 * is no risk of dangerous codes making their way to your output. This
119 * is accomplished by a low-grade ansi emulator that folds raw output
120 * into an intermediate form which is used by the display routines.
121 *
122 * To a certain extent, the original code from FireClown was not yet complete,
123 * and it was evident that the code was in anticipation of some additional
124 * future work. We have completed much of that work, and we are very much
125 * indebted to him for getting the ball rolling and supplying us with ideas. =)
126 */
127
128
129 /* * * * * * * * * * * * * OUTPUT CHAIN * * * * * * * * * * * * * * * * * * *
130 * To put a message to the "default" window, you must first call
131 * set_display_target(nick/channel, lastlog_level)
132 * Then you may call
133 * say(), output(), yell(), put_it(), put_echo(), etc.
134 * When you are done, make sure to
135 * reset_display_target()
136 *
137 * To put a message to a specific, known window (you need it's refnum)
138 * then you may just call directly:
139 * display_to(winref, ...)
140 *
141 * To put a series of messages to a specific, known window, (need it's refnum)
142 * You must first call:
143 * message_to(winref)
144 * Then you may call
145 * say(), ouitput(), yell(), put_it(), put_echo(), etc.
146 * When you are done, make sure to
147 * message_to(-1);
148 *
149 * The 'display' (or 'display_to') functions are the main entry point for
150 * all logical output from epic. These functions then figure out what
151 * window the output will go to and invoke its 'add' function. From there,
152 * whatever happens is implementation defined.
153 *
154 * This file implements the middle part of the "ircII window", everything
155 * from the 'add' function to the low level terminal stuff.
156 *
157 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
158 static int rite (Window *window, const unsigned char *str);
159 static void scroll_window (Window *window);
160 static void add_to_window (Window *window, const unsigned char *str);
161 static void window_disp (Window *window, const unsigned char *str, const unsigned char *orig_str);
162 static int ok_to_output (Window *window);
163
164 /*
165 * XXX -- Full disclosure -- FireClown says it is completely the wrong
166 * idea to do this (re-build attributes from scratch every time) because
167 * it causes those using slow terminals or slow connections more pain than
168 * is absolutely neccesary. While I admit that he has a lot more experience
169 * than I do in all this, I'm not sure I have the ability to do all this
170 * "optimally" while ensuring 100% accuracy. Maybe I'll luck out and he
171 * will provide something that will be optimal *and* 100% accurate. ;-)
172 */
173
174 /*
175 * "Attributes" were an invention for epic5, and the general idea was
176 * to expunge from the output chain all of those nasty logical toggle settings
177 * which never really did work correctly. Rather than have half a dozen
178 * functions all keep state about whether reverse/bold/underline/whatever is
179 * on, or off, or what to do when it sees a toggle, we instead have one
180 * function (normalize_string) which walks the string *once* and outputs a
181 * completely normalized output string. The end result of this change is
182 * that what were formerly "toggle" attributes now are "always-on" attributes,
183 * and to turn off an attribute, you need to do an ALL_OFF (^O) and then
184 * turn on whatever attributes are left. This is *significantly* easier
185 * to parse, and yeilds much better results, at the expense of a few extra
186 * bytes.
187 *
188 * Now on to the nitty gritty. Every character has a fudamental set of
189 * attributes that apply to it. Each character has, by default, the same
190 * set of fundamental attributes as the character before it. In any case
191 * where this is NOT true, an "attribute marker" is put into the normalized
192 * output to indicate what the new fundamental attributes are. These new
193 * attributes continue to be used until another attribute marker is found.
194 *
195 * The "Attribute" structure is an internal structure that represents all
196 * of the supported fundamental attributes. This is the prefered method
197 * for keeping state of the attributes of a line. You can convert this
198 * structure into an "attribute marker" by passing the string and an
199 * Attribute struct to 'display_attributes'. The result is 5 bytes of
200 * output, each byte has the high bit set (so str*() still work). You can
201 * also convert an Attribute struct to standard ircII attribute characters
202 * by calling 'logical_attributes'. The result will be an ALL_OFF (^O)
203 * followed by all of the attributes that are ON in the struct. Finally,
204 * you can suppress all attribute changes by calling ignore_attribute().
205 * These functions are used by normalize_string() to for their appropriate
206 * uses.
207 *
208 * You can read an attribute marker from a string and convert it back to
209 * an Attribute struct by calling the read_attributes() function. You can
210 * actually perform the physical output operations neccesary to switch to
211 * the values in an Attribute struct by calling term_attribute(). These
212 * are used by various output routines for whatever reason.
213 */
214 struct attributes {
215 int reverse;
216 int bold;
217 int blink;
218 int underline;
219 int altchar;
220 int color_fg;
221 int color_bg;
222 int fg_color;
223 int bg_color;
224 };
225 typedef struct attributes Attribute;
226
all_off(void)227 const unsigned char *all_off (void)
228 {
229 #ifdef NO_CHEATING
230 Attribute a;
231 static unsigned char retval[6];
232
233 a->reverse = a->bold = a->blink = a->underline = a->altchar = 0;
234 a->color_fg = a->fg_color = a->color_bg = a->bg_color = 0;
235 display_attributes(retval, &a);
236 return retval;
237 #else
238 static char retval[6];
239 retval[0] = '\006';
240 retval[1] = retval[2] = retval[3] = retval[4] = 0x80;
241 retval[5] = 0;
242 return retval;
243 #endif
244 }
245
246 /* Put into 'output', an attribute marker corresponding to 'a' */
display_attributes(u_char * output,Attribute * a)247 static size_t display_attributes (u_char *output, Attribute *a)
248 {
249 u_char val1 = 0x80;
250 u_char val2 = 0x80;
251 u_char val3 = 0x80;
252 u_char val4 = 0x80;
253
254 if (a->reverse) val1 |= 0x01;
255 if (a->bold) val1 |= 0x02;
256 if (a->blink) val1 |= 0x04;
257 if (a->underline) val1 |= 0x08;
258 if (a->altchar) val1 |= 0x10;
259
260 if (a->color_fg) { val2 |= 0x01; val3 |= a->fg_color; }
261 if (a->color_bg) { val2 |= 0x02; val4 |= a->bg_color; }
262
263 output[0] = '\006';
264 output[1] = val1;
265 output[2] = val2;
266 output[3] = val3;
267 output[4] = val4;
268 output[5] = 0;
269 return 5;
270 }
271
272 /* Put into 'output', logical characters so end result is 'a' */
logic_attributes(u_char * output,Attribute * a)273 static size_t logic_attributes (u_char *output, Attribute *a)
274 {
275 char *str = output;
276 size_t count = 0;
277
278 *str++ = ALL_OFF, count++;
279 /* Colors need to be set first, always */
280 if (a->color_fg)
281 {
282 *str++ = '\003', count++;
283 *str++ = '3', count++;
284 *str++ = '0' + a->fg_color, count++;
285 }
286 if (a->color_bg)
287 {
288 if (!a->color_fg)
289 *str++ = '\003', count++;
290 *str++ = ',', count++;
291 *str++ = '4', count++;
292 *str++ = '0' + a->bg_color, count++;
293 }
294 if (a->bold)
295 *str++ = BOLD_TOG, count++;
296 if (a->blink)
297 *str++ = BLINK_TOG, count++;
298 if (a->reverse)
299 *str++ = REV_TOG, count++;
300 if (a->underline)
301 *str++ = UND_TOG, count++;
302 if (a->altchar)
303 *str++ = ALT_TOG, count++;
304 return count;
305 }
306
307 /* Suppress any attribute changes in the output */
ignore_attributes(u_char * output,Attribute * a)308 static size_t ignore_attributes (u_char *output, Attribute *a)
309 {
310 return 0;
311 }
312
313 /* Read an attribute marker from 'input', put results in 'a'. */
read_attributes(const u_char * input,Attribute * a)314 static int read_attributes (const u_char *input, Attribute *a)
315 {
316 if (!input)
317 return -1;
318 if (*input != '\006')
319 return -1;
320 if (!input[0] || !input[1] || !input[2] || !input[3] || !input[4])
321 return -1;
322
323 a->reverse = a->bold = a->blink = a->underline = a->altchar = 0;
324 a->color_fg = a->fg_color = a->color_bg = a->bg_color = 0;
325
326 input++;
327 if (*input & 0x01) a->reverse = 1;
328 if (*input & 0x02) a->bold = 1;
329 if (*input & 0x04) a->blink = 1;
330 if (*input & 0x08) a->underline = 1;
331 if (*input & 0x10) a->altchar = 1;
332
333 input++;
334 if (*input & 0x01) {
335 a->color_fg = 1;
336 a->fg_color = input[1] & 0x7F;
337 }
338 if (*input & 0x02) {
339 a->color_bg = 1;
340 a->bg_color = input[2] & 0x7F;
341 }
342
343 return 0;
344 }
345
346 /* Invoke all of the neccesary functions so output attributes reflect 'a'. */
term_attribute(Attribute * a)347 static void term_attribute (Attribute *a)
348 {
349 term_all_off();
350 if (a->reverse) term_standout_on();
351 if (a->bold) term_bold_on();
352 if (a->blink) term_blink_on();
353 if (a->underline) term_underline_on();
354 if (a->altchar) term_altcharset_on();
355
356 if (a->color_fg) { if (a->fg_color > 7) abort();
357 else term_set_foreground(a->fg_color); }
358 if (a->color_bg) { if (a->bg_color > 7) abort();
359 else term_set_background(a->bg_color); }
360 }
361
362 /* * * * * * * * * * * * * COLOR SUPPORT * * * * * * * * * * * * * * * * */
363 /*
364 * This parses out a ^C control sequence. Note that it is not acceptable
365 * to simply slurp up all digits after a ^C sequence (either by calling
366 * strtol(), or while (isdigit())), because people put ^C sequences right
367 * before legit output with numbers (like the time in your status bar.)
368 * Se we have to actually slurp up only those digits that comprise a legal
369 * ^C code.
370 */
read_color_seq(const u_char * start,void * d,int blinkbold)371 static const u_char *read_color_seq (const u_char *start, void *d, int blinkbold)
372 {
373 /*
374 * The proper "attribute" color mapping is for each ^C lvalue.
375 * If the value is -1, then that is an illegal ^C lvalue.
376 */
377 static int fore_conv[] = {
378 7, 0, 4, 2, 1, 1, 5, 3, /* 0-7 */
379 3, 2, 6, 6, 4, 5, 0, 7, /* 8-15 */
380 7, -1, -1, -1, -1, -1, -1, -1, /* 16-23 */
381 -1, -1, -1, -1, -1, -1, 0, 1, /* 24-31 */
382 2, 3, 4, 5, 6, 7, -1, -1, /* 32-39 */
383 -1, -1, -1, -1, -1, -1, -1, -1, /* 40-47 */
384 -1, -1, 0, 1, 2, 3, 4, 5, /* 48-55 */
385 6, 7, -1, -1, -1 /* 56-60 */
386 };
387 /*
388 * The proper "attribute" color mapping is for each ^C rvalue.
389 * If the value is -1, then that is an illegal ^C rvalue.
390 */
391 static int back_conv[] = {
392 7, 0, 4, 2, 1, 1, 5, 3,
393 3, 2, 6, 6, 4, 5, 0, 7,
394 7, -1, -1, -1, -1, -1, -1, -1,
395 -1, -1, -1, -1, -1, -1, -1, -1,
396 -1, -1, -1, -1, -1, -1, -1, -1,
397 0, 1, 2, 3, 4, 5, 6, 7,
398 -1, -1, 0, 1, 2, 3, 4, 5,
399 6, 7, -1, -1, -1
400 };
401
402 /*
403 * Some lval codes represent "bold" colors. That actually reduces
404 * to ^C<non bold> + ^B, so that if you do ^B later, you get the
405 * <non bold> color. This table indicates whether a ^C code
406 * turns bold ON or OFF. (Every color does one or the other)
407 */
408 static int fore_bold_conv[] = {
409 1, 0, 0, 0, 1, 0, 0, 0,
410 1, 1, 0, 1, 1, 1, 1, 0,
411 1, 0, 0, 0, 0, 0, 0, 0,
412 0, 0, 0, 0, 0, 0, 0, 0,
413 0, 0, 0, 0, 0, 0, 0, 0,
414 0, 0, 0, 0, 0, 0, 0, 0,
415 0, 0, 1, 1, 1, 1, 1, 1,
416 1, 1, 0, 0, 0
417 };
418 /*
419 * Some rval codes represent "blink" colors. That actually reduces
420 * to ^C<non blink> + ^F, so that if you do ^F later, you get the
421 * <non blink> color. This table indicates whether a ^C code
422 * turns blink ON or OFF. (Every color does one or the other)
423 */
424 static int back_blink_conv[] = {
425 0, 0, 0, 0, 0, 0, 0, 0,
426 0, 0, 0, 0, 0, 0, 0, 0,
427 0, 0, 0, 0, 0, 0, 0, 0,
428 0, 0, 0, 0, 0, 0, 0, 0,
429 0, 0, 0, 0, 0, 0, 0, 0,
430 0, 0, 0, 0, 0, 0, 0, 0,
431 0, 0, 1, 1, 1, 1, 1, 1,
432 1, 1, 0, 0, 0
433 };
434 /*
435 * If /set term_does_bright_blink is on, this will be used instead
436 * of back_blink_conv. On an xterm, this will cause the background
437 * to be bold.
438 */
439 static int back_bold_conv[] = {
440 1, 0, 0, 0, 1, 0, 0, 0,
441 1, 1, 0, 1, 1, 1, 1, 0,
442 1, 0, 0, 0, 0, 0, 0, 0,
443 0, 0, 0, 0, 0, 0, 0, 0,
444 0, 0, 0, 0, 0, 0, 0, 0,
445 0, 0, 0, 0, 0, 0, 0, 0,
446 0, 0, 1, 1, 1, 1, 1, 1,
447 1, 1, 0, 0, 0
448 };
449 /*
450 * And switch between the two.
451 */
452 int *back_blinkbold_conv = blinkbold ? back_bold_conv : back_blink_conv;
453
454 /* Local variables, of course */
455 const u_char * ptr = start;
456 int c1, c2;
457 Attribute * a;
458 Attribute ad;
459 int fg;
460 int val;
461 int noval;
462
463 /* Reset all attributes to zero */
464 ad.bold = ad.underline = ad.reverse = ad.blink = ad.altchar = 0;
465 ad.color_fg = ad.color_bg = ad.fg_color = ad.bg_color = 0;
466
467 /* Copy the inward attributes, if provided */
468 a = (d) ? (Attribute *)d : &ad;
469
470 /*
471 * If we're passed a non ^C code, dont do anything.
472 */
473 if (*ptr != '\003')
474 return ptr;
475
476 /*
477 * This is a one-or-two-time-through loop. We find the maximum
478 * span that can compose a legit ^C sequence, then if the first
479 * nonvalid character is a comma, we grab the rhs of the code.
480 */
481 for (fg = 1; ; fg = 0)
482 {
483 /*
484 * If its just a lonely old ^C, then its probably a terminator.
485 * Just skip over it and go on.
486 */
487 ptr++;
488 if (*ptr == 0)
489 {
490 if (fg)
491 a->color_fg = a->fg_color = 0;
492 a->color_bg = a->bg_color = 0;
493 a->bold = a->blink = 0;
494 return ptr;
495 }
496
497 /*
498 * Check for the very special case of a definite terminator.
499 * If the argument to ^C is -1, then we absolutely know that
500 * this ends the code without starting a new one
501 */
502 /* XXX *cough* is 'ptr[1]' valid here? */
503 else if (ptr[0] == '-' && ptr[1] == '1')
504 {
505 if (fg)
506 a->color_fg = a->fg_color = 0;
507 a->color_bg = a->bg_color = 0;
508 a->bold = a->blink = 0;
509 return ptr + 2;
510 }
511
512 /*
513 * Further checks against a lonely old naked ^C.
514 */
515 else if (!isdigit(ptr[0]) && ptr[0] != ',')
516 {
517 if (fg)
518 a->color_fg = a->fg_color = 0;
519 a->color_bg = a->bg_color = 0;
520 a->bold = a->blink = 0;
521 return ptr;
522 }
523
524
525 /*
526 * Code certainly cant have more than two chars in it
527 */
528 c1 = ptr[0];
529 c2 = ptr[1];
530 val = 0;
531 noval = 0;
532
533 #define mkdigit(x) ((x) - '0')
534
535 /* Our action depends on the char immediately after the ^C. */
536 switch (c1)
537 {
538 /* These might take one or two characters */
539 case '0':
540 case '1':
541 case '2':
542 case '3':
543 case '4':
544 case '5':
545 {
546 if (c2 >= '0' && c2 <= '9')
547 {
548 int val1;
549 int val2;
550
551 ptr++;
552 val1 = mkdigit(c1);
553 val2 = mkdigit(c1) * 10 + mkdigit(c2);
554
555 if (fg)
556 {
557 if (fore_conv[val2] == -1)
558 val = val1;
559 else
560 val = val2, ptr++;
561 }
562 else
563 {
564 if (back_conv[val2] == -1)
565 val = val1;
566 else
567 val = val2, ptr++;
568 }
569 break;
570 }
571
572 /* FALLTHROUGH */
573 }
574
575 /* These can only take one character */
576 case '6':
577 case '7':
578 case '8':
579 case '9':
580 {
581 ptr++;
582
583 val = mkdigit(c1);
584 break;
585 }
586
587 /*
588 * Y -> <stop> Y for any other nonnumeric Y
589 */
590 default:
591 {
592 noval = 1;
593 break;
594 }
595 }
596
597 if (noval == 0)
598 {
599 if (fg)
600 {
601 a->color_fg = 1;
602 a->bold = fore_bold_conv[val];
603 a->fg_color = fore_conv[val];
604 }
605 else
606 {
607 a->color_bg = 1;
608 a->blink = back_blinkbold_conv[val];
609 a->bg_color = back_conv[val];
610 }
611 }
612
613 if (fg && *ptr == ',')
614 continue;
615 break;
616 }
617
618 return ptr;
619 }
620
621 /**************************** STRIP ANSI ***********************************/
622 /*
623 * Used as a translation table when we cant display graphics characters
624 * or we have been told to do translation. A no-brainer, with little attempt
625 * at being smart.
626 * (JKJ: perhaps we should allow a user to /set this?)
627 */
628 static u_char gcxlate[256] = {
629 '*', '*', '*', '*', '*', '*', '*', '*',
630 '#', '*', '#', '*', '*', '*', '*', '*',
631 '>', '<', '|', '!', '|', '$', '_', '|',
632 '^', 'v', '>', '<', '*', '=', '^', 'v',
633 ' ', '!', '"', '#', '$', '%', '&', '\'',
634 '(', ')', '*', '+', ',', '_', '.', '/',
635 '0', '1', '2', '3', '4', '5', '6', '7',
636 '8', '9', ':', ';', '<', '=', '>', '?',
637 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
638 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
639 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
640 'Z', 'Y', 'X', '[', '\\', ']', '^', '_',
641 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
642 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
643 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
644 'x', 'y', 'z', '{', '|', '}', '~', '?',
645 'C', 'u', 'e', 'a', 'a', 'a', 'a', 'c',
646 'e', 'e', 'e', 'i', 'i', 'i', 'A', 'A',
647 'e', 'e', 'e', 'o', 'o', 'o', 'u', 'u',
648 'y', 'O', 'U', 'C', '#', 'Y', 'P', 'f',
649 'a', 'i', 'o', 'u', 'n', 'N', '^', '^',
650 '?', '<', '>', '2', '4', '!', '<', '>',
651 '#', '#', '#', '|', '|', '|', '|', '+',
652 '+', '+', '+', '|', '+', '+', '+', '+',
653 '+', '+', '+', '+', '-', '+', '+', '+',
654 '+', '+', '+', '+', '+', '=', '+', '+',
655 '+', '+', '+', '+', '+', '+', '+', '+',
656 '+', '+', '+', '#', '-', '|', '|', '-',
657 'a', 'b', 'P', 'p', 'Z', 'o', 'u', 't',
658 '#', 'O', '0', 'O', '-', 'o', 'e', 'U',
659 '*', '+', '>', '<', '|', '|', '/', '=',
660 '*', '*', '*', '*', 'n', '2', '*', '*'
661 };
662
663 /*
664 * State 0 is a "normal, printable character"
665 * State 1 is an "eight bit character"
666 * State 2 is an "escape character" (\033)
667 * State 3 is a "color code character" (\003)
668 * State 4 is an "attribute change character"
669 * State 5 is a "suppressed character" (always stripped)
670 * State 6 is a "character that is never printable."
671 * State 7 is a "beep"
672 * State 8 is a "tab"
673 * State 9 is a "non-destructive space"
674 */
675 static u_char ansi_state[256] = {
676 /* ^@ ^A ^B ^C ^D ^E ^F ^G */
677 6, 6, 4, 3, 6, 4, 4, 7, /* 000 */
678 /* ^H ^I ^J ^K ^L ^M ^N ^O */
679 6, 8, 0, 6, 0, 6, 6, 4, /* 010 */
680 /* ^P ^Q ^R ^S ^T ^U ^V ^W */
681 6, 6, 6, 9, 4, 6, 4, 6, /* 020 */
682 /* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
683 6, 6, 6, 2, 6, 6, 6, 4, /* 030 */
684 0, 0, 0, 0, 0, 0, 0, 0, /* 040 */
685 0, 0, 0, 0, 0, 0, 0, 0, /* 050 */
686 0, 0, 0, 0, 0, 0, 0, 0, /* 060 */
687 0, 0, 0, 0, 0, 0, 0, 0, /* 070 */
688 0, 0, 0, 0, 0, 0, 0, 0, /* 100 */
689 0, 0, 0, 0, 0, 0, 0, 0, /* 110 */
690 0, 0, 0, 0, 0, 0, 0, 0, /* 120 */
691 0, 0, 0, 0, 0, 0, 0, 0, /* 130 */
692 0, 0, 0, 0, 0, 0, 0, 0, /* 140 */
693 0, 0, 0, 0, 0, 0, 0, 0, /* 150 */
694 0, 0, 0, 0, 0, 0, 0, 0, /* 160 */
695 0, 0, 0, 0, 0, 0, 0, 0, /* 170 */
696 1, 1, 1, 1, 1, 1, 1, 1, /* 200 */
697 1, 1, 1, 1, 1, 1, 1, 1, /* 210 */
698 1, 1, 1, 1, 1, 1, 1, 1, /* 220 */
699 1, 1, 1, 1, 1, 1, 1, 1, /* 230 */
700 1, 1, 1, 1, 1, 1, 1, 1, /* 240 */
701 1, 1, 1, 1, 1, 1, 1, 1, /* 250 */
702 1, 1, 1, 1, 1, 1, 1, 1, /* 260 */
703 1, 1, 1, 1, 1, 1, 1, 1, /* 270 */
704 1, 1, 1, 1, 1, 1, 1, 1, /* 300 */
705 1, 1, 1, 1, 1, 1, 1, 1, /* 310 */
706 1, 1, 1, 1, 1, 1, 1, 1, /* 320 */
707 1, 1, 1, 1, 1, 1, 1, 1, /* 330 */
708 1, 1, 1, 1, 1, 1, 1, 1, /* 340 */
709 1, 1, 1, 1, 1, 1, 1, 1, /* 350 */
710 1, 1, 1, 1, 1, 1, 1, 1, /* 360 */
711 1, 1, 1, 1, 1, 1, 1, 1 /* 370 */
712 };
713
714 /*
715 * This started off as a general ansi parser, and it worked for stuff that
716 * was going out to the display, but it couldnt deal properly with ansi codes,
717 * and so when I tried to use it for the status bar, it just all fell to
718 * pieces. After working it over, I came up with this. What this does
719 * (believe it or not) is walk through and strip out all the ansi codes in
720 * the target string. Any codes that we recognize as being safe (pretty much
721 * just ^[[<number-list>m), are converted back into their logical characters
722 * (eg, ^B, ^R, ^_, etc), and everything else is completely blown away.
723 *
724 * If "width" is not -1, then every "width" printable characters, a \n
725 * marker is put into the output so you can tell where the line breaks
726 * are. Obviously, this is optional. It is used by prepared_display
727 * and $leftpc().
728 *
729 * XXX Some have asked that i "space out" the outputs with spaces and return
730 * but one row of output, so that rxvt will paste it as all one line. Yea,
731 * that might be nice, but that raises other, more thorny issues.
732 */
733
734 /*
735 * These macros help keep 8 bit chars from sneaking into the output stream
736 * where they might be stripped out.
737 */
738 #define this_char() (eightbit ? *str : (*str) & 0x7f)
739 #define next_char() (eightbit ? *str++ : (*str++) & 0x7f)
740 #define put_back() (str--)
741 #define nlchar '\n'
742
normalize_string(const u_char * str,int logical)743 u_char * normalize_string (const u_char *str, int logical)
744 {
745 u_char * output;
746 u_char chr;
747 Attribute a;
748 int pos;
749 int maxpos;
750 int args[10];
751 int nargs;
752 int i, n;
753 int ansi = get_int_var(DISPLAY_ANSI_VAR);
754 int gcmode = get_int_var(DISPLAY_PC_CHARACTERS_VAR);
755 int eightbit = term_eight_bit();
756 int beep_max, beep_cnt = 0;
757 int tab_max, tab_cnt = 0;
758 int nds_max, nds_cnt = 0;
759 int pc = 0;
760 int reverse, bold, blink, underline, altchar, color, allow_c1, boldback;
761 size_t (*attrout) (u_char *, Attribute *) = NULL;
762
763 /* Figure out how many beeps/tabs/nds's we can handle */
764 if (!(beep_max = get_int_var(BEEP_MAX_VAR)))
765 beep_max = -1;
766 if (!get_int_var(TAB_VAR))
767 tab_max = -1;
768 else if ((tab_max = get_int_var(TAB_MAX_VAR)) < 0)
769 tab_max = -1;
770 if (!(nds_max = get_int_var(ND_SPACE_MAX_VAR)))
771 nds_max = -1;
772 if (normalize_permit_all_attributes) /* XXXX */
773 reverse = bold = blink = underline = altchar = color = allow_c1 = boldback = 1;
774 else
775 {
776 reverse = get_int_var(INVERSE_VIDEO_VAR);
777 bold = get_int_var(BOLD_VIDEO_VAR);
778 blink = get_int_var(BLINK_VIDEO_VAR);
779 underline = get_int_var(UNDERLINE_VIDEO_VAR);
780 altchar = get_int_var(ALT_CHARSET_VAR);
781 color = get_int_var(COLOR_VAR);
782 allow_c1 = get_int_var(ALLOW_C1_CHARS_VAR);
783 boldback = get_int_var(TERM_DOES_BRIGHT_BLINK_VAR);
784 }
785 if (logical == 0)
786 attrout = display_attributes; /* prep for screen output */
787 else if (logical == 1)
788 attrout = logic_attributes; /* non-screen handlers */
789 else if (logical == 2)
790 attrout = ignore_attributes; /* $stripansi() function */
791 else if (logical == 3)
792 attrout = display_attributes; /* The status line */
793 else
794 panic("'logical == %d' is not valid.", logical);
795
796 /* Reset all attributes to zero */
797 a.bold = a.underline = a.reverse = a.blink = a.altchar = 0;
798 a.color_fg = a.color_bg = a.fg_color = a.bg_color = 0;
799
800 /*
801 * The output string has a few extra chars on the end just
802 * in case you need to tack something else onto it.
803 */
804 maxpos = strlen(str);
805 output = (u_char *)new_malloc(maxpos + 192);
806 pos = 0;
807
808 while ((chr = next_char()))
809 {
810 if (pos > maxpos)
811 {
812 maxpos += 192; /* Extend 192 chars at a time */
813 RESIZE(output, unsigned char, maxpos + 192);
814 }
815
816 switch (ansi_state[chr])
817 {
818 /*
819 * State 0 is a normal, printable ascii character
820 */
821 case 0:
822 output[pos++] = chr;
823 pc++;
824 break;
825
826 /*
827 * State 1 is a high-bit character that may or may not
828 * need to be translated first.
829 * State 6 is an unprintable character that must be made
830 * unprintable (gcmode is forced to be 1)
831 */
832 case 1:
833 case 5:
834 case 6:
835 {
836 int my_gcmode = gcmode;
837
838 /*
839 * This is a very paranoid check to make sure that
840 * the 8-bit escape codes dont elude us.
841 */
842 if (allow_c1 == 0 && chr >= 128 && chr <= 159)
843 my_gcmode = 0;
844
845 if (ansi_state[chr] == 5)
846 my_gcmode = 0;
847
848 if (ansi_state[chr] == 6)
849 my_gcmode = 1;
850
851 if (normalize_never_xlate)
852 my_gcmode = 4;
853
854 switch (my_gcmode)
855 {
856 /*
857 * In gcmode 5, translate all characters
858 */
859 case 5:
860 {
861 output[pos++] = gcxlate[chr];
862 break;
863 }
864
865 /*
866 * In gcmode 4, accept all characters
867 */
868 case 4:
869 {
870 output[pos++] = chr;
871 break;
872 }
873
874 /*
875 * In gcmode 3, accept or translate chars
876 */
877 case 3:
878 {
879 if (termfeatures & TERM_CAN_GCHAR)
880 output[pos++] = chr;
881 else
882 output[pos++] = gcxlate[chr];
883 break;
884 }
885
886 /*
887 * In gcmode 2, accept or highlight xlate
888 */
889 case 2:
890 {
891 if (termfeatures & TERM_CAN_GCHAR)
892 output[pos++] = chr;
893 else
894 {
895 /* <REV> char <REV> */
896 a.reverse = !a.reverse;
897 pos += attrout(output + pos, &a);
898 output[pos++] = gcxlate[chr];
899 a.reverse = !a.reverse;
900 pos += attrout(output + pos, &a);
901 }
902 break;
903 }
904
905 /*
906 * gcmode 1 is "accept or reverse mangle"
907 * If youre doing 8-bit, it accepts eight
908 * bit characters. If youre not doing 8 bit
909 * then it converts the char into something
910 * printable and then reverses it.
911 */
912 case 1:
913 {
914 if (termfeatures & TERM_CAN_GCHAR)
915 output[pos++] = chr;
916 else if ((chr & 0x80) && eightbit)
917 output[pos++] = chr;
918 else
919 {
920 a.reverse = !a.reverse;
921 pos += attrout(output + pos, &a);
922 output[pos++] =
923 (chr | 0x40) & 0x7f;
924 a.reverse = !a.reverse;
925 pos += attrout(output + pos, &a);
926 }
927 break;
928 }
929
930 /*
931 * gcmode 0 is "always strip out"
932 */
933 case 0:
934 break;
935 }
936 pc++;
937 break;
938 }
939
940
941 /*
942 * State 2 is the escape character
943 */
944 case 2:
945 {
946 /*
947 * The next thing we do is dependant on what the character
948 * is after the escape. Be very conservative in what we
949 * allow. In general, escape sequences shouldn't be very
950 * complex at this point.
951 * If we see an escape at the end of a string, just mangle
952 * it and dont bother with the rest of the expensive
953 * parsing.
954 */
955 if (!ansi || this_char() == 0)
956 {
957 a.reverse = !a.reverse;
958 pos += attrout(output + pos, &a);
959 output[pos++] = '[';
960 a.reverse = !a.reverse;
961 pos += attrout(output + pos, &a);
962
963 pc++;
964 continue;
965 }
966
967 switch ((chr = next_char()))
968 {
969 /*
970 * All these codes we just skip over. We're not
971 * interested in them.
972 */
973
974 /*
975 * These are two-character commands. The second
976 * char is the argument.
977 */
978 case ('#') : case ('(') : case (')') :
979 case ('*') : case ('+') : case ('$') :
980 case ('@') :
981 {
982 chr = next_char();
983 if (chr == 0)
984 put_back(); /* Bogus sequence */
985 break;
986 }
987
988 /*
989 * These are just single-character commands.
990 */
991 case ('7') : case ('8') : case ('=') :
992 case ('>') : case ('D') : case ('E') :
993 case ('F') : case ('H') : case ('M') :
994 case ('N') : case ('O') : case ('Z') :
995 case ('l') : case ('m') : case ('n') :
996 case ('o') : case ('|') : case ('}') :
997 case ('~') : case ('c') :
998 {
999 break; /* Don't do anything */
1000 }
1001
1002 /*
1003 * Swallow up graphics sequences...
1004 */
1005 case ('G'):
1006 {
1007 while ((chr = next_char()) != 0 &&
1008 chr != ':')
1009 ;
1010 if (chr == 0)
1011 put_back();
1012 break;
1013 }
1014
1015 /*
1016 * Not sure what this is, it's not supported by
1017 * rxvt, but its supposed to end with an ESCape.
1018 */
1019 case ('P') :
1020 {
1021 while ((chr = next_char()) != 0 &&
1022 chr != 033)
1023 ;
1024 if (chr == 0)
1025 put_back();
1026 break;
1027 }
1028
1029 /*
1030 * Anything else, we just munch the escape and
1031 * leave it at that.
1032 */
1033 default:
1034 put_back();
1035 break;
1036
1037
1038 /*
1039 * Strip out Xterm sequences
1040 */
1041 case (']') :
1042 {
1043 while ((chr = next_char()) != 0 && chr != 7)
1044 ;
1045 if (chr == 0)
1046 put_back();
1047 break;
1048 }
1049
1050 /*
1051 * Now these we're interested in....
1052 * (CSI sequences)
1053 */
1054 case ('[') :
1055 {
1056 /* <<<<<<<<<<<< */
1057 /*
1058 * Set up the arguments list
1059 */
1060 nargs = 0;
1061 args[0] = args[1] = args[2] = args[3] = 0;
1062 args[4] = args[5] = args[6] = args[7] = 0;
1063 args[8] = args[9] = 0;
1064
1065 /*
1066 * This stuff was taken/modified/inspired by rxvt. We do it this
1067 * way in order to trap an esc sequence that is embedded in another
1068 * (blah). We're being really really really paranoid doing this,
1069 * but it is for the best.
1070 */
1071
1072 /*
1073 * Check to see if the stuff after the command is a "private"
1074 * modifier. If it is, then we definitely arent interested.
1075 * '<' , '=' , '>' , '?'
1076 */
1077 chr = this_char();
1078 if (chr >= '<' && chr <= '?')
1079 next_char(); /* skip it */
1080
1081
1082 /*
1083 * Now pull the arguments off one at a time. Keep pulling them
1084 * off until we find a character that is not a number or a semicolon.
1085 * Skip everything else.
1086 */
1087 for (nargs = 0; nargs < 10; str++)
1088 {
1089 n = 0;
1090 for (n = 0; isdigit(this_char()); next_char())
1091 n = n * 10 + (this_char() - '0');
1092
1093 args[nargs++] = n;
1094
1095 /*
1096 * If we run out of code here, then we're totaly confused.
1097 * just back out with whatever we have...
1098 */
1099 if (!this_char())
1100 {
1101 output[pos] = output[pos + 1] = 0;
1102 return output;
1103 }
1104
1105 if (this_char() != ';')
1106 break;
1107 }
1108
1109 /*
1110 * If we find a new ansi char, start all over from the top
1111 * and strip it out too
1112 */
1113 if (this_char() == 033)
1114 continue;
1115
1116 /*
1117 * Support "spaces" (cursor right) code
1118 */
1119 if (this_char() == 'a' || this_char() == 'C')
1120 {
1121 next_char();
1122 if (nargs >= 1)
1123 {
1124 /*
1125 * Keep this within reality.
1126 */
1127 if (args[0] > 256)
1128 args[0] = 256;
1129
1130 /* This is just sanity */
1131 if (pos + args[0] > maxpos)
1132 {
1133 maxpos += args[0];
1134 RESIZE(output, u_char, maxpos + 192);
1135 }
1136 while (args[0]-- > 0)
1137 {
1138 if (nds_max > 0 && nds_cnt > nds_max)
1139 break;
1140
1141 output[pos++] = ND_SPACE;
1142 pc++;
1143 nds_cnt++;
1144 }
1145 }
1146 break;
1147 }
1148
1149
1150 /*
1151 * The 'm' command is the only one that we honor.
1152 * All others are dumped.
1153 */
1154 if (next_char() != 'm')
1155 break;
1156
1157
1158 /*
1159 * Walk all of the numeric arguments, plonking the appropriate
1160 * attribute changes as needed.
1161 */
1162 for (i = 0; i < nargs; i++)
1163 {
1164 switch (args[i])
1165 {
1166 case 0: /* Reset to default */
1167 {
1168 a.reverse = a.bold = 0;
1169 a.blink = a.underline = 0;
1170 a.altchar = 0;
1171 a.color_fg = a.color_bg = 0;
1172 a.fg_color = a.bg_color = 0;
1173 pos += attrout(output + pos, &a);
1174 break;
1175 }
1176 case 1: /* bold on */
1177 {
1178 if (bold)
1179 {
1180 a.bold = 1;
1181 pos += attrout(output + pos, &a);
1182 }
1183 break;
1184 }
1185 case 2: /* dim on -- not supported */
1186 break;
1187 case 4: /* Underline on */
1188 {
1189 if (underline)
1190 {
1191 a.underline = 1;
1192 pos += attrout(output + pos, &a);
1193 }
1194 break;
1195 }
1196 case 5: /* Blink on */
1197 case 26: /* Blink on */
1198 {
1199 if (blink)
1200 {
1201 a.blink = 1;
1202 pos += attrout(output + pos, &a);
1203 }
1204 break;
1205 }
1206 case 6: /* Blink off */
1207 case 25: /* Blink off */
1208 {
1209 a.blink = 0;
1210 pos += attrout(output + pos, &a);
1211 break;
1212 }
1213 case 7: /* Reverse on */
1214 {
1215 if (reverse)
1216 {
1217 a.reverse = 1;
1218 pos += attrout(output + pos, &a);
1219 }
1220 break;
1221 }
1222 case 21: /* Bold off */
1223 case 22: /* Bold off */
1224 {
1225 a.bold = 0;
1226 pos += attrout(output + pos, &a);
1227 break;
1228 }
1229 case 24: /* Underline off */
1230 {
1231 a.underline = 0;
1232 pos += attrout(output + pos, &a);
1233 break;
1234 }
1235 case 27: /* Reverse off */
1236 {
1237 a.reverse = 0;
1238 pos += attrout(output + pos, &a);
1239 break;
1240 }
1241 case 30: case 31: case 32: case 33: case 34:
1242 case 35: case 36: case 37: /* Set foreground color */
1243 {
1244 if (color)
1245 {
1246 a.color_fg = 1;
1247 a.fg_color = args[i] - 30;
1248 pos += attrout(output + pos, &a);
1249 }
1250 break;
1251 }
1252 case 39: /* Reset foreground color to default */
1253 {
1254 if (color)
1255 {
1256 a.color_fg = 0;
1257 a.fg_color = 0;
1258 pos += attrout(output + pos, &a);
1259 }
1260 break;
1261 }
1262 case 40: case 41: case 42: case 43: case 44:
1263 case 45: case 46: case 47: /* Set background color */
1264 {
1265 if (color)
1266 {
1267 a.color_bg = 1;
1268 a.bg_color = args[i] - 40;
1269 pos += attrout(output + pos, &a);
1270 }
1271 break;
1272 }
1273 case 49: /* Reset background color to default */
1274 {
1275 if (color)
1276 {
1277 a.color_bg = 0;
1278 a.bg_color = 0;
1279 pos += attrout(output + pos, &a);
1280 }
1281 break;
1282 }
1283
1284 default: /* Everything else is not supported */
1285 break;
1286 }
1287 } /* End of for (handling esc-[...m) */
1288 /* >>>>>>>>>>> */
1289 } /* End of escape-[ code handling */
1290 } /* End of ESC handling */
1291 break;
1292 } /* End of case 2 handling */
1293
1294
1295 /*
1296 * Skip over ^C codes, they're already normalized.
1297 * well, thats not totaly true. We do some mangling
1298 * in order to make it work better
1299 */
1300 case 3:
1301 {
1302 const u_char *end;
1303
1304 put_back();
1305 end = read_color_seq(str, (void *)&a, boldback);
1306
1307 /*
1308 * XXX - This is a short-term hack to prevent an
1309 * infinite loop. I need to come back and fix
1310 * this the right way in the future.
1311 *
1312 * The infinite loop can happen when a character
1313 * 131 is encountered when eight bit chars is OFF.
1314 * We see a character 3 (131 with the 8th bit off)
1315 * and so we ask skip_ctl_c_seq where the end of
1316 * that sequence is. But since it isnt a ^c sequence
1317 * it just shrugs its shoulders and returns the
1318 * pointer as-is. So we sit asking it where the end
1319 * is and it says "its right here". So there is a
1320 * need to check the retval of skip_ctl_c_seq to
1321 * actually see if there is a sequence here. If there
1322 * is not, then we just mangle this character. For
1323 * the record, char 131 is a reverse block, so that
1324 * seems the most appropriate thing to put here.
1325 */
1326 if (end == str)
1327 {
1328 /* Turn on reverse if neccesary */
1329 a.reverse = !a.reverse;
1330 pos += attrout(output + pos, &a);
1331 output[pos++] = ' ';
1332 a.reverse = !a.reverse;
1333 pos += attrout(output + pos, &a);
1334
1335 pc++;
1336 next_char(); /* Munch it */
1337 break;
1338 }
1339
1340 /* Move to the end of the string. */
1341 str = end;
1342
1343 /* Suppress the color if no color is permitted */
1344 if (!color)
1345 {
1346 a.color_fg = a.color_bg = 0;
1347 a.fg_color = a.bg_color = 0;
1348 break;
1349 }
1350
1351 /* Output the new attributes */
1352 pos += attrout(output + pos, &a);
1353 break;
1354 }
1355
1356 /*
1357 * State 4 is for the special highlight characters
1358 */
1359 case 4:
1360 {
1361 put_back();
1362 switch (this_char())
1363 {
1364 case REV_TOG:
1365 if (reverse)
1366 a.reverse = !a.reverse;
1367 break;
1368 case BOLD_TOG:
1369 if (bold)
1370 a.bold = !a.bold;
1371 break;
1372 case BLINK_TOG:
1373 if (blink)
1374 a.blink = !a.blink;
1375 break;
1376 case UND_TOG:
1377 if (underline)
1378 a.underline = !a.underline;
1379 break;
1380 case ALT_TOG:
1381 if (altchar)
1382 a.altchar = !a.altchar;
1383 break;
1384 case ALL_OFF:
1385 a.reverse = a.bold = a.blink = 0;
1386 a.underline = a.altchar = 0;
1387 a.color_fg = a.color_bg = 0;
1388 a.bg_color = a.fg_color = 0;
1389 break;
1390 default:
1391 break;
1392 }
1393
1394 pos += attrout(output + pos, &a);
1395 next_char();
1396 break;
1397 }
1398
1399 case 7: /* bell */
1400 {
1401 beep_cnt++;
1402 if ((beep_max == -1) || (beep_cnt > beep_max))
1403 {
1404 a.reverse = !a.reverse;
1405 pos += attrout(output + pos, &a);
1406 output[pos++] = 'G';
1407 a.reverse = !a.reverse;
1408 pos += attrout(output + pos, &a);
1409 pc++;
1410 }
1411 else
1412 output[pos++] = '\007';
1413
1414 break;
1415 }
1416
1417 case 8: /* Tab */
1418 {
1419 tab_cnt++;
1420 if (tab_max < 0 ||
1421 (tab_max > 0 && tab_cnt > tab_max))
1422 {
1423 a.reverse = !a.reverse;
1424 pos += attrout(output + pos, &a);
1425 output[pos++] = 'I';
1426 a.reverse = !a.reverse;
1427 pos += attrout(output + pos, &a);
1428 pc++;
1429 }
1430 else
1431 {
1432 int len = 8 - (pc % 8);
1433 for (i = 0; i < len; i++)
1434 {
1435 output[pos++] = ' ';
1436 pc++;
1437 }
1438 }
1439 break;
1440 }
1441
1442 case 9: /* Non-destruct space */
1443 {
1444 nds_cnt++;
1445
1446 /*
1447 * Just swallop up any ND's over the max
1448 */
1449 if ((nds_max > 0) && (nds_cnt > nds_max))
1450 ;
1451 else
1452 output[pos++] = ND_SPACE;
1453 break;
1454 }
1455
1456 default:
1457 {
1458 panic("Unknown normalize_string mode");
1459 break;
1460 }
1461 } /* End of huge ansi-state switch */
1462 } /* End of while, iterating over input string */
1463
1464 /* Terminate the output and return it. */
1465 if (logical == 0)
1466 {
1467 a.bold = a.underline = a.reverse = a.blink = a.altchar = 0;
1468 a.color_fg = a.color_bg = a.fg_color = a.bg_color = 0;
1469 pos += attrout(output + pos, &a);
1470 }
1471 output[pos] = output[pos + 1] = 0;
1472 return output;
1473 }
1474
1475 /*
1476 * XXX I'm not sure where this belongs, but for now it goes here.
1477 * This function takes a type-1 normalized string (with the attribute
1478 * markers) and converts them back to logical characters. This is needed
1479 * for lastlog and the status line and so forth.
1480 */
denormalize_string(const u_char * str)1481 u_char * denormalize_string (const u_char *str)
1482 {
1483 u_char * output = NULL;
1484 size_t maxpos;
1485 Attribute a;
1486 size_t span;
1487 size_t pos;
1488
1489 /* Reset all attributes to zero */
1490 a.bold = a.underline = a.reverse = a.blink = a.altchar = 0;
1491 a.color_fg = a.color_bg = a.fg_color = a.bg_color = 0;
1492
1493 /*
1494 * The output string has a few extra chars on the end just
1495 * in case you need to tack something else onto it.
1496 */
1497 maxpos = strlen(str);
1498 output = (u_char *)new_malloc(maxpos + 192);
1499 pos = 0;
1500
1501 while (*str)
1502 {
1503 if (pos > maxpos)
1504 {
1505 maxpos += 192; /* Extend 192 chars at a time */
1506 RESIZE(output, unsigned char, maxpos + 192);
1507 }
1508 switch (*str)
1509 {
1510 case '\006':
1511 {
1512 if (read_attributes(str, &a))
1513 continue; /* Mangled */
1514 str += 5;
1515
1516 span = logic_attributes(output + pos, &a);
1517 pos += span;
1518 break;
1519 }
1520 default:
1521 {
1522 output[pos++] = *str++;
1523 break;
1524 }
1525 }
1526 }
1527 output[pos] = 0;
1528 return output;
1529 }
1530
1531
1532
1533 /*
1534 * Prepare_display -- this is a new twist on FireClown's original function.
1535 * We dont do things quite the way they were explained in the previous
1536 * comment that used to be here, so here's the rewrite. ;-)
1537 *
1538 * This function is used to break a logical line of display into some
1539 * number of physical lines of display, while accounting for various kinds
1540 * of display codes. The logical line is passed in the 'orig_str' variable,
1541 * and the width of the physical display is passed in 'max_cols'. If
1542 * 'lused' is not NULL, then it points at an integer that specifies the
1543 * maximum number of lines that should be prepared. The actual number of
1544 * lines that are prepared is stored into 'lused'. The 'flags' variable
1545 * specifies some extra options, the only one of which that is supported
1546 * right now is "PREPARE_NOWRAP" which indicates that you want the function
1547 * to break off the text at 'max_cols' and not to "wrap" the last partial
1548 * word to the next line. ($leftpc() depends on this)
1549 */
1550 #define SPLIT_EXTENT 40
prepare_display(const unsigned char * str,int max_cols,int * lused,int flags)1551 unsigned char **prepare_display(const unsigned char *str,
1552 int max_cols,
1553 int *lused,
1554 int flags)
1555 {
1556 static int recursion = 0,
1557 output_size = 0;
1558 int pos = 0, /* Current position in "buffer" */
1559 col = 0, /* Current column in display */
1560 word_break = 0, /* Last end of word */
1561 indent = 0, /* Start of second word */
1562 firstwb = 0, /* Buffer position of second word */
1563 line = 0, /* Current pos in "output" */
1564 do_indent, /* Use indent or continued line? */
1565 newline = 0; /* Number of newlines */
1566 static u_char **output = (unsigned char **)0;
1567 const u_char *ptr;
1568 u_char buffer[BIG_BUFFER_SIZE + 1],
1569 *cont_ptr,
1570 *cont = empty_string,
1571 c,
1572 *pos_copy;
1573 const char *words;
1574 Attribute a;
1575 Attribute saved_a;
1576 u_char *cont_free = NULL;
1577 char * free_me_later = NULL;
1578
1579 if (recursion)
1580 panic("prepare_display() called recursively");
1581 recursion++;
1582
1583 /* Reset all attributes to zero */
1584 a.bold = a.underline = a.reverse = a.blink = a.altchar = 0;
1585 a.color_fg = a.color_bg = a.fg_color = a.bg_color = 0;
1586 saved_a.bold = saved_a.underline = saved_a.reverse = 0;
1587 saved_a.blink = saved_a.altchar = 0;
1588 saved_a.color_fg = saved_a.color_bg = saved_a.fg_color = 0;
1589 saved_a.bg_color = 0;
1590
1591 do_indent = get_int_var(INDENT_VAR);
1592 if (!(words = get_string_var(WORD_BREAK_VAR)))
1593 words = ", ";
1594 if (!(cont_ptr = get_string_var(CONTINUED_LINE_VAR)))
1595 cont_ptr = empty_string;
1596
1597 buffer[0] = 0;
1598
1599 if (!output_size)
1600 {
1601 int new_i = SPLIT_EXTENT;
1602 RESIZE(output, char *, new_i);
1603 while (output_size < new_i)
1604 output[output_size++] = 0;
1605 }
1606
1607 /*
1608 * Start walking through the entire string.
1609 */
1610 for (ptr = str; *ptr && (pos < BIG_BUFFER_SIZE - 8); ptr++)
1611 {
1612 switch (*ptr)
1613 {
1614 case '\007': /* bell */
1615 buffer[pos++] = *ptr;
1616 break;
1617
1618 case '\n': /* Forced newline */
1619 {
1620 newline = 1;
1621 if (indent == 0)
1622 indent = -1;
1623 word_break = pos;
1624 break; /* case '\n' */
1625 }
1626
1627 /* Attribute changes -- copy them unmodified. */
1628 case '\006':
1629 {
1630 if (read_attributes(ptr, &a) == 0)
1631 {
1632 buffer[pos++] = *ptr++;
1633 buffer[pos++] = *ptr++;
1634 buffer[pos++] = *ptr++;
1635 buffer[pos++] = *ptr++;
1636 buffer[pos++] = *ptr;
1637 }
1638 else
1639 abort();
1640
1641 /*
1642 * XXX This isn't a hack, but it _is_ ugly!
1643 * Because I'm too lazy to find a better place
1644 * to put this (down among the line wrapping
1645 * logic would be a good place), I take the
1646 * cheap way out by "saving" any attribute
1647 * changes that occur prior to the first space
1648 * in a line. If there are no spaces for the
1649 * rest of the line, then this *is* the saved
1650 * attributes we will need to start the next
1651 * line. This fixes an abort().
1652 */
1653 if (word_break == 0)
1654 saved_a = a;
1655
1656 continue; /* Skip the column check */
1657 }
1658
1659 default:
1660 {
1661 if (!strchr(words, *ptr))
1662 {
1663 if (indent == -1)
1664 indent = col;
1665 buffer[pos++] = *ptr;
1666 col++;
1667 break;
1668 }
1669 /* FALLTHROUGH */
1670 }
1671
1672 case ' ':
1673 case ND_SPACE:
1674 {
1675 if (indent == 0)
1676 {
1677 indent = -1;
1678 firstwb = pos;
1679 }
1680 word_break = pos;
1681 saved_a = a;
1682 if (*ptr != ' ' && ptr[1] &&
1683 (col + 1 < max_cols))
1684 word_break++;
1685 buffer[pos++] = *ptr;
1686 col++;
1687 break;
1688 }
1689 } /* End of switch (*ptr) */
1690
1691 /*
1692 * Must check for cols >= maxcols+1 becuase we can have a
1693 * character on the extreme screen edge, and we would still
1694 * want to treat this exactly as 1 line, and col has already
1695 * been incremented.
1696 */
1697 if ((col > max_cols) || newline)
1698 {
1699 /*
1700 * We just incremented col, but we need to decrement
1701 * it in order to keep the count correct!
1702 * --zinx
1703 */
1704 if (col > max_cols)
1705 col--;
1706
1707 /*
1708 * XXX Hackwork and trickery here. In the very rare
1709 * case where we end the output string *exactly* at
1710 * the end of the line, then do not do any more of
1711 * the following handling. Just punt right here.
1712 */
1713 if (ptr[1] == 0)
1714 break; /* stop all processing */
1715
1716 /*
1717 * Default the end of line wrap to the last character
1718 * we parsed if there were no spaces in the line, or
1719 * if we're preparing output that is not to be
1720 * wrapped (such as for counting output length.
1721 */
1722 if (word_break == 0 || (flags & PREPARE_NOWRAP))
1723 word_break = pos;
1724
1725 /*
1726 * XXXX Massive hackwork here.
1727 *
1728 * Due to some ... interesting design considerations,
1729 * if you have /set indent on and your first line has
1730 * exactly one word seperation in it, then obviously
1731 * there is a really long "word" to the right of the
1732 * first word. Normally, we would just break the
1733 * line after the first word and then plop the really
1734 * big word down to the second line. Only problem is
1735 * that the (now) second line needs to be broken right
1736 * there, and we chew up (and lose) a character going
1737 * through the parsing loop before we notice this.
1738 * Not good. It seems that in this very rare case,
1739 * people would rather not have the really long word
1740 * be sent to the second line, but rather included on
1741 * the first line (it does look better this way),
1742 * and so we can detect this condition here, without
1743 * losing a char but this really is just a hack when
1744 * it all comes down to it. Good thing its cheap. ;-)
1745 */
1746 if (!*cont && (firstwb == word_break) && do_indent)
1747 word_break = pos;
1748
1749 /*
1750 * If we are approaching the number of lines that
1751 * we have space for, then resize the master line
1752 * buffer so we dont run out.
1753 */
1754 if (line >= output_size - 3)
1755 {
1756 int new_i = output_size + SPLIT_EXTENT;
1757 RESIZE(output, char *, new_i);
1758 while (output_size < new_i)
1759 output[output_size++] = 0;
1760 }
1761
1762 /* XXXX HACK! XXXX HACK! XXXX HACK! XXXX */
1763 /*
1764 * Unfortunately, due to the "line wrapping bug", if
1765 * you have a really long line at the end of the first
1766 * line of output, and it needs to be wrapped to the
1767 * second line of input, we were blindly assuming that
1768 * it would fit on the second line, but that may not
1769 * be true! If the /set continued_line jazz ends up
1770 * being longer than whatever was before the wrapped
1771 * word on the first line, then the resulting second
1772 * line would be > max_cols, causing corruption of the
1773 * display (eg, the status bar gets written over)!
1774 *
1775 * To counteract this bug, at the end of the first
1776 * line, we calcluate the continued line marker
1777 * *before* we commit the first line. That way, we
1778 * can know if the word to be wrapped will overflow
1779 * the second line, and in such case, we break that
1780 * word precisely at the current point, rather than
1781 * at the word_break point! This prevents the
1782 * "line wrap bug", albeit in a confusing way.
1783 */
1784
1785 /*
1786 * Calculate the continued line marker. This is
1787 * a little bit tricky because we cant figure it out
1788 * until after the first line is done. The first
1789 * time through, cont == empty_string, so if !*cont,
1790 * we know it has not been initialized.
1791 *
1792 * So if it has not been initialized and /set indent
1793 * is on, and the place to indent is less than a third
1794 * of the screen width and /set continued_line is
1795 * less than the indented width, then we pad the
1796 * /set continued line value out to the appropriate
1797 * width.
1798 */
1799 if (!*cont)
1800 {
1801 int lhs_count = 0;
1802 int continued_count = 0;
1803
1804 if (indent > max_cols / 3)
1805 indent = max_cols / 3;
1806
1807 /* refined this to use proper logic to
1808 ** decide the length of cont. - pegasus
1809 */
1810 char *copy = LOCAL_COPY(cont_ptr);
1811 free_me_later = copy = normalize_string(copy, 0);
1812 size_t cont_len = output_with_count(copy, 0, 0);
1813 if (do_indent && (cont_len < indent))
1814 {
1815 size_t size = indent + 1 +
1816 strlen(cont_ptr) -
1817 cont_len;
1818
1819 cont = alloca(size); /* sb pana */
1820 snprintf(cont, size,
1821 "%-*s", size, cont_ptr);
1822 }
1823
1824 /*
1825 * Otherwise, we just use /set continued_line,
1826 * whatever it is.
1827 */
1828 else if (!*cont && *cont_ptr)
1829 cont = cont_ptr;
1830
1831 cont_free = cont = normalize_string(cont, 0);
1832
1833 /*
1834 * XXXX "line wrap bug" fix. If we are here,
1835 * then we are between the first and second
1836 * lines, and we might have a word that does
1837 * not fit on the first line that also does
1838 * not fit on the second line! We check for
1839 * that right here, and if it won't fit on
1840 * the next line, we revert "word_break" to
1841 * the current position.
1842 *
1843 * THIS IS UNFORTUNATELY VERY EXPENSIVE! :(
1844 */
1845 c = buffer[word_break];
1846 buffer[word_break] = 0;
1847 lhs_count = output_with_count(buffer, 0, 0);
1848 buffer[word_break] = c;
1849 continued_count = output_with_count(cont, 0, 0);
1850
1851 /*
1852 * Chop the line right here if it will
1853 * overflow the next line.
1854 *
1855 * Save the attributes, too! (05/29/02)
1856 *
1857 * XXX Saving the attributes may be
1858 * spurious but i'm erring on the side
1859 * of caution for the moment.
1860 */
1861 if (lhs_count <= continued_count) {
1862 word_break = pos;
1863 saved_a = a;
1864 }
1865
1866 /*
1867 * XXXX End of nasty nasty hack.
1868 */
1869 }
1870
1871 /*
1872 * Now we break off the line at the last space or
1873 * last char and copy it off to the master buffer.
1874 */
1875 c = buffer[word_break];
1876 buffer[word_break] = 0;
1877 malloc_strcpy((char **)&(output[line++]), buffer);
1878 buffer[word_break] = c;
1879
1880
1881 /*
1882 * Skip over all spaces that occur after the break
1883 * point, up to the right part of the screen (where
1884 * we are currently parsing). This is what allows
1885 * lots and lots of spaces to take up their room.
1886 * We let spaces fill in lines as much as neccesary
1887 * and if they overflow the line we let them bleed
1888 * to the next line.
1889 */
1890 while (word_break < pos && buffer[word_break] == ' ')
1891 word_break++;
1892
1893 /*
1894 * At this point, we still have some junk left in
1895 * 'buffer' that needs to be moved to the next line.
1896 * But of course, all new lines must be prefixed by
1897 * the /set continued_line and /set indent stuff, so
1898 * we copy off the stuff we have to a temporary
1899 * buffer, copy the continued-line stuff into buffer
1900 * and then re-append the junk into buffer. Then we
1901 * fix col and pos appropriately and continue parsing
1902 * str...
1903 */
1904 /* 'pos' has already been incremented... */
1905 buffer[pos] = 0;
1906 pos_copy = LOCAL_COPY(buffer + word_break);
1907 strlcpy(buffer, cont, sizeof(buffer) / 2);
1908 display_attributes(buffer + strlen(buffer), &saved_a);
1909 strlcat(buffer, pos_copy, sizeof(buffer) / 2);
1910 display_attributes(buffer + strlen(buffer), &a);
1911
1912 pos = strlen(buffer);
1913 /* Watch this -- ugh. how expensive! :( */
1914 col = output_with_count(buffer, 0, 0);
1915 word_break = 0;
1916 newline = 0;
1917
1918 /*
1919 * The 'lused' argument allows us to truncate the
1920 * parsing at '*lused' lines. This is most helpful
1921 * for the $leftpc() function, which sets a logical
1922 * screen width and asks us to "output" one line.
1923 */
1924 if (*lused && line >= *lused)
1925 {
1926 *buffer = 0;
1927 break;
1928 }
1929 } /* End of new line handling */
1930 } /* End of (ptr = str; *ptr && (pos < BIG_BUFFER_SIZE - 8); ptr++) */
1931
1932 /* Reset all attributes to zero */
1933 a.bold = a.underline = a.reverse = a.blink = a.altchar = 0;
1934 a.color_fg = a.color_bg = a.fg_color = a.bg_color = 0;
1935 pos += display_attributes(buffer + pos, &a);
1936 buffer[pos] = '\0';
1937 if (*buffer)
1938 malloc_strcpy((char **)&(output[line++]),buffer);
1939
1940 recursion--;
1941 new_free(&output[line]);
1942 new_free(&cont_free);
1943 if (free_me_later)
1944 new_free(&free_me_later);
1945 *lused = line - 1;
1946 return output;
1947 }
1948
1949 /*
1950 * rite: This is the primary display wrapper to the 'output_with_count'
1951 * function. This function is called whenever a line of text is to be
1952 * displayed to an irc window. It is assumed that the cursor has been
1953 * placed in the appropriate position before this function is called.
1954 *
1955 * This function will "scroll" the target window. Note that "scrolling"
1956 * is both a logical and physical act. The window needs to be told that
1957 * a line is going to be output, and so it needs to be able to adjust its
1958 * top_of_display pointer; the hardware terminal also needs to be scrolled
1959 * so that there is room to put the new text. scroll_window() handles both
1960 * of these tasks for us.
1961 *
1962 * output_with_count() actually calls putchar_x() for each character in
1963 * the string, doing the physical output. It also emits any attribute
1964 * markers that are in the string. It does do a clear-to-line, but it does
1965 * NOT move the cursor away from the end of the line. We do that after it
1966 * has returned.
1967 *
1968 * This function is used by both irciiwin_display, and irciiwin_repaint.
1969 * Dont ever 'fold' it in anywhere.
1970 *
1971 * The arguments:
1972 * window - The target window for the output
1973 * str - What is to be outputted
1974 */
rite(Window * window,const unsigned char * str)1975 static int rite (Window *window, const unsigned char *str)
1976 {
1977 output_screen = window->screen;
1978 scroll_window(window);
1979
1980 if (window->screen && window->display_size)
1981 output_with_count(str, 1, foreground);
1982
1983 window->cursor++;
1984 return 0;
1985 }
1986
1987 /*
1988 * This is the main physical output routine. In its most obvious
1989 * use, 'cleareol' and 'output' is 1, and it outputs 'str' to the main
1990 * display (controlled by output_screen), outputting any attribute markers
1991 * that it finds along the way. The return value is the number of physical
1992 * printable characters output. However, if 'output' is 0, then no actual
1993 * output is performed, but the counting still takes place. If 'clreol'
1994 * is 0, then the rest of the line is not cleared after 'str' has been
1995 * completely output. If 'output' is 0, then clreol is ignored.
1996 *
1997 * In some cases, you may want to output in multiple calls, and "all_off"
1998 * should be set to 1 when you're all done with the end of the
1999 * If 'output' is 1 and 'all_off' is 1, do a term_all_off() when the output
2000 * is done. If 'all_off' is 0, then don't do an all_off, because
2001 */
output_with_count(const unsigned char * str1,int clreol,int output)2002 int output_with_count (const unsigned char *str1, int clreol, int output)
2003 {
2004 int beep = 0,
2005 out = 0;
2006 Attribute a;
2007 const unsigned char *str;
2008
2009 /* Reset all attributes to zero */
2010 a.bold = a.underline = a.reverse = a.blink = a.altchar = 0;
2011 a.color_fg = a.color_bg = a.fg_color = a.bg_color = 0;
2012
2013 for (str = str1; str && *str; str++)
2014 {
2015 switch (*str)
2016 {
2017 /* Attribute marker */
2018 case '\006':
2019 {
2020 if (read_attributes(str, &a))
2021 break;
2022 if (output)
2023 term_attribute(&a);
2024 str += 4;
2025 break;
2026 }
2027
2028 /* Terminal beep */
2029 case '\007':
2030 {
2031 beep++;
2032 break;
2033 }
2034
2035 /* Dont ask */
2036 case '\f':
2037 {
2038 if (output)
2039 {
2040 a.reverse = !a.reverse;
2041 term_attribute(&a);
2042 putchar_x('f');
2043 a.reverse = !a.reverse;
2044 term_attribute(&a);
2045 }
2046 out++;
2047 break;
2048 }
2049
2050 /* Non-destructive space */
2051 case ND_SPACE:
2052 {
2053 if (output)
2054 term_cursor_right();
2055 out++; /* Ooops */
2056 break;
2057 }
2058
2059 /* Any other printable character */
2060 default:
2061 {
2062 /*
2063 * Note that 'putchar_x()' is safe here
2064 * because normalize_string() has already
2065 * removed all of the nasty stuff that could
2066 * end up getting here. And for those things
2067 * that are nasty that get here, its probably
2068 * because the user specifically asked for it.
2069 */
2070 if (output)
2071 putchar_x(*str);
2072 out++;
2073 break;
2074 }
2075 }
2076 }
2077
2078 if (output)
2079 {
2080 if (beep)
2081 term_beep();
2082 if (clreol)
2083 term_clear_to_eol();
2084 term_all_off(); /* Clean up after ourselves! */
2085 }
2086
2087 return out;
2088 }
2089
2090
2091 /*
2092 * add_to_screen: This adds the given null terminated buffer to the screen.
2093 * That is, it routes the line to the appropriate window. It also handles
2094 * /redirect handling.
2095 */
add_to_screen(const unsigned char * buffer)2096 void add_to_screen (const unsigned char *buffer)
2097 {
2098 Window *tmp = NULL;
2099 int winref;
2100
2101 /*
2102 * Just paranoia.
2103 */
2104 if (!current_window)
2105 {
2106 puts(buffer);
2107 return;
2108 }
2109
2110 if (dumb_mode)
2111 {
2112 add_to_lastlog(current_window, buffer);
2113 if (privileged_output ||
2114 do_hook(WINDOW_LIST, "%u %s", current_window->refnum, buffer))
2115 puts(buffer);
2116 fflush(stdout);
2117 return;
2118 }
2119
2120 /* All windows MUST be "current" before output can occur */
2121 update_all_windows();
2122
2123 /*
2124 * The highest priority is if we have explicitly stated what
2125 * window we want this output to go to.
2126 */
2127 if (to_window)
2128 {
2129 add_to_window(to_window, buffer);
2130 return;
2131 }
2132
2133 /*
2134 * The next priority is "LOG_CURRENT" which is the "default"
2135 * level for all non-routed output. That is meant to ensure that
2136 * any extraneous error messages goes to a window where the user
2137 * will see it. All specific output (e.g. incoming server stuff)
2138 * is routed through one of the LOG_* levels, which is handled
2139 * below.
2140 */
2141 else if (who_level == LOG_CURRENT &&
2142 ((winref = get_winref_by_servref(from_server)) > -1) &&
2143 (tmp = get_window_by_refnum(winref)))
2144 {
2145 add_to_window(tmp, buffer);
2146 return;
2147 }
2148
2149 /*
2150 * Next priority is if the output is targeted at a certain
2151 * user or channel (used for /window bind or /window nick targets)
2152 */
2153 else if (who_from)
2154 {
2155 tmp = NULL;
2156 while (traverse_all_windows(&tmp))
2157 {
2158 const char *chan = get_echannel_by_refnum(tmp->refnum);
2159
2160 /*
2161 * Check for /WINDOW CHANNELs that apply.
2162 * (Any current channel will do)
2163 */
2164 if (chan && !my_stricmp(who_from, chan))
2165 {
2166 if (tmp->server == from_server)
2167 {
2168 add_to_window(tmp, buffer);
2169 return;
2170 }
2171 }
2172
2173 /*
2174 * Check for /WINDOW QUERYs that apply.
2175 */
2176 if (tmp->query_nick &&
2177 ( ((who_level == LOG_MSG || who_level == LOG_NOTICE
2178 || who_level == LOG_DCC || who_level == LOG_CTCP
2179 || who_level == LOG_ACTION)
2180 && !my_stricmp(who_from, tmp->query_nick)
2181 && from_server == tmp->server)
2182 || ((who_level == LOG_DCC || who_level == LOG_CTCP
2183 || who_level == LOG_ACTION)
2184 && *tmp->query_nick == '='
2185 && !my_stricmp(who_from, tmp->query_nick + 1))
2186 || ((who_level == LOG_DCC || who_level == LOG_CTCP
2187 || who_level == LOG_ACTION)
2188 && *tmp->query_nick == '='
2189 && !my_stricmp(who_from, tmp->query_nick))))
2190 {
2191 add_to_window(tmp, buffer);
2192 return;
2193 }
2194 }
2195
2196 tmp = NULL;
2197 while (traverse_all_windows(&tmp))
2198 {
2199 /*
2200 * Check for /WINDOW NICKs that apply
2201 */
2202 if (from_server == tmp->server)
2203 {
2204 if (find_in_list((List **)&(tmp->nicks),
2205 who_from, !USE_WILDCARDS))
2206 {
2207 add_to_window(tmp, buffer);
2208 return;
2209 }
2210 }
2211 }
2212
2213 /*
2214 * we'd better check to see if this should go to a
2215 * specific window (i dont agree with this, though)
2216 */
2217 if (from_server != NOSERV && is_channel(who_from))
2218 {
2219 if ((tmp = get_window_by_refnum(get_channel_winref(who_from, from_server))))
2220 {
2221 add_to_window(tmp, buffer);
2222 return;
2223 }
2224 }
2225 }
2226
2227 /*
2228 * Check to see if this level should go to current window
2229 */
2230 if ((current_window_level & who_level) &&
2231 ((winref = get_winref_by_servref(from_server)) > -1) &&
2232 (tmp = get_window_by_refnum(winref)))
2233 {
2234 add_to_window(tmp, buffer);
2235 return;
2236 }
2237
2238 /*
2239 * Check to see if any window can claim this level
2240 */
2241 tmp = NULL;
2242 while (traverse_all_windows(&tmp))
2243 {
2244 /*
2245 * Check for /WINDOW LEVELs that apply
2246 */
2247 if (who_level == LOG_DCC && tmp->window_level & who_level)
2248 {
2249 add_to_window(tmp, buffer);
2250 return;
2251 }
2252 if (((from_server == tmp->server) || (from_server == NOSERV)) &&
2253 (who_level & tmp->window_level))
2254 {
2255 add_to_window(tmp, buffer);
2256 return;
2257 }
2258 }
2259
2260 /*
2261 * If all else fails, if the current window is connected to the
2262 * given server, use the current window.
2263 */
2264 if (from_server == current_window->server)
2265 {
2266 add_to_window(current_window, buffer);
2267 return;
2268 }
2269
2270 /*
2271 * And if that fails, look for ANY window that is bound to the
2272 * given server (this never fails if we're connected.)
2273 */
2274 tmp = NULL;
2275 while (traverse_all_windows(&tmp))
2276 {
2277 if (tmp->server == from_server)
2278 {
2279 add_to_window(tmp, buffer);
2280 return;
2281 }
2282 }
2283
2284 /*
2285 * No window found for a server is usually because we're
2286 * disconnected or not yet connected.
2287 */
2288 add_to_window(current_window, buffer);
2289 return;
2290 }
2291
2292 /*
2293 * add_to_window: Given a window and a line to display, this handles all
2294 * of the window-level stuff like the logfile, the lastlog, splitting
2295 * the line up into rows, adding it to the display (scrollback) buffer, and
2296 * if we're invisible and the user wants notification, we handle that too.
2297 *
2298 * add_to_display_list() handles the *composure* of the buffer that backs the
2299 * screen, handling HOLD_MODE, trimming the scrollback buffer if it gets too
2300 * big, scrolling the window and moving the top_of_window pointer as neccesary.
2301 * It also tells us if we should display to the screen or not.
2302 *
2303 * rite() handles the *appearance* of the display, writing to the screen as
2304 * neccesary.
2305 */
add_to_window(Window * window,const unsigned char * str)2306 static void add_to_window (Window *window, const unsigned char *str)
2307 {
2308 char * pend;
2309 char * strval;
2310 char * free_me = NULL;
2311
2312 if (get_server_redirect(window->server))
2313 if (redirect_text(window->server,
2314 get_server_redirect(window->server),
2315 str, NULL, 0))
2316 return;
2317
2318 if (!privileged_output)
2319 {
2320 static int recursion = 0;
2321
2322 if (!do_hook(WINDOW_LIST, "%u %s", window->refnum, str))
2323 return;
2324
2325 /*
2326 * If output rewriting causes more output, (such as a stray error
2327 * message) allow a few levels of nesting [just to be kind], but
2328 * cut the recursion off at its knees at 5 levels. This is an
2329 * entirely arbitrary value. Change it if you wish.
2330 * (Recursion detection by larne in epic4-2.1.3)
2331 */
2332 recursion++;
2333 if (recursion < 5)
2334 {
2335 if ((pend = get_string_var(OUTPUT_REWRITE_VAR)))
2336 {
2337 char *prepend_exp;
2338 char argstuff[10240];
2339 int args_flag;
2340
2341 /* First, create the $* list for the expando */
2342 snprintf(argstuff, 10240, "%u %s",
2343 window->refnum, str);
2344
2345 /* Now expand the expando with the above $* */
2346 prepend_exp = expand_alias(pend, argstuff,
2347 &args_flag, NULL);
2348
2349 str = prepend_exp;
2350 free_me = prepend_exp;
2351 }
2352 }
2353 recursion--;
2354
2355 /* Normalize the line of output */
2356 strval = normalize_string(str, 0);
2357 }
2358 else
2359 strval = malloc_strdup(str);
2360
2361 /* Pass it off to the window */
2362 window_disp(window, strval, str);
2363 new_free(&strval);
2364
2365 /*
2366 * This used to be in rite(), but since rite() is a general
2367 * purpose function, and this stuff really is only intended
2368 * to hook on "new" output, it really makes more sense to do
2369 * this here. This also avoids the terrible problem of
2370 * recursive calls to split_up_line, which are bad.
2371 */
2372 if (!window->screen)
2373 {
2374 /*
2375 * This is for archon -- he wanted a way to have
2376 * a hidden window always beep, even if BEEP is off.
2377 * XXX -- str has already been freed here! ACK!
2378 */
2379 if (window->beep_always && strchr(str, '\007'))
2380 {
2381 Window *old_to_window;
2382 term_beep();
2383 old_to_window = to_window;
2384 to_window = current_window;
2385 say("Beep in window %d", window->refnum);
2386 to_window = old_to_window;
2387 }
2388
2389 /*
2390 * Tell some visible window about our problems
2391 * if the user wants us to.
2392 */
2393 if (!(window->miscflags & WINDOW_NOTIFIED) &&
2394 who_level & window->notify_level)
2395 {
2396 window->miscflags |= WINDOW_NOTIFIED;
2397 if (window->miscflags & WINDOW_NOTIFY)
2398 {
2399 Window *old_to_window;
2400 int lastlog_level;
2401
2402 lastlog_level = set_lastlog_msg_level(LOG_CRAP);
2403 old_to_window = to_window;
2404 to_window = current_window;
2405 say("Activity in window %d",
2406 window->refnum);
2407 to_window = old_to_window;
2408 set_lastlog_msg_level(lastlog_level);
2409 }
2410 update_all_status();
2411 }
2412 }
2413 if (free_me)
2414 new_free(&free_me);
2415
2416 cursor_in_display(window);
2417 }
2418
2419 /*
2420 * The mid-level shim for output to all ircII type windows.
2421 *
2422 * By this point, the logical line 'str' is in the state it is going to be
2423 * put onto the screen. We need to put it in our lastlog [XXX Should that
2424 * be done by the front end?] and process it through the display chopper
2425 * (prepare_display) which slices and dices the logical line into manageable
2426 * chunks, suitable for putting onto the display. We then call our back end
2427 * function to do the actual physical output.
2428 */
window_disp(Window * window,const unsigned char * str,const unsigned char * orig_str)2429 static void window_disp (Window *window, const unsigned char *str, const unsigned char *orig_str)
2430 {
2431 u_char ** lines;
2432 int cols;
2433 int numl = 0;
2434
2435 add_to_log(window->log_fp, window->refnum, orig_str, 0, NULL);
2436 add_to_logs(window->refnum, from_server, who_from, who_level, orig_str);
2437 add_to_lastlog(window, orig_str);
2438
2439 if (window->screen)
2440 cols = window->screen->co - 1; /* XXX HERE XXX */
2441 else
2442 cols = window->columns - 1;
2443
2444 /* Suppress status updates while we're outputting. */
2445 for (lines = prepare_display(str, cols, &numl, 0); *lines; lines++)
2446 {
2447 if (add_to_scrollback(window, *lines))
2448 if (ok_to_output(window))
2449 rite(window, *lines);
2450 }
2451
2452 check_window_cursor(window);
2453 trim_scrollback(window);
2454 cursor_to_input();
2455 }
2456
ok_to_output(Window * window)2457 static int ok_to_output (Window *window)
2458 {
2459 /*
2460 * Output is ok as long as the three top of displays all are
2461 * within a screenful of the insertion point!
2462 */
2463 if (window->scrollback_top_of_display)
2464 {
2465 if (window->scrollback_distance_from_display_ip >
2466 window->display_size)
2467 return 0; /* Definitely no output here */
2468 }
2469
2470 if (window->holding_top_of_display)
2471 {
2472 if (window->holding_distance_from_display_ip >
2473 window->display_size)
2474 return 0; /* Definitely no output here */
2475 }
2476
2477 return 1; /* Output is authorized */
2478 }
2479
2480 /*
2481 * scroll_window: Given a window, this is responsible for making sure that
2482 * the cursor is placed onto the "next" line. If the window is full, then
2483 * it will scroll the window as neccesary. The cursor is always set to the
2484 * correct place when this returns.
2485 */
scroll_window(Window * window)2486 static void scroll_window (Window *window)
2487 {
2488 if (dumb_mode)
2489 return;
2490
2491 if (window->cursor > window->display_size)
2492 panic("Window [%d]'s cursor [%d] is off the display [%d]",
2493 window->refnum, window->cursor, window->display_size);
2494
2495 /*
2496 * If the cursor is beyond the window then we should probably
2497 * look into scrolling.
2498 */
2499 if (window->cursor == window->display_size)
2500 {
2501 int scroll;
2502
2503 /*
2504 * If we ever need to scroll a window that is in scrollback
2505 * or in hold_mode, then that means either display_window isnt
2506 * doing its job or something else is completely broken.
2507 * Probably shouldnt be fatal, but i want to trap these.
2508 */
2509 if (window->holding_distance_from_display_ip > window->display_size)
2510 panic("Can't output to window [%d] because it is holding stuff: [%d] [%d]", window->refnum, window->holding_distance_from_display_ip, window->display_size);
2511 if (window->scrollback_distance_from_display_ip > window->display_size)
2512 panic("Can't output to window [%d] because it is scrolling back: [%d] [%d]", window->refnum, window->scrollback_distance_from_display_ip, window->display_size);
2513
2514
2515 /* Scroll by no less than 1 line */
2516 if ((scroll = get_int_var(SCROLL_LINES_VAR)) <= 0)
2517 scroll = 1;
2518
2519 /* Adjust the top of the physical display */
2520 if (window->screen && foreground && window->display_size)
2521 {
2522 term_scroll(window->top,
2523 window->top + window->cursor - 1,
2524 scroll);
2525 }
2526
2527 /* Adjust the cursor */
2528 window->cursor -= scroll;
2529 }
2530
2531 /*
2532 * Move to the new line and wipe it
2533 */
2534 if (window->screen && window->display_size)
2535 {
2536 window->screen->cursor_window = window;
2537 term_move_cursor(0, window->top + window->cursor);
2538 term_clear_to_eol();
2539 cursor_in_display(window);
2540 }
2541 }
2542
2543 /* * * * * CURSORS * * * * * */
2544 /*
2545 * cursor_not_in_display: This forces the cursor out of the display by
2546 * setting the cursor window to null. This doesn't actually change the
2547 * physical position of the cursor, but it will force rite() to reset the
2548 * cursor upon its next call
2549 */
cursor_not_in_display(Screen * s)2550 void cursor_not_in_display (Screen *s)
2551 {
2552 if (!s)
2553 s = output_screen;
2554 if (s->cursor_window)
2555 s->cursor_window = NULL;
2556 }
2557
2558 /*
2559 * cursor_in_display: this forces the cursor_window to be the
2560 * current_screen->current_window.
2561 * It is actually only used in hold.c to trick the system into thinking the
2562 * cursor is in a window, thus letting the input updating routines move the
2563 * cursor down to the input line. Dumb dumb dumb
2564 */
cursor_in_display(Window * w)2565 void cursor_in_display (Window *w)
2566 {
2567 if (!w)
2568 w = current_window;
2569 if (w->screen)
2570 w->screen->cursor_window = w;
2571 }
2572
2573 /*
2574 * is_cursor_in_display: returns true if the cursor is in one of the windows
2575 * (cursor_window is not null), false otherwise
2576 */
is_cursor_in_display(Screen * screen)2577 int is_cursor_in_display (Screen *screen)
2578 {
2579 if (!screen && current_window->screen)
2580 screen = current_window->screen;
2581
2582 return (screen->cursor_window ? 1 : 0);
2583 }
2584
2585
2586 /* * * * * * * SCREEN UDPATING AND RESIZING * * * * * * * * */
2587 /*
2588 * repaint_window_body: redraw the entire window's scrollable region
2589 * The old logic for doing a partial repaint has been removed with prejudice.
2590 */
repaint_window_body(Window * window)2591 void repaint_window_body (Window *window)
2592 {
2593 Display *curr_line;
2594 int count;
2595
2596 if (!window)
2597 window = current_window;
2598
2599 if (dumb_mode || !window->screen)
2600 return;
2601
2602 global_beep_ok = 0; /* Suppress beeps */
2603
2604 if (window->scrollback_distance_from_display_ip > window->holding_distance_from_display_ip)
2605 {
2606 if (window->scrolling_distance_from_display_ip >= window->scrollback_distance_from_display_ip)
2607 curr_line = window->scrolling_top_of_display;
2608 else
2609 curr_line = window->scrollback_top_of_display;
2610 }
2611 else
2612 {
2613 if (window->scrolling_distance_from_display_ip >= window->holding_distance_from_display_ip)
2614 curr_line = window->scrolling_top_of_display;
2615 else
2616 curr_line = window->holding_top_of_display;
2617 }
2618
2619 window->cursor = 0;
2620 for (count = 0; count < window->display_size; count++)
2621 {
2622 rite(window, curr_line->line);
2623
2624 /*
2625 * Clean off the rest of this window.
2626 */
2627 if (curr_line == window->display_ip)
2628 {
2629 window->cursor--; /* Bumped by rite */
2630 for (; count < window->display_size; count++)
2631 {
2632 term_clear_to_eol();
2633 term_newline();
2634 }
2635 break;
2636 }
2637
2638 curr_line = curr_line->next;
2639 }
2640
2641 global_beep_ok = 1; /* Suppress beeps */
2642 }
2643
2644
2645 /* * * * * * * * * * * * * * SCREEN MANAGEMENT * * * * * * * * * * * * */
2646 /*
2647 * create_new_screen creates a new screen structure. with the help of
2648 * this structure we maintain ircII windows that cross screen window
2649 * boundaries.
2650 */
create_new_screen(void)2651 Screen *create_new_screen (void)
2652 {
2653 Screen *new_s = NULL, *list;
2654 static int refnumber = 0;
2655
2656 for (list = screen_list; list; list = list->next)
2657 {
2658 if (!list->alive)
2659 {
2660 new_s = list;
2661 break;
2662 }
2663
2664 if (!list->next)
2665 break; /* XXXX */
2666 }
2667
2668 if (!new_s)
2669 {
2670 new_s = (Screen *)new_malloc(sizeof(Screen));
2671 new_s->screennum = ++refnumber;
2672 new_s->next = NULL;
2673 if (list)
2674 list->next = new_s;
2675 else
2676 screen_list = new_s;
2677 }
2678
2679 new_s->last_window_refnum = 1;
2680 new_s->window_list = NULL;
2681 new_s->window_list_end = NULL;
2682 new_s->cursor_window = NULL;
2683 new_s->current_window = NULL;
2684 new_s->visible_windows = 0;
2685 new_s->window_stack = NULL;
2686 new_s->last_press.tv_sec = new_s->last_press.tv_usec = 0;
2687 new_s->last_key = NULL;
2688 new_s->quote_hit = 0;
2689 new_s->fdout = 1;
2690 new_s->fpout = stdout;
2691 new_s->fdin = 0;
2692 if (use_input)
2693 new_open(0);
2694 new_s->fpin = stdin;
2695 new_s->control = -1;
2696 new_s->wserv_version = 0;
2697 new_s->alive = 1;
2698 new_s->promptlist = NULL;
2699 new_s->tty_name = (char *) 0;
2700 new_s->li = current_term->TI_lines;
2701 new_s->co = current_term->TI_cols;
2702 new_s->old_li = 0;
2703 new_s->old_co = 0;
2704
2705 new_s->buffer_pos = new_s->buffer_min_pos = 0;
2706 new_s->input_buffer[0] = '\0';
2707 new_s->input_cursor = 0;
2708 new_s->input_visible = 0;
2709 new_s->input_start_zone = 0;
2710 new_s->input_end_zone = 0;
2711 new_s->input_prompt = NULL;
2712 new_s->input_prompt_len = 0;
2713 new_s->input_prompt_malloc = 0;
2714 new_s->input_line = 23;
2715
2716 last_input_screen = new_s;
2717
2718 if (!main_screen)
2719 main_screen = new_s;
2720
2721 init_input();
2722 return new_s;
2723 }
2724
2725
2726 #ifdef WINDOW_CREATE
create_additional_screen(void)2727 Window *create_additional_screen (void)
2728 {
2729 Window *win;
2730 Screen *oldscreen, *new_s;
2731 char *displayvar,
2732 *termvar;
2733 int screen_type = ST_NOTHING;
2734 ISA local_sockaddr;
2735 ISA new_socket;
2736 int new_cmd;
2737 fd_set fd_read;
2738 Timeval timeout;
2739 pid_t child;
2740 unsigned short port;
2741 int new_sock_size;
2742 char * wserv_path;
2743
2744 if (!use_input)
2745 return NULL;
2746
2747 if (!(wserv_path = get_string_var(WSERV_PATH_VAR)))
2748 {
2749 say("You need to /SET WSERV_PATH before using /WINDOW CREATE");
2750 return NULL;
2751 }
2752
2753 /*
2754 * Environment variable STY has to be set for screen to work.. so it is
2755 * the best way to check screen.. regardless of what TERM is, the
2756 * execpl() for screen won't just open a new window if STY isn't set,
2757 * it will open a new screen process, and run the wserv in its first
2758 * window, not what we want... -phone
2759 */
2760 if (getenv("STY") && getenv("DISPLAY"))
2761 {
2762 char *p = get_string_var(WSERV_TYPE_VAR);
2763 if (p && !my_stricmp(p, "SCREEN"))
2764 screen_type = ST_SCREEN;
2765 else if (p && !my_stricmp(p, "XTERM"))
2766 screen_type = ST_XTERM;
2767 else
2768 screen_type = ST_SCREEN; /* Sucks to be you */
2769 }
2770 else if (getenv("STY"))
2771 screen_type = ST_SCREEN;
2772 else if (getenv("DISPLAY") && getenv("TERM"))
2773 screen_type = ST_XTERM;
2774 else
2775 {
2776 say("I don't know how to create new windows for this terminal");
2777 return NULL;
2778 }
2779
2780 if (screen_type == ST_SCREEN)
2781 say("Opening new screen...");
2782 else if (screen_type == ST_XTERM)
2783 {
2784 displayvar = getenv("DISPLAY");
2785 termvar = getenv("TERM");
2786 say("Opening new window...");
2787 }
2788 else
2789 panic("Opening new wound");
2790
2791 local_sockaddr.sin_family = AF_INET;
2792 #ifndef INADDR_LOOPBACK
2793 #define INADDR_LOOPBACK 0x7f000001
2794 #endif
2795 local_sockaddr.sin_addr.s_addr = htonl((INADDR_ANY));
2796 local_sockaddr.sin_port = 0;
2797
2798 if ((new_cmd = client_bind((SA *)&local_sockaddr, sizeof(local_sockaddr))) < 0)
2799 {
2800 yell("Couldnt establish server side -- error [%d] [%s]",
2801 new_cmd, my_strerror(new_cmd, errno));
2802 return NULL;
2803 }
2804 port = ntohs(local_sockaddr.sin_port);
2805
2806 oldscreen = current_window->screen;
2807 new_s = create_new_screen();
2808
2809 /*
2810 * At this point, doing a say() or yell() or anything else that would
2811 * output to the screen will cause a refresh of the status bar and
2812 * input line. new_s->current_window is NULL after the above line,
2813 * so any attempt to reference $C or $T will be to NULL pointers,
2814 * which will cause a crash. For various reasons, we can't fire up
2815 * a new window this early, so its just easier to make sure we don't
2816 * output anything until kill_screen() or new_window() is called first.
2817 * You have been warned!
2818 */
2819 switch ((child = fork()))
2820 {
2821 case -1:
2822 {
2823 kill_screen(new_s);
2824 say("I couldnt fire up a new wserv process");
2825 break;
2826 }
2827
2828 case 0:
2829 {
2830 char *opts;
2831 const char *xterm;
2832 char *args[64];
2833 char **args_ptr = args;
2834 char geom[32];
2835 int i;
2836
2837 setuid(getuid());
2838 setgid(getgid());
2839 setsid();
2840
2841 /*
2842 * Make sure that no inhereted file descriptors
2843 * are left over past the exec. xterm will reopen
2844 * any fd's that it is interested in.
2845 * (Start at three sb kanan).
2846 */
2847 for (i = 3; i < 256; i++)
2848 close(i);
2849
2850 /*
2851 * Try to restore some sanity to the signal
2852 * handlers, since theyre not really appropriate here
2853 */
2854 my_signal(SIGINT, SIG_IGN);
2855 my_signal(SIGSEGV, SIG_DFL);
2856 my_signal(SIGBUS, SIG_DFL);
2857 my_signal(SIGABRT, SIG_DFL);
2858
2859 if (screen_type == ST_SCREEN)
2860 {
2861 opts = malloc_strdup(get_string_var(SCREEN_OPTIONS_VAR));
2862 *args_ptr++ = malloc_strdup("screen");
2863 while (opts && *opts)
2864 *args_ptr++ = malloc_strdup(new_next_arg(opts, &opts));
2865 }
2866 else if (screen_type == ST_XTERM)
2867 {
2868 snprintf(geom, 31, "%dx%d",
2869 oldscreen->co + 1,
2870 oldscreen->li);
2871
2872 opts = malloc_strdup(get_string_var(XTERM_OPTIONS_VAR));
2873 if (!(xterm = getenv("XTERM")))
2874 if (!(xterm = get_string_var(XTERM_VAR)))
2875 xterm = "xterm";
2876
2877 *args_ptr++ = malloc_strdup(xterm);
2878 *args_ptr++ = malloc_strdup("-geometry");
2879 *args_ptr++ = malloc_strdup(geom);
2880 while (opts && *opts)
2881 *args_ptr++ = malloc_strdup(new_next_arg(opts, &opts));
2882 *args_ptr++ = malloc_strdup("-e");
2883 }
2884
2885 *args_ptr++ = malloc_strdup(wserv_path);
2886 *args_ptr++ = malloc_strdup("localhost");
2887 *args_ptr++ = malloc_strdup(ltoa((long)port));
2888 *args_ptr++ = NULL;
2889
2890 execvp(args[0], args);
2891 _exit(0);
2892 }
2893 }
2894
2895 /* All the rest of this is the parent.... */
2896 new_sock_size = sizeof(new_socket);
2897 FD_ZERO(&fd_read);
2898 FD_SET(new_cmd, &fd_read);
2899 timeout.tv_sec = (time_t) 10;
2900 timeout.tv_usec = 0;
2901
2902 /*
2903 * This infinite loop sb kanan to allow us to trap transitory
2904 * error signals
2905 */
2906 for (;;)
2907
2908 /*
2909 * You need to kill_screen(new_s) before you do say() or yell()
2910 * if you know what is good for you...
2911 */
2912 switch (select(new_cmd + 1, &fd_read, NULL, NULL, &timeout))
2913 {
2914 case -1:
2915 {
2916 if ((errno == EINTR) || (errno == EAGAIN))
2917 continue;
2918 /* FALLTHROUGH */
2919 }
2920 case 0:
2921 {
2922 int old_errno = errno;
2923 int errnod = get_child_exit(child);
2924
2925 close(new_cmd);
2926 kill_screen(new_s);
2927 kill(child, SIGKILL);
2928 if (new_s->fdin != 0)
2929 {
2930 say("The wserv only connected once -- it's probably "
2931 "an old, incompatable version.");
2932 }
2933
2934 yell("child %s with %d", (errnod < 1) ? "signaled" : "exited",
2935 (errnod < 1) ? -errnod : errnod);
2936 yell("Errno is %d", old_errno);
2937 return NULL;
2938 }
2939 default:
2940 {
2941 if (new_s->fdin == 0)
2942 {
2943 new_s->fdin = accept(new_cmd, (SA *)&new_socket,
2944 &new_sock_size);
2945 if ((new_s->fdout = new_s->fdin) < 0)
2946 {
2947 close(new_cmd);
2948 kill_screen(new_s);
2949 yell("Couldn't establish data connection "
2950 "to new screen");
2951 return NULL;
2952 }
2953 new_open(new_s->fdin);
2954 new_s->fpin = new_s->fpout = fdopen(new_s->fdin, "r+");
2955 continue;
2956 }
2957 else
2958 {
2959 new_s->control = accept(new_cmd, (SA *)&new_socket,
2960 &new_sock_size);
2961 close(new_cmd);
2962 if (new_s->control < 0)
2963 {
2964 kill_screen(new_s);
2965 yell("Couldn't establish control connection "
2966 "to new screen");
2967 return NULL;
2968 }
2969
2970 new_open(new_s->control);
2971
2972 if (!(win = new_window(new_s)))
2973 panic("WINDOW is NULL and it shouldnt be!");
2974 return win;
2975 }
2976 }
2977 }
2978 return NULL;
2979 }
2980
2981 /* Old screens never die. They just fade away. */
kill_screen(Screen * screen)2982 void kill_screen (Screen *screen)
2983 {
2984 Window *window;
2985
2986 if (!screen)
2987 {
2988 say("You may not kill the hidden screen.");
2989 return;
2990 }
2991 if (main_screen == screen)
2992 {
2993 say("You may not kill the main screen");
2994 return;
2995 }
2996 if (screen->fdin)
2997 {
2998 if (use_input)
2999 screen->fdin = new_close(screen->fdin);
3000 close(screen->fdout);
3001 close(screen->fdin);
3002 }
3003 if (screen->control)
3004 screen->control = new_close(screen->control);
3005 while ((window = screen->window_list))
3006 {
3007 screen->window_list = window->next;
3008 add_to_invisible_list(window);
3009 }
3010
3011 /* Take out some of the garbage left around */
3012 screen->current_window = NULL;
3013 screen->window_list = NULL;
3014 screen->window_list_end = NULL;
3015 screen->cursor_window = NULL;
3016 screen->last_window_refnum = -1;
3017 screen->visible_windows = 0;
3018 screen->window_stack = NULL;
3019 screen->fpin = NULL;
3020 screen->fpout = NULL;
3021 screen->fdin = -1;
3022 screen->fdout = -1;
3023 new_free(&screen->input_prompt);
3024
3025 /* Dont fool around. */
3026 if (last_input_screen == screen)
3027 last_input_screen = main_screen;
3028
3029 screen->alive = 0;
3030 make_window_current(NULL);
3031 say("The screen is now dead.");
3032 }
3033 #endif /* WINDOW_CREATE */
3034
3035
3036 /* * * * * * * * * * * * * USER INPUT HANDLER * * * * * * * * * * * */
do_screens(fd_set * rd,fd_set * wd)3037 void do_screens (fd_set *rd, fd_set *wd)
3038 {
3039 Screen *screen;
3040 char buffer[IO_BUFFER_SIZE + 1];
3041
3042 if (use_input)
3043 for (screen = screen_list; screen; screen = screen->next)
3044 {
3045 if (!screen->alive)
3046 continue;
3047
3048 #ifdef WINDOW_CREATE
3049 if (screen->control != -1 &&
3050 FD_ISSET(screen->control, rd)) /* Wserv control */
3051 {
3052 FD_CLR(screen->control, rd);
3053
3054 if (dgets(screen->control, buffer, IO_BUFFER_SIZE, 1, NULL) < 0)
3055 {
3056 kill_screen(screen);
3057 yell("Error from remote screen [%d].", dgets_errno);
3058 continue;
3059 }
3060
3061 if (!strncmp(buffer, "tty=", 4))
3062 malloc_strcpy(&screen->tty_name, buffer + 4);
3063 else if (!strncmp(buffer, "geom=", 5))
3064 {
3065 char *ptr;
3066 if ((ptr = strchr(buffer, ' ')))
3067 *ptr++ = 0;
3068 screen->li = atoi(buffer + 5);
3069 screen->co = atoi(ptr);
3070 refresh_a_screen(screen);
3071 }
3072 else if (!strncmp(buffer, "version=", 8))
3073 {
3074 int version;
3075 version = atoi(buffer + 8);
3076 if (version != CURRENT_WSERV_VERSION)
3077 {
3078 yell("WSERV version %d is incompatable with this binary",
3079 version);
3080 kill_screen(screen);
3081 }
3082 screen->wserv_version = version;
3083 }
3084 }
3085 #endif
3086
3087 if (FD_ISSET(screen->fdin, rd))
3088 {
3089 int server;
3090
3091 FD_CLR(screen->fdin, rd); /* No more! */
3092
3093 #ifdef WINDOW_CREATE
3094 if (screen != main_screen && screen->wserv_version == 0)
3095 {
3096 kill_screen(screen);
3097 yell("The WSERV used to create this new screen is too old.");
3098 return;
3099 }
3100 #endif
3101
3102 /*
3103 * This section of code handles all in put from
3104 * the terminal(s) connected to ircII. Perhaps the
3105 * idle time *shouldn't* be reset unless its not a
3106 * screen-fd that was closed..
3107 */
3108 get_time(&idle_time);
3109 if (cpu_saver)
3110 reset_system_timers();
3111
3112 server = from_server;
3113 last_input_screen = screen;
3114 output_screen = screen;
3115 make_window_current(screen->current_window);
3116 /*
3117 * In a multi-screen environment, it's possible for
3118 * the user to "switch" between windows connected to
3119 * the same server on multiple screens; this would
3120 * be the only place we would know about that. So
3121 * every time the user presses a key we have to set
3122 * the screen's current window to be that window's
3123 * server's current window. Right.
3124 * XXX Why do I know I'm going to regret this?
3125 */
3126 current_window->priority = current_window_priority++;
3127 from_server = current_window->server;
3128
3129 if (dumb_mode)
3130 {
3131 if (dgets(screen->fdin, buffer, IO_BUFFER_SIZE, 1, NULL) < 0)
3132 {
3133 say("IRCII exiting on EOF from stdin");
3134 irc_exit(1, "EPIC - EOF from stdin");
3135 }
3136
3137 if (strlen(buffer))
3138 buffer[strlen(buffer) - 1] = 0;
3139 if (get_int_var(INPUT_ALIASES_VAR))
3140 parse_line(NULL, buffer, empty_string, 1, 0);
3141 else
3142 parse_line(NULL, buffer, NULL, 1, 0);
3143 }
3144 else
3145 {
3146 char loc_buffer[BIG_BUFFER_SIZE + 1];
3147 int n, i;
3148
3149 /*
3150 * Read in from stdin.
3151 */
3152 if ((n = read(screen->fdin, loc_buffer, BIG_BUFFER_SIZE)) > 0)
3153 {
3154 for (i = 0; i < n; i++)
3155 edit_char(loc_buffer[i]);
3156 }
3157
3158 #ifdef WINDOW_CREATE
3159 /*
3160 * if the current screen isn't the main screen,
3161 * then the socket to the current screen must have
3162 * closed, so we call kill_screen() to handle
3163 * this - phone, jan 1993.
3164 * but not when we arent running windows - Fizzy, may 1993
3165 * if it is the main screen we got an EOF on, we exit..
3166 * closed tty -> chew cpu -> bad .. -phone, july 1993.
3167 */
3168 else if (screen != main_screen)
3169 kill_screen(screen);
3170 #endif
3171
3172 /*
3173 * If n == 0 or n == -1 at this point, then the read totally
3174 * died on us. This is almost without exception caused by
3175 * the ctty being revoke(2)d on us. 4.4BSD guarantees that a
3176 * revoke()d ctty will read an EOF, while i believe linux
3177 * fails with EBADF. In either case, a read failure on the
3178 * main screen is totaly fatal.
3179 */
3180 else
3181 irc_exit(1, "Hey! Where'd my controlling terminal go?");
3182
3183 }
3184 from_server = server;
3185 }
3186 }
3187 }
3188
3189
3190 /* * * * * * * * * INPUT PROMPTS * * * * * * * * * * */
3191 /*
3192 * add_wait_prompt: Given a prompt string, a function to call when
3193 * the prompt is entered.. some other data to pass to the function,
3194 * and the type of prompt.. either for a line, or a key, we add
3195 * this to the prompt_list for the current screen.. and set the
3196 * input prompt accordingly.
3197 *
3198 * XXXX - maybe this belongs in input.c? =)
3199 */
add_wait_prompt(const char * prompt,void (* func)(char *,char *),char * data,int type,int echo)3200 void add_wait_prompt (const char *prompt, void (*func)(char *, char *), char *data, int type, int echo)
3201 {
3202 WaitPrompt **AddLoc,
3203 *New;
3204
3205 New = (WaitPrompt *) new_malloc(sizeof(WaitPrompt));
3206 New->prompt = malloc_strdup(prompt);
3207 New->data = malloc_strdup(data);
3208 New->type = type;
3209 New->echo = echo;
3210 New->func = func;
3211 New->next = NULL;
3212 for (AddLoc = ¤t_window->screen->promptlist; *AddLoc;
3213 AddLoc = &(*AddLoc)->next);
3214 *AddLoc = New;
3215 if (AddLoc == ¤t_window->screen->promptlist)
3216 change_input_prompt(1);
3217 }
3218
3219
3220 /* * * * * * * * * * * * * * * * * COLOR SUPPORT * * * * * * * * * * */
3221 /*
3222 * This parses out a ^C control sequence. Note that it is not acceptable
3223 * to simply slurp up all digits after a ^C sequence (either by calling
3224 * strtol(), or while (isdigit())), because people put ^C sequences right
3225 * before legit output with numbers (like the time in your status bar.)
3226 * Se we have to actually slurp up only those digits that comprise a legal
3227 * ^C code.
3228 */
skip_ctl_c_seq(const u_char * start,int * lhs,int * rhs)3229 ssize_t skip_ctl_c_seq (const u_char *start, int *lhs, int *rhs)
3230 {
3231 const u_char *after = start;
3232 u_char c1, c2;
3233 int * val;
3234 int lv1, rv1;
3235
3236 /*
3237 * For our sanity, just use a placeholder if the caller doesnt
3238 * care where the end of the ^C code is.
3239 */
3240 if (!lhs)
3241 lhs = &lv1;
3242 if (!rhs)
3243 rhs = &rv1;
3244
3245 *lhs = *rhs = -1;
3246
3247 /*
3248 * If we're passed a non ^C code, dont do anything.
3249 */
3250 if (*after != '\003')
3251 return 0;
3252
3253 /*
3254 * This is a one-or-two-time-through loop. We find the maximum
3255 * span that can compose a legit ^C sequence, then if the first
3256 * nonvalid character is a comma, we grab the rhs of the code.
3257 */
3258 val = lhs;
3259 for (;;)
3260 {
3261 /*
3262 * If its just a lonely old ^C, then its probably a terminator.
3263 * Just skip over it and go on.
3264 */
3265 after++;
3266 if (*after == 0)
3267 return (after - start);
3268
3269 /*
3270 * Check for the very special case of a definite terminator.
3271 * If the argument to ^C is -1, then we absolutely know that
3272 * this ends the code without starting a new one
3273 */
3274 if (after[0] == '-' && after[1] == '1')
3275 return (after + 2 - start);
3276
3277 /*
3278 * Further checks against a lonely old naked ^C.
3279 */
3280 if (!isdigit(after[0]) && after[0] != ',')
3281 return (after - start);
3282
3283
3284 /*
3285 * Code certainly cant have more than two chars in it
3286 */
3287 c1 = after[0];
3288 c2 = after[1];
3289
3290 /*
3291 * Our action depends on the char immediately after the ^C.
3292 */
3293 switch (c1)
3294 {
3295 /*
3296 * 0X -> 0X <stop> for all numeric X
3297 */
3298 case '0':
3299 after++;
3300 *val = c1 - '0';
3301 if (c2 >= '0' && c2 <= '9')
3302 {
3303 after++;
3304 *val = *val * 10 + (c2 - '0');
3305 }
3306 break;
3307
3308 /*
3309 * 1X -> 1 <stop> X if X >= '7'
3310 * 1X -> 1X <stop> if X < '7'
3311 */
3312 case '1':
3313 after++;
3314 *val = c1 - '0';
3315 if (c2 >= '0' && c2 < '7')
3316 {
3317 after++;
3318 *val = *val * 10 + (c2 - '0');
3319 }
3320 break;
3321
3322 /*
3323 * 3X -> 3 <stop> X if X >= '8'
3324 * 3X -> 3X <stop> if X < '8'
3325 * (Same for 4X and 5X)
3326 */
3327 case '3':
3328 case '4':
3329 after++;
3330 *val = c1 - '0';
3331 if (c2 >= '0' && c2 < '8')
3332 {
3333 after++;
3334 *val = *val * 10 + (c2 - '0');
3335 }
3336 break;
3337
3338 case '5':
3339 after++;
3340 *val = c1 - '0';
3341 if (c2 >= '0' && c2 < '9')
3342 {
3343 after++;
3344 *val = *val * 10 + (c2 - '0');
3345 }
3346 break;
3347
3348 /*
3349 * Y -> Y <stop> for any other numeric Y.
3350 */
3351 case '2':
3352 case '6':
3353 case '7':
3354 case '8':
3355 case '9':
3356 *val = (c1 - '0');
3357 after++;
3358 break;
3359
3360 /*
3361 * Y -> <stop> Y for any other nonnumeric Y
3362 */
3363 default:
3364 break;
3365 }
3366
3367 if (val == lhs)
3368 {
3369 val = rhs;
3370 if (*after == ',')
3371 continue;
3372 }
3373 break;
3374 }
3375
3376 return (after - start);
3377 }
3378
3379