1 /******************************** -*- C -*- ****************************
2 *
3 * Input module: stream interface and Readline completion handling
4 *
5 *
6 ***********************************************************************/
7
8 /***********************************************************************
9 *
10 * Copyright 2001, 2002, 2003, 2005, 2006, 2008, 2009
11 * Free Software Foundation, Inc.
12 * Written by Paolo Bonzini.
13 *
14 * This file is part of GNU Smalltalk.
15 *
16 * GNU Smalltalk is free software; you can redistribute it and/or modify it
17 * under the terms of the GNU General Public License as published by the Free
18 * Software Foundation; either version 2, or (at your option) any later
19 * version.
20 *
21 * Linking GNU Smalltalk statically or dynamically with other modules is
22 * making a combined work based on GNU Smalltalk. Thus, the terms and
23 * conditions of the GNU General Public License cover the whole
24 * combination.
25 *
26 * In addition, as a special exception, the Free Software Foundation
27 * give you permission to combine GNU Smalltalk with free software
28 * programs or libraries that are released under the GNU LGPL and with
29 * independent programs running under the GNU Smalltalk virtual machine.
30 *
31 * You may copy and distribute such a system following the terms of the
32 * GNU GPL for GNU Smalltalk and the licenses of the other code
33 * concerned, provided that you include the source code of that other
34 * code when and as the GNU GPL requires distribution of source code.
35 *
36 * Note that people who make modified versions of GNU Smalltalk are not
37 * obligated to grant this special exception for their modified
38 * versions; it is their choice whether to do so. The GNU General
39 * Public License gives permission to release a modified version without
40 * this exception; this exception also makes it possible to release a
41 * modified version which carries forward this exception.
42 *
43 * GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
44 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
45 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
46 * more details.
47 *
48 * You should have received a copy of the GNU General Public License along with
49 * GNU Smalltalk; see the file COPYING. If not, write to the Free Software
50 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
51 *
52 ***********************************************************************/
53
54 #include "gstpriv.h"
55
56 #ifdef HAVE_READLINE
57 # include <readline/readline.h>
58 # include <readline/history.h>
59 #endif
60
61 typedef struct gst_file_segment
62 {
63 OBJ_HEADER;
64 OOP fileOOP;
65 OOP startPos;
66 OOP length;
67 }
68 *gst_file_segment;
69
70 typedef struct string_stream
71 {
72 char *strBase; /* base of asciz string */
73 const char *str; /* pointer into asciz string */
74 }
75 string_stream;
76
77 typedef struct unix_file_stream
78 {
79 int fd;
80 char *buf;
81 const char *ptr;
82 const char *end;
83 }
84 unix_file_stream;
85
86 typedef struct oop_stream
87 {
88 OOP oop;
89 char *buf;
90 char *ptr;
91 const char *end;
92 }
93 oop_stream;
94
95 typedef struct input_stream
96 {
97 stream_type type;
98 /* the 3 buffered characters */
99 char pushedBackChars[3];
100
101 /* number of chars pushed back */
102 int pushedBackCount;
103
104 int line;
105 int column;
106 const char *prompt;
107
108 OOP fileOOP; /* the object stored in FileSegments */
109 const char *fileName;
110 off_t fileOffset;
111
112 union
113 {
114 unix_file_stream u_st_file;
115 string_stream u_st_str;
116 oop_stream u_st_oop;
117 }
118 st;
119 struct input_stream *prevStream;
120 }
121 *input_stream;
122
123 #define st_file st.u_st_file
124 #define st_str st.u_st_str
125 #define st_oop st.u_st_oop
126
127 /* The internal interface used by _gst_next_char. */
128 static int my_getc (input_stream stream);
129
130 /* Return the File object or a file name for the topmost stream in the stack
131 if it is of type STREAM_FILE; nil otherwise. */
132 static OOP get_cur_file (void);
133
134 /* Print a line indicator in front of an error message. */
135 static void line_stamp (int line);
136
137 /* Allocate and push a new stream of type TYPE on the stack; the new
138 stream is then available through IN_STREAM. */
139 static input_stream push_new_stream (stream_type type);
140
141 /* The topmost stream in the stack, and the head of the linked list
142 that implements the stack. */
143 static input_stream in_stream = NULL;
144
145 /* Poll FD until it is available for input (or until it returns
146 POLLHUP) and then perform a read system call. */
147 static int poll_and_read (int fd, char *buf, int n);
148
149
150 /* If true, readline is disabled. */
151 mst_Boolean _gst_no_tty = false;
152
153 /* >= 1 if completions are enabled, < 1 if they are not. Available
154 for completeness even if Readline is not used. */
155 static int completions_enabled = 1;
156
157
158 #ifdef HAVE_READLINE
159 /* Storage for the possible completions */
160 static char **completions;
161
162 /* Number of completions available. */
163 static int count;
164
165 /* Number of completions before the array must be resized. */
166 static int free_items;
167
168 /* Number of sorted completions. Completions are not sorted until
169 we are requested to use them. */
170 static int sorted_count;
171
172 /* Internal functions */
173 static void merge (char **a1,
174 int count1,
175 char **a2,
176 int count2,
177 mst_Boolean reallocate);
178
179 static void add_completion (const char *str,
180 int len);
181 static int compare_strings (const PTR a,
182 const PTR b);
183
184 /* Readline callbacks */
185 static int readline_getc (FILE * file);
186
187 static char *readline_quote_filename (const char *s,
188 int rtype,
189 const char *qcp);
190 static char *readline_dequote_filename (const char *s,
191 char qc);
192 static char *symbol_generator (const char *text,
193 int state);
194 static char **readline_match_symbols (char *text,
195 int start,
196 int end);
197
198 #endif
199
200
201 /* Generic "stream" interface. A stream is an abstraction for input
202 and output. It is most like common lisp streams. Basically, these
203 streams provide transparent reading from either a Smalltalk string,
204 or a UNIX file. They stack, and the previous stream can be
205 restored by doing a "_gst_pop_stream" which optionally "closes" the
206 current stream and goes back to using the previous stream.
207
208 The `readline()' interface: The behavior is like the Smalltalk
209 String interface. The end-of-string or a NULL strBase-pointer
210 decides to read in a new line. The prompt is still shown by the
211 readline() call. */
212
213 void
_gst_pop_stream(mst_Boolean closeIt)214 _gst_pop_stream (mst_Boolean closeIt)
215 {
216 input_stream stream;
217
218 stream = in_stream;
219 in_stream = in_stream->prevStream;
220 _gst_unregister_oop (stream->fileOOP);
221
222 switch (stream->type)
223 {
224 case STREAM_STRING:
225 xfree (stream->st_str.strBase);
226 break;
227
228 #ifdef HAVE_READLINE
229 case STREAM_READLINE:
230 #endif /* HAVE_READLINE */
231 case STREAM_OOP:
232 xfree (stream->st_oop.buf);
233 _gst_unregister_oop (stream->st_oop.oop);
234 break;
235
236 case STREAM_FILE:
237 xfree (stream->st_file.buf);
238 if (closeIt)
239 close (stream->st_file.fd);
240 break;
241 }
242
243 xfree (stream);
244 }
245
246 void
_gst_push_unix_file(int fd,const char * fileName)247 _gst_push_unix_file (int fd,
248 const char *fileName)
249 {
250 input_stream newStream;
251
252 newStream = push_new_stream (STREAM_FILE);
253 newStream->st_file.fd = fd;
254 newStream->st_file.buf = xmalloc (1024);
255 newStream->st_file.ptr = newStream->st_file.buf;
256 newStream->st_file.end = newStream->st_file.buf;
257 newStream->fileName = fileName;
258 newStream->fileOffset = lseek (fd, 0, SEEK_CUR);
259 }
260
261 void
_gst_push_stream_oop(OOP oop)262 _gst_push_stream_oop (OOP oop)
263 {
264 input_stream newStream;
265
266 newStream = push_new_stream (STREAM_OOP);
267 newStream->st_oop.oop = oop;
268 newStream->st_oop.buf = NULL;
269 newStream->st_oop.ptr = NULL;
270 newStream->st_oop.end = NULL;
271 newStream->fileName = "a Smalltalk Stream";
272
273 _gst_register_oop (oop);
274 }
275
276 void
_gst_push_smalltalk_string(OOP stringOOP)277 _gst_push_smalltalk_string (OOP stringOOP)
278 {
279 input_stream newStream;
280
281 newStream = push_new_stream (STREAM_STRING);
282
283 newStream->st_str.strBase = (char *) _gst_to_cstring (stringOOP);
284 newStream->st_str.str = newStream->st_str.strBase;
285 newStream->fileName = "a Smalltalk string";
286 }
287
288 void
_gst_push_cstring(const char * string)289 _gst_push_cstring (const char *string)
290 {
291 input_stream newStream;
292
293 newStream = push_new_stream (STREAM_STRING);
294
295 newStream->st_str.strBase = xstrdup (string);
296 newStream->st_str.str = newStream->st_str.strBase;
297 newStream->fileName = "a C string";
298 }
299
300 void
_gst_push_stdin_string(void)301 _gst_push_stdin_string (void)
302 {
303 #ifdef HAVE_READLINE
304 input_stream newStream;
305
306 if (_gst_no_tty)
307 {
308 #endif
309 _gst_push_unix_file (0, "stdin");
310 return;
311 #ifdef HAVE_READLINE
312 }
313
314 if (count == 0)
315 _gst_add_all_symbol_completions ();
316
317 newStream = push_new_stream (STREAM_READLINE);
318 newStream->fileOffset = 0;
319 newStream->st_oop.buf = NULL;
320 newStream->st_oop.ptr = NULL;
321 newStream->st_oop.end = NULL;
322 newStream->fileName = "stdin"; /* that's where we get input from */
323 #endif
324 }
325
326 input_stream
push_new_stream(stream_type type)327 push_new_stream (stream_type type)
328 {
329 input_stream newStream;
330
331 newStream = (input_stream) xmalloc (sizeof (struct input_stream));
332
333 newStream->pushedBackCount = 0;
334 newStream->line = 1;
335 newStream->column = 0;
336 newStream->fileOffset = -1;
337 newStream->type = type;
338 newStream->fileName = NULL;
339 newStream->prompt = NULL;
340 newStream->fileOOP = _gst_nil_oop;
341 newStream->prevStream = in_stream;
342 in_stream = newStream;
343 return (newStream);
344 }
345
346
347 void
_gst_set_stream_info(int line,OOP fileOOP,OOP fileNameOOP,int fileOffset)348 _gst_set_stream_info (int line,
349 OOP fileOOP,
350 OOP fileNameOOP,
351 int fileOffset)
352 {
353 in_stream->line = line;
354 in_stream->column = 0;
355
356 _gst_register_oop (fileOOP);
357 in_stream->fileOOP = fileOOP;
358 in_stream->fileOffset = fileOffset;
359
360 if (!IS_NIL (fileNameOOP))
361 in_stream->fileName = _gst_to_cstring (fileNameOOP);
362 }
363
364 void
refill_stream(input_stream stream,char * buf,int new_line)365 refill_stream (input_stream stream, char *buf, int new_line)
366 {
367 size_t old_size = stream->st_oop.ptr - stream->st_oop.buf;
368 size_t size = old_size + strlen (buf);
369
370 /* Leave space for the '\0' at the end. */
371 stream->st_oop.buf = xrealloc (stream->st_oop.buf, size + new_line + 1);
372 stream->st_oop.ptr = stream->st_oop.buf + old_size;
373 stream->st_oop.end = stream->st_oop.buf + size + new_line;
374
375 memcpy (stream->st_oop.ptr, buf, size - old_size);
376 if (new_line)
377 {
378 stream->st_oop.ptr[size - old_size] = '\n';
379 stream->st_oop.ptr[size - old_size + 1] = '\0';
380 }
381 else
382 stream->st_oop.ptr[size - old_size] = '\0';
383
384 free (buf);
385 }
386
387 int
my_getc(input_stream stream)388 my_getc (input_stream stream)
389 {
390 int ic = 0;
391
392 switch (stream->type)
393 {
394 case STREAM_STRING:
395 ic = (unsigned char) *stream->st_str.str;
396 if (!ic)
397 return EOF;
398 else
399 stream->st_str.str++;
400
401 return ic;
402
403 case STREAM_OOP:
404 /* Refill the buffer... */
405 if (stream->st_oop.ptr == stream->st_oop.end)
406 {
407 char *buf;
408 _gst_msg_sendf(&buf, "%s %o nextAvailable: %i", stream->st_oop.oop, 1024);
409 if (!buf || !*buf)
410 return EOF;
411
412 refill_stream (stream, buf, false);
413 }
414
415 return (unsigned char) *stream->st_oop.ptr++;
416
417 case STREAM_FILE:
418 if (in_stream->column == 0 && in_stream->prompt)
419 {
420 printf ("%s", in_stream->prompt);
421 fflush(stdout);
422 }
423
424 /* Refill the buffer... */
425 if (stream->st_file.ptr == stream->st_file.end)
426 {
427 int n = poll_and_read (stream->st_file.fd, stream->st_file.buf, 1024);
428 if (n < 0)
429 n = 0;
430
431 stream->fileOffset += stream->st_file.ptr - stream->st_file.buf;
432 stream->st_file.end = stream->st_file.buf + n;
433 stream->st_file.ptr = stream->st_file.buf;
434 }
435
436 return (stream->st_file.ptr == stream->st_file.end)
437 ? EOF : (unsigned char) *stream->st_file.ptr++;
438
439 #ifdef HAVE_READLINE
440 case STREAM_READLINE:
441 /* Refill the buffer... */
442 if (stream->st_oop.ptr == stream->st_oop.end)
443 {
444 char *buf = readline (in_stream->prompt
445 ? (char *) in_stream->prompt
446 : (char *) "");
447 if (!buf)
448 return EOF;
449
450 add_history (buf);
451 refill_stream (stream, buf, true);
452 }
453
454 return (unsigned char) *stream->st_oop.ptr++;
455 #endif /* HAVE_READLINE */
456
457 default:
458 _gst_errorf ("Bad stream type passed to my_getc");
459 _gst_had_error = true;
460 }
461 return (ic);
462 }
463
464
465 mst_Boolean
_gst_get_cur_stream_prompt(void)466 _gst_get_cur_stream_prompt (void)
467 {
468 return in_stream && in_stream->prompt;
469 }
470
471 stream_type
_gst_get_cur_stream_type(void)472 _gst_get_cur_stream_type (void)
473 {
474 if (in_stream)
475 return (in_stream->type);
476
477 else
478 return (STREAM_UNKNOWN);
479 }
480
481 OOP
_gst_get_source_string(off_t startPos,off_t endPos)482 _gst_get_source_string (off_t startPos, off_t endPos)
483 {
484 char *p;
485 OOP result;
486 int size;
487
488 if (!in_stream)
489 return (_gst_nil_oop);
490
491 /* FIXME: check isPipe too? */
492 if (startPos != -1 && !_gst_get_cur_stream_prompt ())
493 {
494 OOP fileOOP;
495 gst_file_segment fileSegment;
496 inc_ptr incPtr;
497
498 incPtr = INC_SAVE_POINTER ();
499 fileOOP = get_cur_file ();
500
501 if (!IS_NIL (fileOOP))
502 {
503 INC_ADD_OOP (fileOOP);
504 fileSegment = (gst_file_segment) new_instance (_gst_file_segment_class,
505 &result);
506
507 fileSegment->fileOOP = fileOOP;
508 fileSegment->startPos = from_c_int_64 (startPos);
509 fileSegment->length = from_c_int_64 (endPos - startPos);
510
511 assert (to_c_int_64 (fileSegment->length) >= 0);
512 INC_RESTORE_POINTER (incPtr);
513 return (result);
514 }
515
516 INC_RESTORE_POINTER (incPtr);
517 }
518
519 switch (in_stream->type)
520 {
521 case STREAM_STRING:
522 p = in_stream->st_str.strBase;
523 break;
524
525 #ifdef HAVE_READLINE
526 case STREAM_READLINE:
527 #endif /* HAVE_READLINE */
528 case STREAM_OOP:
529 case STREAM_FILE:
530 p = in_stream->st_oop.buf;
531 break;
532
533 default:
534 return (_gst_nil_oop);
535 }
536
537 if (startPos == -1)
538 result = _gst_string_new (p);
539 else
540 result = _gst_counted_string_new (p + (startPos - in_stream->fileOffset),
541 endPos - startPos);
542
543 if (in_stream->type != STREAM_STRING)
544 {
545 /* Copy back to the beginning of the buffer to save memory. */
546 size = in_stream->st_oop.end - in_stream->st_oop.ptr;
547 if (size)
548 memmove (in_stream->st_oop.buf, in_stream->st_oop.ptr, size);
549 in_stream->st_oop.buf[size] = 0;
550 in_stream->fileOffset += in_stream->st_oop.ptr - in_stream->st_oop.buf;
551 in_stream->st_oop.ptr = in_stream->st_oop.buf;
552 in_stream->st_oop.end = in_stream->st_oop.buf + size;
553 }
554
555 return result;
556 }
557
558 OOP
get_cur_file(void)559 get_cur_file (void)
560 {
561 const char *fullFileName;
562
563 if (!in_stream)
564 return _gst_nil_oop;
565
566 if (!IS_NIL (in_stream->fileOOP))
567 return in_stream->fileOOP;
568
569 if (in_stream->type != STREAM_FILE)
570 return (_gst_nil_oop);
571
572 if (strcmp (in_stream->fileName, "stdin") == 0)
573 fullFileName = strdup (in_stream->fileName);
574 else
575 fullFileName =
576 _gst_get_full_file_name (in_stream->fileName);
577
578 in_stream->fileOOP = _gst_string_new (fullFileName);
579 _gst_register_oop (in_stream->fileOOP);
580 return (in_stream->fileOOP);
581 }
582
583
584
585 void
_gst_warningf_at(int line,const char * str,...)586 _gst_warningf_at (int line,
587 const char *str,
588 ...)
589 {
590 va_list ap;
591
592 va_start (ap, str);
593
594 if (!_gst_report_errors)
595 return;
596
597 fflush (stdout);
598 line_stamp (line);
599 vfprintf (stderr, str, ap);
600 fprintf (stderr, "\n");
601 fflush (stderr);
602 va_end (ap);
603 }
604
605 void
_gst_warningf(const char * str,...)606 _gst_warningf (const char *str,
607 ...)
608 {
609 va_list ap;
610
611 va_start (ap, str);
612
613 if (!_gst_report_errors)
614 return;
615
616 fflush (stdout);
617 line_stamp (0);
618 vfprintf (stderr, str, ap);
619 fprintf (stderr, "\n");
620 fflush (stderr);
621 va_end (ap);
622 }
623
624 void
_gst_errorf_at(int line,const char * str,...)625 _gst_errorf_at (int line,
626 const char *str,
627 ...)
628 {
629 va_list ap;
630
631 va_start (ap, str);
632
633 if (_gst_report_errors)
634 fflush (stdout);
635
636 line_stamp (line);
637 if (_gst_report_errors)
638 {
639 vfprintf (stderr, str, ap);
640 fprintf (stderr, "\n");
641 fflush (stderr);
642 }
643 else
644 {
645 if (_gst_first_error_str == NULL)
646 vasprintf (&_gst_first_error_str, str, ap);
647 }
648
649 va_end (ap);
650 }
651
652 void
_gst_errorf(const char * str,...)653 _gst_errorf (const char *str,
654 ...)
655 {
656 va_list ap;
657
658 va_start (ap, str);
659
660 if (_gst_report_errors)
661 fflush (stdout);
662
663 line_stamp (0);
664 if (_gst_report_errors)
665 {
666 vfprintf (stderr, str, ap);
667 fprintf (stderr, "\n");
668 fflush (stderr);
669 }
670 else
671 {
672 if (_gst_first_error_str == NULL)
673 vasprintf (&_gst_first_error_str, str, ap);
674 }
675
676 va_end (ap);
677 }
678
679 void
_gst_yyerror(const char * s)680 _gst_yyerror (const char *s)
681 {
682 _gst_errorf ("%s", s);
683 }
684
685 YYLTYPE
_gst_get_location(void)686 _gst_get_location (void)
687 {
688 YYLTYPE loc;
689 loc.first_line = in_stream->line;
690 loc.first_column = in_stream->column;
691
692 if (!in_stream || in_stream->fileOffset == -1)
693 loc.file_offset = -1;
694 else
695 /* Subtract 1 to mark the position of the last character we read. */
696 loc.file_offset = (in_stream->st_file.ptr - in_stream->st_file.buf
697 + in_stream->fileOffset - 1);
698
699 return loc;
700 }
701
702 void
line_stamp(int line)703 line_stamp (int line)
704 {
705 if (line <= 0 && in_stream)
706 line = in_stream->line;
707
708 if (_gst_report_errors)
709 {
710 if (in_stream)
711 fprintf (stderr, "%s:%d: ", in_stream->fileName, line);
712 else
713 fprintf (stderr, "gst: ");
714 }
715 else
716 { /* called internally with error
717 handling */
718 if (in_stream)
719 {
720 if (in_stream->fileName)
721 {
722 if (_gst_first_error_str == NULL)
723 _gst_first_error_file = strdup (in_stream->fileName);
724 }
725 if (_gst_first_error_str == NULL)
726 _gst_first_error_line = line;
727 }
728 else
729 {
730 if (_gst_first_error_str == NULL)
731 _gst_first_error_line = -1;
732 }
733 }
734 }
735
736
737 int
_gst_next_char(void)738 _gst_next_char (void)
739 {
740 int ic;
741
742 if (in_stream->pushedBackCount > 0)
743 {
744 ic = (unsigned char)
745 in_stream->pushedBackChars[--in_stream->pushedBackCount];
746 return (ic);
747 }
748 else
749 {
750 ic = my_getc (in_stream);
751 if (ic == '\n')
752 { /* a new line that was not pushed back */
753 in_stream->line++;
754 in_stream->column = 0;
755 }
756 else
757 in_stream->column++;
758
759 return (ic);
760 }
761 }
762
763 void
_gst_unread_char(int ic)764 _gst_unread_char (int ic)
765 {
766 if (ic != EOF)
767 in_stream->pushedBackChars[in_stream->pushedBackCount++] = ic;
768 }
769
770
771
772 /* These two are not used, but are provided for additional flexibility. */
773 void
_gst_enable_completion(void)774 _gst_enable_completion (void)
775 {
776 completions_enabled++;
777 }
778
779 void
_gst_disable_completion(void)780 _gst_disable_completion (void)
781 {
782 completions_enabled--;
783 }
784
785 int
poll_and_read(int fd,char * buf,int n)786 poll_and_read (int fd, char *buf, int n)
787 {
788 int result;
789
790 _gst_wait_for_input (fd);
791 if (_gst_sync_file_polling (fd, 0))
792 {
793 do
794 {
795 errno = 0;
796 result = _gst_read (fd, buf, n);
797 }
798 while ((result == -1) && (errno == EINTR));
799 return result;
800 }
801 else
802 return -1;
803 }
804
805 void
_gst_process_stdin(const char * prompt)806 _gst_process_stdin (const char *prompt)
807 {
808 if (_gst_verbosity == 3 || isatty (0))
809 {
810 printf ("GNU Smalltalk ready\n\n");
811 fflush (stdout);
812 }
813
814 _gst_non_interactive = false;
815 _gst_push_stdin_string ();
816 if (isatty (0))
817 in_stream->prompt = prompt;
818 _gst_parse_stream (false);
819 _gst_pop_stream (true);
820 _gst_non_interactive = true;
821 }
822
823 mst_Boolean
_gst_process_file(const char * fileName,enum gst_file_dir dir)824 _gst_process_file (const char *fileName, enum gst_file_dir dir)
825 {
826 enum undeclared_strategy old;
827 int fd;
828 char *f;
829
830 f = _gst_find_file (fileName, dir);
831 if (!f)
832 {
833 errno = ENOENT;
834 return false;
835 }
836
837 errno = 0;
838 fd = _gst_open_file (f, "r");
839 if (fd != -1)
840 {
841 if (_gst_verbosity == 3)
842 printf ("Processing %s\n", f);
843
844 old = _gst_set_undeclared (UNDECLARED_GLOBALS);
845 _gst_push_unix_file (fd, f);
846 _gst_parse_stream (false);
847 _gst_pop_stream (true);
848 _gst_set_undeclared (old);
849 errno = 0;
850 }
851
852 xfree (f);
853 return (fd != -1);
854 }
855
856
857 #ifdef HAVE_READLINE
858 /* Find apostrophes and double them */
859 char *
readline_quote_filename(const char * s,int rtype,const char * qcp)860 readline_quote_filename (const char *s,
861 int rtype,
862 const char *qcp)
863 {
864 char *r, *base = alloca (strlen (s) * 2 + 2);
865 const char *p;
866
867 int quote;
868
869 r = base;
870 quote = *qcp;
871 if (!quote)
872 quote = *rl_completer_quote_characters;
873
874 *r++ = quote;
875 for (p = s; *p;)
876 {
877 if (*p == quote)
878 *r++ = quote;
879
880 *r++ = *p++;
881 }
882 *r++ = 0;
883 return (strdup (base));
884 }
885
886 /* Find double apostrophes and turn them to single ones */
887 char *
readline_dequote_filename(const char * s,char qc)888 readline_dequote_filename (const char *s,
889 char qc)
890 {
891 char *r, *base = alloca (strlen (s) + 2);
892 const char *p;
893
894 if (!qc)
895 return strdup (s);
896
897 r = base;
898 for (p = s; *p;)
899 {
900 if (*p == qc)
901 p++;
902
903 *r++ = *p++;
904 }
905 *r++ = 0;
906 return (strdup (base));
907 }
908
909 /* Enter an item in the list */
910 void
add_completion(const char * str,int len)911 add_completion (const char *str,
912 int len)
913 {
914 char *s = xmalloc (len + 1);
915 memcpy (s, str, len);
916 s[len] = 0;
917
918 if (!free_items)
919 {
920 free_items += 50;
921 completions =
922 (char **) xrealloc (completions, sizeof (char *) * (count + 50));
923 }
924
925 free_items--;
926 completions[count++] = s;
927 }
928
929 void
_gst_add_symbol_completion(const char * str,int len)930 _gst_add_symbol_completion (const char *str,
931 int len)
932 {
933 const char *base = str;
934 const char *p = str;
935
936 if (completions_enabled < 1)
937 return;
938
939 while (len-- && *p)
940 {
941 if (*p++ == ':' && (base != p - 1))
942 {
943 add_completion (base, p - base);
944 base = p;
945 }
946 }
947
948 /* We enter all unary and binary symbols in the table, too */
949 if (base == str)
950 add_completion (base, p - base);
951 }
952
953 /* Merge the contents of a1 with the contents of a2,
954 * storing the result in a2. If a1 and a2 overlap,
955 * reallocate must be true.
956 */
957 void
merge(char ** a1,int count1,char ** a2,int count2,mst_Boolean reallocate)958 merge (char **a1,
959 int count1,
960 char **a2,
961 int count2,
962 mst_Boolean reallocate)
963 {
964 char *source, *dest;
965
966 /* Check if an array is empty */
967 if (!count1)
968 return;
969
970 if (!count2)
971 {
972 memmove (a1, a2, count1 * sizeof (char *));
973 return;
974 }
975
976 if (reallocate)
977 {
978 char **new = (char **) alloca (count1 * sizeof (char *));
979 memcpy (new, a1, count1 * sizeof (char *));
980 a1 = new;
981 }
982
983 source = a1[count1 - 1];
984 dest = a2[count2 - 1];
985 for (;;)
986 {
987 if (strcmp (source, dest) < 0)
988 {
989 /* Take it from the destination array */
990 a2[count2 + count1 - 1] = dest;
991 if (--count2 == 0)
992 {
993 /* Any leftovers from the source array? */
994 memcpy (a1, a2, count1 * sizeof (char *));
995 return;
996 }
997
998 dest = a2[count2 - 1];
999 }
1000 else
1001 {
1002 /* Take it from the source array */
1003 a2[count2 + count1 - 1] = source;
1004 if (--count1 == 0)
1005 return;
1006
1007 source = a1[count1 - 1];
1008 }
1009 }
1010
1011 }
1012
1013 /* Comparison function for qsort */
1014 int
compare_strings(const PTR a,const PTR b)1015 compare_strings (const PTR a,
1016 const PTR b)
1017 {
1018 const char **s1 = (const char **) a;
1019 const char **s2 = (const char **) b;
1020
1021 return strcmp (*s1, *s2);
1022 }
1023
1024 /* Communication between symbol_generator and readline_match_symbols */
1025 static int matches_left, current_index;
1026
1027 char *
symbol_generator(const char * text,int state)1028 symbol_generator (const char *text,
1029 int state)
1030 {
1031 if (matches_left == 0)
1032 return (NULL);
1033
1034 /* Since we have to sort the array to perform the binary search, we
1035 remove duplicates and avoid that readline resorts the result. */
1036 while (matches_left > 1 &&
1037 strcmp (completions[current_index],
1038 completions[current_index + 1]) == 0)
1039 {
1040 current_index++;
1041 matches_left--;
1042 }
1043
1044 matches_left--;
1045 return strdup (completions[current_index++]);
1046 }
1047
1048
1049 char **
readline_match_symbols(char * text,int start,int end)1050 readline_match_symbols (char *text,
1051 int start,
1052 int end)
1053 {
1054 int low, high, middle, len;
1055
1056 /* Check for strings (not matched) and for symbols (matched) */
1057 if (start != 0 && rl_line_buffer[start - 1] == '\'')
1058 {
1059 if (start == 1 || rl_line_buffer[start - 2] != '#')
1060 {
1061 return NULL;
1062 }
1063 }
1064
1065 /* Prepare for binary searching. We use qsort when necessary, and
1066 merge the result, instead of doing expensive (quadratic) insertion
1067 sorts. */
1068 if (sorted_count < count)
1069 {
1070 qsort (&completions[sorted_count], count - sorted_count,
1071 sizeof (char *), compare_strings);
1072
1073 merge (&completions[sorted_count], count - sorted_count,
1074 completions, sorted_count, true);
1075
1076 sorted_count = count;
1077 }
1078
1079 /* Initialize current_index and matches_left with two binary
1080 searches. */
1081 len = strlen (text);
1082
1083 /* The first binary search gives the first matching item. */
1084 low = -1;
1085 high = count;
1086 while (low + 1 != high)
1087 {
1088 middle = (low + high) / 2;
1089 if (strncmp (completions[middle], text, len) < 0)
1090 low = middle;
1091 else
1092 high = middle;
1093 }
1094 current_index = high;
1095
1096 /* This binary search gives the first non-matching item instead */
1097 low = -1;
1098 high = count;
1099 while (low + 1 != high)
1100 {
1101 middle = (low + high) / 2;
1102 if (strncmp (completions[middle], text, len) <= 0)
1103 low = middle;
1104 else
1105 high = middle;
1106 }
1107
1108 matches_left = high - current_index;
1109 return matches_left ? rl_completion_matches (text,
1110 symbol_generator) : NULL;
1111 }
1112
1113 int
readline_getc(FILE * file)1114 readline_getc (FILE * file)
1115 {
1116 int result;
1117 unsigned char ch;
1118 result = poll_and_read (fileno (file), &ch, 1);
1119
1120 return (result < 1) ? EOF : (int) ch;
1121 }
1122
1123 void
_gst_initialize_readline(void)1124 _gst_initialize_readline (void)
1125 {
1126 static char everything[255];
1127 int i;
1128
1129 /* Allow conditional parsing of the ~/.inputrc file. */
1130 rl_readline_name = (char *) "Smalltalk";
1131
1132 /* Always put filenames in quotes */
1133 for (i = 0; i < 255; i++)
1134 everything[i] = i + 1;
1135
1136 rl_filename_quote_characters = everything;
1137 rl_completer_quote_characters = (char *) "'\"";
1138 rl_basic_word_break_characters = (char *) "() []{};+-=*<>~'?%/@|&#^\"\\.";
1139
1140 /* Consider binary selectors both word-breaking characters and
1141 candidates for completion */
1142 rl_special_prefixes = (char *) "+-=*<>~?%/@|&\\";
1143
1144 /* Our rules for quoting are a bit different from the default */
1145 rl_filename_quoting_function = (CPFunction *) readline_quote_filename;
1146 rl_filename_dequoting_function =
1147 (CPFunction *) readline_dequote_filename;
1148
1149 /* Try to match a symbol before a filename */
1150 rl_attempted_completion_function =
1151 (CPPFunction *) readline_match_symbols;
1152
1153 /* Since we have to sort the array to perform the binary search,
1154 remove duplicates and avoid that readline resorts the result. */
1155 rl_ignore_completion_duplicates = 0;
1156
1157 /* Set up to use read to read from stdin */
1158 rl_getc_function = readline_getc;
1159 }
1160
1161 #endif /* HAVE_READLINE */
1162