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 = ¤t_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 = ¤t_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 = ¤t_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(¤t_screen->inputdata.saved_buffer,
615 ¤t_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(¤t_screen->inputdata.buffer,
626 ¤t_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 = ¤t_screen->inputdata;
1361
1362 inputdata->old_co = 0; /* to force recalculation */
1363 input_check_resized();
1364 }
1365 /****************************************************************************/
1366