/*========================================================================= Program: bbtk Module: $RCSfile: bbtkInterpreter.cxx,v $ $ Language: C++ Date: $Date: 2008/02/18 10:41:02 $ Version: $Revision: 1.33 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or http://www.creatis.insa-lyon.fr/Public/bbtk/License.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ /** * \file * \brief class Interpreter : */ #include "bbtkInterpreter.h" #include "bbtkMessageManager.h" #include "bbtkConfigurationFile.h" #include "bbtkWxConsole.h" #include "bbtkUtilities.h" #include #include #ifdef CMAKE_HAVE_TERMIOS_H #include #define BBTK_USE_TERMIOS_BASED_PROMPT #endif #include namespace bbtk { Interpreter* Interpreter::mGlobalInterpreter = NULL; //======================================================================= /** * */ Interpreter::Interpreter() : mCommandLine(false) { bbtk::MessageManager::RegisterMessageType("Echo","Level>0 : Prints the 'echo' commands of the user.\n\tLevel>1 : Prints the command being interpreted",1); bbtk::MessageManager::RegisterMessageType("Interpreter","Messages of the interpreter",0); bbtkDebugMessageInc("Interpreter",9,"Interpreter::Interpreter()" <0) { while ((mFile.size()>0) && (!mFile.back()->eof())) { mLine.back()++; char buf[500]; mFile.back()->getline(buf,500); std::string str(buf); int size=str.length(); if ( str[ size-1 ]==13 ) { str.erase(size-1,1); } InterpretLine(str, insideComment); } //if (mFile.size()>0) CloseCurrentFile(); } } catch (QuitException e) { } catch (bbtk::Exception e) { std::cerr << "* ERROR : "< words; SplitLine(line,words); // Empty line if (words.size()<1) { bbtkDebugDecTab("Interpreter",9); return; } // Single line comment : # or // if ( words[0][0]=='#' || (words[0][0]=='/' && words[0][1]=='/') ) { bbtkDebugDecTab("Interpreter",9); bbtkMessage("Interpreter",9,"Comment"<Define(words[1],words[2],filename); } break; case cEndDefine : mExecuter->EndDefine(); break; case cPrint : Print(words[1]); /// \todo use mExecuter break; case cExec : if (words[1]=="freeze") mExecuter->SetNoExecMode(true); else if (words[1]=="unfreeze") mExecuter->SetNoExecMode(false); else mExecuter->Update(words[1]); break; case cInput : Utilities::SplitAroundFirstDot(words[2],left,right); mExecuter->DefineInput(words[1],left,right,words[3]); break; case cOutput : Utilities::SplitAroundFirstDot(words[2],left,right); mExecuter->DefineOutput(words[1],left,right,words[3]); break; case cSet : Utilities::SplitAroundFirstDot(words[1],left,right); mExecuter->Set(left,right,words[2]); break; case cAuthor : mExecuter->Author(words[1]); break; case cCategory : mExecuter->Category(words[1]); break; case cIndex : if (words.size()==1) Index("tmp_index.html"); else if (words.size()==2) Index(words[1]); else if (words.size()==3) Index(words[1],words[2]); break; case cDescription : mExecuter->Description(words[1]); break; case cHelp : Help(words); break; case cMessage : if (words.size()<3) { bbtk::MessageManager::PrintInfo(); } else { sscanf(words[2].c_str(),"%d",&level); bbtk::MessageManager::SetMessageLevel(words[1],level); } break; case cGraph : Graph(words); break; case cConfig : Config(); break; case cReset : // EED this->mExecuter->Reset(); break; case cInclude : if (mCommandLine) { InterpretFile(words[1], true ); // true : better pass use_config_file } else { SwitchToFile(words[1], true ); // true : better pass use_config_file } // if 'source' was given if (words.size()==3) { this->mExecuter->SetCurrentFileName(words[1]); } break; case cLoad: LoadPackage(words[1], true ); // true : better pass use_config_file break; case cUnload: UnLoadPackage(words[1]); break; case cQuit : delete mExecuter; throw QuitException(); break; case cWorkspace : if (words.size() == 2) { if (words[1]=="freeze") mExecuter->SetNoExecMode(true); else if (words[1]=="unfreeze") mExecuter->SetNoExecMode(false); } else { mExecuter->SetWorkspaceName(words[2]); } break; default: bbtkInternalError("should not reach here !!!"); } bbtkDecTab("Interpreter",9); } //======================================================================= //======================================================================= /** * */ void Interpreter::SplitLine ( const std::string& str, std::vector& tokens) { bbtkDebugMessageInc("Interpreter",9,"Interpreter::SplitLine(\""< quote; Utilities::SplitString(str,delimiters,quote); delimiters = " \t"; std::vector::iterator i; for (i=quote.begin(); i!=quote.end(); ) { Utilities::SplitString(*i,delimiters,tokens); ++i; if (i!=quote.end()) { // bbtkDebugMessage("Interpreter",0,"\""<<*i<<"\""<GetNoExecMode()) return; bbtkDebugMessageInc("Interpreter",9,"Interpreter::SplitLine(\""< chains; std::string delimiters("$"); // Skip delimiters at beginning. std::string::size_type lastPos = str.find_first_not_of(delimiters, 0); bool is_text = true; if (lastPos>0) is_text = false; // Find first delimiter. std::string::size_type pos = str.find_first_of(delimiters, lastPos); while (std::string::npos != pos || std::string::npos != lastPos) { if (is_text) { // Found a text token, add it to the vector. chains.push_back(str.substr(lastPos, pos - lastPos)); } else { // is an output (between $$) : decode std::string tok,box,output; tok = str.substr(lastPos, pos - lastPos); Utilities::SplitAroundFirstDot(tok,box,output); chains.push_back( mExecuter->Get(box,output) ); } // Skip delimiters. Note the "not_of" lastPos = str.find_first_not_of(delimiters, pos); // Find next delimiter pos = str.find_first_of(delimiters, lastPos); // is_text = !is_text; } std::vector::iterator i; for (i= chains.begin(); i!=chains.end(); ++i) { // bbtkMessage("Echo",1,*i); SubsBackslashN(*i); std::cout << *i; } std::cout << std::endl; bbtkDebugDecTab("Interpreter",9); } //======================================================================= /** * */ // =================================================================================== void Interpreter::SwitchToFile( const std::string& name, bool use_configuration_file ) { // Note : in the following : // name : the user supplied name // - abreviated name e.g. scr scr.bbs // - relative full name e.g. ./scr.bbs ../../scr.bbs // - absolute full name e.g. /home/usrname/proj/dir/scr.bbs // same for Windows, with c:, d: ... // // use ./directory/subdir/scrname.bbs // bbtkDebugMessageInc("Interpreter",9,"Interpreter::SwitchToFile( \"" < script_paths; std::string fullPathScriptName; // full path script name std::string pkgname; // e.g. .bbs std::vector Filenames; if (use_configuration_file) { // The following is *NOT* a debug time message : // It's a user intended message. // Please don't remove it. bbtkMessage("Interpreter",1, "look for : [" << name << "] (use_configuration_file == TRUE)" << std::endl); script_paths = ConfigurationFile::GetInstance().Get_bbs_paths(); } std::string upath; pkgname = Utilities::ExtractScriptName(name,upath); bool fullnameGiven = false; bool foundFile = false; if(pkgname == "*") // =========================================== load all boxes (e.g. std/boxes/*) { int nbBssFiles; if (upath[0]=='/' || upath[1] == ':' ) // ==== absolute name, load all .bbs files { int nbFiles = Utilities::Explore(upath, false, Filenames); nbBssFiles = 0; for (std::vector::iterator i = Filenames.begin(); i!= Filenames.end(); ++i) { if ((*i).substr((*i).size()-4, 4) != ".bbs") continue; // ignore non .bbs files LoadScript(*i,name); nbBssFiles++; } if (nbBssFiles==0) bbtkMessage("Interpreter",2, "WARNING : No '.bbs' file found in [" << upath << "]" << std::endl); return; } std::string path; std::vector::iterator i; std::string fullDirectoryName; for (i=script_paths.begin();i!=script_paths.end();i++)// ==== relative name, iterate + load all .bbs files { path = *i; // we *really* want '.' to be the current working directory if (path == ".") { char buf[2048]; // for getcwd char * currentDir = getcwd(buf, 2048); std::string cwd(currentDir); path = currentDir; } fullDirectoryName = Utilities::MakePkgnameFromPath(path, upath, false); //EED 18 Fev 2008 // without last slash "\" std::string fullDirectoryNameClean = fullDirectoryName.substr(0,fullDirectoryName.size()-1); // Check if library exists if ( ! Utilities::IsDirectory( fullDirectoryNameClean ) ) { // The following is *NOT* a debug time message : // It's a user intended message. // Please don't remove it. bbtkMessage("Interpreter",1," [" <::iterator i = Filenames.begin(); i!= Filenames.end(); ++i) { if ((*i).substr((*i).size()-4, 4) != ".bbs") continue; // ignore non .bbs files LoadScript(*i,name); nbBssFiles++; } if (nbBssFiles==0) bbtkMessage("Interpreter",1, "WARNING : No '.bbs' file found in [" << fullDirectoryName << "]" << std::endl); //break; // a directory was found; we stop iterating // LG : No! We want all files included ! } return; } //std::string::size_type slash_position = name.find_last_of("/\\"); // if name starts with a / or a . or contains : user is assumed to have passed a relative/absolute name // (not only a plain script name) // we trust him, and try to expland the directory name // WARNING : starting from current local directory : ./whatYouWant (./ mandatory!) if (name[0]=='/' || name[1] == ':' || name[0]=='.') // absolute path (linux/windows) or relative path { // ===========================================================check user supplied location fullnameGiven = true; fullPathScriptName = Utilities::ExpandLibName(name, false); // allow user to always forget ".bbs" int l = fullPathScriptName.size(); if (l!=0) { if (l>4) { if (fullPathScriptName.substr(l-4, 4) != ".bbs") { fullPathScriptName = fullPathScriptName + ".bbs"; } } else { fullPathScriptName = fullPathScriptName + ".bbs"; } if ( Utilities::FileExists(fullPathScriptName)) { foundFile = true; } } // endif l != 0 } else // =============================================================== iterate on the paths { std::string path; std::vector::iterator i; for (i=script_paths.begin();i!=script_paths.end();++i) { path = *i; // we *really* want '.' to be the current working directory if (path == ".") { char buf[2048]; // for getcwd char * currentDir = getcwd(buf, 2048); std::string cwd(currentDir); path = currentDir; } // fullPathScriptName = Utilities::MakePkgnameFromPath(path, name, true); //pkgname); fullPathScriptName = Utilities::MakePkgnameFromPath(path, name, true); // Check if library exists if ( ! Utilities::FileExists(fullPathScriptName) ) { // The following is *NOT* a debug time message : // It's a user intended message. // Please don't remove it. bbtkMessage("Interpreter",2, " [" <open(fullPathScriptName.c_str()); if (!s->good()) { bbtkError("Could not open file ["<[" << fullPathScriptName << "] found" << std::endl); mFile.push_back(s); mFileName.push_back(fullPathScriptName); mIncludeFileName.push_back(includeScriptName); mLine.push_back(0); return; } //======================================================================= /** * */ void Interpreter::CloseCurrentFile() { bbtkDebugMessage("Interpreter",9,"Interpreter::CloseCurrentFile()" < no file left open"<close(); delete mFile.back(); mFile.pop_back(); bbtkDebugMessage("Interpreter",9, " Closing file '"<close(); delete mFile.back(); mFile.pop_back(); bbtkDebugMessage("Interpreter",9, " Closing file '"<& words, CommandInfoType& info ) { bbtkDebugMessageInc("Interpreter",9,"Interpreter::InterpretCommand(...)"<second.argmin ) || ( words.size()-1 > c->second.argmax ) ) { HelpCommand(words[0]); bbtkError(words[0]<<" : wrong number of arguments"); } info = c->second; bbtkDecTab("Interpreter",9); } //======================================================================= //======================================================================= /// Displays help on all the commands void Interpreter::Help(const std::vector& words) { unsigned int nbarg = words.size()-1; if (nbarg==0) { HelpCommands(); } else if (nbarg==1) { if (words[1]=="packages") { PrintPackages(true); return; } try { HelpCommand(words[1]); } catch (bbtk::Exception e) { try { HelpPackage(words[1]); #ifdef _USE_WXWIDGETS_ if ( WxConsole::GetInstance() != 0 ) { std::string url = ConfigurationFile::GetInstance().Get_doc_path(); url += "/bbdoc/" + words[1] + "/index.html"; if (Utilities::FileExists(url)) { WxConsole::GetInstance()->ShowHtmlPage(url); } } #endif } catch (bbtk::Exception f) { try { std::string package; HelpBlackBox(words[1],package); #ifdef _USE_WXWIDGETS_ if ( WxConsole::GetInstance() != 0 ) { std::string url = ConfigurationFile::GetInstance().Get_doc_path(); url += "/bbdoc/" + package + "/index.html"; if (Utilities::FileExists(url)) { url += "#" + words[1]; WxConsole::GetInstance()->ShowHtmlPage(url); } } #endif } catch (bbtk::Exception g) { try { this->mExecuter->ShowRelations(words[1],"0","9999"); } catch (bbtk::Exception h){ bbtkError("\""<first << std::endl; // std::cout << " usage : " << i->second.syntax << std::endl; // std::cout << " " << i->second.help << std::endl; } } //======================================================================= //======================================================================= /// Displays help on a particular commands void Interpreter::HelpCommand(const std::string& s) { CommandDictType::iterator c; c = mCommandDict.find(s); if ( c == mCommandDict.end() ) { bbtkError(s<<" : Unknown command"); } // std::cout << " " << s << " : "<< std::endl; // CommandParamDictType::iterator i; // for ( i = c->second.begin(); // i != c->second.end(); // ++i) { std::cout << " usage : " << c->second.syntax << std::endl; std::cout << " " << c->second.help << std::endl; } //======================================================================= //======================================================================= /// Fills the vector commands with the commands which /// have the first n chars of buf for prefix /// TODO : skip initial spaces in buf and also return the position of first /// non blank char in buf void Interpreter::FindCommandsWithPrefix( char* buf, int n, std::vector& commands ) { CommandDictType::const_iterator i; for (i=mCommandDict.begin(); i!=mCommandDict.end(); ++i) { if ((i->first).find(buf,0,n) == 0) commands.push_back(i->first); } } //======================================================================= //======================================================================= #ifdef BBTK_USE_TERMIOS_BASED_PROMPT inline void PrintChar(char c) { write(STDOUT_FILENO,&c,1); } inline void BackSpace() { write(STDOUT_FILENO,"\b \b",3); } // LG : KEYBOARD CODES AS SCANNED ON MY TTY : UNIVERSAL ? // IF NOT THE USER SHOULD BE ABLE TO CONFIGURE IT // E.G. STORE THIS IN bbtk_config.xml #define BBTK_UP_ARROW_KBCODE 0x00415B1B #define BBTK_DOWN_ARROW_KBCODE 0x00425B1B #define BBTK_RIGHT_ARROW_KBCODE 0x00435B1B #define BBTK_LEFT_ARROW_KBCODE 0x00445B1B #define BBTK_BACKSPACE_KBCODE 0x00000008 #define BBTK_DEL_KBCODE 0x0000007F #define BBTK_SPACE_KBCODE 0x00000020 //======================================================================= void Interpreter::GetLineFromPrompt(std::string& s) { int c; int ind=0; int MAX_LINE_SIZE = 160; int MAX_HISTORY_SIZE = 100; char* newline = new char[MAX_LINE_SIZE]; memset(newline,0,MAX_LINE_SIZE); char* histline = new char[MAX_LINE_SIZE]; memset(histline,0,MAX_LINE_SIZE); char* line = newline; int hist = mHistory.size(); write(1,"> ",2); while(1) { c=0; read ( STDIN_FILENO, &c, 4) ; bbtkDebugMessage("Debug",9,"[0x"<= BBTK_SPACE_KBCODE ) && ( c < BBTK_DEL_KBCODE )) { PrintChar(c); line[ind++]=c; } // CR else if (c=='\n') { // delete the unused line if (line==newline) delete histline; else delete newline; // empty lines are not stored in from history if (strlen(line)) { // if history too long : delete oldest command if (mHistory.size()>MAX_HISTORY_SIZE) { delete mHistory.front(); mHistory.pop_front(); } mHistory.push_back(line); } break; } // Backspace else if ( (ind>0) && ((c == BBTK_BACKSPACE_KBCODE) || (c == BBTK_DEL_KBCODE)) ) { line[ind--]=' '; BackSpace(); } // Tab else if (c=='\t') { // TODO : Command completion std::vector commands; FindCommandsWithPrefix( line,ind,commands); if (commands.size()==1) { std::string com = *commands.begin(); for (; ind1) { std::vector::iterator i; write(1,"\n",1); for (i=commands.begin();i!=commands.end();++i) { write(STDOUT_FILENO,(*i).c_str(),strlen((*i).c_str())); PrintChar(' '); } write(STDOUT_FILENO,"\n> ",3); //for (int j=0;j0 && c==BBTK_LEFT_ARROW_KBCODE) { PrintChar('\b'); ind--; } } write(STDOUT_FILENO,"\n\r",2); s = line; } #else //======================================================================= void Interpreter::GetLineFromPrompt(std::string& s) { s.clear(); putchar('>'); putchar(' '); do { char c = getchar(); if (c=='\n') { putchar('\n'); break; } if (c=='\t') { // putchar('T'); continue; } // putchar(c); s += c; } while (true); } //======================================================================= #endif //======================================================================= void Interpreter::CommandLineInterpreter() { bbtkDebugMessageInc("Interpreter",9, "Interpreter::CommandLineInterpreter()"<& words) { std::string page; bool system_display = true; #ifdef _USE_WXWIDGETS_ if ( WxConsole::GetInstance() != 0 ) system_display = false; #endif if (words.size()==1) { page = mExecuter->ShowGraph(".","0","0","","","",system_display); } else if (words.size()==2) { page = mExecuter->ShowGraph(words[1],"0","0","","","",system_display); } else if (words.size()==3) { page = mExecuter->ShowGraph(words[1],words[2],"0","","","",system_display); } else if (words.size()==4) { page = mExecuter->ShowGraph(words[1],words[2],words[3],"","","",system_display); } else if (words.size()==5) { page = mExecuter->ShowGraph(words[1],words[2],words[3],words[4],"","",system_display); } else if (words.size()==6) { page = mExecuter->ShowGraph(words[1],words[2],words[3],words[4],words[5],"",system_display); } else if (words.size()==7) { page = mExecuter->ShowGraph(words[1],words[2],words[3],words[4],words[5],words[6],system_display); } #ifdef _USE_WXWIDGETS_ if ( WxConsole::GetInstance() != 0 ) WxConsole::GetInstance()->ShowHtmlPage(page); #endif } //======================================================================= //======================================================================= void Interpreter::Index(const std::string& filename, const std::string& type) { Factory::IndexEntryType t; if (type=="Initials") t = Factory::Initials; else if (type=="Categories") t = Factory::Categories; else if (type=="Packages") t = Factory::Packages; GetGlobalFactory()->CreateHtmlIndex(t,filename); } //======================================================================= }//namespace