00001 /* 00002 This is commands.cpp 00003 00004 Copyright (C) 2004,2005 Fokko du Cloux 00005 part of the Atlas of Reductive Lie Groups 00006 00007 See file main.cpp for full copyright notice 00008 */ 00009 00010 #include "commands.h" 00011 00012 #include <cstring> 00013 #include <iostream> 00014 #include <sstream> 00015 #include <stack> 00016 00017 #include "error.h" 00018 00019 /***************************************************************************** 00020 00021 This module contains the code for the command interface which we use during 00022 the development stage of the Atlas library. It is directly inspired by the 00023 interface for Coxeter, but re-written to be (hopefully) C++ conformant. In 00024 any case, i/o is now done in C++-style, and we also use the containers 00025 map and stack provided by C++, as well as the C++ string facility. Exceptions 00026 remain somewhat of a grey area here. 00027 00028 The basic class is the CommandMode class. This contains a set of recognized 00029 names, for which it will execute corresponding functions. At each point of 00030 time, there is a stack of such modes, the local variable modeStack, the top 00031 of which is the currently active mode. Some commands will lead to pushing 00032 a new mode on the stack; the entry function of the mode is then executed. 00033 Similarly, some commands (typically the "q" command) pop the mode stack; this 00034 results in executing the exit function of the mode. 00035 00036 Also, to each mode is associated a help mode, which usually provides some 00037 help information for each command available in that mode. It could do more 00038 in some cases. 00039 00040 ******************************************************************************/ 00041 00042 namespace atlas { 00043 00044 namespace { // declarations private to commands.cpp 00045 00046 using namespace commands; 00047 00048 std::stack<const CommandMode*> modeStack; // the stack of command modes; the 00049 // active mode is the top of the 00050 // stack 00051 00052 std::stack<const char*> commandStack; // lets us postpone some commands; 00053 00054 input::InputBuffer commandLine; // the current command line 00055 00056 bool runFlag; 00057 00058 // running the command interface 00059 00060 void ambiguous(const std::vector<const char*>&, const char*); 00061 void execute(const char* name, const CommandMode* mode); 00062 const char* getCommand(const CommandMode* mode); 00063 std::istream& getInteractive(std::istream&, std::string&, const char*); 00064 00065 // auxiliary functions 00066 00067 inline bool isEqual(const char* a, const char* b) { 00068 return !memcmp(a,b,strlen(a)); 00069 } 00070 00071 inline bool isInitial(const char* a, const char* b) { 00072 return !memcmp(a,b,strlen(a)); 00073 } 00074 00075 } 00076 00077 /**************************************************************************** 00078 00079 Chapter I -- The CommandMode class. 00080 00081 This is the central class of the command module. 00082 00083 The following member functions are defined : 00084 00085 - constructors and destructors : 00086 00087 - CommandMode(prompt,entry,exit,error) : constructor; 00088 - ~CommandMode() : destructor; 00089 00090 - accessors : 00091 00092 - prompt : prints the prompt; 00093 00094 - manipulators : 00095 00096 - add(name,tag,action,rep) : add a new command to the mode; 00097 - findName : finds a name in the dictionary; 00098 - setAction(name,a) : sets the action associated to name; 00099 - setRepeat(name,b) : sets the repeat flag for name; 00100 00101 *****************************************************************************/ 00102 00103 namespace commands { 00104 00105 00106 /* 00107 Constructor for the command mode class. 00108 */ 00109 CommandMode::CommandMode(const char* str, 00110 void (*entry)(), 00111 void (*exit)(), 00112 void (*error)(const char*)) 00113 :d_nextList(), // start out without successors 00114 d_map(), // and without commands 00115 d_prompt(str), 00116 d_entry(entry), 00117 d_exit(exit), 00118 d_error(error) 00119 {} // don't add any commands, so that |empty()| is true initially 00120 00121 /******** accessors *********************************************************/ 00122 00123 00124 /* 00125 Synopsis: adds to |e| the list of command names in mode and its descendants, 00126 that begin with |name|. 00127 */ 00128 00129 void CommandMode::extensions(std::set<const char*,StrCmp>& e, 00130 const char* name) const 00131 { 00132 for (const_iterator pos = d_map.lower_bound(name); pos != d_map.end(); 00133 ++pos) { 00134 if (isInitial(name,pos->first)) 00135 e.insert(pos->first); 00136 else 00137 break; 00138 } 00139 00140 for (size_t j = 0; j < n_desc(); ++j) { 00141 const CommandMode& mode = nextMode(j); 00142 mode.extensions(e,name); 00143 } 00144 } 00145 00146 00147 /* 00148 Synopsis: puts into |e| the list of command names in mode and its 00149 descendants, that begin with |name|. 00150 00151 Forwarded to the set-version, so that repetitions will be automatically 00152 weeded out. 00153 */ 00154 void CommandMode::extensions(std::vector<const char*>& e, 00155 const char* name) const 00156 { 00157 std::set<const char*,StrCmp> es; 00158 00159 extensions(es,name); 00160 e.clear(); 00161 00162 for (std::set<const char*,StrCmp>::const_iterator i = es.begin(); 00163 i != es.end(); ++i) 00164 e.push_back(*i); 00165 00166 return; 00167 } 00168 00169 00170 /* 00171 Synopsis: finds the command in the current mode or one of its submodes. 00172 */ 00173 CommandMode::const_iterator CommandMode::findName(const char* name) const 00174 { 00175 const_iterator pos = find(name); // search in current mode 00176 00177 if (pos != end()) 00178 return pos; 00179 00180 // if not find here, look recursively in descendant modes 00181 for (size_t j = 0; j < n_desc(); ++j) { 00182 const CommandMode& next = nextMode(j); 00183 pos = next.findName(name); 00184 if (pos != next.end()) 00185 return pos; 00186 } 00187 00188 return end(); 00189 } 00190 00191 /******** manipulators ******************************************************/ 00192 00193 00194 /* 00195 Synopsis: adds a new command to the mode. 00196 00197 The parameters have the following meaning : 00198 - name : name of the command; 00199 - command: the function to be executed by the command; 00200 00201 NOTE: if the name was already present, we override it. 00202 */ 00203 void CommandMode::add(const char* const name, const Command& command) 00204 { 00205 std::pair<const char* const, Command> v(name,command); 00206 00207 std::pair<CommandDict::iterator,bool> p 00208 = d_map.insert(v); 00209 00210 if (not p.second) // then name was already present; override its command! 00211 p.first->second=command; // and don't touch |p.first->first==name| 00212 } 00213 00214 00215 /* 00216 Sets the action of the command associated to name to a. 00217 00218 NOTE : it is assumed that name will be found in mode. 00219 */ 00220 void CommandMode::setAction(const char* name, void (*a)()) 00221 { 00222 CommandMode::iterator pos = find(name); 00223 pos->second.action = a; 00224 } 00225 00226 } 00227 00228 /**************************************************************************** 00229 00230 Chapter II -- Function definitions. 00231 00232 This section contains the definitions of the functions declared in 00233 commands.h : 00234 00235 - activate(CommandMode*) : makes mode the active mode; 00236 - defaultError(const char*) : default error handler for command reading; 00237 - checkName(CommandMode*, const char*) : tries to find name in mode; 00238 - default_help() : the default help function; 00239 - exitInteractive() : sets runFlag to false; 00240 - printTags(stream&,map<std::string,const char*>&) : prints the tag list; 00241 - quitMode() : exits the current mode; 00242 - relax_f() : does nothing; 00243 - run(CommandMode*) : runs the program; 00244 00245 *****************************************************************************/ 00246 00247 namespace commands { 00248 00249 00250 /* 00251 Attempts to activate the command mode |mode|, by executing its entry 00252 function, which could throw an |EntryError|. If not, push |mode| onto stack. 00253 */ 00254 void activate(const CommandMode& mode) 00255 { 00256 mode.entry(); // could throw an EntryError 00257 modeStack.push(&mode); 00258 } 00259 00260 00261 /* 00262 Synopsis: inserts the commands from source into dest. This is used when 00263 going to a "desecendant" mode, to inherit the commands defined for 00264 the parent mode. 00265 00266 NOTE: we do the insertion through add, so it will override existing 00267 commands. Therefore to redefine commands with the same name in the 00268 descendant mode, add them after making the call to |addCommands|. 00269 */ 00270 void addCommands(CommandMode& dest, const CommandMode& source) 00271 { 00272 for (CommandMode::const_iterator i = source.begin(); i != source.end(); ++i) 00273 dest.add(i->first,i->second); 00274 } 00275 00276 00277 /* 00278 Synopsis: tries to find name in mode, or in one of its descendants. 00279 */ 00280 CheckResult checkName(const CommandMode& mode, const char* name) 00281 { 00282 CommandMode::const_iterator pos = mode.findName(name); 00283 00284 if (pos == mode.end()) { // command was not found 00285 return NotFound; 00286 } 00287 00288 // if we get to this point, there is at least one extension of name in mode 00289 00290 std::vector<const char*> ext; 00291 mode.extensions(ext,name); 00292 00293 if (ext.size() > 1) { // ambiguous command 00294 ambiguous(ext,name); 00295 return Ambiguous; 00296 } 00297 00298 // if we get to this point, the command is found in mode 00299 00300 return Found; 00301 } 00302 00303 input::InputBuffer& currentLine() 00304 00305 /* 00306 Synopsis: returns the current command line. 00307 00308 Explanation: the idea is to enable functions to pick off arguments from the 00309 command line, so that the user can type forward. 00310 */ 00311 00312 { 00313 return commandLine; 00314 } 00315 00316 00317 /* 00318 Synopsis: returns the currently active mode. 00319 Mostly useful for communication with readline. 00320 */ 00321 const CommandMode* currentMode() 00322 { 00323 return modeStack.top(); 00324 } 00325 00326 00327 /* 00328 Synopsis: default error handler, which is called when |str| is not a string 00329 which is recognized by the command tree. 00330 00331 It prints the name of |str| and an error message. 00332 */ 00333 void defaultError(const char* str) 00334 { 00335 std::cout << str << ": not found" << std::endl; 00336 } 00337 00338 00339 /* 00340 Synopsis: quits interactive mode, after exiting all active modes. 00341 00342 This should be called only if none of the modes in the stack can throw on 00343 exit, which seems a reasonable assumption. 00344 */ 00345 void exitInteractive() 00346 00347 { 00348 while (modeStack.size()) 00349 exitMode(); // applies to mode at top of stack, which is then popped 00350 00351 runFlag = false; // this will cause the |run| loop to terminate 00352 } 00353 00354 00355 /* 00356 Synopsis: exits the current mode. 00357 */ 00358 void exitMode() // it is assumed that exit functions don't throw 00359 { 00360 modeStack.top()->exit(); 00361 modeStack.pop(); 00362 } 00363 00364 // The |TagDict| type is addressed below, but no instance is defined here. 00365 00366 /* 00367 Synopsis: associates tag with name in t. 00368 00369 NOTE: this is the only way the sun CC compiler will accept it! 00370 */ 00371 00372 void insertTag(TagDict& t, const char* name, const char* tag) 00373 { 00374 TagDict::value_type v(name,tag); // pair of strings acceptable to |TagDict| 00375 t.insert(v); 00376 } 00377 00378 00379 /* 00380 Synopsis: outputs the list of commands with their attached tags. 00381 */ 00382 void printTags(std::ostream& strm, const TagDict& t) 00383 { 00384 typedef TagDict::const_iterator I; 00385 00386 for (I i = t.begin(); i != t.end(); ++i) { 00387 const char* key = i->first; 00388 const char* tag = i->second; 00389 strm << " - " << key << " : " << tag << std::endl; 00390 } 00391 } 00392 00393 /* 00394 Synopsis: runs an interactive session of the program. 00395 00396 Gets commands from the user until it gets the "qq" command, at which time it 00397 returns control. 00398 00399 It works as follows : get an input string from the user (leading whitespace 00400 is chopped off by default in C++); look it up in the current CommandMode, or, 00401 in case of failure, in its descendants; execute it if it is found, get new 00402 input otherwise. 00403 00404 The initMode argument is the startup mode of the interactive session; 00405 that is, the session starts by executing the entry function of initMode. 00406 */ 00407 void run(const CommandMode& initMode) 00408 { 00409 try 00410 { 00411 activate(initMode); // enter mode, pushing it onto |modeStack| 00412 00413 const char* name; 00414 00415 for (runFlag = true; runFlag;)// exit is only through "qq" 00416 { // (or "q" in startup mode) 00417 00418 const CommandMode* mode = modeStack.top(); // get current active mode 00419 name = getCommand(mode); 00420 00421 std::vector<const char*> ext; 00422 mode->extensions(ext,name); 00423 00424 switch (ext.size()) 00425 { 00426 case 0: // command was not found 00427 mode->error(name); 00428 break; 00429 case 1: // command can be unambiguously completed 00430 execute(ext[0],mode); 00431 break; 00432 default: // ambiguous command; execute it if there is an exact match 00433 if (isEqual(ext[0],name)) 00434 execute(ext[0],mode); 00435 else 00436 ambiguous(ext,name); 00437 break; 00438 } 00439 } // for(runFlag) 00440 } 00441 catch(EntryError) { // something is very wrong 00442 return; 00443 } 00444 } 00445 00446 } 00447 00448 /***************************************************************************** 00449 00450 Chapter IV -- Local function definitions 00451 00452 This section contains the definition of the private functions for this 00453 module : 00454 00455 - ambiguous(ext,name) : handles multiple completions; 00456 - getIntreractive(strm, name, prompt) : get a command from the user; 00457 00458 ******************************************************************************/ 00459 00460 namespace { 00461 00462 00463 /* 00464 Synopsis: outputs an informative message when name has more than one 00465 completion in the dictionary. 00466 00467 The message is name: ambiguous ( ... list of possible completions ... ) 00468 */ 00469 void ambiguous(const std::vector<const char*>& ext, const char* name) 00470 { 00471 typedef std::vector<const char*>::const_iterator I; 00472 00473 std::cout << name << " : ambiguous ("; 00474 00475 for (I i = ext.begin(); i != ext.end();) { 00476 std::cout << *i; 00477 ++i; 00478 if (i != ext.end()) 00479 std::cout << ","; 00480 } 00481 00482 std::cout << ")" << std::endl; 00483 } 00484 00485 00486 /*! 00487 \brief Execute the command "name". Also install default error handling. 00488 00489 Precondition: name is defined either in the current mode or in one of its 00490 descendants. 00491 00492 If name is found in the current mode, we simply execute it. Otherwise, 00493 we find out in which descendant mode it is found, we attempt the mode 00494 change, and in case of success, push command on the stack, so that it 00495 will be executed at the next loop in |run|. 00496 */ 00497 void execute(const char* name, const CommandMode* mode) 00498 { 00499 CommandMode::const_iterator pos = mode->find(name); 00500 00501 try 00502 { 00503 if (pos != mode->end()) // the command was found in the current mode 00504 { 00505 const Command& command = pos->second; 00506 command(); 00507 } 00508 else // we have to look in a submode 00509 for (size_t j = 0; j < mode->n_desc(); ++j) 00510 { 00511 const CommandMode& next = mode->nextMode(j); 00512 pos = next.findName(name); 00513 if (pos != next.end()) // name is defined in a descendent of next 00514 { 00515 activate(next); 00516 commandStack.push(name); // retry command if mode entry successful 00517 break; // only attempt to enter the first matching descendant 00518 } 00519 } 00520 } 00521 catch (commands::EntryError&) { // silently ignore failure to enter mode 00522 } 00523 catch (error::MemoryOverflow& e) { 00524 e("error: memory overflow"); 00525 } 00526 catch (error::InputError& e) { 00527 e("aborted"); 00528 } 00529 } 00530 00531 00532 /* 00533 Synopsis: gets the name of the next command. 00534 00535 It is gotten either from the commandStack, if there are commands waiting 00536 to be processed, or interactively from the user. 00537 */ 00538 const char* getCommand(const CommandMode* mode) 00539 { 00540 static std::string nameString; 00541 const char* name; 00542 00543 if (commandStack.size()) { // there is a command to process 00544 name = commandStack.top(); 00545 commandStack.pop(); 00546 } else { // get command from user 00547 nameString.erase(); 00548 getInteractive(std::cin,nameString,mode->prompt()); 00549 name = nameString.c_str(); 00550 } 00551 00552 return name; 00553 } 00554 00555 00556 /* 00557 Synopsis: gets a command interactively from the user. 00558 00559 The actual input line is gotten through the readline library. For convenience 00560 we pack it into an InputBuffer (defined in input.h), in order to have a 00561 C++-like interaction. 00562 */ 00563 std::istream& getInteractive(std::istream& strm, std::string& name, 00564 const char* prompt) 00565 { 00566 using namespace input; 00567 00568 commandLine.getline(strm,prompt); 00569 commandLine >> name; 00570 00571 return strm; 00572 } 00573 00574 } // namespace 00575 00576 } // namespace atlas
1.3.9.1