00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include <stdlib.h>
00017 #include "../sexpress/sexpress.hpp"
00018 #include "../tools/sreader.hpp"
00019 #include "../tools/sstream.hpp"
00020
00021 #ifndef INTELIB_USE_READLINE
00022 #define INTELIB_USE_READLINE 1
00023 #endif
00024
00025 #if INTELIB_USE_READLINE == 1
00026 #include <readline/readline.h>
00027 #include <readline/history.h>
00028 #include <string.h>
00029 #include <signal.h>
00030 #include <setjmp.h>
00031
00032 #include <sys/types.h>
00033 #include <sys/stat.h>
00034 #include <unistd.h>
00035
00036 #include "../sexpress/shashtbl.hpp"
00037 #endif
00038
00039 static SReference plain_read(const SStreamRef &in,
00040 const SStreamRef &out,
00041 const SStreamRef &err,
00042 IntelibGenericReader &reader,
00043 const SString &prompt)
00044 {
00045 char buf[512];
00046 out->Puts("> ");
00047 do {
00048 buf[sizeof(buf)-2] = 0;
00049 if(0 == in->Gets(buf, sizeof(buf))) {
00050 reader.FeedEof();
00051 } else {
00052 if(buf[sizeof(buf)-2]!=0 && buf[sizeof(buf)-2]!='\n') {
00053 err->Puts("#* input line too long\n");
00054 int c;
00055 do {
00056 c = in->Getc();
00057 } while (c!=EOF && c!='\n');
00058 continue;
00059 }
00060 reader.FeedString(buf);
00061 }
00062 } while(!reader.IsReady());
00063 return reader.Get();
00064 }
00065
00066 #if INTELIB_USE_READLINE == 1
00067
00068 static sigjmp_buf backtoloop;
00069 static void (*savesig)(int);
00070
00071 static void inth(int a) {
00072 signal(SIGINT, savesig);
00073 rl_cleanup_after_signal();
00074 rl_free_line_state();
00075 siglongjmp(backtoloop, 1);
00076 }
00077
00078 static bool emptyline(const char *line)
00079 {
00080 while(*line) {
00081 if(!isspace(*line)) return false;
00082 line++;
00083 }
00084 return true;
00085 }
00086
00087
00088 static SExpressionHashTable *epack = 0;
00089
00090 static bool is_dirname(const char *fname)
00091 {
00092 struct stat s;
00093 if(-1 == stat(fname, &s)) return false;
00094 return S_ISDIR(s.st_mode);
00095 }
00096
00097 static char *macro_char_generator(const char *text, int seq)
00098 {
00099 if(text[1] == '\0') {
00100 static int st;
00101 if(seq==0) st = 0;
00102 switch(st++) {
00103 case 0: return strdup("#\\");
00104 case 1: return strdup("#'");
00105 default: return 0;
00106 }
00107 }
00108 if(text[1] != '\\') return 0;
00109
00110 static int state, len;
00111 if(seq == 0) {
00112 state = 0;
00113 len = strlen(text);
00114 }
00115
00116 static const char *names[] = {
00117 "#\\Newline", "#\\Space", "#\\Tab", "#\\Backspace", "#\\Linefeed",
00118 "#\\Page", "#\\Return", "#\\Rubout", 0
00119 };
00120
00121 if(state>=0) {
00122 while(names[state]) {
00123 if(strncasecmp(names[state], text, len) == 0)
00124 return strdup(names[state++]);
00125 state++;
00126 }
00127
00128 state = -1;
00129 }
00130
00131 if(text[2] != '\0' && text[3] != '\0') return 0;
00132
00133 while(state > -256) {
00134 if(isgraph(-state) && (text[2]==(char)(-state) || text[2]=='\0')) {
00135 char *res = (char*) malloc(4);
00136 res[0] = '#'; res[1] = '\\'; res[2] = -state; res[3] = '\0';
00137 state--;
00138 return res;
00139 }
00140 state--;
00141 }
00142 return 0;
00143 }
00144
00145 static char *generator(const char *text, int seq)
00146 {
00147 if(text[0] == '"') {
00148 rl_filename_completion_desired = 1;
00149 char *fname = rl_filename_completion_function(text+1, seq);
00150 if(!fname) return 0;
00151 char *res = (char*) malloc(strlen(fname) + 3);
00152
00153 res[0] = '"';
00154 strcpy(res+1, fname);
00155 if(is_dirname(fname)) {
00156 strcat(res+1, "/");
00157 } else {
00158 strcat(res+1, "\"");
00159 }
00160 rl_completion_suppress_append = 1;
00161 free(fname);
00162 rl_filename_completion_desired = 1;
00163 return res;
00164 }
00165
00166 if(text[0] == '#') {
00167 return macro_char_generator(text, seq);
00168 }
00169
00170 static int state, len;
00171 static SReference list;
00172 if(seq == 0) {
00173 state = 0;
00174 len = strlen(text);
00175
00176 list = *PTheEmptyList;
00177 if(epack) {
00178 SExpressionHashTable::Iterator iter(*epack);
00179 SReference r = iter.GetNext();
00180 while(r.GetPtr()) {
00181 const char *s = r.Car().GetString();
00182 if(strncasecmp(s, text, len) == 0)
00183 list,s;
00184 r = iter.GetNext();
00185 }
00186 }
00187 }
00188
00189 if(list.IsEmptyList())
00190 return 0;
00191 else {
00192 char *res = strdup(list.Car().GetString());
00193 list = list.Cdr();
00194 strncpy(res, text, len);
00195 bool to_lower = false;
00196 for(int i=0; i<len; i++)
00197 if(isalpha(text[i])) to_lower = islower(text[i]);
00198 if(to_lower)
00199 for(int i=len; res[i]; i++) res[i] = tolower(res[i]);
00200 return res;
00201 }
00202 }
00203
00204 static SReference do_readline(IntelibGenericReader &reader,
00205 const SReference& pkg,
00206 const SString &prompt)
00207 {
00208 static bool init = false;
00209 if(!init) {
00210 init = true;
00211 rl_readline_name = "NILL";
00212 rl_completion_entry_function = generator;
00213 rl_basic_word_break_characters = " \n\t()';";
00214 rl_basic_quote_characters = "";
00215
00216 }
00217 epack = pkg.DynamicCastGetPtr<SExpressionHashTable>();
00218
00219 char *line;
00220
00221 for(;;) {
00222 if(0 == sigsetjmp(backtoloop, 1)) {
00223 savesig = signal(SIGINT, inth);
00224 line = readline(prompt.c_str());
00225 signal(SIGINT, savesig);
00226 SString hist_line("");
00227 if(line && emptyline(line)) {
00228 free(line);
00229 continue;
00230 }
00231 for(;;) {
00232 if(!line) {
00233 reader.FeedEof();
00234 } else {
00235 if(hist_line!="") hist_line += " ";
00236 hist_line += line;
00237 reader.FeedString(line);
00238 reader.FeedChar('\n');
00239 free(line);
00240 }
00241 if(reader.IsReady()) {
00242 add_history(hist_line.c_str());
00243 return reader.Get();
00244 }
00245 savesig = signal(SIGINT, inth);
00246 line = readline("> ");
00247 signal(SIGINT, savesig);
00248 }
00249 } else {
00250 reader.Drop();
00251 printf("\n");
00252 }
00253 }
00254 }
00255 #endif
00256
00257
00258 SReference intelib_read_line(const SStreamRef &in,
00259 const SStreamRef &out,
00260 const SStreamRef &err,
00261 IntelibGenericReader &reader,
00262 const SReference &package,
00263 const SString &prompt)
00264 {
00265 #if INTELIB_USE_READLINE == 1
00266 if(in->Fileno() == 0 && out->Fileno() == 1) {
00267 return do_readline(reader, package, prompt);
00268 } else {
00269 return plain_read(in, out, err, reader, prompt);
00270 }
00271 #else
00272 return plain_read(in, out, err, reader, prompt);
00273 #endif
00274 }