1 /*
2
3 editline.c
4
5 is part of:
6
7 WinEditLine (formerly MinGWEditLine)
8 Copyright 2010-2020 Paolo Tosco <paolo.tosco.mail@gmail.com>
9 All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14
15 * Redistributions of source code must retain the above copyright
16 notice, this list of conditions and the following disclaimer.
17 * Redistributions in binary form must reproduce the above copyright
18 notice, this list of conditions and the following disclaimer in the
19 documentation and/or other materials provided with the distribution.
20 * Neither the name of WinEditLine (formerly MinGWEditLine) nor the
21 name of its contributors may 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 COPYRIGHT HOLDER AND CONTRIBUTORS
25 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
30 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37
38 #define _UNICODE
39 #define UNICODE
40
41 #include <editline/readline.h>
42 #include <editline/wineditline.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <ctype.h>
47 #include <wctype.h>
48 #include <el_globals.h>
49 #include <tchar.h>
50 #include <io.h>
51 #include <fcntl.h>
52
53
_el_display_prev_hist()54 int _el_display_prev_hist()
55 {
56 if (where_history() > 0) {
57 if (!_el_w2mb(_el_line_buffer, &rl_line_buffer)) {
58 return -1;
59 }
60 replace_history_entry(where_history(), rl_line_buffer, NULL);
61 _el_previous_history();
62 _el_display_history();
63 }
64
65 return 0;
66 }
67
68
_el_display_next_hist()69 int _el_display_next_hist()
70 {
71 if (where_history() < history_length()) {
72 if (!_el_w2mb(_el_line_buffer, &rl_line_buffer)) {
73 return -1;
74 }
75 replace_history_entry(where_history(), rl_line_buffer, NULL);
76 _el_next_history();
77 _el_display_history();
78 }
79
80 return 0;
81 }
82
83
_el_display_first_hist()84 int _el_display_first_hist()
85 {
86 if (where_history() > 0) {
87 if (!_el_w2mb(_el_line_buffer, &rl_line_buffer)) {
88 return -1;
89 }
90 replace_history_entry(where_history(), rl_line_buffer, NULL);
91 _el_history_set_pos(1);
92 _el_display_history();
93 }
94
95 return 0;
96 }
97
98
_el_display_last_hist()99 int _el_display_last_hist()
100 {
101 if (where_history() < history_length()) {
102 if (!_el_w2mb(_el_line_buffer, &rl_line_buffer)) {
103 return -1;
104 }
105 replace_history_entry(where_history(), rl_line_buffer, NULL);
106 _el_history_set_pos(history_length() + 1);
107 _el_display_history();
108 }
109
110 return 0;
111 }
112
113
_el_mb2w(char * mb,wchar_t ** w)114 wchar_t *_el_mb2w(char *mb, wchar_t **w)
115 {
116 int len_w;
117
118
119 len_w = MultiByteToWideChar(CP_UTF8, 0, mb, -1, NULL, 0);
120 if ((*w = (wchar_t *)realloc(*w, len_w * sizeof(wchar_t)))) {
121 MultiByteToWideChar(CP_UTF8, 0, mb, -1, *w, len_w);
122 }
123
124 return *w;
125 }
126
127
_el_w2mb(wchar_t * w,char ** mb)128 char *_el_w2mb(wchar_t *w, char **mb)
129 {
130 int len_mb;
131
132
133 len_mb = WideCharToMultiByte(CP_UTF8, 0, w, -1, NULL, 0, NULL, NULL);
134 if ((*mb = realloc(*mb, len_mb))) {
135 WideCharToMultiByte(CP_UTF8, 0, w, -1, *mb, len_mb, NULL, NULL);
136 }
137
138 return *mb;
139 }
140
141
_el_alloc_array(int n,int size)142 char **_el_alloc_array(int n, int size)
143 {
144 char **array;
145 int i;
146
147
148 if (!(array = (char **)malloc((n + 1) * sizeof(char *)))) {
149 return NULL;
150 }
151 memset(array, 0, (n + 1) * sizeof(char *));
152 for (i = 0; i < n; ++i) {
153 if (!(array[i] = (char *)malloc(size))) {
154 _el_free_array(array);
155 return NULL;
156 }
157 memset(array[i], 0, size);
158 }
159
160 return array;
161 }
162
163
_el_free_array(void * array)164 void _el_free_array(void *array)
165 {
166 int i = 0;
167
168
169 if (array) {
170 while (((char **)array)[i]) {
171 free(((char **)array)[i]);
172 ((char **)array)[i] = NULL;
173 ++i;
174 }
175 free(array);
176 }
177 }
178
179
180 /*
181 catch CTRL+C signals
182 */
_el_signal_handler(DWORD fdwCtrlType)183 BOOL _el_signal_handler(DWORD fdwCtrlType)
184 {
185 _el_ctrl_c_pressed = FALSE;
186 switch (fdwCtrlType) {
187 case CTRL_C_EVENT:
188 if (_el_line_buffer && wcslen(_el_line_buffer)) {
189 _el_ctrl_c_pressed = TRUE;
190 }
191 else {
192 _el_clean_exit();
193 }
194 break;
195 }
196
197 return _el_ctrl_c_pressed;
198 }
199
200
201 /*
202 free memory buffers before exit
203 */
_el_clean_exit()204 void _el_clean_exit()
205 {
206 free(rl_line_buffer);
207 rl_line_buffer = NULL;
208 free(_el_line_buffer);
209 _el_line_buffer = NULL;
210 _el_free_array(_el_compl_array);
211 _el_compl_array = NULL;
212 free(_el_print);
213 _el_print = NULL;
214 free(_el_temp_print);
215 _el_temp_print = NULL;
216 free(_el_next_compl);
217 _el_next_compl = NULL;
218 free(_el_completer_word_break_characters);
219 _el_completer_word_break_characters = NULL;
220 free(_el_basic_word_break_characters);
221 _el_basic_word_break_characters = NULL;
222 free(rl_prompt);
223 rl_prompt = NULL;
224 free(_el_old_arg);
225 _el_old_arg = NULL;
226 free(_el_wide);
227 _el_wide = NULL;
228 free(_el_text);
229 _el_text = NULL;
230 free(_el_text_mb);
231 _el_text_mb = NULL;
232 free(_el_file_name);
233 _el_file_name = NULL;
234 free(_el_dir_name);
235 _el_dir_name = NULL;
236 if (_el_prev_in_cm_saved) {
237 SetConsoleMode(_el_h_in,
238 _el_prev_in_cm | ENABLE_EXTENDED_FLAGS);
239 }
240 if (_el_prev_out_cm_saved) {
241 SetConsoleMode(_el_h_out, _el_prev_out_cm);
242 }
243 SetConsoleCtrlHandler((PHANDLER_ROUTINE)
244 _el_signal_handler, FALSE);
245 }
246
247
248 /*
249 insert character(s) on the command line
250 */
_el_insert_char(wchar_t * buf,int n)251 int _el_insert_char(wchar_t *buf, int n)
252 {
253 int c;
254 int line_len;
255
256
257 line_len = (int)wcslen(_el_line_buffer);
258 /*
259 get line length from the current
260 logical cursor position
261 to the end, including the terminal '\0'
262 */
263 c = (int)wcslen(&(_el_line_buffer[rl_point])) + 1;
264 /*
265 make sure the buffer is large enough
266 */
267 _el_grow_buffers(line_len + n);
268 /*
269 make the insertion
270 */
271 memmove(&_el_line_buffer[rl_point + n],
272 &_el_line_buffer[rl_point], c * sizeof(wchar_t));
273 memcpy(&_el_line_buffer[rl_point], buf, n * sizeof(wchar_t));
274 /*
275 copy the inserted chars into the string
276 for subsequent printing
277 */
278 memcpy(_el_print, &_el_line_buffer[rl_point],
279 (c + n) * sizeof(wchar_t));
280 _el_print[c + n] = _T('\0');
281 /*
282 set the new logical cursor position
283 */
284 rl_point += n;
285 /*
286 print the insertion
287 */
288 if (_el_print_string(_el_print)) {
289 return -1;
290 }
291 /*
292 set the new cursor position
293 */
294 if (_el_set_cursor(n)) {
295 return -1;
296 }
297 if (!_el_w2mb(_el_line_buffer, &rl_line_buffer)) {
298 return -1;
299 }
300
301 return 0;
302 }
303
304
305 /*
306 delete character(s) on the command line
307 */
_el_delete_char(UINT32 vk,int n)308 int _el_delete_char(UINT32 vk, int n)
309 {
310 int c;
311 int line_len;
312 int eff_n;
313
314
315 line_len = (int)wcslen(_el_line_buffer);
316 eff_n = n;
317 switch (vk) {
318 case VK_DELETE:
319 /*
320 the character in correspondence
321 of the cursor is to be deleted;
322 check that we are not going to delete
323 more characters than those which exist
324 between the logical cursor position
325 and the end of line
326 */
327 if ((line_len - rl_point) < n) {
328 eff_n = line_len - rl_point;
329 }
330
331 break;
332
333 case VK_BACK:
334 /*
335 the character before
336 the cursor is to be deleted
337 check that we are not going to delete
338 more characters than those which exist
339 between the logical cursor position
340 and the beginning of line
341 */
342 if ((rl_point - n) >= 0) {
343 rl_point -= n;
344 }
345 else {
346 eff_n = rl_point;
347 rl_point = 0;
348 }
349 /*
350 with backspace we need to reposition
351 the cursor as well
352 */
353 if (_el_set_cursor(-eff_n)) {
354 return -1;
355 }
356 break;
357
358 default:
359 return -1;
360 }
361 c = (int)wcslen(&_el_line_buffer[rl_point]) + 1;
362 /*
363 cut out deleted characters
364 */
365 memmove(&_el_line_buffer[rl_point],
366 &_el_line_buffer[rl_point + eff_n],
367 (c - eff_n) * sizeof(wchar_t));
368 /*
369 copy the characters from the current cursor
370 position to end of line in a string
371 */
372 memcpy(_el_print, &_el_line_buffer[rl_point],
373 (c - eff_n) * sizeof(wchar_t));
374 _el_print[c - eff_n] = '\0';
375 /*
376 add spaces to the string to be printed
377 to clear out "tails" from the command line
378 */
379 _el_add_char(_el_print, _T(' '), n);
380 if (!_el_w2mb(_el_line_buffer, &rl_line_buffer)) {
381 return -1;
382 }
383
384 /*
385 print out and goodbye
386 */
387 return _el_print_string(_el_print);
388 }
389
390
391 /*
392 add n characters to a string
393 */
_el_add_char(wchar_t * string,wchar_t c,int n)394 void _el_add_char(wchar_t *string, wchar_t c, int n)
395 {
396 int i;
397 int len;
398
399
400 len = (int)wcslen(string);
401 for (i = 0; i < n; ++i) {
402 string[len + i] = c;
403 }
404 string[len + n] = _T('\0');
405 }
406
407
_el_pad(CONSOLE_SCREEN_BUFFER_INFO * sbInfo,wchar_t * string)408 int _el_pad(CONSOLE_SCREEN_BUFFER_INFO *sbInfo, wchar_t *string)
409 {
410 int i;
411
412
413 i = (int)wcslen(string);
414 if (i) {
415 while (i < (sbInfo->dwSize.X - sbInfo->dwCursorPosition.X)) {
416 string[i] = _T(' ');
417 ++i;
418 }
419 string[i] = _T('\0');
420 }
421
422 return i;
423 }
424
425
426 /*
427 print a string to the Windows Console
428 */
_el_print_string(wchar_t * string)429 int _el_print_string(wchar_t *string)
430 {
431 int i;
432 int len;
433 int tail_lines;
434 int padded_len;
435 int c_first_line;
436 int c_last_line = 0;
437 int dy = 0;
438 DWORD n_chars;
439 SHORT x_initial;
440 COORD temp_coord;
441 CONSOLE_SCREEN_BUFFER_INFO sbInfo;
442 int width;
443
444
445 len = (int)wcslen(string);
446 /*
447 get screen buffer info from the current console
448 */
449 if (!GetConsoleScreenBufferInfo(_el_h_out, &sbInfo)) {
450 return -1;
451 }
452 /*
453 record the initial x coordinate of the cursor
454 */
455 x_initial = sbInfo.dwCursorPosition.X;
456 /*
457 compute the current visible console width
458 */
459 width = sbInfo.srWindow.Right - sbInfo.srWindow.Left + 1;
460 /*
461 count how many characters will fit from the current
462 cursor position to the right console margin
463 */
464 c_first_line = sbInfo.srWindow.Right
465 - sbInfo.dwCursorPosition.X + 1;
466 tail_lines = (int)wcslen(&_el_line_buffer[rl_point]) / width;
467 /*
468 if the visible console width is not enough,
469 we will write dy full lines
470 */
471 if (len > c_first_line) {
472 dy = (len - c_first_line) / width;
473 /*
474 these are the residual characters which will
475 be printed on the last line
476 */
477 c_last_line = len - c_first_line - dy * width;
478 /*
479 if the string will not fit on the first line
480 pad with spaces then print it
481 */
482 wcsncpy_s(_el_temp_print, _el_temp_print_size, string, c_first_line);
483 _el_temp_print[c_first_line] = _T('\0');
484 padded_len = _el_pad(&sbInfo, _el_temp_print);
485 if ((sbInfo.dwCursorPosition.Y - tail_lines) >= sbInfo.dwSize.Y) {
486 printf("\n");
487 --(sbInfo.dwCursorPosition.Y);
488 if (!SetConsoleCursorPosition(_el_h_out,
489 sbInfo.dwCursorPosition)) {
490 return -1;
491 }
492 }
493 if (!WriteConsole(_el_h_out, _el_temp_print,
494 padded_len, &n_chars, NULL)) {
495 return -1;
496 }
497 printf("\n");
498 for (i = 0; i < dy; ++i) {
499 wcsncpy_s(_el_temp_print, _el_temp_print_size,
500 &string[c_first_line + i * width], width);
501 _el_temp_print[width] = _T('\0');
502 padded_len = _el_pad(&sbInfo, _el_temp_print);
503 if ((sbInfo.dwCursorPosition.Y - tail_lines) >= sbInfo.dwSize.Y) {
504 printf("\n");
505 --(sbInfo.dwCursorPosition.Y);
506 if (!SetConsoleCursorPosition(_el_h_out,
507 sbInfo.dwCursorPosition)) {
508 return -1;
509 }
510 }
511 if (!WriteConsole(_el_h_out, _el_temp_print,
512 padded_len, &n_chars, NULL)) {
513 return -1;
514 }
515 printf("\n");
516 }
517 /*
518 if there are residual characters on the last line
519 */
520 if (c_last_line >= 0) {
521 /*
522 get the current cursor position
523 */
524 if (!GetConsoleScreenBufferInfo(_el_h_out, &sbInfo)) {
525 return -1;
526 }
527 /*
528 record the current cursor position
529 */
530 memcpy(&temp_coord, &(sbInfo.dwCursorPosition),
531 sizeof(COORD));
532 /*
533 set the cursor to the beginning of the line
534 */
535 sbInfo.dwCursorPosition.X = 0;
536 if (!SetConsoleCursorPosition(_el_h_out,
537 sbInfo.dwCursorPosition)) {
538 return -1;
539 }
540 /*
541 write the residual characters
542 */
543 wcsncpy_s(_el_temp_print, _el_temp_print_size,
544 &string[c_first_line + dy * width], c_last_line);
545 _el_temp_print[c_last_line] = _T('\0');
546 padded_len = _el_pad(&sbInfo, _el_temp_print);
547 if ((sbInfo.dwCursorPosition.Y - tail_lines) >= sbInfo.dwSize.Y) {
548 printf("\n");
549 --(sbInfo.dwCursorPosition.Y);
550 --(temp_coord.Y);
551 if (!SetConsoleCursorPosition(_el_h_out,
552 sbInfo.dwCursorPosition)) {
553 return -1;
554 }
555 }
556 if (!WriteConsole(_el_h_out, _el_temp_print,
557 padded_len, &n_chars, NULL)) {
558 return -1;
559 }
560 /*
561 put the cursor back into the position
562 it occupied before printing
563 */
564 memcpy(&(sbInfo.dwCursorPosition), &temp_coord,
565 sizeof(COORD));
566 if (!SetConsoleCursorPosition(_el_h_out,
567 sbInfo.dwCursorPosition)) {
568 return -1;
569 }
570 }
571 /*
572 put the cursor back into the position
573 it occupied before printing
574 */
575 sbInfo.dwCursorPosition.Y -= (dy + 1);
576 sbInfo.dwCursorPosition.X = x_initial;
577 if (!SetConsoleCursorPosition(_el_h_out,
578 sbInfo.dwCursorPosition)) {
579 return -1;
580 }
581 }
582 else {
583 /*
584 if the whole string will fit on the first line
585 without need to wrap, record current
586 cursor coordinates
587 */
588 memcpy(&temp_coord, &(sbInfo.dwCursorPosition),
589 sizeof(COORD));
590 /*
591 write out the string
592 */
593 wcscpy_s(_el_temp_print, _el_temp_print_size, string);
594 padded_len = _el_pad(&sbInfo, _el_temp_print);
595 if (!WriteConsole(_el_h_out, _el_temp_print,
596 padded_len, &n_chars, NULL)) {
597 return -1;
598 }
599 /*
600 put the cursor back into the position
601 it occupied before printing
602 */
603 memcpy(&(sbInfo.dwCursorPosition), &temp_coord,
604 sizeof(COORD));
605 if (!SetConsoleCursorPosition(_el_h_out,
606 sbInfo.dwCursorPosition)) {
607 return -1;
608 }
609 }
610
611 return 0;
612 }
613
614
615 /*
616 move the cursor
617 */
_el_move_cursor(UINT32 vk,UINT32 ctrl)618 int _el_move_cursor(UINT32 vk, UINT32 ctrl)
619 {
620 int len;
621 int offset = 0;
622 int first = 0;
623
624
625 len = (int)wcslen(_el_line_buffer);
626 switch (vk) {
627 case VK_LEFT:
628 /*
629 if the user wants to go left
630 and we are already at the beginning
631 of the line, then do nothing
632 */
633 if (!rl_point) {
634 return 0;
635 }
636 /*
637 if the user wants to move left
638 characterwise, go back one character
639 with both the physical and the logical
640 cursors
641 */
642 if (!ctrl) {
643 offset = -1;
644 --rl_point;
645 }
646 else {
647 /*
648 if the user wants to move left
649 wordwise, first move left to the first
650 alphanumeric character, checking if
651 beginning of line has been reached
652 */
653 while (rl_point && (!iswalnum
654 (_el_line_buffer[rl_point]))) {
655 --rl_point;
656 --offset;
657 }
658 /*
659 move to the beginning of the word
660 */
661 first = 0;
662 while (rl_point && (iswalnum
663 (_el_line_buffer[rl_point - 1]) || (!first))) {
664 ++first;
665 --rl_point;
666 --offset;
667 }
668 }
669 break;
670
671 case VK_RIGHT:
672 /*
673 if the user wants to go right
674 and we are already at the end
675 of the line, then do nothing
676 */
677 if (rl_point == len) {
678 return 0;
679 }
680 /*
681 if the user wants to move right
682 characterwise, go ahead one character
683 with both the physical and the logical
684 cursors
685 */
686 if (!ctrl) {
687 offset = 1;
688 ++rl_point;
689 }
690 else {
691 /*
692 if the user wants to move right
693 wordwise, first move right to the first
694 alphanumeric character, checking if
695 end of line has been reached
696 */
697 while ((rl_point < len) && (!iswalnum
698 (_el_line_buffer[rl_point]))) {
699 ++rl_point;
700 ++offset;
701 }
702 /*
703 move to the end of the word
704 */
705 first = 0;
706 while ((rl_point < len) && (iswalnum
707 (_el_line_buffer[rl_point]) || (!first))) {
708 ++first;
709 ++rl_point;
710 ++offset;
711 }
712 }
713 break;
714
715 case VK_HOME:
716 /*
717 move to the beginning of line
718 */
719 offset = (rl_point ? -rl_point : 0);
720 rl_point = 0;
721 break;
722
723 case VK_END:
724 /*
725 move to the end of line
726 */
727 offset = len - rl_point;
728 rl_point = len;
729 break;
730
731 default:
732 return -1;
733 }
734
735 /*
736 set the physical cursor position
737 */
738 return (offset ? _el_set_cursor(offset) : 0);
739 }
740
741
742 /*
743 set the physical cursor position
744 */
_el_set_cursor(int offset)745 int _el_set_cursor(int offset)
746 {
747 CONSOLE_SCREEN_BUFFER_INFO sbInfo;
748 int width;
749 int old_x;
750 int dx;
751 int dy = 0;
752 int c_first_line;
753
754
755 /*
756 get screen buffer info from the current console
757 */
758 if (!GetConsoleScreenBufferInfo(_el_h_out, &sbInfo)) {
759 return -1;
760 }
761 /*
762 compute the current visible console width
763 */
764 width = sbInfo.srWindow.Right - sbInfo.srWindow.Left + 1;
765 dx = offset;
766 /*
767 if the cursor has to be moved rightwards
768 */
769 if (offset > 0) {
770 /*
771 count how many characters exist
772 before the right margin
773 */
774 c_first_line = sbInfo.srWindow.Right
775 - sbInfo.dwCursorPosition.X;
776 /*
777 if the cursor needs to be moved further,
778 move to the next line(s)
779 */
780 if (offset > c_first_line) {
781 dy = (offset - c_first_line) / width + 1;
782 }
783 dx = offset - dy * width;
784 }
785 else if (offset < 0) {
786 /*
787 count how many characters exist
788 before the left margin
789 */
790 c_first_line = sbInfo.dwCursorPosition.X
791 - sbInfo.srWindow.Left;
792 /*
793 if the cursor needs to be moved further,
794 move to the previous line(s)
795 */
796 if (-offset > c_first_line) {
797 dy = (offset + c_first_line) / width - 1;
798 }
799 dx = offset - dy * width;
800 }
801 /*
802 set the new physical cursor position
803 */
804 if ((sbInfo.dwCursorPosition.Y + dy -
805 ((int)wcslen(&_el_line_buffer[rl_point]) / width))
806 >= sbInfo.dwSize.Y) {
807 printf("\n");
808 --dy;
809 old_x = sbInfo.dwCursorPosition.X;
810 if (!GetConsoleScreenBufferInfo(_el_h_out, &sbInfo)) {
811 return -1;
812 }
813 sbInfo.dwCursorPosition.X = old_x;
814 }
815 sbInfo.dwCursorPosition.X += dx;
816 sbInfo.dwCursorPosition.Y += dy;
817
818 return (SetConsoleCursorPosition(_el_h_out,
819 sbInfo.dwCursorPosition) ? 0 : 1);
820 }
821
822
823 /*
824 check if characters already entered
825 by the user match the beginning of the
826 directory entry. If no characters have been
827 entered yet, then it is necessarily matching
828 */
_el_check_root_identity(wchar_t * root,wchar_t * entry_name)829 int _el_check_root_identity(wchar_t *root, wchar_t *entry_name)
830 {
831 int len;
832 int result = 1;
833 int open_quote;
834 int close_quote;
835
836
837 if (root) {
838 len = (int)wcslen(root);
839 if (len) {
840 open_quote = ((root[0] == _T('\"')) ? 1 : 0);
841 close_quote = ((root[len - 1] == _T('\"')) ? 1 : 0);
842 result = ((_wcsnicmp(&root[open_quote],
843 entry_name, len - close_quote)) ? 0 : 1);
844 }
845 }
846
847 return result;
848 }
849
850
851 /*
852 function to grow buffers as needed
853 size is the final number of bytes that need to fit in the buffer
854 */
_el_grow_buffers(size_t size)855 int _el_grow_buffers(size_t size)
856 {
857 size_t prev_size = _el_line_buffer_size;
858 if (_el_line_buffer_size && (size < (_el_line_buffer_size - 1))) {
859 return 1;
860 }
861 if (!size) {
862 size = 1;
863 }
864 _el_line_buffer_size = (size / _EL_BUF_LEN + 1) * _EL_BUF_LEN;
865 _el_line_buffer = (wchar_t *)realloc(_el_line_buffer, _el_line_buffer_size * sizeof(wchar_t));
866 if (_el_line_buffer) {
867 memset(&_el_line_buffer[prev_size], 0, (_el_line_buffer_size - prev_size) * sizeof(wchar_t));
868 }
869 _el_print = (wchar_t *)realloc(_el_print, _el_line_buffer_size * sizeof(wchar_t));
870 if (_el_print) {
871 memset(&_el_print[prev_size], 0, (_el_line_buffer_size - prev_size) * sizeof(wchar_t));
872 }
873 return (_el_line_buffer && _el_print ? 1 : 0);
874 }
875
876 /*
877 compare function for qsort
878 */
_el_fn_qsort_string_compare(const void * i1,const void * i2)879 int _el_fn_qsort_string_compare(const void *i1, const void *i2)
880 {
881 const wchar_t *s1 = (const wchar_t *)*(const wchar_t **)i1;
882 const wchar_t *s2 = (const wchar_t *)*(const wchar_t **)i2;
883
884 return wcscmp (s1, s2);
885 }
886
887
888 /*
889 function to free memory allocated by readline()
890 this is to avoid problems using free() whenever
891 memory is not freed by the same module which
892 previously allocated it (especially when, e.g.,
893 the DLL was built with VS and the application with
894 GCC, or viceversa)
895 */
rl_free(void * mem)896 void rl_free(void *mem)
897 {
898 free(mem);
899 }
900
901
902 /*
903 main readline function
904 */
readline(const char * prompt)905 char *readline(const char *prompt)
906 {
907 wchar_t buf[_EL_CONSOLE_BUF_LEN];
908 char **array = NULL;
909 char *ret_string = NULL;
910 char readfile_buf;
911 char have_cursor_x_start = 0;
912 int start = 0;
913 int end = 0;
914 int compl_pos = -1;
915 int n = 0;
916 int index = 0;
917 int len = 0;
918 int line_len = 0;
919 int old_width = 0;
920 int width = 0;
921 UINT32 ctrl = 0;
922 UINT32 special = 0;
923 COORD coord;
924 SHORT cursor_x_start = 0;
925 DWORD count = 0;
926 BOOL read_ok;
927 DWORD file_type;
928 DWORD actually_read;
929 INPUT_RECORD irBuffer;
930 CONSOLE_SCREEN_BUFFER_INFO sbInfo;
931
932
933 _el_ctrl_c_pressed = FALSE;
934 _el_line_buffer = NULL;
935 _el_temp_print = NULL;
936 _el_next_compl = NULL;
937 rl_line_buffer = NULL;
938 _el_file_name = NULL;
939 _el_dir_name = NULL;
940 _el_old_arg = NULL;
941 _el_wide = NULL;
942 _el_text = NULL;
943 _el_text_mb = NULL;
944 _el_compl_array = NULL;
945 _el_completer_word_break_characters = NULL;
946 rl_point = 0;
947 rl_attempted_completion_over = 0;
948 _el_compl_index = 0;
949 _el_n_compl = 0;
950 _el_h_in = NULL;
951 _el_h_out = NULL;
952 _el_line_buffer_size = 0;
953 wcscpy_s(_el_basic_file_break_characters,
954 _EL_MAX_FILE_BREAK_CHARACTERS, _EL_BASIC_FILE_BREAK_CHARACTERS);
955 memset(&coord, 0, sizeof(COORD));
956 memset(buf, 0, _EL_CONSOLE_BUF_LEN * sizeof(wchar_t));
957 memset(&irBuffer, 0, sizeof(INPUT_RECORD));
958 /*
959 allocate buffers
960 */
961 if (!_el_grow_buffers(0)) {
962 _el_clean_exit();
963 return NULL;
964 }
965 if (!_el_mb2w((char *)rl_basic_word_break_characters,
966 &_el_basic_word_break_characters)) {
967 _el_clean_exit();
968 return NULL;
969 }
970 if (rl_completer_word_break_characters) {
971 if (!_el_mb2w((char *)rl_completer_word_break_characters,
972 &_el_completer_word_break_characters)) {
973 _el_clean_exit();
974 return NULL;
975 }
976 }
977 rl_attempted_completion_over = 0;
978 rl_prompt = (prompt ? _strdup(prompt) : _strdup(""));
979 if (!rl_prompt) {
980 _el_clean_exit();
981 return NULL;
982 }
983 if (!_el_mb2w(rl_prompt, &_el_prompt)) {
984 _el_clean_exit();
985 return NULL;
986 }
987 _el_prompt_len = (int)wcslen(_el_prompt);
988 /*
989 get I/O handles for current console
990 */
991 _el_h_in = GetStdHandle(STD_INPUT_HANDLE);
992 _el_h_out = GetStdHandle(STD_OUTPUT_HANDLE);
993 if (!_el_h_in || !_el_h_out) {
994 _el_clean_exit();
995 return NULL;
996 }
997 file_type = GetFileType(_el_h_in);
998 /*
999 if we are reading from a file or a pipe, read one line and exit
1000 */
1001 if ((file_type == FILE_TYPE_DISK) || (file_type == FILE_TYPE_PIPE)) {
1002 readfile_buf = 0;
1003 len = 0;
1004 line_len = 0;
1005 while (readfile_buf != '\n') {
1006 read_ok = ReadFile(_el_h_in, &readfile_buf, 1, &actually_read, NULL);
1007 if (!(read_ok && actually_read)) {
1008 break;
1009 }
1010 if ((len + 1) >= line_len) {
1011 line_len += _EL_BUF_LEN;
1012 rl_line_buffer = realloc(rl_line_buffer, line_len);
1013 if (!rl_line_buffer) {
1014 _el_clean_exit();
1015 return NULL;
1016 }
1017 }
1018 rl_line_buffer[len++] = readfile_buf;
1019 }
1020 if (rl_line_buffer) {
1021 while (len && ((rl_line_buffer[len - 1] == '\n')
1022 || (rl_line_buffer[len - 1] == '\r'))) {
1023 --len;
1024 }
1025 rl_line_buffer[len] = '\0';
1026 ret_string = _strdup(rl_line_buffer);
1027 }
1028 _el_clean_exit();
1029 return ret_string;
1030 }
1031 /*
1032 set console modes
1033 */
1034 _el_prev_in_cm_saved = GetConsoleMode(_el_h_in, &_el_prev_in_cm);
1035 _el_prev_out_cm_saved = GetConsoleMode(_el_h_out, &_el_prev_out_cm);
1036 SetConsoleMode(_el_h_in, ENABLE_PROCESSED_INPUT
1037 | ENABLE_EXTENDED_FLAGS | ENABLE_INSERT_MODE
1038 | ENABLE_QUICK_EDIT_MODE);
1039 SetConsoleMode(_el_h_out, ENABLE_PROCESSED_OUTPUT);
1040 SetConsoleCtrlHandler((PHANDLER_ROUTINE)
1041 _el_signal_handler, TRUE);
1042 rl_point = 0;
1043 while ((buf[0] != VK_RETURN)
1044 && (!_el_ctrl_c_pressed) && _el_line_buffer) {
1045 /*
1046 get screen buffer info from the current console
1047 */
1048 if (!GetConsoleScreenBufferInfo(_el_h_out, &sbInfo)) {
1049 _el_clean_exit();
1050 return NULL;
1051 }
1052 if (!have_cursor_x_start) {
1053 have_cursor_x_start = 1;
1054 cursor_x_start = sbInfo.dwCursorPosition.X;
1055 }
1056 _el_temp_print_size = sbInfo.dwSize.X + 1;
1057 if (!(_el_temp_print = realloc(_el_temp_print,
1058 _el_temp_print_size * sizeof(wchar_t)))) {
1059 _el_clean_exit();
1060 return NULL;
1061 }
1062 _el_temp_print[0] = _T('\0');
1063 /*
1064 compute the current visible console width
1065 */
1066 width = sbInfo.srWindow.Right - sbInfo.srWindow.Left + 1;
1067 /*
1068 if the user has changed the window size
1069 update the view
1070 */
1071 if (old_width != width) {
1072 line_len = (int)wcslen(_el_line_buffer);
1073 sbInfo.dwCursorPosition.X = cursor_x_start;
1074 if (old_width) {
1075 n = (cursor_x_start + line_len - 1) / old_width;
1076 sbInfo.dwCursorPosition.Y -= n;
1077 coord.Y = sbInfo.dwCursorPosition.Y;
1078 }
1079 if (!SetConsoleCursorPosition(_el_h_out,
1080 sbInfo.dwCursorPosition)) {
1081 _el_clean_exit();
1082 return NULL;
1083 }
1084 if (_el_print_string(_el_prompt)) {
1085 _el_clean_exit();
1086 return NULL;
1087 }
1088 if (_el_set_cursor(_el_prompt_len)) {
1089 _el_clean_exit();
1090 return NULL;
1091 }
1092 if (_el_print_string(_el_line_buffer)) {
1093 _el_clean_exit();
1094 return NULL;
1095 }
1096 if (_el_set_cursor(line_len)) {
1097 _el_clean_exit();
1098 return NULL;
1099 }
1100 if (old_width && (old_width < width)) {
1101 coord.X = 0;
1102 coord.Y += (cursor_x_start + line_len - 1) / width + 1;
1103 FillConsoleOutputCharacter(_el_h_out, _T(' '),
1104 sbInfo.dwSize.X * (n + 2), coord, &count);
1105 }
1106 }
1107 old_width = width;
1108 /*
1109 wait for console events
1110 */
1111 if (!PeekConsoleInput(_el_h_in, &irBuffer, 1, &count)) {
1112 _el_clean_exit();
1113 return NULL;
1114 }
1115 if (count) {
1116 if ((irBuffer.EventType == KEY_EVENT) && irBuffer.Event.KeyEvent.bKeyDown) {
1117 /*
1118 the user pressed a key
1119 */
1120 ctrl = (irBuffer.Event.KeyEvent.dwControlKeyState
1121 & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED));
1122 if (irBuffer.Event.KeyEvent.uChar.UnicodeChar == _T('\n')) {
1123 if (!ReadConsoleInput(_el_h_in, &irBuffer, 1, &count)) {
1124 _el_clean_exit();
1125 return NULL;
1126 }
1127 buf[0] = VK_RETURN;
1128 continue;
1129 }
1130 if (irBuffer.Event.KeyEvent.uChar.UnicodeChar == _T('\0')) {
1131 /*
1132 if it is a special key, just remove it from the buffer
1133 */
1134 if (!ReadConsoleInput(_el_h_in, &irBuffer, 1, &count)) {
1135 _el_clean_exit();
1136 return NULL;
1137 }
1138 special = irBuffer.Event.KeyEvent.wVirtualKeyCode;
1139 /*
1140 parse the special key
1141 */
1142 switch (special) {
1143 /*
1144 arrow left, arrow right
1145 HOME and END keys
1146 */
1147 case VK_LEFT:
1148 case VK_RIGHT:
1149 case VK_HOME:
1150 case VK_END:
1151 if (_el_move_cursor(special, ctrl)) {
1152 _el_clean_exit();
1153 return NULL;
1154 }
1155 break;
1156
1157 /*
1158 arrow up: display previous history element (if any)
1159 after recording the current command line
1160 */
1161 case VK_UP:
1162 if (_el_display_prev_hist()) {
1163 _el_clean_exit();
1164 return NULL;
1165 }
1166 break;
1167
1168 /*
1169 page up: display the first history element (if any)
1170 after recording the current command line
1171 */
1172 case VK_PRIOR:
1173 if (_el_display_first_hist()) {
1174 _el_clean_exit();
1175 return NULL;
1176 }
1177 break;
1178
1179 /*
1180 arrow down: display next history element (if any)
1181 after recording the current command line
1182 */
1183 case VK_DOWN:
1184 if (_el_display_next_hist()) {
1185 _el_clean_exit();
1186 return NULL;
1187 }
1188 break;
1189
1190 case VK_NEXT:
1191 /*
1192 page down: display last history element (if any)
1193 after recording the current command line
1194 */
1195 if (_el_display_last_hist()) {
1196 _el_clean_exit();
1197 return NULL;
1198 }
1199 break;
1200
1201 /*
1202 delete char
1203 */
1204 case VK_DELETE:
1205 if (rl_point != wcslen(_el_line_buffer)) {
1206 if (_el_delete_char(VK_DELETE, 1)) {
1207 _el_clean_exit();
1208 return NULL;
1209 }
1210 _el_compl_index = 0;
1211 compl_pos = -1;
1212 }
1213 break;
1214 }
1215 }
1216 else {
1217 /*
1218 if it is a normal key, remove it from the buffer
1219 */
1220 memset(buf, 0, _EL_CONSOLE_BUF_LEN * sizeof(wchar_t));
1221 if (!ReadConsole(_el_h_in, buf, 1, &count, NULL)) {
1222 _el_clean_exit();
1223 return NULL;
1224 }
1225 /*
1226 then parse it
1227 */
1228 switch (buf[0]) {
1229 /*
1230 backspace
1231 */
1232 case VK_BACK:
1233 if (rl_point) {
1234 _el_compl_index = 0;
1235 compl_pos = -1;
1236 if (_el_delete_char(VK_BACK, 1)) {
1237 _el_clean_exit();
1238 return NULL;
1239 }
1240 }
1241 break;
1242
1243 /*
1244 TAB: do completion
1245 */
1246 case VK_TAB:
1247 if ((!array) || (rl_point != compl_pos)) {
1248 _el_free_array(array);
1249 index = 0;
1250 free(_el_text);
1251 _el_text = NULL;
1252 if (!(_el_text = _el_get_compl_text(&start, &end))) {
1253 _el_clean_exit();
1254 return NULL;
1255 }
1256 if (_el_old_arg) {
1257 _el_old_arg[0] = _T('\0');
1258 }
1259 if (!_el_w2mb(_el_text, &_el_text_mb)) {
1260 _el_clean_exit();
1261 return NULL;
1262 }
1263 if (!_el_w2mb(_el_line_buffer, &rl_line_buffer)) {
1264 _el_clean_exit();
1265 return NULL;
1266 }
1267 array = (rl_attempted_completion_function
1268 ? rl_attempted_completion_function(_el_text_mb, start, end)
1269 : rl_completion_matches(_el_text_mb, (rl_completion_entry_function
1270 ? rl_completion_entry_function : rl_filename_completion_function)));
1271 if (!array) {
1272 _el_clean_exit();
1273 return NULL;
1274 }
1275 }
1276 if (!array[index]) {
1277 index = 0;
1278 }
1279 if (array[index]) {
1280 if (!_el_mb2w(array[index], &_el_next_compl)) {
1281 _el_clean_exit();
1282 return NULL;
1283 }
1284 len = 0;
1285 if (_el_old_arg) {
1286 len = (int)wcslen(_el_old_arg);
1287 #if 0
1288 fwprintf(stderr, _T("VK_TAB) _el_old_arg = '%s', len = %d\n"), _el_old_arg, len);
1289 fflush(stderr);
1290 #endif
1291 }
1292 if (!len) {
1293 len = (int)wcslen(_el_text);
1294 }
1295 if (len) {
1296 if (_el_delete_char(VK_BACK, len)) {
1297 _el_clean_exit();
1298 return NULL;
1299 }
1300 }
1301 len = (int)wcslen(_el_next_compl);
1302 if (!(_el_old_arg = realloc(_el_old_arg,
1303 (len + 1) * sizeof(wchar_t)))) {
1304 return NULL;
1305 }
1306 _el_old_arg[len] = _T('\0');
1307 memcpy(_el_old_arg, _el_next_compl, len * sizeof(wchar_t));
1308 line_len = (int)wcslen(_el_line_buffer);
1309 if (_el_insert_char(_el_next_compl, len)) {
1310 _el_clean_exit();
1311 return NULL;
1312 }
1313 free(_el_next_compl);
1314 _el_next_compl = NULL;
1315 compl_pos = ((rl_point && (!wcschr(_el_completer_word_break_characters
1316 ? _el_completer_word_break_characters : _el_basic_word_break_characters,
1317 _el_line_buffer[rl_point - 1]))) ? rl_point : -1);
1318 ++index;
1319 }
1320 break;
1321
1322 /*
1323 ENTER: move the cursor to end of line,
1324 then return to the caller program
1325 */
1326 case VK_RETURN:
1327 if (_el_set_cursor((int)wcslen(_el_line_buffer) - rl_point)) {
1328 _el_clean_exit();
1329 return NULL;
1330 }
1331 break;
1332
1333 /*
1334 delete word
1335 */
1336 case 0x17: /* CTRL + W */
1337 if (ctrl) {
1338 if (!rl_point) {
1339 break;
1340 }
1341 n = 1;
1342 while (((rl_point - n) > 0)
1343 && (iswspace(_el_line_buffer[rl_point - n]))) {
1344 ++n;
1345 }
1346 while ((rl_point - n)
1347 && (!iswspace(_el_line_buffer[rl_point - n]))) {
1348 ++n;
1349 }
1350 if (rl_point - n) {
1351 --n;
1352 }
1353 _el_compl_index = 0;
1354 compl_pos = -1;
1355 if (_el_delete_char(VK_BACK, n)) {
1356 _el_clean_exit();
1357 return NULL;
1358 }
1359 break;
1360 }
1361
1362 /*
1363 delete until end of line
1364 */
1365 case 0x0B: /* CTRL + K */
1366 if (ctrl) {
1367 line_len = (int)wcslen(_el_line_buffer);
1368 if (rl_point < line_len) {
1369 _el_compl_index = 0;
1370 compl_pos = -1;
1371 if (_el_delete_char(VK_DELETE, line_len - rl_point)) {
1372 _el_clean_exit();
1373 return NULL;
1374 }
1375 }
1376 break;
1377 }
1378
1379 /*
1380 beginning-of-line
1381 */
1382 case 0x01: /* CTRL + A */
1383 if (_el_move_cursor(VK_HOME, 0)) {
1384 _el_clean_exit();
1385 return NULL;
1386 }
1387 break;
1388
1389 /*
1390 end-of-line
1391 */
1392 case 0x05: /* CTRL + E */
1393 if (_el_move_cursor(VK_END, 0)) {
1394 _el_clean_exit();
1395 return NULL;
1396 }
1397 break;
1398
1399 /*
1400 forward-char
1401 */
1402 case 0x06: /* CTRL + F */
1403 if (_el_move_cursor(VK_RIGHT, 0)) {
1404 _el_clean_exit();
1405 return NULL;
1406 }
1407 break;
1408
1409 /*
1410 backward-char
1411 */
1412 case 0x02: /* CTRL + B */
1413 if (_el_move_cursor(VK_LEFT, 0)) {
1414 _el_clean_exit();
1415 return NULL;
1416 }
1417 break;
1418
1419 /*
1420 previous-line
1421 */
1422 case 0x10: /* CTRL + P */
1423 if (_el_display_prev_hist()) {
1424 _el_clean_exit();
1425 return NULL;
1426 }
1427 break;
1428
1429 /*
1430 next-line
1431 */
1432 case 0x0E: /* CTRL + N */
1433 if (_el_display_next_hist()) {
1434 _el_clean_exit();
1435 return NULL;
1436 }
1437 break;
1438
1439 /*
1440 delete char
1441 */
1442 case 0x04: /* CTRL + D */
1443 if (rl_point != wcslen(_el_line_buffer)) {
1444 if (_el_delete_char(VK_DELETE, 1)) {
1445 _el_clean_exit();
1446 return NULL;
1447 }
1448 _el_compl_index = 0;
1449 compl_pos = -1;
1450 }
1451 break;
1452
1453 /*
1454 if it is a printable character, print it
1455 NOTE: I have later commented out the
1456 iswprint() check since for instance it
1457 prevents the euro sign from being printed
1458 */
1459 default:
1460 /*if (iswprint(buf[0])) {*/
1461 _el_compl_index = 0;
1462 compl_pos = -1;
1463 if (_el_insert_char(buf, 1)) {
1464 _el_clean_exit();
1465 return NULL;
1466 }
1467 /*}*/
1468 }
1469 }
1470 }
1471 /*
1472 if it was not a keyboard event, just remove it from buffer
1473 */
1474 else if (!ReadConsoleInput(_el_h_in, &irBuffer, 1, &count)) {
1475 _el_clean_exit();
1476 return NULL;
1477 }
1478 }
1479 else {
1480 /*
1481 wait for console input
1482 */
1483 WaitForSingleObject(_el_h_in, INFINITE);
1484 }
1485 }
1486
1487 printf("\n");
1488 history_set_pos(history_length());
1489 /*
1490 if CTRL+C has been pressed, return an empty string
1491 */
1492 if (_el_line_buffer) {
1493 if (_el_ctrl_c_pressed) {
1494 n = (int)wcslen(_el_line_buffer) - rl_point;
1495 if (n) {
1496 _el_set_cursor(n);
1497 }
1498 _el_line_buffer[0] = _T('\0');
1499 }
1500 _el_w2mb(_el_line_buffer, &rl_line_buffer);
1501 ret_string = (rl_line_buffer ? _strdup(rl_line_buffer) : NULL);
1502 }
1503 _el_clean_exit();
1504
1505 return ret_string;
1506 }
1507