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