This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/os/readline.c

482 lines
11 KiB
C
Raw Normal View History

/*************************************************************************
2017-10-11 02:24:15 +01:00
* *
* YAP Prolog *
* *
* Yap Prolog was developed at NCCUP - Universidade do Porto *
* *
* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997 *
* *
**************************************************************************
* *
* File: readline.c *
* Last rev: 5/2/88 *
* mods: *
* comments: Input/Output C implemented predicates *
* *
*************************************************************************/
2015-06-18 01:18:24 +01:00
#ifdef SCCS
static char SccsId[] = "%W% %G%";
#endif
2017-07-24 18:17:51 +01:00
/** @file readline.c
*
*
2015-11-10 14:13:56 +00:00
* This file includes the interface to the readline library, if installed in the
*system.
2015-06-18 01:18:24 +01:00
*
*/
#include "Yap.h"
#include "YapHeap.h"
2016-04-19 22:42:44 +01:00
#include "Yatom.h"
2015-06-18 01:18:24 +01:00
#include "yapio.h"
#include <stdlib.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifdef _WIN32
#if HAVE_IO_H
/* Windows */
#include <io.h>
#endif
#if HAVE_SOCKET
#include <winsock2.h>
#endif
#include <windows.h>
#ifndef S_ISDIR
2015-11-10 14:13:56 +00:00
#define S_ISDIR(x) (((x)&_S_IFDIR) == _S_IFDIR)
2015-06-18 01:18:24 +01:00
#endif
#endif
#include "iopreds.h"
2017-10-17 00:13:09 +01:00
#if defined(HAVE_LIBREADLINE)
2015-06-18 01:18:24 +01:00
#include <readline/history.h>
2016-04-19 22:42:44 +01:00
#include <readline/readline.h>
2015-06-18 01:18:24 +01:00
2015-11-10 14:13:56 +00:00
static int ReadlineGetc(int);
static const char *history_file;
2015-06-18 01:18:24 +01:00
#define READLINE_OUT_BUF_MAX 256
typedef struct scan_atoms {
Int pos;
Atom atom;
} scan_atoms_t;
2015-11-10 14:13:56 +00:00
static char *atom_enumerate(const char *prefix, int state) {
CACHE_REGS
2015-06-18 01:18:24 +01:00
struct scan_atoms *index;
2015-11-10 14:13:56 +00:00
Atom catom;
Int i;
2015-06-18 01:18:24 +01:00
2015-11-10 14:13:56 +00:00
if (!state) {
index = (struct scan_atoms *)malloc(sizeof(struct scan_atoms));
i = 0;
catom = NIL;
} else {
CACHE_REGS
index = LOCAL_search_atoms;
2015-06-18 01:18:24 +01:00
catom = index->atom;
i = index->pos;
}
while (catom != NIL || i < AtomHashTableSize) {
// if ( is_signalled() ) /* Notably allow windows version */
2015-11-10 14:13:56 +00:00
// PL_handle_signals(); /* to break out on ^C */
2015-06-18 01:18:24 +01:00
AtomEntry *ap;
if (catom == NIL) {
/* move away from current hash table line */
READ_LOCK(HashChain[i].AERWLock);
catom = HashChain[i].Entry;
READ_UNLOCK(HashChain[i].AERWLock);
i++;
} else {
ap = RepAtom(catom);
READ_LOCK(ap->ARWLock);
2015-11-10 14:13:56 +00:00
if (strstr((char *)ap->StrOfAE, prefix) == (char *)ap->StrOfAE) {
index->pos = i;
index->atom = ap->NextOfAE;
LOCAL_search_atoms = index;
READ_UNLOCK(ap->ARWLock);
return ap->StrOfAE;
2015-06-18 01:18:24 +01:00
}
catom = ap->NextOfAE;
READ_UNLOCK(ap->ARWLock);
}
}
LOCAL_search_atoms = NULL;
free(index);
return NULL;
}
2015-11-10 14:13:56 +00:00
static char *atom_generator(const char *prefix, int state) {
char *s = atom_enumerate(prefix, state);
2015-06-18 01:18:24 +01:00
2015-11-10 14:13:56 +00:00
if (s) {
char *copy = malloc(1 + strlen(s));
2015-06-18 01:18:24 +01:00
2015-11-10 14:13:56 +00:00
if (copy) /* else pretend no completion */
2015-06-18 01:18:24 +01:00
strcpy(copy, s);
s = copy;
}
return s;
}
2016-04-19 22:42:44 +01:00
typedef struct chain {
2016-04-19 23:30:02 +01:00
struct chain *next;
char data[2];
2016-04-19 22:42:44 +01:00
} chain_t;
2015-06-18 01:18:24 +01:00
2016-04-19 22:42:44 +01:00
static char *predicate_enumerate(const char *prefix, int state) {
CACHE_REGS
PredEntry *p;
ModEntry m0, *mod;
AtomEntry *ap;
2016-04-19 23:30:02 +01:00
if (!state) {
p = NULL;
mod = &m0;
m0.NextME = CurrentModules;
if (mod->AtomOfME == AtomIDB)
mod = mod->NextME;
} else {
Term cmod;
p = LOCAL_SearchPreds;
cmod = (p->ModuleOfPred != PROLOG_MODULE ? p->ModuleOfPred : TermProlog);
mod = Yap_GetModuleEntry(cmod);
}
while (mod) {
// move to next o;
if (p)
p = p->NextPredOfModule;
while (p == NULL) {
mod = mod->NextME;
if (!mod) {
// done
2016-04-19 22:42:44 +01:00
LOCAL_SearchPreds = NULL;
2016-04-19 23:30:02 +01:00
return NULL;
}
if (mod->AtomOfME == AtomIDB)
mod = mod->NextME;
p = mod->PredForME;
}
char *c = RepAtom(ap = NameOfPred(p))->StrOfAE;
if (strlen(c) > strlen(prefix) && strstr(c, prefix) == c &&
!(p->PredFlags & HiddenPredFlag)) {
LOCAL_SearchPreds = p;
arity_t ar = p->ArityOfPE;
int l, r;
if (Yap_IsPrefixOp(AbsAtom(ap), &l, &r) && ar == 1) {
return c;
}
strncpy(LOCAL_FileNameBuf, c, YAP_FILENAME_MAX);
strncat(LOCAL_FileNameBuf, "(", YAP_FILENAME_MAX);
return LOCAL_FileNameBuf;
}
}
LOCAL_SearchPreds = NULL;
return NULL;
2015-06-18 01:18:24 +01:00
}
2016-04-19 23:30:02 +01:00
static char *predicate_generator(const char *prefix, int state) {
char *s = predicate_enumerate(prefix, state);
2016-04-19 22:42:44 +01:00
2016-04-19 23:30:02 +01:00
if (s) {
char *copy = malloc(1 + strlen(s));
2016-04-19 22:42:44 +01:00
2016-04-19 23:30:02 +01:00
if (copy) /* else pretend no completion */
strcpy(copy, s);
s = copy;
2015-06-18 01:18:24 +01:00
}
2016-04-19 23:30:02 +01:00
return s;
}
2016-04-19 22:42:44 +01:00
2016-04-19 23:30:02 +01:00
static char **prolog_completion(const char *text, int start, int end) {
char **matches = NULL;
if (start == 0 && isalpha(text[0])) {
int i = 0;
while (i < end) {
if (isalnum(text[i]) || text[i] == '_')
i++;
else
break;
2016-04-19 22:42:44 +01:00
}
2016-04-19 23:30:02 +01:00
if (i == end) {
matches = rl_completion_matches((char *)text, predicate_generator);
2016-04-19 22:42:44 +01:00
}
2016-04-19 23:30:02 +01:00
return matches;
} else if (start == 0) {
int i = 0;
const char *p;
while (isspace(text[i++]) && i <= end)
2016-04-19 23:30:02 +01:00
;
p = text + i;
if ((strstr(p, "[") == p) || (strstr(p, "compile(") == p) ||
(strstr(p, "consult(") == p) || (strstr(p, "load_files(") == p) ||
(strstr(p, "reconsult(") == p) || (strstr(p, "use_module(") == p) ||
(strstr(p, "cd(") == p))
matches = rl_completion_matches((char *)text, /* for pre-4.2 */
rl_filename_completion_function);
return matches;
2015-06-18 01:18:24 +01:00
}
2016-04-19 23:30:02 +01:00
int i = end, ch = '\0';
while (i > start) {
ch = text[--i];
if (ch == '\'')
return rl_completion_matches((char *)text, /* for pre-4.2 */
rl_filename_completion_function);
if (isalnum(text[i]))
continue;
break;
}
if (islower(ch))
return rl_completion_matches((char *)text, atom_generator);
2015-06-18 01:18:24 +01:00
2016-04-19 23:30:02 +01:00
return NULL;
}
void Yap_ReadlineFlush(int sno) {
if (GLOBAL_Stream[sno].status & Tty_Stream_f &&
GLOBAL_Stream[sno].status & Output_Stream_f) {
rl_redisplay();
2015-06-18 01:18:24 +01:00
}
2016-04-19 23:30:02 +01:00
}
2015-06-18 01:18:24 +01:00
2018-01-27 10:17:27 +00:00
bool Yap_readline_clear_pending_input(StreamDesc *s) {
#if HAVE_RL_CLEAR_PENDING_INPUT
2017-03-03 11:21:29 +00:00
rl_clear_pending_input();
#endif
2017-03-03 11:21:29 +00:00
if (s->u.irl.buf) {
2017-10-11 02:24:15 +01:00
free((void *)s->u.irl.buf);
}
s->u.irl.ptr = s->u.irl.buf = NULL;
return true;
2017-03-03 11:21:29 +00:00
}
2016-04-19 23:30:02 +01:00
bool Yap_ReadlineOps(StreamDesc *s) {
if (s->status & Tty_Stream_f) {
2016-07-31 15:52:45 +01:00
if (GLOBAL_Stream[0].status & (Input_Stream_f | Tty_Stream_f) &&
2018-01-27 10:17:27 +00:00
is_same_tty(s->file, GLOBAL_Stream[0].file)) {
2016-07-31 15:52:45 +01:00
s->stream_getc = ReadlineGetc;
2018-01-27 10:17:27 +00:00
s->stream_peek = Yap_ReadlinePeekChar;
s->stream_wpeek = Yap_ReadlinePeekChar;
s->status |= Readline_Stream_f;
}
2016-04-19 23:30:02 +01:00
return true;
2016-04-19 22:42:44 +01:00
}
2016-04-19 23:30:02 +01:00
return false;
}
2016-04-19 22:42:44 +01:00
2016-04-19 23:30:02 +01:00
bool Yap_InitReadline(Term enable) {
// don't call readline within emacs
2018-06-28 12:48:29 +01:00
if (Yap_Embedded)
return false;
2016-07-31 15:52:45 +01:00
if (!(GLOBAL_Stream[StdInStream].status & Tty_Stream_f) ||
getenv("INSIDE_EMACS") || enable != TermTrue) {
if (GLOBAL_Flags)
setBooleanGlobalPrologFlag(READLINE_FLAG, false);
2016-05-19 13:34:27 +01:00
return false;
2016-07-31 15:52:45 +01:00
}
2016-04-19 23:30:02 +01:00
GLOBAL_Stream[StdInStream].u.irl.buf = NULL;
GLOBAL_Stream[StdInStream].u.irl.ptr = NULL;
GLOBAL_Stream[StdInStream].status |= Readline_Stream_f;
#if _WIN32
rl_instream = stdin;
2015-06-18 01:18:24 +01:00
#endif
2016-05-19 13:34:27 +01:00
// rl_outstream = stderr;
2016-04-19 23:30:02 +01:00
using_history();
2018-02-14 00:13:13 +00:00
const char *s = Yap_AbsoluteFile("~/.YAP.history", true);
2016-04-19 23:30:02 +01:00
history_file = s;
if (read_history(s) != 0) {
FILE *f = fopen(s, "a");
if (f) {
fclose(f);
2015-06-18 01:18:24 +01:00
}
2016-04-19 23:30:02 +01:00
}
2016-05-19 13:34:27 +01:00
rl_readline_name = "YAP Prolog";
2016-04-19 23:30:02 +01:00
rl_attempted_completion_function = prolog_completion;
2016-05-19 13:34:27 +01:00
// rl_prep_terminal(1);
2016-07-31 15:52:45 +01:00
if (GLOBAL_Flags)
setBooleanGlobalPrologFlag(READLINE_FLAG, true);
2016-04-19 23:30:02 +01:00
return Yap_ReadlineOps(GLOBAL_Stream + StdInStream);
}
2016-04-19 22:42:44 +01:00
#if !HAVE_RL_SET_SIGNALS
#define rl_clear_signals()
#define rl_set_signals()
#endif
2016-05-19 13:34:27 +01:00
static bool getLine(int inp) {
2016-04-19 23:30:02 +01:00
CACHE_REGS
rl_instream = GLOBAL_Stream[inp].file;
2016-05-19 13:34:27 +01:00
const unsigned char *myrl_line = NULL;
2016-04-19 23:30:02 +01:00
StreamDesc *s = GLOBAL_Stream + inp;
2019-05-19 09:48:13 +01:00
bool shouldPrompt = Yap_DoPrompt(s);
2016-07-31 15:52:45 +01:00
2016-04-19 23:30:02 +01:00
/* window of vulnerability opened */
LOCAL_PrologMode |= ConsoleGetcMode;
2019-05-19 09:48:13 +01:00
if (true || shouldPrompt) { // no output so far
2017-07-24 18:17:51 +01:00
rl_set_signals();
2016-04-19 23:30:02 +01:00
myrl_line = (unsigned char *)readline(LOCAL_Prompt);
2017-07-24 18:17:51 +01:00
rl_clear_signals();
2017-10-11 02:24:15 +01:00
} else {
2017-07-24 18:17:51 +01:00
rl_set_signals();
2016-04-19 23:30:02 +01:00
myrl_line = (unsigned char *)readline(NULL);
2017-07-24 18:17:51 +01:00
rl_clear_signals();
2016-04-19 23:30:02 +01:00
}
/* Do it the gnu way */
2017-07-24 18:17:51 +01:00
LOCAL_PrologMode &= ~ConsoleGetcMode;
2017-10-11 02:24:15 +01:00
#if HAVE_RL_PENDING_SIGNAL
2017-07-24 18:17:51 +01:00
if (rl_pending_signal()) {
LOCAL_PrologMode |= InterruptMode;
}
2017-10-11 02:24:15 +01:00
#endif
2016-04-19 23:30:02 +01:00
if (LOCAL_PrologMode & InterruptMode) {
2017-07-24 18:17:51 +01:00
Yap_HandleSIGINT();
2016-04-19 23:30:02 +01:00
} else {
LOCAL_newline = true;
}
strncpy(LOCAL_Prompt, RepAtom(LOCAL_AtPrompt)->StrOfAE, MAX_PROMPT);
/* window of vulnerability closed */
if (myrl_line == NULL)
return false;
if (myrl_line[0] != '\0' && myrl_line[1] != '\0') {
add_history((char *)myrl_line);
2017-10-27 13:55:34 +01:00
write_history(history_file);
2016-04-19 23:30:02 +01:00
fflush(NULL);
2015-06-18 01:18:24 +01:00
}
2016-04-19 23:30:02 +01:00
s->u.irl.ptr = s->u.irl.buf = myrl_line;
2016-05-19 13:34:27 +01:00
myrl_line = NULL;
2016-04-19 23:30:02 +01:00
return true;
}
2015-06-18 01:18:24 +01:00
2016-04-19 23:30:02 +01:00
/**
@brief reading from the console is complicated because we need to
know whether to prompt and so on...
EOF must be handled by resetting the file.
*/
static int ReadlineGetc(int sno) {
StreamDesc *s = &GLOBAL_Stream[sno];
int ch;
bool fetch = (s->u.irl.buf == NULL);
2016-05-19 13:34:27 +01:00
if (!fetch || getLine(sno)) {
2016-04-19 23:30:02 +01:00
const unsigned char *ttyptr = s->u.irl.ptr++, *myrl_line = s->u.irl.buf;
ch = *ttyptr;
if (ch == '\0') {
ch = '\n';
free((void *)myrl_line);
s->u.irl.ptr = s->u.irl.buf = NULL;
2015-06-18 01:18:24 +01:00
}
2016-04-19 23:30:02 +01:00
} else {
return EOF;
2015-06-18 01:18:24 +01:00
}
2016-04-19 23:30:02 +01:00
return console_post_process_read_char(ch, s);
}
2015-06-18 01:18:24 +01:00
2016-04-19 23:30:02 +01:00
/**
@brief Yap_ReadlinePeekChar peeks the next char from the
readline buffer, but does not actually grab it.
2015-12-15 09:14:15 +00:00
2016-04-19 23:30:02 +01:00
The idea is to take advantage of the buffering. Special care must be taken
with EOF, though.
2015-12-15 09:14:15 +00:00
2016-04-19 23:30:02 +01:00
*/
2018-01-18 14:47:27 +00:00
int Yap_ReadlinePeekChar(int sno) {
2016-04-19 23:30:02 +01:00
StreamDesc *s = &GLOBAL_Stream[sno];
int ch;
2015-12-15 09:14:15 +00:00
2016-04-19 23:30:02 +01:00
if (s->u.irl.buf) {
const unsigned char *ttyptr = s->u.irl.ptr;
ch = *ttyptr;
if (ch == '\0') {
ch = '\n';
2015-12-15 09:14:15 +00:00
}
2016-04-19 22:42:44 +01:00
return ch;
2015-12-15 09:14:15 +00:00
}
2016-05-19 13:34:27 +01:00
if (getLine(sno)) {
2016-04-19 22:42:44 +01:00
CACHE_REGS
2016-04-19 23:30:02 +01:00
ch = s->u.irl.ptr[0];
if (ch == '\0') {
ch = '\n';
}
if (ch == '\n') {
LOCAL_newline = true;
} else {
LOCAL_newline = false;
}
} else {
return EOF;
}
return ch;
}
2016-04-19 22:42:44 +01:00
2016-04-19 23:30:02 +01:00
int Yap_ReadlineForSIGINT(void) {
CACHE_REGS
int ch;
StreamDesc *s = &GLOBAL_Stream[StdInStream];
const unsigned char *myrl_line = s->u.irl.buf;
if ((LOCAL_PrologMode & ConsoleGetcMode) && myrl_line != NULL) {
ch = myrl_line[0];
free((void *)myrl_line);
myrl_line = NULL;
2016-05-16 11:21:54 +01:00
fflush(NULL);
2016-04-19 23:30:02 +01:00
return ch;
} else {
myrl_line = (const unsigned char *)readline("Action (h for help): ");
if (!myrl_line) {
ch = EOF;
return ch;
} else {
2015-06-18 01:18:24 +01:00
ch = myrl_line[0];
free((void *)myrl_line);
myrl_line = NULL;
2016-05-16 11:21:54 +01:00
fflush(NULL);
2015-06-18 01:18:24 +01:00
return ch;
}
}
2016-04-19 23:30:02 +01:00
}
2015-06-18 01:18:24 +01:00
2016-04-19 23:30:02 +01:00
static Int has_readline(USES_REGS1) {
#if USE_READLINE
2018-06-28 12:48:29 +01:00
if (!Yap_Embedded) {
2017-10-11 02:24:15 +01:00
return true;
}
return false;
2015-06-18 01:18:24 +01:00
#else
2016-04-19 23:30:02 +01:00
return false;
2015-06-18 01:18:24 +01:00
#endif
2016-04-19 23:30:02 +01:00
}
2015-06-18 01:18:24 +01:00
2016-04-19 23:30:02 +01:00
void Yap_InitReadlinePreds(void) {
Yap_InitCPred("$has_readline", 0, has_readline,
SafePredFlag | HiddenPredFlag);
}
2015-06-18 01:18:24 +01:00
#else
bool Yap_InitReadline(Term enable) {
2016-05-19 13:34:27 +01:00
return enable == TermTrue && !getenv("INSIDE_EMACS");
}
void Yap_InitReadlinePreds(void) {}
2015-06-18 01:18:24 +01:00
#endif
void Yap_CloseReadline(void) {
#if USE_READLINE
write_history(history_file);
2016-07-31 15:52:45 +01:00
history_truncate_file(history_file, 300);
#endif
}