source: Sophya/trunk/SophyaLib/SysTools/commander.cc@ 4078

Last change on this file since 4078 was 4063, checked in by ansari, 13 years ago

Introduction de la classe interface CE_VarListInterface et modifs classe CExpressionEvaluator pour permettre l'inclusion de variables nommees dans les expressions traitees par CExpressionEvaluator, adaptation de la classe Commander afin que les variables de l'interpreteur soit visible par l'evaluateur CExpressionEvaluator, Reza 27/04/2012

File size: 70.0 KB
Line 
1#include "sopnamsp.h"
2#include "commander.h"
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <unistd.h>
7#include <ctype.h>
8#include <math.h>
9#include <signal.h>
10
11#include "strutil.h"
12#include "strutilxx.h"
13#include "cexpre.h"
14#include "rpneval.h"
15#include "srandgen.h"
16#include "zthread.h"
17
18
19namespace SOPHYA {
20
21//--------------------------------------------------
22// Pour ecrire une valeur double sous forme de string
23// sans perte de precision, et si possible de maniere
24// lisible (%g, sinon format adapte jusqu'a %.17e)
25static char cval_strbuff_[80];
26////1er essai binaire: char * cval_dble2str(double v)
27////1er essai binaire: {
28////1er essai binaire: char* str1 = cval_strbuff_;
29////1er essai binaire: sprintf(str1,"%g",v);
30////1er essai binaire: char* str2 = cval_strbuff_+32;
31////1er essai binaire: sprintf(str2,"%.17e",v);
32////1er essai binaire: double x1 = atof(str1);
33////1er essai binaire: double x2 = atof(str2);
34////1er essai binaire: double fx = fabs(v);
35////1er essai binaire: if (fx>0. && fabs(x2-x1)/fx>1e-12) return str2;
36////1er essai binaire: else return str1;
37////1er essai binaire: }
38char * cval_dble2str(double v)
39{
40 char* strgood = cval_strbuff_; sprintf(strgood,"%g",v);
41 char* strfull = cval_strbuff_+32; sprintf(strfull,"%.17e",v);
42 char* strform = cval_strbuff_+64;
43 double x1 = atof(strfull), fx = fabs(x1);
44 if(fx>0.) {
45 for(int i=7;i<=17;i+=2) { // increment de 2 pour aller + vite
46 double x2 = atof(strgood);
47 if(fabs(x2-x1)/fx<1.e-16) break;
48 sprintf(strform,"%%.%de",i);
49 sprintf(strgood,strform,v);
50 }
51 }
52 return strgood;
53}
54//--------------------------------------------------
55
56// Differents code de retour specifiques
57#define CMD_RETURN_RC 99900
58#define CMD_BREAK_RC 99990
59#define CMD_LOOPBREAK_RC 99910
60#define CMD_BREAKALL_RC 99998
61#define CMD_BREAKEXE_RC 99999
62
63// ------------------------------------------------------------
64// Bloc de commandes (Foreach, ...)
65// Classe CommanderBloc
66// ------------------------------------------------------------
67/*!
68 \internal
69 \class SOPHYA::CommanderBloc
70 \ingroup SysTools
71 Class for internal use by class Commander to handle loops
72*/
73class CommanderBloc {
74public:
75 enum BType { BT_None, BT_ForeachList, BT_ForeachInt, BT_ForeachFloat,
76 BT_ForeachLineInFile };
77
78 CommanderBloc(Commander* piac, CommanderBloc* par, string& kw, vector<string>& args);
79 ~CommanderBloc();
80 inline CommanderBloc* Parent() { return(parent); }
81 inline bool CheckOK() { return blkok; }
82 inline void AddLine(string& line)
83 { lines.push_back(line); bloclineid.push_back(lines.size()); }
84 void AddLine(string& line, string& kw);
85 inline void AddBloc(CommanderBloc* blk)
86 { blocs.push_back(blk); bloclineid.push_back(-blocs.size()); }
87
88 // Execution complete du bloc (boucle)
89 int Execute();
90 // Execution pour un element de bloc
91 int ExecuteOnce(string& lvv);
92
93 inline int& TestLevel() { return testlevel; }
94 inline int& LoopLevel() { return looplevel; }
95 inline bool CheckBloc()
96 { return ((testlevel == 0)&&(looplevel == 0)&&(!scrdef)); }
97
98protected:
99 Commander* _commander;
100 CommanderBloc* parent;
101 bool blkok; // true -> block OK
102 BType typ; // foreach , integer loop, float loop, test
103 string varname;
104 string filename; // forinfile bloc
105 vector<string> strlist;
106 vector<string> lines;
107 vector<CommanderBloc *> blocs;
108 vector<int> bloclineid;
109 int i1,i2,di;
110 float f1,f2,df;
111 int testlevel; // niveau d'imbrication des if
112 int looplevel; // niveau d'imbrication des for/foreach
113 bool scrdef; // true -> commande defscript ds for/foreach
114};
115
116/* --Methode-- */
117CommanderBloc::CommanderBloc(Commander* piac, CommanderBloc* par, string& kw, vector<string>& args)
118{
119_commander = piac;
120parent = par;
121blkok = false;
122typ = BT_None;
123i1 = 0; i2 = -1; di = 1;
124f1 = 0.; f2 = -1.; df = 1.;
125testlevel = looplevel = 0;
126scrdef = false;
127
128if ((args.size() < 2) || !isalpha((int)args[0][0]) ) return;
129if ((kw != "foreach") && (kw != "for") && (kw != "forinfile")) return;
130if (!piac->CheckVarName(args[0])) return;
131varname = args[0];
132
133//if (isalpha((int)args[1][0]) ) { This is a foreach bloc with string list
134if (kw == "forinfile") {
135 filename = args[1];
136 typ = BT_ForeachLineInFile;
137 blkok = true;
138}
139else if (kw == "foreach" ) { // This is a foreach bloc with string list
140 if ( (args[1] == "(") && (args[args.size()-1] == ")") ) {
141 // foreach varname ( w1 w2 w3 ... )
142 for(unsigned int kk=2; kk<args.size()-1; kk++) strlist.push_back(args[kk]);
143 }
144 else {
145 // foreach varname WordVectorName
146 if (!piac->GetVar(args[1], strlist)) return;
147 }
148 if (strlist.size() < 1) return;
149 typ = BT_ForeachList;
150 blkok = true;
151}
152else { // This is an integer or float loop
153 size_t l = args[1].length();
154 size_t p = args[1].find(':');
155 size_t pp = args[1].find('.');
156 bool fl = (pp < l) ? true : false; // Float loop or integer loop
157 if (p >= l) return; // Syntaxe error
158 string a1 = args[1].substr(0, p);
159 string aa = args[1].substr(p+1);
160 p = aa.find(':');
161 string a2, a3;
162 bool hasa3 = false;
163 if (p < aa.length() ) {
164 a2 = aa.substr(0,p);
165 a3 = aa.substr(p+1);
166 hasa3 = true;
167 }
168 else a2 = aa;
169 if (fl) {
170 typ = BT_ForeachFloat;
171 blkok = true;
172 f1 = atof(a1.c_str());
173 f2 = atof(a2.c_str());
174 if (hasa3) df = atof(a3.c_str());
175 else df = 1.;
176 }
177 else {
178 typ = BT_ForeachInt;
179 blkok = true;
180 i1 = atoi(a1.c_str());
181 i2 = atoi(a2.c_str());
182 if (hasa3) di = atoi(a3.c_str());
183 else di = 1;
184 }
185 }
186}
187
188/* --Methode-- */
189CommanderBloc::~CommanderBloc()
190{
191for(unsigned int k=0; k<blocs.size(); k++) delete blocs[k];
192}
193
194/* --Methode-- */
195void CommanderBloc::AddLine(string& line, string& kw)
196{
197 AddLine(line);
198 if (kw == "if") testlevel++;
199 else if (kw == "endif") testlevel--;
200 // bug trouve par JEC en Mai 2011 forinfile manquant ds la ligne suivante
201 else if ((kw == "for") || (kw == "foreach") || (kw == "forinfile")) looplevel++;
202 else if (kw == "end") looplevel--;
203 else if (kw == "defscript") scrdef = true;
204}
205
206/* --Methode-- */
207int CommanderBloc::Execute()
208{
209int k=0;
210char buff[32];
211int rcc = 0;
212
213int mxloop = _commander->GetMaxLoopLimit();
214
215if (typ == BT_ForeachLineInFile) { // foreach line in file loop
216 ifstream is(filename.c_str());
217 string line;
218 while (!is.eof()) {
219 rcc = 0;
220 line = "";
221 getline(is,line);
222 if (is.good()) { // bug signale par JEC en mai 2011 || is.eof()) {
223 rcc = ExecuteOnce(line);
224 if ((rcc == CMD_BREAKEXE_RC) || (rcc == CMD_BREAKALL_RC)) return rcc;
225 else if (rcc == CMD_BREAK_RC) break;
226 }
227 }
228 rcc=0;
229}
230else if (typ == BT_ForeachList) { // foreach string loop
231 for(unsigned int k=0; k<strlist.size(); k++) {
232 rcc = ExecuteOnce(strlist[k]);
233 if ((rcc == CMD_BREAKEXE_RC) || (rcc == CMD_BREAKALL_RC)) return rcc;
234 else if (rcc == CMD_BREAK_RC) break;
235 }
236 rcc = 0;
237}
238else if (typ == BT_ForeachInt) { // Integer loop
239 for(int i=i1; i<i2; i+=di) {
240 k++;
241 if ((mxloop>0) && (k > mxloop)) {
242 cout << ">>> Maximum CommanderBloc loop limit ("<< mxloop << ") -> break " << endl;
243 break;
244 }
245 sprintf(buff, "%d", i);
246 string lvv = buff;
247 rcc = ExecuteOnce(lvv);
248 if ((rcc == CMD_BREAKEXE_RC) || (rcc == CMD_BREAKALL_RC)) return rcc;
249 else if (rcc == CMD_BREAK_RC) break;
250 }
251 rcc = 0;
252}
253else if (typ == BT_ForeachFloat) { // float loop
254 for(double f=f1; f<f2; f+=df) {
255 k++;
256 if ((mxloop>0) && (k > mxloop)) {
257 cout << ">>> Maximum CommanderBloc loop limit ("<< mxloop << ") -> break " << endl;
258 break;
259 }
260 string lvv = cval_dble2str(f);
261 rcc = ExecuteOnce(lvv);
262 if ((rcc == CMD_BREAKEXE_RC) || (rcc == CMD_BREAKALL_RC)) return rcc;
263 else if (rcc == CMD_BREAK_RC) break;
264 }
265 rcc = 0;
266}
267return(rcc);
268}
269
270/* --Methode-- */
271int CommanderBloc::ExecuteOnce(string& lvv)
272{
273 unsigned int kj=0;
274 int kk=0;
275 int rcc = 0;
276 _commander->SetVar(varname, lvv);
277 for(kj=0; kj<bloclineid.size(); kj++) {
278 rcc = 0;
279 kk = bloclineid[kj];
280 if (kk > 0)
281 rcc = _commander->Interpret(lines[kk-1]);
282 else
283 rcc = blocs[-kk-1]->Execute();
284 if ((rcc == CMD_BREAKEXE_RC) || (rcc == CMD_BREAKALL_RC)) return rcc;
285 if (rcc == CMD_BREAK_RC) break;
286 }
287 return rcc;
288}
289
290// ---------------------------------------------------------------
291// Classe CommanderScript
292// Definition et execution d'un script de Commander
293// script : Une liste de commande Commander - Lors de l'execution,
294// les variables-argument $# $0 $1 sont definies.
295// ---------------------------------------------------------------
296
297/*!
298 \internal
299 \class SOPHYA::CommanderScript
300 \ingroup SysTools
301 Class for internal use by class Commander to handle functions
302 or scripts
303*/
304
305class CommanderScript {
306public:
307 CommanderScript(Commander* piac, string const& name, string const& comm);
308 virtual ~CommanderScript();
309
310 void AddLine(string& line, string& kw);
311 virtual int Execute(vector<string>& args);
312
313 inline string& Name() { return mName; }
314 inline string& Comment() { return mComm; }
315 inline int& TestLevel() { return testlevel; }
316 inline int& LoopLevel() { return looplevel; }
317 inline bool CheckScript()
318 { return ((testlevel == 0)&&(looplevel == 0)&&(!scrdef)&&fgok); }
319
320protected:
321 Commander* _commander;
322 string mName;
323 string mComm;
324 vector<string> lines;
325 int testlevel; // niveau d'imbrication des if
326 int looplevel; // niveau d'imbrication des for/foreach
327 bool scrdef; // true -> commande defscript ds for/foreach
328 bool fgok; // Script name OK
329
330};
331
332/* --Methode-- */
333CommanderScript::CommanderScript(Commander* piac, string const& name,
334 string const& comm)
335{
336_commander = piac;
337testlevel = looplevel = 0;
338scrdef = false;
339mName = name;
340if (!isalpha(name[0])) fgok = false;
341else fgok = true;
342mComm = comm;
343}
344
345/* --Methode-- */
346CommanderScript::~CommanderScript()
347{
348}
349
350/* --Methode-- */
351void CommanderScript::AddLine(string& line, string& kw)
352{
353 if (kw == "if") testlevel++;
354 else if (kw == "endif") testlevel--;
355 // bug trouve par JEC en Mai 2011 forinfile manquant ds la ligne suivante
356 else if ((kw == "for") || (kw == "foreach") || (kw == "forinfile")) looplevel++;
357 else if (kw == "end") looplevel--;
358 else if (kw == "defscript") scrdef = true;
359 lines.push_back(line);
360}
361
362/* --Methode-- */
363int CommanderScript::Execute(vector<string>& args)
364{
365 int rcc=-2;
366 if (!CheckScript()) return(-1);
367 cout << " CommanderScript::Execute() - Executing script " << Name() << endl;
368 for(unsigned int k=0; k<lines.size(); k++) {
369 rcc = _commander->Interpret(lines[k]);
370 if ( (rcc == CMD_BREAKEXE_RC) || (rcc == CMD_RETURN_RC) ) break;
371 }
372 return(rcc);
373}
374
375
376// ------------------------------------------------------------
377// Classe CommandExeThr
378// ------------------------------------------------------------
379/*!
380 \internal
381 \class SOPHYA::CommandExeThr
382 \ingroup SysTools
383 Class for internal use by class Commander for command execution in separate threads.
384 The command execution is carried in method run() which has its own exception handling bloc.
385*/
386class CommandExeThr : public ZThread {
387public:
388 CommandExeThr(uint_8 id, CmdExecutor * cmdex, string& keyw,
389 vector<string>& args, string& toks);
390 virtual void run();
391 inline uint_8 Id() { return _id; }
392 inline bool IfDone() { return _fgdone; }
393 inline string& Tokens() { return _toks; }
394 inline string& Keyword() { return _keyw; }
395protected:
396 uint_8 _id;
397 CmdExecutor * _cmdex;
398 string _keyw, _toks;
399 vector<string> _args;
400 bool _fgdone;
401};
402
403/* --Methode-- */
404CommandExeThr::CommandExeThr(uint_8 id, CmdExecutor * cmdex, string& keyw,
405 vector<string>& args, string& toks)
406{
407 _id = id;
408 _cmdex = cmdex;
409 _keyw = keyw;
410 _args = args;
411 _args.erase(_args.end()-1);
412 _toks = toks;
413 for(size_t k=_toks.size()-1; k>0; k--)
414 if (_toks[k] == '&') { _toks[k] = ' '; break; }
415 _fgdone = false;
416}
417
418/* --Methode-- */
419void CommandExeThr::run()
420{
421 int rc = 0;
422 try {
423 rc = _cmdex->Execute(_keyw, _args, _toks);
424 }
425 catch (PThrowable& exc) {
426 cout << "CommandExeThr::run() Catched Exception Msg()= "
427 << exc.Msg() << endl;
428 rc = -77;
429 }
430 catch (std::exception& sex) {
431 cout << "CommandExeThr::run() Catched std::exception what()= "
432 << sex.what() << endl;
433 rc = -78;
434 }
435 catch (...) {
436 cout << "CommandExeThr::run() Catched unknown (...) exception " << endl;
437 rc = -79;
438 }
439 _fgdone = true;
440 setRC(rc);
441}
442
443// ------------------------------------------------------------
444// Classe Cmd_CE_VarList
445// ------------------------------------------------------------
446/*!
447 \internal
448 \class SOPHYA::Cmd_CE_VarList
449 \ingroup SysTools
450 This class, reserved for internal use by class Commander, enables the CExpressionEvaluator class
451 to access the interpreter variable values.
452*/
453
454class Cmd_CE_VarList : public CE_VarListInterface {
455public:
456 Cmd_CE_VarList(Commander& cmd) : cmd_(cmd) { }
457 virtual void Update();
458 virtual double* GetVarPointer(std::string const& name);
459private:
460 Commander& cmd_;
461 std::map<std::string, double> namevals_;
462};
463
464/* --Methode-- */
465void Cmd_CE_VarList::Update()
466{
467 bool exist=false;
468 string sval;
469 std::map<std::string, double>::iterator it;
470 for(it=namevals_.begin(); it!=namevals_.end(); it++) {
471 exist=cmd_.GetVar((*it).first, sval);
472 if (!exist) throw NotFoundExc("Cmd_CE_VarList::Update() missing variable !");
473 (*it).second=atof(sval.c_str());
474 }
475 return;
476}
477
478/* --Methode-- */
479double* Cmd_CE_VarList::GetVarPointer(std::string const& name)
480{
481 std::map<std::string, double>::iterator it=namevals_.find(name);
482 if (it!=namevals_.end()) return &((*it).second);
483 bool exist=false;
484 string sval;
485 exist=cmd_.GetVar(name, sval);
486 if (!exist) return NULL;
487 namevals_[name]=atof(sval.c_str());
488 it=namevals_.find(name);
489 if (it!=namevals_.end()) return &((*it).second);
490 return NULL;
491}
492
493
494// ------------------------------------------------------------
495// Classe Commander
496// ------------------------------------------------------------
497typedef void (* DlModuleInitEndFunction) ();
498
499/*!
500 \class Commander
501 \ingroup SysTools
502 \brief Simple command interpreter
503
504 This Simple command interpreter with c-shell like syntax
505 can be used to add scripting capabilities
506 to applications.
507
508 Although the interpreter has many limitations compared to
509 c-shell, or Tcl , it provides some interesting possibilities:
510
511 - Extended arithmetic operations (c-like and RPN)
512 - Simple and vector variables
513 - Script definition
514 - Command execution in separate threads
515 - Dynamic Load
516
517 \sa CmdExecutor CExpressionEvaluator RPNExpressionEvaluator
518
519 Usage example:
520 \code
521 #include "commander.h"
522 ...
523 Commander cmd;
524 char* ss[3] = {"foreach f ( AA bbb CCCC ddddd )", "echo $f" , "end"};
525 for(int k=0; k<3; k++) {
526 string line = ss[k];
527 cmd.Interpret(line);
528 }
529 \endcode
530*/
531
532#define _MAGENTA_ 1
533
534static Commander* cur_commander = NULL;
535/* --Methode-- */
536/*!
537 \brief Constructor. Initializes variable list and copies \c history.pic to \c hisold.pic
538 if \b fgsigstop == true , calls ZThread::ActivateExitOnSignal()
539*/
540Commander::Commander(bool fgsigzt)
541{
542system("cp history.pic hisold.pic");
543hist.open("history.pic");
544histon = true;
545trace = false; timing = false; varcexp = true;
546gltimer = NULL;
547felevel_ = 0;
548SetMaxLoopLimit();
549mulinecmd = "";
550mulinefg = false;
551spromptmul = "Cmd> ";
552SetCurrentPrompt(spromptmul);
553SetDefaultPrompt(spromptmul);
554curscript = NULL;
555
556_xstatus = 0;
557 _retstr = "";
558
559// Controle du flot d'execution
560 fgexebrk = false;
561
562curtestresult_ = true;
563PushStack();
564
565// Pour la numerotation et l'identification des threads
566ThrId = 0;
567
568// Numero de help-groupe courant - Le premier groupe ajoute aura un gid = 1
569// gid = 0 n'existe pas : c'est le groupe de toutes les commandes
570cmdgrpid = 0;
571
572string grp = "Commander";
573string gdesc = "Basic (generic) interpreter (class SOPHYA::Commander) builtin commands";
574AddHelpGroup(grp, gdesc);
575
576string kw = "Commander";
577string usage;
578usage = ">>> (Commander) Interpreter's keywords : \n";
579usage += " > set varname string # To set a variable, $varname \n";
580usage += " > unset varname # clear variable definition \n";
581usage += " > rpneval varname RPNExpression # Reverse Polish Notation evaluation \n";
582usage += " > varname = ArithmeticExpression # C-like Expression evaluation \n";
583usage += " > varname = 'String' # Set variable vname \n";
584usage += " > var2words varname wordvarname [sep] # to break varname into words \n";
585usage += " > echo string # output string \n";
586usage += " > echo2file filename string # Append the string to the specified file \n";
587usage += " > alias name string # define a command alias \n";
588usage += " > foreach varname ( string-list ) # Loop \n";
589usage += " > for varname i1:i2[:di] # Integer loop \n";
590usage += " > for varname f1:f2[:df] # Float loop \n";
591usage += " > forinfile varname FileName # Loop over lines in file \n";
592usage += " > end # end loops \n";
593usage += " > if ( test ) then # Conditional test : a == != < > <= >= b \n";
594usage += " > else # Conditional \n";
595usage += " > endif # End of conditional if bloc \n";
596usage += " > break # exit from the current loop blocs \n";
597usage += " > breakall # exit all loop blocs \n";
598usage += " > return # Stops command execution from a file \n";
599usage += " > defscript endscript # Command script definition \n";
600usage += " > listvars # List of variable names and values \n";
601usage += " > listalias # List of alias names and values \n";
602usage += " > listcommands # List of all known commands \n";
603usage += " > listscripts # List of all known scripts \n";
604usage += " > clearcript # Clear a script definition \n";
605usage += " > thrlist # List of command execution threads (& as the last character) \n";
606usage += " > clearthrlist # Removes finished threads from the list \n";
607usage += " > killthr Id # Try to stop a given thread (ThrId=id) by sending SIGUSR1 \n";
608usage += " > cancelthr Id # Try to cancel a given thread (ThrId=id) \n";
609usage += " > waitthr # Waits until all active threads have finished (join()) \n";
610usage += " > exec filename # Execute commands from file \n";
611usage += " > help <command_name> # <command_name> usage info \n";
612usage += " > sleep nsec # sleep nsec seconds \n";
613usage += " > readstdin varname # reads a line from stdin into $varname \n";
614usage += " > timingon timingoff traceon traceoff varcexpon varcexpoff \n";
615RegisterHelp(kw, usage, grp);
616
617kw = "RPNEvaluator";
618usage = " Reverse Polish Notation (HP calculator like) expression evaluation \n";
619usage += " >> Stack: \n";
620usage += " ... (4) (3) z=(2) y=(1) x=(0)=Stack.Top() \n";
621usage += " >> Examples: \n";
622usage += " - sin(PI/6): pi 6 / sin \n";
623usage += " - 1*2*...*5: 1 2 3 4 5 product \n";
624usage += " - x=x+y: x = $x $y * \n";
625usage += " >>> Stack operations : \n";
626usage += " print x<>y pop push (duplicate x) \n";
627usage += " >>> Constants (Cst pushed to stack): \n";
628usage += " pi e \n";
629usage += " >>> Arithmetic operators (x,y) --> x@y \n";
630usage += " + - * / % ( (int)y % (int)x )\n";
631usage += " >>> F(X): x --> F(x) \n";
632usage += " chs sqrt sq log log10 exp \n";
633usage += " fabs floor ceil \n";
634usage += " cos sin tan acos asin atan deg2rad rad2deg \n";
635usage += " >>> F(X,Y): (x,y) --> F(x,y) \n";
636usage += " pow atan2 \n";
637usage += " >>> F(): random number generators \n";
638usage += " rand (flat 0..1) norand (normal/gaussian) \n";
639usage += " >>> Stack sum/product/mean/sigma/sigma^2 \n";
640usage += " sum product mean sigma sigma2 sigmean (y->sigma x->mean) \n";
641RegisterHelp(kw, usage, grp);
642
643kw = "autoiniranf";
644usage = "> Automatic random number generator initialisation\n";
645usage += " by AutoInitRand(int lp) \n";
646usage += " Usage: autoiniranf";
647RegisterCommand(kw, usage, NULL, grp);
648
649kw = "CExpEvaluator";
650usage = "> Evaluation of C-like expression (used in V = C-like-Expression) \n";
651usage += " >>> Arithmetic operators, parenthesis ( + - * / ) \n";
652usage += " >>> Functions : sqrt fabs floor hypot \n";
653usage += " ... exp log log10 pow ; sinh cosh tanh \n";
654usage += " ... sin cos tan asin acos atan atan2 \n";
655usage += " ... rand01() randpm1() gaurand() \n";
656usage += " >>> Constants : Pi = M_PI E = M_E \n";
657usage += " Example: x = 5.*(2.+sin(0.3*Pi))";
658RegisterCommand(kw, usage, NULL, grp);
659
660kw = "shell execute";
661usage = "> shell command_string # Execute shell command\n";
662usage += "> cshell command_string # Execute cshell command\n";
663usage += "---Examples:\n";
664usage += " > shell ls\n";
665usage += " > cshell echo '$LD_LIBRARY_PATH'; map2cl -h; ls\n";
666usage += " > shell myfile.csh [arg1] [arg2] [...]\n";
667usage += " (where the first line of \"myfile.csh\" is \"#!/bin/csh\")\n";
668RegisterCommand(kw, usage, NULL, grp);
669
670
671AddInterpreter(this);
672curcmdi = this;
673if (fgsigzt) ZThread::ActivateExitOnSignal(SIGUSR1);
674}
675
676/* --Methode-- */
677Commander::~Commander()
678{
679hist.close();
680if (gltimer) { delete gltimer; gltimer = NULL; }
681Modmap::iterator it;
682for(it = modmap.begin(); it != modmap.end(); it++) {
683 string name = (*it).first + "_end";
684 DlModuleInitEndFunction fend = (*it).second->GetFunction(name);
685 if (fend) fend();
686 delete (*it).second;
687 }
688
689for(ScriptList::iterator sit = mScripts.begin();
690 sit != mScripts.end(); sit++) delete (*sit).second;
691
692if (cur_commander == this) cur_commander = NULL;
693}
694
695/* --Methode-- */
696Commander* Commander::GetInterpreter()
697{
698return(cur_commander);
699}
700
701/* --Methode-- */
702//! Returns the string \c Commander as the interpreter's name.
703string Commander::Name()
704{
705return("Commander");
706}
707
708/* --Methode-- */
709//! Add the \b grp help group with description \b desc.
710void Commander::AddHelpGroup(string& grp, string& desc)
711{
712 int gid;
713 CheckHelpGrp(grp, gid, desc);
714}
715
716/* --Methode-- */
717/*!
718 \brief Register a command executor associated with a given keyword.
719 \param keyw : keyword identifying the command
720 \param usage : the command help and usage information
721 \param ce : CmdExecutor pointer for this a command. The same object can be registered
722 multiple time for different commands.
723 \param grp : The help group corresponding to this command.
724*/
725void Commander::RegisterCommand(string& keyw, string& usage, CmdExecutor * ce, string& grp)
726{
727if (!ce) {
728 RegisterHelp(keyw, usage, grp);
729 return;
730 }
731int gid;
732CheckHelpGrp(grp,gid);
733cmdex cme;
734cme.group = gid;
735cme.us = usage;
736cme.cex = ce;
737cmdexmap[keyw] = cme;
738}
739
740/* --Methode-- */
741/*!
742 \brief Register a help text.
743 \param keyw : help keyword
744 \param usage : help text
745 \param grp : help group
746*/
747void Commander::RegisterHelp(string& keyw, string& usage, string& grp)
748{
749int gid;
750CheckHelpGrp(grp,gid);
751cmdex cme;
752cme.group = gid;
753cme.us = usage;
754cme.cex = NULL;
755helpexmap[keyw] = cme;
756}
757
758/* --Methode-- */
759bool Commander::CheckHelpGrp(string& grp, int& gid, string& desc)
760{
761gid = 0;
762CmdHGroup::iterator it = cmdhgrp.find(grp);
763if (it == cmdhgrp.end()) {
764 cmdgrpid++; gid = cmdgrpid;
765 hgrpst hgs; hgs.gid = gid; hgs.desc = desc;
766 cmdhgrp[grp] = hgs;
767 return true;
768 }
769else {
770 if (desc.length() > 0) (*it).second.desc = desc;
771 gid = (*it).second.gid;
772 return false;
773}
774}
775
776
777/* --Methode-- */
778/*!
779 \brief Dynamic loader for modules
780
781 A module is a shared library extending the application functionalities.
782 Typically, a module adds new commands to the interpreter. Once loaded,
783 the module is activated (initialized) by calling a function with the
784 name \b modulename_init . This function should be declared extern C
785 to avoid C++ name mangling. A cleanup function \b modulename_end
786 is called by the Commander destructor.
787
788 \param fnameso : Shared library name containing the module functions
789 and classes.
790 \param name : Module name. This string is used to form module
791 initializer and cleanup function name \c name_init \c name_end
792*/
793void Commander::LoadModule(string& fnameso, string& name)
794{
795PDynLinkMgr * dynlink = new PDynLinkMgr(fnameso, false);
796if (dynlink == NULL) {
797 cerr << "Commander/LoadModule_Error: Pb opening SO " << fnameso << endl;
798 return;
799 }
800string fname = name + "_init";
801DlModuleInitEndFunction finit = dynlink->GetFunction(fname);
802if (!finit) {
803 cerr << "Commander/LoadModule_Error: Pb linking " << fname << endl;
804 return;
805 }
806cout << "Commander/LoadModule_Info: Initialisation module" << name
807 << " " << fname << "() ..." << endl;
808finit();
809modmap[name] = dynlink;
810return;
811}
812
813/* --Methode-- */
814//! Declare a new interpreter
815void Commander::AddInterpreter(CmdInterpreter * cl)
816{
817if (!cl) return;
818interpmap[cl->Name()] = cl;}
819
820/* --Methode-- */
821//! Select an interpreter by its name as the current interpreter.
822void Commander::SelInterpreter(string& name)
823{
824InterpMap::iterator it = interpmap.find(name);
825if (it == interpmap.end()) return;
826curcmdi = (*it).second;
827}
828
829
830
831/* Fonction */
832static string GetStringFrStdin(Commander* piac)
833{
834char buff[128];
835fgets(buff, 128, stdin);
836buff[127] = '\0';
837return((string)buff);
838}
839
840/* --Methode-- */
841/*!
842 \brief Method which has to be invoked to interpret a given command line or string.
843*/
844int Commander::Interpret(string& s)
845{
846int rc = 0;
847ScriptList::iterator sit;
848
849// Si le flag d'arret d'execution a ete positionne on returne avec le code
850// de BREAKEXECUTION
851if (fgexebrk) {
852 cout << " ===> Commander::Interpret() - STOP Execution (CMD_BREAKEXE_RC)" << endl;
853 fgexebrk = false; return CMD_BREAKEXE_RC;
854}
855
856// On saute de commandes vides
857size_t l;
858l = s.length();
859if (!mulinefg && (l < 1)) return(0);
860
861// On enregistre les commandes
862if (histon) hist << s << endl;
863
864if (s[0] == '#') return(0); // si c'est un commentaire
865
866// Logique de gestion des lignes suite
867// un \ en derniere position indique la presence d'une ligne suite
868size_t lnb = s.find_last_not_of(' ');
869if (lnb >= l) return(0); // il n'y a que des blancs
870if (s[lnb] == '\\' ) { // Lignes suite ...
871 mulinecmd += s.substr(0,lnb);
872 if (!mulinefg) {
873 spromptmul = GetCurrentPrompt();
874 SetCurrentPrompt("...? ");
875 mulinefg = true;
876 }
877 return(0);
878}
879
880if (mulinefg) { // Il y avait des lignes suite
881 s = mulinecmd + s;
882 l = s.length();
883 mulinecmd = "";
884 mulinefg = false;
885 SetCurrentPrompt(spromptmul);
886}
887
888// Removing leading blanks
889size_t p,q;
890
891// On enleve le dernier caractere, si celui-ci est \n
892if (s[l-1] == '\n') s[l-1] = '\0';
893p=s.find_first_not_of(" \t");
894if (p < l) s = s.substr(p);
895// >>>> Substitution d'alias (1er mot)
896CmdStrList::iterator it;
897p = 0;
898q = s.find_first_of(" \t");
899l = s.length();
900string w1 = (q < l) ? s.substr(p,q-p) : s.substr(p);
901it = mAliases.find(w1);
902if (it != mAliases.end()) {
903 s = (q < l) ? ((*it).second + s.substr(q)) : (*it).second ;
904 l = s.length();
905 p=s.find_first_not_of(" \t");
906 if (p < l) s = s.substr(p);
907 p = 0;
908 q = s.find_first_of(" ");
909 }
910
911// >>>> Separating keyword
912string toks,kw;
913if (q < l)
914 { kw = s.substr(p,q-p); toks = s.substr(q, l-q); }
915else { kw = s.substr(p,l-p); toks = ""; }
916
917// les mot-cle end else endif doivent etre le seul mot de la ligne
918if ( (kw == "end") || (kw == "else") || (kw == "endif") || (kw == "endscript") ) {
919 size_t ltk = toks.length();
920 if (toks.find_first_not_of(" \t") < ltk) {
921 cerr << "Commander::Interpret()/syntax error near end else endif endscript \n"
922 << "line: " << s << endl;
923 _xstatus = 91;
924 return(91);
925 }
926}
927
928// On verifie si on est en train de definir un script
929if (curscript) {
930 if (kw == "endscript") {
931 if (curscript->CheckScript()) {
932 sit = mScripts.find(curscript->Name());
933 if (sit != mScripts.end()) {
934 cout << "Commander::Interpret() replacing script "
935 << curscript->Name() << endl;
936 CommanderScript* scr = mScripts[curscript->Name()];
937 mScripts.erase(sit);
938 delete scr;
939 }
940 cout << "Commander::Interpret() Script " << curscript->Name()
941 << " defined successfully" << endl;
942 mScripts[curscript->Name()] = curscript;
943 SetCurrentPrompt("Cmd> ");
944 curscript = NULL;
945 _xstatus = 0;
946 return(0);
947 }
948 else {
949 cout << "Commander::Interpret() Error in Script " << curscript->Name()
950 << " definition " << endl;
951 SetCurrentPrompt("Cmd> ");
952 curscript = NULL;
953 _xstatus = 92;
954 return(92);
955 }
956 }
957 else curscript->AddLine(s, kw);
958 _xstatus = 0;
959 return(0);
960}
961// On verifie si nous sommes dans un bloc (for , foreach)
962if (CmdBlks.top() != NULL) { // On est dans un bloc
963 if ( (kw == "for") || (kw == "foreach") || (kw == "forinfile") ) felevel_++;
964 else if (kw == "end") felevel_--;
965
966 int rcbex = 0;
967 if (felevel_ == 0) { // Il faut executer le bloc
968 CommanderBloc* curb = CmdBlks.top();
969 CmdBlks.top() = curb->Parent();
970 SetCurrentPrompt("Cmd> ");
971 if (!curb->CheckBloc()) {
972 cerr << "Commander::Interpret()/syntax error - unbalenced if ... endif"
973 << " within for/foreach/forinfile bloc ! " << endl;
974 delete curb;
975 _xstatus = 93;
976 return(93);
977 }
978 // cout << " *DBG* Executing bloc " << endl;
979 bool ohv = histon;
980 histon = false;
981 if (curtestresult_) {
982 // We push also CommanderBloc and testresult on the stack
983 PushStack();
984 rcbex = curb->Execute();
985 // And then, pop CommanderBloc and TestResult from the corresponding stacks
986 PopStack(false);
987 }
988 SetCurrentPrompt(defprompt);
989 delete curb;
990 histon = ohv;
991 }
992 else CmdBlks.top()->AddLine(s, kw);
993 _xstatus = rcbex;
994 return(rcbex);
995}
996else if (kw == "end") {
997 cerr << "Commander::Interpret()/syntax error - end outside for/foreach/forinfile bloc \n"
998 << "line: " << s << endl;
999 _xstatus = 94;
1000 return(94);
1001}
1002
1003// Sommes-nous dans un bloc de test if then else
1004if (TestsStack.top().tstlist.size() > 0) { // Nous sommes ds un bloc if
1005 if (kw == "else") {
1006 if ((*tresit_) & 2) {
1007 cerr << "Commander::Interpret()/syntax error - multiple else in if bloc \n"
1008 << "line: " << s << endl;
1009 _xstatus = 95;
1010 return(95);
1011 }
1012 else {
1013 const char * npr = ((*tresit_)&1) ? "else-F> " : "else-T> ";
1014 if ((*tresit_)&1) curtestresult_ = false;
1015 SetCurrentPrompt(npr);
1016 (*tresit_) |= 2;
1017 _xstatus = 0;
1018 return(0);
1019 }
1020 }
1021 else if (kw == "endif") {
1022 list<char>::iterator dbit = tresit_;
1023 tresit_--;
1024 TestsStack.top().tstlist.erase(dbit);
1025 const char * npr = "Cmd> ";
1026 if (TestsStack.top().tstlist.size() > 1) {
1027 curtestresult_ = true;
1028 list<char>::iterator it;
1029 for(it=TestsStack.top().tstlist.begin(); it!=TestsStack.top().tstlist.end(); it++) {
1030 // Si on n'est pas ds le else et le if est faux
1031 if ( !((*it)&2) && !((*it)&1) ) curtestresult_ = false;
1032 // Si on est ds else et le if etait vrai !
1033 if ( ((*it)&2) && ((*it)&1) ) curtestresult_ = false;
1034 if (!curtestresult_) break;
1035 }
1036
1037 if (!((*tresit_)&2))
1038 npr = ((*tresit_)&1) ? "if-T> " : "if-F> ";
1039 else
1040 npr = ((*tresit_)&1) ? "else-F> " : "else-T> ";
1041 }
1042 else curtestresult_ = true;
1043 SetCurrentPrompt(npr);
1044 _xstatus = 0;
1045 return(0);
1046 }
1047}
1048else if ((kw == "else") || (kw == "endif")) {
1049 cerr << "Commander::Interpret()/syntax error - else,endif outside if bloc \n"
1050 << "line: " << s << endl;
1051 _xstatus = 91;
1052 return(91);
1053}
1054
1055bool fgcont = true;
1056if (TestsStack.top().tstlist.size() > 0) { // Resultat de if ou else
1057 list<char>::iterator it;
1058 for(it=TestsStack.top().tstlist.begin(); it!=TestsStack.top().tstlist.end(); it++) {
1059 // Si on n'est pas ds le else et le if est faux
1060 if ( !((*it)&2) && !((*it)&1) ) fgcont = false;
1061 // Si on est ds else et le if etait vrai !
1062 if ( ((*it)&2) && ((*it)&1) ) fgcont = false;
1063 if (!fgcont) break;
1064 }
1065}
1066
1067if ((!fgcont) && (kw != "if")) {
1068 _xstatus = 0;
1069 return(0);
1070}
1071
1072
1073// Les mots cles break et return peuvent de sortir de boucles/scripts/execfile
1074if (kw == "break") return CMD_BREAK_RC;
1075if (kw == "breakall") return CMD_BREAKALL_RC;
1076else if (kw == "return") {
1077 _retstr = toks;
1078 return CMD_RETURN_RC;
1079}
1080
1081// Nous ne sommes donc pas dans un bloc .... Substitution de variables
1082string s2;
1083int rcs ;
1084
1085rcs = SubstituteVars(s, s2);
1086if (rcs) {
1087 cerr << "Commander::Interpret()/syntax error in SubstituteVars() \n"
1088 << "line: " << s << endl;
1089 _xstatus = 99;
1090 return(99);
1091}
1092// >>>> Separating keyword and tokens
1093vector<string> tokens;
1094vector<bool> qottoks;
1095/* decoupage en mots */
1096LineToWords(s2, kw, tokens, qottoks, toks, true);
1097
1098// Si c'est un for/foreach, on cree un nouveau bloc
1099if ((kw == "foreach") || (kw == "for") || (kw == "forinfile") ) {
1100 // cout << " *DBG* We got a foreach... " << endl;
1101 CommanderBloc* bloc = new CommanderBloc(this, CmdBlks.top(), kw, tokens);
1102 if (!bloc->CheckOK()) {
1103 cerr << "Commander::Interpret() for/foreach syntax Error ! " << endl;
1104 delete bloc;
1105 _xstatus = 91;
1106 return(91);
1107 }
1108 felevel_++;
1109 if (CmdBlks.top()) CmdBlks.top()->AddBloc(bloc);
1110 else SetCurrentPrompt("for...> ");
1111 CmdBlks.top() = bloc;
1112 // cout << " *DBG* New Bloc created ... " << endl;
1113 return(0);
1114 }
1115else if (kw == "if") { // Un test if
1116 bool restst = true;
1117 int rct = EvaluateTest(tokens, s, restst);
1118 if (rct) {
1119 cerr << "Commander::Interpret() if syntax Error ! " << "line: " << s << endl;
1120 _xstatus = 91;
1121 return(91);
1122 }
1123 char res_tst = (restst) ? 1 : 0;
1124 TestsStack.top().tstlist.push_back(res_tst);
1125 if (TestsStack.top().tstlist.size() == 1) tresit_ = TestsStack.top().tstlist.begin();
1126 else tresit_++;
1127 const char * npr = (restst) ? "if-T> " : "if-F> ";
1128 SetCurrentPrompt(npr);
1129}
1130else if ((tokens.size() > 0) && (tokens[0] == "=")) {
1131 // x = Expression
1132 if (qottoks[1]) { // decodage sous forme de chaine
1133 SetVariable(kw, tokens[1]);
1134 }
1135 else {
1136 try {
1137 double res = 0.;
1138 string sex=tokens[1];
1139 if (tokens.size() > 2)
1140 for(unsigned int js=2; js<tokens.size(); js++) sex += tokens[js];
1141 if (varcexp) { //--- Evaluation d'expression avec decodage de noms de variables
1142 Cmd_CE_VarList cevl(*this);
1143 CExpressionEvaluator cex(sex,&cevl);
1144 res = cex.Value();
1145 }
1146 else { //--- Evaluation d'expression SANS decodage des noms de variables
1147 CExpressionEvaluator cex(sex);
1148 res = cex.Value();
1149 }
1150 string vv = cval_dble2str(res);
1151 SetVariable(kw, vv);
1152 }
1153 catch (CExprException& cexerr) {
1154 cerr << "Commander::Interpret() evaluation Error : \n " << "line: " << s
1155 << " \n Msg=" << cexerr.Msg() << endl;
1156 _xstatus = 98;
1157 return(98);
1158 }
1159 }
1160}
1161else if (kw == "defscript") { // definition de script
1162 if (tokens.size() > 0) {
1163 if (tokens.size() < 2) tokens.push_back("");
1164 curscript = new CommanderScript(this, tokens[0], tokens[1]);
1165 SetCurrentPrompt("Script...> ");
1166 return(0);
1167 }
1168 else {
1169 cerr << "Commander::Interpret() No script name in defscript" << "line: " << s << endl;
1170 _xstatus = 91;
1171 return(91);
1172 }
1173}
1174else {
1175 // Si c'est le nom d'un script
1176 sit = mScripts.find(kw);
1177 if (sit != mScripts.end()) {
1178 bool ohv = histon;
1179 histon = false;
1180 tokens.insert(tokens.begin(), kw);
1181 PushStack(tokens);
1182 (*sit).second->Execute(tokens);
1183 PopStack(true);
1184 histon = ohv;
1185 }
1186 // Execution de commandes
1187 else rc = ExecuteCommandLine(kw, tokens, toks);
1188 _xstatus = rc;
1189 return(rc);
1190}
1191// cout << "Commander::Do() DBG KeyW= " << kw << " NbArgs= " << tokens.size() << endl;
1192// for(int ii=0; ii<tokens.size(); ii++)
1193// cout << "arg[ " << ii << " ] : " << tokens[ii] << endl;
1194
1195return(0);
1196}
1197
1198//! Can be called asynchronously (from a separate thread) to break (halt) execution (in loops, scripts ...)
1199void Commander::StopExecution()
1200{
1201 fgexebrk = true;
1202}
1203
1204
1205/* --Methode-- */
1206int Commander::LineToWords(string& line, string& kw, vector<string>& tokens,
1207 vector<bool>& qottoks, string& toks, bool uq)
1208{
1209if (line.length() < 1) return(0);
1210int nw = 1;
1211size_t p = line.find_first_not_of(" ");
1212line = line.substr(p);
1213p = 0;
1214size_t q = line.find_first_of(" ");
1215size_t l = line.length();
1216
1217if (q < l)
1218 { kw = line.substr(p,q-p); toks = line.substr(q, l-q); }
1219else { kw = line.substr(p,l-p); toks = ""; }
1220
1221q = 0;
1222while (q < l) {
1223 bool swq = false; // true -> chaine delimite par ' ou "
1224 p = toks.find_first_not_of(" \t",q+1); // au debut d'un token
1225 if (p>=l) break;
1226 if ( uq && ((toks[p] == '\'') || (toks[p] == '"')) ) {
1227 q = toks.find(toks[p],p+1);
1228 if (q>=l) {
1229 cerr << "Commander::LineToWords/Syntax Error - Unbalenced quotes " << toks[p] << '.' << endl;
1230 return(-1);
1231 }
1232 p++; swq = true;
1233 }
1234 else {
1235 q = toks.find_first_of(" \t",p); // la fin du token;
1236 }
1237 string token = toks.substr(p,q-p);
1238 tokens.push_back(token);
1239 qottoks.push_back(swq);
1240 nw++;
1241 }
1242
1243return(nw);
1244}
1245
1246/* --Methode-- */
1247int Commander::SubstituteVars(string & s, string & s2)
1248// Variable substitution
1249{
1250
1251int iarr = -1; // index d'element de tableau
1252size_t p,q,q2,q3,l;
1253bool fgvarapp = false; // Si true, VarApp en priorite
1254
1255s2="";
1256p = 0;
1257l = s.length();
1258string vn, vv;
1259while (p < l) {
1260 iarr = -1;
1261 fgvarapp = false;
1262 q = s.find('$',p);
1263 if (q > l) break;
1264 q2 = s.find('\'',p);
1265 if ((q2 < l) && (q2 < q)) { // On saute la chaine delimitee par ' '
1266 q2 = s.find('\'',q2+1);
1267 if (q2 >= l) {
1268 cerr << " Syntax error - Unbalenced quotes !!! " << endl;
1269 return(1);
1270 }
1271 s2 += s.substr(p, q2-p+1);
1272 p = q2+1; continue;
1273 }
1274 // cout << "DBG: " << s2 << " p= " << p << " q= " << q << " L= " << l << endl;
1275 if ((q>0) && (s[q-1] == '\\')) { // Escape character \$
1276 s2 += (s.substr(p,q-1-p) + '$') ; p = q+1;
1277 continue;
1278 }
1279 if (q >= l-1) {
1280 cerr << " Syntax error - line ending with $ !!! " << endl;
1281 return(2);
1282 }
1283 vn = "";
1284 if ( s[q+1] == '{' ) { // Variable in the form ${name}
1285 q2 = s.find('}',q+1);
1286 if (q2 >= l) {
1287 cerr << " Syntax error - Unbalenced brace {} !!! " << endl;
1288 return(3);
1289 }
1290 vn = s.substr(q+2,q2-q-2);
1291 q2++;
1292 fgvarapp = true;
1293 }
1294 else if ( s[q+1] == '(' ) { // Variable in the form $(name)
1295 q2 = s.find(')',q+1);
1296 if (q2 >= l) {
1297 cerr << " Syntax error - Unbalenced parenthesis () !!! " << endl;
1298 return(3);
1299 }
1300 vn = s.substr(q+2,q2-q-2);
1301 q2++;
1302 }
1303 else if ( s[q+1] == '[' ) { // Variable in the form $[varname] -> This is $$varname
1304 q2 = s.find(']',q+1);
1305 if (q2 >= l) {
1306 cerr << " Syntax error - Unbalenced brace [] !!! " << endl;
1307 return(4);
1308 }
1309 vn = s.substr(q+2,q2-q-2);
1310 if (!Var2Str(vn, vv)) return(5);
1311 vn = vv;
1312 q2++;
1313 }
1314 else {
1315 if (s[q+1] == '#' ) q3 = q+2; // Variable in the form $#varname
1316 else q3 = q+1;
1317 q2 = s.find_first_of(" .:+-*/,[](){}&|!$\"'<>^%=#@\\",q3);
1318 if (q2 > l) q2 = l;
1319 q3 = q2;
1320 vn = s.substr(q+1, q2-q-1);
1321 // Si variable de type $varname[index] : element de tableau
1322 if ((q2 < l) && (s[q2] == '[') ) {
1323 q3 = s.find_first_of("]",q2+1);
1324 string sia = s.substr(q2+1, q3-q2-1);
1325 if (sia.length() < 1) {
1326 cerr << " Syntax error - in $varname[index] : $"
1327 << vn << "[" << sia <<"]" << endl;
1328 return(4);
1329 }
1330 if (isalpha(sia[0])) {
1331 string sia2;
1332 if (!Var2Str(sia, sia2) || (sia2.length() < 1)) {
1333 cerr << " Syntax error - in $varname[index] : $"
1334 << vn << "[" << sia <<"]" << endl;
1335 return(4);
1336 }
1337 sia = sia2;
1338 }
1339 int rcdia = ctoi(sia.c_str(), &iarr);
1340 if (rcdia < 0) {
1341 cerr << " Syntax error - in $varname[iarr] : $"
1342 << vn << "[" << sia <<"]" << endl;
1343 return(4);
1344 }
1345 }
1346 }
1347 if (fgvarapp) {
1348 if (!GetVarApp(vn, vv))
1349 if (!Var2Str(vn, vv)) return(5);
1350 s2 += (s.substr(p, q-p) + vv);
1351 p = q2;
1352 }
1353 else if (iarr < 0) {
1354 if (!Var2Str(vn, vv)) return(5);
1355 s2 += (s.substr(p, q-p) + vv);
1356 p = q2;
1357 }
1358 else {
1359 if (! Var2Str(vn, iarr, vv) ) {
1360 cerr << " Substitution error - word index out of range in "
1361 << "$varname[iarr] : $" << vn << "[" << iarr <<"]" << endl;
1362 return(4);
1363 }
1364 else s2 += (s.substr(p, q-p) + vv);
1365 p = q3+1;
1366 }
1367}
1368if (p < l) s2 += s.substr(p);
1369
1370p = s2.find_first_not_of(" \t");
1371if (p < l) s2 = s2.substr(p);
1372
1373return(0);
1374}
1375
1376/* --Methode-- */
1377bool Commander::Var2Str(string const & vn, string & vv)
1378{
1379if (vn.length() < 1) {
1380 cerr << " Commander::Var2Str/Error: length(varname=" << vn << ")<1 !" << endl;
1381 vv = ""; return(false);
1382}
1383// Variable de type $# $0 $1 ... (argument de .pic ou de script)
1384int ka = 0;
1385char buff[32];
1386
1387if (vn == "#") {
1388 if (ArgsStack.empty()) {
1389 cerr << " Commander::Var2Str/Error: ArgsStack empty ! "
1390 << " ($" << vn << ")" << endl;
1391 vv = ""; return(false);
1392 }
1393 char buff[32];
1394 long an = ArgsStack.top().size();
1395 if (an > 0) an--; // Pour se conformer a l'usage de csh : Nb args sans le $0
1396 sprintf(buff,"%ld", an);
1397 vv = buff; return(true);
1398}
1399else if (vn == "*") {
1400 if (ArgsStack.empty()) {
1401 cerr << " Commander::Var2Str/Error: ArgsStack empty ! "
1402 << " ($" << vn << ")" << endl;
1403 vv = ""; return(false);
1404 }
1405 vv = ArgsStack.top()[0];
1406 for(unsigned int ssk=1; ssk<ArgsStack.top().size(); ssk++) vv += ArgsStack.top()[ssk];
1407 return(true);
1408}
1409else if (ctoi(vn.c_str(), &ka) > 0) { // $0 $1 $2 ...
1410 if (ArgsStack.empty()) {
1411 cerr << " Commander::Var2Str/Error: ArgsStack empty ! "
1412 << " ($" << vn << ")" << endl;
1413 vv = ""; return(false);
1414 }
1415 if ( (ka < 0) || (ka >= (int)ArgsStack.top().size()) ) {
1416 cerr << " Commander::Var2Str/Error: ArgsStack index <0 or >=args.size() ! "
1417 << " ($" << vn << ")" << endl;
1418 vv = ""; return(false);
1419 }
1420 vv = ArgsStack.top()[ka]; return(true);
1421}
1422else if (vn[0] == '#') { // Variable de type $#vname --> size(vname)
1423 CmdVarList::iterator it = variables.find(vn.substr(1));
1424 if (it == variables.end()) {
1425 cerr << " Commander::Var2Str/Error #vname Undefined variable "
1426 << vn << " ! " << endl;
1427 vv = ""; return(false);
1428 }
1429 sprintf(buff,"%d", (int)(*it).second.size());
1430 vv = buff; return(true);
1431}
1432else if (vn == "status") {
1433 sprintf(buff,"%d", _xstatus);
1434 vv = buff;
1435 return true;
1436}
1437else if ((vn == "retstr") || (vn == "retval")) {
1438 vv = _retstr;
1439 return true;
1440}
1441else { // Variable de l'interpreteur, ou de l'environnement application , env. global
1442 if (GetVar(vn, vv)) return true;
1443 else if (GetVarApp(vn, vv)) return true;
1444 else if (GetVarEnv(vn, vv)) return true;
1445 else {
1446 cerr << " Commander::Var2Str/Error Undefined variable "
1447 << vn << " ! " << endl;
1448 vv = ""; return false;
1449 }
1450}
1451
1452return false;
1453}
1454
1455/* --Methode-- */
1456bool Commander::SetVariable(string const & vn, string const & vv)
1457{
1458 // On verifie si le nom est de type vname[idx]
1459 size_t p,q,l;
1460 l = vn.length();
1461 p = vn.find('[');
1462 if (p < l) {
1463 q = vn.find(']');
1464 if (q != (l-1)) {
1465 cout << "Commander::Str2Var/SetVar() - Bad varname with []: "
1466 << vn << endl;
1467 return false;
1468 }
1469 string vna = vn.substr(0, p);
1470 string sia = vn.substr(p+1, q-(p+1));
1471 if (isalpha(sia[0])) {
1472 string sia2;
1473 if (!Var2Str(sia, sia2) || (sia2.length() < 1)) {
1474 cerr << "Commander::Str2Var/SetVar() Syntax error- varname[index]:"
1475 << vn << endl;
1476 return false;
1477 }
1478 sia = sia2;
1479 }
1480 int iarr;
1481 int rcdia = ctoi(sia.c_str(), &iarr);
1482 if (rcdia < 0) {
1483 cerr << "Commander::Str2Var/SetVar() Syntax error- varname[iarr]: "
1484 << vn << endl;
1485 return false;
1486 }
1487 return SetVar(vna, iarr, vv);
1488 }
1489 else {
1490 if (vn == "status") {
1491 _xstatus = atoi(vv.c_str());
1492 return true;
1493 }
1494 else if (vn == "retstr") {
1495 _retstr = vv;
1496 return true;
1497 }
1498 else return SetVar(vn, vv);
1499 }
1500}
1501
1502/* --Methode-- */
1503bool Commander::GetVar(string const & vn, string & vv)
1504{
1505 CmdVarList::iterator it = variables.find(vn);
1506 if (it == variables.end()) {
1507 vv = "";
1508 return false;
1509 }
1510 vv = (*it).second[0];
1511 if ((*it).second.size() > 1) {
1512 for(unsigned int k=1; k<(*it).second.size(); k++) {
1513 vv += ' '; vv += (*it).second[k];
1514 }
1515 }
1516 return true;
1517}
1518
1519/* --Methode-- */
1520bool Commander::GetVar(string const & vn, int idx, string & vv)
1521{
1522 vv = "";
1523 CmdVarList::iterator it = variables.find(vn);
1524 if (it == variables.end()) return false;
1525 if ((idx < 0) || (idx > (int)(*it).second.size()-1))
1526 return false;
1527 vv = (*it).second[idx];
1528 return true;
1529}
1530
1531/* --Methode-- */
1532bool Commander::GetVar(string const & vn, vector<string> & vv)
1533{
1534 vv.clear();
1535 // vv.erase(vv.begin(),vv.end());
1536 CmdVarList::iterator it = variables.find(vn);
1537 if (it == variables.end()) return false;
1538 vv = (*it).second;
1539 return true;
1540}
1541
1542/* --Methode-- */
1543bool Commander::SetVar(string const & vn, string const & val)
1544{
1545 if ( !CheckVarName(vn) ) {
1546 cerr << "Commander::SetVar( " << vn << " ...) Bad VarName " << endl;
1547 return(false);
1548 }
1549 bool fg = false;
1550 vector<string> nouv;
1551 nouv.push_back(val);
1552 CmdVarList::iterator it = variables.find(vn);
1553 if (it == variables.end()) variables[vn] = nouv;
1554 else {
1555 (*it).second = nouv;
1556 fg = true;
1557 }
1558 return fg;
1559}
1560
1561/* --Methode-- */
1562bool Commander::SetVar(string const & vn, int idx, string const & val)
1563{
1564 if ( !CheckVarName(vn) ) {
1565 cerr << "Commander::SetVar( " << vn << " ,idx, ...) Bad VarName " << endl;
1566 return(false);
1567 }
1568 if ((vn == "status") || (vn == "retstr")) {
1569 cerr << "Commander::SetVar(vn,idx,val) ERROR - special var status/retstr "
1570 << endl;
1571 return(false);
1572 }
1573 if (idx < 0) {
1574 cout << "Commander::SetVar(vn," << idx << ",...) Error idx < 0" << endl;
1575 return(false);
1576 }
1577 bool fg = false;
1578 CmdVarList::iterator it = variables.find(vn);
1579 if (it == variables.end()) {
1580 vector<string> nouv;
1581 for(int j=0; j<idx; j++) nouv.push_back("");
1582 nouv.push_back(val);
1583 variables[vn] = nouv;
1584 }
1585 else {
1586 if (idx >= (int)(*it).second.size())
1587 for(int j=(*it).second.size(); j<=idx; j++) (*it).second.push_back("");
1588 (*it).second[idx] = val;
1589 fg = true;
1590 }
1591 return fg;
1592}
1593
1594/* --Methode-- */
1595bool Commander::SetVar(string const & vn, vector<string> const & val)
1596{
1597 if ( !CheckVarName(vn) ) {
1598 cerr << "Commander::SetVar( " << vn << " ...) Bad VarName " << endl;
1599 return(false);
1600 }
1601 if ((vn == "status") || (vn == "retstr")) {
1602 cerr << "Commander::SetVar(vn, vector<string>) ERROR - special var status/retstr "
1603 << endl;
1604 return(false);
1605 }
1606 bool fg = false;
1607 CmdVarList::iterator it = variables.find(vn);
1608 if (it == variables.end()) variables[vn] = val;
1609 else {
1610 (*it).second = val;
1611 fg = true;
1612 }
1613 return fg;
1614}
1615
1616/* --Methode-- */
1617bool Commander::CheckVarName(string const & vn)
1618{
1619 size_t l,k;
1620 l = vn.length();
1621 if (l < 1) return false;
1622 if (!isalpha(vn[0])) return false;
1623 for(k=1; k<l; k++)
1624 if ((!isalnum(vn[k])) && (vn[k] != '_')) return false;
1625 return true;
1626}
1627
1628/* --Methode-- */
1629bool Commander::DeleteVar(string const & vn)
1630{
1631 CmdVarList::iterator it = variables.find(vn);
1632 if (it == variables.end()) {
1633 cerr << "Commander::DeleteVar() Var " << vn << " undefined!" << endl;
1634 return false;
1635 }
1636 variables.erase(it);
1637 return true;
1638}
1639
1640/* --Methode-- */
1641void Commander::ListVar()
1642{
1643 cout << " ---- Commander::ListVar() List of defined variables ---- "
1644 << endl;
1645 CmdVarList::iterator it;
1646 for(it = variables.begin(); it != variables.end(); it++) {
1647 string vn = (*it).first;
1648 int vs = (*it).second.size();
1649 cout << vn << " -> Size= " << vs << endl;
1650 }
1651 cout << "---------------------------------------------------------- "
1652 << endl;
1653}
1654
1655/* --Methode-- */
1656bool Commander::GetVarApp(string const & vn, string & vv)
1657{
1658 vv = "";
1659 // cout << " Commander::GetVarApp() Not available ! " << endl;
1660 return false;
1661}
1662
1663/* --Methode-- */
1664bool Commander::SetVarApp(string const & vn, string const & vv)
1665{
1666 // cout << " Commander::SetVarApp() Not available ! " << endl;
1667 return false;
1668}
1669
1670/* --Methode-- */
1671bool Commander::DeleteVarApp(string const & vn)
1672{
1673 // cout << " Commander::DeleteVarApp() Not available ! " << endl;
1674 return false;
1675}
1676
1677/* --Methode-- */
1678void Commander::ListVarApp()
1679{
1680 // cout << " Commander::ListVarApp() Not available ! " << endl;
1681 return;
1682}
1683
1684
1685/* --Methode-- */
1686bool Commander::GetVarEnv(string const & vn, string & vv)
1687{
1688 char* vev = getenv(vn.c_str());
1689 if (vev) {
1690 vv = vev;
1691 return true;
1692 }
1693 else {
1694 vv = "";
1695 return false;
1696 }
1697}
1698
1699/* --Methode-- */
1700bool Commander::SetVarEnv(string const & vn, string const & vv)
1701{
1702 string pev = vn;
1703 pev += '=';
1704 pev += vv;
1705// if defined(Linux) || defined(AIX)
1706// Reza - 28/04/2004
1707// putenv de Linux ne declare pas la variable char *string const
1708// On ne doit meme pas utiliser une variable automatique
1709// J'alloue donc un nouveau tableau - mais qui va le liberer ?
1710// Idem AIX , Reza Dec 2005
1711// Pb apparu avec g++ 4 sur darwin (Mac) - Jan 2006
1712// Je fais copie pour tout le monde
1713 char* bev = new char[pev.size()+1];
1714 strcpy(bev, pev.c_str());
1715 if (putenv(bev) == 0) return true;
1716// else
1717// if (putenv(pev.c_str()) == 0) return true;
1718// endif
1719 else return false;
1720}
1721
1722/* --Methode-- */
1723bool Commander::DeleteVarEnv(string const & vn)
1724{
1725 // cout << " Commander::DeleteVarEnv() Not available ! " << endl;
1726 return false;
1727}
1728
1729/* --Methode-- */
1730void Commander::ListVarEnv()
1731{
1732 cout << " Commander::ListVarEnv() Not available ! " << endl;
1733 return;
1734}
1735
1736
1737/* --Methode-- */
1738string Commander::GetTmpDir()
1739{
1740 return("/tmp");
1741}
1742
1743/* --Methode-- */
1744void Commander::SetCurrentPrompt(const char* pr)
1745{
1746 curprompt = pr;
1747}
1748
1749/* --Methode-- */
1750void Commander::ShowMessage(const char * msg, int att)
1751{
1752 cout << msg ;
1753}
1754
1755
1756
1757/* --Methode-- */
1758int Commander::EvaluateTest(vector<string> & args, string & line, bool & res)
1759{
1760 res = true;
1761 if ((args.size() != 6) || (args[5] != "then") ||
1762 (args[0] != "(") || (args[4] != ")") ) return(1);
1763 if (args[2] == "==") res = (args[1] == args[3]);
1764 else if (args[2] == "!=") res = (args[1] != args[3]);
1765 else if (args[2] == "<")
1766 res = (atof(args[1].c_str()) < atof(args[3].c_str()));
1767 else if (args[2] == ">")
1768 res = (atof(args[1].c_str()) > atof(args[3].c_str()));
1769 else if (args[2] == "<=")
1770 res = (atof(args[1].c_str()) <= atof(args[3].c_str()));
1771 else if (args[2] == ">=")
1772 res = (atof(args[1].c_str()) >= atof(args[3].c_str()));
1773 else return(2);
1774 return(0);
1775}
1776
1777
1778
1779/* --Methode-- */
1780void Commander::PushStack(vector<string>* pargs)
1781{
1782 // We push the argument list (args) on the stack if not NULL
1783 if (pargs != NULL) ArgsStack.push(*pargs);
1784 // We push also CommanderBloc and testresult on the stack
1785 CmdBlks.push(NULL);
1786 if (TestsStack.size()>0) {
1787 TestsStack.top().tstresit=tresit_;
1788 TestsStack.top().tstcurres=curtestresult_;
1789 }
1790 TstStatus tstat;
1791 tstat.tstcurres=curtestresult_;
1792 TestsStack.push(tstat);
1793}
1794
1795/* --Methode-- */
1796void Commander::PopStack(bool psta)
1797{
1798 // We remove the argument list (args) from the stack
1799 if (psta) ArgsStack.pop();
1800 // And CommanderBloc and TestResult from the corresponding stacks
1801 CommanderBloc* curb = CmdBlks.top();
1802 while (curb != NULL) {
1803 CommanderBloc* parb = curb->Parent();
1804 delete curb; curb = parb;
1805 }
1806 CmdBlks.pop();
1807 TestsStack.pop();
1808 tresit_=TestsStack.top().tstresit;
1809 curtestresult_=TestsStack.top().tstcurres;
1810}
1811
1812/* --Methode-- */
1813int Commander::ExecuteCommandLine(string & kw, vector<string> & tokens, string & toks)
1814{
1815int rc = 0;
1816
1817// >>>>>>>>>>> Commande d'interpreteur
1818if (kw == "help") {
1819 if (tokens.size() > 0) cout << GetUsage(tokens[0]) << endl;
1820 else {
1821 string kwh = "Commander";
1822 cout << GetUsage(kwh) << endl;
1823 }
1824 }
1825else if (kw == "sleep") {
1826 if (tokens.size() < 1) {
1827 cout << "Commander::Interpret() Usage: sleep nsec " << endl;
1828 return(1);
1829 }
1830 int nsec = atoi(tokens[0].c_str());
1831 cout << "Commander::Interpret() sleep " << nsec << " seconds" << endl;
1832 sleep(nsec);
1833}
1834
1835else if (kw == "set") {
1836 if (tokens.size() < 2) {
1837 cout << "Commander::Interpret() Usage: set varname value or set vecvar ( w1 w2 ... ) " << endl;
1838 return(1);
1839 }
1840
1841 if (tokens.size() == 2)
1842 SetVariable(tokens[0], tokens[1]);
1843 else {
1844 if ( (tokens[1] != "(") || (tokens[tokens.size()-1] != ")") ) {
1845 cout << "Commander::Interpret() Usage: set vecvar ( w1 w2 ... ) " << endl;
1846 return(1);
1847 }
1848 string vname = tokens[0];
1849 vector<string>::iterator vit;
1850 vit = tokens.begin(); tokens.erase(vit);
1851 vit = tokens.begin(); tokens.erase(vit);
1852 tokens.pop_back();
1853 SetVar(vname, tokens);
1854 }
1855 return 0;
1856}
1857else if (kw == "var2words") {
1858 if (tokens.size() < 2) {
1859 cout << "Commander::Interpret() Usage: var2words varname wordvarname [sep]" << endl;
1860 return(1);
1861 }
1862 char sep = ' ';
1863 if (tokens.size() > 2) sep = tokens[2][0];
1864 string vv;
1865 if (!GetVar(tokens[0], vv)) {
1866 cout << "Commander::Interpret() var2words/Error No variable with name " << tokens[0] << endl;
1867 return 2;
1868 }
1869 vector<string> vs;
1870 FillVStringFrString(vv, vs, sep);
1871 SetVar(tokens[1], vs);
1872}
1873else if (kw == "alias") {
1874 if (tokens.size() < 2) { cout << "Commander::Interpret() Usage: alias aliasname string" << endl; return(0); }
1875 if ((tokens[0].length() < 1) || !isalpha((int)tokens[0][0]) ) {
1876 cerr << "Commander::Interpret()/Error alias name should start with alphabetic" << endl;
1877 return(1);
1878 }
1879 string xx = tokens[1];
1880 for (unsigned int kk=2; kk<tokens.size(); kk++) xx += (' ' + tokens[kk]);
1881 mAliases[tokens[0]] = xx;
1882}
1883
1884else if ( (kw == "unset") || (kw == "clearvar") ) {
1885 if (tokens.size() < 1) {
1886 cout << "Commander::Interpret() Usage: unset/clearvar varname" << endl;
1887 return(1);
1888 }
1889 else DeleteVar(tokens[0]);
1890}
1891// Evaluation d'expression en notation polonaise inverse
1892else if (kw == "rpneval") {
1893 try {
1894 RPNExpressionEvaluator rpn(tokens, 1);
1895 double res = rpn.Value();
1896 string vv = cval_dble2str(res);
1897 SetVariable(tokens[0],vv);
1898 return 0;
1899 }
1900 catch (RPNExprException& rpnerr) {
1901 cerr << " rpneval: Syntax error - Msg=" << rpnerr.Msg()
1902 << " \n Line=" << toks << endl;
1903 return 98;
1904 }
1905}
1906else if (kw == "echo") {
1907 for (unsigned int ii=0; ii<tokens.size(); ii++)
1908 cout << tokens[ii] << " " ;
1909 cout << endl;
1910 }
1911else if (kw == "echo2file") {
1912 if (tokens.size() < 1) {
1913 cout << "Commander::Interpret() Usage: echo2file filename [string ] " << endl;
1914 return(1);
1915 }
1916 ofstream ofs(tokens[0].c_str(), ios::app);
1917 for (unsigned int ii=1; ii<tokens.size(); ii++)
1918 ofs << tokens[ii] << " " ;
1919 ofs << endl;
1920 }
1921else if (kw == "readstdin") {
1922 if (tokens.size() < 1) { cout << "Commander::Interpret() Usage: readstdin varname" << endl; return(0); }
1923 if ((tokens[0].length() < 1) || !isalpha((int)tokens[0][0]) ) {
1924 cerr << "Commander::Interpret()/Error Variable name should start with alphabetic" << endl;
1925 return(0);
1926 }
1927 ShowMessage(">>> Reading From StdIn \n", _MAGENTA_);
1928 cout << tokens[0] << " ? " << endl;
1929 SetVar(tokens[0], GetStringFrStdin(this) );
1930 }
1931
1932else if (kw == "listvars" || kw == "listvar") ListVar();
1933else if (kw == "listalias") {
1934 cout << "Commander::Interpret() Alias List , AliasName = Value \n";
1935 CmdStrList::iterator it;
1936 for(it = mAliases.begin(); it != mAliases.end(); it++)
1937 cout << (*it).first << " = " << (*it).second << "\n";
1938 cout << endl;
1939 }
1940else if (kw == "listcommands") {
1941 cout << "---- Commander::Interpret() Command List ----- \n";
1942 CmdExmap::iterator it;
1943 int kc = 0;
1944 for(it = cmdexmap.begin(); it != cmdexmap.end(); it++) {
1945 cout << (*it).first << " ";
1946 kc++;
1947 if (kc >= 5) { cout << "\n"; kc = 0; }
1948 }
1949 cout << endl;
1950 }
1951else if (kw == "listscripts") {
1952 cout << "---- Commander::Interpret() Script List ----- \n";
1953 for(ScriptList::iterator sit = mScripts.begin();
1954 sit != mScripts.end(); sit++)
1955 cout << " Script: " << (*sit).second->Name() << " - "
1956 << (*sit).second->Comment() << endl;
1957}
1958else if (kw == "clearscript") {
1959 if (tokens.size() < 1) {
1960 cout << "Commander::Interpret() Usage: clearscript scriptname" << endl;
1961 return(0);
1962 }
1963 ScriptList::iterator sit = mScripts.find(tokens[0]);
1964 if (sit == mScripts.end()) {
1965 cout << "Commander::Interpret() No script with name" << tokens[0] << endl;
1966 return(0);
1967 }
1968 else {
1969 delete (*sit).second;
1970 mScripts.erase(sit);
1971 cout << "Commander::Interpret() script " << tokens[0] << " cleared" << endl;
1972 return(0);
1973 }
1974}
1975//---------------------------------------------
1976//--- Commandes de gestion des threads ------
1977//---------------------------------------------
1978else if (kw == "thrlist") {
1979 ListThreads();
1980 return(0);
1981}
1982else if ( (kw == "killthr") || (kw == "cancelthr") ) {
1983 if (tokens.size() < 1) {
1984 cout << "Commander::Interpret() Usage: killthr/cancelthr thrid" << endl;
1985 return(0);
1986 }
1987 uint_8 id = atol(tokens[0].c_str());
1988 bool fgkill = false;
1989 if (kw == "killthr") fgkill = true;
1990 StopThr(id, fgkill);
1991 return (0);
1992}
1993else if (kw == "waitthr") {
1994 WaitThreads();
1995 return (0);
1996}
1997else if (kw == "cleanthrlist") {
1998 CleanThrList();
1999 return (0);
2000}
2001
2002else if (kw == "traceon") { cout << "Commander::Interpret() -> Trace ON mode " << endl; trace = true; }
2003else if (kw == "traceoff") { cout << "Commander::Interpret() -> Trace OFF mode " << endl; trace = false; }
2004else if (kw == "timingon") {
2005 cout << "Commander::Interpret() -> Timing ON mode " << endl;
2006 if (gltimer) delete gltimer; gltimer = new Timer("PIA-CmdInterpreter "); timing = true;
2007 }
2008else if (kw == "timingoff") {
2009 cout << "Commander::Interpret() -> Timing OFF mode " << endl;
2010 if (gltimer) delete gltimer; gltimer = NULL; timing = false;
2011 }
2012else if (kw == "varcexpon")
2013 { cout << "Commander::Interpret() -> Activating variable name decoding in expression evaluation " << endl; varcexp = true; }
2014else if (kw == "varcexpoff")
2015 { cout << "Commander::Interpret() -> Deactivating variable name decoding in expression evaluation " << endl; varcexp = false; }
2016else if (kw == "exec") {
2017 if (tokens.size() < 1) { cout << "Commander::Interpret() Usage: exec filename" << endl; return(0); }
2018 ExecFile(tokens[0], tokens);
2019 }
2020else if (kw == "autoiniranf") {
2021 AutoInitRand(1);
2022 return(0);
2023}
2024else if (kw == "shell") {
2025 if (tokens.size() < 1) { cout << "Commander::Interpret() Usage: shell cmdline" << endl; return(0); }
2026 string cmd;
2027 for (unsigned int ii=0; ii<tokens.size(); ii++)
2028 cmd += (tokens[ii] + ' ');
2029 system(cmd.c_str());
2030 }
2031else if (kw == "cshell") {
2032 if(tokens.size()<1) {cout<<"Commander::Interpret() Usage: cshell cmdline"<<endl; return(0);}
2033 string cmd="";
2034 for(unsigned int ii=0;ii<tokens.size();ii++) cmd+=(tokens[ii]+' ');
2035 CShellExecute(cmd);
2036 }
2037
2038// Execution d'une commande enregistree
2039else rc = ExecuteCommand(kw, tokens, toks);
2040
2041if (timing) gltimer->Split();
2042return(rc);
2043}
2044
2045/* --Methode-- */
2046int Commander::ParseLineExecute(string& line, bool qw)
2047 // Si qw == true, on decoupe entre '' ou "" ou espaces
2048{
2049vector<string> tokens;
2050vector<bool> qottoks;
2051string kw, toks;
2052if (line.length() < 1) return(0);
2053LineToWords(line, kw, tokens, qottoks, toks, qw);
2054return(ExecuteCommand(kw, tokens, toks));
2055}
2056
2057/* --Methode-- */
2058int Commander::ExecuteCommand(string& keyw, vector<string>& args, string& toks)
2059{
2060 int rc = -1;
2061 CmdExmap::iterator it = cmdexmap.find(keyw);
2062 if (it == cmdexmap.end()) cout << "No such command : " << keyw << " ! " << endl;
2063 else {
2064 if ((*it).second.cex) {
2065 // Doit-on l'executer sous forme de thread separe ?
2066 if ( (args.size()>0) && (args[args.size()-1] == "&") ) {
2067 if ((*it).second.cex->IsThreadable(keyw) ) {
2068 ThrId++;
2069 CommandExeThr * thr =
2070 new CommandExeThr(ThrId, (*it).second.cex, keyw, args, toks);
2071 CmdThrExeList.push_back(thr);
2072 cout << " Commander::ExecuteCommand() : Thread execution of command " << keyw << endl;
2073 thr->start();
2074 if (CmdThrExeList.size() > 5) CleanThrList();
2075 rc = 0;
2076 }
2077 else {
2078 args.erase(args.end()-1);
2079 for(size_t k=toks.size()-1; k>0; k--)
2080 if (toks[k] == '&') { toks[k] = ' '; break; }
2081 cout << " Commander::ExecuteCommand() : Thread execution NOT available for" << keyw << endl;
2082 rc = (*it).second.cex->Execute(keyw, args, toks);
2083 }
2084 }
2085 else rc = (*it).second.cex->Execute(keyw, args, toks);
2086 }
2087 else cout << "Dont know how to execute " << keyw << " ? " << endl;
2088 }
2089 return(rc);
2090}
2091
2092/* --Methode-- */
2093int Commander::ExecFile(string& file, vector<string>& args)
2094{
2095char line_buff[1024];
2096FILE *fip;
2097int rcc = 0;
2098if ( (fip = fopen(file.c_str(),"r")) == NULL ) {
2099 if (file.find('.') >= file.length()) {
2100 cout << "Commander::Exec(): Error opening file " << file << endl;
2101 file += ".pic";
2102 cout << " Trying file " << file << endl;
2103 fip = fopen(file.c_str(),"r");
2104 }
2105 }
2106
2107if(fip == NULL) {
2108 cerr << "Commander::Exec() Error opening file " << file << endl;
2109 hist << "##! Commander::Exec() Error opening file " << file << endl;
2110 return(0);
2111 }
2112
2113// hist << "### Executing commands from " << file << endl;
2114PushStack(args);
2115if (trace) {
2116 ShowMessage("### Executing commands from ", _MAGENTA_);
2117 ShowMessage(file.c_str(), _MAGENTA_);
2118 ShowMessage("\n", _MAGENTA_);
2119 }
2120
2121bool ohv = histon;
2122histon = false;
2123while (fgets(line_buff,1023,fip) != NULL)
2124 {
2125 if (trace) ShowMessage(line_buff, _MAGENTA_);
2126 line_buff[strlen(line_buff)-1] = '\0'; /* LF/CR de la fin */
2127 string line(line_buff);
2128 rcc = Interpret(line);
2129 if ((rcc == CMD_RETURN_RC) || (rcc == CMD_BREAKEXE_RC)) break;
2130 }
2131histon = ohv;
2132
2133// hist << "### End of Exec( " << file << " ) " << endl;
2134if (trace) {
2135 ShowMessage("### End of Exec( ", _MAGENTA_);
2136 ShowMessage(file.c_str(), _MAGENTA_);
2137 ShowMessage(" ) \n", _MAGENTA_);
2138 }
2139
2140PopStack(true);
2141
2142return(0);
2143}
2144
2145/* --Methode-- */
2146void Commander::ListThreads()
2147{
2148 cout << "--- Commander::ListThreads()- command execution threads NThread="
2149 << CmdThrExeList.size() << " ---" << endl;
2150 for(list<CommandExeThr *>::iterator tit = CmdThrExeList.begin();
2151 tit != CmdThrExeList.end(); tit++) {
2152 cout << "Id=" << (*tit)->Id();
2153 if ( (*tit)->IfRunning() ) cout << " Executing";
2154 else if ( (*tit)->IfDone() ) cout << " Finished , Rc= " << (*tit)->getRC();
2155 else cout << " Stopped/Canceled";
2156 cout << " (Cmd= " << (*tit)->Keyword() << " " << (*tit)->Tokens().substr(0,35);
2157 if ((*tit)->Tokens().length() > 35) cout << "... )" << endl;
2158 else cout << " )" << endl;
2159 }
2160}
2161/* --Methode-- */
2162void Commander::StopThr(uint_8 id, bool fgkill)
2163{
2164 for(list<CommandExeThr *>::iterator tit = CmdThrExeList.begin();
2165 tit != CmdThrExeList.end(); tit++) {
2166 if ( ((*tit)->Id() == id) && ((*tit)->IfRunning()) ) {
2167 if (fgkill) {
2168 (*tit)->kill(SIGUSR1);
2169 cout << "Commander::StopThr() Send signal SIGUSR1 to Thread Id= " << id << endl;
2170 }
2171 else {
2172 (*tit)->cancel();
2173 cout << "Commander::StopThr() Canceling Thread Id= " << id << endl;
2174 }
2175 return;
2176 }
2177 }
2178 cout << "Commander::StopThr()/Error: No active thread with Id= " << id << endl;
2179}
2180
2181/* --Methode-- */
2182void Commander::CleanThrList()
2183{
2184 cout << "---- Commander::CleanThrList() Cleaning thrlist ----- \n";
2185 list<CommandExeThr *> thrcopie;
2186 int ncl = 0;
2187 for(list<CommandExeThr *>::iterator tit = CmdThrExeList.begin();
2188 tit != CmdThrExeList.end(); tit++) {
2189 if ( ((*tit)->IfEnded() || (*tit)->IfStopped()) && (ncl < 3) ) {
2190 cout << " Thread Id= " << (*tit)->Id() << " rc= " << (*tit)->getRC() << " Cleaned" << endl;
2191 delete (*tit);
2192 ncl++;
2193 }
2194 else thrcopie.push_back((*tit));
2195 }
2196 CmdThrExeList = thrcopie;
2197 cout << " ... " << CmdThrExeList.size() << " threads still active " << endl;
2198}
2199
2200/* --Methode-- */
2201void Commander::WaitThreads()
2202{
2203 cout << "---- Commander::WaitThreads() Wait/Join command execution threads - NThread="
2204 << CmdThrExeList.size() << " ----- " << endl;
2205 for(list<CommandExeThr *>::iterator tit = CmdThrExeList.begin();
2206 tit != CmdThrExeList.end(); tit++) {
2207 try {
2208 if (! (*tit)->IfDone()) (*tit)->join();
2209 }
2210 catch (std::exception & e) {
2211 cout << " Commander::WaitThreads()/Exception msg= " << e.what() << endl;
2212 }
2213 cout << " Joined thread Id= " << (*tit)->Id() << " rc= " << (*tit)->getRC() << endl;
2214 delete (*tit);
2215 }
2216 CmdThrExeList.erase(CmdThrExeList.begin(), CmdThrExeList.end());
2217}
2218
2219/* --Methode-- */
2220int Commander::CShellExecute(string cmd)
2221{
2222 if(cmd.size()<=0) return -1;
2223
2224 string fname = GetTmpDir(); fname += "cshell_exec_pia.csh";
2225
2226 string cmdrm = "rm -f " + fname;
2227 system(cmdrm.c_str());
2228
2229 FILE *fip = fopen(fname.c_str(),"w");
2230 if(fip==NULL) {
2231 cout << "Commander/CShellExecute_Error: fopen("<<fname<<") failed"<<endl;
2232 return -2;
2233 }
2234 fprintf(fip,"#!/bin/csh\n\n");
2235 fprintf(fip,"%s\n",cmd.c_str());
2236 fprintf(fip,"\nexit 0\n");
2237 fclose(fip);
2238
2239 cmd = "csh "; cmd += fname;
2240 system(cmd.c_str());
2241
2242 system(cmdrm.c_str());
2243
2244 return 0;
2245}
2246
2247static string* videstr = NULL;
2248/* --Methode-- */
2249string& Commander::GetUsage(const string& kw)
2250{
2251bool fndok = false;
2252CmdExmap::iterator it = cmdexmap.find(kw);
2253if (it == cmdexmap.end()) {
2254 it = helpexmap.find(kw);
2255 if (it != helpexmap.end()) fndok = true;
2256 }
2257 else fndok = true;
2258if (fndok) return( (*it).second.us );
2259// Keyword pas trouve
2260if (videstr == NULL) videstr = new string("");
2261*videstr = "Nothing known about " + kw + " ?? ";
2262return(*videstr);
2263
2264}
2265
2266
2267/* Les definitions suivantes doivent se trouver ds l'en-tete du fichier LaTeX
2268 \newcommand{\piacommand}[1]{
2269 \framebox{\bf \Large #1 } \index{#1} % (Command)
2270 }
2271
2272 \newcommand{\piahelpitem}[1]{
2273 \framebox{\bf \Large #1 } \index{#1} (Help item)
2274 }
2275
2276 \newcommand{\myppageref}[1]{ (p. \pageref{#1} ) }
2277*/
2278
2279// Fonction qui remplace tout caractere non alphanumerique en Z
2280static void check_latex_reflabel(string & prl)
2281{
2282 for(unsigned int k=0; k<prl.length(); k++)
2283 if (! isalnum(prl[k]) ) prl[k] = 'Z';
2284}
2285
2286// Fonction qui remplace _ en \_
2287static string check_latex_underscore(string const & mot)
2288{
2289 string rs;
2290 for(unsigned int k=0; k<mot.length(); k++) {
2291 if (mot[k] == '_') rs += "\\_";
2292 else rs += mot[k];
2293 }
2294 return rs;
2295}
2296
2297/* --Methode-- */
2298/*!
2299 \brief Produces a LaTeX file containing the registered command helps
2300 The file \b fname is created and can be inserted into a LaTeX document
2301 in order to produce the list of registered commands and corresponding description
2302 texts.
2303 The LaTeX file should contain the following definitions:
2304\verbatim
2305 \newcommand{\piacommand}[1]{
2306 \framebox{\bf \Large #1 } \index{#1} % (Command)
2307 }
2308
2309 \newcommand{\piahelpitem}[1]{
2310 \framebox{\bf \Large #1 } \index{#1} (Help item)
2311 }
2312
2313 \newcommand{\myppageref}[1]{ (p. \pageref{#1} ) }
2314
2315\endverbatim
2316*/
2317void Commander::HelptoLaTeX(string const & fname)
2318{
2319FILE *fip;
2320if ((fip = fopen(fname.c_str(), "w")) == NULL) {
2321 cout << "Commander::HelptoLaTex_Error: fopen( " << fname << endl;
2322 return;
2323 }
2324
2325fputs("% ----- Liste des groupes de Help ----- \n",fip);
2326fputs("List of {\\bf piapp} on-line Help groups: \n", fip);
2327fputs("\\begin{itemize} \n",fip);
2328string prl;
2329string mol;
2330CmdHGroup::iterator it;
2331for(it = cmdhgrp.begin(); it != cmdhgrp.end(); it++) {
2332 if ((*it).first == "All") continue;
2333 prl = (*it).first; check_latex_reflabel(prl);
2334 mol = check_latex_underscore((*it).first);
2335 fprintf(fip,"\\item {\\bf %s } (p. \\pageref{%s}) \n",
2336 mol.c_str(), prl.c_str());
2337}
2338
2339fputs("\\end{itemize} \n",fip);
2340
2341fputs("\\vspace*{10mm} \n",fip);
2342
2343CmdExmap::iterator ite;
2344fputs("% ----- Liste de toutes les commandes et help item ----- \n",fip);
2345fputs("\\vspace{5mm} \n",fip);
2346// fputs("\\begin{table}[h!] \n", fip);
2347fputs("\\begin{center} \n ", fip);
2348fputs("\\rule{2cm}{1mm} List of {\\bf piapp} Help items \\rule{2cm}{1mm} \\\\ \n", fip);
2349fputs("\\vspace{3mm} \n",fip);
2350fputs("\\begin{tabular}{llllll} \n", fip);
2351int kt = 0;
2352for(ite = helpexmap.begin(); ite != helpexmap.end(); ite++) {
2353 prl = (*ite).first; check_latex_reflabel(prl);
2354 mol = check_latex_underscore((*ite).first);
2355 fprintf(fip,"%s & p. \\pageref{%s} ", mol.c_str(), prl.c_str() );
2356 kt++;
2357 if (kt < 3) fputs(" & ", fip);
2358 else { fputs(" \\\\ \n", fip); kt = 0; }
2359 }
2360if (kt == 1) fputs(" & & & \\\\ \n", fip);
2361else if (kt == 2) fputs(" & \\\\ \n", fip);
2362fputs("\\end{tabular} \n", fip);
2363fputs("\\end{center} \n", fip);
2364//fputs("\\end{table} \n", fip);
2365fputs("\\newpage \n",fip);
2366
2367int gid;
2368for(it = cmdhgrp.begin(); it != cmdhgrp.end(); it++) {
2369 gid = (*it).second.gid;
2370 if (gid == 0) continue;
2371 // fputs("\\begin{table}[h!] \n",fip);
2372 fputs("\\vspace{6mm} \n",fip);
2373 fputs("\\begin{center} \n ", fip);
2374 fprintf(fip, "\\rule{2cm}{0.5mm} \\makebox[60mm]{{ \\bf %s } help group} \\rule{2cm}{0.5mm} \\\\ \n",
2375 (*it).first.c_str());
2376 fputs("\\vspace{3mm} \n",fip);
2377 fputs("\\begin{tabular}{llllll} \n", fip);
2378 kt = 0;
2379 for(ite = helpexmap.begin(); ite != helpexmap.end(); ite++) {
2380 if ((*ite).second.group != gid) continue;
2381 prl = (*ite).first; check_latex_reflabel(prl);
2382 mol = check_latex_underscore((*ite).first);
2383 fprintf(fip,"%s & p. \\pageref{%s} ", mol.c_str(), prl.c_str() );
2384 kt++;
2385 if (kt < 3) fputs(" & ", fip);
2386 else { fputs(" \\\\ \n", fip); kt = 0; }
2387 }
2388 for(ite = cmdexmap.begin(); ite != cmdexmap.end(); ite++) {
2389 if ((*ite).second.group != gid) continue;
2390 prl = (*ite).first; check_latex_reflabel(prl);
2391 mol = check_latex_underscore((*ite).first);
2392 fprintf(fip,"%s & p. \\pageref{%s} ", mol.c_str(), prl.c_str() );
2393 kt++;
2394 if (kt < 3) fputs(" & ", fip);
2395 else { fputs(" \\\\ \n", fip); kt = 0; }
2396 }
2397 if (kt == 1) fputs(" & & & \\\\ \n", fip);
2398 else if (kt == 2) fputs(" & \\\\ \n", fip);
2399 fputs("\\end{tabular} \n", fip);
2400 fputs("\\end{center} \n", fip);
2401 // fputs("\\end{table} \n",fip);
2402 // fputs("\\vspace{5mm} \n",fip);
2403}
2404// fputs("\\newline \n",fip);
2405
2406fputs("% ----- Liste des commandes dans chaque groupe ----- \n",fip);
2407fputs("\\newpage \n",fip);
2408
2409for(it = cmdhgrp.begin(); it != cmdhgrp.end(); it++) {
2410 gid = (*it).second.gid;
2411 if (gid == 0) continue;
2412 prl = (*it).first; check_latex_reflabel(prl);
2413 fprintf(fip,"\\subsection{%s} \\label{%s} \n",
2414 (*it).first.c_str(), prl.c_str());
2415 if ((*it).second.desc.length() > 0)
2416 fprintf(fip,"%s \n \\\\[2mm] ", (*it).second.desc.c_str());
2417 fprintf(fip,"\\noindent \n");
2418 for(ite = helpexmap.begin(); ite != helpexmap.end(); ite++) {
2419 if ((*ite).second.group != gid) continue;
2420 prl = (*ite).first; check_latex_reflabel(prl);
2421 mol = check_latex_underscore((*ite).first);
2422 fprintf(fip,"\\piahelpitem{%s} \\label{%s} \n",
2423 mol.c_str(), prl.c_str());
2424 fputs("\\begin{verbatim} \n",fip);
2425 fprintf(fip,"%s\n", (*ite).second.us.c_str());
2426 fputs("\\end{verbatim} \n",fip);
2427 }
2428 for(ite = cmdexmap.begin(); ite != cmdexmap.end(); ite++) {
2429 if ((*ite).second.group != gid) continue;
2430 prl = (*ite).first; check_latex_reflabel(prl);
2431 mol = check_latex_underscore((*ite).first);
2432 fprintf(fip,"\\piacommand{%s} \\label{%s} \n",
2433 mol.c_str(), prl.c_str());
2434 fputs("\\begin{verbatim} \n",fip);
2435 fprintf(fip,"%s\n", (*ite).second.us.c_str());
2436 fputs("\\end{verbatim} \n",fip);
2437 }
2438}
2439
2440fclose(fip);
2441cout << " Commander::HelptoLaTeX() - LaTeX format help written to file " << fname << endl;
2442
2443return;
2444}
2445
2446
2447} // End of namespace SOPHYA
2448
Note: See TracBrowser for help on using the repository browser.