jim.c: Jim_Length/Jim_String internal checks
[jimtcl.git] / jim-interactive.c
blob1d6eb00c64eedc930e9b7912c4231de663ff294a
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 Jim_Obj *objPtr;
33 long mlmode = 0;
34 /* Set any completion callback just during the call to linenoise()
35 * to allow for per-interp settings
37 if (compinfo) {
38 linenoiseSetCompletionCallback(JimCompletionCallback, compinfo);
40 objPtr = Jim_GetVariableStr(interp, "history::multiline", JIM_NONE);
41 if (objPtr && Jim_GetLong(interp, objPtr, &mlmode) == JIM_NONE) {
42 linenoiseSetMultiLine(mlmode);
45 result = linenoise(prompt);
46 /* unset the callback */
47 linenoiseSetCompletionCallback(NULL, NULL);
48 return result;
49 #else
50 int len;
51 char *line = malloc(MAX_LINE_LEN);
53 fputs(prompt, stdout);
54 fflush(stdout);
56 if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
57 free(line);
58 return NULL;
60 len = strlen(line);
61 if (len && line[len - 1] == '\n') {
62 line[len - 1] = '\0';
64 return line;
65 #endif
68 void Jim_HistoryLoad(const char *filename)
70 #ifdef USE_LINENOISE
71 linenoiseHistoryLoad(filename);
72 #endif
75 void Jim_HistoryAdd(const char *line)
77 #ifdef USE_LINENOISE
78 linenoiseHistoryAdd(line);
79 #endif
82 void Jim_HistorySave(const char *filename)
84 #ifdef USE_LINENOISE
85 #ifdef HAVE_UMASK
86 mode_t mask;
87 /* Just u=rw, but note that this is only effective for newly created files */
88 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
89 #endif
90 linenoiseHistorySave(filename);
91 #ifdef HAVE_UMASK
92 mask = umask(mask);
93 #endif
94 #endif
97 void Jim_HistoryShow(void)
99 #ifdef USE_LINENOISE
100 /* built-in history command */
101 int i;
102 int len;
103 char **history = linenoiseHistory(&len);
104 for (i = 0; i < len; i++) {
105 printf("%4d %s\n", i + 1, history[i]);
107 #endif
110 #ifdef USE_LINENOISE
111 struct JimCompletionInfo {
112 Jim_Interp *interp;
113 Jim_Obj *command;
116 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata)
118 struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
119 Jim_Obj *objv[2];
120 int ret;
122 objv[0] = info->command;
123 objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
125 ret = Jim_EvalObjVector(info->interp, 2, objv);
127 /* XXX: Consider how best to handle errors here. bgerror? */
128 if (ret == JIM_OK) {
129 int i;
130 Jim_Obj *listObj = Jim_GetResult(info->interp);
131 int len = Jim_ListLength(info->interp, listObj);
132 for (i = 0; i < len; i++) {
133 linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i)));
138 static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data)
140 struct JimCompletionInfo *compinfo = data;
142 Jim_DecrRefCount(interp, compinfo->command);
144 Jim_Free(compinfo);
146 #endif
149 * Sets a completion command to be used with Jim_HistoryGetline()
150 * If commandObj is NULL, deletes any existing completion command.
152 void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *commandObj)
154 #ifdef USE_LINENOISE
155 if (commandObj) {
156 /* Increment now in case the existing object is the same */
157 Jim_IncrRefCount(commandObj);
160 Jim_DeleteAssocData(interp, completion_callback_assoc_key);
162 if (commandObj) {
163 struct JimCompletionInfo *compinfo = Jim_Alloc(sizeof(*compinfo));
164 compinfo->interp = interp;
165 compinfo->command = commandObj;
167 Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo);
169 #endif
172 int Jim_InteractivePrompt(Jim_Interp *interp)
174 int retcode = JIM_OK;
175 char *history_file = NULL;
176 #ifdef USE_LINENOISE
177 const char *home;
179 home = getenv("HOME");
180 if (home && isatty(STDIN_FILENO)) {
181 int history_len = strlen(home) + sizeof("/.jim_history");
182 history_file = Jim_Alloc(history_len);
183 snprintf(history_file, history_len, "%s/.jim_history", home);
184 Jim_HistoryLoad(history_file);
187 Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1));
188 #endif
190 printf("Welcome to Jim version %d.%d\n",
191 JIM_VERSION / 100, JIM_VERSION % 100);
192 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
194 while (1) {
195 Jim_Obj *scriptObjPtr;
196 const char *result;
197 int reslen;
198 char prompt[20];
200 if (retcode != JIM_OK) {
201 const char *retcodestr = Jim_ReturnCode(retcode);
203 if (*retcodestr == '?') {
204 snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode);
206 else {
207 snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr);
210 else {
211 strcpy(prompt, ". ");
214 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
215 Jim_IncrRefCount(scriptObjPtr);
216 while (1) {
217 char state;
218 char *line;
220 line = Jim_HistoryGetline(interp, prompt);
221 if (line == NULL) {
222 if (errno == EINTR) {
223 continue;
225 Jim_DecrRefCount(interp, scriptObjPtr);
226 retcode = JIM_OK;
227 goto out;
229 if (Jim_Length(scriptObjPtr) != 0) {
230 /* Line continuation */
231 Jim_AppendString(interp, scriptObjPtr, "\n", 1);
233 Jim_AppendString(interp, scriptObjPtr, line, -1);
234 free(line);
235 if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
236 break;
238 snprintf(prompt, sizeof(prompt), "%c> ", state);
240 #ifdef USE_LINENOISE
241 if (strcmp(Jim_String(scriptObjPtr), "h") == 0) {
242 /* built-in history command */
243 Jim_HistoryShow();
244 Jim_DecrRefCount(interp, scriptObjPtr);
245 continue;
248 Jim_HistoryAdd(Jim_String(scriptObjPtr));
249 if (history_file) {
250 Jim_HistorySave(history_file);
252 #endif
253 retcode = Jim_EvalObj(interp, scriptObjPtr);
254 Jim_DecrRefCount(interp, scriptObjPtr);
256 if (retcode == JIM_EXIT) {
257 break;
259 if (retcode == JIM_ERR) {
260 Jim_MakeErrorMessage(interp);
262 result = Jim_GetString(Jim_GetResult(interp), &reslen);
263 if (reslen) {
264 printf("%s\n", result);
267 out:
268 Jim_Free(history_file);
270 return retcode;