1 /*
2  * input.c: does the actual input line stuff... keeps the appropriate stuff
3  * on the input line, handles insert/delete of characters/words... the whole
4  * ball o wax
5  *
6  * Written By Michael Sandrof
7  *
8  * Copyright (c) 1990 Michael Sandrof.
9  * Copyright (c) 1991, 1992 Troy Rollo.
10  * Copyright (c) 1992-2003 Matthew R. Green.
11  * All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. The name of the author may not be used to endorse or promote products
22  *    derived from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * $Id: input.c,v 1.27 2008-10-04 17:37:46 f Exp $
37  */
38 
39 #include "irc.h"
40 
41 #ifdef HAVE_ICONV_H
42 # include <iconv.h>
43 #endif /* HAVE ICONV_H */
44 
45 #include "input.h"
46 #include "ircterm.h"
47 #include "alias.h"
48 #include "vars.h"
49 #include "ircaux.h"
50 #include "window.h"
51 #include "screen.h"
52 #include "exec.h"
53 #include "output.h"
54 #include "translat.h"
55 
56 #include "debug.h"
57 
58 /**************************** PATCHED by Flier ******************************/
59 #include "myvars.h"
60 
61 #ifdef WANTANSI
62 extern int CountAnsiInput _((char *, int));
63 #endif
64 extern void StripAnsi _((char *, char *, int));
65 extern NickList *tabnickcompl;
66 /****************************************************************************/
67 
68 #define WIDTH 10
69 
70 /* input_prompt: contains the current, unexpanded input prompt */
71 static	char	*input_prompt = (char *) 0;
72 
73 static struct mb_data mbdata;
74 static int mbdata_ok = 0;
75 
76 /**************************** PATCHED by Flier ******************************/
77 static int sz_width = 10; /* how far from right border to shift */
78 static int sz_prev_len = 0; /* how many prev. characters to show on shift */
79 
ResetNickCompletion()80 static void ResetNickCompletion() {
81     tabnickcompl = NULL;
82 }
83 /****************************************************************************/
84 
85 /* cursor_to_input: move the cursor to the input line, if not there already */
86 void
cursor_to_input(void)87 cursor_to_input(void)
88 {
89 	Screen *old_current_screen, *screen;
90 
91 	old_current_screen = current_screen;
92 	for (screen = screen_list; screen; screen = screen->next)
93 	{
94 		set_current_screen(screen);
95 		if (screen->alive && is_cursor_in_display())
96 		{
97 			term_move_cursor(screen->inputdata.cursor_x, screen->inputdata.cursor_y);
98 			Debug((3, "cursor_to_input: moving cursor to input for screen %d", screen->screennum));
99 			Debug((3, "- x %u; y %u", screen->inputdata.cursor_x, screen->inputdata.cursor_y));
100 			cursor_not_in_display();
101 			term_flush();
102 		}
103 	}
104 	set_current_screen(old_current_screen);
105 }
106 
107 static unsigned
input_do_calculate_width(unsigned unival,iconv_t conv)108 input_do_calculate_width(unsigned unival, iconv_t conv)
109 {
110 	if (!displayable_unival(unival, conv))
111 	{
112 		/* undisplayable values are printed raw */
113 		if (unival < 0x80)
114 			return 1;
115 		if (unival < 0x800)
116 			return 2;
117 		if (unival < 0x10000)
118 			return 3;
119 		return 4;
120 	}
121 	return calc_unival_width(unival);
122 }
123 
124 static void
input_do_set_cursor_pos(unsigned pos)125 input_do_set_cursor_pos(unsigned pos)
126 {
127 	cursor_to_input();
128 	current_screen->inputdata.buffer.pos = pos;
129 	update_input(UPDATE_JUST_CURSOR);
130 }
131 
132 static void
input_do_insert_raw(char * source)133 input_do_insert_raw(char* source)
134 {
135 	char* buf  = current_screen->inputdata.buffer.buf;
136 	unsigned pos = current_screen->inputdata.buffer.pos;
137 	unsigned max = sizeof(current_screen->inputdata.buffer.buf);
138 	unsigned inslen = strlen(source);
139 
140 	/* This function inserts the given substring of bytes
141 	 * to the input line at the current editing position.
142 	 * Cursor is moved to point to the end of the substring.
143 	 */
144 
145 	if (pos + inslen > max)
146 	{
147 		inslen = max-pos;
148 	}
149 
150 	/* Move the tail out of way */
151 	memmove(buf + pos + inslen,
152 	        buf + pos,
153 	        max - pos - inslen);
154 	/* Then put the substring in */
155 	memcpy(buf + pos,
156 	       source,
157 	       inslen);
158 	/* Ensure the buffer is terminated */
159 	buf[max-1] = '\0';
160 
161 	pos += inslen;
162 	if (pos > max)
163 		pos = max;
164 
165 	/* Update the screen from the old cursor position */
166 	cursor_to_input();
167 	update_input(UPDATE_FROM_CURSOR);
168 	/* Then place the cursor correctly */
169 	input_do_set_cursor_pos(pos);
170 }
171 
172 static void
input_do_delete_raw(int n,int do_save_cut)173 input_do_delete_raw(int n, int do_save_cut)
174 {
175 	char* buf  = current_screen->inputdata.buffer.buf;
176 	unsigned pos = current_screen->inputdata.buffer.pos;
177 	unsigned max = sizeof(current_screen->inputdata.buffer.buf);
178 
179 	cursor_to_input();
180 /*
181 	fprintf(stderr, "pos(%u)max(%u)buf(%s)do_delete_raw(%d, %d)\n",
182 	    pos,max,buf, n, do_save_cut);
183 */
184 	/* If n>0, deletes from front
185 	 * if n<0, deletes from back & moves cursor
186 	 */
187 	if (n < 0)
188 	{
189 		unsigned limit = current_screen->inputdata.buffer.minpos;
190 		/* Number of bytes LEFT from the cursor (prompt excluding) */
191 		unsigned oldbytes = pos-limit;
192 		unsigned erasebytes = -n;
193 
194 		/* Don't delete more than we can */
195 		if (erasebytes > oldbytes)
196 			erasebytes = oldbytes;
197 
198 		/* Move cursor backward */
199 		pos -= erasebytes;
200 		input_do_set_cursor_pos(pos);
201 
202 		/* Then delete from forward */
203 		n = erasebytes;
204 	}
205 	if (n > 0)
206 	{
207 		unsigned oldbytes = max-pos;
208 		unsigned erasebytes = n > oldbytes ? oldbytes : n;
209 		unsigned newbytes = oldbytes - erasebytes;
210 
211 		if (do_save_cut)
212 		{
213 			if (cut_buffer) new_free(&cut_buffer);
214 			cut_buffer = new_malloc(erasebytes+1);
215 			memcpy(cut_buffer, buf+pos, erasebytes);
216 			cut_buffer[erasebytes] = '\0';
217 		}
218 
219 		memmove(buf+pos,
220 		        buf+pos+erasebytes,
221 		        newbytes);
222 		buf[pos+newbytes] = '\0';
223 	}
224 /*
225 	fprintf(stderr, "-> pos(%u)max(%u)buf(%s)\n",
226 	    pos,max,buf);
227 */
228 	/* Now update the right side from cursor */
229 	update_input(UPDATE_FROM_CURSOR);
230 }
231 
232 static void
input_do_delete_chars(int n,int do_save_cut)233 input_do_delete_chars(int n, int do_save_cut)
234 {
235 	char* buf  = current_screen->inputdata.buffer.buf;
236 	unsigned pos = current_screen->inputdata.buffer.pos;
237 
238 	/* The usage of this function is identical to input_do_delete_raw(),
239 	 * but instead of bytes, n means the number of characters.
240 	 * Since characters may consist of 1-4 bytes, this
241 	 * function converts the number to bytes and then
242 	 * calls input_do_delete_raw().
243 	 */
244 	if (n > 0)
245 	{
246 		int bytes;
247 		for (bytes = 0; buf[pos] != '\0' && n > 0; --n)
248 		{
249 			unsigned length = calc_unival_length(buf+pos);
250 			bytes += length;
251 			pos   += length;
252 		}
253 		input_do_delete_raw(bytes, do_save_cut);
254 	}
255 	else if (n < 0)
256 	{
257 		unsigned limit = current_screen->inputdata.buffer.minpos;
258 
259 		int bytes;
260 		for(bytes=0; n<0 && pos > limit; ++n)
261 		{
262 			/* Go back until we reach a beginning of a character */
263 			for(;;)
264 			{
265 				unsigned length = calc_unival_length(buf + --pos);
266 				if (length) { bytes += length; break; }
267 				/* But don't go more than we're allowed to */
268 				if (pos == limit) break;
269 			}
270 		}
271 		input_do_delete_raw(-bytes, do_save_cut);
272 	}
273 }
274 
275 static void
input_do_replace_prompt(u_char * newprompt)276 input_do_replace_prompt(u_char *newprompt)
277 {
278 	ScreenInputBufferData* bufdata = &current_screen->inputdata.buffer;
279 	char* buf  = bufdata->buf;
280 	unsigned oldlen = bufdata->minpos;
281 	unsigned newlen = strlen(newprompt);
282 	unsigned max = sizeof(bufdata->buf);
283 	unsigned saved_len = max - (oldlen > newlen ? oldlen : newlen);
284 /*
285 	fprintf(stderr, "oldlen=%u, newlen=%u, max=%u, saved_len=%u\n",
286 		oldlen,newlen, max, saved_len);
287 */
288 	memmove(buf+newlen,
289 	        buf+oldlen,
290 	        saved_len);
291 	memcpy(buf,
292 	       newprompt,
293 	       newlen);
294 	buf[max-1] = '\0'; /* prevent dragons */
295 
296 	bufdata->minpos = newlen;
297 	bufdata->pos	= bufdata->pos - oldlen + newlen;
298 
299 	if (bufdata->pos < newlen)
300 		bufdata->pos = newlen;
301 }
302 
303 static int
input_is_password_prompt(void)304 input_is_password_prompt(void)
305 {
306 	char* buf    = current_screen->inputdata.buffer.buf;
307 	unsigned limit = current_screen->inputdata.buffer.minpos;
308 
309 	if (limit < 9)
310 		return 0;
311 
312 	/* If the prompt ends with "Password:", it is a password prompt. */
313 	/* This includes the following known prompts:
314 	 *   "Password:"
315 	 *   "Operator Password:"
316 	 *   "Server Password:"
317          * PATCHED by Flier
318 	 *   "Master Password:"
319 	 *   "Old Master Password:"
320 	 */
321 	return my_strnicmp(buf+limit-9, "Password:", 9) == 0;
322 }
323 
324 static char
input_do_check_prompt(int update)325 input_do_check_prompt(int update)
326 {
327 	char	*prompt;
328 
329 	char changed = 0;
330 
331 	if (current_screen->promptlist)
332 		prompt = current_screen->promptlist->prompt;
333 	else
334 		prompt = input_prompt ? input_prompt : empty_string;
335 	if (prompt)
336 	{
337 		if (update != NO_UPDATE)
338 		{
339 			int free_it = 1;
340 			unsigned len;
341 			char	*ptr;
342 			int	args_used;	/* this isn't used here but is
343 						 * passed to expand_alias()
344 						 */
345 			if (is_process(get_target_by_refnum(0)))
346 			{
347 				ptr = get_prompt_by_refnum(0);
348 				free_it = 0;
349 			}
350 			else
351 				ptr = expand_alias((u_char *) 0, prompt, empty_string, &args_used, NULL);
352 
353 			len = strlen(ptr);
354 			if (strncmp(ptr, current_screen->inputdata.buffer.buf, len) || !len)
355 			{
356 				input_do_replace_prompt(ptr);
357 				changed = 1;
358 			}
359 			if (free_it)
360 				new_free(&ptr);
361 		}
362 	}
363 	return changed;
364 }
365 
366 static char
input_check_resized(void)367 input_check_resized(void)
368 {
369 	ScreenInputData* inputdata = &current_screen->inputdata;
370 
371 	if (inputdata->old_li == current_screen->li
372 	    && inputdata->old_co == current_screen->co)
373 		return 0;
374 
375 	/* resized?  Keep it simple and reset everything */
376 	inputdata->cursor_x = 0;
377 	inputdata->cursor_y = current_screen->li - 1;
378 	inputdata->old_li   = current_screen->li;
379 	inputdata->old_co   = current_screen->co;
380 
381 	inputdata->zone     = current_screen->co;
382 /**************************** PATCHED by Flier ******************************/
383         if (get_int_var(INPUT_PROMPT_SHIFT_NEW_VAR)) {
384             /* 10% of context seems useful */
385             sz_prev_len = current_screen->inputdata.old_co / 10 + 1;
386             sz_width = 2;
387         }
388         else {
389             sz_prev_len = 0;
390             sz_width = WIDTH;
391         }
392 	if (inputdata->zone > sz_width)
393 		inputdata->zone -= sz_width;
394 /****************************************************************************/
395 	return 1;
396 }
397 
398 /*
399  * update_input: does varying amount of updating on the input line depending
400  * upon the position of the cursor and the update flag.  If the cursor has
401  * move toward one of the edge boundaries on the screen, update_cursor()
402  * flips the input line to the next (previous) line of text. The update flag
403  * may be:
404  *
405  * NO_UPDATE - only do the above bounds checking.
406  *
407  * UPDATE_JUST_CURSOR - do bounds checking and position cursor where is should
408  * be.
409  *
410  * UPDATE_FROM_CURSOR - does all of the above, and makes sure everything from
411  * the cursor to the right edge of the screen is current (by redrawing it).
412  *
413  * UPDATE_ALL - redraws the entire line
414  */
415 void
update_input(update)416 update_input(update)
417 	int	update;
418 {
419 	ScreenInputData* inputdata = &current_screen->inputdata;
420 	iconv_t display_conv = NULL;
421 
422 	int term_echo = 1;
423 
424 	if (dumb)
425 		return;
426 
427 	cursor_to_input();
428 
429 	if (input_do_check_prompt(update))
430 		update = UPDATE_ALL;
431 
432 	term_echo = !input_is_password_prompt();
433 
434 	if (input_check_resized())
435 		update = UPDATE_ALL;
436 
437 	if (update != NO_UPDATE)
438 	{
439 #ifdef HAVE_ICONV_OPEN
440 		const char *enc = display_encoding;
441 		if (!enc)
442 			enc = "ISO-8859-1";
443 		display_conv = iconv_open(enc, "UTF-8");
444 #endif /* HAVE_ICONV_OPEN */
445 	}
446 
447 /*
448 	{
449 		unsigned a;
450 		u_char* buf  = inputdata->buffer.buf;
451 		fputc('"', stderr);
452 		for (a = 0; buf[a]; ++a)
453 			fputc(buf[a], stderr);
454 		fprintf(stderr, "\"\n");
455 		fputc('>', stderr);
456 		for (a = 0; buf[a]; ++a)
457 			if (a == inputdata->buffer.pos)
458 				fputc('^', stderr);
459 			else if (a == inputdata->buffer.minpos)
460 				fputc('|', stderr);
461 			else
462 				fputc(' ', stderr);
463 		fprintf(stderr, "\n");
464 	}
465 */
466 	if (update == UPDATE_JUST_CURSOR || update == UPDATE_ALL)
467 	{
468 		char* buf  = inputdata->buffer.buf;
469 		unsigned pos = inputdata->buffer.pos;
470 		unsigned limit = inputdata->buffer.minpos;
471 		unsigned column, ptr;
472 
473 		unsigned window;
474 
475 		/* Recalculate pos_column */
476 		for (column = ptr = 0; ptr < pos; )
477 		{
478 			unsigned unival = calc_unival(buf+ptr);
479 
480 			if (!term_echo && ptr >= limit)
481 				unival = ' ';
482 
483 			column += input_do_calculate_width(unival, display_conv);
484 			ptr    += calc_unival_length(buf+ptr);
485 		}
486 		inputdata->pos_column = column;
487 
488 		window = column - (column % inputdata->zone);
489 
490 /**************************** PATCHED by Flier ******************************/
491                 /* window defines how much of the left side of input prompt */
492                 /* is not visible */
493                 if (column >= inputdata->zone)
494                 {
495                     if (column >= 2 * inputdata->zone - sz_prev_len)
496                     {
497                         int len = inputdata->zone - sz_prev_len;
498 
499                         window = (column - sz_prev_len) / len * len;
500                     }
501                     else
502                     {
503                         window -= sz_prev_len;
504                     }
505                 }
506 /****************************************************************************/
507 
508 		/* Recalculate left_ptr */
509 		for (column = ptr = 0; column < window; )
510 		{
511 			unsigned unival = calc_unival(buf+ptr);
512 
513 			if (!term_echo && ptr >= limit)
514 				unival = ' ';
515 
516 			column += input_do_calculate_width(unival, display_conv);
517 			ptr    += calc_unival_length(buf+ptr);
518 		}
519 		/* If the left edge has been moved, redraw the input line */
520 		if (ptr != inputdata->left_ptr)
521 			update = UPDATE_ALL;
522 		inputdata->left_ptr = ptr;
523 
524 		/* Recalculate cursor_x */
525 		inputdata->cursor_x = inputdata->pos_column - column;
526 	}
527 
528 	if (update == UPDATE_FROM_CURSOR || update == UPDATE_ALL)
529 	{
530 		unsigned limit = inputdata->buffer.minpos;
531 		char *buf = inputdata->buffer.buf;
532 		char VisBuf[BIG_BUFFER_SIZE];
533 		unsigned column, iptr, optr;
534 		int written;
535 
536 		for (column = 0, optr = 0, iptr = inputdata->left_ptr;
537 			buf[iptr] != '\0' && column < current_screen->co; )
538 		{
539 			unsigned unival = calc_unival(buf+iptr);
540 			unsigned len = calc_unival_length(buf+iptr);
541 
542 			if (!term_echo && iptr >= limit)
543 				unival = ' ';
544 
545 			if (displayable_unival(unival, display_conv))
546 			{
547 				column += calc_unival_width(unival);
548 				if (!term_echo && unival == ' ')
549 				{
550 					memcpy(VisBuf+optr, " ", 1);
551 					optr += 1;
552 				}
553 				else
554 				{
555 					memcpy(VisBuf+optr, buf+iptr, len);
556 					optr += len;
557 				}
558 				iptr += len;
559 			}
560 			else
561 			{
562 				unsigned n;
563 				VisBuf[optr++] = REV_TOG;
564 				for (n = 0; n < len; ++n)
565 					VisBuf[optr++] = (buf[iptr++] & 127) | 64;
566 				VisBuf[optr++] = REV_TOG;
567 				column += len;
568 			}
569 		}
570 		VisBuf[optr] = '\0';
571 		term_move_cursor(0, inputdata->cursor_y);
572 		written = output_line(VisBuf, 0);
573 		if (term_clear_to_eol() && written < current_screen->co)
574 		{
575 			/* EOL wasn't implemented, so do it with spaces */
576 			term_space_erase(current_screen->co - written);
577 		}
578 	}
579 
580 	if (update != NO_UPDATE)
581 	{
582 		/* Always update cursor */
583 		term_move_cursor(inputdata->cursor_x, inputdata->cursor_y);
584 	}
585 
586 	if (update != NO_UPDATE)
587 	{
588 #ifdef HAVE_ICONV_OPEN
589 /**************************** PATCHED by Flier ******************************/
590             if (display_conv != (iconv_t) (-1))
591 /****************************************************************************/
592 		iconv_close(display_conv);
593 #endif /* HAVE_ICONV_OPEN */
594 	}
595 
596 	term_flush();
597 }
598 
599 void
refresh_inputline(key,ptr)600 refresh_inputline(key, ptr)
601  	u_int	key;
602 	char *	ptr;
603 {
604 	update_input(UPDATE_ALL);
605 }
606 
607 void
change_input_prompt(direction)608 change_input_prompt(direction)
609 	int	direction;
610 {
611 	if (!current_screen->promptlist)
612 	{
613 		/* Restore stuff */
614 		memcpy(&current_screen->inputdata.saved_buffer,
615 		       &current_screen->inputdata.buffer,
616 		       sizeof(current_screen->inputdata.buffer));
617 	}
618 	else if (direction == -1)
619 	{
620 		/* Just update whatever should be on screen */
621 	}
622 	else if (!current_screen->promptlist->next)
623 	{
624 		/* Save stuff */
625 		memcpy(&current_screen->inputdata.buffer,
626 		       &current_screen->inputdata.saved_buffer,
627 		       sizeof(current_screen->inputdata.buffer));
628 
629 		/* Erase input line */
630 		*current_screen->inputdata.buffer.buf = '\0';
631 		current_screen->inputdata.buffer.pos =
632 				current_screen->inputdata.buffer.minpos = 0;
633 	}
634 	update_input(UPDATE_ALL);
635 }
636 
637 /* input_move_cursor: moves the cursor left or right... got it? */
638 /* zero=left, nonzero=right */
639 void
input_move_cursor(dir)640 input_move_cursor(dir)
641 	int	dir;
642 {
643 	char* buf  = current_screen->inputdata.buffer.buf;
644 	unsigned pos = current_screen->inputdata.buffer.pos;
645 	if (dir)
646 	{
647 		/* if there are still chars remaining */
648 		if (buf[pos] != '\0')
649 		{
650 			/* Skip over 1 character */
651 			pos += calc_unival_length(buf+pos);
652 
653 			input_do_set_cursor_pos(pos);
654 		}
655 	}
656 	else
657 	{
658 		unsigned limit = current_screen->inputdata.buffer.minpos;
659 
660 		if (pos > limit)
661 		{
662 			/* Go back until we reach a beginning of a character */
663 			while(!calc_unival_length(buf + --pos))
664 			{
665 				/* But don't go more than we're allowed to */
666 				if (pos == limit) break;
667 			}
668 			input_do_set_cursor_pos(pos);
669 		}
670 	}
671 }
672 
673 /*
674  * input_forward_word: move the input cursor forward one word in the input
675  * line
676  */
677 void
input_forward_word(key,ptr)678 input_forward_word(key, ptr)
679  	u_int	key;
680 	char *	ptr;
681 {
682 	char* buf  = current_screen->inputdata.buffer.buf;
683 	unsigned pos = current_screen->inputdata.buffer.pos;
684 
685 	/* Skip while definitely space, then skip while definitely nonspace */
686 	/* For nontypical characters, does nothing */
687 
688 	while (buf[pos] != '\0')
689 	{
690 		unsigned unival = calc_unival(buf+pos);
691 
692 		if (!isspace(unival) && !ispunct(unival))
693 			break;
694 		pos += calc_unival_length(buf+pos);
695 	}
696 	while (buf[pos] != '\0')
697 	{
698 		unsigned unival = calc_unival(buf+pos);
699 		if (isspace(unival) || ispunct(unival) || unival >= 0x300)
700 			break;
701 		pos += calc_unival_length(buf+pos);
702 	}
703 	input_do_set_cursor_pos(pos);
704 }
705 
706 /* input_backward_word: move the cursor left on word in the input line */
707 void
input_backward_word(key,ptr)708 input_backward_word(key, ptr)
709  	u_int	key;
710 	char *	ptr;
711 {
712 	char* buf  = current_screen->inputdata.buffer.buf;
713 	unsigned pos = current_screen->inputdata.buffer.pos;
714 	unsigned limit = current_screen->inputdata.buffer.minpos;
715 
716 	/* Skip while previous is definitely space,         */
717 	/* then skip while previous is definitely nonspace. */
718 	/* For nontypical characters, does nothing */
719 	while (pos > limit)
720 	{
721 		unsigned prevpos = pos, unival;
722 
723 		while (!calc_unival_length(buf + --prevpos))
724 			if (prevpos <= limit)
725 				break;
726 		unival = calc_unival(buf + prevpos);
727 		if (!isspace(unival) && !ispunct(unival))
728 			break;
729 		pos = prevpos;
730 	}
731 	while (pos > limit)
732 	{
733 		unsigned prevpos = pos, unival;
734 		while (!calc_unival_length(buf + --prevpos))
735 			if (prevpos <= limit)
736 				break;
737 		unival = calc_unival(buf + prevpos);
738 		if (isspace(unival) || ispunct(unival) || unival >= 0x300)
739 			break;
740 		pos = prevpos;
741 	}
742 	input_do_set_cursor_pos(pos);
743 }
744 
745 /* input_delete_character: deletes a character from the input line */
746 void
input_delete_character(key,ptr)747 input_delete_character(key, ptr)
748  	u_int	key;
749 	char *	ptr;
750 {
751 	input_do_delete_chars(1, 0);
752 /**************************** PATCHED by Flier ******************************/
753         ResetNickCompletion();
754 /****************************************************************************/
755 }
756 
757 /* input_backspace: does a backspace in the input buffer */
758 /*ARGSUSED*/
759 void
input_backspace(key,ptr)760 input_backspace(key, ptr)
761  	u_int	key;
762 	char *	ptr;
763 {
764 	input_do_delete_chars(-1, 0);
765 /**************************** PATCHED by Flier ******************************/
766         ResetNickCompletion();
767 /****************************************************************************/
768 }
769 
770 /*
771  * input_beginning_of_line: moves the input cursor to the first character in
772  * the input buffer
773  */
774 void
input_beginning_of_line(key,ptr)775 input_beginning_of_line(key, ptr)
776  	u_int	key;
777 	char	*ptr;
778 {
779 	input_do_set_cursor_pos(current_screen->inputdata.buffer.minpos);
780 }
781 
782 /*
783  * input_end_of_line: moves the input cursor to the last character in the
784  * input buffer
785  */
786 void
input_end_of_line(key,ptr)787 input_end_of_line(key, ptr)
788  	u_int	key;
789 	char	*ptr;
790 {
791 	input_do_set_cursor_pos(strlen(current_screen->inputdata.buffer.buf));
792 }
793 
794 /*
795  * input_delete_previous_word: deletes from the cursor backwards to the next
796  * space character.
797  */
798 void
input_delete_previous_word(key,ptr)799 input_delete_previous_word(key, ptr)
800  	u_int	key;
801 	char *	ptr;
802 {
803 	unsigned pos = current_screen->inputdata.buffer.pos;
804 	int bytes;
805 
806 	/* moves cursor backwards */
807 	input_backward_word(key,ptr);
808 
809 	bytes = pos - current_screen->inputdata.buffer.pos;
810 
811 	/* now that we're back, delete next n bytes */
812 	input_do_delete_raw(bytes, 1);
813 /**************************** PATCHED by Flier ******************************/
814         tabnickcompl = NULL;
815 /****************************************************************************/
816 }
817 
818 /*
819  * input_delete_next_word: deletes from the cursor to the end of the next
820  * word
821  */
822 void
input_delete_next_word(key,ptr)823 input_delete_next_word(key, ptr)
824  	u_int	key;
825 	char *	ptr;
826 {
827 	unsigned pos = current_screen->inputdata.buffer.pos;
828 	int bytes;
829 
830 	/* moves cursor forward */
831 	input_forward_word(key,ptr);
832 
833 	bytes = current_screen->inputdata.buffer.pos - pos;
834 
835 	/* now that we're after the word, delete past n bytes */
836 	input_do_delete_raw(-bytes, 1);
837 }
838 
839 /*
840  * input_add_character: adds the byte c to the input buffer, repecting
841  * the current overwrite/insert mode status, etc
842  */
843 /*ARGSUSED*/
844 void
input_add_character(key,ptr)845 input_add_character(key, ptr)
846  	u_int	key;
847 	char *	ptr;
848 {
849 	u_char output_buffer[8];
850 
851 #ifdef HAVE_ICONV_OPEN
852 	static u_char   input_buffer[32]="";
853 	static unsigned input_pos=0;
854 	size_t retval;
855 
856 	iconv_const char *iptr;
857 	char *optr;
858 	size_t isize, osize;
859 
860 	input_buffer[input_pos++] = key;
861 
862 	if (!mbdata_ok)
863 	{
864 		mbdata_init(&mbdata, input_encoding);
865 		mbdata_ok = 1;
866 	}
867 
868 re_encode:
869 	if (!input_pos)
870 	{
871 		return;
872 	}
873 	iptr = (iconv_const char *)input_buffer;
874 	isize = input_pos;
875 	optr = (char *)output_buffer;
876 	osize = sizeof output_buffer;
877 
878 /**************************** PATCHED by Flier ******************************/
879         if (mbdata.conv_in == NULL) {
880             size_t cnt = isize < osize ? isize : osize;
881 
882             strmcpy(output_buffer, input_buffer, cnt + 1);
883             while (cnt > 0) {
884                 optr++;
885                 cnt--;
886             }
887             retval = 0;
888         }
889         else
890 /****************************************************************************/
891 	retval = iconv(mbdata.conv_in,
892 		       (iconv_const char **) &iptr, &isize,
893 		       &optr, &osize);
894 
895 	if (retval == (size_t)-1)
896 	{
897 		switch(errno)
898 		{
899 			case EINVAL:
900 			{
901 				/* User didn't give enough bytes. Must give more later. */
902 				return;
903 			}
904 			case EILSEQ:
905 			{
906 /**************************** PATCHED by Flier ******************************/
907                                 static time_t lastkeypress = 0;
908 
909                                 if (lastkeypress == 0) lastkeypress = time(NULL);
910                                 else {
911                                     time_t timenow = time(NULL);
912 
913                                     if (timenow - lastkeypress > 2) {
914                                         /* Probably bad input encoding, ignore bad bytes! */
915                                         say("Wrong input sequence, resetting");
916                                         input_pos = 0;
917                                         input_buffer[0] = '\0';
918                                         return;
919                                     }
920                                 }
921 /****************************************************************************/
922 				/* User gave a bad byte. Ignore bad bytes! */
923 				memmove(input_buffer+1,
924 				        input_buffer,
925 				        --input_pos);
926 				goto re_encode;
927 			}
928 		}
929 	}
930 	*optr = '\0';
931 	input_pos = 0;
932 #else
933 	/* no iconv, assume in=iso-8859-1 */
934 	utf8_sequence(key, output_buffer);
935 #endif /* HAVE_ICONV_OPEN */
936 
937 	if (!get_int_var(INSERT_MODE_VAR))
938 	{
939 		/* Delete the next character */
940 		input_do_delete_chars(1, 0);
941 	}
942 	input_do_insert_raw(output_buffer);
943 /**************************** PATCHED by Flier ******************************/
944         if (key == ' ') ResetNickCompletion();
945 /****************************************************************************/
946 }
947 
948 /*
949  * set_input: sets the input buffer to the given string, discarding whatever
950  * was in the input buffer before
951  */
952 void
set_input(str)953 set_input(str)
954 	char	*str;
955 {
956 	u_char converted_input[INPUT_BUFFER_SIZE];
957 	struct mb_data mbdata1;
958 	unsigned dest;
959 #ifdef HAVE_ICONV_OPEN
960 	mbdata_init(&mbdata1, irc_encoding);
961 #else
962 	mbdata_init(&mbdata1, NULL);
963 #endif /* HAVE_ICONV_OPEN */
964 	for (dest = 0; *str != '\0'; )
965 	{
966 /**************************** PATCHED by Flier ******************************/
967                 if (*str == REV_TOG || *str == UND_TOG || *str == BOLD_TOG ||
968                     *str == ALL_OFF || *str == '\007') {
969                     converted_input[dest++] = *str++;
970                     continue;
971                 }
972 /****************************************************************************/
973 		if (dest + 8 >= sizeof(converted_input))
974 			break;
975 
976 		decode_mb(str, converted_input+dest, &mbdata1);
977 		dest += mbdata1.output_bytes;
978 		str  += mbdata1.input_bytes;
979 	}
980 	converted_input[dest] = '\0';
981 
982 	set_input_raw(converted_input);
983 
984 /**************************** PATCHED by Flier ******************************/
985         /* fix memory leak */
986         mbdata_done(&mbdata1);
987 /****************************************************************************/
988 }
989 
990 void
set_input_raw(str)991 set_input_raw(str)
992 	char* str;
993 {
994 	char* buf    = current_screen->inputdata.buffer.buf;
995 	unsigned pos   = current_screen->inputdata.buffer.pos;
996 	unsigned limit = current_screen->inputdata.buffer.minpos;
997 
998 	strmcpy(buf + limit,
999 		str,
1000 		(size_t)sizeof(current_screen->inputdata.buffer.buf) - limit);
1001 
1002 	pos = strlen(buf);
1003 
1004 	current_screen->inputdata.buffer.pos = pos;
1005 
1006 	if (mbdata_ok)
1007 	{
1008 		mbdata_done(&mbdata);
1009 		mbdata_ok = 0;
1010 	}
1011 }
1012 
1013 /*
1014  * get_input: returns a pointer to the input buffer.  Changing this will
1015  * actually change the input buffer.  This is a bad way to change the input
1016  * buffer tho, cause no bounds checking won't be done
1017  */
1018 char	*
get_input()1019 get_input()
1020 {
1021 	iconv_const char* source = (iconv_const char*)get_input_raw();
1022 
1023 	/* The input buffer is UTF-8 encoded. Clients will want irc_encoding instead. */
1024 	static u_char converted_buffer[INPUT_BUFFER_SIZE];
1025 
1026 #ifdef HAVE_ICONV_OPEN
1027 	iconv_t conv = (iconv_t) (-1);
1028 	char* dest = (char *)converted_buffer;
1029 	size_t left, space;
1030 
1031 /**************************** PATCHED by Flier ******************************/
1032 	if (irc_encoding) conv = iconv_open(irc_encoding, "UTF-8");
1033         if (conv == (iconv_t) (-1)) {
1034             strmcpy(dest, (char *) source, sizeof(converted_buffer));
1035             return(converted_buffer);
1036         }
1037 /****************************************************************************/
1038 	left = strlen(source);
1039 	space  = sizeof(converted_buffer);
1040 	while (*source != '\0')
1041 	{
1042 		size_t retval;
1043 
1044 		retval = iconv(conv,
1045 		               (iconv_const char **) &source, &left,
1046 		                &dest, &space);
1047 		if (retval == (size_t)-1)
1048 		{
1049 			if (errno == E2BIG)
1050 				break;
1051 			if (errno == EILSEQ)
1052 			{
1053 				/* Ignore silently 1 illegal byte */
1054 				if (left > 0)
1055 				{
1056 					++source;
1057 					--left;
1058 				}
1059 			}
1060 			if (errno == EINVAL)
1061 			{
1062 				/* Input terminated with a partial byte. */
1063 				/* Ignore the error silently. */
1064 				break;
1065 			}
1066 		}
1067 	}
1068 	/* Reset the converter, create a reset-sequence */
1069 	iconv(conv, NULL, &left, &dest, &space);
1070 
1071 	/* Ensure null-terminators are where they should be */
1072 	converted_buffer[sizeof(converted_buffer)-1] = '\0';
1073 	*dest = '\0';
1074 
1075 	iconv_close(conv);
1076 #else
1077 	/* Must convert manually - assume output is ISO-8859-1 */
1078 	unsigned dest = 0;
1079 
1080 	while (*source != '\0')
1081 	{
1082 		unsigned len = calc_unival_length(source);
1083 		unsigned unival;
1084 
1085 		if (!len)
1086 		{
1087 			/* ignore illegal byte (shouldn't happen) */
1088 			++source;
1089 			continue;
1090 		}
1091 		unival = calc_unival(source);
1092 		if (displayable_unival(unival, NULL))
1093 		{
1094 			converted_buffer[dest++] = unival;
1095 			if (dest+1 >= sizeof(converted_buffer))
1096 				break;
1097 		}
1098 		source += len;
1099 	}
1100 	converted_buffer[dest] = '\0';
1101 #endif /* HAVE_ICONV_OPEN */
1102 
1103 	return converted_buffer;
1104 }
1105 
1106 char	*
get_input_raw()1107 get_input_raw()
1108 {
1109 	char* buf    = current_screen->inputdata.buffer.buf;
1110 	unsigned limit = current_screen->inputdata.buffer.minpos;
1111 
1112 	return buf+limit;
1113 }
1114 
1115 /* input_clear_to_eol: erases from the cursor to the end of the input buffer */
1116 void
input_clear_to_eol(key,ptr)1117 input_clear_to_eol(key, ptr)
1118  	u_int	key;
1119 	char *	ptr;
1120 {
1121 	char* buf  = current_screen->inputdata.buffer.buf;
1122 	unsigned pos = current_screen->inputdata.buffer.pos;
1123 	int bytes;
1124 
1125 	for (bytes = 0; buf[pos] != '\0';)
1126 	{
1127 		unsigned length = calc_unival_length(buf+pos);
1128 		bytes += length;
1129 		pos   += length;
1130 	}
1131 
1132 	input_do_delete_raw(bytes, 1);
1133 }
1134 
1135 /*
1136  * input_clear_to_bol: clears from the cursor to the beginning of the input
1137  * buffer
1138  */
1139 void
input_clear_to_bol(key,ptr)1140 input_clear_to_bol(key, ptr)
1141  	u_int	key;
1142 	char	*ptr;
1143 {
1144 	char* buf  = current_screen->inputdata.buffer.buf;
1145 	unsigned pos = current_screen->inputdata.buffer.pos;
1146 	int bytes;
1147 
1148 	unsigned limit = current_screen->inputdata.buffer.minpos;
1149 	for (bytes = 0; limit < pos; )
1150 	{
1151 		unsigned length = calc_unival_length(buf+limit);
1152 		bytes += length;
1153 		limit += length;
1154 	}
1155 
1156 	input_do_delete_raw(-bytes, 1);
1157 }
1158 
1159 /*
1160  * input_clear_line: clears entire input line
1161  */
1162 void
input_clear_line(key,ptr)1163 input_clear_line(key, ptr)
1164  	u_int	key;
1165 	char	*ptr;
1166 {
1167 	input_beginning_of_line(key, ptr);
1168 	input_clear_to_eol(key, ptr);
1169 /**************************** PATCHED by Flier ******************************/
1170         tabnickcompl = NULL;
1171 /****************************************************************************/
1172 }
1173 
1174 /*
1175  * input_transpose_characters: swaps the positions of the two characters
1176  * before the cursor position
1177  * and moves cursor 1 forward
1178  */
1179 void
input_transpose_characters(key,ptr)1180 input_transpose_characters(key, ptr)
1181  	u_int	key;
1182 	char *	ptr;
1183 {
1184 	char* buf  = current_screen->inputdata.buffer.buf;
1185 	unsigned pos = current_screen->inputdata.buffer.pos;
1186 
1187 	/*
1188 	  delete previous char,
1189 	  then move 1 right,
1190 	  then insert the deleted char
1191 
1192 	  example (^ denotes cursor position):
1193 
1194 		before:
1195 		  defg
1196 		    ^
1197 		after:
1198 		  dfeg
1199 		     ^
1200 
1201 	  this is how bash transposes at least /Bisqwit
1202 
1203 	  FIXME: cut-buffer shouldn't be affected when transposing
1204 	*/
1205 	if (!buf[pos])
1206 	{
1207 		/* back off 1 char */
1208 		input_move_cursor(0);
1209 		pos = current_screen->inputdata.buffer.pos;
1210 		/* If we're still at the end of the string,
1211 		 * the string consists of only 1 char.
1212 		 * In which case there's nothing to transpose.
1213 		 */
1214 		if (!buf[pos])
1215 		{
1216 			return;
1217 		}
1218 	}
1219 	input_do_delete_chars(-1, 1);
1220 	input_move_cursor(1);
1221 	input_yank_cut_buffer(key, ptr);
1222 }
1223 
1224 /* init_input: initialized the input buffer by clearing it out */
1225 void
init_input()1226 init_input()
1227 {
1228 	*current_screen->inputdata.buffer.buf = (char) 0;
1229 	current_screen->inputdata.buffer.pos = current_screen->inputdata.buffer.minpos;
1230 }
1231 
1232 /*
1233  * input_yank_cut_buffer: takes the contents of the cut buffer and inserts it
1234  * into the input line
1235  */
1236 void
input_yank_cut_buffer(key,ptr)1237 input_yank_cut_buffer(key, ptr)
1238  	u_int	key;
1239 	char *	ptr;
1240 {
1241 	if (cut_buffer)
1242 		input_do_insert_raw(cut_buffer);
1243 }
1244 
1245 /* get_input_prompt: returns the current input_prompt */
1246 char	*
get_input_prompt()1247 get_input_prompt()
1248 {
1249 	return (input_prompt);
1250 }
1251 
1252 /*
1253  * set_input_prompt: sets a prompt that will be displayed in the input
1254  * buffer.  This prompt cannot be backspaced over, etc.  It's a prompt.
1255  * Setting the prompt to null uses no prompt
1256  */
1257 void
set_input_prompt(prompt)1258 set_input_prompt(prompt)
1259 	char	*prompt;
1260 {
1261 	if (!prompt)
1262 	{
1263 		malloc_strcpy(&input_prompt, empty_string);
1264 	}
1265 	else
1266 	{
1267 		char converted_prompt[INPUT_BUFFER_SIZE];
1268 		struct mb_data mbdata1;
1269 		unsigned dest;
1270 #ifdef HAVE_ICONV_OPEN
1271 		mbdata_init(&mbdata1, irc_encoding);
1272 #else
1273 		mbdata_init(&mbdata1, NULL);
1274 #endif /* HAVE_ICONV_OPEN */
1275 		for (dest = 0; *prompt != '\0'; )
1276 		{
1277 			decode_mb(prompt, converted_prompt+dest, &mbdata1);
1278 			dest   += mbdata1.output_bytes;
1279 			prompt += mbdata1.input_bytes;
1280 		}
1281 		converted_prompt[dest] = '\0';
1282 
1283 		if (input_prompt && !strcmp(converted_prompt, input_prompt))
1284 			return;
1285 		malloc_strcpy(&input_prompt, converted_prompt);
1286 	}
1287 	update_input(UPDATE_ALL);
1288 }
1289 
1290 /*
1291  * input_pause: prompt the user with a message then waits for a single
1292  * keystroke before continuing.  The key hit is returned.
1293  *
1294  * This function is NEVER to be called, once the inital irc_io()
1295  * loop has been called from main().  Perhaps it would be better
1296  * to just put this code at the only place it is called, and not
1297  * have the function.  If you want an input_pause, use add_wait_prompt()
1298  * with WAIT_PROMPT_KEY.
1299  */
1300 char
input_pause(msg)1301 input_pause(msg)
1302 	char	*msg;
1303 {
1304 	char	*ptr = (char *) 0;
1305 	char	c;
1306 
1307 	if (dumb)
1308 	{
1309 		puts(msg);
1310 		fflush(stdout);
1311 		if (use_input)
1312 			irc_io(&c, NULL, -1, 1);
1313 	}
1314 	else
1315 	{
1316 		malloc_strcpy(&ptr, get_input());
1317 		set_input(msg);
1318 		update_input(UPDATE_ALL);
1319 		irc_io(&c, NULL, -1, 1);
1320 		set_input(ptr);
1321 		update_input(UPDATE_ALL);
1322 		new_free(&ptr);
1323 	}
1324 	return (c);
1325 }
1326 
1327 void
input_reset_screen(Screen * new)1328 input_reset_screen(Screen* new)
1329 {
1330 	new->inputdata.old_li = -1;
1331 	new->inputdata.old_co = -1;
1332 	new->inputdata.cursor_x = 0;
1333 	new->inputdata.cursor_y = 0;
1334 	new->inputdata.left_ptr = 0;
1335 	new->inputdata.pos_column = 0;
1336 	new->inputdata.buffer.pos = 0;
1337 	new->inputdata.buffer.minpos = 0;
1338 	new->inputdata.buffer.buf[0] = '\0';
1339 }
1340 
1341 /* moved from alias.c */
1342 u_char	*
function_curpos(input)1343 function_curpos(input)
1344 	u_char	*input;
1345 {
1346 	char	*new = (char *) 0,
1347 		pos[8];
1348 
1349 	snprintf(pos, sizeof pos, "%d", current_screen->inputdata.buffer.pos);
1350 	malloc_strcpy(&new, pos);
1351 	return new;
1352 }
1353 
1354 /**************************** PATCHED by Flier ******************************/
1355 /* set parameters for input prompt shifting */
1356 void
set_input_prompt_shift_new(val)1357 set_input_prompt_shift_new(val)
1358 int val;
1359 {
1360     ScreenInputData* inputdata = &current_screen->inputdata;
1361 
1362     inputdata->old_co = 0;   /* to force recalculation */
1363     input_check_resized();
1364 }
1365 /****************************************************************************/
1366