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