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