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