Compare commits
17
Commits
main
...
line-editing
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0b638d78d | ||
|
|
7b4fedf5ec | ||
|
|
4666174eeb | ||
|
|
8830e6e2d5 | ||
|
|
249c13106b | ||
|
|
8bb9d9f89e | ||
|
|
804537d7a4 | ||
|
|
3375a169cb | ||
|
|
e81092befd | ||
|
|
0394d78fc8 | ||
|
|
d179c45a2b | ||
|
|
f5616a2c3d | ||
|
|
ca0c92fa39 | ||
|
|
f06f925a63 | ||
|
|
50cb7cb5b4 | ||
|
|
42167757a6 | ||
|
|
edbb278a7d |
@@ -2552,6 +2552,16 @@ declare 683 {
|
||||
int Tcl_GetEncodingNulLength(Tcl_Encoding encoding)
|
||||
}
|
||||
|
||||
# line-edit branch
|
||||
declare 684 {
|
||||
int Tcl_GetLine(char *historyPath, Tcl_DString *lineString)
|
||||
}
|
||||
|
||||
declare 685 {
|
||||
int Tcl_GetLineObj(char *historyPath, Tcl_Obj *lineObj)
|
||||
}
|
||||
|
||||
|
||||
# ----- BASELINE -- FOR -- 8.7.0 ----- #
|
||||
|
||||
##############################################################################
|
||||
|
||||
@@ -2038,6 +2038,11 @@ EXTERN int Tcl_RemoveChannelMode(Tcl_Interp *interp,
|
||||
Tcl_Channel chan, int mode);
|
||||
/* 683 */
|
||||
EXTERN int Tcl_GetEncodingNulLength(Tcl_Encoding encoding);
|
||||
/* 684 */
|
||||
EXTERN int Tcl_GetLine(char *historyPath,
|
||||
Tcl_DString *lineString);
|
||||
/* 685 */
|
||||
EXTERN int Tcl_GetLineObj(char *historyPath, Tcl_Obj *lineObj);
|
||||
|
||||
typedef struct {
|
||||
const struct TclPlatStubs *tclPlatStubs;
|
||||
@@ -2757,6 +2762,8 @@ typedef struct TclStubs {
|
||||
int (*tcl_GetNumber) (Tcl_Interp *interp, const char *bytes, size_t numBytes, void **clientDataPtr, int *typePtr); /* 681 */
|
||||
int (*tcl_RemoveChannelMode) (Tcl_Interp *interp, Tcl_Channel chan, int mode); /* 682 */
|
||||
int (*tcl_GetEncodingNulLength) (Tcl_Encoding encoding); /* 683 */
|
||||
int (*tcl_GetLine) (char *historyPath, Tcl_DString *lineString); /* 684 */
|
||||
int (*tcl_GetLineObj) (char *historyPath, Tcl_Obj *lineObj); /* 685 */
|
||||
} TclStubs;
|
||||
|
||||
extern const TclStubs *tclStubsPtr;
|
||||
@@ -4155,6 +4162,10 @@ extern const TclStubs *tclStubsPtr;
|
||||
(tclStubsPtr->tcl_RemoveChannelMode) /* 682 */
|
||||
#define Tcl_GetEncodingNulLength \
|
||||
(tclStubsPtr->tcl_GetEncodingNulLength) /* 683 */
|
||||
#define Tcl_GetLine \
|
||||
(tclStubsPtr->tcl_GetLine) /* 684 */
|
||||
#define Tcl_GetLineObj \
|
||||
(tclStubsPtr->tcl_GetLineObj) /* 685 */
|
||||
|
||||
#endif /* defined(USE_TCL_STUBS) */
|
||||
|
||||
|
||||
+128
@@ -16,6 +16,11 @@
|
||||
#include "tclIO.h"
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef USE_LINENOISE
|
||||
#include <termios.h>
|
||||
#include "linenoise.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* For each channel handler registered in a call to Tcl_CreateChannelHandler,
|
||||
* there is one record of the following type. All of records for a specific
|
||||
@@ -5050,6 +5055,129 @@ Tcl_GetsObj(
|
||||
return copiedTotal;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
*
|
||||
* Tcl_GetLine / Tcl_GetLineObj --
|
||||
*
|
||||
* These functions behave like Tcl_Gets and Tcl_GetsObj, except that
|
||||
* they always uses the stdin channel and they allow the user to access
|
||||
* command line history and do basic editing before submitting the line
|
||||
* by hitting the Enter key. They accept a path to a history file
|
||||
* (~/.tcl_history for tclsh) and a pointer to a Tcl Dynamic String or
|
||||
* String Object. The input line is appended to the string argument.
|
||||
*
|
||||
* If stdin is not a tty, or if USE_LINENOISE is not defined, they revert
|
||||
* to Tcl_Gets or Tcl_GetsObj respectively.
|
||||
*
|
||||
* Results:
|
||||
* Returns the length of the String Object after appending the line,
|
||||
* or -1 if EOF is encountered or an error occurs.
|
||||
*
|
||||
* Side effects:
|
||||
* The String Object is modified and the terminal state of the terminal
|
||||
* associated to stdin is temporarily changed to raw mode while editing
|
||||
* is taking place.
|
||||
*
|
||||
* The history file is initialized on the first call to this function,
|
||||
* and the path is ignored on subsequent calls.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifdef USE_LINENOISE
|
||||
|
||||
/*
|
||||
* Helper used by both functions.
|
||||
*/
|
||||
|
||||
static char* _GetLine(char *historyPath) {
|
||||
struct termios initial_termios;
|
||||
static int historyLoaded = 0;
|
||||
static int useHistory = 0;
|
||||
static char cachedPath[1024];
|
||||
char *line;
|
||||
if (tcgetattr(fileno(stdin), &initial_termios) == -1) {
|
||||
perror("tcgetattr:");
|
||||
Tcl_Panic("Cannot read terminal state!");
|
||||
}
|
||||
if (!historyLoaded && historyPath && historyPath[0] != '\0') {
|
||||
int noHistoryFile = linenoiseHistoryLoad(historyPath);
|
||||
historyLoaded = 1;
|
||||
if (noHistoryFile) {
|
||||
/*
|
||||
* Make sure this path is usable.
|
||||
*/
|
||||
FILE *history = fopen(historyPath, "a");
|
||||
if (history) {
|
||||
useHistory = 1;
|
||||
fclose(history);
|
||||
}
|
||||
} else {
|
||||
useHistory = 1;
|
||||
}
|
||||
if (useHistory) {
|
||||
strncpy(cachedPath, historyPath, sizeof(cachedPath)-1);
|
||||
}
|
||||
}
|
||||
line = linenoise(stdin, stdout, NULL);
|
||||
if (line && useHistory) {
|
||||
linenoiseHistoryAdd(line);
|
||||
linenoiseHistorySave(cachedPath);
|
||||
}
|
||||
tcsetattr(fileno(stdin), TCSADRAIN, &initial_termios);
|
||||
return line;
|
||||
}
|
||||
|
||||
int Tcl_GetLine(char *historyPath, Tcl_DString *lineString) {
|
||||
char *line;
|
||||
Tcl_Channel input = Tcl_GetStdChannel(TCL_STDIN);
|
||||
if (!isatty(0)) {
|
||||
/*
|
||||
* If stdin is not a tty, revert to Tcl_Gets.
|
||||
*/
|
||||
return Tcl_Gets(input, lineString);
|
||||
}
|
||||
line = _GetLine(historyPath);
|
||||
if (line) {
|
||||
Tcl_DStringAppend(lineString, line, -1);
|
||||
free(line);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return Tcl_DStringLength(lineString);
|
||||
}
|
||||
|
||||
int Tcl_GetLineObj(char *historyPath, Tcl_Obj *lineObj) {
|
||||
char *line;
|
||||
Tcl_Channel input = Tcl_GetStdChannel(TCL_STDIN);
|
||||
if (!isatty(0)) {
|
||||
/*
|
||||
* If stdin is not a tty, revert to Tcl_GetsObj.
|
||||
*/
|
||||
return Tcl_GetsObj(input, lineObj);
|
||||
}
|
||||
line = _GetLine(historyPath);
|
||||
if (line) {
|
||||
Tcl_AppendToObj(lineObj, line, -1);
|
||||
free(line);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return Tcl_GetCharLength(lineObj);
|
||||
}
|
||||
#else
|
||||
int Tcl_GetLine(char *historyPath, Tcl_DString *lineString) {
|
||||
Tcl_Channel input = Tcl_GetStdChannel(TCL_STDIN);
|
||||
(void) historyPath;
|
||||
return Tcl_Gets(input, lineString);
|
||||
}
|
||||
int Tcl_GetLineObj(char *historyPath, Tcl_Obj *lineObj) {
|
||||
Tcl_Channel input = Tcl_GetStdChannel(TCL_STDIN);
|
||||
(void) historyPath;
|
||||
return Tcl_GetsObj(input, lineObj);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------------
|
||||
*
|
||||
|
||||
+197
-18
@@ -101,7 +101,14 @@ typedef struct {
|
||||
* commands. */
|
||||
PromptType prompt; /* Next prompt to print */
|
||||
Tcl_Interp *interp; /* Interpreter that evaluates interactive
|
||||
* commands. */
|
||||
commands. */
|
||||
#ifdef USE_LINENOISE
|
||||
char *historyPath; /* Path to history file. */
|
||||
Tcl_Channel signalRead; /* Read channel for signal pipe. */
|
||||
Tcl_Channel signalWrite; /* Write channel for signal pipe. */
|
||||
ClientData signalReadFH; /* Signal pipe read file descriptor. */
|
||||
ClientData signalWriteFH; /* Signal pipe write file descriptor. */
|
||||
#endif
|
||||
} InteractiveState;
|
||||
|
||||
/*
|
||||
@@ -259,7 +266,6 @@ Tcl_SourceRCFile(
|
||||
}
|
||||
}
|
||||
#endif /* !UNICODE */
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
*
|
||||
* Tcl_MainEx --
|
||||
@@ -278,6 +284,95 @@ Tcl_SourceRCFile(
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifdef USE_LINENOISE
|
||||
#define SIGNAL_MAX 32
|
||||
static Tcl_ThreadId lineThreadId = 0;
|
||||
static int eventLoopRunning = 0;
|
||||
TCL_DECLARE_MUTEX(signalPipeMutex) /* Protects reading and writing the signal pipe. */
|
||||
TCL_DECLARE_MUTEX(promptMutex) /* Wait for a prompt before calling linenoise. */
|
||||
TCL_DECLARE_MUTEX(lineThreadMutex) /* Prevent calling Tcl_GetLineObj while the thread
|
||||
* is running. */
|
||||
/* Messages sent over the signal pipe. */
|
||||
const char *readySignal = "Ready \n";
|
||||
const char *eofSignal = "EOF\n";
|
||||
|
||||
static void sendSignal(InteractiveState *isPtr, const char *signal) {
|
||||
int size = strlen(signal);
|
||||
int written;
|
||||
Tcl_MutexLock(&signalPipeMutex);
|
||||
written = write(PTR2INT(isPtr->signalWriteFH), signal, size);
|
||||
if (written != size){
|
||||
Tcl_Panic("Write to signal pipe failed!\n");
|
||||
}
|
||||
Tcl_MutexUnlock(&signalPipeMutex);
|
||||
}
|
||||
|
||||
static void lineThreadProc(ClientData data) {
|
||||
int length;
|
||||
InteractiveState *isPtr = (InteractiveState *)data;
|
||||
Tcl_Channel output = Tcl_GetStdChannel(TCL_STDOUT);
|
||||
Tcl_DString lineString;
|
||||
Tcl_DStringInit(&lineString);
|
||||
Tcl_MutexLock(&lineThreadMutex);
|
||||
|
||||
while (1) {
|
||||
Tcl_Flush(output);
|
||||
|
||||
/*
|
||||
* Wait for the user to compose a command line.
|
||||
*/
|
||||
|
||||
Tcl_DStringSetLength(&lineString, 0);
|
||||
length = Tcl_GetLine(isPtr->historyPath, &lineString);
|
||||
if (length < 0) {
|
||||
sendSignal(isPtr, eofSignal);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Push the line onto the stdin channel with a newline added.
|
||||
*/
|
||||
|
||||
Tcl_Ungets(isPtr->input, Tcl_DStringValue(&lineString),
|
||||
Tcl_DStringLength(&lineString), 1);
|
||||
Tcl_Ungets(isPtr->input, "\n", 1, 1);
|
||||
|
||||
if (eventLoopRunning) {
|
||||
|
||||
/*
|
||||
* Send a ready signal to wake up the StdinProc. Then wait for a
|
||||
* prompt.
|
||||
*/
|
||||
|
||||
sendSignal(isPtr, readySignal);
|
||||
Tcl_MutexLock(&promptMutex);
|
||||
} else {
|
||||
|
||||
/*
|
||||
* The event loop terminated during the call to Tcl_GetLine, so
|
||||
* the channel handler will no longer exist. So we have to call
|
||||
* StdinProc directly.
|
||||
*/
|
||||
|
||||
sendSignal(isPtr, readySignal);
|
||||
StdinProc(isPtr, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
lineThreadId = 0;
|
||||
Tcl_MutexUnlock(&lineThreadMutex);
|
||||
Tcl_ExitThread(0);
|
||||
}
|
||||
|
||||
static void startLineThread(InteractiveState *isPtr) {
|
||||
if (lineThreadId == 0) {
|
||||
Tcl_CreateChannelHandler(isPtr->signalRead, TCL_READABLE, StdinProc, isPtr);
|
||||
Tcl_CreateThread(&lineThreadId, lineThreadProc, isPtr,
|
||||
TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
Tcl_MainEx(
|
||||
int argc, /* Number of arguments. */
|
||||
@@ -296,6 +391,11 @@ Tcl_MainEx(
|
||||
Tcl_Channel chan;
|
||||
InteractiveState is;
|
||||
|
||||
#ifdef USE_LINENOISE
|
||||
char *home = getenv("HOME");
|
||||
char historyPath[1024];
|
||||
#endif
|
||||
|
||||
TclpSetInitialEncodings();
|
||||
if (0 < argc) {
|
||||
--argc; /* "consume" argv[0] */
|
||||
@@ -304,13 +404,17 @@ Tcl_MainEx(
|
||||
TclpFindExecutable ((const char *)argv [0]); /* nb: this could be NULL
|
||||
* w/ (eg) an empty argv
|
||||
* supplied to execve() */
|
||||
|
||||
Tcl_InitMemory(interp);
|
||||
|
||||
is.interp = interp;
|
||||
is.prompt = PROMPT_START;
|
||||
TclNewObj(is.commandPtr);
|
||||
|
||||
#ifdef USE_LINENOISE
|
||||
Tcl_CreatePipe(interp, &is.signalRead, &is.signalWrite, 0);
|
||||
Tcl_GetChannelHandle(is.signalRead, TCL_READABLE,
|
||||
&is.signalReadFH);
|
||||
Tcl_GetChannelHandle(is.signalWrite, TCL_WRITABLE,
|
||||
&is.signalWriteFH);
|
||||
#endif
|
||||
/*
|
||||
* If the application has not already set a startup script, parse the
|
||||
* first few command line arguments to determine the script path and
|
||||
@@ -449,11 +553,30 @@ Tcl_MainEx(
|
||||
|
||||
Tcl_LinkVar(interp, "tcl_interactive", &is.tty, TCL_LINK_BOOLEAN);
|
||||
is.input = Tcl_GetStdChannel(TCL_STDIN);
|
||||
|
||||
#ifdef USE_LINENOISE
|
||||
/*
|
||||
* Set up the history file.
|
||||
*/
|
||||
|
||||
historyPath[0] = '\0';
|
||||
if (home) {
|
||||
strncpy(historyPath, home, sizeof(historyPath) - 1);
|
||||
strncat(historyPath, "/.tcl_history",
|
||||
sizeof(historyPath) - strlen(historyPath) - 1);
|
||||
}
|
||||
is.historyPath = historyPath;
|
||||
#endif
|
||||
|
||||
while ((is.input != NULL) && !Tcl_InterpDeleted(interp)) {
|
||||
mainLoopProc = TclGetMainLoop();
|
||||
if (mainLoopProc == NULL) {
|
||||
int length;
|
||||
|
||||
#ifdef USE_LINENOISE
|
||||
eventLoopRunning = 0;
|
||||
#endif
|
||||
|
||||
if (is.tty) {
|
||||
Prompt(interp, &is);
|
||||
if (Tcl_InterpDeleted(interp)) {
|
||||
@@ -472,7 +595,15 @@ Tcl_MainEx(
|
||||
is.commandPtr = Tcl_DuplicateObj(is.commandPtr);
|
||||
Tcl_IncrRefCount(is.commandPtr);
|
||||
}
|
||||
|
||||
#ifdef USE_LINENOISE
|
||||
Tcl_MutexLock(&lineThreadMutex);
|
||||
length = Tcl_GetLineObj(historyPath, is.commandPtr);
|
||||
Tcl_MutexUnlock(&lineThreadMutex);
|
||||
#else
|
||||
length = Tcl_GetsObj(is.input, is.commandPtr);
|
||||
#endif
|
||||
|
||||
if (length < 0) {
|
||||
if (Tcl_InputBlocked(is.input)) {
|
||||
/*
|
||||
@@ -482,7 +613,6 @@ Tcl_MainEx(
|
||||
* event loop running). If this causes bad CPU hogging, we
|
||||
* might try toggling the blocking on stdin instead.
|
||||
*/
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -530,6 +660,7 @@ Tcl_MainEx(
|
||||
if (chan) {
|
||||
Tcl_WriteObj(chan, Tcl_GetObjResult(interp));
|
||||
Tcl_WriteChars(chan, "\n", 1);
|
||||
Tcl_Flush(chan);
|
||||
}
|
||||
} else if (is.tty) {
|
||||
resultPtr = Tcl_GetObjResult(interp);
|
||||
@@ -539,31 +670,47 @@ Tcl_MainEx(
|
||||
if ((length > 0) && chan) {
|
||||
Tcl_WriteObj(chan, resultPtr);
|
||||
Tcl_WriteChars(chan, "\n", 1);
|
||||
Tcl_Flush(chan);
|
||||
}
|
||||
Tcl_DecrRefCount(resultPtr);
|
||||
}
|
||||
} else { /* (mainLoopProc != NULL) */
|
||||
/*
|
||||
* If a main loop has been defined while running interactively, we
|
||||
* want to start a fileevent based prompt by establishing a
|
||||
* channel handler for stdin.
|
||||
* An event loop has been created while running interactively.
|
||||
* Since Tcl_DoOneEvent processes file events, we can arrange to
|
||||
* execute commands while the event loop is running by creating a
|
||||
* channel handler that is activated when a file becomes readable.
|
||||
* Traditionally, Tcl would create a ChannelHandler that would be
|
||||
* called when stdin became readable, i.e. when it contained a
|
||||
* complete command ending in \n. But this precludes line editing.
|
||||
* If USE_LINENOISE is defined, we instead launch a thread to
|
||||
* manage the line editing, and assign a ChannelHandler to the
|
||||
* read end of a pipe. When a command line has been submitted
|
||||
* with the Enter key, the thread pushes it onto the stdin channel
|
||||
* with Tcl_Ungets and writes a message to the pipe to activate
|
||||
* the handler.
|
||||
*/
|
||||
|
||||
#ifdef USE_LINENOISE
|
||||
eventLoopRunning = 1;
|
||||
#endif
|
||||
if (is.input) {
|
||||
if (is.tty) {
|
||||
Prompt(interp, &is);
|
||||
#ifdef USE_LINENOISE
|
||||
startLineThread(&is);
|
||||
#endif
|
||||
}
|
||||
|
||||
Tcl_CreateChannelHandler(is.input, TCL_READABLE,
|
||||
StdinProc, &is);
|
||||
}
|
||||
|
||||
/* Run the event loop. */
|
||||
mainLoopProc();
|
||||
Tcl_SetMainLoop(NULL);
|
||||
|
||||
#ifdef USE_LINENOISE
|
||||
Tcl_DeleteChannelHandler(is.signalRead, StdinProc, &is);
|
||||
#else
|
||||
if (is.input) {
|
||||
Tcl_DeleteChannelHandler(is.input, StdinProc, &is);
|
||||
}
|
||||
#endif
|
||||
is.input = Tcl_GetStdChannel(TCL_STDIN);
|
||||
}
|
||||
|
||||
@@ -744,6 +891,25 @@ StdinProc(
|
||||
Tcl_Obj *commandPtr = isPtr->commandPtr;
|
||||
Tcl_Interp *interp = isPtr->interp;
|
||||
|
||||
#ifdef USE_LINENOISE
|
||||
|
||||
/*
|
||||
* We were called by the channel handler because the signal pipe became
|
||||
* readable. Drain the pipe, using the file descriptor because the pipe is
|
||||
* shared between two threads. If the message says EOF was received,
|
||||
* call Tcl_Exit. Otherwise it must be the ready message, so run the
|
||||
* command.
|
||||
*/
|
||||
|
||||
char message[SIGNAL_MAX];
|
||||
Tcl_MutexLock(&signalPipeMutex);
|
||||
length = read(PTR2INT(isPtr->signalReadFH), message, sizeof(message));
|
||||
Tcl_MutexUnlock(&signalPipeMutex);
|
||||
if (strncmp(message, eofSignal, strlen(eofSignal)) == 0) {
|
||||
Tcl_Exit(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (Tcl_IsShared(commandPtr)) {
|
||||
Tcl_DecrRefCount(commandPtr);
|
||||
commandPtr = Tcl_DuplicateObj(commandPtr);
|
||||
@@ -760,13 +926,13 @@ StdinProc(
|
||||
* evaluate [exit]? Leaving as is for now due to compatibility
|
||||
* concerns.
|
||||
*/
|
||||
|
||||
Tcl_Exit(0);
|
||||
}
|
||||
#ifndef USE_LINENOISE
|
||||
Tcl_DeleteChannelHandler(chan, StdinProc, isPtr);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (Tcl_IsShared(commandPtr)) {
|
||||
Tcl_DecrRefCount(commandPtr);
|
||||
commandPtr = Tcl_DuplicateObj(commandPtr);
|
||||
@@ -781,6 +947,7 @@ StdinProc(
|
||||
(void)Tcl_GetStringFromObj(commandPtr, &length);
|
||||
Tcl_SetObjLength(commandPtr, --length);
|
||||
|
||||
#ifndef USE_LINENOISE
|
||||
/*
|
||||
* Disable the stdin channel handler while evaluating the command;
|
||||
* otherwise if the command re-enters the event loop we might process
|
||||
@@ -789,15 +956,18 @@ StdinProc(
|
||||
*/
|
||||
|
||||
Tcl_CreateChannelHandler(chan, 0, StdinProc, isPtr);
|
||||
#endif
|
||||
code = Tcl_RecordAndEvalObj(interp, commandPtr, TCL_EVAL_GLOBAL);
|
||||
isPtr->input = chan = Tcl_GetStdChannel(TCL_STDIN);
|
||||
Tcl_DecrRefCount(commandPtr);
|
||||
TclNewObj(commandPtr);
|
||||
isPtr->commandPtr = commandPtr;
|
||||
Tcl_IncrRefCount(commandPtr);
|
||||
#ifndef USE_LINENOISE
|
||||
if (chan != NULL) {
|
||||
Tcl_CreateChannelHandler(chan, TCL_READABLE, StdinProc, isPtr);
|
||||
}
|
||||
#endif
|
||||
if (code != TCL_OK) {
|
||||
chan = Tcl_GetStdChannel(TCL_STDERR);
|
||||
|
||||
@@ -814,6 +984,7 @@ StdinProc(
|
||||
if ((length > 0) && (chan != NULL)) {
|
||||
Tcl_WriteObj(chan, resultPtr);
|
||||
Tcl_WriteChars(chan, "\n", 1);
|
||||
Tcl_Flush(chan);
|
||||
}
|
||||
Tcl_DecrRefCount(resultPtr);
|
||||
}
|
||||
@@ -827,6 +998,7 @@ StdinProc(
|
||||
Prompt(interp, isPtr);
|
||||
isPtr->input = Tcl_GetStdChannel(TCL_STDIN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -859,7 +1031,6 @@ Prompt(
|
||||
if (isPtr->prompt == PROMPT_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
promptCmdPtr = Tcl_GetVar2Ex(interp,
|
||||
(isPtr->prompt==PROMPT_CONTINUE ? "tcl_prompt2" : "tcl_prompt1"),
|
||||
NULL, TCL_GLOBAL_ONLY);
|
||||
@@ -874,6 +1045,7 @@ Prompt(
|
||||
if (chan != NULL) {
|
||||
Tcl_WriteChars(chan, DEFAULT_PRIMARY_PROMPT,
|
||||
sizeof(DEFAULT_PRIMARY_PROMPT) - 1);
|
||||
Tcl_Flush(chan);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -885,6 +1057,7 @@ Prompt(
|
||||
if (chan != NULL) {
|
||||
Tcl_WriteObj(chan, Tcl_GetObjResult(interp));
|
||||
Tcl_WriteChars(chan, "\n", 1);
|
||||
Tcl_Flush(chan);
|
||||
}
|
||||
goto defaultPrompt;
|
||||
}
|
||||
@@ -895,6 +1068,12 @@ Prompt(
|
||||
Tcl_Flush(chan);
|
||||
}
|
||||
isPtr->prompt = PROMPT_NONE;
|
||||
#ifdef USE_LINENOISE
|
||||
if (promptMutex) {
|
||||
Tcl_MutexUnlock(&promptMutex);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -2050,6 +2050,8 @@ const TclStubs tclStubs = {
|
||||
Tcl_GetNumber, /* 681 */
|
||||
Tcl_RemoveChannelMode, /* 682 */
|
||||
Tcl_GetEncodingNulLength, /* 683 */
|
||||
Tcl_GetLine, /* 684 */
|
||||
Tcl_GetLineObj, /* 685 */
|
||||
};
|
||||
|
||||
/* !END!: Do not edit above this line. */
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,247 @@
|
||||
# Linenoise
|
||||
|
||||
A minimal, zero-config, BSD licensed, readline replacement used in Redis,
|
||||
MongoDB, and Android.
|
||||
|
||||
* Single and multi line editing mode with the usual key bindings implemented.
|
||||
* History handling.
|
||||
* Completion.
|
||||
* Hints (suggestions at the right of the prompt as you type).
|
||||
* About 1,100 lines of BSD license source code.
|
||||
* Only uses a subset of VT100 escapes (ANSI.SYS compatible).
|
||||
|
||||
## Can a line editing library be 20k lines of code?
|
||||
|
||||
Line editing with some support for history is a really important feature for command line utilities. Instead of retyping almost the same stuff again and again it's just much better to hit the up arrow and edit on syntax errors, or in order to try a slightly different command. But apparently code dealing with terminals is some sort of Black Magic: readline is 30k lines of code, libedit 20k. Is it reasonable to link small utilities to huge libraries just to get a minimal support for line editing?
|
||||
|
||||
So what usually happens is either:
|
||||
|
||||
* Large programs with configure scripts disabling line editing if readline is not present in the system, or not supporting it at all since readline is GPL licensed and libedit (the BSD clone) is not as known and available as readline is (Real world example of this problem: Tclsh).
|
||||
* Smaller programs not using a configure script not supporting line editing at all (A problem we had with Redis-cli for instance).
|
||||
|
||||
The result is a pollution of binaries without line editing support.
|
||||
|
||||
So I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporting line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to Linenoise if not.
|
||||
|
||||
## Terminals, in 2010.
|
||||
|
||||
Apparently almost every terminal you can happen to use today has some kind of support for basic VT100 escape sequences. So I tried to write a lib using just very basic VT100 features. The resulting library appears to work everywhere I tried to use it, and now can work even on ANSI.SYS compatible terminals, since no
|
||||
VT220 specific sequences are used anymore.
|
||||
|
||||
The library is currently about 1100 lines of code. In order to use it in your project just look at the *example.c* file in the source distribution, it is trivial. Linenoise is BSD code, so you can use both in free software and commercial software.
|
||||
|
||||
## Tested with...
|
||||
|
||||
* Linux text only console ($TERM = linux)
|
||||
* Linux KDE terminal application ($TERM = xterm)
|
||||
* Linux xterm ($TERM = xterm)
|
||||
* Linux Buildroot ($TERM = vt100)
|
||||
* Mac OS X iTerm ($TERM = xterm)
|
||||
* Mac OS X default Terminal.app ($TERM = xterm)
|
||||
* OpenBSD 4.5 through an OSX Terminal.app ($TERM = screen)
|
||||
* IBM AIX 6.1
|
||||
* FreeBSD xterm ($TERM = xterm)
|
||||
* ANSI.SYS
|
||||
* Emacs comint mode ($TERM = dumb)
|
||||
|
||||
Please test it everywhere you can and report back!
|
||||
|
||||
## Let's push this forward!
|
||||
|
||||
Patches should be provided in the respect of Linenoise sensibility for small
|
||||
easy to understand code.
|
||||
|
||||
Send feedbacks to antirez at gmail
|
||||
|
||||
# The API
|
||||
|
||||
Linenoise is very easy to use, and reading the example shipped with the
|
||||
library should get you up to speed ASAP. Here is a list of API calls
|
||||
and how to use them.
|
||||
|
||||
char *linenoise(const char *prompt);
|
||||
|
||||
This is the main Linenoise call: it shows the user a prompt with line editing
|
||||
and history capabilities. The prompt you specify is used as a prompt, that is,
|
||||
it will be printed to the left of the cursor. The library returns a buffer
|
||||
with the line composed by the user, or NULL on end of file or when there
|
||||
is an out of memory condition.
|
||||
|
||||
When a tty is detected (the user is actually typing into a terminal session)
|
||||
the maximum editable line length is `LINENOISE_MAX_LINE`. When instead the
|
||||
standard input is not a tty, which happens every time you redirect a file
|
||||
to a program, or use it in an Unix pipeline, there are no limits to the
|
||||
length of the line that can be returned.
|
||||
|
||||
The returned line should be freed with the `free()` standard system call.
|
||||
However sometimes it could happen that your program uses a different dynamic
|
||||
allocation library, so you may also used `linenoiseFree` to make sure the
|
||||
line is freed with the same allocator it was created.
|
||||
|
||||
The canonical loop used by a program using Linenoise will be something like
|
||||
this:
|
||||
|
||||
while((line = linenoise("hello> ")) != NULL) {
|
||||
printf("You wrote: %s\n", line);
|
||||
linenoiseFree(line); /* Or just free(line) if you use libc malloc. */
|
||||
}
|
||||
|
||||
## Single line VS multi line editing
|
||||
|
||||
By default, Linenoise uses single line editing, that is, a single row on the
|
||||
screen will be used, and as the user types more, the text will scroll towards
|
||||
left to make room. This works if your program is one where the user is
|
||||
unlikely to write a lot of text, otherwise multi line editing, where multiple
|
||||
screens rows are used, can be a lot more comfortable.
|
||||
|
||||
In order to enable multi line editing use the following API call:
|
||||
|
||||
linenoiseSetMultiLine(1);
|
||||
|
||||
You can disable it using `0` as argument.
|
||||
|
||||
## History
|
||||
|
||||
Linenoise supporst history, so that the user does not have to retype
|
||||
again and again the same things, but can use the down and up arrows in order
|
||||
to search and re-edit already inserted lines of text.
|
||||
|
||||
The followings are the history API calls:
|
||||
|
||||
int linenoiseHistoryAdd(const char *line);
|
||||
int linenoiseHistorySetMaxLen(int len);
|
||||
int linenoiseHistorySave(const char *filename);
|
||||
int linenoiseHistoryLoad(const char *filename);
|
||||
|
||||
Use `linenoiseHistoryAdd` every time you want to add a new element
|
||||
to the top of the history (it will be the first the user will see when
|
||||
using the up arrow).
|
||||
|
||||
Note that for history to work, you have to set a length for the history
|
||||
(which is zero by default, so history will be disabled if you don't set
|
||||
a proper one). This is accomplished using the `linenoiseHistorySetMaxLen`
|
||||
function.
|
||||
|
||||
Linenoise has direct support for persisting the history into an history
|
||||
file. The functions `linenoiseHistorySave` and `linenoiseHistoryLoad` do
|
||||
just that. Both functions return -1 on error and 0 on success.
|
||||
|
||||
## Mask mode
|
||||
|
||||
Sometimes it is useful to allow the user to type passwords or other
|
||||
secrets that should not be displayed. For such situations linenoise supports
|
||||
a "mask mode" that will just replace the characters the user is typing
|
||||
with `*` characters, like in the following example:
|
||||
|
||||
$ ./linenoise_example
|
||||
hello> get mykey
|
||||
echo: 'get mykey'
|
||||
hello> /mask
|
||||
hello> *********
|
||||
|
||||
You can enable and disable mask mode using the following two functions:
|
||||
|
||||
void linenoiseMaskModeEnable(void);
|
||||
void linenoiseMaskModeDisable(void);
|
||||
|
||||
## Completion
|
||||
|
||||
Linenoise supports completion, which is the ability to complete the user
|
||||
input when she or he presses the `<TAB>` key.
|
||||
|
||||
In order to use completion, you need to register a completion callback, which
|
||||
is called every time the user presses `<TAB>`. Your callback will return a
|
||||
list of items that are completions for the current string.
|
||||
|
||||
The following is an example of registering a completion callback:
|
||||
|
||||
linenoiseSetCompletionCallback(completion);
|
||||
|
||||
The completion must be a function returning `void` and getting as input
|
||||
a `const char` pointer, which is the line the user has typed so far, and
|
||||
a `linenoiseCompletions` object pointer, which is used as argument of
|
||||
`linenoiseAddCompletion` in order to add completions inside the callback.
|
||||
An example will make it more clear:
|
||||
|
||||
void completion(const char *buf, linenoiseCompletions *lc) {
|
||||
if (buf[0] == 'h') {
|
||||
linenoiseAddCompletion(lc,"hello");
|
||||
linenoiseAddCompletion(lc,"hello there");
|
||||
}
|
||||
}
|
||||
|
||||
Basically in your completion callback, you inspect the input, and return
|
||||
a list of items that are good completions by using `linenoiseAddCompletion`.
|
||||
|
||||
If you want to test the completion feature, compile the example program
|
||||
with `make`, run it, type `h` and press `<TAB>`.
|
||||
|
||||
## Hints
|
||||
|
||||
Linenoise has a feature called *hints* which is very useful when you
|
||||
use Linenoise in order to implement a REPL (Read Eval Print Loop) for
|
||||
a program that accepts commands and arguments, but may also be useful in
|
||||
other conditions.
|
||||
|
||||
The feature shows, on the right of the cursor, as the user types, hints that
|
||||
may be useful. The hints can be displayed using a different color compared
|
||||
to the color the user is typing, and can also be bold.
|
||||
|
||||
For example as the user starts to type `"git remote add"`, with hints it's
|
||||
possible to show on the right of the prompt a string `<name> <url>`.
|
||||
|
||||
The feature works similarly to the history feature, using a callback.
|
||||
To register the callback we use:
|
||||
|
||||
linenoiseSetHintsCallback(hints);
|
||||
|
||||
The callback itself is implemented like this:
|
||||
|
||||
char *hints(const char *buf, int *color, int *bold) {
|
||||
if (!strcasecmp(buf,"git remote add")) {
|
||||
*color = 35;
|
||||
*bold = 0;
|
||||
return " <name> <url>";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
The callback function returns the string that should be displayed or NULL
|
||||
if no hint is available for the text the user currently typed. The returned
|
||||
string will be trimmed as needed depending on the number of columns available
|
||||
on the screen.
|
||||
|
||||
It is possible to return a string allocated in dynamic way, by also registering
|
||||
a function to deallocate the hint string once used:
|
||||
|
||||
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
|
||||
|
||||
The free hint callback will just receive the pointer and free the string
|
||||
as needed (depending on how the hits callback allocated it).
|
||||
|
||||
As you can see in the example above, a `color` (in xterm color terminal codes)
|
||||
can be provided together with a `bold` attribute. If no color is set, the
|
||||
current terminal foreground color is used. If no bold attribute is set,
|
||||
non-bold text is printed.
|
||||
|
||||
Color codes are:
|
||||
|
||||
red = 31
|
||||
green = 32
|
||||
yellow = 33
|
||||
blue = 34
|
||||
magenta = 35
|
||||
cyan = 36
|
||||
white = 37;
|
||||
|
||||
## Screen handling
|
||||
|
||||
Sometimes you may want to clear the screen as a result of something the
|
||||
user typed. You can do this by calling the following function:
|
||||
|
||||
void linenoiseClearScreen(void);
|
||||
|
||||
## Related projects
|
||||
|
||||
* [Linenoise NG](https://github.com/arangodb/linenoise-ng) is a fork of Linenoise that aims to add more advanced features like UTF-8 support, Windows support and other features. Uses C++ instead of C as development language.
|
||||
* [Linenoise-swift](https://github.com/andybest/linenoise-swift) is a reimplementation of Linenoise written in Swift.
|
||||
@@ -0,0 +1,79 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "linenoise.h"
|
||||
|
||||
|
||||
void completion(const char *buf, linenoiseCompletions *lc) {
|
||||
if (buf[0] == 'h') {
|
||||
linenoiseAddCompletion(lc,"hello");
|
||||
linenoiseAddCompletion(lc,"hello there");
|
||||
}
|
||||
}
|
||||
|
||||
char *hints(const char *buf, int *color, int *bold) {
|
||||
if (!strcasecmp(buf,"hello")) {
|
||||
*color = 35;
|
||||
*bold = 0;
|
||||
return " World";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *line;
|
||||
char *prgname = argv[0];
|
||||
|
||||
/* Parse options, with --multiline we enable multi line editing. */
|
||||
while(argc > 1) {
|
||||
argc--;
|
||||
argv++;
|
||||
if (!strcmp(*argv,"--multiline")) {
|
||||
linenoiseSetMultiLine(1);
|
||||
printf("Multi-line mode enabled.\n");
|
||||
} else if (!strcmp(*argv,"--keycodes")) {
|
||||
linenoisePrintKeyCodes();
|
||||
exit(0);
|
||||
} else {
|
||||
fprintf(stderr, "Usage: %s [--multiline] [--keycodes]\n", prgname);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the completion callback. This will be called every time the
|
||||
* user uses the <tab> key. */
|
||||
linenoiseSetCompletionCallback(completion);
|
||||
linenoiseSetHintsCallback(hints);
|
||||
|
||||
/* Load history from file. The history file is just a plain text file
|
||||
* where entries are separated by newlines. */
|
||||
linenoiseHistoryLoad("history.txt"); /* Load the history at startup */
|
||||
|
||||
/* Now this is the main loop of the typical linenoise-based application.
|
||||
* The call to linenoise() will block as long as the user types something
|
||||
* and presses enter.
|
||||
*
|
||||
* The typed string is returned as a malloc() allocated string by
|
||||
* linenoise, so the user needs to free() it. */
|
||||
|
||||
while((line = linenoise("hello> ")) != NULL) {
|
||||
/* Do something with the string. */
|
||||
if (line[0] != '\0' && line[0] != '/') {
|
||||
printf("echo: '%s'\n", line);
|
||||
linenoiseHistoryAdd(line); /* Add to the history. */
|
||||
linenoiseHistorySave("history.txt"); /* Save the history on disk. */
|
||||
} else if (!strncmp(line,"/historylen",11)) {
|
||||
/* The "/historylen" command will change the history len. */
|
||||
int len = atoi(line+11);
|
||||
linenoiseHistorySetMaxLen(len);
|
||||
} else if (!strncmp(line, "/mask", 5)) {
|
||||
linenoiseMaskModeEnable();
|
||||
} else if (!strncmp(line, "/unmask", 7)) {
|
||||
linenoiseMaskModeDisable();
|
||||
} else if (line[0] == '/') {
|
||||
printf("Unreconized command: %s\n", line);
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
||||
/* linenoise.h -- VERSION 1.0
|
||||
*
|
||||
* Guerrilla line editing library against the idea that a line editing lib
|
||||
* needs to be 20,000 lines of C code.
|
||||
*
|
||||
* See linenoise.c for more information.
|
||||
*
|
||||
* ------------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __LINENOISE_H
|
||||
#define __LINENOISE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct linenoiseCompletions {
|
||||
size_t len;
|
||||
char **cvec;
|
||||
} linenoiseCompletions;
|
||||
|
||||
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
|
||||
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
|
||||
typedef void(linenoiseFreeHintsCallback)(void *);
|
||||
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
|
||||
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
|
||||
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
|
||||
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
|
||||
char *linenoise(FILE *input, FILE *output, const char *prompt);
|
||||
void linenoiseFree(void *ptr);
|
||||
int linenoiseHistoryAdd(const char *line);
|
||||
int linenoiseHistorySetMaxLen(int len);
|
||||
int linenoiseHistorySave(const char *filename);
|
||||
int linenoiseHistoryLoad(const char *filename);
|
||||
void linenoiseSetMultiLine(int ml);
|
||||
void linenoisePrintKeyCodes(void);
|
||||
void linenoiseMaskModeEnable(void);
|
||||
void linenoiseMaskModeDisable(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __LINENOISE_H */
|
||||
+19
-9
@@ -241,6 +241,7 @@ ZLIB_DIR = ${COMPAT_DIR}/zlib
|
||||
ZLIB_INCLUDE = @ZLIB_INCLUDE@
|
||||
TOMMATH_DIR = $(TOP_DIR)/libtommath
|
||||
TOMMATH_INCLUDE = @TOMMATH_INCLUDE@
|
||||
LINENOISE_DIR = $(TOP_DIR)/linenoise
|
||||
|
||||
CC = @CC@
|
||||
OBJEXT = @OBJEXT@
|
||||
@@ -276,10 +277,11 @@ VALGRINDARGS = --tool=memcheck --num-callers=24 \
|
||||
# modify it and you shouldn't need to modify it either.
|
||||
#--------------------------------------------------------------------------
|
||||
|
||||
STUB_CC_SWITCHES = -I"${BUILD_DIR}" -I${UNIX_DIR} -I${GENERIC_DIR} -I${TOMMATH_DIR} \
|
||||
${CFLAGS} ${CFLAGS_WARNING} ${SHLIB_CFLAGS} \
|
||||
${AC_FLAGS} ${ENV_FLAGS} ${EXTRA_CFLAGS} @EXTRA_CC_SWITCHES@ \
|
||||
${NO_DEPRECATED_FLAGS} -DMP_FIXED_CUTOFFS -DMP_NO_STDINT
|
||||
STUB_CC_SWITCHES = -I"${BUILD_DIR}" -I${UNIX_DIR} -I${GENERIC_DIR} \
|
||||
-I${TOMMATH_DIR} -I${LINENOISE_DIR} ${CFLAGS} ${CFLAGS_WARNING} \
|
||||
${SHLIB_CFLAGS} ${AC_FLAGS} ${ENV_FLAGS} ${EXTRA_CFLAGS} \
|
||||
@EXTRA_CC_SWITCHES@ ${NO_DEPRECATED_FLAGS} -DMP_FIXED_CUTOFFS \
|
||||
-DMP_NO_STDINT
|
||||
|
||||
CC_SWITCHES = $(STUB_CC_SWITCHES) -DBUILD_tcl
|
||||
|
||||
@@ -346,6 +348,8 @@ TOMMATH_OBJS = bn_s_mp_reverse.o bn_s_mp_mul_digs_fast.o \
|
||||
bn_mp_ubin_size.o bn_mp_xor.o bn_mp_zero.o bn_s_mp_add.o \
|
||||
bn_s_mp_mul_digs.o bn_s_mp_sqr.o bn_s_mp_sub.o
|
||||
|
||||
LINENOISE_OBJS = linenoise.o
|
||||
|
||||
STUB_LIB_OBJS = tclStubLib.o \
|
||||
tclTomMathStubLib.o \
|
||||
tclOOStubLib.o \
|
||||
@@ -370,7 +374,7 @@ ZLIB_OBJS = Zadler32.o Zcompress.o Zcrc32.o Zdeflate.o Zinfback.o \
|
||||
TCL_OBJS = ${GENERIC_OBJS} ${UNIX_OBJS} ${NOTIFY_OBJS} ${COMPAT_OBJS} \
|
||||
${OO_OBJS} @DL_OBJS@ @PLAT_OBJS@
|
||||
|
||||
OBJS = ${TCL_OBJS} @DTRACE_OBJ@ @ZLIB_OBJS@ @TOMMATH_OBJS@
|
||||
OBJS = ${TCL_OBJS} ${LINENOISE_OBJS} @DTRACE_OBJ@ @ZLIB_OBJS@ @TOMMATH_OBJS@
|
||||
|
||||
TCL_DECLS = \
|
||||
$(GENERIC_DIR)/tcl.decls \
|
||||
@@ -653,6 +657,8 @@ TOMMATH_SRCS = \
|
||||
$(TOMMATH_DIR)/bn_s_mp_toom_mul.c \
|
||||
$(TOMMATH_DIR)/bn_s_mp_toom_sqr.c
|
||||
|
||||
LINENOISE_SRCS = $(LINENOISE_DIR)/linenoise.c
|
||||
|
||||
UNIX_HDRS = \
|
||||
$(UNIX_DIR)/tclUnixPort.h
|
||||
# $(UNIX_DIR)/tclConfig.h
|
||||
@@ -716,8 +722,8 @@ ZLIB_SRCS = \
|
||||
# won't compile on the current machine, and they will cause problems for
|
||||
# things like "make depend".
|
||||
|
||||
SRCS = $(GENERIC_SRCS) $(UNIX_SRCS) $(NOTIFY_SRCS) \
|
||||
$(OO_SRCS) $(STUB_SRCS) @PLAT_SRCS@ @ZLIB_SRCS@ @TOMMATH_SRCS@
|
||||
SRCS = $(GENERIC_SRCS) $(UNIX_SRCS) $(NOTIFY_SRCS) $(OO_SRCS) $(STUB_SRCS) \
|
||||
$(LINENOISE_SRCS) @PLAT_SRCS@ @ZLIB_SRCS@ @TOMMATH_SRCS@
|
||||
|
||||
###
|
||||
# Tip 430 - ZipFS Modifications
|
||||
@@ -824,7 +830,7 @@ tclLibObjs:
|
||||
objs: ${OBJS}
|
||||
|
||||
${TCL_EXE}: ${TCLSH_OBJS} ${TCL_LIB_FILE} ${TCL_STUB_LIB_FILE} ${TCL_ZIP_FILE}
|
||||
${CC} ${CFLAGS} ${LDFLAGS} ${TCLSH_OBJS} \
|
||||
${CC} ${CFLAGS} ${LDFLAGS} ${TCLSH_OBJS} ${LINENOISE_OBJS}\
|
||||
@TCL_BUILD_LIB_SPEC@ ${TCL_STUB_LIB_FILE} ${LIBS} @EXTRA_TCLSH_LIBS@ \
|
||||
${CC_SEARCH_FLAGS} -o ${TCL_EXE}
|
||||
@if test "${ZIPFS_BUILD}" = "2" ; then \
|
||||
@@ -1804,6 +1810,10 @@ tclUnixInit.o: $(UNIX_DIR)/tclUnixInit.c tclConfig.sh
|
||||
tclUnixCompat.o: $(UNIX_DIR)/tclUnixCompat.c
|
||||
$(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tclUnixCompat.c
|
||||
|
||||
# Linenoise
|
||||
linenoise.o: $(LINENOISE_DIR)/linenoise.c
|
||||
$(CC) -c $(CC_SWITCHES) $(LINENOISE_DIR)/linenoise.c
|
||||
|
||||
# The following are Mac OS X only sources:
|
||||
tclMacOSXBundle.o: $(MAC_OSX_DIR)/tclMacOSXBundle.c
|
||||
$(CC) -c $(CC_SWITCHES) $(MAC_OSX_DIR)/tclMacOSXBundle.c
|
||||
@@ -1820,7 +1830,7 @@ tclWinError.o: $(TOP_DIR)/win/tclWinError.c
|
||||
|
||||
# DTrace support
|
||||
|
||||
$(TCL_OBJS) $(STUB_LIB_OBJS) $(TCLSH_OBJS) $(TCLTEST_OBJS) $(XTTEST_OBJS) $(TOMMATH_OBJS): @DTRACE_HDR@
|
||||
$(TCL_OBJS) $(STUB_LIB_OBJS) $(TCLSH_OBJS) $(TCLTEST_OBJS) $(XTTEST_OBJS) $(TOMMATH_OBJS) $(LINENOISE_OBJS): @DTRACE_HDR@
|
||||
|
||||
$(DTRACE_HDR): $(DTRACE_SRC)
|
||||
$(DTRACE) -h $(DTRACE_SWITCHES) -o $@ -s $(DTRACE_SRC)
|
||||
|
||||
@@ -699,6 +699,13 @@ typedef int socklen_t;
|
||||
# define AF_INET6 10
|
||||
#endif
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------------
|
||||
* Add support for comand line editing and history in tclsh via linenoise
|
||||
*---------------------------------------------------------------------------
|
||||
*/
|
||||
#define USE_LINENOISE
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------------
|
||||
* Set of MT-safe implementations of some known-to-be-MT-unsafe library calls.
|
||||
|
||||
+9
-5
@@ -82,7 +82,7 @@ CFLAGS_OPTIMIZE = @CFLAGS_OPTIMIZE@
|
||||
#CFLAGS = $(CFLAGS_DEBUG)
|
||||
#CFLAGS = $(CFLAGS_OPTIMIZE)
|
||||
#CFLAGS = $(CFLAGS_DEBUG) $(CFLAGS_OPTIMIZE)
|
||||
CFLAGS = @CFLAGS@ @CFLAGS_DEFAULT@ -D__USE_MINGW_ANSI_STDIO=0 -DMP_FIXED_CUTOFFS -DMP_NO_STDINT
|
||||
CFLAGS = @CFLAGS@ @CFLAGS_DEFAULT@ -D__USE_MINGW_ANSI_STDIO=0 -DMP_FIXED_CUTOFFS -DMP_NO_STDINT -DUSE_LINENOISE
|
||||
|
||||
# To compile without backward compatibility and deprecated code uncomment the
|
||||
# following
|
||||
@@ -106,6 +106,7 @@ PKGS_DIR = $(TOP_DIR)/pkgs
|
||||
ZLIB_DIR = $(COMPAT_DIR)/zlib
|
||||
MINIZIP_DIR = $(ZLIB_DIR)/contrib/minizip
|
||||
TOMMATH_DIR = $(TOP_DIR)/libtommath
|
||||
LINENOISE_DIR = $(TOP_DIR)/linenoise
|
||||
|
||||
# Converts a POSIX path to a Windows native path.
|
||||
CYGPATH = @CYGPATH@
|
||||
@@ -125,6 +126,7 @@ ROOT_DIR_WIN_NATIVE = $(shell cd '$(ROOT_DIR)' ; pwd -W 2>/dev/null || pwd -P)
|
||||
ZLIB_DIR_NATIVE = $(shell $(CYGPATH) '$(ZLIB_DIR)')
|
||||
MINIZIP_DIR_NATIVE = $(shell $(CYGPATH) '$(MINIZIP_DIR)')
|
||||
TOMMATH_DIR_NATIVE = $(shell $(CYGPATH) '$(TOMMATH_DIR)')
|
||||
LINENOISE_DIR_NATIVE = $(shell $(CYGPATH) '$(LINENOISE_DIR)')
|
||||
|
||||
# Fully qualify library path so that `make test`
|
||||
# does not depend on the current directory.
|
||||
@@ -183,7 +185,7 @@ TCL_EXE = @TCL_EXE@
|
||||
# Setting the VPATH variable to a list of paths will cause the Makefile to
|
||||
# look into these paths when resolving .c to .obj dependencies.
|
||||
|
||||
VPATH = $(GENERIC_DIR):$(WIN_DIR):$(COMPAT_DIR):$(ZLIB_DIR):$(TOMMATH_DIR)
|
||||
VPATH = $(GENERIC_DIR):$(WIN_DIR):$(COMPAT_DIR):$(ZLIB_DIR):$(TOMMATH_DIR):$(LINENOISE_DIR)
|
||||
|
||||
AR = @AR@
|
||||
RANLIB = @RANLIB@
|
||||
@@ -253,7 +255,7 @@ MINIZIP_OBJS = \
|
||||
ZIP_INSTALL_OBJS = @ZIP_INSTALL_OBJS@
|
||||
|
||||
CC_SWITCHES = -I"${BUILD_DIR}" -I"${GENERIC_DIR_NATIVE}" -I"${TOMMATH_DIR_NATIVE}" \
|
||||
-I"${ZLIB_DIR_NATIVE}" -I"${WIN_DIR_NATIVE}" \
|
||||
-I"${LINENOISE_DIR_NATIVE}" -I"${ZLIB_DIR_NATIVE}" -I"${WIN_DIR_NATIVE}" \
|
||||
${CFLAGS} ${CFLAGS_WARNING} ${SHLIB_CFLAGS} -DMP_PREC=4 \
|
||||
${AC_FLAGS} ${COMPILE_DEBUG_FLAGS} ${NO_DEPRECATED_FLAGS}
|
||||
|
||||
@@ -261,7 +263,7 @@ CC_OBJNAME = @CC_OBJNAME@
|
||||
CC_EXENAME = @CC_EXENAME@
|
||||
|
||||
STUB_CC_SWITCHES = -I"${GENERIC_DIR_NATIVE}" -I"${TOMMATH_DIR_NATIVE}" \
|
||||
-I"${ZLIB_DIR_NATIVE}" -I"${WIN_DIR_NATIVE}" \
|
||||
-I"${LINENOISE_DIR_NATIVE}" -I"${ZLIB_DIR_NATIVE}" -I"${WIN_DIR_NATIVE}" \
|
||||
${CFLAGS} ${CFLAGS_WARNING} ${SHLIB_CFLAGS} -DMP_PREC=4 \
|
||||
${AC_FLAGS} ${COMPILE_DEBUG_FLAGS}
|
||||
|
||||
@@ -432,6 +434,8 @@ TOMMATH_OBJS = \
|
||||
bn_s_mp_toom_mul.${OBJEXT} \
|
||||
bn_s_mp_toom_sqr.${OBJEXT}
|
||||
|
||||
LINENOISE_OBJS = \
|
||||
linenoise.${OBJEXT} \
|
||||
|
||||
WIN_OBJS = \
|
||||
tclWin32Dll.$(OBJEXT) \
|
||||
@@ -474,7 +478,7 @@ ZLIB_OBJS = \
|
||||
uncompr.$(OBJEXT) \
|
||||
zutil.$(OBJEXT)
|
||||
|
||||
TCL_OBJS = ${GENERIC_OBJS} ${WIN_OBJS} @ZLIB_OBJS@ @TOMMATH_OBJS@
|
||||
TCL_OBJS = ${GENERIC_OBJS} ${WIN_OBJS} ${LINENOISE_OBJS} @ZLIB_OBJS@ @TOMMATH_OBJS@
|
||||
|
||||
TCL_DOCS = "$(ROOT_DIR_NATIVE)"/doc/*.[13n]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user