]> Creatis software - bbtk.git/blob - kernel/src/bbtkInterpreter.cxx
775ae989d66f26dc42536ddf5f30aba91bb3d055
[bbtk.git] / kernel / src / bbtkInterpreter.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   bbtk
4   Module:    $RCSfile: bbtkInterpreter.cxx,v $ $
5   Language:  C++
6   Date:      $Date: 2008/01/28 15:28:15 $
7   Version:   $Revision: 1.7 $
8                                                                                 
9   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10   l'Image). All rights reserved. See Doc/License.txt or
11   http://www.creatis.insa-lyon.fr/Public/bbtk/License.html for details.
12
13      This software is distributed WITHOUT ANY WARRANTY; without even
14      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15      PURPOSE.  See the above copyright notices for more information.
16                                                                                 
17 =========================================================================*/
18 /**
19  *  \file 
20  *  \brief class Interpreter : 
21  */
22
23 #include "bbtkInterpreter.h" 
24 #include "bbtkMessageManager.h"
25 #include "bbtkConfigurationFile.h"
26 #include "bbtkWxConsole.h"
27 #include "bbtkUtilities.h"
28 #include <sys/stat.h>
29 #ifdef CMAKE_HAVE_TERMIOS_H
30 #include <termios.h>
31 #define BBTK_USE_TERMIOS_BASED_PROMPT
32 #endif
33
34 #include <string>
35
36 namespace bbtk
37 {
38
39 Interpreter* Interpreter::mGlobalInterpreter = NULL;
40
41  //=======================================================================
42  /**
43    *  
44    */
45   Interpreter::Interpreter() 
46     :
47     mCommandLine(false), verbose(false)
48   {
49     bbtk::MessageManager::RegisterMessageType("Echo","Level>0 : Prints the 'echo' commands of the user.\n\tLevel>1 : Prints the command being interpreted",1);
50     bbtk::MessageManager::RegisterMessageType("Interpreter","Messages of the interpreter",0);
51     bbtkDebugMessageInc("Interpreter",9,"Interpreter::Interpreter()" <<std::endl);
52  
53     mGlobalInterpreter = this;
54
55     //    mFactory = new bbtk::Factory();
56     mExecuter = new bbtk::Executer();
57     //mExecuter->SetFactory(mFactory);
58
59     // Builds the commands dict
60     CommandInfoType info;
61    
62     info.keyword = "new";
63     info.argmin = 2;
64     info.argmax = 2;
65     info.code = cNew;
66     info.syntax = "new <type> <name>";
67     info.help = "Creates a new black box of type <type> with name <name>";
68     mCommandDict[info.keyword] = info;
69     
70     info.keyword = "delete";
71     info.argmin = 1;
72     info.argmax = 1;
73     info.code = cDelete;
74     info.syntax = "delete <box>";
75     info.help = "Deletes the black box of name <box>";
76     mCommandDict[info.keyword] = info;
77
78     info.keyword = "connect";
79     info.argmin = 2;
80     info.argmax = 2;
81     info.code = cConnect;
82     info.syntax = "connect <box1.output> <box2.input>";
83     info.help = "Connects the ouput <output> of black box <box1> to the input <input> of black box <box2>";
84     mCommandDict[info.keyword] = info;
85
86     info.keyword = "print";
87     info.argmin = 1;
88     info.argmax = 1;
89     info.code = cPrint;
90     info.syntax = "print <string>";
91     info.help = "Prints the string. Substitutes any token of the form '$box.output$' by the string adaptation of the output of the box (requires the right adaptor). No carriage return is issued at the end, use '\\n' to add carriage returns. The level of 'Echo' messages must be greater than 1 (see the command 'message').";
92     mCommandDict[info.keyword] = info;
93
94     info.keyword = "exec";
95     info.argmin = 1;
96     info.argmax = 2;
97     info.code = cExec;
98     info.syntax = "exec <box | 'freeze' | 'unfreeze' >";
99     info.help = "Executes the black box of name <box> (and connected boxes if needed). If the special keyword 'freeze' is given then freezes any further execution command. 'unfreeze' reverts to normal execution mode.";
100     mCommandDict[info.keyword] = info;
101
102     info.keyword = "package";
103     info.argmin = 1;
104     info.argmax = 1;
105     info.code = cPackage;
106     info.syntax = "package <name>";
107     info.help = "Begins the definition of a package.";
108     mCommandDict[info.keyword] = info;
109     
110     info.keyword = "endpackage";
111     info.argmin = 0;
112     info.argmax = 0;
113     info.code = cEndPackage;
114     info.syntax = "endpackage";
115     info.help = "Ends the definition of a package.";
116     mCommandDict[info.keyword] = info;
117
118     info.keyword = "define";
119     info.argmin = 1;
120     info.argmax = 2;
121     info.code = cDefine;
122     info.syntax = "define <type> [<package>]";
123     info.help = "Begins the definition of a new type of complex black box called <type>. If <package> if provided will create it in the given package.";
124     mCommandDict[info.keyword] = info;
125     
126     info.keyword = "endefine";
127     info.argmin = 0;
128     info.argmax = 0;
129     info.code = cEndDefine;
130     info.syntax = "endefine";
131     info.help = "Ends the definition of a new type of complex black box";
132     mCommandDict[info.keyword] = info;
133
134     info.keyword = "input";
135     info.argmin = 3;
136     info.argmax = 3;
137     info.code = cInput;
138     info.syntax = "input <name> <box.input> <help>";
139     info.help = "Defines the input <name> of the current working black box as being an alias for the input <input> of the black box <box>. <help> defines the help string for the newly created input";
140     mCommandDict[info.keyword] = info;
141
142     info.keyword = "output";
143     info.argmin = 3;
144     info.argmax = 3;
145     info.code = cOutput;
146     info.syntax = "output <name> <box.output> <help>";
147     info.help = "Defines the output <name> of the current working black box as being an alias for the output <output> of the black box <box>. <help> defines the help string for the newly created output";
148     mCommandDict[info.keyword] = info;
149
150     info.keyword = "set";
151     info.argmin = 2;
152     info.argmax = 2;
153     info.code = cSet;
154     info.syntax = "set <box.input> <value>";
155     info.help = "Sets the value of the input <input> of the black box <box> to <value>. There must exist a string to the value type adaptor";
156     mCommandDict[info.keyword] = info;
157    
158     info.keyword = "config";  // JPR
159     info.argmin = 0;
160     info.argmax = 1;
161     info.code = cConfig;
162     info.syntax = "config [<verbose>|<v>]";
163     info.help = "Prints the value of all configuration parameters";
164     mCommandDict[info.keyword] = info;
165
166     info.keyword = "reset";  //EED
167     info.argmin = 0;
168     info.argmax = 0;
169     info.code = cReset;
170     info.syntax = "reset";
171     info.help = "Deletes all boxes and unloads all packages (bbi is reset to its start state)";
172     mCommandDict[info.keyword] = info;
173
174     info.keyword = "author";
175     info.argmin = 1;
176     info.argmax = 1;
177     info.code = cAuthor;
178     info.syntax = "author <string>";
179     info.help = "Adds the string <string> to the author information of the black box being defined";
180     mCommandDict[info.keyword] = info;
181
182     info.keyword = "description";
183     info.argmin = 1;
184     info.argmax = 1;
185     info.code = cDescription;
186     info.syntax = "description <string>";
187     info.help = "Adds the string <string> to the descriptive information of the black box being defined";
188     mCommandDict[info.keyword] = info;
189
190     info.keyword = "help";
191     info.argmin = 0;
192     info.argmax = 2;
193     info.code = cHelp;
194     info.syntax = "help";
195     info.syntax = "\n         (1) help \n         (2) help <command name> \n         (3) help packages [all]\n         (4) help <package name> [all]\n         (5) help <black box type> \n         (6) help <black box name>";
196     info.help = "Effect :\n         (1) Lists all available commands;\n         (2) Prints help on a particular command; \n         (3) Lists the packages loaded and their black boxes.\n             Add 'all' to list adaptors; \n         (4) Prints short help on the black boxes of a package.\n             Add 'all' to include adaptors; \n         (5) Prints full help on a black box type; \n         (6) Prints information on the inputs, outputs and connections of a black box instance.";
197     mCommandDict[info.keyword] = info;
198
199     info.keyword = "message";
200     info.argmin = 0;
201     info.argmax = 2;
202     info.code = cMessage;
203     info.syntax = "message <category> <level>";
204     info.help = "Sets the level of the category of messages <category> to <level>.\n  If category='All' then sets the level for all categories. If no category nor level is passed then prints info on available categories of messages and their current level.";
205     mCommandDict[info.keyword] = info;
206
207     info.keyword = "include";
208     info.argmin = 1;
209     info.argmax = 1;
210     info.code = cInclude;
211     info.syntax = "include <filename>";
212     info.help = "Includes the file <filename>";
213     mCommandDict[info.keyword] = info;
214
215     info.keyword = "quit";
216     info.argmin = 0;
217     info.argmax = 0;
218     info.code = cQuit;
219     info.syntax = "quit";
220     info.help = "Quits the program (during script execution it stops the complete execution)";
221     mCommandDict[info.keyword] = info;
222
223     info.keyword = "load";
224     info.argmin = 1;
225     info.argmax = 1;
226     info.code = cLoad;
227     info.syntax = "load <packagename>";
228     info.help = "Loads the black box package <packagename>";
229     mCommandDict[info.keyword] = info;
230
231     info.keyword = "unload";
232     info.argmin = 1;
233     info.argmax = 1;
234     info.code = cUnload;
235     info.syntax = "unload <packagename>";
236     info.help = "Unloads the black box package <packagename>";
237     mCommandDict[info.keyword] = info;
238
239     info.keyword = "graph";
240     info.argmin = 0;
241     info.argmax = 6;
242     info.code = cGraph;
243     info.syntax = "graph [ BlackBoxName [ Detail 0..1 [ Level 0..99999 [ Output html file [ Custom header [ Custom title ]]]]]] \n         graph [ BlackBoxNameType [ Detail 0..1 [ Level 0..99999 [ Output html file [ Custom header [ Custom title ]]]]]]";
244     info.help = "Shows a graphical view of a bbtk pipeline.\n- BlackBoxName : name of the box to view. Default '.' : current box.\n- BlackBoxNameType : name of the type of box to view, ex : 'workspace')";
245     mCommandDict[info.keyword] = info;
246
247     /*
248     info.keyword = "workspace";
249     info.argmin = 1;
250     info.argmax = 2;
251     info.code = cWorkspace;
252     info.syntax = "workspace < ( freeze | unfreeze ) | ( rename <newname> ) >";
253     info.help = "Configures the workspace.\n        'freeze' allow to block execution commands while keeping definition commands active. 'unfreeze' turns back the worspace in 'normal' mode.\n      'rename' allow to set a new name to the workspace.";
254     mCommandDict[info.keyword] = info;
255     */
256
257     bbtkDebugDecTab("Interpreter",9);
258
259   } 
260   //=======================================================================
261   
262   
263   
264   //=======================================================================  
265   /**
266    *  
267    */
268   Interpreter::~Interpreter()
269   {
270     bbtkDebugMessageInc("Interpreter",9,"Interpreter::~Interpreter()" <<std::endl);
271     delete mExecuter;
272     //delete mFactory;
273
274     //    std::cout <<"EO Interpreter::~Interpreter()"<<std::endl;
275     bbtkDebugDecTab("Interpreter",9);
276   }
277   //=======================================================================
278
279
280   //=======================================================================
281   /**
282    *  
283    */
284   void Interpreter::InterpretFile( const std::string& filename,  bool use_configuration_file, bool verbose)
285   {
286     bbtkDebugMessageInc("Interpreter",9,"Interpreter::InterpretFile(\""<<filename<<"\")"<<std::endl);
287
288     bool exm = mCommandLine;
289     mCommandLine = false;
290
291     try 
292     {
293       SwitchToFile(filename, use_configuration_file, verbose);
294       bool insideComment = false; // for multiline comment
295       while (mFile.size()>0) 
296       {
297          while ((mFile.size()>0) && 
298                 (!mFile.back()->eof()))
299          {
300             mLine.back()++;
301             char buf[500];
302             mFile.back()->getline(buf,500);
303       
304             std::string str(buf);
305             int size=str.length();
306             if ( str[ size-1 ]==13  )
307             {
308                str.erase(size-1,1);
309             }
310       
311             InterpretLine(str, insideComment);
312          }
313         //if (mFile.size()>0) 
314         CloseCurrentFile();
315       }
316     }
317     catch (QuitException e) 
318     {
319     }
320     catch (bbtk::Exception e) 
321     {
322       std::cerr << "* ERROR : "<<e.GetMessage()<<std::endl;
323       if (mFileName.size()) {
324          std::cerr << "* FILE  : \""<<mFileName.back()<<"\""<<std::endl;
325          std::cerr << "* LINE  : "<<mLine.back()<<std::endl;
326       }    
327     }
328     catch (std::exception& e) 
329     {
330        std::cerr << "* ERROR : "<<e.what()<<" (not in bbtk)"<<std::endl;
331        if (mFileName.size()) {
332           std::cerr << "* FILE  : \""<<mFileName.back()<<"\""<<std::endl;
333           std::cerr << "* LINE  : "<<mLine.back()<<std::endl;
334        }    
335     }  
336     catch (...)
337     {
338        std::cout << "* UNDEFINED ERROR (not a bbtk nor a std exception)"<<std::endl;
339        if (mFileName.size()) {
340           std::cout << "* FILE  : \""<<mFileName.back()<<"\""<<std::endl;
341           std::cout << "* LINE  : "<<mLine.back()<<std::endl;
342       }    
343     }
344
345     CloseAllFiles();
346     bbtkDebugMessage("Interpreter",9,"EO Interpreter::InterpretFile(\""<<filename<<"\")"<<std::endl);
347     bbtkDecTab("Interpreter",9);
348
349     mCommandLine = exm;
350   }
351   //=======================================================================
352
353
354
355   //=======================================================================  
356   /**
357    *
358    */
359 void Interpreter::InterpretLine( const std::string& line, bool &insideComment )
360 {
361
362     bbtkDebugMessageInc("Interpreter",9,"Interpreter::InterpretLine(\""<<line<<"\")"<<std::endl);
363     bbtkMessage("Echo",2,"\""<<line<<"\""<<std::endl);
364
365     std::vector<std::string> words;
366     SplitLine(line,words);
367
368     // Empty line
369     if (words.size()<1) 
370     {
371        bbtkDebugDecTab("Interpreter",9);
372        return;
373     }
374
375     // Single line comment : # or //
376     if ( words[0][0]=='#' || (words[0][0]=='/' && words[0][1]=='/') ) 
377     {  
378        bbtkDebugDecTab("Interpreter",9);
379        bbtkMessage("Interpreter",9,"Comment"<<std::endl);
380        return;
381     }
382
383     // Multi line comment ( /* ... */ ) -delimiters on different lines !-
384     
385     if (words[0][0]=='/' && words[0][1]=='*') 
386     {  
387        bbtkDebugDecTab("Interpreter",9);
388        bbtkMessage("Interpreter",9,"In multiline comment"<<std::endl);
389        insideComment = true;
390        return;
391     }
392
393     if (words[0][0]=='*' && words[0][1]=='/') 
394     {  
395        bbtkDebugDecTab("Interpreter",9);
396        bbtkMessage("Interpreter",9,"Out multiline comment"<<std::endl);
397        if ( !insideComment ) {
398           bbtkDebugDecTab("Interpreter",9);
399           bbtkMessage("Interpreter",9,"Comment mismatch : '*/' with no matching '/*'"<<std::endl);       
400        }
401        insideComment = false;
402        return;
403     }
404
405     if (insideComment) 
406     {  
407        bbtkDebugDecTab("Interpreter",9);
408        bbtkMessage("Interpreter",9,"Multiline Comment"<<std::endl);
409        return;
410     }
411
412     // Command 
413     CommandInfoType command;
414     InterpretCommand(words,command);
415
416     bbtkDebugMessage("Interpreter",9,
417                      "Command='"<<command.keyword
418                       <<"' code="<<command.code<<std::endl); 
419     int level=0;
420     std::string left,right,left2,right2;
421     std::string filename;
422     switch (command.code) 
423     {
424       case cNew :
425         mExecuter->Create(words[1],words[2]);
426         break;
427         
428       case cDelete :
429         // TO DO !!
430         // mExecuter->Remove(words[1]);
431         break;
432         
433       case cConnect :
434         Utilities::SplitAroundFirstDot(words[1],left,right);
435         Utilities::SplitAroundFirstDot(words[2],left2,right2);      
436         mExecuter->Connect(left,right,left2,right2);
437         break;
438         
439       case cPackage :
440         mExecuter->BeginPackage(words[1]);
441         break;
442
443       case cEndPackage :
444         mExecuter->EndPackage();
445         break;
446         
447       case cDefine :
448         if (mFileName.size()>0) 
449         {
450            filename = Utilities::get_file_name(mFileName.back());
451         }
452         if (words.size()==2) 
453         {
454            mExecuter->Define(words[1],"",filename);
455         }
456         else 
457         {
458            mExecuter->Define(words[1],words[2],filename);
459         }
460         break;
461         
462       case cEndDefine :
463         mExecuter->EndDefine();
464         break;
465         
466       case cPrint :
467         Print(words[1]); /// \todo use mExecuter 
468         break;
469         
470       case cExec :
471         if (words[1]=="freeze") 
472           mExecuter->SetNoExecMode(true);
473         else if (words[1]=="unfreeze") 
474           mExecuter->SetNoExecMode(false);
475         else 
476           mExecuter->Update(words[1]);
477         break;
478         
479       case cInput :
480         Utilities::SplitAroundFirstDot(words[2],left,right);
481         mExecuter->DefineInput(words[1],left,right,words[3]);
482         break;
483         
484       case cOutput :
485         Utilities::SplitAroundFirstDot(words[2],left,right);
486         mExecuter->DefineOutput(words[1],left,right,words[3]);
487         break;
488         
489       case cSet :
490         Utilities::SplitAroundFirstDot(words[1],left,right);
491         mExecuter->Set(left,right,words[2]);
492         break;
493         
494       case cAuthor :
495         mExecuter->Author(words[1]);
496         break;
497         
498       case cDescription :
499         mExecuter->Description(words[1]);
500         break;
501         
502       case cHelp :
503         Help(words);
504         break;
505         
506       case cMessage : 
507         if (words.size()<3) 
508         {
509             bbtk::MessageManager::PrintInfo();
510         }
511         else 
512         {
513            sscanf(words[2].c_str(),"%d",&level);
514            bbtk::MessageManager::SetMessageLevel(words[1],level);
515         }
516         break;
517         
518       case cGraph : 
519         Graph(words);
520         break;
521         
522       case cConfig :
523         if (words.size()>1) // any param for config means verbose = true
524           verbose = true;
525         else
526           verbose = false;
527         Config(verbose);            
528         break;
529         
530       case cReset :  // EED
531         this->mExecuter->Reset();
532         break;
533         
534       case cInclude :
535         if (mCommandLine) 
536         {
537            InterpretFile(words[1], true, verbose); // true : better pass use_config_file
538         }
539         else 
540         {
541             SwitchToFile(words[1], true, verbose); // true : better pass use_config_file
542         }
543         break;
544         
545       case cLoad:
546         LoadPackage(words[1], true, verbose); // true : better pass use_config_file
547         break;
548         
549       case cUnload:
550         UnLoadPackage(words[1]);
551         break;
552         
553       case cQuit :
554         throw QuitException();
555         break;
556         
557          case cWorkspace :
558         if (words.size() == 2) 
559         {
560            if (words[1]=="freeze")        mExecuter->SetNoExecMode(true);
561            else if (words[1]=="unfreeze") mExecuter->SetNoExecMode(false);
562         }
563         else 
564         {
565            mExecuter->SetWorkspaceName(words[2]);
566         }
567         break;
568         
569       default:
570         bbtkInternalError("should not reach here !!!");
571    }
572     
573    bbtkDecTab("Interpreter",9);
574 }
575   //=======================================================================  
576
577
578
579   //=======================================================================
580   void SplitString ( const std::string& str, const std::string& delimiters, 
581                      std::vector<std::string>& tokens)
582   {
583     // Skip delimiters at beginning.
584     std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
585     // Find first delimiter.
586     std::string::size_type pos     = str.find_first_of(delimiters, lastPos);
587     
588     while (std::string::npos != pos || std::string::npos != lastPos)
589       {
590         // Found a token, add it to the vector.
591         tokens.push_back(str.substr(lastPos, pos - lastPos));
592         // Skip delimiters.  Note the "not_of"
593         lastPos = str.find_first_not_of(delimiters, pos);
594         // Find next delimiter
595         pos = str.find_first_of(delimiters, lastPos);
596       }
597     
598   }
599   //=======================================================================
600
601
602
603   //=======================================================================
604   /**
605    *  
606    */
607 void Interpreter::SplitLine ( const std::string& str, std::vector<std::string>& tokens)
608 {
609     bbtkDebugMessageInc("Interpreter",9,"Interpreter::SplitLine(\""<<str<<"\")"<<std::endl);
610  
611     std::string delimiters = "\"";
612     std::vector<std::string> quote;
613     SplitString(str,delimiters,quote);
614
615     delimiters = " \t";
616     std::vector<std::string>::iterator i;
617     for (i=quote.begin(); i!=quote.end(); ) 
618     {
619        SplitString(*i,delimiters,tokens);
620        ++i;
621        if (i!=quote.end()) 
622        {
623         //    bbtkDebugMessage("Interpreter",0,"\""<<*i<<"\""<<std::endl);
624           tokens.push_back(*i);
625           ++i;
626        }
627     }
628
629     for (i=tokens.begin(); i!=tokens.end(); ++i) 
630     {
631        bbtkDebugMessage("Interpreter",9,"["<<*i<<"] ");
632     }
633     bbtkDebugMessageCont("Interpreter",9,std::endl);
634
635     bbtkDebugDecTab("Interpreter",9);    
636  }
637   //=======================================================================
638
639
640   //=======================================================================
641   // Replaces substrings "\\n" by a real carriage return "\n"
642   void SubsBackslashN ( std::string& s )
643   {
644     //   std::cout << "BEFORE=["<<s<<"]"<<std::endl;
645     std::string ss("\\n");
646     std::string::size_type pos = 0;
647     pos = s.find(ss,0);
648     char* cr = "\n";
649     while ( pos != std::string::npos )
650    {
651       //  std::cout << "*** find one "<<std::endl;
652       s.replace(pos,2,cr,1);
653       pos = s.find(ss, pos-1);
654    } 
655     //    std::cout << "AFTER=["<<s<<"]"<<std::endl;
656   }
657   //=======================================================================
658
659
660   //=======================================================================
661   /**
662    *  
663    */
664   void Interpreter::Print( const std::string& str)
665   {
666     if (mExecuter->GetNoExecMode()) return;
667
668     bbtkDebugMessageInc("Interpreter",9,"Interpreter::SplitLine(\""<<str<<"\")"<<std::endl);
669
670     std::vector<std::string> chains;
671     std::string delimiters("$");
672
673     // Skip delimiters at beginning.
674     std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
675     bool is_text = true;
676     if (lastPos>0) is_text = false;
677   
678     // Find first delimiter.
679     std::string::size_type pos     = str.find_first_of(delimiters, lastPos);
680     
681     while (std::string::npos != pos || std::string::npos != lastPos)
682     {
683        if (is_text) 
684        {
685           // Found a text token, add it to the vector.
686           chains.push_back(str.substr(lastPos, pos - lastPos));
687           // std::cout << "text='"<<chains.back()<<"'"<<std::endl;
688        }
689        else 
690        {
691        // is an output (between $$) : decode 
692          std::string tok,box,output;
693          tok = str.substr(lastPos, pos - lastPos);
694          Utilities::SplitAroundFirstDot(tok,box,output);
695          chains.push_back( mExecuter->Get(box,output) );
696     //    std::cout << "outp='"<<chains.back()<<"'"<<std::endl;
697        }
698         // Skip delimiters.  Note the "not_of"
699        lastPos = str.find_first_not_of(delimiters, pos);
700         // Find next delimiter
701        pos = str.find_first_of(delimiters, lastPos);
702     //
703        is_text = !is_text;
704      }
705     //    std::cout << "nb="<<chains.size()<<std::endl;
706      std::vector<std::string>::iterator i;
707      for (i= chains.begin(); i!=chains.end(); ++i) 
708      {
709        //  bbtkMessage("Echo",1,*i);
710        SubsBackslashN(*i);
711        std::cout << *i;
712      }
713      std::cout << std::endl;
714      bbtkDebugDecTab("Interpreter",9);    
715  }
716
717   //=======================================================================
718   /**
719    *  
720    */
721
722 // ===================================================================================
723
724   void Interpreter::SwitchToFile( const std::string& name,
725                                   bool use_configuration_file, bool verbose)
726   {
727   // Note : in the following :
728   // name : the user supplied name 
729   //      - abreviated name    e.g.       scr   scr.bbs
730   //      - relative full name e.g.       ./scr.bbs   ../../scr.bbs 
731   //      - absolute full name e.g.       /home/usrname/proj/dir/scr.bbs
732   //          same for Windows, with      c:, d: ...
733   //
734     bbtkDebugMessageInc("Interpreter",9,"Interpreter::SwitchToFile( \""
735                          <<name<<"\")"<<std::endl);
736 verbose = true;
737     std::vector<std::string> script_paths;
738     std::string libname;  // full path library name
739     std::string pkgname;  // e.g. <scriptname>.bbs
740      
741     // Add the void path to handle absolute paths
742     script_paths.push_back("");
743     // Add the "." path to handle paths relative to current directory
744     script_paths.push_back(".");
745
746     pkgname = Utilities::ExtractScriptName(name);
747     if (use_configuration_file)
748     {
749       if (verbose)
750          std::cout << "look for : [" << name << "] (use_configuration_file == TRUE)"  << std::endl;
751       std::vector<std::string> paths = 
752         ConfigurationFile::GetInstance().Get_bbs_paths();
753       std::vector<std::string>::iterator i;
754       for (i=paths.begin();i!=paths.end();++i)
755         {
756           script_paths.push_back(*i); 
757         }
758   
759     }
760
761     bool fullnameGiven = false; 
762     bool foundFile     = false;
763     std::string::size_type slash_position = name.find_last_of("/\\");
764             
765     if (false) //slash_position != std::string::npos)
766     {       // ------------------------------------- check user supplied location 
767       fullnameGiven = true;
768       libname =  Utilities::ExpandLibName(name, verbose);
769
770       // allow user to always forget ".bbs"
771       int l = libname.size();
772       if (l>4) 
773       {       
774          if (libname.substr(l-4, 4) != ".bbs")
775          {
776             libname = libname + ".bbs";    
777          }
778       }
779       else
780       {
781          libname = libname + ".bbs";
782       }
783
784       if (libname != "") {
785         if ( Utilities::FileExists(libname))
786         {
787           foundFile = true;
788         }
789       }
790     }
791     else    // ------------------------------------- iterate on the paths
792     {
793       std::string path;
794       std::vector<std::string>::iterator i;
795       for (i=script_paths.begin();i!=script_paths.end();++i)
796       {
797         path = *i;
798
799        // we *really* want '.' to be the current working directory
800         if (path == ".") {
801           char buf[2048]; // for getcwd
802           char * currentDir = getcwd(buf, 2048);
803           std::string cwd(currentDir);
804           path = currentDir;
805         }
806
807         libname = Utilities::MakePkgnameFromPath(path, name); //pkgname);
808       // Check if library exists           
809         if ( ! Utilities::FileExists(libname) )
810         {
811         // The following is *NOT* a debug time message :
812         // It's a user intended message.
813         // Please don't remove it.
814           if (verbose)
815             std::cout <<"   [" <<libname <<"] : doesn't exist" <<std::endl;
816           continue;  // try next path
817         }
818         foundFile = true;
819         break; // a script was found; we stop iterating
820
821       } //------------------ // end for ( package_paths.begin();i!=package_paths.end() )
822     }
823
824     std::ifstream* s;
825
826     if (!foundFile)
827     {
828        if (fullnameGiven)
829          if(libname == "")
830             bbtkError("Path ["<<name<<"] doesn't exist");
831          else
832             bbtkError("Script ["<<libname<<"] not found");
833        else
834           bbtkError("No ["<<pkgname<<".bbs] script found");
835        return;    
836     }
837
838     bbtkMessage("Interpreter",1,pkgname<<" loaded"<<std::endl);
839     
840     s = new std::ifstream;
841     s->open(libname.c_str());
842     if (!s->good()) 
843     {
844         bbtkError("Could not open file ["<<libname<<"]");
845         return;     
846     }
847
848     if (verbose)
849        std::cout << "   -->[" << libname << "] found" << std::endl;
850
851     mFile.push_back(s);
852     mFileName.push_back(libname);
853     mLine.push_back(0);
854   }   
855    
856
857   //=======================================================================
858
859
860   //=======================================================================
861   /**
862    *  
863    */
864   void Interpreter::CloseCurrentFile()
865   {
866     bbtkDebugMessage("Interpreter",9,"Interpreter::CloseCurrentFile()"
867                       <<std::endl);
868     
869     if (mFile.size()==0) 
870     {
871       bbtkDebugMessage("Interpreter",9," -> no file left open"<<std::endl);
872       return;
873     }
874
875     mFile.back()->close();
876     delete mFile.back();
877     mFile.pop_back();
878     bbtkDebugMessage("Interpreter",9,
879                      " Closing file '"<<mFileName.back()<<"'"<<std::endl);
880     
881     mFileName.pop_back();
882     mLine.pop_back();
883     bbtkDebugMessage("Interpreter",9," Remains "
884                      <<mFile.size()
885                      <<" open"<<std::endl);
886     bbtkDebugMessage("Interpreter",9,"EO Interpreter::CloseCurrentFile()"
887                      <<std::endl);
888   }
889   //=======================================================================
890
891  //=======================================================================
892   /**
893    *  
894    */
895   void Interpreter::CloseAllFiles()
896   {
897     bbtkDebugMessage("Interpreter",9,"Interpreter::CloseAllFiles()"
898                       <<std::endl);
899     
900     while (mFile.size() != 0) 
901     {
902       mFile.back()->close();
903       delete mFile.back();
904       mFile.pop_back();
905       bbtkDebugMessage("Interpreter",9,
906                       " Closing file '"<<mFileName.back()<<"'"<<std::endl);
907       mFileName.pop_back();
908       mLine.pop_back();
909     }
910     bbtkDebugMessage("Interpreter",9,"EO Interpreter::CloseAllFiles()"
911                       <<std::endl);
912   }
913   //=======================================================================
914
915
916
917   //=======================================================================
918   /**
919    *  
920    */
921   void Interpreter::InterpretCommand( const std::vector<std::string>& words,
922                                       CommandInfoType& info )
923   {
924     bbtkDebugMessageInc("Interpreter",9,"Interpreter::InterpretCommand(...)"<<std::endl);
925     
926     // searches the command keyword
927     CommandDictType::iterator c;
928     c = mCommandDict.find(words[0]);
929     if ( c == mCommandDict.end() ) {
930       bbtkError(words[0]<<" : unknown command");
931     }
932
933     // tests the number of args 
934     if ( ( words.size()-1 < c->second.argmin ) ||
935          ( words.size()-1 > c->second.argmax ) )
936     {
937        HelpCommand(words[0]);
938        bbtkError(words[0]<<" : wrong number of arguments");
939     }
940
941     info = c->second;
942     bbtkDecTab("Interpreter",9);
943   }
944   //=======================================================================
945
946
947   //=======================================================================
948   /// Displays help on all the commands
949 void Interpreter::Help(const std::vector<std::string>& words)
950 {
951     unsigned int nbarg = words.size()-1;
952     
953     if (nbarg==0) 
954     {
955        HelpCommands();
956     }
957     else if (nbarg==1) 
958     {
959       if (words[1]=="packages") 
960       {
961          PrintPackages(true);
962          return;
963       }
964       try 
965       {
966           HelpCommand(words[1]);
967       }
968       catch (bbtk::Exception e) 
969       {
970          try 
971          {
972             HelpPackage(words[1]);
973          }
974          catch (bbtk::Exception f) 
975          {
976             try 
977             {
978                HelpBlackBox(words[1]);
979             }
980            catch (bbtk::Exception g) 
981            {
982               try
983               {
984                  this->mExecuter->ShowRelations(words[1],"0","9999");
985               }
986               catch (bbtk::Exception h){
987                 bbtkError("\""<<words[1].c_str()
988                           <<"\" is not a known command, package, black box type or black box name");
989               }
990            }
991          }
992        }
993     }
994     else if (nbarg==2) 
995     {
996       if (words[2]=="all")
997       {
998          if ( words[1]=="packages" )
999          {
1000             PrintPackages(true,true);
1001             return;
1002           }
1003          try 
1004          {
1005             HelpPackage(words[1],true);
1006          }
1007          catch (bbtk::Exception f) 
1008          {
1009          }
1010      }
1011      else 
1012      {
1013         HelpCommand(words[0]);
1014         bbtkError(words[0]<<" : syntax error");
1015      }
1016   }
1017   else 
1018   {
1019      bbtkError("Should not reach here !!!");
1020   }
1021 }
1022   //=======================================================================
1023
1024    //===================================================================    
1025   /// Displays the Configuration
1026   void Interpreter::Config( bool verbose ) const
1027   {
1028     bbtkDebugMessageInc("Core",9,"Factory::Config"<<std::endl);
1029     
1030     ConfigurationFile cf = ConfigurationFile::GetInstance();
1031     
1032     const std::string config_xml_full_path      = cf.Get_config_xml_full_path();    
1033     const std::string description               = cf.Get_description();
1034     const std::string url                       = cf.Get_url();
1035     const std::string data_path                 = cf.Get_data_path();
1036     const std::string default_doc_tmp           = cf.Get_default_doc_tmp();    
1037     const std::string file_separator            = cf.Get_file_separator();    
1038     const std::vector<std::string>bbs_paths     = cf.Get_bbs_paths();
1039     const std::vector<std::string>package_paths = cf.Get_package_paths();
1040     
1041     bbtkMessage("Help",1, "============="   << std::endl);           
1042     bbtkMessage("Help",1, "Configuration"   << std::endl);
1043     bbtkMessage("Help",1, "============="   << std::endl);
1044     bbtkMessage("Help",1, "bbtk_config.xml   : [" << config_xml_full_path  << "]" << std::endl); 
1045     bbtkMessage("Help",1, "Documentation Url : [" << url             << "]" << std::endl);
1046     bbtkMessage("Help",1, "Data Path         : [" << data_path       << "]" << std::endl);
1047     bbtkMessage("Help",1, "Default Doc_tmp   : [" << default_doc_tmp << "]" << std::endl);
1048     bbtkMessage("Help",1, "File Separator    : [" << file_separator  << "]" << std::endl);
1049
1050     std::vector<std::string>::const_iterator i;
1051            
1052     bbtkMessage("Help",1, "BBS Paths   " << std::endl);     
1053     for (i = bbs_paths.begin(); i!=bbs_paths.end(); ++i )
1054     {
1055       bbtkMessage("Help",1,"--- ["<<*i<<"]"<<std::endl);
1056     }    
1057     
1058     bbtkMessage("Help",1, "PACKAGE Paths : " << std::endl);     
1059     for (i = package_paths.begin(); i!=package_paths.end(); ++i )
1060     {
1061       bbtkMessage("Help",1,"--- ["<<*i<<"]"<<std::endl);
1062     }
1063
1064     bbtkDebugDecTab("Core",9);
1065   }  
1066
1067   //=======================================================================
1068   /// Displays help on all the commands
1069   void Interpreter::HelpCommands()
1070   {
1071     std::cout << "Available commands :" << std::endl;
1072     CommandDictType::iterator i;
1073     for ( i =  mCommandDict.begin();
1074           i != mCommandDict.end();
1075         ++i) {
1076               std::cout << " " << i->first << std::endl;
1077       //      std::cout << "   usage : " << i->second.syntax << std::endl;
1078       //     std::cout << "    " << i->second.help << std::endl;
1079  
1080     }
1081   }
1082   //=======================================================================
1083
1084
1085   //=======================================================================
1086   /// Displays help on a particular commands
1087   void Interpreter::HelpCommand(const std::string& s)
1088   {
1089     CommandDictType::iterator c;
1090     c = mCommandDict.find(s);
1091     if ( c == mCommandDict.end() ) {
1092       bbtkError(s<<" : Unknown command");
1093     }
1094     //    std::cout << " " << s << " : "<<  std::endl;
1095     //    CommandParamDictType::iterator i;
1096     //    for ( i =  c->second.begin();
1097     //      i != c->second.end();
1098     //      ++i) {
1099     std::cout << " usage : " << c->second.syntax << std::endl;
1100     std::cout << "  " << c->second.help << std::endl;
1101     
1102   }
1103   //=======================================================================
1104
1105
1106   //=======================================================================
1107   /// Fills the vector commands with the commands which 
1108   /// have the first n chars of buf for prefix
1109   /// TODO : skip initial spaces in buf and also return the position of first
1110   /// non blank char in buf
1111   void Interpreter::FindCommandsWithPrefix( char* buf,
1112                                             int n,
1113                                             std::vector<std::string>& commands )
1114   {
1115     CommandDictType::const_iterator i;
1116     for (i=mCommandDict.begin(); i!=mCommandDict.end(); ++i)
1117     {
1118       if ((i->first).find(buf,0,n) == 0) 
1119         commands.push_back(i->first);
1120     }
1121   }
1122   //=======================================================================
1123
1124
1125   
1126   //=======================================================================
1127 #ifdef BBTK_USE_TERMIOS_BASED_PROMPT
1128   
1129   inline void PrintChar(char c) { write(STDOUT_FILENO,&c,1); }
1130   inline void BackSpace() { write(STDOUT_FILENO,"\b \b",3); }
1131   
1132   // LG : KEYBOARD CODES AS SCANNED ON MY TTY : UNIVERSAL ?
1133   // IF NOT THE USER SHOULD BE ABLE TO CONFIGURE IT
1134   // E.G. STORE THIS IN bbtk_config.xml
1135 #define BBTK_UP_ARROW_KBCODE    0x00415B1B
1136 #define BBTK_DOWN_ARROW_KBCODE  0x00425B1B
1137 #define BBTK_RIGHT_ARROW_KBCODE 0x00435B1B
1138 #define BBTK_LEFT_ARROW_KBCODE  0x00445B1B
1139 #define BBTK_BACKSPACE_KBCODE   0x00000008
1140 #define BBTK_DEL_KBCODE         0x0000007F
1141 #define BBTK_SPACE_KBCODE       0x00000020 
1142
1143   //=======================================================================
1144   void Interpreter::GetLineFromPrompt(std::string& s)
1145   {
1146     int c;
1147     int ind=0;
1148     
1149     int MAX_LINE_SIZE = 160;
1150     int MAX_HISTORY_SIZE = 100;
1151     
1152     char* newline = new char[MAX_LINE_SIZE];
1153     memset(newline,0,MAX_LINE_SIZE);
1154     char* histline = new char[MAX_LINE_SIZE];
1155     memset(histline,0,MAX_LINE_SIZE);
1156     
1157     char* line = newline;
1158     int hist = mHistory.size();
1159     
1160     
1161     write(1,"> ",2);
1162     while(1)
1163     {
1164        c=0;
1165        read ( STDIN_FILENO, &c, 4) ;
1166
1167        bbtkDebugMessage("Debug",9,"[0x"<<std::hex<<c<<"]\n");
1168
1169        // Printable character
1170        if ( (ind<MAX_LINE_SIZE-1) &&
1171             ( c >= BBTK_SPACE_KBCODE ) && 
1172             ( c <  BBTK_DEL_KBCODE )) 
1173        {
1174           PrintChar(c);
1175           line[ind++]=c;
1176        }
1177       // CR
1178        else if (c=='\n')
1179        {
1180        // delete the unused line
1181           if (line==newline)
1182               delete histline;
1183           else
1184               delete newline;
1185    
1186     // empty lines are not stored in from history
1187           if (strlen(line)) 
1188           {
1189                 // if history too long : delete oldest command
1190                 if (mHistory.size()>MAX_HISTORY_SIZE) 
1191                   {
1192                     delete mHistory.front();
1193                     mHistory.pop_front();
1194                   }
1195                 mHistory.push_back(line);
1196           }
1197     
1198           break;
1199         }
1200         // Backspace
1201         else if ( (ind>0) && 
1202                   ((c == BBTK_BACKSPACE_KBCODE) ||
1203                    (c == BBTK_DEL_KBCODE)) )
1204           {
1205             line[ind--]=' ';
1206             BackSpace();
1207           }
1208         // Tab 
1209         else if (c=='\t')
1210           {
1211             // TODO : Command completion  
1212             std::vector<std::string> commands;
1213             FindCommandsWithPrefix( line,ind,commands);
1214             if (commands.size()==1) 
1215               {
1216                 std::string com = *commands.begin();
1217                 for (; ind<com.size(); ++ind) 
1218                   {
1219                     PrintChar(com[ind]); 
1220                     line[ind]=com[ind];
1221                   }
1222                 PrintChar(' '); 
1223                 line[ind++]=' ';
1224               }
1225             else if (commands.size()>1) 
1226               {
1227                 std::vector<std::string>::iterator i;
1228                 write(1,"\n",1);
1229                 for (i=commands.begin();i!=commands.end();++i) 
1230                   {
1231                     write(STDOUT_FILENO,(*i).c_str(),strlen((*i).c_str()));
1232                     PrintChar(' ');
1233                   }
1234                 write(STDOUT_FILENO,"\n> ",3);
1235                 //for (int j=0;j<ind;++j) 
1236                   //{
1237                     write(STDOUT_FILENO,line,ind); 
1238                     //  }
1239               }
1240           }
1241         // Arrow up : back in history
1242         else if (c==BBTK_UP_ARROW_KBCODE)
1243           {
1244             if (hist) 
1245               {
1246                 // erase current line
1247                 while (ind--) BackSpace();
1248                 // 
1249                 hist--;
1250                 // 
1251                 strcpy(histline,mHistory[hist]);
1252                 line = histline;
1253                 ind = strlen(line);
1254                 
1255                 write(STDOUT_FILENO,line,ind);
1256               }
1257           }
1258         // Arrow down : down in history
1259         else if (c==BBTK_DOWN_ARROW_KBCODE)
1260           {
1261             if (hist<mHistory.size()-1) 
1262               {
1263                 // erase current line
1264                 while (ind--) BackSpace();
1265                 // 
1266                 hist++;
1267                 // 
1268                 strcpy(histline,mHistory[hist]);
1269                 line = histline;
1270                 ind = strlen(line);
1271                 
1272                 write(STDOUT_FILENO,line,ind);
1273               }
1274             // end of history : switch back to newline
1275             else if (hist==mHistory.size()-1)
1276               {
1277                 // erase current line
1278                 while (ind--) BackSpace();
1279                 // 
1280                 hist++;
1281                 // 
1282                 line = newline;
1283                 ind = strlen(line);
1284                 
1285                 write(STDOUT_FILENO,line,ind);
1286               }
1287           }
1288         // Arrow right
1289         else if (line[ind]!=0 && c==BBTK_RIGHT_ARROW_KBCODE)
1290           {
1291             PrintChar(line[ind]);
1292             ind++;
1293           }
1294         
1295         // Arrow left
1296         else if (ind>0 && c==BBTK_LEFT_ARROW_KBCODE)
1297           {
1298             PrintChar('\b');
1299             ind--;
1300             
1301           }
1302
1303       }
1304     write(STDOUT_FILENO,"\n\r",2);
1305     
1306     
1307     s = line;
1308     
1309   }
1310 #else
1311
1312   //=======================================================================
1313   void Interpreter::GetLineFromPrompt(std::string& s)
1314   {  
1315     s.clear();
1316
1317     putchar('>');
1318     putchar(' ');
1319
1320     do 
1321     {
1322       char c = getchar();
1323       if (c=='\n') 
1324       {
1325         putchar('\n');
1326         break;
1327       }
1328       if (c=='\t') 
1329       {
1330         // putchar('T');
1331         continue;
1332       }
1333       // putchar(c);
1334       s += c;
1335     } 
1336     while (true);  
1337     
1338   }
1339   //=======================================================================  
1340
1341 #endif
1342
1343
1344
1345   //=======================================================================
1346   void Interpreter::CommandLineInterpreter()
1347   {
1348     bbtkDebugMessageInc("Interpreter",9,
1349                         "Interpreter::CommandLineInterpreter()"<<std::endl);
1350     
1351 #ifdef BBTK_USE_TERMIOS_BASED_PROMPT  
1352     // Initialise the tty in non canonical mode with no echo
1353     // oter remembers the previous settings to restore them after 
1354     struct termios ter,oter;
1355     tcgetattr(0,&ter);
1356     oter=ter;
1357     ter.c_lflag &= ~ECHO;
1358     ter.c_lflag &= ~ICANON;
1359     ter.c_cc[VMIN]=1;
1360     ter.c_cc[VTIME]=0;
1361     tcsetattr(0,TCSANOW,&ter);
1362 #endif
1363     
1364     mCommandLine = true;
1365     bool again = true;
1366     bool insideComment = false; // for multiline comment  
1367     do 
1368     {
1369       try
1370       {
1371         std::string line;
1372         GetLineFromPrompt(line);
1373         InterpretLine(line, insideComment);
1374       }
1375       catch (QuitException e)
1376       {
1377         again = false;
1378       }
1379       catch (bbtk::Exception e) 
1380       {
1381         e.Print();
1382       }
1383         catch (std::exception& e) 
1384       {
1385         std::cerr << "* ERROR :: "<<e.what()<<" (not in bbtk)"<<std::endl;
1386       }
1387       catch (...)
1388       {
1389         std::cerr << "* UNDEFINED ERROR (not a bbtk nor a std exception)"<<std::endl;
1390       }
1391     }
1392     while (again);
1393   
1394 #ifdef BBTK_USE_TERMIOS_BASED_PROMPT
1395     tcsetattr(0,TCSANOW,&oter);
1396 #endif
1397   
1398     std::cout << "Good bye !" << std::endl;
1399     
1400     bbtkDebugDecTab("Interpreter",9);
1401   }
1402
1403   //=======================================================================
1404   void Interpreter::Graph(const std::vector<std::string>& words)
1405   {
1406     std::string page;
1407     bool system_display = true;
1408
1409 #ifdef _USE_WXWIDGETS_
1410     if ( WxConsole::GetInstance() != 0 ) system_display = false; 
1411 #endif
1412  
1413     if (words.size()==1) 
1414     {
1415       page = mExecuter->ShowGraph(".","0","0","","","",system_display);
1416     }
1417     else if (words.size()==2) 
1418     {
1419       page = mExecuter->ShowGraph(words[1],"0","0","","","",system_display);
1420     }
1421     else if (words.size()==3) 
1422     {
1423       page = mExecuter->ShowGraph(words[1],words[2],"0","","","",system_display);
1424     }
1425     else if (words.size()==4) 
1426     {
1427       page = mExecuter->ShowGraph(words[1],words[2],words[3],"","","",system_display);
1428     } 
1429     else if (words.size()==5) 
1430     {
1431       page = mExecuter->ShowGraph(words[1],words[2],words[3],words[4],"","",system_display);
1432     } 
1433     else if (words.size()==6) 
1434     {
1435       page = mExecuter->ShowGraph(words[1],words[2],words[3],words[4],words[5],"",system_display);
1436     } 
1437     else if (words.size()==7) 
1438     {
1439       page = mExecuter->ShowGraph(words[1],words[2],words[3],words[4],words[5],words[6],system_display);
1440     } 
1441
1442 #ifdef _USE_WXWIDGETS_
1443     if ( WxConsole::GetInstance() != 0 )
1444       WxConsole::GetInstance()->ShowHtmlPage(page);
1445 #endif
1446   }
1447   //=======================================================================
1448
1449
1450 }//namespace
1451
1452