1 /*  Part of SWI-Prolog
2 
3     Author:        Jan Wielemaker
4     E-mail:        J.Wielemaker@vu.nl
5     WWW:           http://www.swi-prolog.org
6     Copyright (c)  1999-2011, University of Amsterdam
7     All rights reserved.
8 
9     Redistribution and use in source and binary forms, with or without
10     modification, are permitted provided that the following conditions
11     are met:
12 
13     1. Redistributions of source code must retain the above copyright
14        notice, this list of conditions and the following disclaimer.
15 
16     2. Redistributions in binary form must reproduce the above copyright
17        notice, this list of conditions and the following disclaimer in
18        the documentation and/or other materials provided with the
19        distribution.
20 
21     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25     COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32     POSSIBILITY OF SUCH DAMAGE.
33 */
34 
35 #include <windows.h>
36 #include <tchar.h>
37 #define _MAKE_DLL 1
38 #undef _export
39 #include "console.h"
40 #include "console_i.h"
41 #include "common.h"
42 #include <memory.h>
43 #include <string.h>
44 #include <ctype.h>
45 
46 #ifndef EOF
47 #define EOF -1
48 #endif
49 
50 typedef void (*function)(Line ln, int chr);	/* edit-function */
51 
52 static function dispatch_table[256];	/* general dispatch-table */
53 static function dispatch_meta[256];	/* ESC-char dispatch */
54 static RlcCompleteFunc _rlc_complete_function = rlc_complete_file_function;
55 
56 static void	init_line_package(RlcData b);
57 static void	bind_actions(void);
58 
59 #ifndef min
60 #define min(a, b) ((a) < (b) ? (a) : (b))
61 #define max(a, b) ((a) > (b) ? (a) : (b))
62 #endif
63 
64 #ifndef TRUE
65 #define TRUE 1
66 #define FALSE 0
67 #endif
68 
69 #ifndef EOS
70 #define EOS 0
71 #endif
72 
73 #ifndef ESC
74 #define ESC 27
75 #endif
76 
77 #define COMPLETE_NEWLINE 1
78 #define COMPLETE_EOF	 2
79 
80 #define ctrl(c)	((c) - '@')
81 #define META_OFFSET 128
82 #define meta(c) ((c) + META_OFFSET)
83 
84 		 /*******************************
85 		 *	       BUFFER		*
86 		 *******************************/
87 
88 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
89 make_room(Line, int room)
90 	Make n-characters space after the point.
91 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
92 
93 static void
make_room(Line ln,size_t room)94 make_room(Line ln, size_t room)
95 { while ( ln->size + room + 1 > ln->allocated )
96   { if ( !ln->data )
97     { ln->data = rlc_malloc(256 * sizeof(TCHAR));
98       ln->allocated = 256;
99     } else
100     { ln->allocated *= 2;
101       ln->data = rlc_realloc(ln->data, ln->allocated * sizeof(TCHAR));
102     }
103   }
104 
105   memmove(&ln->data[ln->point + room], &ln->data[ln->point],
106 	  (ln->size - ln->point)*sizeof(TCHAR));
107   ln->size += room;
108   if ( room > 0 )
109     ln->change_start = min(ln->change_start, ln->point);
110 }
111 
112 
113 static void
set_line(Line ln,const TCHAR * s)114 set_line(Line ln, const TCHAR *s)
115 { size_t len = _tcslen(s);
116 
117   ln->size = ln->point = 0;
118   make_room(ln, len);
119   _tcsncpy(ln->data, s, len);
120 }
121 
122 
123 static void
terminate(Line ln)124 terminate(Line ln)
125 { if ( !ln->data )
126   { ln->data = rlc_malloc(sizeof(TCHAR));
127     ln->allocated = 1;
128   }
129   ln->data[ln->size] = EOS;
130 }
131 
132 
133 static void
delete(Line ln,size_t from,size_t len)134 delete(Line ln, size_t from, size_t len)
135 { if ( from < 0 || from > ln->size || len < 0 || from + len > ln->size )
136     return;
137 
138   _tcsncpy(&ln->data[from], &ln->data[from+len], ln->size - (from+len));
139   ln->size -= len;
140 }
141 
142 
143 		 /*******************************
144 		 *	     POSITIONING	*
145 		 *******************************/
146 
147 static size_t
back_word(Line ln,size_t from)148 back_word(Line ln, size_t from)
149 { from = min(from, ln->size);
150   from = max(0, from);
151 
152   if ( ln->data )
153   { while(!rlc_is_word_char(ln->data[from-1]) && from > 0 )
154       from--;
155     while(rlc_is_word_char(ln->data[from-1]) && from > 0 )
156       from--;
157   }
158 
159   return from;
160 }
161 
162 static size_t
forw_word(Line ln,size_t from)163 forw_word(Line ln, size_t from)
164 { from = min(from, ln->size);
165   from = max(0, from);
166 
167   if ( ln->data )
168   { while(!rlc_is_word_char(ln->data[from]) && from < ln->size )
169       from++;
170     while(rlc_is_word_char(ln->data[from]) && from < ln->size )
171       from++;
172   }
173 
174   return from;
175 }
176 
177 		 /*******************************
178 		 *       EDITING FUNCTIONS	*
179 		 *******************************/
180 
181 static __inline void
changed(Line ln,size_t from)182 changed(Line ln, size_t from)
183 { ln->change_start = min(ln->change_start, from);
184 }
185 
186 
187 static void
insert_self(Line ln,int chr)188 insert_self(Line ln, int chr)
189 { make_room(ln, 1);
190   ln->data[ln->point++] = chr;
191 }
192 
193 
194 static void
backward_delete_character(Line ln,int chr)195 backward_delete_character(Line ln, int chr)
196 { if ( ln->point > 0 )
197   { memmove(&ln->data[ln->point-1], &ln->data[ln->point],
198 	    (ln->size - ln->point)*sizeof(TCHAR));
199     ln->size--;
200     ln->point--;
201   }
202 
203   changed(ln, ln->point);
204 }
205 
206 
207 static void
delete_character(Line ln,int chr)208 delete_character(Line ln, int chr)
209 { if ( ln->point < ln->size )
210   { ln->point++;
211     backward_delete_character(ln, chr);
212   }
213 }
214 
215 
216 static void
backward_character(Line ln,int chr)217 backward_character(Line ln, int chr)
218 { if ( ln->point > 0 )
219     ln->point--;
220 }
221 
222 
223 static void
forward_character(Line ln,int chr)224 forward_character(Line ln, int chr)
225 { if ( ln->point < ln->size )
226     ln->point++;
227 }
228 
229 
230 static void
backward_word(Line ln,int chr)231 backward_word(Line ln, int chr)
232 { ln->point = back_word(ln, ln->point);
233 }
234 
235 
236 static void
forward_word(Line ln,int chr)237 forward_word(Line ln, int chr)
238 { ln->point = forw_word(ln, ln->point);
239 }
240 
241 
242 static void
backward_delete_word(Line ln,int chr)243 backward_delete_word(Line ln, int chr)
244 { size_t from = back_word(ln, ln->point);
245 
246   memmove(&ln->data[from], &ln->data[ln->point],
247 	  (ln->size - ln->point)*sizeof(TCHAR));
248   ln->size -= ln->point - from;
249   ln->point = from;
250   changed(ln, from);
251 }
252 
253 
254 static void
forward_delete_word(Line ln,int chr)255 forward_delete_word(Line ln, int chr)
256 { size_t to = forw_word(ln, ln->point);
257 
258   memmove(&ln->data[ln->point], &ln->data[to], (ln->size - to)*sizeof(TCHAR));
259   ln->size -= to - ln->point;
260   changed(ln, ln->point);
261 }
262 
263 
264 static void
transpose_chars(Line ln,int chr)265 transpose_chars(Line ln, int chr)
266 { if ( ln->point > 0 && ln->point < ln->size )
267   { int c0 = ln->data[ln->point-1];
268     ln->data[ln->point-1] = ln->data[ln->point];
269     ln->data[ln->point] = c0;
270     changed(ln, ln->point-1);
271   }
272 }
273 
274 
275 static void
start_of_line(Line ln,int chr)276 start_of_line(Line ln, int chr)
277 { ln->point = 0;
278 }
279 
280 
281 static void
end_of_line(Line ln,int chr)282 end_of_line(Line ln, int chr)
283 { ln->point = ln->size;
284 }
285 
286 
287 static void
kill_line(Line ln,int chr)288 kill_line(Line ln, int chr)
289 { ln->size = ln->point;
290   changed(ln, ln->size);
291 }
292 
293 
294 static void
empty_line(Line ln,int chr)295 empty_line(Line ln, int chr)
296 { ln->size = ln->point = 0;
297   changed(ln, 0);
298 }
299 
300 
301 static void
enter(Line ln,int chr)302 enter(Line ln, int chr)
303 { ln->point = ln->size;
304 #ifdef DOS_CRNL
305   make_room(ln, 2);
306   ln->data[ln->point++] = '\r';
307   ln->data[ln->point++] = '\n';
308 #else
309   make_room(ln, 1);
310   ln->data[ln->point++] = '\n';
311 #endif
312   terminate(ln);
313   ln->complete = COMPLETE_NEWLINE;
314 }
315 
316 
317 static void
eof(Line ln,int chr)318 eof(Line ln, int chr)
319 { ln->point = ln->size;
320   terminate(ln);
321   ln->complete = COMPLETE_EOF;
322 }
323 
324 
325 static void
delete_character_or_eof(Line ln,int chr)326 delete_character_or_eof(Line ln, int chr)
327 { if ( ln->size == 0 )
328   { ln->point = ln->size;
329     terminate(ln);
330     ln->complete = COMPLETE_EOF;
331   } else
332     delete_character(ln, chr);
333 }
334 
335 
336 static void
undefined(Line ln,int chr)337 undefined(Line ln, int chr)
338 {
339 }
340 
341 
342 static void
interrupt(Line ln,int chr)343 interrupt(Line ln, int chr)
344 { raise(SIGINT);
345 }
346 
347 		 /*******************************
348 		 *		HISTORY		*
349 		 *******************************/
350 
351 static void
add_history(rlc_console c,const TCHAR * data)352 add_history(rlc_console c, const TCHAR *data)
353 { const TCHAR *s = data;
354 
355   while(*s && *s <= ' ')
356     s++;
357 
358   if ( *s )
359     rlc_add_history(c, s);
360 }
361 
362 
363 static void
backward_history(Line ln,int chr)364 backward_history(Line ln, int chr)
365 { const TCHAR *h;
366 
367   if ( rlc_at_head_history(ln->console) && ln->size > 0 )
368   { terminate(ln);
369     add_history(ln->console, ln->data);
370   }
371 
372   if ( (h = rlc_bwd_history(ln->console)) )
373   { set_line(ln, h);
374     ln->point = ln->size;
375   }
376 }
377 
378 
379 static void
forward_history(Line ln,int chr)380 forward_history(Line ln, int chr)
381 { if ( !rlc_at_head_history(ln->console) )
382   { const TCHAR *h = rlc_fwd_history(ln->console);
383 
384     if ( h )
385     { set_line(ln, h);
386       ln->point = ln->size;
387     }
388   } else
389     empty_line(ln, chr);
390 }
391 
392 		 /*******************************
393 		 *	      COMPLETE		*
394 		 *******************************/
395 
396 RlcCompleteFunc
rlc_complete_hook(RlcCompleteFunc new)397 rlc_complete_hook(RlcCompleteFunc new)
398 { RlcCompleteFunc old = _rlc_complete_function;
399 
400   _rlc_complete_function = new;
401 
402   return old;
403 }
404 
405 
406 static int
common(const TCHAR * s1,const TCHAR * s2,int insensitive)407 common(const TCHAR *s1, const TCHAR *s2, int insensitive)
408 { int n = 0;
409 
410   if ( !insensitive )
411   { while(*s1 && *s1 == *s2)
412     { s1++, s2++;
413       n++;
414     }
415     return n;
416   } else
417   { while(*s1)
418     { if ( _totlower(*s1) == _totlower(*s2) )
419       { s1++, s2++;
420 	n++;
421       } else
422 	break;
423     }
424     return n;
425   }
426 }
427 
428 
429 static void
complete(Line ln,int chr)430 complete(Line ln, int chr)
431 { if ( _rlc_complete_function )
432   { rlc_complete_data dbuf;
433     RlcCompleteData data = &dbuf;
434 
435     memset(data, 0, sizeof(dbuf));
436     data->line      = ln;
437     data->call_type = COMPLETE_INIT;
438 
439     if ( (*_rlc_complete_function)(data) )
440     { TCHAR match[COMPLETE_MAX_WORD_LEN];
441       int nmatches = 1;
442       size_t ncommon = _tcslen(data->candidate);
443       size_t patlen = ln->point - data->replace_from;
444 
445       _tcscpy(match, data->candidate);
446 
447       data->call_type = COMPLETE_ENUMERATE;
448       while( (*data->function)(data) )
449       { ncommon = common(match, data->candidate, data->case_insensitive);
450 	match[ncommon] = EOS;
451 	nmatches++;
452       }
453       data->call_type = COMPLETE_CLOSE;
454       (*data->function)(data);
455 
456       delete(ln, data->replace_from, patlen);
457       ln->point = data->replace_from;
458       make_room(ln, ncommon);
459       _tcsncpy(&ln->data[data->replace_from], match, ncommon);
460       ln->point += ncommon;
461       if ( nmatches == 1 && data->quote )
462 	insert_self(ln, data->quote);
463     }
464   }
465 }
466 
467 #define MAX_LIST_COMPLETIONS 256
468 
469 static void
list_completions(Line ln,int chr)470 list_completions(Line ln, int chr)
471 { if ( _rlc_complete_function )
472   { rlc_complete_data dbuf;
473     RlcCompleteData data = &dbuf;
474 
475     memset(data, 0, sizeof(dbuf));
476     data->line      = ln;
477     data->call_type = COMPLETE_INIT;
478 
479     if ( (*_rlc_complete_function)(data) )
480     { TCHAR *buf[COMPLETE_MAX_MATCHES];
481       int n, nmatches = 0;
482       size_t len = _tcslen(data->candidate) + 1;
483       size_t longest = len;
484       size_t cols;
485 
486       buf[nmatches] = rlc_malloc(len*sizeof(TCHAR));
487       _tcsncpy(buf[nmatches], data->candidate, len);
488       nmatches++;
489 
490       data->call_type = COMPLETE_ENUMERATE;
491       while( (*data->function)(data) )
492       { len = _tcslen(data->candidate) + 1;
493 	buf[nmatches] = rlc_malloc(len*sizeof(TCHAR));
494 	_tcsncpy(buf[nmatches], data->candidate, len);
495 	nmatches++;
496 	longest = max(longest, len);
497 
498 	if ( nmatches > COMPLETE_MAX_MATCHES )
499 	{ TCHAR *msg = _T("\r\n! Too many matches\r\n");
500 
501 	  while(*msg)
502 	    rlc_putchar(ln->console, *msg++);
503 	  ln->reprompt = TRUE;
504 	  data->call_type = COMPLETE_CLOSE;
505 	  (*data->function)(data);
506 	  return;
507 	}
508       }
509       data->call_type = COMPLETE_CLOSE;
510       (*data->function)(data);
511 
512       cols = ScreenCols(ln->console) / longest;
513       rlc_putchar(ln->console, '\r');
514       rlc_putchar(ln->console, '\n');
515 
516       for(n=0; n<nmatches; )
517       { TCHAR *s = buf[n];
518 	len = 0;
519 
520 	while(*s)
521 	{ len++;
522 	  rlc_putchar(ln->console, *s++);
523 	}
524 
525 	rlc_free(buf[n++]);
526 
527 	if ( n % cols == 0 )
528 	{ rlc_putchar(ln->console, '\r');
529 	  rlc_putchar(ln->console, '\n');
530 	} else
531 	{ while( len++ < longest )
532 	  rlc_putchar(ln->console, ' ');
533 	}
534       }
535       if ( nmatches % cols != 0 )
536       { rlc_putchar(ln->console, '\r');
537 	rlc_putchar(ln->console, '\n');
538       }
539 
540       ln->reprompt = TRUE;
541     }
542   }
543 }
544 
545 
546 		 /*******************************
547 		 *	      REPAINT		*
548 		 *******************************/
549 
550 static void
output(rlc_console b,TCHAR * s,size_t len)551 output(rlc_console b, TCHAR *s, size_t len)
552 { while(len-- > 0)
553   { if ( *s == '\n' )
554       rlc_putchar(b, '\r');
555     rlc_putchar(b, *s++);
556   }
557 }
558 
559 
560 static void
update_display(Line ln)561 update_display(Line ln)
562 { if ( ln->reprompt )
563   { const TCHAR *prompt = rlc_prompt(ln->console, NULL);
564     const TCHAR *s = prompt;
565 
566     rlc_putchar(ln->console, '\r');
567     while(*s)
568       rlc_putchar(ln->console, *s++);
569 
570     rlc_get_mark(ln->console, &ln->origin);
571 
572     ln->change_start = 0;
573     ln->reprompt = FALSE;
574   }
575 
576   rlc_goto_mark(ln->console, &ln->origin, ln->data, ln->change_start);
577   output(ln->console,
578 	 &ln->data[ln->change_start], ln->size - ln->change_start);
579   rlc_erase_from_caret(ln->console);
580   rlc_goto_mark(ln->console, &ln->origin, ln->data, ln->point);
581   rlc_update(ln->console);
582 
583   ln->change_start = ln->size;
584 }
585 
586 		 /*******************************
587 		 *	     TOPLEVEL		*
588 		 *******************************/
589 
590 TCHAR *
read_line(rlc_console b)591 read_line(rlc_console b)
592 { line ln;
593 
594   init_line_package(b);
595 
596   memset(&ln, 0, sizeof(line));
597   ln.console = b;
598   rlc_get_mark(b, &ln.origin);
599 
600   while(!ln.complete)
601   { int c;
602     rlc_mark m0, m1;
603     function func;
604 
605     rlc_get_mark(b, &m0);
606     if ( (c = getch(b)) == IMODE_SWITCH_CHAR )
607       return RL_CANCELED_CHARP;
608 
609     if ( c == EOF )
610     { eof(&ln, c);
611       update_display(&ln);
612       break;
613     } else if ( c == ESC )
614     { if ( (c = getch(b)) == IMODE_SWITCH_CHAR )
615 	return RL_CANCELED_CHARP;
616       if ( c > 256 )
617 	func = undefined;
618       else
619 	func = dispatch_meta[c&0xff];
620     } else
621     { if ( c >= 256 )
622 	func = insert_self;
623       else
624 	func = dispatch_table[c&0xff];
625     }
626 
627     rlc_get_mark(b, &m1);
628 
629     (*func)(&ln, c);
630     if ( m0.mark_x != m1.mark_x || m0.mark_y != m1.mark_y )
631       ln.reprompt = TRUE;
632     update_display(&ln);
633   }
634   rlc_clearprompt(b);
635 
636   add_history(b, ln.data);
637 
638   return ln.data;
639 }
640 
641 
642 		 /*******************************
643 		 *	     DISPATCH		*
644 		 *******************************/
645 
646 static void
init_dispatch_table()647 init_dispatch_table()
648 { static int done;
649 
650   if ( !done )
651   { int n;
652 
653     for(n=0; n<32; n++)
654       dispatch_table[n] = undefined;
655     for(n=32; n<256; n++)
656       dispatch_table[n] = insert_self;
657     for(n=0; n<256; n++)
658       dispatch_meta[n] = undefined;
659 
660     bind_actions();
661 
662     done = TRUE;
663   }
664 }
665 
666 
667 static void
init_line_package(RlcData b)668 init_line_package(RlcData b)
669 { init_dispatch_table();
670   rlc_init_history(b, 50);
671 }
672 
673 		 /*******************************
674 		 *	       BIND		*
675 		 *******************************/
676 
677 typedef struct _action
678 { char		*name;
679   function	 function;
680   unsigned char  keys[4];
681 } action, *Action;
682 
683 #define ACTION(n, f, k) { n, f, k }
684 
685 static action actions[] = {
686   ACTION("insert_self",		      insert_self,		 ""),
687   ACTION("backward_delete_character", backward_delete_character, "\b"),
688   ACTION("complete",		      complete,			 "\t"),
689   ACTION("enter",		      enter, 			 "\r\n"),
690   ACTION("start_of_line",	      start_of_line,		 {ctrl('A')}),
691   ACTION("backward_character",	      backward_character,        {ctrl('B')}),
692   ACTION("interrupt",		      interrupt,		 {ctrl('C')}),
693   ACTION("end_of_line",		      end_of_line,		 {ctrl('E')}),
694   ACTION("forward_character",	      forward_character,	 {ctrl('F')}),
695   ACTION("transpose_chars",	      transpose_chars,		 {ctrl('T')}),
696   ACTION("kill_line",		      kill_line,		 {ctrl('K')}),
697   ACTION("backward_history",	      backward_history,		 {ctrl('P')}),
698   ACTION("forward_history",	      forward_history,		 {ctrl('N')}),
699   ACTION("empty_line",		      empty_line,		 {ctrl('U')}),
700   ACTION("eof",			      eof,			 {ctrl('Z')}),
701 
702   ACTION("delete_character_or_eof",   delete_character_or_eof,	 {ctrl('D')}),
703   ACTION("delete_character",	      delete_character,		 {127}),
704   { "forward_word",		forward_word,  {meta(ctrl('F')), meta('f')}},
705   { "backward_word",		backward_word, {meta(ctrl('B')), meta('b')}},
706   { "forward_delete_word",	forward_delete_word, {meta(127), meta('d')}},
707   ACTION("list_completions",	      list_completions,     {meta('?')}),
708   ACTION("backward_delete_word",      backward_delete_word, {meta('\b')}),
709 
710   ACTION(NULL,			      NULL,			 "")
711 };
712 
713 int
rlc_bind(int chr,const char * fname)714 rlc_bind(int chr, const char *fname)
715 { if ( chr >= 0 && chr <= 256 )
716   { Action a = actions;
717 
718     for( ; a->name; a++ )
719     { if ( strcmp(a->name, fname) == 0 )
720       { if ( chr > META_OFFSET )
721 	  dispatch_meta[chr-META_OFFSET] = a->function;
722 	else
723 	  dispatch_table[chr] = a->function;
724 
725 	return TRUE;
726       }
727     }
728   }
729 
730   return FALSE;
731 }
732 
733 static void
bind_actions()734 bind_actions()
735 { Action a = actions;
736 
737   for( ; a->name; a++ )
738   { unsigned char *k = a->keys;
739 
740     for( ; *k; k++ )
741     { int chr = *k & 0xff;
742 
743       if ( chr > META_OFFSET )
744 	dispatch_meta[chr-META_OFFSET] = a->function;
745       else
746 	dispatch_table[chr] = a->function;
747     }
748   }
749 }
750 
751 
752