00001
00002
00003
00004 #include <string>
00005 #include <string.h>
00006 #include <stdio.h>
00007 #include <stdlib.h>
00008 #include <zlib.h>
00009 #include <boost/scoped_ptr.hpp>
00010
00011 #include "ui.h"
00012 #include "settings.h"
00013 #include "logic.h"
00014 #include "data.h"
00015 #include "cparser.h"
00016 #include "runner.h"
00017
00018 #ifdef HAVE_LUALIB_H
00019 extern "C" {
00020 #include <lua.h>
00021 #include <lualib.h>
00022 #include <lauxlib.h>
00023 #include "swigluarun.h"
00024 extern int luaopen_fityk(lua_State*L);
00025 }
00026 #endif
00027
00028 using namespace std;
00029
00030 const char* config_dirname = ".fityk";
00031 const char* startup_commands_filename = "init";
00032
00033
00034
00035
00036
00037 int our_getline(char **lineptr, size_t *n, FILE *stream)
00038 {
00039 int c;
00040 int counter = 0;
00041 while ((c = getc (stream)) != EOF && c != '\n') {
00042 if (counter >= (int) *n - 1) {
00043 *n = 2 * (*n) + 80;
00044 *lineptr = (char *) realloc (*lineptr, *n);
00045 }
00046 (*lineptr)[counter] = c;
00047 ++counter;
00048 }
00049 (*lineptr)[counter] = '\0';
00050 return c == EOF ? -1 : counter;
00051 }
00052
00053
00054
00055 int gzipped_getline(char **lineptr, size_t *n, gzFile stream)
00056 {
00057 int c;
00058 int counter = 0;
00059 while ((c = gzgetc (stream)) != EOF && c != '\n') {
00060 if (counter >= (int) *n - 1) {
00061 *n = 2 * (*n) + 80;
00062 *lineptr = (char *) realloc (*lineptr, *n);
00063 }
00064 (*lineptr)[counter] = c;
00065 ++counter;
00066 }
00067 (*lineptr)[counter] = '\0';
00068 return c == EOF ? -1 : counter;
00069 }
00070
00071
00072 class LineReader
00073 {
00074 public:
00075 LineReader() : len_(160), buf_((char*) malloc(len_)) {}
00076 ~LineReader() { free(buf_); }
00077
00078 char* next(FILE *fp)
00079 {
00080 #if HAVE_GETLINE
00081 return return_buf(getline(&buf_, &len_, fp));
00082 #else
00083
00084 return return_buf(our_getline(&buf_, &len_, fp));
00085 #endif
00086 }
00087
00088 char* gz_next(gzFile gz_stream)
00089 {
00090 return return_buf(gzipped_getline(&buf_, &len_, gz_stream));
00091 }
00092
00093 private:
00094 size_t len_;
00095 char* buf_;
00096
00097 char* return_buf(int n)
00098 {
00099
00100 if (n > 0 && buf_[n-1] == '\n')
00101 buf_[n-1] = '\0';
00102 return n == -1 ? NULL : buf_;
00103 }
00104
00105 };
00106
00107
00108 string UserInterface::Cmd::str() const
00109 {
00110 switch (status) {
00111 case kStatusOk: return cmd;
00112 case kStatusExecuteError: return cmd + " #>Runtime Error";
00113 case kStatusSyntaxError: return cmd + " #>Syntax Error";
00114 }
00115 return "";
00116 }
00117
00118 string UserInterface::get_history_summary() const
00119 {
00120 string s = S(cmd_count_) + " commands since the start of the program,";
00121 if (cmd_count_ == size(cmds_))
00122 s += " of which:";
00123 else
00124 s += "\nin last " + S(cmds_.size()) + " commands:";
00125 int n_ok = 0, n_execute_error = 0, n_syntax_error = 0;
00126 for (vector<Cmd>::const_iterator i = cmds_.begin(); i != cmds_.end(); ++i)
00127 if (i->status == kStatusOk)
00128 ++n_ok;
00129 else if (i->status == kStatusExecuteError)
00130 ++n_execute_error;
00131 else if (i->status == kStatusSyntaxError)
00132 ++n_syntax_error;
00133 s += "\n " + S(n_ok) + " executed successfully"
00134 + "\n " + S(n_execute_error) + " finished with execute error"
00135 + "\n " + S(n_syntax_error) + " with syntax error";
00136 return s;
00137 }
00138
00139
00140
00141 UserInterface::UserInterface(Ftk* F)
00142 : F_(F), show_message_(NULL), do_draw_plot_(NULL),
00143 exec_command_(NULL), refresh_(NULL), compute_ui_(NULL), wait_(NULL),
00144 cmd_count_(0)
00145 {
00146 parser_ = new Parser(F);
00147 runner_ = new Runner(F);
00148 }
00149
00150 UserInterface::~UserInterface()
00151 {
00152 delete parser_;
00153 delete runner_;
00154 }
00155
00156 UserInterface::Status UserInterface::exec_and_log(const string& c)
00157 {
00158 if (strip_string(c).empty())
00159 return UserInterface::kStatusOk;
00160
00161
00162 if (!F_->get_settings()->logfile.empty()) {
00163 FILE* f = fopen(F_->get_settings()->logfile.c_str(), "a");
00164 if (f) {
00165 fprintf(f, "%s\n", c.c_str());
00166 fclose(f);
00167 }
00168 }
00169
00170 UserInterface::Status r = this->exec_command(c);
00171 cmds_.push_back(Cmd(c, r));
00172 ++cmd_count_;
00173 return r;
00174 }
00175
00176 void UserInterface::raw_execute_line(const string& str)
00177 {
00178 Lexer lex(str.c_str());
00179 while (parser_->parse_statement(lex))
00180 runner_->execute_statement(parser_->statement());
00181 }
00182
00183 UserInterface::Status UserInterface::execute_line(const string& str)
00184 {
00185 try {
00186 raw_execute_line(str);
00187 }
00188 catch (fityk::SyntaxError &e) {
00189 F_->warn(string("Syntax error: ") + e.what());
00190 return UserInterface::kStatusSyntaxError;
00191 }
00192
00193
00194 catch (runtime_error &e) {
00195 F_->warn(string("Error: ") + e.what());
00196 return UserInterface::kStatusExecuteError;
00197 }
00198
00199 if (F_->is_plot_outdated() && F_->get_settings()->autoplot)
00200 draw_plot(UserInterface::kRepaint);
00201
00202 return UserInterface::kStatusOk;
00203 }
00204
00205 bool UserInterface::check_syntax(const string& str)
00206 {
00207 return parser_->check_syntax(str);
00208 }
00209
00210 void UserInterface::output_message(Style style, const string& s) const
00211 {
00212 show_message(style, s);
00213
00214 if (!F_->get_settings()->logfile.empty() &&
00215 F_->get_settings()->log_full) {
00216 FILE* f = fopen(F_->get_settings()->logfile.c_str(), "a");
00217 if (f) {
00218
00219 fprintf(f, "# ");
00220 for (const char *p = s.c_str(); *p; p++) {
00221 fputc(*p, f);
00222 if (*p == '\n')
00223 fprintf(f, "# ");
00224 }
00225 fprintf(f, "\n");
00226 fclose(f);
00227 }
00228 }
00229
00230 if (style == kWarning && F_->get_settings()->exit_on_warning) {
00231 show_message(kNormal, "Warning -> exiting program.");
00232 throw ExitRequestedException();
00233 }
00234 }
00235
00236
00237 class FileOpener
00238 {
00239 public:
00240 virtual ~FileOpener() {}
00241 virtual bool open(const char* fn) = 0;
00242 virtual char* read_line() = 0;
00243 protected:
00244 LineReader reader;
00245 };
00246
00247 class NormalFileOpener : public FileOpener
00248 {
00249 public:
00250 virtual ~NormalFileOpener() { if (f_) fclose(f_); }
00251 virtual bool open(const char* fn) { f_ = fopen(fn, "r"); return f_; }
00252 virtual char* read_line() { return reader.next(f_); }
00253 private:
00254 FILE *f_;
00255 };
00256
00257 class GzipFileOpener : public FileOpener
00258 {
00259 public:
00260 virtual ~GzipFileOpener() { if (f_) gzclose(f_); }
00261 virtual bool open(const char* fn) { f_ = gzopen(fn, "rb"); return f_; }
00262 virtual char* read_line() { return reader.gz_next(f_); }
00263 private:
00264 gzFile f_;
00265 };
00266
00267
00268 #ifdef HAVE_LUALIB_H
00269 static
00270 void exec_lua_script(Ftk *F_, const string& filename)
00271 {
00272 lua_State *L = lua_open();
00273 luaL_openlibs(L);
00274 luaopen_fityk(L);
00275 swig_type_info *type_info = SWIG_TypeQuery(L, "fityk::Fityk *");
00276 assert(type_info != NULL);
00277 int owned = 1;
00278 fityk::Fityk *f = new fityk::Fityk(F_);
00279 SWIG_NewPointerObj(L, f, type_info, owned);
00280 lua_setglobal(L, "F");
00281 int status = luaL_dofile(L, filename.c_str());
00282 if (status != 0) {
00283 const char *msg = lua_tostring(L, -1);
00284 F_->warn("Lua Error:\n" + S(msg ? msg : "(non-string error)"));
00285 }
00286 lua_close(L);
00287 }
00288 #endif
00289
00290 void UserInterface::exec_script(const string& filename)
00291 {
00292 user_interrupt = false;
00293
00294 if (endswith(filename, ".lua")) {
00295 #ifdef HAVE_LUALIB_H
00296 exec_lua_script(F_, filename);
00297 #else
00298 F_->warn("Lua support is disabled.");
00299 #endif
00300 return;
00301 }
00302
00303 boost::scoped_ptr<FileOpener> opener;
00304 if (endswith(filename, ".gz"))
00305 opener.reset(new GzipFileOpener);
00306 else
00307 opener.reset(new NormalFileOpener);
00308 if (!opener->open(filename.c_str())) {
00309 F_->warn("Can't open file: " + filename);
00310 return;
00311 }
00312
00313 string dir = get_directory(filename);
00314
00315 int line_index = 0;
00316 char *line;
00317 while ((line = opener->read_line()) != NULL) {
00318 ++line_index;
00319 string s = line;
00320 if (s.empty())
00321 continue;
00322 if (F_->get_verbosity() >= 0)
00323 show_message (kQuoted, S(line_index) + "> " + s);
00324 replace_all(s, "_EXECUTED_SCRIPT_DIR_/", dir);
00325 bool r = execute_line(s);
00326 if (r != kStatusOk)
00327 break;
00328 if (user_interrupt) {
00329 F_->msg ("Script stopped by signal INT.");
00330 break;
00331 }
00332 }
00333 }
00334
00335
00336 void UserInterface::exec_stream(FILE *fp)
00337 {
00338 LineReader reader;
00339 char *line;
00340 while ((line = reader.next(fp)) != NULL) {
00341 string s = line;
00342 if (F_->get_verbosity() >= 0)
00343 show_message (kQuoted, "> " + s);
00344 bool r = execute_line(s);
00345 if (r != kStatusOk)
00346 break;
00347 }
00348 }
00349
00350 void UserInterface::exec_string_as_script(const char* s)
00351 {
00352 const char* start = s;
00353 for (;;) {
00354 const char* end = start;
00355 while (*end != '\0' && *end != '\n')
00356 ++end;
00357 while (isspace(*(end-1)) && end > start)
00358 --end;
00359 if (end > start) {
00360 string line(start, end);
00361 if (!F_->get_settings()->logfile.empty()) {
00362 FILE* f = fopen(F_->get_settings()->logfile.c_str(), "a");
00363 if (f) {
00364 fprintf(f, " %s\n", line.c_str());
00365 fclose(f);
00366 }
00367 }
00368 if (F_->get_verbosity() >= 0)
00369 show_message(kQuoted, "> " + line);
00370 bool r = execute_line(line);
00371 if (r != kStatusOk)
00372 break;
00373 }
00374 if (*end == '\0')
00375 break;
00376 start = end + 1;
00377 }
00378 }
00379
00380 void UserInterface::draw_plot(RepaintMode mode)
00381 {
00382 if (do_draw_plot_)
00383 (*do_draw_plot_)(mode);
00384 F_->updated_plot();
00385 }
00386
00387
00388 UserInterface::Status UserInterface::exec_command(const string& s)
00389 {
00390 return exec_command_ ? (*exec_command_)(s) : execute_line(s);
00391 }
00392
00393 bool is_fityk_script(string filename)
00394 {
00395 const char *magic = "# Fityk";
00396
00397 FILE *f = fopen(filename.c_str(), "rb");
00398 if (!f)
00399 return false;
00400
00401 if (endswith(filename, ".fit") || endswith(filename, ".fityk") ||
00402 endswith(filename, ".fit.gz") || endswith(filename, ".fityk.gz")) {
00403 fclose(f);
00404 return true;
00405 }
00406
00407 const int magic_len = strlen(magic);
00408 char buffer[32];
00409 fgets(buffer, magic_len, f);
00410 fclose(f);
00411 return !strncmp(magic, buffer, magic_len);
00412 }
00413
00414 void UserInterface::process_cmd_line_filename(const string& par)
00415 {
00416 if (startswith(par, "=->"))
00417 exec_and_log(string(par, 3));
00418 else if (is_fityk_script(par) || endswith(par, ".lua"))
00419 exec_script(par);
00420 else {
00421 exec_and_log("@+ <'" + par + "'");
00422 }
00423 }
00424
00425