tclcompat.tcl: minor comment updates
[jimtcl.git] / jim-interactive.c
blob8a163c8a6d62964633389dd821bf93da158fae33
1 #include <errno.h>
2 #include <string.h>
4 #include "jimautoconf.h"
5 #include <jim.h>
7 #ifdef USE_LINENOISE
8 #ifdef HAVE_UNISTD_H
9 #include <unistd.h>
10 #endif
11 #ifdef HAVE_SYS_STAT_H
12 #include <sys/stat.h>
13 #endif
14 #include "linenoise.h"
15 #else
16 #define MAX_LINE_LEN 512
17 #endif
19 #ifdef USE_LINENOISE
20 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata);
21 static const char completion_callback_assoc_key[] = "interactive-completion";
22 #endif
24 /**
25 * Returns an allocated line, or NULL if EOF.
27 char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt)
29 #ifdef USE_LINENOISE
30 struct JimCompletionInfo *compinfo = Jim_GetAssocData(interp, completion_callback_assoc_key);
31 char *result;
32 /* Set any completion callback just during the call to linenoise()
33 * to allow for per-interp settings
35 if (compinfo) {
36 linenoiseSetCompletionCallback(JimCompletionCallback, compinfo);
38 result = linenoise(prompt);
39 /* unset the callback */
40 linenoiseSetCompletionCallback(NULL, NULL);
41 return result;
42 #else
43 int len;
44 char *line = malloc(MAX_LINE_LEN);
46 fputs(prompt, stdout);
47 fflush(stdout);
49 if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
50 free(line);
51 return NULL;
53 len = strlen(line);
54 if (len && line[len - 1] == '\n') {
55 line[len - 1] = '\0';
57 return line;
58 #endif
61 void Jim_HistoryLoad(const char *filename)
63 #ifdef USE_LINENOISE
64 linenoiseHistoryLoad(filename);
65 #endif
68 void Jim_HistoryAdd(const char *line)
70 #ifdef USE_LINENOISE
71 linenoiseHistoryAdd(line);
72 #endif
75 void Jim_HistorySave(const char *filename)
77 #ifdef USE_LINENOISE
78 #ifdef HAVE_UMASK
79 mode_t mask;
80 /* Just u=rw, but note that this is only effective for newly created files */
81 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
82 #endif
83 linenoiseHistorySave(filename);
84 #ifdef HAVE_UMASK
85 mask = umask(mask);
86 #endif
87 #endif
90 void Jim_HistoryShow(void)
92 #ifdef USE_LINENOISE
93 /* built-in history command */
94 int i;
95 int len;
96 char **history = linenoiseHistory(&len);
97 for (i = 0; i < len; i++) {
98 printf("%4d %s\n", i + 1, history[i]);
100 #endif
103 #ifdef USE_LINENOISE
104 struct JimCompletionInfo {
105 Jim_Interp *interp;
106 Jim_Obj *command;
109 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata)
111 struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
112 Jim_Obj *objv[2];
113 int ret;
115 objv[0] = info->command;
116 objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
118 ret = Jim_EvalObjVector(info->interp, 2, objv);
120 /* XXX: Consider how best to handle errors here. bgerror? */
121 if (ret == JIM_OK) {
122 int i;
123 Jim_Obj *listObj = Jim_GetResult(info->interp);
124 int len = Jim_ListLength(info->interp, listObj);
125 for (i = 0; i < len; i++) {
126 linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i)));
131 static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data)
133 struct JimCompletionInfo *compinfo = data;
135 Jim_DecrRefCount(interp, compinfo->command);
137 Jim_Free(compinfo);
139 #endif
142 * Sets a completion command to be used with Jim_HistoryGetline()
143 * If commandObj is NULL, deletes any existing completion command.
145 void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *commandObj)
147 #ifdef USE_LINENOISE
148 if (commandObj) {
149 /* Increment now in case the existing object is the same */
150 Jim_IncrRefCount(commandObj);
153 Jim_DeleteAssocData(interp, completion_callback_assoc_key);
155 if (commandObj) {
156 struct JimCompletionInfo *compinfo = Jim_Alloc(sizeof(*compinfo));
157 compinfo->interp = interp;
158 compinfo->command = commandObj;
160 Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo);
162 #endif
165 int Jim_InteractivePrompt(Jim_Interp *interp)
167 int retcode = JIM_OK;
168 char *history_file = NULL;
169 #ifdef USE_LINENOISE
170 const char *home;
172 home = getenv("HOME");
173 if (home && isatty(STDIN_FILENO)) {
174 int history_len = strlen(home) + sizeof("/.jim_history");
175 history_file = Jim_Alloc(history_len);
176 snprintf(history_file, history_len, "%s/.jim_history", home);
177 Jim_HistoryLoad(history_file);
180 Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1));
181 #endif
183 printf("Welcome to Jim version %d.%d\n",
184 JIM_VERSION / 100, JIM_VERSION % 100);
185 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
187 while (1) {
188 Jim_Obj *scriptObjPtr;
189 const char *result;
190 int reslen;
191 char prompt[20];
193 if (retcode != JIM_OK) {
194 const char *retcodestr = Jim_ReturnCode(retcode);
196 if (*retcodestr == '?') {
197 snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode);
199 else {
200 snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr);
203 else {
204 strcpy(prompt, ". ");
207 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
208 Jim_IncrRefCount(scriptObjPtr);
209 while (1) {
210 char state;
211 char *line;
213 line = Jim_HistoryGetline(interp, prompt);
214 if (line == NULL) {
215 if (errno == EINTR) {
216 continue;
218 Jim_DecrRefCount(interp, scriptObjPtr);
219 retcode = JIM_OK;
220 goto out;
222 if (Jim_Length(scriptObjPtr) != 0) {
223 /* Line continuation */
224 Jim_AppendString(interp, scriptObjPtr, "\n", 1);
226 Jim_AppendString(interp, scriptObjPtr, line, -1);
227 free(line);
228 if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
229 break;
231 snprintf(prompt, sizeof(prompt), "%c> ", state);
233 #ifdef USE_LINENOISE
234 if (strcmp(Jim_String(scriptObjPtr), "h") == 0) {
235 /* built-in history command */
236 Jim_HistoryShow();
237 Jim_DecrRefCount(interp, scriptObjPtr);
238 continue;
241 Jim_HistoryAdd(Jim_String(scriptObjPtr));
242 if (history_file) {
243 Jim_HistorySave(history_file);
245 #endif
246 retcode = Jim_EvalObj(interp, scriptObjPtr);
247 Jim_DecrRefCount(interp, scriptObjPtr);
249 if (retcode == JIM_EXIT) {
250 break;
252 if (retcode == JIM_ERR) {
253 Jim_MakeErrorMessage(interp);
255 result = Jim_GetString(Jim_GetResult(interp), &reslen);
256 if (reslen) {
257 printf("%s\n", result);
260 out:
261 Jim_Free(history_file);
263 return retcode;