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