build: On NetBSD backtrace is in -lexecinfo
[jimtcl.git] / jim-interactive.c
blobd263e8375accd4f1652930f24979be40968b508c
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 /**
20 * Returns an allocated line, or NULL if EOF.
22 char *Jim_HistoryGetline(const char *prompt)
24 #ifdef USE_LINENOISE
25 return linenoise(prompt);
26 #else
27 int len;
28 char *line = malloc(MAX_LINE_LEN);
30 fputs(prompt, stdout);
31 fflush(stdout);
33 if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
34 free(line);
35 return NULL;
37 len = strlen(line);
38 if (len && line[len - 1] == '\n') {
39 line[len - 1] = '\0';
41 return line;
42 #endif
45 void Jim_HistoryLoad(const char *filename)
47 #ifdef USE_LINENOISE
48 linenoiseHistoryLoad(filename);
49 #endif
52 void Jim_HistoryAdd(const char *line)
54 #ifdef USE_LINENOISE
55 linenoiseHistoryAdd(line);
56 #endif
59 void Jim_HistorySave(const char *filename)
61 #ifdef USE_LINENOISE
62 #ifdef HAVE_UMASK
63 mode_t mask;
64 /* Just u=rw, but note that this is only effective for newly created files */
65 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
66 #endif
67 linenoiseHistorySave(filename);
68 #ifdef HAVE_UMASK
69 mask = umask(mask);
70 #endif
71 #endif
74 void Jim_HistoryShow(void)
76 #ifdef USE_LINENOISE
77 /* built-in history command */
78 int i;
79 int len;
80 char **history = linenoiseHistory(&len);
81 for (i = 0; i < len; i++) {
82 printf("%4d %s\n", i + 1, history[i]);
84 #endif
87 #ifdef USE_LINENOISE
88 struct JimCompletionInfo {
89 Jim_Interp *interp;
90 Jim_Obj *command;
93 void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata)
95 struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
96 Jim_Obj *objv[2];
98 objv[0] = info->command;
99 objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
101 int ret = Jim_EvalObjVector(info->interp, 2, objv);
103 /* XXX: Consider how best to handle errors here. bgerror? */
104 if (ret == JIM_OK) {
105 int i;
106 Jim_Obj *listObj = Jim_GetResult(info->interp);
107 int len = Jim_ListLength(info->interp, listObj);
108 for (i = 0; i < len; i++) {
109 linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i)));
113 #endif
115 int Jim_InteractivePrompt(Jim_Interp *interp)
117 int retcode = JIM_OK;
118 char *history_file = NULL;
119 #ifdef USE_LINENOISE
120 const char *home;
121 struct JimCompletionInfo compinfo;
123 home = getenv("HOME");
124 if (home && isatty(STDIN_FILENO)) {
125 int history_len = strlen(home) + sizeof("/.jim_history");
126 history_file = Jim_Alloc(history_len);
127 snprintf(history_file, history_len, "%s/.jim_history", home);
128 Jim_HistoryLoad(history_file);
131 compinfo.interp = interp;
132 compinfo.command = Jim_NewStringObj(interp, "tcl::autocomplete", -1);
133 Jim_IncrRefCount(compinfo.command);
135 /* Register a callback function for tab-completion. */
136 linenoiseSetCompletionCallback(JimCompletionCallback, &compinfo);
137 #endif
139 printf("Welcome to Jim version %d.%d\n",
140 JIM_VERSION / 100, JIM_VERSION % 100);
141 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
143 while (1) {
144 Jim_Obj *scriptObjPtr;
145 const char *result;
146 int reslen;
147 char prompt[20];
149 if (retcode != JIM_OK) {
150 const char *retcodestr = Jim_ReturnCode(retcode);
152 if (*retcodestr == '?') {
153 snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode);
155 else {
156 snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr);
159 else {
160 strcpy(prompt, ". ");
163 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
164 Jim_IncrRefCount(scriptObjPtr);
165 while (1) {
166 char state;
167 char *line;
169 line = Jim_HistoryGetline(prompt);
170 if (line == NULL) {
171 if (errno == EINTR) {
172 continue;
174 Jim_DecrRefCount(interp, scriptObjPtr);
175 retcode = JIM_OK;
176 goto out;
178 if (Jim_Length(scriptObjPtr) != 0) {
179 /* Line continuation */
180 Jim_AppendString(interp, scriptObjPtr, "\n", 1);
182 Jim_AppendString(interp, scriptObjPtr, line, -1);
183 free(line);
184 if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
185 break;
187 snprintf(prompt, sizeof(prompt), "%c> ", state);
189 #ifdef USE_LINENOISE
190 if (strcmp(Jim_String(scriptObjPtr), "h") == 0) {
191 /* built-in history command */
192 Jim_HistoryShow();
193 Jim_DecrRefCount(interp, scriptObjPtr);
194 continue;
197 Jim_HistoryAdd(Jim_String(scriptObjPtr));
198 if (history_file) {
199 Jim_HistorySave(history_file);
201 #endif
202 retcode = Jim_EvalObj(interp, scriptObjPtr);
203 Jim_DecrRefCount(interp, scriptObjPtr);
205 if (retcode == JIM_EXIT) {
206 break;
208 if (retcode == JIM_ERR) {
209 Jim_MakeErrorMessage(interp);
211 result = Jim_GetString(Jim_GetResult(interp), &reslen);
212 if (reslen) {
213 printf("%s\n", result);
216 out:
217 Jim_Free(history_file);
219 #ifdef USE_LINENOISE
220 Jim_DecrRefCount(interp, compinfo.command);
221 linenoiseSetCompletionCallback(NULL, NULL);
222 #endif
224 return retcode;