1 /****************************************
2 *  Computer Algebra System SINGULAR     *
3 ****************************************/
4 /*
5 * ABSTRACT: input from ttys, simulating fgets
6 */
7 
8 #include "kernel/mod2.h"
9 #include <errno.h>
10 
11 // ----------------------------------------
12 // system settings:
13 
14 #undef USE_READLINE4
15 
16 //----------------------------------------
17 #ifdef __CYGWIN__
18 #define READLINE_STATIC
19 #endif
20 #include "omalloc/omalloc.h"
21 #include "misc/options.h"
22 
23 #include "kernel/oswrapper/feread.h"
24 
25 #if defined(HAVE_DYN_RL)
26 #include <unistd.h>
27 #endif
28 
29 static char * fe_fgets_stdin_init(const char *pr,char *s, int size);
30 char * (*fe_fgets_stdin)(const char *pr,char *s, int size)
31  = fe_fgets_stdin_init;
32 
33 extern char *iiArithGetCmd(int);
34 
35 /* ===================================================================*/
36 /* =                   static/dymanic readline                      = */
37 /* ===================================================================*/
38 #if defined(HAVE_READLINE) || defined(HAVE_DYN_RL) || defined(HAVE_LIBREADLINE)
39 
40 #ifndef STDOUT_FILENO
41 #define STDOUT_FILENO 1
42 #endif
43 
44 /* Generator function for command completion.  STATE lets us know whether
45 *   to start from scratch; without any state (i.e. STATE == 0), then we
46 *   start at the top of the list.
47 */
48 #include "Singular/ipid.h"
49 extern "C"
command_generator(char * text,int state)50 char *command_generator (char *text, int state)
51 {
52   STATIC_VAR int list_index, len;
53   STATIC_VAR idhdl h;
54   const char *name;
55 
56   /* If this is a new word to complete, initialize now.  This includes
57      saving the length of TEXT for efficiency, and initializing the index
58      variable to 0. */
59   if (state==0)
60   {
61     list_index = 1;
62     len = strlen (text);
63     h=basePack->idroot;
64   }
65 
66   /* Return the next name which partially matches from the command list. */
67   while ((name = iiArithGetCmd(list_index))!=NULL)
68   {
69     list_index++;
70 
71     if (strncmp (name, text, len) == 0)
72       return (strdup(name));
73   }
74   if (len>1)
75   {
76     while (h!=NULL)
77     {
78       name=h->id;
79       h=h->next;
80       if (strncmp (name, text, len) == 0)
81         return (strdup(name));
82     }
83   }
84   /* If no names matched, then return NULL. */
85   return ((char *)NULL);
86 }
87 #endif
88 
89 /* ===================================================================*/
90 /* =                      static readline                           = */
91 /* ===================================================================*/
92 /* some procedure are shared with "dynamic readline" */
93 #if (defined(HAVE_READLINE) || defined(HAVE_LIBREADLINE) || defined(HAVE_DYN_RL))
94 #include <unistd.h>
95 #include <stdio.h>
96 #include <stdlib.h>
97 #include <sys/types.h>
98 #include <sys/file.h>
99 #include <sys/stat.h>
100 
101 // #undef READLINE_READLINE_H_OK
102 
103 extern "C" {
104   typedef char * (*RL_PROC)(const char*,int);
105   #ifdef READLINE_READLINE_H_OK
106     #include <readline/readline.h>
107     #ifdef HAVE_READLINE_HISTORY_H
108       #include <readline/history.h>
109     #endif
110   #endif
111 
112   #ifdef RL_VERSION_MAJOR
113     #if (RL_VERSION_MAJOR >= 4)
114       #define USE_READLINE4
115     #endif
116   #endif
117 
118   #ifndef USE_READLINE4
119     #define rl_filename_completion_function filename_completion_function
120     #define rl_completion_matches           completion_matches
121   #endif
122   #ifndef READLINE_READLINE_H_OK
123     /* declare everything we need explicitely and do not rely on includes */
124     EXTERN_VAR char * rl_readline_name;
125     EXTERN_VAR char *rl_line_buffer;
126     char *rl_filename_completion_function(const char*, int);
127     typedef char **CPPFunction ();
128 
129     extern char ** rl_completion_matches (const char*, RL_PROC);
130     EXTERN_VAR CPPFunction * rl_attempted_completion_function;
131     EXTERN_VAR FILE * rl_outstream;
132     extern char * readline (const char *);
133     extern void add_history (char *);
134     extern int write_history ();
135     extern void using_history();
136     extern int read_history(char *);
137     extern int history_total_bytes();
138   #endif /* READLINE_READLINE_H_OK */
139 
140   typedef char * (*PROC)();
141 
142   typedef char **RL_CPPFunction (const char*, int,int);
143 }
144 
145 
146 char * fe_fgets_stdin_rl(const char *pr,char *s, int size);
147 
148 /* Tell the GNU Readline library how to complete.  We want to try to complete
149    on command names  or on filenames if it is preceded by " */
150 
151 /* Attempt to complete on the contents of TEXT.  START and END show the
152 *   region of TEXT that contains the word to complete.  We can use the
153 *   entire line in case we want to do some simple parsing.  Return the
154 *   array of matches, or NULL if there aren't any.
155 */
156 #if defined(HAVE_DYN_RL)
157 extern "C"
158 {
159   int fe_init_dyn_rl();
160   char *(*fe_filename_completion_function)(); /* 3 */
161   char *(* fe_readline) (char *);             /* 4 */
162   VAR void (*fe_add_history) (char *);            /* 5 */
163   VAR char ** fe_rl_readline_name;                /* 6 */
164   VAR char **fe_rl_line_buffer;                   /* 7 */
165   char **(*fe_completion_matches)(...);          /* 8 */
166   VAR CPPFunction **fe_rl_attempted_completion_function; /* 9 */
167   VAR FILE ** fe_rl_outstream;                    /* 10 */
168   VAR int (*fe_write_history) ();                 /* 11 */
169   VAR int (*fe_history_total_bytes) ();           /* 12 */
170   VAR void (*fe_using_history) ();                /* 13 */
171   VAR int (*fe_read_history) (char *);            /* 14 */
172 
173 }
174 #endif
singular_completion(char * text,int start,int end)175 char ** singular_completion (char *text, int start, int end)
176 {
177   /* If this word is not in a string, then it may be a command
178      to complete.  Otherwise it may be the name of a file in the current
179      directory. */
180 #ifdef HAVE_DYN_RL
181   #define x_rl_line_buffer (*fe_rl_line_buffer)
182   #define x_rl_completion_matches (*fe_completion_matches)
183   #define x_rl_filename_completion_function (*fe_filename_completion_function)
184 #else
185   #define x_rl_line_buffer rl_line_buffer
186   #define x_rl_completion_matches rl_completion_matches
187   #define x_rl_filename_completion_function rl_filename_completion_function
188 #endif
189   if ((start>0) && (x_rl_line_buffer[start-1]=='"'))
190     return x_rl_completion_matches (text, (RL_PROC)x_rl_filename_completion_function);
191   char **m=x_rl_completion_matches (text, (RL_PROC)command_generator);
192 #undef x_rl_line_buffer
193 #undef x_rl_completion_matches
194   if (m==NULL)
195   {
196     m=(char **)malloc(2*sizeof(char*));
197     m[0]=(char *)malloc(end-start+2);
198     strncpy(m[0],text,end-start+1);
199     m[1]=NULL;
200   }
201   return m;
202 }
203 
204 #ifndef HAVE_DYN_RL
fe_fgets_stdin_rl(const char * pr,char * s,int size)205 char * fe_fgets_stdin_rl(const char *pr,char *s, int size)
206 {
207   if (!BVERBOSE(V_PROMPT))
208   {
209     pr="";
210   }
211   mflush();
212 
213   char *line;
214   line = readline (pr);
215 
216   if (line==NULL)
217     return NULL;
218 
219   int l=strlen(line);
220   for (int i=l-1;i>=0;i--) line[i]=line[i]&127;
221 
222   if (*line!='\0')
223   {
224     add_history (line);
225   }
226   if (l>=size-1)
227   {
228     strncpy(s,line,size);
229   }
230   else
231   {
232     strncpy(s,line,l);
233     s[l]='\n';
234     s[l+1]='\0';
235   }
236   free (line);
237 
238   return s;
239 }
240 #endif
241 #endif
242 
243 /* ===================================================================*/
244 /* =                    emulated readline                           = */
245 /* ===================================================================*/
246 #if !defined(HAVE_READLINE) && defined(HAVE_FEREAD)
247 extern "C" {
248 char * fe_fgets_stdin_fe(const char *pr,char *s, int size);
249 }
fe_fgets_stdin_emu(const char * pr,char * s,int size)250 char * fe_fgets_stdin_emu(const char *pr,char *s, int size)
251 {
252   if (!BVERBOSE(V_PROMPT))
253   {
254     pr="";
255   }
256   mflush();
257   return fe_fgets_stdin_fe(pr,s,size);
258 }
259 #endif
260 
261 /* ===================================================================*/
262 /* =                     dynamic readline                           = */
263 /* ===================================================================*/
264 /* some procedure are shared with "static readline" */
265 #if defined(HAVE_DYN_RL)
fe_fgets_stdin_drl(const char * pr,char * s,int size)266 char * fe_fgets_stdin_drl(const char *pr,char *s, int size)
267 {
268   if (!BVERBOSE(V_PROMPT))
269   {
270     pr="";
271   }
272   mflush();
273 
274   char *line;
275   line = (*fe_readline) ((char*)pr);
276 
277   if (line==NULL)
278     return NULL;
279 
280   int l=strlen(line);
281   for (int i=l-1;i>=0;i--) line[i]=line[i]&127;
282 
283   if (*line!='\0')
284   {
285     (*fe_add_history) (line);
286   }
287   if (l>=size-1)
288   {
289     strncpy(s,line,size);
290   }
291   else
292   {
293     strncpy(s,line,l);
294     s[l]='\n';
295     s[l+1]='\0';
296   }
297   free (line);
298 
299   return s;
300 }
301 #endif
302 
303 /* ===================================================================*/
304 /* =                        fgets                                   = */
305 /* ===================================================================*/
fe_fgets(const char * pr,char * s,int size)306 char * fe_fgets(const char *pr,char *s, int size)
307 {
308   if (BVERBOSE(V_PROMPT))
309   {
310     fputs(pr,stdout);
311   }
312   mflush();
313   errno=0;
314   char *line=fgets(s,size,stdin);
315   if (line!=NULL)
316   {
317     for (int i=strlen(line)-1;i>=0;i--) line[i]=line[i]&127;
318   }
319   else
320   {
321     /* NULL can mean various things... */
322     switch(errno)
323     {
324       case 0:     return NULL;           /*EOF */
325       case EBADF: return NULL;           /* stdin got closed */
326       case EINTR: return strcpy(s,"\n"); /* CTRL-C or other signal */
327       default:                           /* other error */
328       {
329         int errsv = errno;
330         fprintf(stderr,"fgets() failed with errno %d\n%s\n",errsv,strerror(errsv));
331         return NULL;
332       }
333     }
334   }
335   return line;
336 }
337 
338 /* ===================================================================*/
339 /* =       init for static rl, dyn. rl, emu. rl                     = */
340 /* ===================================================================*/
fe_fgets_stdin_init(const char * pr,char * s,int size)341 static char * fe_fgets_stdin_init(const char *pr,char *s, int size)
342 {
343 #if (defined(HAVE_READLINE) || defined(HAVE_LIBREADLINE)) && !defined(HAVE_DYN_RL) && !defined(HAVE_FEREAD)
344   /* Allow conditional parsing of the ~/.inputrc file. */
345   rl_readline_name = (char*)"Singular";
346   /* Tell the completer that we want a crack first. */
347 #ifdef USE_READLINE4
348   rl_attempted_completion_function = (rl_completion_func_t *)singular_completion;
349 #else
350   rl_attempted_completion_function = (CPPFunction *)singular_completion;
351 #endif
352 
353   /* set the output stream */
354   if(!isatty(STDOUT_FILENO))
355   {
356     #ifdef atarist
357       rl_outstream = fopen( "/dev/tty", "w" );
358     #else
359       char *fn=ttyname(fileno(stdin));//if stdout is not a tty, maybe stdin is?
360       if (fn!=NULL) rl_outstream = fopen( fn, "w" );
361     #endif
362   }
363 
364   if(isatty(fileno(stdin)))
365   {
366     /* try to read a history */
367     using_history();
368     char *p = getenv("SINGULARHIST");
369     if (p != NULL)
370     {
371       read_history (p);
372     }
373     fe_fgets_stdin=fe_fgets_stdin_rl;
374     return(fe_fgets_stdin_rl(pr,s,size));
375   }
376   else
377   {
378     fe_fgets_stdin=fe_fgets;
379     return(fe_fgets(pr,s,size));
380   }
381 #endif
382 #ifdef HAVE_DYN_RL
383   /* do dynamic loading */
384   int res=fe_init_dyn_rl();
385   if (res!=0)
386   {
387     //if (res==1)
388     //  WarnS("dynamic loading of libreadline failed");
389     //else
390     //  Warn("dynamic loading failed: %d\n",res);
391     if (res!=1)
392       Warn("dynamic loading failed: %d\n",res);
393     #ifdef HAVE_FEREAD
394     fe_fgets_stdin=fe_fgets_stdin_emu;
395     #else
396     fe_fgets_stdin=fe_fgets;
397     #endif
398     return fe_fgets_stdin(pr,s,size);
399   }
400   else if (isatty(STDIN_FILENO))/*and could load libreadline: */
401   {
402     /* Allow conditional parsing of the ~/.inputrc file. */
403     *fe_rl_readline_name = "Singular";
404     /* Tell the completer that we want a crack first. */
405     *fe_rl_attempted_completion_function = (CPPFunction *)singular_completion;
406     /* try to read a history */
407     (*fe_using_history)();
408     char *p = getenv("SINGULARHIST");
409     if (p != NULL)
410     {
411       (*fe_read_history) (p);
412     }
413 
414     /* set the output stream */
415     if(!isatty(STDOUT_FILENO))
416     {
417       #ifdef atarist
418         *fe_rl_outstream = fopen( "/dev/tty", "w" );
419       #else
420         char *fn=ttyname(fileno(stdin));//if stdout is not a tty, maybe stdin is?
421         if (fn!=NULL) *fe_rl_outstream = fopen( fn, "w" );
422       #endif
423     }
424     fe_fgets_stdin=fe_fgets_stdin_drl;
425     return fe_fgets_stdin_drl(pr,s,size);
426   }
427   else
428   {
429     fe_fgets_stdin=fe_fgets;
430     return fe_fgets(pr,s,size);
431   }
432 #else
433   #if !defined(HAVE_READLINE) && defined(HAVE_FEREAD)
434     fe_fgets_stdin=fe_fgets_stdin_emu;
435     return(fe_fgets_stdin_emu(pr,s,size));
436   #else
437     fe_fgets_stdin=fe_fgets;
438     return(fe_fgets(pr,s,size));
439   #endif
440 #endif
441 }
442 
443 /* ===================================================================*/
444 /* =                      batch mode                                = */
445 /* ===================================================================*/
446 /* dummy (for batch mode): */
fe_fgets_dummy(const char *,char *,int)447 char * fe_fgets_dummy(const char */*pr*/,char */*s*/, int /*size*/)
448 {
449   return NULL;
450 }
451 
452