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-2016 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 
37 #include "irc.h"
38 IRCII_RCSID("@(#)$eterna: input.c,v 1.80 2019/01/16 10:04:19 mrg Exp $");
39 
40 #ifdef HAVE_ICONV_H
41 # include <iconv.h>
42 #endif /* HAVE_ICONV_H */
43 
44 #include "input.h"
45 #include "ircterm.h"
46 #include "alias.h"
47 #include "vars.h"
48 #include "ircaux.h"
49 #include "window.h"
50 #include "screen.h"
51 #include "exec.h"
52 #include "output.h"
53 #include "translat.h"
54 
55 #include "debug.h"
56 
57 #define WIDTH 10
58 
59 typedef struct ScreenInputBufferData
60 {
61 	u_char buf[INPUT_BUFFER_SIZE+1];
62 	unsigned pos;
63 	unsigned minpos;
64 	/* If you put pointers here, remember to change
65 	 * change_input_prompt() which uses memcpy to
66 	 * make copies of this struct
67 	 */
68 } ScreenInputBufferData;
69 
70 struct ScreenInputData
71 {
72 	ScreenInputBufferData buffer;
73 	ScreenInputBufferData saved_buffer;
74 
75 	/* Used by update_input() to check if the screen geometry has changed */
76 	int	old_co, old_li;
77 
78 	/* screen->co    = number of columns on screen
79 	 *
80 	 * buffer.buf    = input line and prompt (utf-8 encoded)
81 	 * buffer.pos    = byte position of the cursor within string
82 	 * buffer.minpos = length of prompt in bytes
83 	 *
84 	 * When update_input() is ran,
85 	 *   It checks if the prompt in buffer differs from input_prompt.
86 	 *   If it does, it replaces the buffer-prompt with the new prompt.
87 	 *        And sets update to UPDATE_ALL
88 	 *
89 	 * If geometry has changed,
90 	 *        update is set to UPDATE_ALL
91 	 *
92 	 * left_ptr = byte-position of the left edge of screen in buffer
93 	 *     recalculated when:
94 	 *        update==UPDATE_JUST_CURSOR
95 	 *        update==UPDATE_ALL
96 	 *
97 	 * pos_column = column position of the cursor in the buffer
98 	 *     recalculated when left_ptr is too
99 	 *
100 	 * cursor_x = cursor horizontal position on screen (columns, not chars, not bytes)
101 	 *     recalculated when:
102 	 *        update==UPDATE_JUST_CURSOR
103 	 *        update==UPDATE_ALL
104 	 *
105 	 * cursor_y = cursor vertical position on screen (lines)
106 	 *     recalculated when screen geometry has changed
107 	 *
108 	 * zone     = screen width
109 	 */
110 
111 	unsigned zone;
112 	unsigned cursor_x;
113 	unsigned cursor_y;
114 	unsigned left_ptr;
115 	unsigned pos_column;
116 };
117 
118 /* input_prompt: contains the current, unexpanded input prompt */
119 static	u_char	*input_prompt = NULL;
120 
121 static	u_char	*cut_buffer;
122 
123 static struct mb_data mbdata;
124 static int mbdata_ok = 0;
125 
126 
127 /* cursor_to_input: move the cursor to the input line, if not there already */
128 void
cursor_to_input(void)129 cursor_to_input(void)
130 {
131 	if (screen_get_alive(get_current_screen()) && is_cursor_in_display())
132 	{
133 		ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
134 
135 		term_move_cursor(inputdata->cursor_x, inputdata->cursor_y);
136 		Debug(DB_CURSOR, "cursor_to_input: moving cursor to input for screen %d",
137 		       screen_get_screennum(get_current_screen()));
138 		cursor_not_in_display();
139 		term_flush();
140 	}
141 }
142 
143 static unsigned
input_do_calculate_width(unsigned unival,iconv_t conv)144 input_do_calculate_width(unsigned unival, iconv_t conv)
145 {
146 	if (!displayable_unival(unival, conv))
147 	{
148 		/* undisplayable values are printed raw */
149 		if (unival < 0x80)
150 			return 1;
151 		if (unival < 0x800)
152 			return 2;
153 		if (unival < 0x10000)
154 			return 3;
155 		return 4;
156 	}
157 	return calc_unival_width(unival);
158 }
159 
160 static void
input_do_set_cursor_pos(unsigned pos)161 input_do_set_cursor_pos(unsigned pos)
162 {
163 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
164 
165 	inputdata->buffer.pos = pos;
166 	update_input(UPDATE_JUST_CURSOR);
167 }
168 
169 static void
input_do_insert_raw(u_char * source)170 input_do_insert_raw(u_char* source)
171 {
172 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
173 	u_char* buf  = inputdata->buffer.buf;
174 	unsigned pos = inputdata->buffer.pos;
175 	unsigned max = sizeof(inputdata->buffer.buf);
176 	unsigned inslen = my_strlen(source);
177 
178 	/* This function inserts the given substring of bytes
179 	 * to the input line at the current editing position.
180 	 * Cursor is moved to point to the end of the substring.
181 	 */
182 
183 	if (pos + inslen > max)
184 	{
185 		inslen = max - pos;
186 	}
187 
188 	/* Move the tail out of way */
189 	memmove(buf + pos + inslen,
190 	        buf + pos,
191 	        max - pos - inslen);
192 	/* Then put the substring in */
193 	memcpy(buf + pos,
194 	       source,
195 	       inslen);
196 	/* Ensure the buffer is terminated */
197 	buf[max - 1] = '\0';
198 
199 	pos += inslen;
200 	if (pos > max)
201 		pos = max;
202 
203 	/* Update the screen from the old cursor position */
204 	update_input(UPDATE_FROM_CURSOR);
205 	/* Then place the cursor correctly */
206 	input_do_set_cursor_pos(pos);
207 }
208 
209 static void
input_do_delete_raw(int n,int do_save_cut)210 input_do_delete_raw(int n, int do_save_cut)
211 {
212 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
213 	u_char* buf  = inputdata->buffer.buf;
214 	unsigned pos = inputdata->buffer.pos;
215 	unsigned max = sizeof(inputdata->buffer.buf);
216 
217 	/* If n>0, deletes from front
218 	 * if n<0, deletes from back & moves cursor
219 	 */
220 	if (n < 0)
221 	{
222 		unsigned limit = inputdata->buffer.minpos;
223 		/* Number of bytes LEFT from the cursor (prompt excluding) */
224 		unsigned oldbytes = pos-limit;
225 		unsigned erasebytes = -n;
226 
227 		/* Don't delete more than we can */
228 		if (erasebytes > oldbytes)
229 			erasebytes = oldbytes;
230 
231 		/* Move cursor backward */
232 		pos -= erasebytes;
233 		input_do_set_cursor_pos(pos);
234 
235 		/* Then delete from forward */
236 		n = erasebytes;
237 	}
238 	if (n > 0)
239 	{
240 		unsigned oldbytes = max-pos;
241 		unsigned erasebytes = n > oldbytes ? oldbytes : n;
242 		unsigned newbytes = oldbytes - erasebytes;
243 
244 		if (do_save_cut)
245 		{
246 			if (cut_buffer)
247 				new_free(&cut_buffer);
248 			cut_buffer = new_malloc(erasebytes+1);
249 			memcpy(cut_buffer, buf+pos, erasebytes);
250 			cut_buffer[erasebytes] = '\0';
251 		}
252 
253 		memmove(buf+pos,
254 		        buf+pos+erasebytes,
255 		        newbytes);
256 		buf[pos+newbytes] = '\0';
257 	}
258 	/* Now update the right side from cursor */
259 	update_input(UPDATE_FROM_CURSOR);
260 }
261 
262 static void
input_do_delete_chars(int n,int do_save_cut)263 input_do_delete_chars(int n, int do_save_cut)
264 {
265 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
266 	u_char* buf  = inputdata->buffer.buf;
267 	unsigned pos = inputdata->buffer.pos;
268 
269 	/* The usage of this function is identical to input_do_delete_raw(),
270 	 * but instead of bytes, n means the number of characters.
271 	 * Since characters may consist of 1-4 bytes, this
272 	 * function converts the number to bytes and then
273 	 * calls input_do_delete_raw().
274 	 */
275 	if (n > 0)
276 	{
277 		int bytes;
278 		for (bytes = 0; buf[pos] != '\0' && n > 0; --n)
279 		{
280 			unsigned length = calc_unival_length(buf+pos);
281 			bytes += length;
282 			pos   += length;
283 		}
284 		input_do_delete_raw(bytes, do_save_cut);
285 	}
286 	else if (n < 0)
287 	{
288 		unsigned limit = inputdata->buffer.minpos;
289 		int bytes;
290 
291 		for (bytes = 0; n < 0 && pos > limit; ++n)
292 		{
293 			/* Go back until we reach a beginning of a character */
294 			for (;;)
295 			{
296 				unsigned length = calc_unival_length(buf + --pos);
297 				if (length)
298 				{
299 					bytes += length;
300 					break;
301 				}
302 				/* But don't go more than we're allowed to */
303 				if (pos == limit)
304 					break;
305 			}
306 		}
307 		input_do_delete_raw(-bytes, do_save_cut);
308 	}
309 }
310 
311 static void
input_do_replace_prompt(u_char * newprompt)312 input_do_replace_prompt(u_char *newprompt)
313 {
314 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
315 	ScreenInputBufferData* bufdata = &inputdata->buffer;
316 	u_char* buf  = bufdata->buf;
317 	unsigned oldlen = bufdata->minpos;
318 	unsigned newlen = my_strlen(newprompt);
319 	unsigned max = sizeof(bufdata->buf);
320 	unsigned saved_len = max - (oldlen > newlen ? oldlen : newlen);
321 
322 	memmove(buf+newlen,
323 	        buf+oldlen,
324 	        saved_len);
325 	memcpy(buf,
326 	       newprompt,
327 	       newlen);
328 	buf[max-1] = '\0'; /* prevent dragons */
329 
330 	bufdata->minpos = newlen;
331 	bufdata->pos	= bufdata->pos - oldlen + newlen;
332 
333 	if (bufdata->pos < newlen)
334 		bufdata->pos = newlen;
335 }
336 
337 static int
input_is_password_prompt(void)338 input_is_password_prompt(void)
339 {
340 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
341 	u_char* buf    = inputdata->buffer.buf;
342 	unsigned limit = inputdata->buffer.minpos;
343 
344 	if (limit < 9)
345 		return 0;
346 
347 	/* If the prompt ends with "Password:", it is a password prompt. */
348 	/* This includes the following known prompts:
349 	 *   "Password:"
350 	 *   "Operator Password:"
351 	 *   "Server Password:"
352 	 */
353 	return my_strnicmp(buf+limit-9, UP("Password:"), 9) == 0;
354 }
355 
356 static char
input_do_check_prompt(int update)357 input_do_check_prompt(int update)
358 {
359 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
360 	u_char	*prompt;
361 	u_char	*ptr;
362 	char	changed = 0;
363 	int	free_it = 1;
364 	unsigned len;
365 	int	args_used;	/* unused */
366 
367 	if (update == NO_UPDATE)
368 		return changed;
369 
370 	prompt = prompt_current_prompt();
371 	if (!prompt)
372 		prompt = input_prompt ? input_prompt : empty_string();
373 
374 	if (is_process(get_target_by_refnum(0)))
375 	{
376 		ptr = get_prompt_by_refnum(0);
377 		free_it = 0;
378 	}
379 	else
380 		ptr = expand_alias(NULL, prompt, empty_string(), &args_used, NULL);
381 
382 	len = my_strlen(ptr);
383 	if (my_strncmp(ptr, inputdata->buffer.buf, len) || !len)
384 	{
385 		input_do_replace_prompt(ptr);
386 		changed = 1;
387 	}
388 	if (free_it)
389 		new_free(&ptr);
390 	return changed;
391 }
392 
393 static char
input_check_resized(void)394 input_check_resized(void)
395 {
396 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
397 	int	new_li = get_li();
398 	int	new_co = get_co();
399 
400 	if (inputdata->old_li == new_li && inputdata->old_co == new_co)
401 		return 0;
402 
403 	/* resized?  Keep it simple and reset everything */
404 	inputdata->cursor_x = 0;
405 	inputdata->cursor_y = new_li - 1;
406 	inputdata->old_li   = new_li;
407 	inputdata->old_co   = new_co;
408 
409 	inputdata->zone     = new_co;
410 	if (inputdata->zone > WIDTH)
411 		inputdata->zone -= WIDTH;
412 	return 1;
413 }
414 
415 /*
416  * update_input: does varying amount of updating on the input line depending
417  * upon the position of the cursor and the update flag.  If the cursor has
418  * move toward one of the edge boundaries on the screen, update_cursor()
419  * flips the input line to the next (previous) line of text. The update flag
420  * may be:
421  *
422  * NO_UPDATE - only do the above bounds checking.
423  *
424  * UPDATE_JUST_CURSOR - do bounds checking and position cursor where is should
425  * be.
426  *
427  * UPDATE_FROM_CURSOR - does all of the above, and makes sure everything from
428  * the cursor to the right edge of the screen is current (by redrawing it).
429  *
430  * UPDATE_ALL - redraws the entire line
431  */
432 void
update_input(update)433 update_input(update)
434 	int	update;
435 {
436 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
437 	iconv_t display_conv = NULL;
438 	int redraw_from_pos = 0;
439 
440 	int term_echo = 1;
441 
442 	if (term_basic())
443 		return;
444 
445 	cursor_to_input();
446 
447 	if (input_do_check_prompt(update))
448 		update = UPDATE_ALL;
449 
450 	term_echo = !input_is_password_prompt();
451 
452 	if (input_check_resized())
453 		update = UPDATE_ALL;
454 
455 	if (update != NO_UPDATE)
456 	{
457 #ifdef HAVE_ICONV_OPEN
458 		const char *enc = CP(current_display_encoding());
459 		if (!enc)
460 			enc = "ISO-8859-1";
461 		display_conv = iconv_open(enc, "UTF-8");
462 #endif /* HAVE_ICONV_OPEN */
463 	}
464 
465 	if (update == UPDATE_FROM_CURSOR)
466 	{
467 		redraw_from_pos = inputdata->buffer.pos;
468 	}
469 	if (update == UPDATE_JUST_CURSOR || update == UPDATE_ALL)
470 	{
471 		u_char* buf  = inputdata->buffer.buf;
472 		unsigned pos = inputdata->buffer.pos;
473 		unsigned limit = inputdata->buffer.minpos;
474 		unsigned column, ptr;
475 		unsigned window;
476 
477 		/* Recalculate pos_column */
478 		for (column = ptr = 0; ptr < pos; )
479 		{
480 			unsigned unival = calc_unival(buf+ptr);
481 
482 			if (!term_echo && ptr >= limit)
483 				unival = ' ';
484 
485 			column += input_do_calculate_width(unival, display_conv);
486 			ptr    += calc_unival_length(buf+ptr);
487 		}
488 		inputdata->pos_column = column;
489 
490 		window = column - (column % inputdata->zone);
491 
492 		/* Recalculate left_ptr */
493 		for (column = ptr = 0; column < window; )
494 		{
495 			unsigned unival = calc_unival(buf+ptr);
496 
497 			if (!term_echo && ptr >= limit)
498 				unival = ' ';
499 
500 			column += input_do_calculate_width(unival, display_conv);
501 			ptr    += calc_unival_length(buf+ptr);
502 		}
503 
504 		/*
505 		 * XXX this is a hack.  this avoids the cursor going back to the
506 		 * very start of the line when scrolling input, but instead keeping
507 		 * WIDTH worth of characters on the input line visible from the
508 		 * prior context.
509 		 */
510 		if (ptr > WIDTH && get_co() > (WIDTH*2))
511 		{
512 			ptr -= WIDTH;
513 			column -= WIDTH;
514 		}
515 
516 		/* If the left edge has been moved, redraw the input line */
517 		if (ptr != inputdata->left_ptr)
518 			update = UPDATE_ALL;
519 		inputdata->left_ptr = ptr;
520 
521 		/* Recalculate cursor_x */
522 		inputdata->cursor_x = inputdata->pos_column - column;
523 
524 		/* delete this when the above hack is fixed properly. */
525 		Debug(DB_CURSOR, "inputdata: column %u pos_column %u left_ptr %u cursor_x %u (window %u)",
526 		      column, inputdata->pos_column, inputdata->left_ptr, inputdata->cursor_x, window);
527 	}
528 
529 	if (update == UPDATE_FROM_CURSOR || update == UPDATE_ALL)
530 	{
531 		unsigned limit = inputdata->buffer.minpos;
532 		u_char *buf = inputdata->buffer.buf;
533 		u_char VisBuf[BIG_BUFFER_SIZE]; /* XXX */
534 		unsigned column, iptr, optr;
535 		int written;
536 
537 		for (column = 0, optr = 0, iptr = inputdata->left_ptr;
538 			buf[iptr] != '\0' && column < get_co(); )
539 		{
540 			unsigned unival = calc_unival(buf+iptr);
541 			unsigned len = calc_unival_length(buf+iptr);
542 
543 			if (!term_echo && iptr >= limit)
544 				unival = ' ';
545 
546 			if (displayable_unival(unival, display_conv))
547 			{
548 				column += calc_unival_width(unival);
549 				if (!term_echo && unival == ' ')
550 				{
551 					memcpy(VisBuf+optr, " ", 1);
552 					optr += 1;
553 				}
554 				else
555 				{
556 					memcpy(VisBuf+optr, buf+iptr, len);
557 					optr += len;
558 				}
559 				iptr += len;
560 			}
561 			else
562 			{
563 				unsigned n;
564 
565 				VisBuf[optr++] = REV_TOG;
566 				for (n = 0; n < len; ++n)
567 					VisBuf[optr++] = (buf[iptr++] & 127) | 64;
568 				VisBuf[optr++] = REV_TOG;
569 				column += len;
570 			}
571 		}
572 		VisBuf[optr] = '\0';
573 		if (redraw_from_pos > inputdata->cursor_x)
574 			redraw_from_pos = 0;
575 		term_move_cursor(redraw_from_pos, inputdata->cursor_y);
576 		written = output_line(VisBuf + redraw_from_pos, 0);
577 		if (term_clear_to_eol() && written < get_co())
578 		{
579 			/* EOL wasn't implemented, so do it with spaces */
580 			term_space_erase(get_co() - written);
581 		}
582 	}
583 
584 	if (update != NO_UPDATE)
585 	{
586 		/* Always update cursor */
587 		term_move_cursor(inputdata->cursor_x, inputdata->cursor_y);
588 	}
589 
590 	if (update != NO_UPDATE)
591 	{
592 #ifdef HAVE_ICONV_OPEN
593 		iconv_close(display_conv);
594 #endif /* HAVE_ICONV_OPEN */
595 	}
596 
597 	term_flush();
598 }
599 
600 void
refresh_inputline(u_int key,u_char * ptr)601 refresh_inputline(u_int key, u_char *ptr)
602 {
603 	update_input(UPDATE_ALL);
604 }
605 
606 /* XXXMRG: i don't think this recurses properly since utf8 */
607 void
change_input_prompt(int direction)608 change_input_prompt(int direction)
609 {
610 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
611 	int count = prompt_active_count();
612 
613 	if (count == 0)
614 	{
615 		/* Restore stuff */
616 		memcpy(&inputdata->saved_buffer, &inputdata->buffer,
617 		       sizeof(inputdata->buffer));
618 	}
619 	else if (direction == -1)
620 	{
621 		/* Just update whatever should be on screen */
622 	}
623 	else if (count == 1)
624 	{
625 		/* Save stuff */
626 		memcpy(&inputdata->buffer, &inputdata->saved_buffer,
627 		       sizeof(inputdata->buffer));
628 
629 		/* Erase input line */
630 		*inputdata->buffer.buf = '\0';
631 		inputdata->buffer.pos = inputdata->buffer.minpos = 0;
632 	}
633 	update_input(UPDATE_ALL);
634 }
635 
636 /* input_move_cursor: moves the cursor left or right... got it? */
637 /* zero=left, nonzero=right */
638 void
input_move_cursor(int dir)639 input_move_cursor(int dir)
640 {
641 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
642 	u_char* buf  = inputdata->buffer.buf;
643 	unsigned pos = inputdata->buffer.pos;
644 	if (dir)
645 	{
646 		/* if there are still chars remaining */
647 		if (buf[pos] != '\0')
648 		{
649 			/* Skip over 1 character */
650 			pos += calc_unival_length(buf+pos);
651 
652 			input_do_set_cursor_pos(pos);
653 		}
654 	}
655 	else
656 	{
657 		unsigned limit = inputdata->buffer.minpos;
658 
659 		if (pos > limit)
660 		{
661 			/* Go back until we reach a beginning of a character */
662 			while (!calc_unival_length(buf + --pos))
663 			{
664 				/* But don't go more than we're allowed to */
665 				if (pos == limit)
666 					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(u_int key,u_char * ptr)678 input_forward_word(u_int key, u_char *ptr)
679 {
680 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
681 	u_char* buf  = inputdata->buffer.buf;
682 	unsigned pos = inputdata->buffer.pos;
683 
684 	/* Skip while definitely space, then skip while definitely nonspace */
685 	/* For nontypical characters, does nothing */
686 
687 	while (buf[pos] != '\0')
688 	{
689 		unsigned unival = calc_unival(buf+pos);
690 
691 		if (!isspace(unival) && !ispunct(unival))
692 			break;
693 		pos += calc_unival_length(buf+pos);
694 	}
695 	while (buf[pos] != '\0')
696 	{
697 		unsigned unival = calc_unival(buf+pos);
698 		if (isspace(unival) || ispunct(unival) || unival >= 0x300)
699 			break;
700 		pos += calc_unival_length(buf+pos);
701 	}
702 	input_do_set_cursor_pos(pos);
703 }
704 
705 /* input_backward_word: move the cursor left on word in the input line */
706 void
input_backward_word(u_int key,u_char * ptr)707 input_backward_word(u_int key, u_char *ptr)
708 {
709 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
710 	u_char* buf  = inputdata->buffer.buf;
711 	unsigned pos = inputdata->buffer.pos;
712 	unsigned limit = inputdata->buffer.minpos;
713 
714 	/* Skip while previous is definitely space,         */
715 	/* then skip while previous is definitely nonspace. */
716 	/* For nontypical characters, does nothing */
717 	while (pos > limit)
718 	{
719 		unsigned prevpos = pos, unival;
720 
721 		while (!calc_unival_length(buf + --prevpos))
722 			if (prevpos <= limit)
723 				break;
724 		unival = calc_unival(buf + prevpos);
725 		if (!isspace(unival) && !ispunct(unival))
726 			break;
727 		pos = prevpos;
728 	}
729 	while (pos > limit)
730 	{
731 		unsigned prevpos = pos, unival;
732 		while (!calc_unival_length(buf + --prevpos))
733 			if (prevpos <= limit)
734 				break;
735 		unival = calc_unival(buf + prevpos);
736 		if (isspace(unival) || ispunct(unival) || unival >= 0x300)
737 			break;
738 		pos = prevpos;
739 	}
740 	input_do_set_cursor_pos(pos);
741 }
742 
743 /* input_delete_character: deletes a character from the input line */
744 void
input_delete_character(u_int key,u_char * ptr)745 input_delete_character(u_int key, u_char *ptr)
746 {
747 	input_do_delete_chars(1, 0);
748 }
749 
750 /* input_backspace: does a backspace in the input buffer */
751 void
input_backspace(u_int key,u_char * ptr)752 input_backspace(u_int key, u_char *ptr)
753 {
754 	input_do_delete_chars(-1, 0);
755 }
756 
757 /*
758  * input_beginning_of_line: moves the input cursor to the first character in
759  * the input buffer
760  */
761 void
input_beginning_of_line(u_int key,u_char * ptr)762 input_beginning_of_line(u_int key, u_char *ptr)
763 {
764 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
765 
766 	input_do_set_cursor_pos(inputdata->buffer.minpos);
767 }
768 
769 /*
770  * input_end_of_line: moves the input cursor to the last character in the
771  * input buffer
772  */
773 void
input_end_of_line(u_int key,u_char * ptr)774 input_end_of_line(u_int key, u_char *ptr)
775 {
776 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
777 
778 	input_do_set_cursor_pos(my_strlen(inputdata->buffer.buf));
779 }
780 
781 /*
782  * input_delete_previous_word: deletes from the cursor backwards to the next
783  * space character.
784  */
785 void
input_delete_previous_word(u_int key,u_char * ptr)786 input_delete_previous_word(u_int key, u_char *ptr)
787 {
788 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
789 
790 	unsigned pos = inputdata->buffer.pos;
791 	int bytes;
792 
793 	/* moves cursor backwards */
794 	input_backward_word(key,ptr);
795 
796 	bytes = pos - inputdata->buffer.pos;
797 
798 	/* now that we're back, delete next n bytes */
799 	input_do_delete_raw(bytes, 1);
800 }
801 
802 /*
803  * input_delete_next_word: deletes from the cursor to the end of the next
804  * word
805  */
806 void
input_delete_next_word(u_int key,u_char * ptr)807 input_delete_next_word(u_int key, u_char *ptr)
808 {
809 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
810 	unsigned pos = inputdata->buffer.pos;
811 	int bytes;
812 
813 	/* moves cursor forward */
814 	input_forward_word(key,ptr);
815 
816 	bytes = inputdata->buffer.pos - pos;
817 
818 	/* now that we're after the word, delete past n bytes */
819 	input_do_delete_raw(-bytes, 1);
820 }
821 
822 /*
823  * input_add_character: adds the byte c to the input buffer, respecting
824  * the current overwrite/insert mode status, etc
825  */
826 void
input_add_character(u_int key,u_char * ptr)827 input_add_character(u_int key, u_char *ptr)
828 {
829 	u_char output_buffer[8];
830 
831 #ifdef HAVE_ICONV_OPEN
832 	static u_char   input_buffer[32]="";
833 	static unsigned input_pos=0;
834 	size_t retval;
835 
836 	iconv_const char *iptr;
837 	char *optr;
838 	size_t isize, osize;
839 
840 	input_buffer[input_pos++] = key;
841 
842 	if (!mbdata_ok)
843 	{
844 		mbdata_init(&mbdata, CP(current_input_encoding()));
845 		mbdata_ok = 1;
846 	}
847 
848 re_encode:
849 	if (!input_pos)
850 	{
851 		return;
852 	}
853 	iptr = (iconv_const char *)input_buffer;
854 	isize = input_pos;
855 	optr = (char *)output_buffer;
856 	osize = sizeof output_buffer;
857 
858 	retval = iconv(mbdata.conv_in,
859 	               &iptr, &isize,
860 	               (char **)&optr, &osize);
861 
862 	if (retval == (size_t)-1)
863 	{
864 		switch (errno)
865 		{
866 		default:
867 			/* User didn't give enough bytes. Must give more later. */
868 			return;
869 		case EILSEQ:
870 			/* User gave a bad byte. Ignore bad bytes! */
871 			memmove(input_buffer+1,
872 				input_buffer,
873 				--input_pos);
874 			goto re_encode;
875 		}
876 	}
877 	*optr = '\0';
878 	input_pos = 0;
879 #else
880 	/* no iconv, assume in=iso-8859-1 */
881 	utf8_sequence(key, output_buffer);
882 #endif /* HAVE_ICONV_OPEN */
883 
884 	if (!get_int_var(INSERT_MODE_VAR))
885 	{
886 		/* Delete the next character */
887 		input_do_delete_chars(1, 0);
888 	}
889 	input_do_insert_raw(output_buffer);
890 }
891 
892 /*
893  * set_input: sets the input buffer to the given string, discarding whatever
894  * was in the input buffer before
895  */
896 void
set_input(u_char * str)897 set_input(u_char *str)
898 {
899 	u_char converted_input[INPUT_BUFFER_SIZE];
900 	struct mb_data mbdata1;
901 	unsigned dest;
902 
903 #ifdef HAVE_ICONV_OPEN
904 	mbdata_init(&mbdata1, CP(current_irc_encoding()));
905 #else
906 	mbdata_init(&mbdata1, NULL);
907 #endif /* HAVE_ICONV_OPEN */
908 	for (dest = 0; *str != '\0'; )
909 	{
910 		if (dest + 8 >= sizeof(converted_input))
911 			break;
912 
913 		decode_mb(str, converted_input + dest,
914 			  sizeof(converted_input) - dest, &mbdata1);
915 		dest += mbdata1.output_bytes;
916 		str  += mbdata1.input_bytes;
917 	}
918 	converted_input[dest] = '\0';
919 
920 	set_input_raw(converted_input);
921 }
922 
923 void
set_input_raw(str)924 set_input_raw(str)
925 	u_char* str;
926 {
927 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
928 	u_char* buf    = inputdata->buffer.buf;
929 	unsigned pos   = inputdata->buffer.pos;
930 	unsigned limit = inputdata->buffer.minpos;
931 
932 	strmcpy(buf + limit,
933 		str,
934 		(size_t)sizeof(inputdata->buffer.buf) - limit);
935 
936 	pos = my_strlen(buf);
937 
938 	inputdata->buffer.pos = pos;
939 
940 	if (mbdata_ok)
941 	{
942 		mbdata_done(&mbdata);
943 		mbdata_ok = 0;
944 	}
945 }
946 
947 /*
948  * get_input: returns a pointer to a copy of the input buffer.
949  */
950 u_char	*
get_input(void)951 get_input(void)
952 {
953 	iconv_const char *source = (iconv_const char*)get_input_raw();
954 	/*
955 	 * The input buffer is UTF-8 encoded. Clients will want
956 	 * current_irc_encoding() instead.
957 	 */
958 	static u_char converted_buffer[INPUT_BUFFER_SIZE];
959 
960 #ifdef HAVE_ICONV_OPEN
961 	const char *enc = CP(current_irc_encoding());
962 	iconv_t conv;
963 	char* dest = (char *)converted_buffer;
964 	size_t left, space;
965 
966 	left = my_strlen(source);
967 	space = sizeof(converted_buffer);
968 	if (!enc)
969 		enc = "ISO-8859-1";
970 	conv = iconv_open(enc, "UTF-8");
971 	while (*source != '\0')
972 	{
973 		size_t retval;
974 
975 		retval = iconv(conv,
976 		               &source, &left,
977 		               &dest, &space);
978 		if (retval == (size_t)-1)
979 		{
980 			if (errno == EILSEQ)
981 			{
982 				if (left == 0)
983 					break;
984 
985 				/* Ignore silently 1 illegal byte */
986 				++source;
987 				--left;
988 			} else
989 				break;
990 		}
991 	}
992 	/* Reset the converter, create a reset-sequence */
993 	iconv(conv, NULL, &left, &dest, &space);
994 
995 	/* Ensure null-terminators are where they should be */
996 	converted_buffer[sizeof(converted_buffer)-1] = '\0';
997 	*dest = '\0';
998 
999 	iconv_close(conv);
1000 #else
1001 	/* Must convert manually - assume output is ISO-8859-1 */
1002 	unsigned dest = 0;
1003 
1004 	while (*source != '\0')
1005 	{
1006 		unsigned len = calc_unival_length(UP(source));
1007 		unsigned unival;
1008 
1009 		if (!len)
1010 		{
1011 			/* ignore illegal byte (shouldn't happen) */
1012 			++source;
1013 			continue;
1014 		}
1015 		unival = calc_unival(UP(source));
1016 		if (displayable_unival(unival, NULL))
1017 		{
1018 			converted_buffer[dest++] = unival;
1019 			if (dest+1 >= sizeof(converted_buffer))
1020 				break;
1021 		}
1022 		source += len;
1023 	}
1024 	converted_buffer[dest] = '\0';
1025 #endif /* HAVE_ICONV_OPEN */
1026 
1027 	return converted_buffer;
1028 }
1029 
1030 u_char	*
get_input_raw()1031 get_input_raw()
1032 {
1033 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
1034 	u_char* buf    = inputdata->buffer.buf;
1035 	unsigned limit = inputdata->buffer.minpos;
1036 
1037 	return buf+limit;
1038 }
1039 
1040 /* input_clear_to_eol: erases from the cursor to the end of the input buffer */
1041 void
input_clear_to_eol(u_int key,u_char * ptr)1042 input_clear_to_eol(u_int key, u_char *ptr)
1043 {
1044 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
1045 	u_char* buf  = inputdata->buffer.buf;
1046 	unsigned pos = inputdata->buffer.pos;
1047 	int bytes;
1048 
1049 	for (bytes = 0; buf[pos] != '\0';)
1050 	{
1051 		unsigned length = calc_unival_length(buf+pos);
1052 		bytes += length;
1053 		pos   += length;
1054 	}
1055 
1056 	input_do_delete_raw(bytes, 1);
1057 }
1058 
1059 /*
1060  * input_clear_to_bol: clears from the cursor to the beginning of the input
1061  * buffer
1062  */
1063 void
input_clear_to_bol(u_int key,u_char * ptr)1064 input_clear_to_bol(u_int key, u_char *ptr)
1065 {
1066 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
1067 	u_char* buf  = inputdata->buffer.buf;
1068 	unsigned pos = inputdata->buffer.pos;
1069 	int bytes;
1070 
1071 	unsigned limit = inputdata->buffer.minpos;
1072 	for (bytes = 0; limit < pos; )
1073 	{
1074 		unsigned length = calc_unival_length(buf+limit);
1075 		bytes += length;
1076 		limit += length;
1077 	}
1078 
1079 	input_do_delete_raw(-bytes, 1);
1080 }
1081 
1082 /*
1083  * input_clear_line: clears entire input line
1084  */
1085 void
input_clear_line(u_int key,u_char * ptr)1086 input_clear_line(u_int key, u_char *ptr)
1087 {
1088 	input_beginning_of_line(key, ptr);
1089 	input_clear_to_eol(key, ptr);
1090 }
1091 
1092 /*
1093  * input_transpose_characters: swaps the positions of the two characters
1094  * before the cursor position and moves cursor 1 forward
1095  */
1096 void
input_transpose_characters(u_int key,u_char * ptr)1097 input_transpose_characters(u_int key, u_char *ptr)
1098 {
1099 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
1100 	u_char* buf  = inputdata->buffer.buf;
1101 	unsigned pos = inputdata->buffer.pos;
1102 
1103 	/*
1104 	  delete previous char,
1105 	  then move 1 right,
1106 	  then insert the deleted char
1107 
1108 	  example (^ denotes cursor position):
1109 
1110 		before:
1111 		  defg
1112 		    ^
1113 		after:
1114 		  dfeg
1115 		     ^
1116 
1117 	  this is how bash transposes at least /Bisqwit
1118 
1119 	  FIXME: cut-buffer shouldn't be affected when transposing
1120 	*/
1121 	if (!buf[pos])
1122 	{
1123 		/* back off 1 char */
1124 		input_move_cursor(0);
1125 		pos = inputdata->buffer.pos;
1126 		/* If we're still at the end of the string,
1127 		 * the string consists of only 1 char.
1128 		 * In which case there's nothing to transpose.
1129 		 */
1130 		if (!buf[pos])
1131 		{
1132 			return;
1133 		}
1134 
1135 	}
1136 	input_do_delete_chars(-1, 1);
1137 	input_move_cursor(1);
1138 	input_yank_cut_buffer(key, ptr);
1139 }
1140 
1141 /* init_input: initialized the input buffer by clearing it out */
1142 void
init_input(void)1143 init_input(void)
1144 {
1145 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
1146 	*inputdata->buffer.buf = '\0';
1147 	inputdata->buffer.pos = inputdata->buffer.minpos;
1148 }
1149 
1150 /*
1151  * input_yank_cut_buffer: takes the contents of the cut buffer and inserts it
1152  * into the input line
1153  */
1154 void
input_yank_cut_buffer(u_int key,u_char * ptr)1155 input_yank_cut_buffer(u_int key, u_char *ptr)
1156 {
1157 	if (cut_buffer)
1158 		input_do_insert_raw(cut_buffer);
1159 }
1160 
1161 /* get_input_prompt: returns the current input_prompt */
1162 u_char	*
get_input_prompt(void)1163 get_input_prompt(void)
1164 {
1165 	return (input_prompt);
1166 }
1167 
1168 /*
1169  * set_input_prompt: sets a prompt that will be displayed in the input
1170  * buffer.  This prompt cannot be backspaced over, etc.  It's a prompt.
1171  * Setting the prompt to null uses no prompt
1172  */
1173 void
set_input_prompt(u_char * prompt)1174 set_input_prompt(u_char *prompt)
1175 {
1176 	if (!prompt)
1177 	{
1178 		malloc_strcpy(&input_prompt, empty_string());
1179 	}
1180 	else
1181 	{
1182 		u_char converted_prompt[INPUT_BUFFER_SIZE];
1183 		struct mb_data mbdata1;
1184 		unsigned dest;
1185 #ifdef HAVE_ICONV_OPEN
1186 		mbdata_init(&mbdata1, CP(current_irc_encoding()));
1187 #else
1188 		mbdata_init(&mbdata1, NULL);
1189 #endif /* HAVE_ICONV_OPEN */
1190 		for (dest = 0; *prompt != '\0'; )
1191 		{
1192 			decode_mb(prompt, converted_prompt + dest,
1193 				  sizeof(converted_prompt) - dest, &mbdata1);
1194 			dest   += mbdata1.output_bytes;
1195 			prompt += mbdata1.input_bytes;
1196 		}
1197 		converted_prompt[dest] = '\0';
1198 
1199 		if (input_prompt && !my_strcmp(converted_prompt, input_prompt))
1200 			return;
1201 		malloc_strcpy(&input_prompt, converted_prompt);
1202 	}
1203 	update_input(UPDATE_ALL);
1204 }
1205 
1206 void
input_reset_screen(Screen * new)1207 input_reset_screen(Screen *new)
1208 {
1209 	ScreenInputData *inputdata = screen_get_inputdata(new);
1210 
1211 	if (inputdata == NULL)
1212 	{
1213 		inputdata = new_malloc(sizeof *inputdata);
1214 		screen_set_inputdata(new, inputdata);
1215 	}
1216 	inputdata->old_li = -1;
1217 	inputdata->old_co = -1;
1218 	inputdata->cursor_x = 0;
1219 	inputdata->cursor_y = 0;
1220 	inputdata->left_ptr = 0;
1221 	inputdata->pos_column = 0;
1222 	inputdata->buffer.pos = 0;
1223 	inputdata->buffer.minpos = 0;
1224 	inputdata->buffer.buf[0] = '\0';
1225 }
1226 
1227 u_char	*
function_curpos(input)1228 function_curpos(input)
1229 	u_char	*input;
1230 {
1231 	ScreenInputData *inputdata = screen_get_inputdata(get_current_screen());
1232 	u_char	*new = (u_char *) 0,
1233 		pos[8];
1234 
1235 	snprintf(CP(pos), sizeof pos, "%d", inputdata->buffer.pos);
1236 	malloc_strcpy(&new, pos);
1237 	return new;
1238 }
1239 
1240 u_char	*
alias_buffer(void)1241 alias_buffer(void)
1242 {
1243 	return cut_buffer;
1244 }
1245