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 = &current_window->screen->promptlist; *AddLoc;
3213 			AddLoc = &(*AddLoc)->next);
3214 	*AddLoc = New;
3215 	if (AddLoc == &current_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