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