YAP 7.1.0
readline.c
Go to the documentation of this file.
1/*************************************************************************
2 * *
3 * YAP Prolog *
4 * *
5 * Yap Prolog was developed at NCCUP - Universidade do Porto *
6 * *
7 * Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997 *
8 * *
9 **************************************************************************
10 * *
11 * File: readline.c *
12 * Last rev: 5/2/88 *
13 * mods: *
14 * comments: Input/Output C implemented predicates *
15 * *
16 *************************************************************************/
17#ifdef SCCS
18static char SccsId[] = "%W% %G%";
19#endif
20
29#include "Yap.h"
30#include "YapHeap.h"
31#include "Yatom.h"
32#include "yapio.h"
33#include <stdlib.h>
34#if HAVE_UNISTD_H
35#include <unistd.h>
36#endif
37#if HAVE_STDARG_H
38#include <stdarg.h>
39#endif
40#ifdef _WIN32
41#if HAVE_IO_H
42/* Windows */
43#include <io.h>
44#endif
45#if HAVE_SOCKET
46#include <winsock2.h>
47#endif
48#include <windows.h>
49#ifndef S_ISDIR
50#define S_ISDIR(x) (((x)&_S_IFDIR) == _S_IFDIR)
51#endif
52#endif
53
54#include "iopreds.h"
55
56#if defined(HAVE_LIBREADLINE)
57
58#include <readline/history.h>
59#include <readline/readline.h>
60
61static int ReadlineGetc(int);
62
63static const char *history_file;
64
65#define READLINE_OUT_BUF_MAX 256
66
67static Int usable_readline(USES_REGS1) {
68#if USE_READLINE
69 if ((!(GLOBAL_Stream[StdInStream].status & Tty_Stream_f) ||
70 !(GLOBAL_Stream[StdOutStream].status & Tty_Stream_f))) {
71 return false;
72 }
73 if (getenv("PMIX_ID")) {
74 return false;
75 }
76 if (getenv("INSIDE_EMACS")) {
77 return false;
78 }
79 if (Yap_Embedded) {
80 return false;
81 }
82 return true;
83#else
84 return false;
85#endif
86}
87
88typedef struct scan_atoms {
89 Int pos;
90 Atom atom;
91} scan_atoms_t;
92
93static char *atom_enumerate(const char *prefix, int state) {
94 CACHE_REGS
95 struct scan_atoms *index;
96 Atom catom;
97 Int i;
98
99 if (!state) {
100 index = (struct scan_atoms *)malloc(sizeof(struct scan_atoms));
101 i = 0;
102 catom = NIL;
103 } else {
104 CACHE_REGS
105 index = LOCAL_search_atoms;
106 catom = index->atom;
107 i = index->pos;
108 }
109
110 while (catom != NIL || i < AtomHashTableSize) {
111 // if ( is_signalled() ) /* Notably allow windows version */
112 // PL_handle_signals(); /* to break out on ^C */
113 AtomEntry *ap;
114
115 if (catom == NIL) {
116 /* move away from current hash table line */
117 READ_LOCK(HashChain[i].AERWLock);
118 catom = HashChain[i].Entry;
119 READ_UNLOCK(HashChain[i].AERWLock);
120 i++;
121 } else {
122 ap = RepAtom(catom);
123 READ_LOCK(ap->ARWLock);
124
125 if (strstr(ap->StrOfAE, prefix) == (char *)ap->StrOfAE) {
126 index->pos = i;
127 index->atom = ap->NextOfAE;
128 LOCAL_search_atoms = index;
129 READ_UNLOCK(ap->ARWLock);
130 return ap->StrOfAE;
131 }
132 catom = ap->NextOfAE;
133 READ_UNLOCK(ap->ARWLock);
134 }
135 }
136 LOCAL_search_atoms = NULL;
137 free(index);
138 return NULL;
139}
140
141static char *atom_generator(const char *prefix, int state) {
142 char *s = atom_enumerate(prefix, state);
143
144 if (s) {
145 char *copy = malloc(1 + strlen(s));
146
147 if (copy) /* else pretend no completion */
148 strcpy(copy, s);
149 s = copy;
150 }
151
152 return s;
153}
154
155typedef struct chain {
156 struct chain *next;
157 char data[2];
158} chain_t;
159
160static char *predicate_enumerate(const char *prefix, int state, char *buf0,
161 size_t sz) {
162 CACHE_REGS
163 PredEntry *p;
164 ModEntry m0, *mod;
165 AtomEntry *ap;
166
167 if (!state) {
168 p = NULL;
169 mod = &m0;
170 m0.NextME = CurrentModules;
171 if (mod->AtomOfME == AtomIDB)
172 mod = mod->NextME;
173 } else {
174 Term cmod;
175 p = LOCAL_SearchPreds;
176 cmod = (p->ModuleOfPred != PROLOG_MODULE ? p->ModuleOfPred : TermProlog);
177 mod = Yap_GetModuleEntry(cmod);
178 }
179 while (mod) {
180 // move to next o;
181 if (p)
182 p = p->NextPredOfModule;
183 while (p == NULL) {
184 mod = mod->NextME;
185 if (!mod) {
186 // done
187 LOCAL_SearchPreds = NULL;
188 return NULL;
189 }
190 if (mod->AtomOfME == AtomIDB)
191 mod = mod->NextME;
192 p = mod->PredForME;
193 }
194
195 char *c = RepAtom(ap = NameOfPred(p))->StrOfAE;
196 if (strlen(c) > strlen(prefix) && strstr(c, prefix) == c &&
197 !(p->PredFlags & HiddenPredFlag)) {
198 LOCAL_SearchPreds = p;
199 arity_t ar = p->ArityOfPE;
200 int l, r;
201 if (Yap_IsPrefixOp(AbsAtom(ap), &l, &r) && ar == 1) {
202 return c;
203 }
204 char *buf = buf0;
205 strncpy(buf, c, sz);
206 sz -= strlen(c);
207 if (p->ArityOfPE)
208 strncat(buf0, "(", MAX_PATH);
209 return buf0;
210 }
211 }
212 LOCAL_SearchPreds = NULL;
213 return NULL;
214}
215
216static char *predicate_generator(const char *prefix, int state) {
217
218 char *buf = malloc(PATH_MAX + 1);
219 char *s = predicate_enumerate(prefix, state, buf, PATH_MAX);
220
221 if (s) {
222 char *copy = malloc(1 + strlen(s));
223
224 if (copy) /* else pretend no completion */
225 strcpy(copy, s);
226 s = copy;
227 }
228 free(buf);
229 return s;
230}
231
232static char **prolog_completion(const char *text, int start, int end) {
233 char **matches = NULL;
234 const char *pt = text + strlen(text), *pt0 = pt;
235
236 while (--pt >= text) {
237 if (isalnum(pt[0]) || pt[0] == '_')
238 continue;
239 else
240 break;
241 }
242 matches = rl_completion_matches(pt + 1, predicate_generator);
243
244 if (pt == pt0) {
245 int i = 0;
246 const char *p;
247 while (isspace(text[i++]) && i <= end)
248 ;
249 p = text + i;
250
251 if ((strstr(p, "[") == p) || (strstr(p, "compile(") == p) ||
252 (strstr(p, "consult(") == p) || (strstr(p, "load_files(") == p) ||
253 (strstr(p, "reconsult(") == p) || (strstr(p, "use_module(") == p) ||
254 (strstr(p, "cd(") == p))
255 matches = rl_completion_matches((char *)p, /* for pre-4.2 */
256 rl_filename_completion_function);
257 return matches;
258 }
259 return rl_completion_matches((char *)text, atom_generator);
260}
261
262void Yap_ReadlineFlush(int sno) {
263 if (GLOBAL_Flags && trueGlobalPrologFlag(READLINE_FLAG)) {
264 if (GLOBAL_Stream[sno].status & Tty_Stream_f &&
265 GLOBAL_Stream[sno].status & Output_Stream_f) {
266 rl_redisplay();
267 }
268 }
269}
270
271bool Yap_readline_clear_pending_input(StreamDesc *s) {
272 if (GLOBAL_Flags && trueGlobalPrologFlag(READLINE_FLAG)) {
273#if HAVE_RL_CLEAR_PENDING_INPUT
274 rl_clear_pending_input();
275#endif
276 if (s->u.irl.buf) {
277 free((void *)s->u.irl.buf);
278 }
279 s->u.irl.ptr = s->u.irl.buf = NULL;
280 return true;
281 }
282 return false;
283}
284
285bool Yap_ReadlineOps(StreamDesc *s) {
286 if (GLOBAL_Flags && trueGlobalPrologFlag(READLINE_FLAG)) {
287 if (GLOBAL_Stream[0].status & (Input_Stream_f | Tty_Stream_f) &&
288 is_same_tty(s->file, GLOBAL_Stream[0].file)) {
289 s->stream_getc = ReadlineGetc;
290 s->stream_peek = Yap_ReadlinePeekChar;
291 s->stream_wpeek = Yap_ReadlinePeekChar;
292 s->status |= Readline_Stream_f;
293 }
294 return true;
295 }
296 return false;
297}
298extern bool Yap_Embedded;
299
300bool Yap_InitReadline(Term enable) {
301 // don't call readline within emacs
302 if (enable != TermTrue || !usable_readline()) {
303 if (GLOBAL_Flags)
304 setBooleanGlobalPrologFlag(READLINE_FLAG, false);
305 return false;
306 }
307 GLOBAL_Stream[StdInStream].u.irl.buf = NULL;
308 GLOBAL_Stream[StdInStream].u.irl.ptr = NULL;
309 GLOBAL_Stream[StdInStream].status |= Readline_Stream_f;
310#if _WIN32
311 rl_instream = stdin;
312#endif
313 // rl_outstream = stderr;
314 using_history();
315 const char *s = Yap_AbsoluteFile("~/.YAP.history", true);
316 history_file = s;
317 if (read_history(s) != 0) {
318 FILE *f = fopen(s, "a");
319 if (f) {
320 fclose(f);
321 }
322 }
323 rl_readline_name = "YAP Prolog";
324 rl_attempted_completion_function = prolog_completion;
325 // rl_prep_terminal(1);
326 if (GLOBAL_Flags)
327 setBooleanGlobalPrologFlag(READLINE_FLAG, true);
328 return Yap_ReadlineOps(GLOBAL_Stream + StdInStream);
329}
330
331#if !HAVE_RL_SET_SIGNALS
332#define rl_clear_signals()
333#define rl_set_signals()
334
335#endif
336
337static bool getLine(int inp) {
338 CACHE_REGS
339 rl_instream = GLOBAL_Stream[inp].file;
340 const unsigned char *myrl_line = NULL;
341 StreamDesc *s = GLOBAL_Stream + inp;
342 Yap_set_sigaction(SIGINT,Yap_ReadlineForSIGINT);
343 LOCAL_PrologMode |= ConsoleGetcMode;
344 rl_set_signals();
345 myrl_line = (unsigned char *)readline(LOCAL_Prompt);
346 rl_clear_signals();
347 Yap_set_sigaction(SIGINT,Yap_HandleSIGINT);
348 LOCAL_PrologMode &= ~ConsoleGetcMode;
349#if HAVE_RL_PENDING_SIGNAL
350 if (rl_pending_signal()) {
351 LOCAL_PrologMode |= InterruptMode;
352 }
353#endif
354 if (LOCAL_PrologMode & InterruptMode) {
355 Yap_HandleSIGINT();
356 } else {
357 LOCAL_newline = true;
358 }
359 strncpy(LOCAL_Prompt, RepAtom(LOCAL_AtPrompt)->StrOfAE, MAX_PROMPT);
360 /* window of vulnerability closed */
361 if (myrl_line == NULL)
362 return false;
363 if (myrl_line[0] != '\0' && myrl_line[1] != '\0') {
364 add_history((char *)myrl_line);
365 history_truncate_file(history_file, 300);
366 write_history(history_file);
367 }
368 s->u.irl.ptr = s->u.irl.buf = myrl_line;
369 myrl_line = NULL;
370 LOCAL_PrologMode |= ConsoleGetcMode;
371 LOCAL_PrologMode &= ~ InterruptMode;
372 return true;
373}
374
381static int ReadlineGetc(int sno) {
382 StreamDesc *s = &GLOBAL_Stream[sno];
383 int ch = 0;
384 bool fetch = (s->u.irl.buf == NULL);
385
386 if (fetch) {
387 // readline disabled.
388 if (!GLOBAL_Flags || falseGlobalPrologFlag(READLINE_FLAG)) {
389 Yap_DefaultStreamOps(s);
390 return s->stream_getc(sno);
391 }
392
393 /* window of vulnerability opened */
394 LOCAL_PrologMode |= ConsoleGetcMode;
395 if (!getLine(sno)) {
396 return console_post_process_eof(s);
397 }
398 }
399 const unsigned char *ttyptr = s->u.irl.ptr, *myrl_line = s->u.irl.buf;
400 if (!myrl_line) ch = '\n';
401 else {
402 ch = *ttyptr;
403 if (ch == '\0') {
404 ch = '\n';
405 free((void *)myrl_line);
406 s->u.irl.ptr = s->u.irl.buf = NULL;
407 } else { s->u.irl.ptr ++;
408 }
409 }
410 return console_post_process_read_char(ch, s);
411}
412
421int Yap_ReadlinePeekChar(int sno) {
422 StreamDesc *s = &GLOBAL_Stream[sno];
423 int ch;
424
425 if (s->u.irl.buf) {
426 const unsigned char *ttyptr = s->u.irl.ptr;
427 ch = *ttyptr;
428 if (ch == '\0') {
429 ch = '\n';
430 }
431 return ch;
432 }
433 ch = 0;
434 if (getLine(sno)) {
435 CACHE_REGS
436 if (!ch)
437 ch = s->u.irl.ptr[0];
438 if (ch == '\0') {
439 ch = '\n';
440 }
441 if (ch == '\n') {
442 LOCAL_newline = true;
443 } else {
444 LOCAL_newline = false;
445 }
446 } else {
447ch = EOF;
448 }
449 return ch;
450}
451
452int Yap_ReadlineForSIGINT(void) {
453 CACHE_REGS
454 int ch;
455 StreamDesc *s = &GLOBAL_Stream[StdInStream];
456 const unsigned char *myrl_line = s->u.irl.buf;
457 if ((LOCAL_PrologMode & ConsoleGetcMode) && myrl_line != NULL) {
458 ch = myrl_line[0];
459 free((void *)myrl_line);
460 myrl_line = NULL;
461 fflush(NULL);
462 return ch;
463 } else {
464 myrl_line = (const unsigned char *)readline("Action (h for help): ");
465 if (!myrl_line) {
466 ch = EOF;
467 return ch;
468 } else {
469 ch = myrl_line[0];
470 free((void *)myrl_line);
471 myrl_line = NULL;
472 fflush(NULL);
473 return ch;
474 }
475 }
476}
477
478#else
479
480bool Yap_InitReadline(Term enable) {
481 if (GLOBAL_Flags)
482 setBooleanGlobalPrologFlag(READLINE_FLAG, false);
483 return false;
484}
485#endif
486
487static Int has_readline(USES_REGS) { return usable_readline(); }
488
489void Yap_InitReadlinePreds(void) {
490 Yap_InitCPred("$has_readline", 0, has_readline,
491 SafePredFlag | HiddenPredFlag);
492}
493
494void Yap_CloseReadline(void) {
495#if USE_READLINE
496 write_history(history_file);
497 history_truncate_file(history_file, 300);
498#endif
499}
Main definitions.
const char * Yap_AbsoluteFile(const char *spec, bool ok)
generate absolute path, if ok first expand SICStus Prolog style
Definition: absf.c:145
@ index
index
Definition: YapGFlagInfo.h:354
@ readline
readline(boolean, changeable) }
Definition: YapGFlagInfo.h:504
Module property: low-level data used to manage modes.
Definition: Yatom.h:209
struct mod_entry * NextME
Module local flags (from SWI compat)
Definition: Yatom.h:220
Definition: Yatom.h:544
int(* stream_getc)(int)
function the stream uses for writing a character
Definition: YapStreams.h:256
int(* stream_wpeek)(int)
check if the next character is available
Definition: YapStreams.h:264
int(* stream_peek)(int)
function the stream uses for parser
Definition: YapStreams.h:263