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

395 lines
9.3 KiB
C
Raw Normal View History

/*************************************************************************
2015-06-18 01:18:24 +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 *
* *
**************************************************************************
* *
2015-11-10 14:13:56 +00:00
* File: readline.c *
2015-06-18 01:18:24 +01:00
* Last rev: 5/2/88 *
* mods: *
* comments: Input/Output C implemented predicates *
* *
*************************************************************************/
#ifdef SCCS
static char SccsId[] = "%W% %G%";
#endif
/*
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 "Yatom.h"
#include "YapHeap.h"
#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"
#if USE_READLINE
2015-06-18 01:18:24 +01:00
#include <readline/readline.h>
#include <readline/history.h>
2015-11-10 14:13:56 +00:00
static int ReadlineGetc(int);
static int ReadlinePutc(int, 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;
}
2015-11-10 14:13:56 +00:00
static char **prolog_completion(const char *text, int start, int end) {
char **matches = NULL;
2015-06-18 01:18:24 +01:00
2015-11-10 14:13:56 +00:00
if ((start == 1 && rl_line_buffer[0] == '[') || /* [file */
(start == 2 && strncmp(rl_line_buffer, "['", 2)))
matches = rl_completion_matches((char *)text, /* for pre-4.2 */
rl_filename_completion_function);
2015-06-18 01:18:24 +01:00
else
2015-11-10 14:13:56 +00:00
matches = rl_completion_matches((char *)text, atom_generator);
2015-06-18 01:18:24 +01:00
return matches;
}
2015-11-10 14:13:56 +00:00
void Yap_ReadlineFlush(int sno) {
if (GLOBAL_Stream[sno].status & Tty_Stream_f &&
2015-06-18 01:18:24 +01:00
GLOBAL_Stream[sno].status & Output_Stream_f) {
2015-11-10 14:13:56 +00:00
rl_redisplay();
2015-06-18 01:18:24 +01:00
}
}
2015-11-10 14:13:56 +00:00
bool Yap_ReadlinePrompt(StreamDesc *s) {
2015-06-18 01:18:24 +01:00
if (s->status & Tty_Stream_f) {
s->stream_getc = ReadlineGetc;
if (GLOBAL_Stream[0].status & Tty_Stream_f &&
2015-11-10 14:13:56 +00:00
s->name == GLOBAL_Stream[0].name)
2015-06-18 01:18:24 +01:00
s->stream_putc = ReadlinePutc;
return true;
}
return false;
}
2015-11-10 14:13:56 +00:00
bool Yap_ReadlineOps(StreamDesc *s) {
2015-06-18 01:18:24 +01:00
if (s->status & Tty_Stream_f) {
if (GLOBAL_Stream[0].status & Tty_Stream_f &&
2015-11-10 14:13:56 +00:00
is_same_tty(s->file, GLOBAL_Stream[0].file))
2015-06-18 01:18:24 +01:00
s->stream_putc = ReadlinePutc;
s->stream_getc = ReadlineGetc;
s->status |= Readline_Stream_f;
2015-06-18 01:18:24 +01:00
return true;
}
return false;
}
2015-11-10 14:13:56 +00:00
static int prolog_complete(int ignore, int key) {
if (rl_point > 0 && rl_line_buffer[rl_point - 1] != ' ') {
#if HAVE_DECL_RL_CATCH_SIGNALS_ /* actually version >= 1.2, or true readline \
*/
rl_begin_undo_group();
2015-06-18 01:18:24 +01:00
rl_complete(ignore, key);
2015-11-10 14:13:56 +00:00
if (rl_point > 0 && rl_line_buffer[rl_point - 1] == ' ') {
rl_delete_text(rl_point - 1, rl_point);
2015-06-18 01:18:24 +01:00
rl_point -= 1;
rl_delete(-1, key);
}
rl_end_undo_group();
#endif
} else
rl_complete(ignore, key);
return 0;
}
bool Yap_InitReadline(Term enable) {
2015-06-18 01:18:24 +01:00
// don't call readline within emacs
2015-11-10 14:13:56 +00:00
// if (getenv("ËMACS"))
2015-07-06 12:03:16 +01:00
// return;
if (enable == TermFalse)
return true;
2015-06-18 01:18:24 +01:00
GLOBAL_Stream[StdInStream].u.irl.buf = NULL;
GLOBAL_Stream[StdInStream].u.irl.ptr = NULL;
GLOBAL_Stream[StdInStream].status |= Readline_Stream_f;
2015-06-18 01:18:24 +01:00
#if _MSC_VER || defined(__MINGW32__)
rl_instream = stdin;
#endif
rl_outstream = stderr;
using_history();
2016-04-10 14:21:17 +01:00
const char *s = Yap_AbsoluteFile("~/.YAP.history", NULL, true);
2015-11-10 14:13:56 +00:00
if (!read_history(s)) {
FILE *f = fopen(s, "w");
if (f) {
fclose(f);
read_history(s);
2015-06-18 01:18:24 +01:00
}
2015-11-10 14:13:56 +00:00
}
2015-06-18 01:18:24 +01:00
rl_readline_name = "Prolog";
rl_attempted_completion_function = prolog_completion;
#ifdef HAVE_RL_COMPLETION_FUNC_T
rl_add_defun("prolog-complete", prolog_complete, '\t');
#else
rl_add_defun("prolog-complete", (void *)prolog_complete, '\t');
#endif
return Yap_ReadlineOps(GLOBAL_Stream + StdInStream);
2015-06-18 01:18:24 +01:00
}
2015-11-10 14:13:56 +00:00
static bool getLine(int inp, int out) {
CACHE_REGS
2015-06-18 01:18:24 +01:00
rl_instream = GLOBAL_Stream[inp].file;
rl_outstream = GLOBAL_Stream[out].file;
2016-02-18 12:10:58 +00:00
const unsigned char *myrl_line;
2015-11-10 14:13:56 +00:00
StreamDesc *s = GLOBAL_Stream + inp;
2015-06-18 01:18:24 +01:00
if (!(s->status & Tty_Stream_f))
return false;
/* window of vulnerability opened */
fflush(NULL);
LOCAL_PrologMode |= ConsoleGetcMode;
if (LOCAL_newline) { // no output so far
2016-02-18 12:10:58 +00:00
myrl_line = (unsigned char *)readline(LOCAL_Prompt);
2015-06-18 01:18:24 +01:00
} else {
2016-02-18 12:10:58 +00:00
myrl_line = (unsigned char *)readline(NULL);
2015-06-18 01:18:24 +01:00
}
/* Do it the gnu way */
if (LOCAL_PrologMode & InterruptMode) {
2015-11-10 14:13:56 +00:00
Yap_external_signal(0, YAP_INT_SIGNAL);
2015-06-18 01:18:24 +01:00
LOCAL_PrologMode &= ~ConsoleGetcMode;
if (LOCAL_PrologMode & AbortMode) {
2015-09-25 10:57:26 +01:00
Yap_Error(ABORT_EVENT, TermNil, "");
2015-06-18 01:18:24 +01:00
LOCAL_ErrorMessage = "Abort";
return console_post_process_eof(s);
}
} else {
LOCAL_PrologMode &= ~ConsoleGetcMode;
LOCAL_newline = true;
2015-06-18 01:18:24 +01:00
}
2015-11-10 14:13:56 +00:00
strncpy(LOCAL_Prompt, RepAtom(LOCAL_AtPrompt)->StrOfAE, MAX_PROMPT);
2015-06-18 01:18:24 +01:00
/* window of vulnerability closed */
if (myrl_line == NULL)
return false;
if (myrl_line[0] != '\0' && myrl_line[1] != '\0') {
2016-02-18 12:10:58 +00:00
add_history((char *)myrl_line);
2015-11-10 14:13:56 +00:00
append_history(1, history_file);
2015-06-18 01:18:24 +01:00
}
2015-11-10 14:13:56 +00:00
s->u.irl.ptr = s->u.irl.buf = myrl_line;
return true;
}
2015-06-18 01:18:24 +01:00
2015-11-10 14:13:56 +00:00
static int ReadlinePutc(int sno, int ch) {
2016-02-22 13:01:09 +00:00
CACHE_REGS
2015-06-18 01:18:24 +01:00
StreamDesc *s = &GLOBAL_Stream[sno];
#if MAC || _MSC_VER || defined(__MINGW32__)
2015-11-10 14:13:56 +00:00
if (ch == 10) {
putc('\n', s->file);
} else
2015-06-18 01:18:24 +01:00
#endif
2015-11-10 14:13:56 +00:00
putc(ch, s->file);
console_count_output_char(ch, s);
if (ch == 10) {
Yap_ReadlineFlush(sno);
LOCAL_newline = true;
2015-06-18 01:18:24 +01:00
}
2015-11-10 14:13:56 +00:00
return ((int)ch);
2015-06-18 01:18:24 +01:00
}
/**
@brief reading from the console is complicated because we need to
2015-06-18 01:18:24 +01:00
know whether to prompt and so on...
EOF must be handled by resetting the file.
2015-06-18 01:18:24 +01:00
*/
2015-11-10 14:13:56 +00:00
static int ReadlineGetc(int sno) {
2015-06-18 01:18:24 +01:00
StreamDesc *s = &GLOBAL_Stream[sno];
int ch;
bool fetch = (s->u.irl.buf == NULL);
2015-11-10 14:13:56 +00:00
if (!fetch || getLine(sno, StdErrStream)) {
2016-02-18 12:10:58 +00:00
const unsigned char *ttyptr = s->u.irl.ptr++, *myrl_line = s->u.irl.buf;
2015-06-18 01:18:24 +01:00
ch = *ttyptr;
if (ch == '\0') {
ch = '\n';
2015-11-10 14:13:56 +00:00
free((void *)myrl_line);
s->u.irl.ptr = s->u.irl.buf = NULL;
2015-06-18 01:18:24 +01:00
}
} else {
return EOF;
}
return console_post_process_read_char(ch, s);
}
/**
2015-12-15 09:14:15 +00:00
@brief Yap_ReadlinePeekChar peeks the next char from the
readline buffer, but does not actually grab it.
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
*/
Int Yap_ReadlinePeekChar(int sno) {
2015-12-15 09:14:15 +00:00
StreamDesc *s = &GLOBAL_Stream[sno];
int ch;
if (s->u.irl.buf) {
2016-02-18 12:10:58 +00:00
const unsigned char *ttyptr = s->u.irl.ptr;
2015-12-15 09:14:15 +00:00
ch = *ttyptr;
if (ch == '\0') {
ch = '\n';
2015-12-15 09:14:15 +00:00
}
2016-02-14 04:14:31 +00:00
return ch;
}
if (getLine(sno, StdErrStream)) {
CACHE_REGS
2015-12-15 09:14:15 +00:00
ch = s->u.irl.ptr[0];
if (ch == '\0') {
ch = '\n';
2015-12-15 09:14:15 +00:00
}
if (ch == '\n') {
LOCAL_newline = true;
} else {
LOCAL_newline = false;
}
} else {
return EOF;
}
return ch;
}
2015-11-10 14:13:56 +00:00
int Yap_ReadlineForSIGINT(void) {
CACHE_REGS
2015-06-18 01:18:24 +01:00
int ch;
StreamDesc *s = &GLOBAL_Stream[StdInStream];
2016-02-18 12:10:58 +00:00
const unsigned char *myrl_line = s->u.irl.buf;
2015-06-18 01:18:24 +01:00
2016-02-18 12:10:58 +00:00
if ((LOCAL_PrologMode & ConsoleGetcMode) && myrl_line != NULL) {
2015-06-18 01:18:24 +01:00
ch = myrl_line[0];
free((void *)myrl_line);
myrl_line = NULL;
return ch;
} else {
2016-02-18 12:10:58 +00:00
myrl_line = (const unsigned char *)readline("Action (h for help): ");
2015-06-18 01:18:24 +01:00
if (!myrl_line) {
ch = EOF;
return ch;
} else {
ch = myrl_line[0];
free((void *)myrl_line);
myrl_line = NULL;
return ch;
}
}
}
2015-11-10 14:13:56 +00:00
static Int has_readline(USES_REGS1) {
2015-11-09 18:22:43 +00:00
#if USE_READLINE
2015-06-18 01:18:24 +01:00
return true;
#else
return false;
#endif
}
void Yap_InitReadlinePreds(void) {
2015-11-10 14:13:56 +00:00
Yap_InitCPred("$has_readline", 0, has_readline,
SafePredFlag | HiddenPredFlag);
2015-06-18 01:18:24 +01:00
}
#else
bool Yap_InitReadline(Term enable) {
if (enable == TermTrue)
return true;
return false;
}
void Yap_InitReadlinePreds(void) {}
2015-06-18 01:18:24 +01:00
#endif