1 /* ---------------------------------------------------------------------
2    $Id: redline.c 5736 2021-03-16 10:41:22Z arthurcnorman $
3    ---------------------------------------------------------------------
4    Copyright (c) 2009 Thomas Sturm
5    ---------------------------------------------------------------------
6    Redistribution and use in source and binary forms, with or without
7    modification, are permitted provided that the following conditions
8    are met:
9 
10       * Redistributions of source code must retain the relevant
11         copyright notice, this list of conditions and the following
12         disclaimer.
13       * Redistributions in binary form must reproduce the above
14         copyright notice, this list of conditions and the following
15         disclaimer in the documentation and/or other materials provided
16         with the distribution.
17 
18    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22    OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30 
31 #include "redfront.h"
32 
33 static strl line_switchlist = NULL;
34 static strl line_packlist = NULL;
35 static strl line_loadlist = NULL;
36 static strl line_adhoclist = NULL;
37 
38 #include <sys/stat.h>
39 
40 static char line_prompt[50];
41 
42 static wchar_t line_break_chars[] = {' ', '\t', '\n', '"', '\\', '\'', '`', '@',
43 				  '$', '>', '<', '=', ';', '|', '&', '{', '(',
44 				  ',', '\0'};
45 
46 
line_init(void)47 void line_init(void) {
48   e = el_init("redfront",stdin,stdout,stderr);
49   el_set(e,EL_SIGNAL,0);
50 #ifdef EL_PROMPT_ESC
51   el_set(e,EL_PROMPT_ESC,line_get_prompt,PROMPT_IGNORE);
52 #else
53   el_set(e,EL_PROMPT,line_get_prompt);
54 #endif
55   el_set(e,EL_EDITOR,"emacs");
56   el_set(e,EL_BIND,"^R","em-inc-search-prev",NULL);
57   el_set(e,EL_ADDFN,"line_complete","ReadLine style completion",line_complete);
58   el_set(e,EL_BIND,"^I","line_complete",NULL);
59   el_set(e,EL_ADDFN,"line_help","bind",line_help);
60   el_set(e,EL_BIND,"\033OQ","line_help",NULL);
61   el_set(e,EL_ADDFN,"line_learn_completion","learn completions",
62 	 _line_learn_completion);
63   el_set(e,EL_BIND,"^[z","line_learn_completion",NULL);
64   el_source(e, NULL);
65 }
66 
_line_learn_completion(EditLine * ignore,int invoking_key)67 unsigned char _line_learn_completion(EditLine *ignore,int invoking_key) {
68   char dummy_prompt[50];
69 
70   line_learn_completion(dummy_prompt);
71   return CC_REDISPLAY;
72 }
73 
74 
line_get_prompt(EditLine * e)75 char *line_get_prompt(EditLine *e) {
76   return line_prompt;
77 }
78 
line_complete(EditLine * ignore,int invoking_key)79 unsigned char line_complete(EditLine *ignore,int invoking_key)
80 {
81   unsigned char hit=0;
82   const LineInfo *li;
83   const char *ctemp;
84 
85   li = el_line(e);
86 
87   for (ctemp = li->cursor; ctemp >= li->buffer; ctemp--) {
88 
89     if (strncmp(ctemp,"load_package",12) == 0) {
90       hit = line_fn_complete(e,
91 			      line_pack_completion_function,
92 			      line_break_chars,
93 			      line_append_no_char,
94 			      QUERY_ITEMS);
95       break;
96     }
97 
98     else if (strncmp(ctemp,"load",4) == 0) {
99       hit = line_fn_complete(e,
100 			      line_load_completion_function,
101 			      line_break_chars,
102 			      line_append_no_char,
103 			      QUERY_ITEMS);
104       break;
105     }
106 
107     else if (*ctemp == '"') {
108       hit = line_fn_complete(e,
109 			      line_filename_completion_function,
110 			      line_break_chars,
111 			      line_append_char_function,
112 			      QUERY_ITEMS);
113       break;
114     }
115 
116     else if (strncmp(ctemp,"on",2) == 0 || strncmp(ctemp,"off",3) == 0) {
117       hit = line_fn_complete(e,
118 			      line_switch_completion_function,
119 			      line_break_chars,
120 			      line_append_no_char,
121 			      QUERY_ITEMS);
122       break;
123     }
124   }
125 
126   if (hit)
127     return hit;
128 
129   return line_fn_complete(e,
130 			  line_adhoc_completion_function,
131 			  line_break_chars,
132 			  line_append_no_char,
133 			  QUERY_ITEMS);
134 }
135 
line_fn_complete(EditLine * el,char * (* complet_func)(const char *,int),const wchar_t * word_break,const char * (* app_func)(const char *),size_t query_items)136 unsigned char line_fn_complete(EditLine *el,
137 			       char *(*complet_func)(const char *, int),
138 			       const wchar_t *word_break,
139 			       const char *(*app_func)(const char *),
140 			       size_t query_items) {
141   return (unsigned char )fn_complete(el,
142 				     complet_func,
143 				     NULL,
144 				     word_break,
145 				     NULL,
146 				     app_func,
147 				     query_items,
148 				     NULL,
149 				     NULL,
150 				     NULL,
151 				     NULL);
152 }
153 
line_filename_completion_function(const char * text,int state)154 char *line_filename_completion_function(const char *text, int state) {
155   char *completion;
156   char *mycompletion;
157 
158   completion = fn_filename_completion_function(text,state);
159 
160   if (!completion)
161     return completion;
162 
163   if (completion[0] == '.' && completion[1] == '/') {
164     mycompletion = (char *)malloc((strlen(completion)-1)*sizeof(char));
165     strcpy(mycompletion,completion+2*sizeof(char));
166     free(completion);
167     return mycompletion;
168   }
169 
170   return completion;
171 }
172 
line_append_char_function(const char * name)173 const char *line_append_char_function(const char *name) {
174   struct stat stbuf;
175   char *expname = *name == '~' ? fn_tilde_expand(name) : NULL;
176   const char *rs = "";
177 
178   if (stat(expname ? expname : name, &stbuf) == -1)
179     goto out;
180   if (S_ISDIR(stbuf.st_mode))
181     rs = "/";
182  out:
183   if (expname)
184     free(expname);
185   return rs;
186 }
187 
line_switch_completion_function(const char * text,int state)188 char *line_switch_completion_function(const char *text, int state) {
189   return line_strl_completion_function(text,state,line_switchlist);
190 }
191 
line_pack_completion_function(const char * text,int state)192 char *line_pack_completion_function(const char *text, int state) {
193   return line_strl_completion_function(text,state,line_packlist);
194 }
195 
line_load_completion_function(const char * text,int state)196 char *line_load_completion_function(const char *text, int state) {
197   return line_strl_completion_function(text,state,line_loadlist);
198 }
199 
line_strl_completion_function(const char * text,int state,strl clist)200 char *line_strl_completion_function(const char *text, int state,strl clist) {
201   char *res;
202   const char *this;
203   static char init = 1;
204   static strl clist_current = NULL;
205 
206   if (init) {
207     init = 0;
208     clist_current = clist;
209   }
210 
211   while (clist_current) {
212     this = clist_current->this;
213     clist_current = clist_current->next;
214     // printf("\nclist_current=%d, text=%s, this=%s",clist_current,text,this);
215     if (strncmp(text,this,strlen(text)) == 0) {
216       // printf("\nHit!");
217       res = malloc((strlen(this)+1)*sizeof(char));
218       strcpy(res,this);
219       return res;
220     }
221   }
222 
223   init = 1;
224   return NULL;
225 }
226 
line_adhoc_completion_function(const char * text,int state)227 char *line_adhoc_completion_function(const char *text, int state) {
228   if (strcmp(text,"") == 0) {
229     /* This is going to happen systematically and frequently when there
230        is no match at the first TAB strike with the other completion
231        functions. So I catch this asap here. */
232     return (char *)0;
233   }
234 
235   if (strncmp(text,"load_",5) == 0)
236     return line_strl_completion_function(text,state,line_adhoclist);
237 
238   return (char *)0;
239 }
240 
line_help(EditLine * ignore,int invoking_key)241 unsigned char line_help(EditLine *ignore,int invoking_key) {
242   const char *argv[2];
243 
244   argv[0]="bind";
245   argv[1]=(char *)0;
246   textcolor(redfrontcolor);
247   el_parse(e,1,argv);
248   textcolor(inputcolor);
249   return CC_REDISPLAY;
250 }
251 
line_append_no_char(const char * name)252 const char *line_append_no_char(const char *name) {
253   return "";
254 }
255 
line_read(char * prompt)256 char *line_read(char *prompt) {
257   const char *ret;
258   static char *line=NULL;
259   int nchar;
260 
261   if (line)
262     free(line);
263   textcolor(inputcolor);
264   strcpy(line_prompt,prompt);
265   deb_fprintf(stderr,"before el_gets\n");
266   ret = el_gets(e,&nchar);
267   deb_fprintf(stderr,"after el_gets\n");
268   if (ret && nchar > 0) {
269     line = strdup(ret);
270     if (line == NULL)
271       return NULL;
272     if (line[nchar - 1] == '\n')
273       line[nchar - 1] = '\0';
274   } else
275     line = NULL;
276   return line;
277 }
278 
line_quit(const char * quit)279 char *line_quit(const char *quit) {
280   static char *line=NULL;
281 
282   if (line != NULL)
283     free(line);
284   line = malloc((strlen(quit) + 2) * sizeof(char));
285   sprintf(line,"%s\n",quit);
286   textcolor(inputcolor);
287   printf("\b\b  \b\b%s",line);
288   resetcolor();
289   return line;
290 }
291 
line_color_prompt(char der_prompt[])292 char *line_color_prompt(char der_prompt[]) {
293   if (color) {
294     char tmp[100];
295     if (strlen(der_prompt) + 26 > sizeof(tmp)-8)
296     {   fprintf(stderr, "\nBuffer overflow with long prompt string\n");
297         abort();
298     }
299     strcpy(tmp,der_prompt);
300 /*
301  * I suspect the issue is within libedit - and I will need to investigate -
302  * but the recolouring of the prompt that should be expected here seems
303  * not to occur. I suspect that PROMPT_IGNORE is having a bad effect???
304  */
305     sprintf(der_prompt,"%c%c[%d;%d;%dm%c%s%c%c[%d;%d;%dm%c",
306 	    PROMPT_IGNORE,0x1B,0,promptcolor+30,9+40,PROMPT_IGNORE,
307 	    tmp,
308 	    PROMPT_IGNORE,0x1B,0,inputcolor+30,9+40,PROMPT_IGNORE);
309   }
310   return der_prompt;
311 }
312 
line_end(void)313 void line_end(void) {
314   el_end(e);
315 }
316 
line_init_history(void)317 void line_init_history(void) {
318   char *hname;
319 
320   h = history_init();
321   el_set(e,EL_HIST,history,h);
322   history(h,&ev,H_SETSIZE,HISTFILESIZE);
323   history(h,&ev,H_SETUNIQUE,1); /* Ignore duplicates */
324   hname = line_histname();
325   history(h,&ev,H_LOAD,hname);
326   free(hname);
327 }
328 
line_add_history(char this_command[])329 void line_add_history(char this_command[]) {
330   if (this_command != (char *)NULL && *this_command != 0) {
331     history(h,&ev,H_ENTER,this_command);
332     free(this_command);
333   }
334 }
335 
line_end_history(void)336 void line_end_history(void) {
337   char *hname;
338 
339   history(h,&ev,H_SETSIZE,HISTFILESIZE);
340   hname = line_histname();
341   history(h,&ev,H_SAVE,hname);
342   free(hname);
343 }
344 
line_learn_completion(char der_prompt[])345 void line_learn_completion(char der_prompt[]) {
346   char tmp[1024];
347 
348   vbprintf("Redfront learned ");
349 
350   sprintf(tmp,"lisp redfront_send!-packages(nil)$");
351   line_packlist = strl_delete(line_packlist);
352   send_reduce(tmp);
353   line_packlist = line_learn_until_prompt(der_prompt,"packages");
354 
355 #ifdef PSL
356   line_loadlist = line_packlist;
357 #else
358   vbprintf(", ");
359   line_loadlist = strl_delete(line_loadlist);
360   send_reduce("lisp redfront_send!-modules()$");
361   line_loadlist = line_learn_until_prompt(der_prompt,"modules");
362 #endif
363 
364   vbprintf(", ");
365 
366   line_switchlist = strl_delete(line_switchlist);
367   send_reduce("lisp redfront_send!-switches()$");
368   line_switchlist = line_learn_until_prompt(der_prompt,"switches");
369 
370   vbprintf("\n");
371 
372   line_adhoclist = strl_delete(line_adhoclist);
373   line_adhoclist = strl_cadd(line_adhoclist,"load_package ");
374 }
375 
line_learn_until_prompt(char der_prompt[],const char * what)376 strl line_learn_until_prompt(char der_prompt[],const char *what){
377   int status=SKIPPING_WHITESPACE;
378   char buffer[1000];
379   int ncharread;
380   int ii;
381   int pii=0;
382   char ch;
383   char current[256];  // I assume that there are no longer switch names for now
384   int i = 0;
385   int learned=0;
386   strl clist=NULL;
387 
388   deb_fprintf(stderr,
389 	      "parent: entering line_learn_until_prompt() ... der_prompt=%s\n",
390 	      der_prompt);
391 
392   while(status != FINISHED) {
393     ncharread = redread(ReduceToMe[0],buffer,1000);
394     for (ii=0; ii < ncharread; ii++) {
395       ch = buffer[ii];
396       if (ch == (char) 0x01) {
397 	pii = 0;
398 	status = READING_PROMPT;
399       } else if (ch == (char) 0x02) {
400 	status = FINISHED;
401       } else if (ch == (char) 0x05) {
402 	i = 0;
403 	status = LEARNING;
404       } else if (ch == (char) 0x06) {
405 	current[i] = 0;
406 	clist = strl_cadd(clist,current);
407 	learned++;
408 	status = SKIPPING_WHITESPACE;
409       } else if (status == LEARNING) {
410 	current[i++] = ch;
411       } else if (status == READING_PROMPT) {
412 	if ((int) ch > 31)
413 	  der_prompt[pii++] = ch;
414       } else {	/* (status == FINISHED) */
415 	der_prompt[pii] = 0x00;
416       }
417     }
418   }
419 
420   vbprintf("%d %s",learned,what);
421 
422   deb_fprintf(stderr,"parent: ... leaving learn_until_prompt()\n");
423 
424   return clist;
425 }
426 
line_histname(void)427 char *line_histname(void) {
428   char *hname, *hname1;
429   hname1 = getenv("HOME");
430   if (hname1 == NULL) hname1 = "/tmp";
431   hname = (char *)malloc(strlen(hname1)+strlen("/.reduce_history")+1);
432   sprintf(hname,"%s/.reduce_history",hname1);
433   return hname;
434 }
435 
436 /* end of redline.c */
437