1 #include <errno.h>
2 #include <string.h>
3
4 #include "jimautoconf.h"
5 #include <jim.h>
6
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
18
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
23
24 /**
25 * Returns an allocated line, or NULL if EOF.
26 */
Jim_HistoryGetline(Jim_Interp * interp,const char * prompt)27 char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt)
28 {
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
36 */
37 if (compinfo) {
38 linenoiseSetCompletionCallback(JimCompletionCallback, compinfo);
39 }
40 objPtr = Jim_GetVariableStr(interp, "history::multiline", JIM_NONE);
41 if (objPtr && Jim_GetLong(interp, objPtr, &mlmode) == JIM_NONE) {
42 linenoiseSetMultiLine(mlmode);
43 }
44
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);
52
53 fputs(prompt, stdout);
54 fflush(stdout);
55
56 if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
57 free(line);
58 return NULL;
59 }
60 len = strlen(line);
61 if (len && line[len - 1] == '\n') {
62 line[len - 1] = '\0';
63 }
64 return line;
65 #endif
66 }
67
Jim_HistoryLoad(const char * filename)68 void Jim_HistoryLoad(const char *filename)
69 {
70 #ifdef USE_LINENOISE
71 linenoiseHistoryLoad(filename);
72 #endif
73 }
74
Jim_HistoryAdd(const char * line)75 void Jim_HistoryAdd(const char *line)
76 {
77 #ifdef USE_LINENOISE
78 linenoiseHistoryAdd(line);
79 #endif
80 }
81
Jim_HistorySave(const char * filename)82 void Jim_HistorySave(const char *filename)
83 {
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 umask(mask);
93 #endif
94 #endif
95 }
96
Jim_HistoryShow(void)97 void Jim_HistoryShow(void)
98 {
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]);
106 }
107 #endif
108 }
109
110 #ifdef USE_LINENOISE
111 struct JimCompletionInfo {
112 Jim_Interp *interp;
113 Jim_Obj *command;
114 };
115
JimCompletionCallback(const char * prefix,linenoiseCompletions * comp,void * userdata)116 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata)
117 {
118 struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
119 Jim_Obj *objv[2];
120 int ret;
121
122 objv[0] = info->command;
123 objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
124
125 ret = Jim_EvalObjVector(info->interp, 2, objv);
126
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)));
134 }
135 }
136 }
137
JimHistoryFreeCompletion(Jim_Interp * interp,void * data)138 static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data)
139 {
140 struct JimCompletionInfo *compinfo = data;
141
142 Jim_DecrRefCount(interp, compinfo->command);
143
144 Jim_Free(compinfo);
145 }
146 #endif
147
148 /**
149 * Sets a completion command to be used with Jim_HistoryGetline()
150 * If commandObj is NULL, deletes any existing completion command.
151 */
Jim_HistorySetCompletion(Jim_Interp * interp,Jim_Obj * commandObj)152 void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *commandObj)
153 {
154 #ifdef USE_LINENOISE
155 if (commandObj) {
156 /* Increment now in case the existing object is the same */
157 Jim_IncrRefCount(commandObj);
158 }
159
160 Jim_DeleteAssocData(interp, completion_callback_assoc_key);
161
162 if (commandObj) {
163 struct JimCompletionInfo *compinfo = Jim_Alloc(sizeof(*compinfo));
164 compinfo->interp = interp;
165 compinfo->command = commandObj;
166
167 Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo);
168 }
169 #endif
170 }
171
Jim_InteractivePrompt(Jim_Interp * interp)172 int Jim_InteractivePrompt(Jim_Interp *interp)
173 {
174 int retcode = JIM_OK;
175 char *history_file = NULL;
176 #ifdef USE_LINENOISE
177 const char *home;
178
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);
185 }
186
187 Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1));
188 #endif
189
190 printf("Welcome to Jim version %d.%d\n",
191 JIM_VERSION / 100, JIM_VERSION % 100);
192 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
193
194 while (1) {
195 Jim_Obj *scriptObjPtr;
196 const char *result;
197 int reslen;
198 char prompt[20];
199
200 if (retcode != JIM_OK) {
201 const char *retcodestr = Jim_ReturnCode(retcode);
202
203 if (*retcodestr == '?') {
204 snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode);
205 }
206 else {
207 snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr);
208 }
209 }
210 else {
211 strcpy(prompt, ". ");
212 }
213
214 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
215 Jim_IncrRefCount(scriptObjPtr);
216 while (1) {
217 char state;
218 char *line;
219
220 line = Jim_HistoryGetline(interp, prompt);
221 if (line == NULL) {
222 if (errno == EINTR) {
223 continue;
224 }
225 Jim_DecrRefCount(interp, scriptObjPtr);
226 retcode = JIM_OK;
227 goto out;
228 }
229 if (Jim_Length(scriptObjPtr) != 0) {
230 /* Line continuation */
231 Jim_AppendString(interp, scriptObjPtr, "\n", 1);
232 }
233 Jim_AppendString(interp, scriptObjPtr, line, -1);
234 free(line);
235 if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
236 break;
237
238 snprintf(prompt, sizeof(prompt), "%c> ", state);
239 }
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;
246 }
247
248 Jim_HistoryAdd(Jim_String(scriptObjPtr));
249 if (history_file) {
250 Jim_HistorySave(history_file);
251 }
252 #endif
253 retcode = Jim_EvalObj(interp, scriptObjPtr);
254 Jim_DecrRefCount(interp, scriptObjPtr);
255
256 if (retcode == JIM_EXIT) {
257 break;
258 }
259 if (retcode == JIM_ERR) {
260 Jim_MakeErrorMessage(interp);
261 }
262 result = Jim_GetString(Jim_GetResult(interp), &reslen);
263 if (reslen) {
264 printf("%s\n", result);
265 }
266 }
267 out:
268 Jim_Free(history_file);
269
270 return retcode;
271 }
272