]> Creatis software - bbtk.git/blob - kernel/src/bbtkInterpreter.cxx
- Finished the Transcriptor
[bbtk.git] / kernel / src / bbtkInterpreter.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   bbtk
4   Module:    $RCSfile: bbtkInterpreter.cxx,v $ $
5   Language:  C++
6   Date:      $Date: 2008/03/26 14:47:36 $
7   Version:   $Revision: 1.57 $
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 "bbtkExecuter.h"
25 #include "bbtkTranscriptor.h"
26 #include "bbtkMessageManager.h"
27 #include "bbtkConfigurationFile.h"
28 #include "bbtkUtilities.h"
29 #include <sys/stat.h>
30 #include <algorithm>
31 #ifdef CMAKE_HAVE_TERMIOS_H
32 #include <termios.h>
33 #define BBTK_USE_TERMIOS_BASED_PROMPT
34 #endif
35
36 #include <string>
37
38 namespace bbtk
39 {
40
41   //Interpreter* Interpreter::mGlobalInterpreter = NULL;
42
43  //=======================================================================
44  /**
45    *
46    */
47   Interpreter::Interpreter(const std::string& cpp_file) 
48     :
49     mUser(0),
50     mCommandLine(false),
51     mThrow(false)
52   {
53 bufferNb =0;  
54     bbtk::MessageManager::RegisterMessageType("Echo","Level>0 : Prints the 'echo' commands of the user.\n\tLevel>1 : Prints the command being interpreted",1);
55     bbtk::MessageManager::RegisterMessageType("Interpreter","Messages of the interpreter",0);
56     bbtkDebugMessageInc("Interpreter",9,"Interpreter::Interpreter()" <<std::endl);
57  
58     if (cpp_file.size()!=0)
59       {
60         mExecuter = new bbtk::Transcriptor(cpp_file);
61       }
62     else 
63       {
64         mExecuter = new bbtk::Executer();
65       }
66     mExecuter->SetInterpreter(this);
67     
68     // For the time being, comment out previous line, and
69     // uncomment next line to check Transcriptor
70
71     //mExecuter = new bbtk::Transcriptor("GeneratedProgram.txt");
72
73     // Builds the commands dict
74     CommandInfoType info;
75    
76     info.category = "new";
77     info.argmin = 2;
78     info.argmax = 2;
79     info.code = cNew;
80     info.syntax = "new <type> <name>";
81     info.help = "Creates a new black box of type <type> with name <name>";
82     mCommandDict[info.category] = info;
83     
84     info.category = "delete";
85     info.argmin = 1;
86     info.argmax = 1;
87     info.code = cDelete;
88     info.syntax = "delete <box>";
89     info.help = "Deletes the black box of name <box>";
90     mCommandDict[info.category] = info;
91
92     info.category = "connect";
93     info.argmin = 2;
94     info.argmax = 2;
95     info.code = cConnect;
96     info.syntax = "connect <box1.output> <box2.input>";
97     info.help = "Connects the ouput <output> of black box <box1> to the input <input> of black box <box2>";
98     mCommandDict[info.category] = info;
99
100     info.category = "print";
101     info.argmin = 1;
102     info.argmax = 1;
103     info.code = cPrint;
104     info.syntax = "print <string>";
105     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').";
106     mCommandDict[info.category] = info;
107
108     info.category = "exec";
109     info.argmin = 1;
110     info.argmax = 2;
111     info.code = cExec;
112     info.syntax = "exec <box | 'freeze' | 'unfreeze' >";
113     info.help = "Executes the black box of name <box> (and connected boxes if needed). If the special category 'freeze' is given then freezes any further execution command. 'unfreeze' reverts to normal execution mode.";
114     mCommandDict[info.category] = info;
115
116     info.category = "package";
117     info.argmin = 1;
118     info.argmax = 1;
119     info.code = cPackage;
120     info.syntax = "package <name>";
121     info.help = "Begins the definition of a package.";
122     mCommandDict[info.category] = info;
123     
124     info.category = "endpackage";
125     info.argmin = 0;
126     info.argmax = 0;
127     info.code = cEndPackage;
128     info.syntax = "endpackage";
129     info.help = "Ends the definition of a package.";
130     mCommandDict[info.category] = info;
131
132     info.category = "define";
133     info.argmin = 1;
134     info.argmax = 2;
135     info.code = cDefine;
136     info.syntax = "define <type> [<package>]";
137     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.";
138     mCommandDict[info.category] = info;
139     
140     info.category = "endefine";
141     info.argmin = 0;
142     info.argmax = 0;
143     info.code = cEndDefine;
144     info.syntax = "endefine";
145     info.help = "Ends the definition of a new type of complex black box";
146     mCommandDict[info.category] = info;
147
148     info.category = "input";
149     info.argmin = 3;
150     info.argmax = 3;
151     info.code = cInput;
152     info.syntax = "input <name> <box.input> <help>";
153     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";
154     mCommandDict[info.category] = info;
155
156     info.category = "output";
157     info.argmin = 3;
158     info.argmax = 3;
159     info.code = cOutput;
160     info.syntax = "output <name> <box.output> <help>";
161     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";
162     mCommandDict[info.category] = info;
163
164     info.category = "set";
165     info.argmin = 2;
166     info.argmax = 2;
167     info.code = cSet;
168     info.syntax = "set <box.input> <value>";
169     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";
170     mCommandDict[info.category] = info;
171    
172     info.category = "config";  // JPR
173     info.argmin = 0;
174     info.argmax = 0;
175     info.code = cConfig;
176     info.syntax = "config";
177     info.help = "Prints the value of all configuration parameters";
178     mCommandDict[info.category] = info;
179
180     info.category = "index";  // LG
181     info.argmin = 0;
182     info.argmax = 2;
183     info.code = cIndex;
184
185     info.syntax = "index [<filename> ['Initials'(default)|'Packages'|'Categories'|'Adaptors']]";
186     info.help = "Creates an html index of known boxes. If filename is provided then save it to the file 'filename'. The default index entries are the initial letters of the names of the boxes. If 'Packages' or 'Categories' is provided then the entries are either the packages names or the categories. If 'Adaptors' is provided then an alphabetical index of all adaptors is created.";
187     mCommandDict[info.category] = info;
188
189     info.category = "reset";  
190     info.argmin = 0;
191     info.argmax = 0;
192     info.code = cReset;
193     info.syntax = "reset";
194     info.help = "Deletes all boxes and unloads all packages (bbi is reset to its start state)";
195     mCommandDict[info.category] = info;
196
197     info.category = "author";
198     info.argmin = 1;
199     info.argmax = 1;
200     info.code = cAuthor;
201     info.syntax = "author <string>";
202     info.help = "Adds the string <string> to the author information of the black box being defined";
203     mCommandDict[info.category] = info;
204     
205     info.category = "category"; //JP
206     info.argmin = 1;
207     info.argmax = 1;
208     info.code = cCategory;
209     info.syntax = "category <list of items, separated by ;>";
210     info.help = "Adds the string <string> to the category information of the black box being defined";
211     mCommandDict[info.category] = info;
212
213     info.category = "description";
214     info.argmin = 1;
215     info.argmax = 1;
216     info.code = cDescription;
217     info.syntax = "description <string>";
218     info.help = "Adds the string <string> to the descriptive information of the black box being defined";
219     mCommandDict[info.category] = info;
220
221     info.category = "help";
222     info.argmin = 0;
223     info.argmax = 2;
224     info.code = cHelp;
225     info.syntax = "help";
226     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>";
227     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.";
228     mCommandDict[info.category] = info;
229
230     info.category = "message";
231     info.argmin = 0;
232     info.argmax = 2;
233     info.code = cMessage;
234     info.syntax = "message <kind> <level>";
235     info.help = "Sets the level of the kind of messages <kind> to <level>.\n  If kind='All' then sets the level for all kinds. If no kind nor level is passed then prints info on available kinds of messages and their current level.";  mCommandDict[info.category] = info;
236
237     info.category = "include";
238     info.argmin = 1;
239     info.argmax = 2;
240     info.code = cInclude;
241     info.syntax = "include <filename> [source]";
242     info.help = "Includes the file <filename>.\n  'source' : If the keyword 'source' is provided then informs bbi that the included file is the source of the current box definition (Advanced; used to get the right 'Include' field in html doc of packages 'appli' scripts).";
243     mCommandDict[info.category] = info;
244
245     info.category = "quit";
246     info.argmin = 0;
247     info.argmax = 0;
248     info.code = cQuit;
249     info.syntax = "quit";
250     info.help = "Quits the program (during script execution it stops the complete execution)";
251     mCommandDict[info.category] = info;
252
253     info.category = "load";
254     info.argmin = 1;
255     info.argmax = 1;
256     info.code = cLoad;
257     info.syntax = "load <packagename>";
258     info.help = "Loads the black box package <packagename>";
259     mCommandDict[info.category] = info;
260
261     info.category = "unload";
262     info.argmin = 1;
263     info.argmax = 1;
264     info.code = cUnload;
265     info.syntax = "unload <packagename>";
266     info.help = "Unloads the black box package <packagename>";
267     mCommandDict[info.category] = info;
268
269     info.category = "graph";
270     info.argmin = 0;
271     info.argmax = 6;
272     info.code = cGraph;
273     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 ]]]]]]";
274     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')";
275     mCommandDict[info.category] = info;
276
277     /*
278     info.category = "workspace";
279     info.argmin = 1;
280     info.argmax = 2;
281     info.code = cWorkspace;
282     info.syntax = "workspace < ( freeze | unfreeze ) | ( rename <newname> ) >";
283     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.";
284     mCommandDict[info.category] = info;
285     */
286
287     bbtkDebugDecTab("Interpreter",9);
288
289   } 
290   //=======================================================================
291   
292   
293   
294   //=======================================================================  
295   /**
296    *  
297    */
298   Interpreter::~Interpreter()
299   {
300     bbtkDebugMessageInc("Interpreter",9,"Interpreter::~Interpreter()" <<std::endl);
301     delete mExecuter;
302
303     bbtkDebugDecTab("Interpreter",9);
304   }
305   //=======================================================================
306
307
308   InterpreterError::InterpreterError( const std::string& message,
309                                       bool in_script_file,
310                                       const std::string& script_file,
311                                       int script_line 
312                                       )
313     : Exception("Interpreter",0,message),
314       mInScriptFile(in_script_file),
315       mScriptFile(script_file),
316       mScriptLine(script_line)
317   {
318   }
319   InterpreterError::InterpreterError( const Exception& excep,
320                       bool in_script_file,
321                       const std::string& script_file,
322                       int script_line 
323                       )
324     : Exception(excep),
325       mInScriptFile(in_script_file),
326       mScriptFile(script_file),
327       mScriptLine(script_line)
328   {
329   }
330   //=======================================================================
331   void Interpreter::CatchBbtkException( const bbtk::Exception& e )
332   {
333     if (mThrow) 
334       {
335         bool in_script = false;
336         std::string file("");
337         int line = 0;
338         if (mFileName.size()) {
339           std::ifstream* fs = dynamic_cast<std::ifstream*>(mFile.back());
340           if (fs!=0) in_script = true;    
341           file = mFileName.back();
342           line = mLine.back();
343         }   
344         CloseAllFiles();
345         throw InterpreterError(e,in_script,file,line);
346       }
347     else
348       {
349         std::stringstream mess;
350         mess << "* ERROR : "<<e.GetMessage()<<std::endl;
351         if (mFileName.size()) {
352           mess << "* FILE  : \""<<mFileName.back()<<"\""<<std::endl;
353           mess << "* LINE  : "<<mLine.back()<<std::endl;
354         }    
355         std::cerr << mess.str();
356       }
357   }
358   //=======================================================================
359   
360   //=======================================================================
361   void Interpreter::CatchStdException( const std::exception& e )
362   {  
363     if (mThrow) 
364       {
365         bool in_script = false;
366         std::string file("");
367         int line = 0;
368         if (mFileName.size()) {
369           std::ifstream* fs = dynamic_cast<std::ifstream*>(mFile.back());
370           if (fs!=0) in_script = true;    
371           file = mFileName.back();
372           line = mLine.back();
373         }    
374         CloseAllFiles();
375         throw InterpreterError(e.what(),in_script,file,line);
376       }
377     else
378       {
379         std::stringstream mess;
380         mess << "* ERROR : "<<e.what()<<std::endl;
381         if (mFileName.size()) {
382           mess << "* FILE  : \""<<mFileName.back()<<"\""<<std::endl;
383           mess << "* LINE  : "<<mLine.back()<<std::endl;
384         }    
385         std::cerr << mess.str();
386       }
387   }
388   //=======================================================================
389
390   //=======================================================================
391   void Interpreter::CatchUnknownException()
392   {
393     if (mThrow) 
394       {
395         bool in_script = false;
396         std::string file("");
397         int line = 0;
398         if (mFileName.size()) {
399           std::ifstream* fs = dynamic_cast<std::ifstream*>(mFile.back());
400           if (fs!=0) in_script = true;    
401           file = mFileName.back();
402           line = mLine.back();
403         }    
404         CloseAllFiles();
405         throw InterpreterError("Unknown exception caught",
406                                    in_script,file,line);
407       }
408     else
409       {
410         std::stringstream mess;
411         mess << "* UNDEFINED ERROR (not a bbtk nor a std exception)" 
412              << std::endl;
413         if (mFileName.size()) {
414           mess << "* FILE  : \""<<mFileName.back()<<"\""<<std::endl;
415           mess << "* LINE  : "<<mLine.back()<<std::endl;
416         }    
417         std::cerr << mess.str();
418       }
419   }
420   //=======================================================================
421
422   //=======================================================================
423
424 #define CATCH_MACRO                             \
425   catch (QuitException e)                       \
426     {                                           \
427       status = Interpreter_QUIT;                \
428       if (mThrow) throw QuitException();        \
429     }                                           \
430   catch (bbtk::Exception e)                     \
431     {                                           \
432       status = Interpreter_ERROR;               \
433       CatchBbtkException(e);                    \
434     }                                           \
435   catch (std::exception& e)                     \
436     {                                           \
437       status = Interpreter_ERROR;               \
438       CatchStdException(e);                     \
439     }                                           \
440   catch (...)                                   \
441     {                                           \
442       status = Interpreter_ERROR;               \
443       CatchUnknownException();                  \
444     }                                           
445   //=======================================================================
446    
447
448   //=======================================================================
449   /**
450    *  
451    */
452   Interpreter::ExitStatus Interpreter::InterpretFile( const std::string& filename )
453   {
454     bbtkDebugMessageInc("Interpreter",9,"Interpreter::InterpretFile(\""<<filename<<"\")"<<std::endl);
455
456     bool exm = mCommandLine;
457     mCommandLine = false;
458
459     ExitStatus status = Interpreter_OK;
460
461     try 
462     {
463       SwitchToFile(filename);
464
465       bool insideComment = false; // for multiline comment
466       while (mFile.size()>0) 
467       {
468         while (!mFile.back()->eof()) {
469           mLine.back()++;
470           char buf[500];
471           mFile.back()->getline(buf,500);
472           std::string str(buf);
473           int size=str.length();
474           if ( str[ size-1 ]==13  )
475             {
476               str.erase(size-1,1);
477             }
478           
479           InterpretLine(str, insideComment);
480           
481        }//while
482        CloseCurrentFile();
483       }
484     }
485     CATCH_MACRO;
486     
487     CloseAllFiles();
488     bbtkDebugMessage("Interpreter",9,"EO Interpreter::InterpretFile(\""<<filename<<"\")"<<std::endl);
489     bbtkDecTab("Interpreter",9);
490
491     mCommandLine = exm;
492     
493     return status;
494   }
495   //=======================================================================
496
497
498   //=======================================================================
499   /**
500    *  
501    */
502   Interpreter::ExitStatus 
503   Interpreter::InterpretBuffer( std::stringstream* buffer )
504   {
505     bbtkDebugMessageInc("Interpreter",9,"Interpreter::InterpretBuffer()"<<std::endl);
506
507     bool exm = mCommandLine;
508     mCommandLine = false;
509
510     ExitStatus status = Interpreter_OK;
511
512     try 
513     {
514       SwitchToStream(buffer);
515       bool insideComment = false; // for multiline comment
516       while (mFile.size()>0) 
517       {
518         while (!mFile.back()->eof()) {
519           mLine.back()++;
520           char buf[500];
521           mFile.back()->getline(buf,500);
522           std::string str(buf);
523           
524           int size=str.length();
525           if ( str[ size-1 ]==13  )
526             {
527               str.erase(size-1,1);
528             }
529           
530           InterpretLine(str, insideComment);
531           
532         }//while
533         
534         CloseCurrentFile();
535       }
536     }
537     CATCH_MACRO;
538     
539     CloseAllFiles();
540     bbtkDebugMessage("Interpreter",9,"EO Interpreter::InterpretBuffer()"<<std::endl);
541     bbtkDecTab("Interpreter",9);
542     
543     mCommandLine = exm;
544     return status;
545   }
546   //=======================================================================
547
548   //=======================================================================
549   /// Runs the interpretation of a command
550   Interpreter::ExitStatus Interpreter::InterpretLine( const std::string& line )
551   {
552 printf("EED Interpreter::InterpretLine %s \n", line.c_str() );
553     bbtkDebugMessageInc("Interpreter",9,"Interpreter::InterpretLine('"<<line<<"')"<<std::endl);
554
555     ExitStatus status = Interpreter_OK;
556
557     try 
558     {
559       bool insideComment = false;
560       InterpretLine(line, insideComment);
561     }
562     CATCH_MACRO;
563     /*
564    catch (QuitException e) 
565       { 
566       status = Interpreter_QUIT;
567       }
568     catch (bbtk::Exception e) 
569       {
570         std::cerr << "* ERROR : "<<e.GetMessage()<<std::endl;
571         status = Interpreter_ERROR;
572       }
573     catch (std::exception& e) 
574       {
575         std::cerr << "* ERROR : "<<e.what()<<" (not in bbtk)"<<std::endl;
576         status = Interpreter_ERROR;
577     }  
578     catch (...)
579       {
580         std::cerr 
581           << "* UNDEFINED ERROR (not a bbtk nor a std exception)"<<std::endl;
582         status = Interpreter_ERROR;
583       }
584     */
585
586     bbtkDebugMessage("Interpreter",9,"EO Interpreter::InterpretLine()"
587                      <<std::endl);
588     bbtkDecTab("Interpreter",9);
589     
590     return status;
591   }
592   
593
594   //=======================================================================  
595   /**
596    *
597    */
598 void Interpreter::InterpretLine( const std::string& line, bool &insideComment )
599 {
600     bbtkDebugMessageInc("Interpreter",9,"Interpreter::InterpretLine(\""<<line<<"\")"<<std::endl);
601     bbtkMessage("Echo",2,"\""<<line<<"\""<<std::endl);
602
603     std::vector<std::string> words;
604     SplitLine(line,words);
605
606     // Empty line
607     if (words.size()<1) 
608     {
609        bbtkDebugDecTab("Interpreter",9);
610        return;
611     }
612
613     // Single line comment : # or //
614     if ( words[0][0]=='#' || (words[0][0]=='/' && words[0][1]=='/') ) 
615     {  
616        bbtkDebugDecTab("Interpreter",9);
617        bbtkMessage("Interpreter",9,"Comment"<<std::endl);
618        return;
619     }
620
621     // Multi line comment ( /* ... */ ) -delimiters on different lines !-
622     
623     if (words[0][0]=='/' && words[0][1]=='*') 
624     {  
625        bbtkDebugDecTab("Interpreter",9);
626        bbtkMessage("Interpreter",9,"In multiline comment"<<std::endl);
627        insideComment = true;
628        return;
629     }
630
631     if (words[0][0]=='*' && words[0][1]=='/') 
632     {  
633        bbtkDebugDecTab("Interpreter",9);
634        bbtkMessage("Interpreter",9,"Out multiline comment"<<std::endl);
635        if ( !insideComment ) {
636           bbtkDebugDecTab("Interpreter",9);
637           bbtkMessage("Interpreter",9,"Comment mismatch : '*/' with no matching '/*'"<<std::endl);       
638        }
639        insideComment = false;
640        return;
641     }
642
643     if (insideComment) 
644     {  
645        bbtkDebugDecTab("Interpreter",9);
646        bbtkMessage("Interpreter",9,"Multiline Comment"<<std::endl);
647        return;
648     }
649
650     // Command 
651     CommandInfoType command;
652     InterpretCommand(words,command);
653
654     bbtkDebugMessage("Interpreter",9,
655                      "Command='"<<command.category
656                       <<"' code="<<command.code<<std::endl); 
657     int level=0;
658     std::string left,right,left2,right2;
659     std::string filename;
660     switch (command.code) 
661     {
662       case cNew :
663         mExecuter->Create(words[1],words[2]);
664         break;
665
666       case cDelete :
667         mExecuter->Destroy(words[1]);
668         break;
669
670       case cConnect :
671         Utilities::SplitAroundFirstDot(words[1],left,right);
672         Utilities::SplitAroundFirstDot(words[2],left2,right2);      
673         mExecuter->Connect(left,right,left2,right2);
674         break;
675
676       case cPackage :
677         mExecuter->BeginPackage(words[1]);
678         break;
679
680       case cEndPackage :
681         mExecuter->EndPackage();
682         break;
683
684       case cDefine :
685         if (mFileName.size()>0) 
686         {
687           filename = mIncludeFileName.back(); //Utilities::get_file_name(mFileName.back());
688         }
689         if (words.size()==2) 
690         {
691            mExecuter->Define(words[1],"",filename);
692         }
693         else
694         {
695            mExecuter->Define(words[1],words[2],filename);
696         }
697         break;
698
699       case cEndDefine :
700         mExecuter->EndDefine();
701         break;
702
703       case cPrint :
704         mExecuter->Print(words[1]);
705         break;
706         
707       case cExec :
708         if (words[1]=="freeze") 
709           mExecuter->SetNoExecMode(true);
710         else if (words[1]=="unfreeze") 
711           mExecuter->SetNoExecMode(false);
712         else
713           mExecuter->Execute(words[1]);
714         break;
715
716       case cInput :
717         Utilities::SplitAroundFirstDot(words[2],left,right);
718         mExecuter->DefineInput(words[1],left,right,words[3]);
719         break;
720
721       case cOutput :
722         Utilities::SplitAroundFirstDot(words[2],left,right);
723         mExecuter->DefineOutput(words[1],left,right,words[3]);
724         break;
725
726       case cSet :
727         Utilities::SplitAroundFirstDot(words[1],left,right);
728         mExecuter->Set(left,right,words[2]);
729         break;
730
731       case cAuthor :
732         mExecuter->Author(words[1]);
733         break;
734
735       case cCategory :
736         mExecuter->Category(words[1]);
737         break;
738
739       case cIndex :
740         if (words.size()==1)
741              Index("tmp_index.html");
742         else if (words.size()==2)
743              Index(words[1]);
744         else if (words.size()==3)
745              Index(words[1],words[2]);
746         break;
747
748       case cDescription :
749         mExecuter->Description(words[1]);
750         break;
751
752       case cHelp :
753         Help(words);
754         break;
755
756       case cMessage : 
757         if (words.size()<3)
758         {
759           mExecuter->HelpMessages();
760         }
761         else
762         {
763           sscanf(words[2].c_str(),"%d",&level);
764           mExecuter->SetMessageLevel(words[1],level);
765         }
766         break;
767
768       case cGraph :
769         Graph(words);
770         break;
771
772       case cConfig :
773         Config();
774         break;
775
776       case cReset :  
777         this->mExecuter->Reset();
778         break;
779
780       case cInclude :
781         if (mCommandLine)
782         {
783            InterpretFile(words[1]); 
784         }
785         else
786         {
787             SwitchToFile(words[1]);
788         }
789         // if 'source' was given
790         if (words.size()==3) 
791           {
792             GetExecuter()->SetCurrentFileName(words[1]);
793           }
794         break;
795
796       case cLoad:
797         GetExecuter()->LoadPackage(words[1]);
798         break;
799
800       case cUnload:
801         GetExecuter()->UnLoadPackage(words[1]);
802         break;
803
804       case cQuit :
805         delete mExecuter;
806         throw QuitException();
807         break;
808         /* obsolete
809       case cWorkspace :
810         if (words.size() == 2) 
811         {
812            if (words[1]=="freeze")        mExecuter->SetNoExecMode(true);
813            else if (words[1]=="unfreeze") mExecuter->SetNoExecMode(false);
814         }
815         else
816         {
817            mExecuter->SetWorkspaceName(words[2]);
818         }
819         break;
820         */
821       default:
822         bbtkInternalError("should not reach here !!!");
823    }
824
825    bbtkDecTab("Interpreter",9);
826 }
827   //=======================================================================  
828
829
830
831
832
833   //=======================================================================
834   /**
835    *
836    */
837 void Interpreter::SplitLine ( const std::string& str, std::vector<std::string>& tokens)
838 {
839     bbtkDebugMessageInc("Interpreter",9,"Interpreter::SplitLine(\""<<str<<"\")"<<std::endl);
840
841     std::string delimiters = "\"";
842     std::vector<std::string> quote;
843     Utilities::SplitString(str,delimiters,quote);
844
845     delimiters = " \t";
846     std::vector<std::string>::iterator i;
847     for (i=quote.begin(); i!=quote.end(); ) 
848     {
849        Utilities::SplitString(*i,delimiters,tokens);
850        ++i;
851        if (i!=quote.end()) 
852        {
853         //    bbtkDebugMessage("Interpreter",0,"\""<<*i<<"\""<<std::endl);
854           tokens.push_back(*i);
855           ++i;
856        }
857     }
858
859     for (i=tokens.begin(); i!=tokens.end(); ++i) 
860     {
861        bbtkDebugMessage("Interpreter",9,"["<<*i<<"] ");
862     }
863     bbtkDebugMessageCont("Interpreter",9,std::endl);
864
865     bbtkDebugDecTab("Interpreter",9);
866  }
867   //=======================================================================
868
869
870  
871
872   //=======================================================================
873   /**
874    *
875    */
876   /*
877   void Interpreter::Print( const std::string& str)
878   {
879     if (mExecuter->GetNoExecMode()) return;
880
881     bbtkDebugMessageInc("Interpreter",9,"Interpreter::Print(\""<<str<<"\")"<<std::endl);
882
883  // TO DO :
884  // InterpretLine ("load std")
885  // InterpretLine("new ConcatStrings _C_ ") -> trouver un nom unique : # commande 
886  // InterpretLine("new Print _P_") 
887  // InterpretLine("connect _C_.Out _P_.In")
888  // int num = 1
889  
890
891     std::vector<std::string> chains;
892     std::string delimiters("$");
893
894     // Skip delimiters at beginning.
895     std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
896     bool is_text = true;
897     if (lastPos>0) is_text = false;
898
899     // Find first delimiter.
900     std::string::size_type pos     = str.find_first_of(delimiters, lastPos);
901
902     while (std::string::npos != pos || std::string::npos != lastPos)
903     {
904        if (is_text) 
905        {
906           // Found a text token, add it to the vector.
907           chains.push_back(str.substr(lastPos, pos - lastPos));
908  // std::string token = str.substr(lastPos, pos - lastPos)
909  // InterpretLine("set _C_.In%num% %token%")
910  
911        }
912        else 
913        {
914
915        // is an output (between $$) : decode 
916          std::string tok,box,output;
917          tok = str.substr(lastPos, pos - lastPos);
918          Utilities::SplitAroundFirstDot(tok,box,output);
919          chains.push_back( mExecuter->Get(box,output) );
920
921 // InterpretLine("connect %tok% _C_.In%num%") 
922
923        }
924         // Skip delimiters.  Note the "not_of"
925        lastPos = str.find_first_not_of(delimiters, pos);
926         // Find next delimiter
927        pos = str.find_first_of(delimiters, lastPos);
928     //
929        is_text = !is_text;
930 // num ++;
931      }
932 // InterpretLine("exec _P_")
933 // if (IS_IN_WORKSPACE) InterpretLine("delete _C_; delete _P_");
934
935      std::vector<std::string>::iterator i;
936      for (i= chains.begin(); i!=chains.end(); ++i) 
937      {
938        //  bbtkMessage("Echo",1,*i);
939        Utilities::SubsBackslashN(*i);
940        std::cout << *i;
941      }
942      std::cout << std::endl;
943      bbtkDebugDecTab("Interpreter",9);
944  }
945 */
946
947   //=======================================================================
948   /**
949    *
950    */
951
952 // ===================================================================================
953
954   void Interpreter::SwitchToFile( const std::string& name )
955   {
956   // Note : in the following :
957   // name : the user supplied name 
958   //      - abreviated name    e.g.       scr   scr.bbs
959   //      - relative full name e.g.       ./scr.bbs   ../../scr.bbs 
960   //      - absolute full name e.g.       /home/usrname/proj/dir/scr.bbs
961   //          same for Windows, with      c:, d: ...
962   //
963   // use ./directory/subdir/scrname.bbs
964   //
965
966     bbtkDebugMessageInc("Interpreter",9,"Interpreter::SwitchToFile( \""
967                          <<name<<"\")"<<std::endl);
968
969     std::vector<std::string> script_paths;
970     std::string fullPathScriptName;  // full path script name
971     std::string pkgname;             // e.g. <scriptname>.bbs
972     std::vector<std::string> Filenames;
973
974     // The following is *NOT* a debug time message :
975     // It's a user intended message.
976     // Please don't remove it.
977     bbtkMessage("Interpreter",1,
978         "look for : [" << name
979         << "]" << std::endl);
980
981
982     std::string upath;
983     pkgname = Utilities::ExtractScriptName(name,upath);
984
985     bbtkMessage("Interpreter",3,
986                 "extract : pkgname [" << pkgname
987                  << "] upath [" << upath << "]" << std::endl);
988     bool fullnameGiven = false; 
989     bool foundFile     = false;
990
991     // ==== "*" provided : load all scripts in given path 
992     // relative (e.g. std/boxes/*) or absolute 
993     if (pkgname == "*") 
994       {
995         int nbBssFiles;
996         
997         std::stringstream* stream = new std::stringstream;
998         //if (upath.size()!=0) // avoid troubles for "*"
999         
1000         // ==== no path provided : look in root bbs path
1001         if (upath.size()==0)
1002           {
1003             //      bbtkMessage("Interpreter",1,
1004             script_paths.push_back(  ConfigurationFile::GetInstance().Get_root_bbs_path() );
1005           }
1006         // ==== absolute path provided 
1007         else if (upath[0]=='/' || upath[1] == ':' ) 
1008           {
1009             if ( Utilities::IsDirectory( upath ) )
1010               {
1011                 script_paths.push_back(upath);
1012               }
1013             else 
1014               {
1015                 bbtkError("'"<<upath<<"' : directory does not exist"); 
1016               }
1017           }
1018         // ==== relative path provided : search all bbs path appended with 
1019         // the relative path provided
1020         else
1021           {    
1022              std::vector<std::string>::const_iterator i;
1023              for (i=ConfigurationFile::GetInstance().Get_bbs_paths().begin();
1024                   i!=ConfigurationFile::GetInstance().Get_bbs_paths().end();
1025                   i++)
1026                {
1027                  std::string full_path(*i);
1028                  // we *really* want '.' to be the current working directory
1029                  if (full_path == ".") {
1030                    char buf[2048]; // for getcwd
1031                    char * currentDir = getcwd(buf, 2048);
1032                    std::string cwd(currentDir);
1033                    full_path = currentDir;
1034                  } // if full_path
1035             
1036                  full_path += ConfigurationFile::GetInstance().Get_file_separator();
1037                  full_path += upath;
1038
1039                  if ( Utilities::IsDirectory( full_path ) )
1040                    {
1041                      script_paths.push_back(full_path);
1042                    }
1043                } 
1044              if (script_paths.empty())
1045                {
1046                  bbtkError("no '"<<upath<<"' subdir found in search paths" 
1047                            << std::endl);
1048                }
1049
1050           }
1051         // === search paths list complete : now explore it
1052
1053
1054         std::vector<std::string>::iterator i;
1055         for (i=script_paths.begin();i!=script_paths.end();i++)// ==== relative name, iterate + load all .bbs files
1056           {
1057             bbtkMessage("Interpreter",1,
1058                         "--> Looking in '" << *i << "'" << std::endl);
1059          
1060             
1061             Filenames.clear();
1062             int nbFiles = Utilities::Explore(*i, false, Filenames);
1063             
1064             nbBssFiles = 0;
1065             for (std::vector<std::string>::iterator j = Filenames.begin(); 
1066                  j!= Filenames.end(); ++j)
1067               {
1068                 int lgr = (*j).size();
1069                 if (lgr < 5)
1070                   continue;  // ignore non .bbs file
1071                 if ((*j).substr(lgr-4, 4) != ".bbs")
1072                   continue;
1073                 
1074                 (*stream) << "include \"" << *j << "\"\n";
1075                 bbtkMessage("Interpreter",2,
1076                             "  --> Found '" << *j << "'" << std::endl);
1077                 
1078                 nbBssFiles++;
1079               } // for vector
1080             
1081             if (nbBssFiles==0)
1082               {
1083                 bbtkMessage("Interpreter",1,
1084                             "  --> No .bbs found"<< std::endl);
1085               } 
1086             else 
1087               {
1088                 bbtkMessage("Interpreter",1,
1089                             "  --> "<<nbBssFiles<<" .bbs found"<< std::endl);
1090                 SwitchToStream(stream);
1091               }
1092             
1093             //break; // a directory was found; we stop iterating
1094             // LG : No! We want all files included !
1095           } // for vector
1096         return;
1097       }  
1098     //=============== end pkgname=="*" ===========
1099
1100
1101     // if name starts with a / or a . or contains : user is assumed to have passed a relative/absolute name
1102     // (not only a plain script name)
1103     // we trust him, and try to expland the directory name
1104     // WARNING : starting from current local directory :  ./whatYouWant  (./ mandatory!)
1105
1106     if (name[0]=='/' || name[1] == ':' || name[0]=='.')  // absolute path (linux/windows) or relative path
1107     { 
1108
1109       // ===========================================================check user supplied location
1110       fullnameGiven = true;
1111
1112       fullPathScriptName =  Utilities::ExpandLibName(name, false);
1113
1114       // allow user to always forget ".bbs"
1115       int l = fullPathScriptName.size();
1116
1117       if (l!=0) {
1118
1119       if (l>4)
1120       {
1121          if (fullPathScriptName.substr(l-4, 4) != ".bbs")
1122          {
1123             fullPathScriptName = fullPathScriptName + ".bbs";
1124          }
1125       }
1126       else
1127       {
1128          fullPathScriptName = fullPathScriptName + ".bbs";   
1129       }
1130
1131       if ( Utilities::FileExists(fullPathScriptName))
1132       {
1133         foundFile = true;
1134       }
1135     } // endif l != 0
1136   }
1137   else
1138     // =============================== iterate on the paths
1139     {
1140       script_paths = ConfigurationFile::GetInstance().Get_bbs_paths();
1141       std::string path;
1142       std::vector<std::string>::iterator i;
1143       for (i=script_paths.begin();i!=script_paths.end();++i)
1144         {
1145           path = *i;
1146           // we *really* want '.' to be the current working directory
1147           if (path == ".") {
1148             char buf[2048]; // for getcwd
1149             char * currentDir = getcwd(buf, 2048);
1150             std::string cwd(currentDir);
1151             path = currentDir;
1152           }
1153           
1154           fullPathScriptName = Utilities::MakePkgnameFromPath(path, name, true);
1155           
1156           // Check if library exists
1157           if ( ! Utilities::FileExists(fullPathScriptName) )
1158             {
1159               // The following is *NOT* a debug time message :
1160               // It's a user intended message.
1161               // Please don't remove it.
1162               bbtkMessage("Interpreter",2,
1163                           "   [" <<fullPathScriptName <<"] : doesn't exist" 
1164                           <<std::endl);
1165               continue;  // try next path
1166             }
1167           bbtkMessage("Interpreter",2,
1168                       "   [" <<fullPathScriptName 
1169                       <<"] : found" <<std::endl);
1170           foundFile = true;
1171           break; // a script was found; we stop iterating
1172
1173         } //------------------ // end for ( package_paths.begin();i!=package_paths.end() )
1174     }
1175     
1176     if (!foundFile)
1177       {
1178         if (fullnameGiven)
1179           if(fullPathScriptName == "")
1180             bbtkError("Path ["<<upath<<"] doesn't exist");
1181           else
1182             bbtkError("Script ["<<fullPathScriptName<<"] not found");
1183         else
1184           bbtkError("No ["<<pkgname<<".bbs] script found");
1185         return;
1186       }
1187     else
1188       LoadScript(fullPathScriptName,name);
1189     
1190     return;
1191   }
1192   //=======================================================================
1193
1194
1195   //=======================================================================
1196 void Interpreter::SwitchToStream( std::stringstream* stream )
1197 {
1198     mFile.push_back(stream);
1199     std::ostringstream buffer_name;
1200     bufferNb++;
1201     buffer_name << "buffer_" ;
1202
1203     if (mFileName.size()>0 )
1204     {
1205        buffer_name << mFileName.back() << "_" << mLine.back();
1206     }
1207     mFileName.push_back(buffer_name.str());
1208     mIncludeFileName.push_back(buffer_name.str());
1209     mLine.push_back(0);
1210 }
1211   //=======================================================================
1212
1213   //=======================================================================
1214
1215   void Interpreter::LoadScript( std::string fullPathScriptName,
1216                                 std::string includeScriptName)
1217   {
1218      Utilities::replace( fullPathScriptName , INVALID_FILE_SEPARATOR , VALID_FILE_SEPARATOR);
1219
1220      bool okScriptExist=false;
1221      int iStrScript,sizeVecStricpt=mFileName.size();
1222      for ( iStrScript=0;iStrScript<sizeVecStricpt;iStrScript++)
1223      {
1224         if (mFileName[iStrScript] == fullPathScriptName )
1225         {
1226            okScriptExist=true;
1227         } // if
1228      } // for
1229
1230      if (find(mFileName.begin(),mFileName.end(),fullPathScriptName)!=mFileName.end())
1231 //    if (okScriptExist==true)
1232      {
1233             bbtkMessage("Interpreter",1,"file '"<<fullPathScriptName
1234                     <<"' already open : I do not open it once more to prevent recursive inclusion"<<std::endl);
1235         return;
1236      }
1237
1238     std::ifstream* s;
1239     s = new std::ifstream;
1240     s->open(fullPathScriptName.c_str());
1241     if (!s->good())
1242     {
1243         bbtkError("Could not open file ["<<fullPathScriptName<<"]");
1244         return;
1245     }
1246
1247     bbtkMessage("Interpreter",1,"   -->[" << fullPathScriptName 
1248                 << "] found" << std::endl);
1249
1250     mFile.push_back(s);
1251     mFileName.push_back(fullPathScriptName);
1252     mIncludeFileName.push_back(includeScriptName);
1253     mLine.push_back(0);
1254
1255     return;
1256   }
1257
1258   //=======================================================================
1259   /**
1260    *  
1261    */
1262   void Interpreter::CloseCurrentFile()
1263   {
1264     bbtkDebugMessage("Interpreter",9,"Interpreter::CloseCurrentFile()"
1265                       <<std::endl);
1266
1267     if (mFile.size()==0)
1268     {
1269       bbtkDebugMessage("Interpreter",9," -> no file left open"<<std::endl);
1270       return;
1271     }
1272
1273     bbtkDebugMessage("Interpreter",9," Closing file '"<<mFileName.back()<<"'"<<std::endl);
1274
1275     std::ifstream* file = dynamic_cast<std::ifstream*>(mFile.back());
1276     if (file!=0) file->close();
1277
1278     delete mFile.back();
1279     mFile.pop_back();
1280     mFileName.pop_back();
1281     mIncludeFileName.pop_back();
1282     mLine.pop_back();
1283
1284     bbtkDebugMessage("Interpreter",9," Remains "
1285                      <<mFile.size()
1286                      <<" open"<<std::endl);
1287     bbtkDebugMessage("Interpreter",9,"EO Interpreter::CloseCurrentFile()"
1288                      <<std::endl);
1289   }
1290   //=======================================================================
1291
1292  //=======================================================================
1293   /**
1294    *  
1295    */
1296   void Interpreter::CloseAllFiles()
1297   {
1298     bbtkDebugMessage("Interpreter",9,"Interpreter::CloseAllFiles()"
1299                       <<std::endl);
1300
1301     while (mFile.size() != 0) 
1302     {
1303        CloseCurrentFile();
1304     /*
1305       mFile.back()->close();
1306       delete mFile.back();
1307       mFile.pop_back();
1308       bbtkDebugMessage("Interpreter",9,
1309                       " Closing file '"<<mFileName.back()<<"'"<<std::endl);
1310       mFileName.pop_back();
1311       mIncludeFileName.pop_back();
1312       mLine.pop_back();
1313 */
1314     }
1315     bbtkDebugMessage("Interpreter",9,"EO Interpreter::CloseAllFiles()"
1316                       <<std::endl);
1317   }
1318   //=======================================================================
1319
1320
1321
1322   //=======================================================================
1323   /**
1324    *  
1325    */
1326   void Interpreter::InterpretCommand( const std::vector<std::string>& words,
1327                                       CommandInfoType& info )
1328   {
1329     bbtkDebugMessageInc("Interpreter",9,"Interpreter::InterpretCommand(...)"<<std::endl);
1330
1331     // searches the command category
1332     CommandDictType::iterator c;
1333     c = mCommandDict.find(words[0]);
1334     if ( c == mCommandDict.end() ) {
1335       bbtkError(words[0]<<" : unknown command");
1336     }
1337
1338     // tests the number of args 
1339     if ( ( words.size()-1 < c->second.argmin ) ||
1340          ( words.size()-1 > c->second.argmax ) )
1341     {
1342        HelpCommand(words[0]);
1343        bbtkError(words[0]<<" : wrong number of arguments");
1344     }
1345
1346     info = c->second;
1347     bbtkDecTab("Interpreter",9);
1348   }
1349   //=======================================================================
1350
1351
1352   //=======================================================================
1353   /// Displays help on all the commands
1354 void Interpreter::Help(const std::vector<std::string>& words)
1355 {
1356     unsigned int nbarg = words.size()-1;
1357
1358     if (nbarg==0) 
1359     {
1360        HelpCommands();
1361     }
1362     else if (nbarg==1) 
1363     {
1364       if (words[1]=="packages") 
1365       {
1366          GetExecuter()->GetFactory()->PrintPackages(true);
1367          return;
1368       }
1369       try 
1370       {
1371           HelpCommand(words[1]);
1372       }
1373       catch (bbtk::Exception e) 
1374       {
1375          try 
1376          {
1377             GetExecuter()->GetFactory()->HelpPackage(words[1]);
1378             if ( mUser != 0 )
1379               {
1380                 std::string url = 
1381                   ConfigurationFile::GetInstance().Get_doc_path();
1382                 url += "/bbdoc/" + words[1] + "/index.html";
1383                 if (Utilities::FileExists(url)) 
1384                   {
1385                     mUser->InterpreterUserViewHtmlPage(url);
1386                   }
1387               }
1388          }
1389          catch (bbtk::Exception f) 
1390          {
1391            try 
1392              {
1393                std::string package;
1394                GetExecuter()->GetFactory()->HelpBlackBox(words[1],package);
1395                if ( mUser != 0 )
1396                  {
1397                    std::string url = 
1398                      ConfigurationFile::GetInstance().Get_doc_path();
1399                    url += "/bbdoc/" + package + "/index.html";
1400                    if (Utilities::FileExists(url)) 
1401                      {
1402                        url += "#" + words[1];
1403                        mUser->InterpreterUserViewHtmlPage(url);
1404                      }
1405                  }
1406              }
1407            catch (bbtk::Exception g) 
1408              {
1409                try
1410                  {
1411                    GetExecuter()->ShowRelations(words[1],"0","9999");
1412                  }
1413                catch (bbtk::Exception h){
1414                  bbtkError("\""<<words[1].c_str()
1415                            <<"\" is not a known command, package, black box type or black box name");
1416                }
1417              }
1418          }
1419       }
1420     }
1421     else if (nbarg==2) 
1422     {
1423       if (words[2]=="all")
1424       {
1425          if ( words[1]=="packages" )
1426          {
1427             GetExecuter()->GetFactory()->PrintPackages(true,true);
1428             return;
1429           }
1430          try 
1431          {
1432             GetExecuter()->GetFactory()->HelpPackage(words[1],true);
1433          }
1434          catch (bbtk::Exception f) 
1435          {
1436          }
1437      }
1438      else 
1439      {
1440         HelpCommand(words[0]);
1441         bbtkError(words[0]<<" : syntax error");
1442      }
1443   }
1444   else 
1445   {
1446      bbtkError("Should not reach here !!!");
1447   }
1448 }
1449   //=======================================================================
1450
1451    //===================================================================    
1452   /// Displays the Configuration
1453   void Interpreter::Config() const
1454   {
1455     ConfigurationFile::GetInstance().GetHelp(1);
1456   }  
1457    //===================================================================    
1458
1459   //=======================================================================
1460   /// Displays help on all the commands
1461   void Interpreter::HelpCommands()
1462   {
1463     std::cout << "Available commands :" << std::endl;
1464     CommandDictType::iterator i;
1465     for ( i =  mCommandDict.begin();
1466           i != mCommandDict.end();
1467         ++i) {
1468               std::cout << " " << i->first << std::endl;
1469       //      std::cout << "   usage : " << i->second.syntax << std::endl;
1470       //     std::cout << "    " << i->second.help << std::endl;
1471
1472     }
1473   }
1474   //=======================================================================
1475
1476
1477   //=======================================================================
1478   /// Displays help on a particular commands
1479   void Interpreter::HelpCommand(const std::string& s)
1480   {
1481     CommandDictType::iterator c;
1482     c = mCommandDict.find(s);
1483     if ( c == mCommandDict.end() ) {
1484       bbtkError(s<<" : Unknown command");
1485     }
1486     //    std::cout << " " << s << " : "<<  std::endl;
1487     //    CommandParamDictType::iterator i;
1488     //    for ( i =  c->second.begin();
1489     //      i != c->second.end();
1490     //      ++i) {
1491     std::cout << " usage : " << c->second.syntax << std::endl;
1492     std::cout << "  " << c->second.help << std::endl;
1493
1494   }
1495   //=======================================================================
1496
1497
1498   //=======================================================================
1499   /// Fills the vector commands with the commands which 
1500   /// have the first n chars of buf for prefix
1501   /// TODO : skip initial spaces in buf and also return the position of first
1502   /// non blank char in buf
1503   void Interpreter::FindCommandsWithPrefix( char* buf,
1504                                             int n,
1505                                             std::vector<std::string>& commands )
1506   {
1507     CommandDictType::const_iterator i;
1508     for (i=mCommandDict.begin(); i!=mCommandDict.end(); ++i)
1509     {
1510       if ((i->first).find(buf,0,n) == 0) 
1511         commands.push_back(i->first);
1512     }
1513   }
1514   //=======================================================================
1515
1516
1517
1518   //=======================================================================
1519 #ifdef BBTK_USE_TERMIOS_BASED_PROMPT
1520   
1521   inline void PrintChar(char c) { write(STDOUT_FILENO,&c,1); }
1522   inline void BackSpace() { write(STDOUT_FILENO,"\b \b",3); }
1523   
1524   // LG : KEYBOARD CODES AS SCANNED ON MY TTY : UNIVERSAL ?
1525   // IF NOT THE USER SHOULD BE ABLE TO CONFIGURE IT
1526   // E.G. STORE THIS IN bbtk_config.xml
1527 #define BBTK_UP_ARROW_KBCODE    0x00415B1B
1528 #define BBTK_DOWN_ARROW_KBCODE  0x00425B1B
1529 #define BBTK_RIGHT_ARROW_KBCODE 0x00435B1B
1530 #define BBTK_LEFT_ARROW_KBCODE  0x00445B1B
1531 #define BBTK_BACKSPACE_KBCODE   0x00000008
1532 #define BBTK_DEL_KBCODE         0x0000007F
1533 #define BBTK_SPACE_KBCODE       0x00000020 
1534
1535   //=======================================================================
1536   void Interpreter::GetLineFromPrompt(std::string& s)
1537   {
1538     int c;
1539     int ind=0;
1540
1541     int MAX_LINE_SIZE = 160;
1542     int MAX_HISTORY_SIZE = 100;
1543
1544     char* newline = new char[MAX_LINE_SIZE];
1545     memset(newline,0,MAX_LINE_SIZE);
1546     char* histline = new char[MAX_LINE_SIZE];
1547     memset(histline,0,MAX_LINE_SIZE);
1548
1549     char* line = newline;
1550     int hist = mHistory.size();
1551
1552     write(1,"> ",2);
1553     while(1)
1554     {
1555        c=0;
1556        read ( STDIN_FILENO, &c, 4) ;
1557
1558        bbtkDebugMessage("Debug",9,"[0x"<<std::hex<<c<<"]\n");
1559
1560        // Printable character
1561        if ( (ind<MAX_LINE_SIZE-1) &&
1562             ( c >= BBTK_SPACE_KBCODE ) && 
1563             ( c <  BBTK_DEL_KBCODE )) 
1564        {
1565           PrintChar(c);
1566           line[ind++]=c;
1567        }
1568       // CR
1569        else if (c=='\n')
1570        {
1571        // delete the unused line
1572           if (line==newline)
1573               delete histline;
1574           else
1575               delete newline;
1576    
1577     // empty lines are not stored in from history
1578           if (strlen(line)) 
1579           {
1580              // if history too long : delete oldest command
1581              if (mHistory.size()>MAX_HISTORY_SIZE) 
1582              {
1583                 delete mHistory.front();
1584                 mHistory.pop_front();
1585              }
1586              mHistory.push_back(line);
1587           }
1588           break;
1589         }
1590        // Backspace
1591         else if ( (ind>0) && 
1592                   ((c == BBTK_BACKSPACE_KBCODE) ||
1593                    (c == BBTK_DEL_KBCODE)) )
1594           {
1595             line[ind--]=' ';
1596             BackSpace();
1597           }
1598         // Tab 
1599         else if (c=='\t')
1600           {
1601             // TODO : Command completion  
1602             std::vector<std::string> commands;
1603             FindCommandsWithPrefix( line,ind,commands);
1604             if (commands.size()==1) 
1605               {
1606                 std::string com = *commands.begin();
1607                 for (; ind<com.size(); ++ind) 
1608                   {
1609                     PrintChar(com[ind]); 
1610                     line[ind]=com[ind];
1611                   }
1612                 PrintChar(' '); 
1613                 line[ind++]=' ';
1614               }
1615             else if (commands.size()>1) 
1616               {
1617                 std::vector<std::string>::iterator i;
1618                 write(1,"\n",1);
1619                 for (i=commands.begin();i!=commands.end();++i) 
1620                   {
1621                     write(STDOUT_FILENO,(*i).c_str(),strlen((*i).c_str()));
1622                     PrintChar(' ');
1623                   }
1624                 write(STDOUT_FILENO,"\n> ",3);
1625                 //for (int j=0;j<ind;++j) 
1626                   //{
1627                     write(STDOUT_FILENO,line,ind); 
1628                     //  }
1629               }
1630           }
1631         // Arrow up : back in history
1632         else if (c==BBTK_UP_ARROW_KBCODE)
1633           {
1634             if (hist) 
1635               {
1636                 // erase current line
1637                 while (ind--) BackSpace();
1638                 // 
1639                 hist--;
1640                 // 
1641                 strcpy(histline,mHistory[hist]);
1642                 line = histline;
1643                 ind = strlen(line);
1644                 
1645                 write(STDOUT_FILENO,line,ind);
1646               }
1647           }
1648         // Arrow down : down in history
1649         else if (c==BBTK_DOWN_ARROW_KBCODE)
1650           {
1651             if (hist<mHistory.size()-1) 
1652               {
1653                 // erase current line
1654                 while (ind--) BackSpace();
1655                 // 
1656                 hist++;
1657                 // 
1658                 strcpy(histline,mHistory[hist]);
1659                 line = histline;
1660                 ind = strlen(line);
1661                 
1662                 write(STDOUT_FILENO,line,ind);
1663               }
1664             // end of history : switch back to newline
1665             else if (hist==mHistory.size()-1)
1666               {
1667                 // erase current line
1668                 while (ind--) BackSpace();
1669                 // 
1670                 hist++;
1671                 // 
1672                 line = newline;
1673                 ind = strlen(line);
1674                 
1675                 write(STDOUT_FILENO,line,ind);
1676               }
1677           }
1678         // Arrow right
1679         else if (line[ind]!=0 && c==BBTK_RIGHT_ARROW_KBCODE)
1680           {
1681             PrintChar(line[ind]);
1682             ind++;
1683           }
1684
1685         // Arrow left
1686         else if (ind>0 && c==BBTK_LEFT_ARROW_KBCODE)
1687           {
1688             PrintChar('\b');
1689             ind--;
1690     
1691           }
1692
1693       }
1694     write(STDOUT_FILENO,"\n\r",2);
1695     
1696     
1697     s = line;
1698     
1699   }
1700 #else
1701
1702   //=======================================================================
1703   void Interpreter::GetLineFromPrompt(std::string& s)
1704   {  
1705     s.clear();
1706
1707     putchar('>');
1708     putchar(' ');
1709
1710     do 
1711     {
1712       char c = getchar();
1713       if (c=='\n') 
1714       {
1715         putchar('\n');
1716         break;
1717       }
1718       if (c=='\t') 
1719       {
1720         // putchar('T');
1721         continue;
1722       }
1723       // putchar(c);
1724       s += c;
1725     } 
1726     while (true);  
1727     
1728   }
1729   //=======================================================================  
1730
1731 #endif
1732
1733
1734
1735   //=======================================================================
1736   void Interpreter::CommandLineInterpreter()
1737   {
1738     bbtkDebugMessageInc("Interpreter",9,
1739                         "Interpreter::CommandLineInterpreter()"<<std::endl);
1740
1741 #ifdef BBTK_USE_TERMIOS_BASED_PROMPT  
1742     // Initialise the tty in non canonical mode with no echo
1743     // oter remembers the previous settings to restore them after 
1744     struct termios ter,oter;
1745     tcgetattr(0,&ter);
1746     oter=ter;
1747     ter.c_lflag &= ~ECHO;
1748     ter.c_lflag &= ~ICANON;
1749     ter.c_cc[VMIN]=1;
1750     ter.c_cc[VTIME]=0;
1751     tcsetattr(0,TCSANOW,&ter);
1752 #endif
1753     
1754     mCommandLine = true;
1755     bool again = true;
1756     bool insideComment = false; // for multiline comment  
1757     do 
1758     {
1759       try
1760       {
1761         std::string line;
1762         GetLineFromPrompt(line);
1763         InterpretLine(line, insideComment);
1764       }
1765       catch (QuitException e)
1766       {
1767         bbtkMessage("Interpreter",1,"Interpreter : Quit"<<std::endl);
1768         again = false;
1769       }
1770       catch (bbtk::Exception e) 
1771       {
1772         e.Print();
1773       }
1774         catch (std::exception& e) 
1775       {
1776         std::cerr << "* ERROR :: "<<e.what()<<" (not in bbtk)"<<std::endl;
1777       }
1778       catch (...)
1779       {
1780         std::cerr << "* UNDEFINED ERROR (not a bbtk nor a std exception)"<<std::endl;
1781       }
1782     }
1783     while (again);
1784
1785 #ifdef BBTK_USE_TERMIOS_BASED_PROMPT
1786     tcsetattr(0,TCSANOW,&oter);
1787 #endif
1788
1789     std::cout << "Good bye !" << std::endl;
1790
1791     bbtkDebugDecTab("Interpreter",9);
1792   }
1793
1794 //=======================================================================
1795 void Interpreter::Graph(const std::vector<std::string>& words)
1796 {
1797   std::string page;
1798     bool system_display = true;
1799
1800     if ( ( mUser != 0 ) && ( mUser->InterpreterUserHasOwnHtmlPageViewer() ) )
1801       system_display = false; 
1802  
1803     if (words.size()==1) 
1804     {
1805       page = mExecuter->ShowGraph(".","0","0","","","",system_display);
1806     }
1807     else if (words.size()==2) 
1808     {
1809       page = mExecuter->ShowGraph(words[1],"0","0","","","",system_display);
1810     }
1811     else if (words.size()==3) 
1812     {
1813       page = mExecuter->ShowGraph(words[1],words[2],"0","","","",system_display);
1814     }
1815     else if (words.size()==4) 
1816     {
1817       page = mExecuter->ShowGraph(words[1],words[2],words[3],"","","",system_display);
1818     } 
1819     else if (words.size()==5) 
1820     {
1821       page = mExecuter->ShowGraph(words[1],words[2],words[3],words[4],"","",system_display);
1822     } 
1823     else if (words.size()==6) 
1824     {
1825       page = mExecuter->ShowGraph(words[1],words[2],words[3],words[4],words[5],"",system_display);
1826     } 
1827     else if (words.size()==7) 
1828       {
1829         page = mExecuter->ShowGraph(words[1],words[2],words[3],words[4],words[5],words[6],system_display);
1830       } 
1831     
1832     if ( ( mUser != 0 ) && ( mUser->InterpreterUserHasOwnHtmlPageViewer() ) )
1833       mUser->InterpreterUserViewHtmlPage(page);
1834
1835   }
1836 //=======================================================================
1837
1838
1839 //=======================================================================
1840 void  Interpreter::Index(const std::string& filename, 
1841                          const std::string& type)
1842 {
1843   Factory::IndexEntryType t;
1844   if (type=="Initials") t = Factory::Initials;
1845   else if (type=="Categories") t = Factory::Categories;
1846   else if (type=="Packages") t = Factory::Packages;
1847   else if (type=="Adaptors") t = Factory::Adaptors;
1848   
1849   GetExecuter()->GetFactory()->CreateHtmlIndex(t,filename);
1850 }
1851 //=======================================================================
1852
1853 }//namespace
1854
1855