Main Page   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members  

cmdparam.c

Go to the documentation of this file.
00001 
00062 #include <string.h>
00063 #include <stdlib.h>
00064 #include <time.h>
00065 #include <ctype.h>
00066 #include <math.h>
00067 #include <string>
00068 using std::string;
00069 #include <stack>
00070 #include <vector>
00071 #include <strstream.h>
00072 #include <fstream>
00073 using std::vector;
00074 
00075 /* If enabled, assume POSIX-compliant signal/longjump handling (via sigsetjmp) */
00076 #ifndef NO_SIGNAL_HANDLING
00077 #include <signal.h>
00078 #include <setjmp.h>
00079 #endif
00080 
00081 #include "ipc.h"
00082 #include "cmdparam.h"
00083 #include "stringutils.h"
00084 #include "polymorph.h"
00085 
00086 
00087 #ifdef EXPLICIT_INSTANTIATION_FOR_STATICS
00088 template class PolymorphBase<Tristate>;
00089 template class PolymorphBase<int>;
00090 template class PolymorphBase<double>;
00091 template class PolymorphBase<string>;
00092 template class ParameterDefinition<int>;
00093 #endif 
00094 
00095 
00096 
00097 /******************************************************************************/
00098 /* Defines and typedefs                                                       */
00099 /******************************************************************************/
00100 
00101 /* Determine which PE is in charge of file handling */
00102 #define PARENTPE (0)
00103 #define AMPARENTPE (ipc_my_process()==PARENTPE)
00104 
00105 
00106 /* Command definitions */
00107 
00108 #define CMDDEF_MAX_NUM         64   
00109 #define CMDDEF_MAX_NAME_LENGTH 64   
00110 #define CMDDEF_MAX_PREREQS      2   
00112 /* Holds a single command definition. */
00113 struct CommandDefinition {
00114   char name[CMDDEF_MAX_NAME_LENGTH+1]; 
00115   CmdWrapper* function;             
00116   int minargs;                      
00117   int catchup;                      
00118   int num_prereqs;                  
00119   int prereqs[CMDDEF_MAX_PREREQS];  
00120   int callstatus;                   
00121   const char *usage;                
00122   const char *doc;                  
00127   BlackboardType* params;
00128   BlackboardType& parameters() {  assert(params);  return *params;  }
00129   ~CommandDefinition()         {  delete params;  }
00130 };
00131 
00132 /* Holds all the command definitions. */
00133 typedef struct{                     
00134   int num;
00135   CommandDefinition list[CMDDEF_MAX_NUM];
00136 }CommandDefinitionBlackboard;
00137 
00138 /* Holds a single command instance. */
00139 typedef struct{
00140   int  argc;                            
00141   const char *argv[CMD_MAX_ARGUMENTS];  
00142   CommandDefinition* def;               
00143 }Command;
00144 
00145 #define CMD_CALL(cmd) (*((cmd).def->function))((cmd).argc, (cmd).argv)
00146 
00147 
00148 /* Counter hook definitions */
00149 
00150 /* Holds a command to be executed at a specified counter value. */
00151 typedef struct
00152 {
00153   int  start;     
00154   int  end;       
00155   int  step;      
00157   int  order;     
00158   char *argstofree[CMD_MAX_ARGUMENTS+1];  
00159   Command cmd;    
00160 } CounterHookDefinition;
00161 
00162 #define HOOKLIST_MAX_NUM      128  
00163 #define HOOKLIST_MAX_NAME_LENGTH  32
00164 
00165 /* Holds a sorted list of the specified counter hooks of a given type. */
00166 typedef struct
00167 {
00168   char name[HOOKLIST_MAX_NAME_LENGTH]; 
00169   char countername[HOOKLIST_MAX_NAME_LENGTH]; 
00170   int current;                         
00171   int num;                             
00172   CounterHookDefinition defs[HOOKLIST_MAX_NUM]; 
00173 } CounterHooklist;
00174 
00175 #define HOOKLISTS_MAX_NUM 4
00176 
00177 #define HAS_CURRENT_HOOK(hooklist) ((hooklist).current < (hooklist).num)
00178 #define CURRENT_HOOK(hooklist)     ((hooklist).defs[(hooklist).current])
00179 
00180 /* Holds all the counter Hooklists. */
00181 typedef struct
00182 {
00183   int num;        
00184   CounterHooklist lists[HOOKLISTS_MAX_NUM]; 
00185 } CounterHooklists;
00186 
00187 
00188 /* Temporary list of command names and corresponding prerequisites,
00189    to store the info until the commands have all been defined.
00190 */
00191 typedef struct
00192 {
00193   int num;        
00194   const char* names[CMDDEF_MAX_NUM*CMDDEF_MAX_PREREQS];
00195   const char* prereqs[CMDDEF_MAX_NUM*CMDDEF_MAX_PREREQS];
00196 } PrereqList;
00197 
00198 
00199 
00200 /******************************************************************************/
00201 /* Global variables                                                           */
00202 /******************************************************************************/
00203 
00204 CounterHooklists hooklists;
00205 
00206 BlackboardType  global_blackboard;                
00207 BlackboardType* blackboard_ptr=&global_blackboard;
00208 BlackboardType blackboard_for_commands(&blackboard,"cmd"); 
00209 CommandDefinitionBlackboard command_definitions;  
00211 int commands_succeeded = 0;                       /* So far                 */
00212 int commands_failed    = 0;                       /* So far                 */
00213 int command_num_called = 0;                       /* unique global counter  */
00214 
00215 int hook_definition_counter=0;                    /* unique global counter  */
00216 int cmdparam_warn_unknown=true;
00217 double command_msg_time=1.0;
00218 
00219 
00220 FILE *cmddefs_file=NULL;
00221 string commandpath = "../command/ ../../command/ ../samples/ ../ /usr/local/lissom/command/";
00222 
00223 
00224 std::stack<StringParser::arglist> cmddefs_exec_file_arglists;  
00226 char cmddefs_line_buffer[CMD_MAX_LINE_LENGTH+1+sizeof(int)];
00227 
00228 PrereqList prereq_storage;
00229 
00230 
00231 #ifndef NO_SIGNAL_HANDLING
00232 typedef void (*signal_handler_type)(int);
00233 sigjmp_buf jmp_env;
00234 #endif
00235 
00236 
00237 
00238 /******************************************************************************/
00239 /* Prototypes for private functions                                           */
00240 /******************************************************************************/
00241 
00242 CMD_DECLARE(call);
00243 CMD_DECLARE(clear_hooks);
00244 CMD_DECLARE(define_param);
00245 CMD_DECLARE(define_param_set);
00246 CMD_DECLARE(select_param_set);
00247 CMD_DECLARE(param_default);
00248 CMD_DECLARE(dotimes);
00249 CMD_DECLARE(exec);
00250 CMD_DECLARE(exec_catchup);
00251 CMD_DECLARE(exec_file);
00252 CMD_DECLARE(for);
00253 CMD_DECLARE(hook);
00254 CMD_DECLARE(if);
00255 CMD_DECLARE(echo);
00256 CMD_DECLARE(print);
00257 CMD_DECLARE(quit);
00258 CMD_DECLARE(read_args);
00259 CMD_DECLARE(set);
00260 CMD_DECLARE(system);
00261 
00262 size_t  cmdparam_identifier_length(const char* str);
00263 void    ipc_init_hook( void );
00264 
00265 int     cmddef_compare(const void* hook1, const void* hook2);
00266 void    cmddefs_sort(void);
00267 int     cmddefs_get_line_from_file( void );
00268 
00269 int     command_check_usage(Command* command);
00270 cmdstat command_exec(Command *cmd);
00271 CommandDefinition* cmddefs_lookup(const char *name);
00272 int     command_parse_line(Command *command, char *string);
00273 const char* command_parse_token(char** remaining);
00274 void    command_print_to_str( const Command *cmd, char *str, size_t nchars);
00275 void    cmddef_print_usage(FILE *fp, const CommandDefinition* cmddef);
00276 void    cmddef_print_usage_to_str(const CommandDefinition* cmddef, char *str, size_t nchars);
00277 void    cmddefs_activate_prereqs( void );
00278 void    cmddefs_activate_command_prereq( const char* name, const char *prereq );
00279 
00280 int     hook_compare(const void* hook1, const void* hook2);
00281 void    hook_copy( CounterHookDefinition *source, CounterHookDefinition *dest);
00282 void    hooklists_log(void);
00283 int     hook_parse_specifier( CounterHookDefinition *hook, Parse& parser );
00284 void    hook_print_to_str( CounterHookDefinition *hook, char *hooklistname, char *str, size_t nchars);
00285 void    hooklists_sort(HooklistNum hooklist);
00286 
00287 
00288 
00289 #ifndef NO_SIGNAL_HANDLING
00290 void command_exec_sigint_handler( int sig );
00291 #endif
00292 
00293 
00294 
00295 /******************************************************************************/
00296 /* Initialization hook                                                        */
00297 /******************************************************************************/
00298 
00303 void  cmdparam_init_hook( void )
00304 {
00305   ipc_init_hook();
00306 
00307   /* Commands provided by this file */
00308   
00309   CMD_DOC(clear_hooks,NULL,0,"%s [<hook_list>]*",
00310           "Delete all the currently-defined hooks in the given list(s), or delete\n"
00311           "all hooks in all lists if no lists are specified.");
00312 
00313   CMD_DOC(define_param,NULL,2,"%s [<path>::]<name> <type> [<lowbound> [<highbound> [<helpstring>] [<initialvalue>]]]",
00314           "Define a new parameter of the given type.  Space for the parameter is\n"
00315           "immediately allocated from the heap, but it is not initialized until\n"
00316           "the parameter is set unless an initial value is supplied.  Available <types>\n"
00317           "include Integer, Float, Boolean, and String.  The <highbound> is ignored if\n"
00318           "it is lower than the <lowbound>.  Calling this command multiple times for\n"
00319           "the same <name> is not an error unless the type differs, but the bounds,\n"
00320           "<helpstring>, and <initialvalue> are only installed on the first call.\n"
00321           "Note: The <lowbound> and <highbound> are currently truncated to integers,\n"
00322           "but for Float parameters this restriction may be relaxed in future");
00323 
00324   CMD_DOC(define_param_set,NULL,1,"%s [<path>::]<name>",
00325           "Define a new set of parameters rooted at the given path, which defaults to\n"
00326           "the global set of parameters.  Useful in e.g. scripts to avoid conflicts\n"
00327           "with any global parameters that may be defined.  Calling this command\n"
00328           "multiple times with the same arguments> is not an error, but has no effect.");
00329 
00330   CMD_DOC(select_param_set,NULL,0,"%s [<path>::]",
00331           "Make the specified set of parameters (previously defined with\n"
00332           "define_param_set) be the default for all operations.  With no arguments makes\n"
00333           "the previous parameter set (or root if none) be the default.");
00334 
00335   CMD_DOC(param_default,NULL,2,"%s <name> <expression>",
00336           "For a existing parameter <name>, declare that if the user has not explicitly\n"
00337           "set this parameter (using the set command) by the time init_network\n"
00338           "is called, the program should then evaluate the given expression (i.e.\n"
00339           "anything accepted by the set command) to determine the value for that\n"
00340           "parameter.\n\n"
00341           
00342           "The global order of evaluation of default expressions is undefined, and thus\n"
00343           "no expression should refer to any other parameter that also has a default\n"
00344           "expression defined. (In that case the resulting values would depend on the\n"
00345           "order of evaluation of the default expressions, and would be undefined.)");
00346 
00347   CMD_DOC(dotimes,NULL,2,"%s <expr> <command> [<argument>]*",
00348           "Execute the given command with the given arguments, the number of times\n"
00349           "specified by the given numeric expression.");
00350 
00351   CMD_DOC(for,NULL,3,"%s <param>=<initvalue> <test-expr> <param>=<incrementvalue> [<command> [<argument>]*]",
00352           "Execute the given command with the given arguments a number of times.\n"
00353           "First, the first param-value pair is passed to the set command, then as\n"
00354           "long as the numeric <test-expr> evaluates to a true value, the given command\n"
00355           "is executed and the second param-value pair is passed to the set command.\n"
00356           "Example: `define_param angle Param_Integer', then\n"
00357           "`for angle=0 angle<180 angle=angle+10 print angle'.");
00358 
00359   CMD_DOC(if,NULL,2,"%s <expr> <command> [<argument>]*",
00360           "If the given numeric expression evaluates to a non-zero value, execute\n"
00361           "the given command with the given arguments.");
00362 
00363   CMD_DOC(call,NULL,1,"%s <filename> [<param>=<value>]*",
00364           "Same as exec_file, but suppresses verbose parameter change and other\n"
00365           "messages.  This is intended for calling a file as a procedure, where only\n"
00366           "error reporting is usually desired.  The output would otherwise be very\n"
00367           "verbose if the file contains a long loop or calls other files repeatedly.");
00368 
00369   CMD_DOC(exec_file,NULL,1,"%s <filename> [<param>=<value>]*",
00370           "Execute the given command file.  The parameter settings included on the\n"
00371           "command line will be saved but not evaluated until a read_args command\n"
00372           "is called within the command file.  Also see the help for read_args.");
00373 
00374   CMD_DOC(exec,NULL,1,"%s [<command-string>]*",
00375           "Execute the commands specified by the given strings, in order.  Before being\n"
00376           "executed, each string is interpreted for string substitutions as for the set\n"
00377           "command for string parameters, so it's possible to use constructions like\n"
00378           "`exec \"${command}\" \"set x=y\"'.");
00379 
00380   CMD_DEFINE_CATCHUP(1,exec_catchup,"%s [<command-string>]*",
00381           "Same as exec, but never skipped when catching up to a new iteration.  Useful\n"
00382           "to force execution of a hook which would otherwise be skipped.");
00383 
00384   CMD_DOC(echo,NULL,1,"%s <string>*",
00385           "Displays the given strings, each on a new line, after string substitution.");
00386 
00387   CMD_DOC(print,NULL,1,"%s <expression>*",
00388           "Displays the value of the given expression(s), which can either be the name of\n"
00389           "any parameter (preceded by a dollar sign (`$') if desired), or an arbitrary\n"
00390           "numeric expression allowed as a <value> by the set command.  Examples: \n"
00391           "`print filename', `print 2*radius-1'.");
00392 
00393   CMD_DOC(read_args,NULL,0,"%s [<parent>]",
00394           "If called within a file executed using the exec_file or call commands,\n"
00395           "evaluates the arguments supplied when the file was originally called (if any).\n"
00396           "The evaluation is done using the set command, called within the <parent>'s\n"
00397           "set of parameters.  This command provides a rudimentary form of argument\n"
00398           "passing to allow command files to be used like procedures.  Trivial example\n"
00399           "(e.g. in a file named `countup.command'):\n\n"
00400           
00401           "  define_param n Param_Integer # Declare argument(s)\n"
00402           "  set n=10                     # Default value\n"
00403           "  read_args                    # Read argument(s)\n\n"
00404 
00405           "  define_param i Param_Integer # Declare global variable to be used as a local\n"
00406           "  for i=0 i<n i=i+1 print i    # Actually do something\n\n"
00407 
00408           "If this file is called as `exec_file countup.command n=5', it will print\n"
00409           "out the numbers less than 5 (amidst various \"helpful\" debugging messages).\n"
00410           "Of course, since all parameters are currently globals, be careful\n"
00411           "not to assume the procedures act like safer ones offered by a more powerful\n"
00412           "language.");
00413   // Actually, it wouldn't be particularly difficult to fix this problem --
00414   // a new parameter set could just be created within the current parameter set, then
00415   // deleted on exit from exec_file.  That would act as an environment for local
00416   // variables to be defined in.  However, it would mean fixing all currently extant
00417   // scripts to put what they intend to be globals in :: instead of assuming that
00418   // variable accesses will always access globals.
00419   
00420 
00421   CMD_DEFINE_CATCHUP(1,set,"[%s] [[[::]<path>::]<name>=<value>]+",
00422                      "Allows any parameter to be set to an arbitrary value, within the type\n"
00423                      "and range of that parameter.  The keyword 'set' can be omitted.  \n"
00424                      "Multiple pairs of names and values are acceptable as long as the\n"
00425                      "CMD_MAX_ARGUMENTS limit is not reached.  Values may not contain spaces\n"
00426                      "unless the entire triple ('x=y') or the value (x='y') are enclosed in\n"
00427                      "quotes.  The path may contain the name of a particular set of parameters\n"
00428                      "to use; it is often omitted, in which case the global set is used.\n"
00429                      "Parameter set names in the path may start and/or end with a wildcard\n"
00430                      "('*'), in which case all sets of parameters matching at that level\n"
00431                      "are searched for parameters to set.  If the path starts with :: the\n"
00432                      "value is searched from the toplevel (root) parameter set.\n\n"
00433 
00434                      "If the <value> is surrounded by backquotes (`...`), it is evaluated for\n"
00435                      "string substitution and then passed to the system shell as a command, and\n"
00436                      "the output of that command is used as the <value> instead.  This evaluation\n"
00437                      "requires a temporary file, and is not truly multi-processor or multi-process\n"
00438                      "safe because the file names from different processes may (in rare cases)\n"
00439                      "conflict.\n\n"
00440                      
00441                      "The <value> for a parameter of String type may contain references to\n"
00442                      "other parameters if preceded by a dollar sign (`$') and (optionally)\n"
00443                      "enclosed in braces.  Non-string parameter values are printed to a\n"
00444                      "simple string representation.  Example: `set filename=\n"
00445                      "${filename}_${angle}_A' might set it to `oldname_76.5_A'.\n\n"
00446                      
00447                      "If you wish to control the printing format used, you may supply\n"
00448                      "flags in approximately the format accepted by the C printf command\n"
00449                      "for the appropriate datatype, just before the parameter name.  Example:\n"
00450                      "`set filename=${filename}_${06.2angle}_A' might set it to\n"
00451                      "`oldname_076.50_A', if angle is 76.5.\n\n"
00452                      
00453                      "As an extension to the C printf-style formatting, conditional text\n"
00454                      "expansion is supported.  Given a value like `${?param,text}', the\n"
00455                      "expansion will be `text' unless `param' is numeric and zero, or\n"
00456                      "non-numeric and expanding to the empty string.  If `,text' is omitted,\n"
00457                      "the expansion will be the parameter value itself. Example:\n"
00458                      "`set filename=${filename}${?plotname,_}${plotname}' might set the\n"
00459                      "filename to `oldname_Plot', if plotname is Plot, or `oldname' if\n"
00460                      "plotname is "". Or `set filename=${filename}${?angle,_}${?06.2angle}_A'\n"
00461                      "might set filename to `oldname_076.50_A', if angle is 76.5, or `oldname_A'\n"
00462                      "if angle is zero.\n\n"
00463                      
00464                      "The <value> for numeric types (Integer, Float, and Boolean) may be an\n"
00465                      "expression matching the following (recursive) grammar:\n\n"
00466                      
00467                      "   <term>  == <number>|<paramname>|(<value>)\n"
00468                      "   <value> == <term>[<binop><value>]\n"
00469                      "   <binop> == +|-|*|/|%|<|>|==|!=|<=|>=|<?|>?|@;|@:\n\n"
00470 
00471                      "Thus a literal numeric value will suffice, or else an infix arithmetic\n"
00472                      "expression like `RN' or `i/2' or `RN/2.5*(N-1)' or `RN%8' can be used,\n"
00473                      "where i, RN, and N are parameters whose current value will be substituted\n"
00474                      "before evaluation.\n\n"
00475 
00476                      "The operators <binop> are as defined in ANSI C, except as noted here and\n"
00477                      "below.  The >? and <? operators are borrowed from GNU C++; they take the\n"
00478                      "maximum and the minimum of their arguments, respectively.  The @; and @:\n"
00479                      "operators were invented as a bizarre but useful hack; they multiply the\n"
00480                      "left argument by the sine or cosine of the right argument, respectively.\n\n"
00481 
00482                      "The `*', `/', and `%%' operators are equal in precedence and are greater\n"
00483                      "in precedence than all the others; otherwise evaluation occurs from left\n"
00484                      "to right.  Thus simple arithmetic expressions will have the same\n"
00485                      "interpretation as in C, but parentheses should be used liberally on\n"
00486                      "anything complicated (particularly for comparisons) to ensure that they\n"
00487                      "are calculated correctly.\n\n"
00488                      
00489                      "All intermediate numeric results are of type double (or long double,\n"
00490                      "if available), which is then rounded (not truncated) to an integer.\n"
00491                      "Thus expression values involving e.g. integer division may differ from\n"
00492                      "their C equivalents, but this is usually an improvement.\n\n"
00493 
00494                      "Boolean variables have numeric types, but the symbolic values True,\n"
00495                      "False, Yes, No, and Uninitialized are accepted as standing for 1,0,1,0,\n"
00496                      "and -1, respectively.  Boolean expressions like ((x<0)||(x>N))&&C can be\n"
00497                      "computed by writing their arithmetic equivalent, e.g. `((x<0)+(x>N))*C',\n"
00498                      "as long as none of the Boolean variables are Uninitialized.");
00499 
00500   CMD_DOC(system,NULL,1,"%s <shell-command> [<shell-command-arg>]*",
00501                      "Execute the given system shell command (e.g. `ls -l file').  Arguments\n"
00502                      "which require quoting should be enclosed in single quotes surrounded by\n"
00503                      "double quotes, e.g. `ls -l \"'file with spaces'\"', or vice versa.\n"
00504                      "Parameter values may be used in arguments if preceded by a dollar sign, as\n"
00505                      "for string parameters in the `set' command.  Consequently, shell variables\n"
00506                      "cannot be used in arguments; if this were to present a problem, \n"
00507                      "the system command (or cmdi) could be rewritten to suppress variable\n"
00508                      "substitution if it detects embedded single quotes.");
00509 
00510   CMD_DOC(quit,NULL,0,"%s [<exit-message>]",
00511                      "Exit from the program with no error condition.");
00512 
00513   CMD_DEFINE_CATCHUP(3,hook,"%s <hook_list> <counter_specifier> <command> [<argument>]*",
00514                      "This command allows you to specify that the given command should be\n"
00515                      "executed at location <hook_list> when a counter reaches the value\n"
00516                      "specified by the <counter_specifier>, where the <counter_specifier>\n"
00517                      "is of the (recursive) form:\n\n"
00518 
00519                      "   <counter_start>[-<counter_end>[%<step>]][,<counter_specifier>]\n\n"
00520 
00521                      "For example, the <counter_specifier> can be\n"
00522                      "a single value ((e.g.  1000), a range of values (e.g.  1-1000), a\n"
00523                      "range with a stepsize (e.g.  1-1000%%10), or a list of any of these\n"
00524                      "(e.g. 1,10,100-200%%20,7,50-60).\n\n"
00525 
00526                      "For a given hook_list and counter value, all the hooks that apply\n"
00527                      "are executed in the order of their counter_start and (if those\n"
00528                      "match) the order in which they were defined.");
00529 
00530 
00531   PARAM_N(PARAM_USTRING,commandpath,
00532           "Space-separated list of paths to search for command files.  The current\n"
00533           "directory is always searched first.");
00534 
00535   PARAM_N(PARAM_DOUBLE,command_msg_time,
00536           "If a command takes longer than this time (in seconds) to execute, the time\n"
00537           "taken will be displayed when the command completes.");
00538 
00539   PARAM_L(PARAM_INT, command_num_called,0,
00540           "Read-only parameter returning the number of commands called so far this\n"
00541           "run; is incremented whenever a command is executed.  Primarily useful for\n"
00542           "constructing the command prompt.");
00543 
00544   PARAM_A(blackboard,"squote",string("'"),,
00545           "Read-only parameter holding a single quote mark ('); used for hairy string\n"
00546           "substitution tricks.");
00547   
00548   PARAM_A(blackboard,"dquote",string("\""),,
00549           "Read-only parameter holding a double quote mark (\"); used for hairy string\n"
00550           "substitution tricks.");
00551   
00552   blackboard.init_hook();
00553   ipc_init_hook();
00554 
00555   /* For pretty */
00556   Polymorph<Tristate>::set_typestring("Boolean");
00557   Polymorph<int>::set_typestring("Integer");
00558   Polymorph<double>::set_typestring("Float");
00559   Polymorph<string>::set_typestring("String");
00560 
00561   /* Old comment: any parameters defined after this point must be
00562      defined using cmd_define_param.  No longer clear why that would
00563      have to be true, except for niceties like sorting lists of
00564      parameters.  */
00565 
00566   cmddefs_sort(); /* For pretty */
00567   cmddefs_activate_prereqs(); /* No more cmddefs sorting allowed after this,
00568                                  since each command keeps the index of others as prereqs. */
00569 
00570   /* Any parameters defined after this point must be defined using cmd_define_param (Why?) */
00571 }
00572 
00573 
00574 
00579 void  ipc_init_hook( void )
00580 {
00581   PARAM_I(PARAM_INT, ipc_msg_level,          IPC_NONE,IPC_OVERWHELM,          ipc_msg_level_docstring);
00582   PARAM_I(PARAM_INT, ipc_msg_forceall_level, IPC_NONE,IPC_OVERWHELM, ipc_msg_forceall_level_docstring);
00583   PARAM_I(PARAM_INT, ipc_msg_synch_level,    IPC_NONE,IPC_OVERWHELM,    ipc_msg_synch_level_docstring);
00584   PARAM_L(PARAM_INT, ipc_exit_on_error_num,0,                         ipc_exit_on_error_num_docstring);
00585   PARAM_L(PARAM_INT, ipc_max_warnings,0,                                   ipc_max_warnings_docstring);
00586   PARAM_L(PARAM_INT, ipc_max_errors,0,                                       ipc_max_errors_docstring);
00587 
00588   CONST_I(IPC_None,      IPC_NONE,     False,
00589           "Symbol representing `no message'.  Ordinarily, no message should be declared\n"
00590           "with a level this high, and then this level may be used to turn off all\n"
00591           "messages.");
00592 
00593   CONST_I(IPC_Error,     IPC_ERROR,    False,
00594           "Symbol representing `error message'.  It should be used for a fairly serious\n"
00595           "problem, e.g. one which causes the current computation to be aborted.");
00596 
00597   CONST_I(IPC_Warning,   IPC_WARNING,  False,
00598           "Symbol representing `warning message'.  It should be used for a condition\n"
00599           "that is clearly bad but which won't stop computation from proceeding, e.g.\n"
00600           "if a meaningful default value can be used in place of an out-of-range\n"
00601           "parameter.");
00602 
00603   CONST_I(IPC_Caution,   IPC_CAUTION,  False,
00604           "Symbol representing `caution message'.  It should be used for a condition that\n"
00605           "is probably bad but which is common enough not to be worth tabulating in total\n"
00606           "warning counts.");
00607   
00608   CONST_I(IPC_Alert,     IPC_ALERT,    False,
00609           "Symbol representing `alert message'.  It is primarily a placeholder, marking\n"
00610           "all higher message levels as being in the `alert' category instead of merely\n"
00611           "informational.  It may also be used for any particularly important message\n"
00612           "which should almost never be suppressed.");
00613 
00614   CONST_I(IPC_Requested, IPC_REQUESTED,False,
00615           "Symbol representing `user-requested message'.  It should be used for messages\n"
00616           "that a user has requested specifically, and thus which should not ordinarily\n"
00617           "be turned off.");
00618 
00619   CONST_I(IPC_Summary,   IPC_SUMMARY,  False,
00620           "Symbol representing `summary message'.  It should be used for messages that\n"
00621           "are particularly important but which do not report an anomalous condition.");
00622 
00623   CONST_I(IPC_Std,       IPC_STD,      False,
00624           "Symbol representing `standard message'.  It should be used for messages that\n"
00625           "a user will probably want to see for every run.");
00626 
00627   CONST_I(IPC_Verbose,   IPC_VERBOSE,  False,
00628           "Symbol representing `verbose message'.  It should be used for messages which\n"
00629           "a user will probably want to see only when requested.");
00630 
00631   CONST_I(IPC_Overwhelm, IPC_OVERWHELM,False,
00632           "Symbol representing `overwhelming amount of messages'.  It should be used\n"
00633           "for debugging messages and similar trivia that may generate a lot of output.");
00634 }
00635 
00636 
00637 
00638 /******************************************************************************/
00639 /* Parameter-handling routines                                                */
00640 /******************************************************************************/
00641 
00642 /******************************************************************************/
00643 /* Command-file processing                                                    */
00644 /******************************************************************************/
00645 
00646 
00648 void cmddefs_define_command( const char* name, CmdWrapper* function, int minargs, int exec_for_catchups )
00649 {
00650   if (command_definitions.num >= CMDDEF_MAX_NUM)
00651     ipc_notify(IPC_ONE,IPC_WARNING,"Command blackboard is full; can't add entry for %s",name);
00652   else if (strlen(name)!=cmdparam_identifier_length(name))
00653     ipc_notify(IPC_ALL,IPC_WARNING,"Invalid characters in command name; can't define it");
00654   else if (minargs > CMD_MAX_ARGUMENTS) 
00655     ipc_notify(IPC_ONE,IPC_ERROR,"Command %s expects too many arguments (%d > %d); can't define it",
00656                name, minargs,(int)CMD_MAX_ARGUMENTS);
00657   else {
00658     CommandDefinition& newcmd = command_definitions.list[command_definitions.num];
00659     strncpy(newcmd.name, name,CMDDEF_MAX_NAME_LENGTH);
00660     newcmd.function    = function;
00661     newcmd.minargs     = minargs;
00662     newcmd.catchup     = exec_for_catchups;
00663     newcmd.usage       = NULL;
00664     newcmd.doc         = NULL;
00665     newcmd.num_prereqs = 0;
00666     newcmd.callstatus  = CMD_NEVER_CALLED;
00667     newcmd.params      = new BlackboardType(&blackboard_for_commands,name);
00668     command_definitions.num++;
00669   }
00670 }
00671 
00672 
00673 
00675 void cmddefs_define_command_doc( const char* name, const char *usage, const char *doc )
00676 {
00677   int idx = 0;
00678   int changed = False;
00679 
00680   /* Counts backward, which is very quick if this command was just added */
00681   for (idx=command_definitions.num-1; !changed && idx >= 0; idx--)
00682     if (strncmp(name,command_definitions.list[idx].name,CMDDEF_MAX_NAME_LENGTH) == 0) {
00683       command_definitions.list[idx].usage = usage;
00684       command_definitions.list[idx].doc = doc;
00685       changed = True;
00686     }
00687   
00688   if (!changed) 
00689     ipc_notify(IPC_ONE,IPC_ERROR,"Failed to add documentation to command %s",name);
00690 }
00691 
00692 
00693 
00699 void cmddefs_define_command_prereq( const char* name, const char *prereq )
00700 {
00701   if (!prereq)
00702     return;
00703   
00704   if (prereq_storage.num >= CMDDEF_MAX_NUM*CMDDEF_MAX_PREREQS) {
00705     ipc_notify(IPC_ONE,IPC_ERROR,"Prerequisite storage area full; can't add prerequisite %s for command %s",
00706               prereq,name);
00707     return;
00708   }
00709 
00710   prereq_storage.names[prereq_storage.num]=name;
00711   prereq_storage.prereqs[prereq_storage.num]=prereq;
00712   prereq_storage.num++;
00713 }
00714 
00715 
00716 
00724 void cmddefs_declare_prereq_status( const char * name, cmdstat status )
00725 {
00726   CommandDefinition* def=cmddefs_lookup(name);
00727   if (def) def->callstatus = status;
00728 }
00729 
00730 
00731 
00732 void cmddefs_activate_prereqs( void )
00733 {
00734   int i;
00735 
00736   for (i=0; i< prereq_storage.num; i++)
00737     cmddefs_activate_command_prereq(prereq_storage.names[i],prereq_storage.prereqs[i]);
00738 }
00739 
00740 
00741 
00746 void cmddefs_activate_command_prereq( const char* name, const char *prereq )
00747 {
00748   int idx,pidx;
00749   int changed = False;
00750   int found = False;
00751 
00752   if (!prereq)
00753     return;
00754 
00755   /* Counts backward, which is very quick if this command was just added */
00756   for (idx=command_definitions.num-1; !changed && idx >= 0; idx--)
00757     if (strncmp(name,command_definitions.list[idx].name,CMDDEF_MAX_NAME_LENGTH) == 0) {
00758       CommandDefinition* cmddef = &(command_definitions.list[idx]);
00759         
00760       for (pidx=command_definitions.num-1; !found && pidx >= 0; pidx--)
00761         if (strncmp(prereq,command_definitions.list[pidx].name,CMDDEF_MAX_NAME_LENGTH) == 0) {
00762           found=True;
00763           if (cmddef->num_prereqs >= CMDDEF_MAX_PREREQS)
00764             ipc_notify(IPC_ONE,IPC_ERROR,"Maximum limit on command prerequisites (%d) reached; can't add another",
00765                       CMDDEF_MAX_PREREQS);
00766           else {
00767             cmddef->prereqs[cmddef->num_prereqs++]=pidx;
00768             /* ipc_notify(IPC_ONE,IPC_STD,"Added prerequisite %s (%d) for command %s (%d)",prereq,pidx,name,idx); */
00769           }
00770         }
00771       changed = True;
00772     }
00773   
00774   if (!changed) 
00775     ipc_notify(IPC_ONE,IPC_ERROR,"Failed to add prerequisite to command %s",name);
00776 }
00777 
00778 
00779 
00784 CommandDefinition* cmddefs_lookup(const char *name)
00785 {
00786   int  command_def=0;
00787   
00788   /* Iterate through commands array looking for this command */
00789   while ((command_def < command_definitions.num) &&
00790          (strncmp(name,command_definitions.list[command_def].name,CMDDEF_MAX_NAME_LENGTH) != 0))
00791     command_def++;
00792 
00793   /* If found, return a pointer to its definition */
00794   if (command_def < command_definitions.num)
00795     return &(command_definitions.list[command_def]);
00796   else {
00797     if (cmdparam_warn_unknown)
00798       ipc_notify(IPC_ONE,IPC_ERROR,"Unknown command: %s",name);
00799     return NULL;
00800   }
00801 }
00802 
00803 
00804 
00805 cmdstat cmddefs_exec_by_name(const char* name, CMD_ARGS)
00806 {
00807   CommandDefinition* def = cmddefs_lookup(name);
00808   
00809   if (!def)
00810     return CMD_PARAMETER_ERROR;
00811 
00812   else {
00813     Command cmd;
00814     int i;
00815     
00816     cmd.def=def;
00817     cmd.argc=argc;
00818     for (i=0; i<argc; i++)
00819       cmd.argv[i]=argv[i];
00820     
00821     return command_exec(&cmd);
00822   }
00823 }
00824 
00825 
00826 /* Given a pointer to an opening quote mark (a single character,
00827    assumed non-null), returns a pointer to the matching closing quote
00828    (or eos, if none), leaving *remaining pointing to the character
00829    after the quote, if any.
00830 */
00831 char* consume_quote(char** remaining)
00832 {
00833   const char* quotepos=*remaining;
00834   while (*(++(*remaining)) && (**remaining!=*quotepos));
00835   char* end=*remaining;
00836   if (!**remaining) ipc_notify(IPC_ONE,IPC_ERROR,"Unmatched %c",*quotepos);
00837   else (*remaining)++;
00838   return end;
00839 }
00840 
00841 
00842 inline bool isquote(char character)
00843 {  return (character=='"' || character=='\'' || character=='`');  }
00844 
00845   
00859 const char* command_parse_token(char** remaining)
00860 {
00861   char* start;
00862   char* end;
00863 
00864   while (isspace(**remaining)) (*remaining)++;
00865   start=*remaining;
00866   end=*remaining;
00867   
00868   switch (**remaining) {
00869 
00870   case '\0':
00871     break;
00872 
00873   case  '#':
00874     while (*(++(*remaining)));
00875     start=end=(*remaining);
00876     break;
00877     
00878   case '\"':
00879     start++;
00880     end=consume_quote(remaining);
00881     break;
00882     
00883   case '`':
00884     start++;
00885     end=consume_quote(remaining);
00886     break;
00887     
00888   case '\'':
00889     start++;
00890     end=consume_quote(remaining);
00891     break;
00892     
00893   default:
00894     while (*(++(*remaining))  && !isspace(**remaining) && !isquote(**remaining) && **remaining!='#');
00895     if (isquote(**remaining))
00896       consume_quote(remaining);
00897     end=*remaining;
00898     break;
00899   }
00900 
00901   /* Swallow trailing garbage until we're sure there's something interesting left */
00902   while (isspace(**remaining)) (*remaining)++;
00903   if (**remaining=='#') while (*(++(*remaining)));
00904 
00905   /* Return pointer to token */
00906   if (end) *end='\0';  /* Overwrites delimiter */
00907   return start;
00908 }
00909 
00910 
00911 
00922 int command_parse_line(Command *command, char* const cmdtxt)
00923 {
00924   bool seterror=false;
00925   bool paramsset=false;
00926   char* remaining=cmdtxt;
00927   
00928   /* First token is name of command */
00929   const char* name = command_parse_token(&remaining);
00930   
00931   /* First, consume parameter setting in the form x=y */
00932   while (cmdparams_set_single(blackboard,name,seterror)) {
00933     paramsset=true;
00934     name = command_parse_token(&remaining);
00935   }
00936   if (seterror) {
00937     commands_failed++;
00938     return 0;
00939   }
00940   
00941   /* Continue with regular processing */
00942   if (!*name) {
00943     if (paramsset) commands_succeeded++;
00944     return 0; /* Must be blank line, comment, or completed set */
00945   }
00946   
00947   /* Look up command definition and link it in */
00948   command->def = cmddefs_lookup(name);
00949   if (!command->def) {
00950     commands_failed++;
00951     return 0;
00952   }
00953   
00954   /* Read the command's arguments, if any */
00955   command->argc=0;
00956   while ( (command->argc < CMD_MAX_ARGUMENTS) && *remaining )
00957     command->argv[(command->argc)++]=command_parse_token(&remaining);
00958 
00959   
00960   if (!command_check_usage(command)) {
00961     commands_failed++;
00962     return 0;
00963   }
00964 
00965   return 1;
00966 }
00967 
00968 
00969 
00970 int command_check_usage(Command* command)
00971 {
00972   /* Make sure right number of arguments were supplied */
00973   if (command->argc >= CMD_MAX_ARGUMENTS)
00974     ipc_notify(IPC_ONE,IPC_WARNING,"Argument list may be truncated (limit is %d)",CMD_MAX_ARGUMENTS);
00975   if (command->argc < command->def->minargs) {
00976     char buf[CMD_MAX_LINE_LENGTH];
00977     cmddef_print_usage_to_str(command->def, buf, CMD_MAX_LINE_LENGTH);
00978     ipc_notify(IPC_ONE,IPC_ERROR,"Command %s requires %d arguments but %d supplied",
00979                command->def->name,command->def->minargs,command->argc);
00980     ipc_notify(IPC_ONE,IPC_STD,"Usage: %s",buf);
00981     return 0;
00982   }
00983 
00984   return 1;
00985 }
00986 
00987 
00988 
00989 #ifndef NO_SIGNAL_HANDLING
00990 void command_exec_sigint_handler( int sig)
00991 {
00992   (void)sig; /* Unused */
00993   
00994   ipc_notify(IPC_ALL,IPC_ERROR,
00995              "Aborting command due to SIGINT; program may now be in an undefined state");
00996   longjmp(jmp_env,CMD_SIGINT_ERROR); /* Return to command_exec */
00997 }
00998 #endif
00999 
01000 
01001 
01003 cmdstat command_exec(Command *cmd)
01004 {
01005   time_t end_of_command;
01006   time_t start_of_command;
01007   double elapsed_time;
01008   int command_number=command_num_called++; /* local copy for recursive calls */
01009   char str[CMD_MAX_LINE_LENGTH];
01010   int i;
01011   int status = CMD_NO_ERROR;  /* Any non-negative integer also accepted */
01012   
01013   if (ipc_msg_level >= IPC_VERBOSE) {
01014     command_print_to_str( cmd, str, CMD_MAX_LINE_LENGTH);
01015     ipc_notify(IPC_ONE,IPC_VERBOSE,"Command %2d (%s) executing",
01016                command_number,str);
01017   }
01018 
01019   /* Call any prerequisites this command defines */
01020   for (i=0; i< cmd->def->num_prereqs && status >= CMD_NO_ERROR; i++)
01021     if (command_definitions.list[cmd->def->prereqs[i]].callstatus == CMD_NEVER_CALLED) {
01022       Command temp;
01023       temp.def=cmddefs_lookup(command_definitions.list[cmd->def->prereqs[i]].name);
01024       temp.argc=0;
01025       status = command_exec(&temp);
01026     }
01027     else status = command_definitions.list[cmd->def->prereqs[i]].callstatus;
01028   
01029 
01030   /* Call command unless already in an error state */
01031   start_of_command = time(NULL);
01032   if (status >= CMD_NO_ERROR) {
01033 
01034 #ifdef NO_SIGNAL_HANDLING
01035     status = CMD_CALL(*cmd);
01036 #else
01037     /* Set up interrupt handler */
01038     signal_handler_type original_handler = 
01039       signal(SIGINT,command_exec_sigint_handler);
01040 
01041     /* Save state in case of interrupt */
01042     if (sigsetjmp(jmp_env,True)==0)
01043       /* Enter region that might have an interrupt */
01044       status = CMD_CALL(*cmd);
01045     else
01046       /* Continue from interrupt, if there was one */
01047       status = CMD_SIGINT_ERROR; 
01048 
01049     /* Restore original interrupt handler */
01050     signal(SIGINT,original_handler);
01051 #endif
01052     
01053     ipc_notify(IPC_ONE,IPC_VERBOSE,"Command %2d exited with return code %d",
01054                command_number,status);
01055     cmd->def->callstatus = status;
01056   }
01057   else {
01058     ipc_notify(IPC_ONE,IPC_WARNING,"Command %2d not being called due to unsatisfiable prerequisite: %s",
01059              command_number,command_definitions.list[cmd->def->prereqs[i-1]].name);
01060     status = CMD_PREREQ_ERROR;
01061   }
01062 
01063   
01064   if (status >= CMD_NO_ERROR) { 
01065     commands_succeeded++;
01066   }
01067   else
01068     commands_failed++;
01069 
01070   ipc_barrier(); /* Sync after each command ; should add bcast of error status here */
01071   
01072   end_of_command = time(NULL);
01073   elapsed_time = difftime(end_of_command, start_of_command);
01074   
01075   if (elapsed_time > command_msg_time || ipc_msg_level >= IPC_OVERWHELM) {
01076     int elapsed_sec=int(fmod(elapsed_time,      60));
01077     int elapsed_min=int(fmod(elapsed_time/60,   60));
01078     int elapsed_hours=int(   elapsed_time/60/60);
01079     if (ipc_msg_level >= IPC_VERBOSE) {
01080       command_print_to_str( &(*cmd), str, CMD_MAX_LINE_LENGTH);
01081       ipc_notify(IPC_ONE,IPC_VERBOSE,"Command %2d (%s) took %g seconds (%d:%02d:%02d), completing %.24s",
01082                  command_number, str, (double)elapsed_time,
01083                  elapsed_hours,elapsed_min,elapsed_sec,
01084                  ctime(&end_of_command));
01085     }
01086     else
01087       ipc_notify(IPC_ONE,IPC_STD,"Command %2d (%s) took %g seconds (%d:%02d:%02d), completing %.24s",
01088                  command_number, cmd->def->name, (double)elapsed_time,
01089                  elapsed_hours,elapsed_min,elapsed_sec,
01090                  ctime(&end_of_command));
01091   }
01092   
01093   return status;
01094 }
01095 
01096 
01097 
01099 cmdstat cmddefs_exec_str(const char* cmdtxt)
01100 {
01101   Command cmd;
01102   char buf[CMD_MAX_LINE_LENGTH];
01103 
01104   /* Checks that last two characters are filled with nulls; if not string must be near the limit */
01105   buf[CMD_MAX_LINE_LENGTH-2]=0; /* In case strncpy doesn't fill with nulls on this system */
01106   strncpy(buf,cmdtxt,CMD_MAX_LINE_LENGTH);
01107   if (buf[CMD_MAX_LINE_LENGTH-2]!=0)
01108     ipc_notify(IPC_ONE,IPC_ERROR,"Line length exceeded hard-coded limit of %d",int(CMD_MAX_LINE_LENGTH));
01109 
01110   if (!command_parse_line(&cmd, buf))
01111     return CMD_EMPTY;
01112 
01113   return command_exec(&cmd);
01114 }
01115 
01116 
01117 
01133 int cmddefs_exec_batch(CmdDefs_LineGenerator fn, const char * description)
01134 {
01135   int command_count;
01136   int orig_commands_succeeded=commands_succeeded;
01137   int orig_commands_failed=commands_failed;
01138   static int done;
01139   static unsigned linelength;
01140   time_t current_time;
01141 
01142   command_count=0;
01143   done=False;
01144 
01145   if (cmdparam_changes_verbose){ 
01146     current_time = time(NULL);
01147     ipc_notify(IPC_ONE,IPC_SUMMARY,"Command %s execution started %.24s",
01148                description,ctime(&current_time));
01149   }
01150 
01151   
01152   /* Read a line, broadcast it to all PEs, execute it on all PEs,
01153      and repeat until the commands have all been read.
01154   */
01155   while (!done) {
01156 
01157     /* One PE gets the next line */
01158     if (AMPARENTPE){
01159       linelength=0;
01160       cmddefs_line_buffer[0]='\0';
01161       done=(*fn)();
01162       
01163       if (!done && *cmddefs_line_buffer) {
01164         linelength=strlen(cmddefs_line_buffer);
01165         
01166         /* Strip trailing newline, if any */
01167         if (cmddefs_line_buffer[linelength-1] == '\n') {
01168           cmddefs_line_buffer[linelength-1]='\0';
01169           linelength--;
01170         }
01171         
01172         if (linelength >= CMD_MAX_LINE_LENGTH)
01173           ipc_notify(IPC_ALL,IPC_WARNING,"Command line may be truncated (limit is %d)",CMD_MAX_LINE_LENGTH);
01174       }
01175     }
01176 
01177     /* Synchronize and copy flags (and cmd line, if any) */
01178     ipc_barrier(); 
01179     ipc_get(&done,       IPC_INT,      1, PARENTPE);
01180     ipc_get(&linelength, IPC_UNSIGNED, 1, PARENTPE);
01181     if (!done && linelength>0)
01182       ipc_get((int *)(&cmddefs_line_buffer[0]), IPC_INT, (linelength+1)/sizeof(int)+1, PARENTPE);
01183 
01184     /* Make sure every PE has a local copy of the cmd line */
01185     ipc_barrier(); 
01186 
01187     /* If there's a command line present, parse and execute it (on all PEs) */
01188     if (!done && linelength>0) {
01189       ipc_notify(IPC_ONE,IPC_OVERWHELM,"Parsing command string `%s'",cmddefs_line_buffer);
01190       if (cmddefs_exec_str(cmddefs_line_buffer) != CMD_EMPTY)
01191         command_count++;
01192     }
01193   }
01194   /* To allow multiply-nested calls to this function */
01195   done=False;
01196 
01197   current_time = time(NULL);
01198   const bool haderrors = (commands_failed-orig_commands_failed > 0);
01199   if (cmdparam_changes_verbose || haderrors)
01200     ipc_notify(IPC_ONE,(haderrors? IPC_ALERT : IPC_SUMMARY),
01201                "Command %s execution completed %.24s; %d succeeded, %d failed",
01202                description,ctime(&current_time),
01203                commands_succeeded-orig_commands_succeeded,
01204                commands_failed-orig_commands_failed);
01205 
01206   return commands_failed-orig_commands_failed;
01207 }
01208 
01209 
01210 
01213 int cmddefs_get_line_from_file( void )
01214 {
01215   if (!cmddefs_file)
01216     return true;
01217   
01218   bool done=false;
01219   char* lastchar=cmddefs_line_buffer;
01220   *lastchar='\\';
01221 
01222   while (!done && *lastchar=='\\') {
01223     done = (fgets(lastchar,CMD_MAX_LINE_LENGTH+1,cmddefs_file) == NULL);
01224     lastchar += strlen(lastchar)-2;
01225     if (lastchar < cmddefs_line_buffer) lastchar = cmddefs_line_buffer;
01226   }
01227   return done;
01228 }
01229 
01230 
01231 
01235 int cmddefs_exec_file(const char *filename)
01236 {
01237   char description[255];
01238   int status,nofile=False;
01239   FILE *orig_cmddefs_file=cmddefs_file;
01240   
01241   cmddefs_file=0;
01242 
01243   
01244   /* 25 January 2002  This code seems to fail silently if the requested
01245      file is a directory.  It would be better to either ignore directories
01246      or to fail verbosely, but I don't know of a portable method for
01247      deciding if a file is a directory. */
01248 
01249   /* Only one PE opens the file */
01250   if (AMPARENTPE){
01251     /* First try the current directory */
01252     cmddefs_file=fopen(filename,"r");
01253     
01254     /* Then try all the possible paths */
01255     if (!cmddefs_file){
01256       StringParser p; /* Parse commandpath to get list of paths */
01257       StringParser::arglist words;
01258       p.parse(commandpath,words);
01259       StringParser::argptr i=words.begin();
01260       while (i!=words.end() && !cmddefs_file) {
01261         const string name = (*i)+filename;
01262         cmddefs_file=fopen(name.c_str(),"r");
01263         i++;
01264       }
01265     }
01266     if (!cmddefs_file){ 
01267       ipc_notify(IPC_ALL,IPC_ERROR,"Couldn't open command file %s",filename);
01268       nofile=True;
01269     }
01270   }
01271   
01272   SNPRINTF(description,255,"file %s",filename);
01273   status = cmddefs_exec_batch(&cmddefs_get_line_from_file,description);
01274   
01275   if (cmddefs_file)
01276     fclose(cmddefs_file);
01277   
01278   cmddefs_file=orig_cmddefs_file;
01279   
01280   return (nofile? CMD_FILE_ERROR : status);
01281 }
01282 
01283 
01284 
01286 int cmddefs_num_total(void)
01287 {  return command_definitions.num;  }
01288 
01289 
01290 
01295 void command_print_to_str( const Command *cmd, char *str, size_t nchars)
01296 {
01297   int i;
01298   char *str_ptr=str;
01299   size_t charsleft=nchars-1;
01300 
01301   /* Print command name */
01302   SNPRINTF(str_ptr,charsleft,"%s",cmd->def->name);
01303   charsleft -= strlen(str_ptr);
01304   str_ptr   += strlen(str_ptr);
01305 
01306   /* Print arguments */
01307   for(i=0; i < cmd->argc; i++){
01308     SNPRINTF(str_ptr,charsleft," %s",cmd->argv[i]);
01309     charsleft -= strlen(str_ptr);
01310     str_ptr   += strlen(str_ptr);
01311   }
01312 }
01313 
01314 
01315 
01316 void cmddefs_sort(void)
01317 {
01318   qsort(command_definitions.list, command_definitions.num,
01319         sizeof(CommandDefinition), cmddef_compare);
01320 }
01321 
01322 
01324 int my_strncasecmp(const char *str1, const char *str2, size_t n)
01325 {
01326   for (; n>0 ; n--, str1++, str2++) {
01327     if (toupper(*str1) != toupper(*str2))
01328       return (toupper(*str1) - toupper(*str2));
01329     if (*str1 == '\0')  return 0;
01330   }
01331   return 0;
01332 }
01333 
01334 
01336 int cmddef_compare(const void* def1, const void* def2)
01337 {
01338   return( my_strncasecmp(((const CommandDefinition*)def1)->name,
01339                          ((const CommandDefinition*)def2)->name,
01340                          CMDDEF_MAX_NAME_LENGTH) );
01341 }
01342 
01343 
01345 void cmddef_print_usage(FILE *fp, const CommandDefinition* cmddef)
01346 {
01347   if (cmddef->usage) 
01348     fprintf(fp,cmddef->usage,cmddef->name);
01349   else {
01350     int i;
01351     fprintf(fp,"%s",cmddef->name);
01352     for (i=0; i< cmddef->minargs; i++)
01353         fprintf(fp," <arg>");
01354     fprintf(fp," [<optional-args>]");
01355   }
01356 }
01357 
01358 
01360 void cmddef_print_usage_to_str(const CommandDefinition* cmddef, char *str, size_t nchars)
01361 {
01362   if (cmddef->usage)
01363     SNPRINTF(str,nchars,cmddef->usage,cmddef->name);
01364   else {
01365     char *str_ptr=str;
01366     size_t charsleft=nchars-1;
01367     int i;
01368     
01369     /* Print command name */
01370     SNPRINTF(str_ptr,charsleft,"%s",cmddef->name);
01371     charsleft -= strlen(str_ptr);
01372     str_ptr   += strlen(str_ptr);
01373     
01374     /* Print arguments */
01375     for (i=0; i< cmddef->minargs; i++) {
01376       SNPRINTF(str_ptr,charsleft," <arg%d>",i+1);
01377       charsleft -= strlen(str_ptr);
01378       str_ptr   += strlen(str_ptr);
01379     }
01380     SNPRINTF(str_ptr,charsleft," [<optional-args>]");
01381     charsleft -= strlen(str_ptr);
01382     str_ptr   += strlen(str_ptr);
01383   }
01384 }
01385 
01386 
01387 
01389 void cmddefs_print_all(FILE *fp)
01390 {
01391   int i;
01392   
01393   for(i=0; i< command_definitions.num; i++) {
01394     cmddef_print_usage(fp,&(command_definitions.list[i]));
01395     fprintf(fp,"\n");
01396   }
01397 }
01398 
01399 
01400 
01401 void cmddefs_print_doc_for_index(FILE *fp, int idx)
01402 {
01403   cmddef_print_usage(fp,&(command_definitions.list[idx]));
01404   fprintf(fp,"\n\n");
01405   
01406   if (command_definitions.list[idx].doc) {
01407     fprintf(fp,command_definitions.list[idx].doc,
01408             command_definitions.list[idx].name);
01409     fprintf(fp,"\n");
01410   }
01411   else
01412     fprintf(fp,"No further help available.\n");
01413 }
01414 
01415 
01416 
01418 void cmddefs_print_doc(FILE *fp, const char* name)
01419 {
01420   int idx = 0;
01421   int found = False;
01422 
01423   for (idx=command_definitions.num-1; !found && idx >= 0; idx--)
01424     if (strncmp(name,command_definitions.list[idx].name,CMDDEF_MAX_NAME_LENGTH) == 0) {
01425       cmddefs_print_doc_for_index(fp,idx);
01426       found = True;
01427     }
01428   
01429   if (!found) 
01430     ipc_notify(IPC_ONE,IPC_ERROR,"Don't know anything about command %s",name);
01431 }
01432 
01433 
01434 
01435 void cmddefs_print_all_doc(FILE *file)
01436 {
01437   int i;
01438   fprintf(file,"%s\n",BlackboardType::help_separator);
01439   for (i=0; i< cmddefs_num_total(); i++) {
01440     cmddefs_print_doc_for_index(file,i);
01441     fprintf(file,BlackboardType::help_separator);
01442     fprintf(file,"\n");
01443   }
01444 }
01445 
01446 
01447 
01454 char* cmddefs_completion_generator (char *text, int state)
01455 {
01456   static int idx, len;
01457   char *name;
01458 
01459   /*
01460     If this is a new word to complete, initialize the index counters
01461     to the beginning and save the length of text for efficiency
01462   */
01463   if (!state) {
01464     idx = 0;
01465     len = strlen(text);
01466   }
01467   
01468   /* Return the next command which partially matches, if any. */
01469   while (idx < command_definitions.num) {
01470     name = command_definitions.list[idx++].name;
01471     if (strncmp (name, text, len) == 0)
01472       return (cmdparam_dupstr(name));
01473   }
01474   
01475   return ((char *)NULL); /* No more matches */
01476 }
01477 
01478 
01479 
01480 /******************************************************************************/
01481 /* Counter hook handling                                                    */
01482 /******************************************************************************/
01483 
01484 
01506 int hooklists_define_list( const char * listname, const char * countername )
01507 {
01508   if (hooklists.num >= HOOKLISTS_MAX_NUM) {
01509     ipc_notify(IPC_ONE,IPC_ERROR,"Maximum number of hook lists reached");
01510     return CMD_PARAMETER_ERROR;
01511   }
01512   
01513   strncpy(hooklists.lists[hooklists.num].name,listname,HOOKLIST_MAX_NAME_LENGTH);
01514   strncpy(hooklists.lists[hooklists.num].countername,countername,HOOKLIST_MAX_NAME_LENGTH);
01515   hooklists.lists[hooklists.num].current=0;
01516   hooklists.num++;
01517   
01518   return hooklists.num - 1; /* Returns the HooklistNum for the new list */
01519 }
01520 
01521 
01522 
01523 /*
01524   Define a command to execute at a specfied counter.
01525 
01526   First argument is the name of the Hooklist, the second is the
01527   counter number or range to which it applies, second is the command
01528   name, and rest are arguments to be given to the command.  A counter
01529   range may be of the form "b" (a single counter value, b), "b-e" (a
01530   range of counter values b through e, inclusive), or "b-e%s", an
01531   inclusive range where the command is executed only when the counter
01532   is evenly divisible by the given step s.
01533 
01534   Examples:
01535 
01536      hook before_input        5 set exc_rad 1
01537       (Sets the param "exc_rad" to 1.0 at counter value 5)
01538 
01539      hook after_learning    6-9 plot
01540       (Calls command "plot" at counter values 6, 7, 8 and 9)
01541 
01542      hook after_learning  6-9%3 plot
01543       (Calls command "plot" at counter values 6 and 9)
01544 
01545   Hooks with the same start counter are executed in the order
01546   defined.  If desired, could add a way to set the "order" struct 
01547   elem explicitly to allow full user control over the order of
01548   execution, but it's probably not necessary.
01549 */
01550 #define nexthooknum (hooklists.lists[hooklist].num)
01551 cmdstat cmd_hook( CMD_ARGS )
01552 {
01553   int i;
01554   HooklistNum hooklist=Uninitialized;
01555   CounterHookDefinition* hook = NULL;
01556   const char *specifier=argv[1];
01557   
01558   /* Look up hook list index */
01559   for (i=0; i<hooklists.num; i++)
01560     if (!strncmp(hooklists.lists[i].name,argv[0],HOOKLIST_MAX_NAME_LENGTH))
01561       hooklist=i;
01562   
01563   if (hooklist==Uninitialized) {
01564     ipc_notify(IPC_ONE,IPC_ERROR,"Hook list is not defined: %s",argv[0]);
01565     return CMD_PARAMETER_ERROR;
01566   }
01567 
01568   if (nexthooknum >= HOOKLIST_MAX_NUM) {
01569     ipc_notify(IPC_ONE,IPC_ERROR,"Reached limit for number of counter hooks");
01570     return CMD_PARAMETER_ERROR;
01571   }
01572 
01573   hook = &(hooklists.lists[hooklist].defs[nexthooknum]);
01574   hook->order=hook_definition_counter++;
01575   
01576   /* Look up the command and link in its definition */
01577   {
01578     CommandDefinition* def = cmddefs_lookup(argv[2]);
01579     if (!def) return CMD_PARAMETER_ERROR;
01580     hook->cmd.def = def;
01581   }
01582   
01583   /*  Copy arguments to this hook's command slot  */
01584   hook->cmd.argc = argc-3;
01585   for (i=0; i< argc-3; i++) {
01586     hook->argstofree[i] = cmdparam_dupstr(argv[i+3]);
01587     hook->cmd.argv[i] = hook->argstofree[i];
01588   }
01589   hook->argstofree[i]=NULL;
01590   
01591   if (!command_check_usage(&(hook->cmd)))
01592     return CMD_PARAMETER_ERROR;
01593   
01594   if (hook->cmd.def->minargs > argc-3)
01595     ipc_notify(IPC_ONE,IPC_WARNING,"Command %s in hook requires %d args but only %d supplied");
01596   
01597   /* Create one or more hooks, as appropriate for the counter specifier */
01598   Parse parser(blackboard, specifier);
01599   while (!parser.empty()) {
01600     
01601     /* Parse counter specifier */
01602     if (!hook_parse_specifier( hook, parser)) {
01603       ipc_notify(IPC_ONE,IPC_ERROR,"Could not parse counter specifier");
01604       return CMD_PARAMETER_ERROR;
01605     }
01606     nexthooknum++;
01607     
01608     /* Display result of parsing, if desired */
01609     if (ipc_msg_level >= IPC_VERBOSE) {
01610       char str[CMD_MAX_LINE_LENGTH];
01611       command_print_to_str(&(hook->cmd),str,CMD_MAX_LINE_LENGTH);
01612       ipc_notify(IPC_ONE,IPC_VERBOSE,"Defined counter_hook %d-%d%%%d %s",
01613                  hook->start,hook->end,hook->step,str);
01614     }
01615       
01616     /* Define another hook with same parameters, if requested */
01617     if (*specifier) {
01618       if (nexthooknum >= HOOKLIST_MAX_NUM) {
01619         ipc_notify(IPC_ONE,IPC_ERROR,"Reached limit for number of counter hooks");
01620         return CMD_PARAMETER_ERROR;
01621       }
01622       
01623       hook_copy( hook, &(hooklists.lists[hooklist].defs[nexthooknum]));
01624       hook =           &(hooklists.lists[hooklist].defs[nexthooknum]);
01625     }
01626   }
01627 
01628 
01629   /* Keep the list sorted */
01630   hooklists_sort(hooklist);
01631 
01632   return CMD_NO_ERROR;
01633 }
01634 
01635 
01636 
01638 int hook_parse_specifier( CounterHookDefinition *hook, Parse& parser )
01639 {
01640   hook->step = 1; /* standard default */
01641   
01642   /* First token is required -- the counter start value */
01643   hook->start = hook->end = (int)ROUND(parser.getnumericval());
01644   if (hook->start < 0) return False;
01645   if (parser.empty()) return True;
01646   
01647   /* Next token, if any, is the counter end value */
01648   if (parser.consumeifpresent("-")) {
01649     if (parser.empty()) return False;
01650     hook->end = (int)ROUND(parser.getnumericval());
01651     if (hook->end < 0) return False;
01652   }
01653   if (parser.empty()) return True;
01654   
01655   /* Next token, if any, is the counter step specifier */
01656   if (parser.consumeifpresent("%")) {
01657     if (parser.empty()) return False;
01658     int num = (int)ROUND(parser.getnumericval());
01659     if (num < 1) return False;
01660     hook->step = num;
01661   }
01662   if (parser.empty()) return True;
01663 
01664   /* String should either be empty now, or have a comma followed by more specs */
01665   if (parser.consumeifpresent(",")) {
01666     if (parser.empty()) return False;
01667   }
01668   else
01669     if (!parser.empty())
01670       return False;
01671     
01672   return True;
01673 }
01674 
01675 
01676     
01678 void hook_copy( CounterHookDefinition *source, CounterHookDefinition *dest)
01679 {
01680   int i;
01681   
01682   /* Command */
01683   dest->cmd.def = source->cmd.def; 
01684 
01685   /* Arguments (pointers copied only, not the strings) */
01686   dest->cmd.argc = source->cmd.argc;
01687   for (i=0; i< source->cmd.argc; i++)
01688     dest->cmd.argv[i] = source->cmd.argv[i];
01689   dest->argstofree[0]=NULL;
01690   
01691   /* Counter specifier */
01692   dest->start = source->start;
01693   dest->end   = source->end;
01694   dest->step  = source->step;
01695 
01696   /* Execution order */
01697   dest->order = source->order;
01698 }
01699 
01700 
01701 
01706 void hooklists_sort(HooklistNum hooklist)
01707 {
01708   qsort(&(CURRENT_HOOK(hooklists.lists[hooklist])),
01709         hooklists.lists[hooklist].num - hooklists.lists[hooklist].current,
01710         sizeof(CounterHookDefinition),
01711         hook_compare);
01712 }
01713 
01714 
01715 
01718 int hook_compare(const void* hook1, const void* hook2)
01719 {
01720   const CounterHookDefinition* h1=(const CounterHookDefinition*)hook1;
01721   const CounterHookDefinition* h2=(const CounterHookDefinition*)hook2;
01722 
01723   return ( h1->start == h2->start ?
01724            h1->order - h2->order :
01725            h1->start - h2->start );
01726 }
01727 
01728 
01729 
01739 void hooklists_run_list(HooklistNum hooklist, int counter, int catchuponly)
01740 {
01741   int i;
01742 
01743   /* Abort early if no more hooks available */
01744   if (!HAS_CURRENT_HOOK(hooklists.lists[hooklist]))
01745     return;
01746   
01747   /* Abort early if next valid hook not yet reached */
01748   if (counter < CURRENT_HOOK(hooklists.lists[hooklist]).start)
01749     return;
01750   
01751   /*
01752     Clear out expired hooks, calling only the ones that specify to do so,
01753     and those at most once.  No command should ever end up being called
01754     here if this procedure is called once for every counter value, since
01755     commands expiring on the previous counter value are not called.  Note
01756     that the latter policy may open up a potential odd bug in some weird
01757     situations; since the behavior differs when a counter is called every
01758     other time compared to being called every time.  It might be better
01759     to totally dispense with the automatic calls of catchup commands,
01760     using a separate call (for each missing iteration, with catchuponly
01761     true) to force catchup.
01762   */
01763   for (; (HAS_CURRENT_HOOK(hooklists.lists[hooklist])) && 
01764          (counter > CURRENT_HOOK(hooklists.lists[hooklist]).end);
01765        hooklists.lists[hooklist].current++)
01766     if ( ( CURRENT_HOOK(hooklists.lists[hooklist]).end != counter-1) && 
01767          ( CURRENT_HOOK(hooklists.lists[hooklist]).cmd.def->catchup ) )
01768       command_exec(&(CURRENT_HOOK(hooklists.lists[hooklist]).cmd));
01769 
01770   /* Check every hook starting at before this counter value */
01771   for (i=hooklists.lists[hooklist].current;
01772        (i<hooklists.lists[hooklist].num) &&
01773          (counter >= hooklists.lists[hooklist].defs[i].start);
01774        i++) {
01775     CounterHookDefinition* hookdef= &(hooklists.lists[hooklist].defs[i]);
01776   
01777     /* If counter is in specified range and is at the appropriate step, call the command */
01778     if ( (counter <= hookdef->end) &&
01779          ((counter % hookdef->step) == 0) &&
01780          (!catchuponly || hookdef->cmd.def->catchup ) ) {
01781       /* For continuous hooks, only notify user when started */
01782       if (hookdef->step !=1 || counter == hookdef->start)
01783         ipc_notify(IPC_ONE,IPC_STD,"Running hook `%s' %s at %s %d",
01784                    hookdef->cmd.def->name,
01785                    hooklists.lists[hooklist].name,
01786                    hooklists.lists[hooklist].countername,
01787                    counter);
01788       if (hookdef->step==1 && counter==hookdef->start && hookdef->end > hookdef->start)
01789         ipc_notify(IPC_ONE,IPC_STD,"(Hook `%s' will run at every %s until %s %d)",
01790                    hookdef->cmd.def->name,
01791                    hooklists.lists[hooklist].countername,
01792                    hooklists.lists[hooklist].countername,
01793                    hookdef->end);
01794       command_exec(&(hookdef->cmd));
01795     }
01796   }
01797 }   
01798 
01799 
01800 
01802 void hooklists_reset_list(HooklistNum hooklist)
01803 {
01804   hooklists.lists[hooklist].current=0;
01805   hooklists_sort(hooklist);
01806   
01807   return;
01808 }   
01809 
01810 
01811 
01813 void hooklists_empty_list(HooklistNum hooklistnum)
01814 {
01815   int i;
01816   CounterHooklist* hooklist = &(hooklists.lists[hooklistnum]);
01817 
01818   /* Free allocated items */
01819   for (i=0; i<hooklist->num; i++) {
01820     int arg=0;
01821     while (hooklist->defs[i].argstofree[arg])
01822       free(hooklist->defs[i].argstofree[arg++]);
01823   }
01824   
01825   hooklist->num=0;
01826   hooklist->current=0;
01827   
01828   return;
01829 }   
01830 
01831 
01832 
01837 void hook_print_to_str( CounterHookDefinition *hook, char *hooklistname, char *str, size_t nchars)
01838 {
01839   char *str_ptr=str;
01840   size_t charsleft=nchars-1;
01841 
01842   SNPRINTF(str_ptr,charsleft,"hook %-15s %6d", hooklistname, hook->start);
01843   charsleft -= strlen(str_ptr); str_ptr += strlen(str_ptr);
01844 
01845   /* Only print counter end if different */
01846   if (hook->end != hook->start) {
01847     SNPRINTF(str_ptr,charsleft,"-%d",hook->end);
01848     charsleft -= strlen(str_ptr); str_ptr += strlen(str_ptr);
01849   }
01850   
01851   /* Only print non-single steps */
01852   if (hook->step != 1) {
01853     SNPRINTF(str_ptr,charsleft,"%%%d", hook->step);
01854     charsleft -= strlen(str_ptr); str_ptr += strlen(str_ptr);
01855   }
01856 
01857   /* Print command text */
01858   SNPRINTF(str_ptr,charsleft,"  ");
01859   charsleft -= strlen(str_ptr); str_ptr += strlen(str_ptr);
01860   command_print_to_str(&(hook->cmd), str_ptr,charsleft);
01861   charsleft -= strlen(str_ptr); str_ptr += strlen(str_ptr);
01862 }
01863 
01864 
01865 
01870 void hooklists_log(void) 
01871 {
01872   int i,hooklist;
01873   char str[CMD_MAX_LINE_LENGTH];
01874   
01875   for (hooklist=0; hooklist<hooklists.num; hooklist++)
01876     for (i=0; i<hooklists.lists[hooklist].num; i++) {
01877       hook_print_to_str( &(hooklists.lists[hooklist].defs[i]),
01878                          hooklists.lists[hooklist].name,
01879                          str, CMD_MAX_LINE_LENGTH);
01880       ipc_log(IPC_ONE,"%s\n",str);
01881     }
01882               
01883   ipc_log(IPC_ONE,"\n");
01884 }
01885 
01886 
01887 
01888 /******************************************************************************/
01889 /* Routines that work with parameters and commands both                       */
01890 /******************************************************************************/
01891 
01892 
01896 size_t  cmdparam_identifier_length(const char* str)
01897 {
01898   const char * rem;
01899 
01900   /* 1st char must be alphabetical */
01901   if (!str || !isalpha(*str))  return 0;
01902 
01903   /* Rest must be alphanumeric or an underscore */
01904   for (rem=str; isalnum(*rem) || *rem == '_'; rem++);
01905   
01906   return rem-str;
01907 }
01908 
01909 
01910 
01912 char* cmdparam_dupstr (const char *str)
01913 {
01914   char *newstr = (char *)malloc(strlen(str)+1);
01915   if (!newstr)
01916     ipc_abort(IPC_EXIT_OUT_OF_MEMORY,"Cannot allocate string");
01917   strcpy (newstr, str);
01918   return (newstr);
01919 }
01920 
01921 
01922 
01925 int cmdparams_print_doc(FILE *fp, const char* name)
01926 {
01927   int idx = 0;
01928   int found = False;
01929 
01930   const ParamType& param=blackboard.lookup_with_path(name,false);
01931   if (param.is_valid()) {
01932     fprintf(fp,param.doc_string().c_str());
01933     found = True;
01934   }
01935   else 
01936     for (idx=command_definitions.num-1; !found && idx >= 0; idx--)
01937       if (strncmp(name,command_definitions.list[idx].name,CMDDEF_MAX_NAME_LENGTH) == 0) {
01938         cmddefs_print_doc_for_index(fp,idx);
01939         found = True;
01940       }
01941   
01942   return found;
01943 }
01944 
01945 
01948 char* params_completion_generator (char *text, int state)
01949 {  return blackboard.completion_generator (text,state);  }
01950 
01951 
01953 char* cmdparams_completion_generator (char *text, int state)
01954 {
01955   char *name = cmddefs_completion_generator (text, state);
01956   if (name) return name;
01957   return blackboard.completion_generator (text, state);
01958 }
01959 
01960 
01961 int cmdparam_save_current_state(const char * filename)
01962 {
01963   int i,hooklist;
01964   FILE *file;
01965   char str[CMD_MAX_LINE_LENGTH];
01966 
01967   file=fopen(filename,"w+");
01968   if(file==NULL) {
01969     ipc_notify(IPC_ALL,IPC_ERROR,"Can't open %s file", filename);
01970     return CMD_FILE_ERROR;
01971   }
01972   
01973   fprintf(file,"# Parameters:\n\n");
01974   blackboard.root().save_parameters(file);
01975 
01976   fprintf(file,"\n# Hooks:\n\n");
01977   for (hooklist=0; hooklist<hooklists.num; hooklist++)
01978     for (i=0; i<hooklists.lists[hooklist].num; i++) {
01979       hook_print_to_str( &(hooklists.lists[hooklist].defs[i]),
01980                          hooklists.lists[hooklist].name,
01981                          str, CMD_MAX_LINE_LENGTH);
01982       fprintf(file,"%s\n",str);
01983     }
01984 
01985   fprintf(file,"\n");
01986   fclose(file);
01987 
01988   return CMD_NO_ERROR;
01989 }
01990 
01991 
01992 
01994 string trim_quotes(const string& s)
01995 {
01996   if (s.empty()) return s;
01997   unsigned front = 0;
01998   unsigned back  = s.length()-1;
01999   if (s[front] == '"' || s[front] == '\'') {
02000     front++;
02001     if (s[back] == s[0])
02002       back--;
02003   }
02004   return string(s,front,back-front+1);
02005 }
02006 
02007 
02008 /*
02009   If the given string is a backquoted shell command, evaluate it;
02010   otherwise return it unchanged.
02011 
02012   Implementation: Yuck.
02013 
02014   This capability is not truly multi-process or multi-processor safe,
02015   since the temporary file may clash between two processors or two
02016   processes.  It would be safer to use mkstemp, yet "system" doesn't
02017   appear to have any way to pass an existing open file.  Instead shell
02018   redirection is used to save the output to a file, which is highly
02019   undesirable.
02020 */
02021 string filter_backquotes(const string arg)
02022 {
02023   if (arg.empty() || arg[0]!='`' || arg[arg.length()-1]!='`')
02024     return arg;
02025 
02026   /* Do string substitution */
02027   const string noquotes(arg,1,arg.length()-2);
02028   string parsed;
02029   parsed = CmdParamStringParser(blackboard).parse(noquotes,parsed);
02030   
02031   const string tempname = TEMPNAME(NULL,"backq");
02032   const string shellcmd = parsed+" > "+tempname;
02033   system(shellcmd.c_str());
02034   std::ifstream s(tempname.c_str());
02035 
02036   /* Uses a fixed-size buffer because getline for strings is not
02037      implemented for egcs 1.1.2 */
02038   const int buffsize=1024;
02039   char buffer[buffsize];
02040   s.getline(buffer,buffsize,EOF);
02041 
02042   /* Remove trailing newline, if present */
02043   string str=buffer;
02044   if (!str.empty() && str[str.length()-1]=='\n')
02045     str.replace(str.length()-1,1,"");
02046 
02047   remove(tempname.c_str());
02048   
02049   return str;
02050 }
02051 
02052 
02053 
02060 bool cmdparams_set_single( BlackboardType& params, const string& arg,
02061                            bool& seterror, bool mark, bool verbose,
02062                            bool copysetfn)
02063 {
02064   /* Abort if no equals sign found */
02065   unsigned pos = arg.find("=");
02066   if (pos>=arg.length())
02067     return false;
02068 
02069   /* Space before equals sign: either an error to be detected later
02070      or a sign that the item is not something for us to handle. */
02071   unsigned spacepos = arg.find(" ");
02072   if (spacepos<pos)
02073     return false;  
02074   
02075   const string pathstr = string(arg,0,pos);
02076   const string valstr  = filter_backquotes(trim_quotes(string(arg,pos+1,arg.length())));
02077 
02078   if (!params.set_matching(CmdParamStringParser(params),
02079                            pathstr,valstr,mark,verbose,false,copysetfn))
02080     seterror=true;
02081   
02082   return true;
02083 }
02084 
02085 /* Implementation of cmd_set for multiple arguments in a StringArgs */
02086 cmdstat cmdparams_set( BlackboardType& params, StringArgs& arglist,
02087                        bool mark, bool warn, bool verbose, bool copysetfns)
02088 {
02089   bool seterror = false;
02090   
02091   while (!arglist.empty()) {
02092     if (!cmdparams_set_single(params, arglist.top(string("")),
02093                               seterror,mark,verbose,copysetfns)) {
02094       if (warn) {
02095         const string rest = arglist.stringrep();
02096         ipc_notify(IPC_ONE,IPC_ERROR,"Syntax error in set arguments ('%s' is not of the form 'x=y')",
02097                    rest.c_str());
02098         return CMD_PARAMETER_ERROR;
02099       }
02100       return CMD_NO_ERROR;
02101     }
02102 
02103     /* Consume the argument */
02104     arglist.skip();
02105   }
02106   
02107   return (seterror? CMD_PARAMETER_ERROR : CMD_NO_ERROR);
02108 }
02109 
02110 
02111 
02112 /******************************************************************************/
02113 /* Commands                                                                   */
02114 /******************************************************************************/
02115 
02116 
02118 cmdstat cmd_set( CMD_ARGS )
02119 {
02120   CMD_ARG_LIST;
02121   return cmdparams_set(blackboard,arglist,true,true);
02122 }
02123 
02124 
02125 
02126 cmdstat cmd_quit( CMD_ARGS )
02127 {
02128   time_t current_time = time(NULL);
02129   ipc_notify(IPC_ONE,IPC_SUMMARY,"Exiting at %.24s", ctime(&current_time));
02130   
02131   if (argc>0)
02132     ipc_exit(IPC_EXIT_NORMAL,argv[0]);
02133   else
02134     ipc_exit(IPC_EXIT_NORMAL,"Exited via quit command");
02135 
02136   return CMD_NO_ERROR;
02137 }
02138 
02139 
02140 
02141 cmdstat cmd_clear_hooks( CMD_ARGS )
02142 {
02143   int status=CMD_NO_ERROR;
02144   HooklistNum hooklist;
02145   
02146   if (argc<1) /* Clear all lists by default */
02147     for (hooklist=0; hooklist<hooklists.num; hooklist++) {
02148       if (hooklists.lists[hooklist].num) {
02149         hooklists_empty_list(hooklist);
02150         ipc_notify(IPC_ONE,IPC_STD,"Emptied hooklist %s",hooklists.lists[hooklist].name);
02151       }
02152     }
02153   else {
02154     int i,arg;
02155 
02156     for (arg=0; arg<argc; arg++) {
02157       hooklist = Uninitialized;
02158       
02159       /* Look up hook list index */
02160       for (i=0; i<hooklists.num; i++)
02161         if (!strncmp(hooklists.lists[i].name,argv[arg],HOOKLIST_MAX_NAME_LENGTH))
02162           hooklist=i;
02163   
02164       if (hooklist==Uninitialized) {
02165         ipc_notify(IPC_ONE,IPC_ERROR,"Hook list is not defined: %s",argv[0]);
02166         status= CMD_PARAMETER_ERROR;
02167       }
02168       else if (hooklists.lists[hooklist].num) {
02169         hooklists_empty_list(hooklist);
02170         ipc_notify(IPC_ONE,IPC_STD,"Emptied hooklist %s",hooklists.lists[hooklist].name);
02171       }
02172     }
02173   }
02174   
02175   return status;
02176 }
02177 
02178 
02179 
02180 cmdstat cmd_exec( CMD_ARGS )
02181 {
02182   int status=0;
02183   
02184   for (int i=0; i<argc; i++) {
02185     int newstatus = cmddefs_exec_str(PARSE_C(argv[i]));
02186     if (newstatus>=0) status=newstatus;
02187   }
02188 
02189   return status;
02190 }
02191 
02192 
02193 
02194 cmdstat cmd_exec_catchup( CMD_ARGS )
02195 {
02196   int status=0;
02197   
02198   for (int i=0; i<argc; i++) {
02199     int newstatus = cmddefs_exec_str(PARSE_C(argv[i]));
02200     if (newstatus>=0) status=newstatus;
02201   }
02202 
02203   return status;
02204 }
02205 
02206 
02207 
02208 cmdstat cmd_exec_file( CMD_ARGS )
02209 {
02210   CMD_ARG_LIST;
02211   int status;
02212 
02213   cmddefs_exec_file_arglists.push(argl);
02214   status=cmddefs_exec_file(PARSE_C(argv[0]));
02215   cmddefs_exec_file_arglists.pop();
02216   
02217   return (status? CMD_FILE_ERROR : CMD_NO_ERROR);
02218 }
02219 
02220 
02221 
02222 cmdstat cmd_call( CMD_ARGS )
02223 {
02224   CMD_ARG_LIST;
02225 
02226   int status;
02227   const int oldipcverbosity = ipc_msg_level;
02228   const int newipcverbosity = IPC_REQUESTED;
02229 
02230   if (ipc_msg_level > newipcverbosity)
02231     ipc_msg_level = newipcverbosity;
02232   
02233   cmddefs_exec_file_arglists.push(argl);
02234   status=cmddefs_exec_file(PARSE_C(argv[0]));
02235   cmddefs_exec_file_arglists.pop();
02236   if (ipc_msg_level==newipcverbosity) /* If unchanged by file */
02237     ipc_msg_level=oldipcverbosity;
02238     
02239   return (status? CMD_FILE_ERROR : CMD_NO_ERROR);
02240 }
02241 
02242 
02243 
02245 cmdstat cmd_read_args( CMD_ARGS )
02246 {
02247   CMD_ARG_LIST;
02248   StringArgs sargs(p, cmddefs_exec_file_arglists.top().begin(), cmddefs_exec_file_arglists.top().end());
02249   sargs.skip();
02250   
02251   const string    parentstr = arglist.next(string(""));
02252   BlackboardType& parent = blackboard.lookup_map(parentstr);  
02253   return cmdparams_set(parent,sargs,true,true);
02254 }
02255 
02256 
02257 
02258 cmdstat cmd_define_param( CMD_ARGS )
02259 {
02260   CMD_ARG_PARAMS(define_param);
02261   
02262   const string pathstr     = arglist.next(string(""));
02263   PathSpecification path(pathstr);
02264   const string name        = path.last();
02265 
02266   // 10/2002 Why are the lower and upper bounds truncated to integers,
02267   // even for Float parameters?  Presumably that's a historical anomaly?
02268   const string type        = arglist.next(string());
02269   const bool   has_lbound  =!arglist.empty();
02270   const int    lower_bound = arglist.next(int(Uninitialized));
02271   const bool   has_ubound  =!arglist.empty();
02272   const int    upper_bound = arglist.next(int(Uninitialized));
02273   const string helpstr     = arglist.next(string());
02274   const bool   has_initval =!arglist.empty();
02275   const string initval     = arglist.next(string());
02276 
02277   /* Add support for newlines */
02278   const string helpstring = String::replace_all(string(helpstr),string("\\n"),string("\n")); 
02279   
02280   BlackboardType& parent = blackboard.lookup_map(path.allbutlast(),true,path.isrooted());
02281   if (parent.defined_locally(name)) /* Already defined */
02282     return CMD_NO_ERROR;
02283   
02284   BlackboardType::ParamType newparam;
02285   if      (type == "Param_String"  || type == "String" ) newparam = DECLARE_A(name.c_str(),false,string());
02286   else if (type == "Param_Boolean" || type == "Boolean") newparam = DECLARE_A(name.c_str(),false,False);
02287   else if (type == "Param_Integer" || type == "Integer") newparam = DECLARE_A(name.c_str(),false,0);
02288   else if (type == "Param_Float"   || type == "Float"  ) newparam = DECLARE_A(name.c_str(),false,0.0);
02289   else {
02290     ipc_notify(IPC_ONE,IPC_ERROR,"Couldn't define parameter of type %s",type.c_str());
02291     return CMD_PARAMETER_ERROR;
02292   }
02293 
02294   if (has_lbound && type != "Param_String")  newparam.add_lower_bound(lower_bound);
02295   if (has_ubound && type != "Param_String" && !(upper_bound < lower_bound))
02296     newparam.add_upper_bound(upper_bound);
02297   if (helpstring!="")  newparam.add_doc(cmdparam_dupstr(helpstring.c_str()));
02298   if (has_initval) newparam.set(CmdParamStringParser(parent),initval);
02299   
02300   if (parent.define_param(newparam) == 1 && cmdparam_changes_verbose)
02301     ipc_notify(IPC_ONE,IPC_STD,"Defined parameter %s",pathstr.c_str());
02302   
02303   return CMD_NO_ERROR;
02304 }
02305 
02306 
02307 
02308 cmdstat cmd_define_param_set( CMD_ARGS )
02309 {
02310   CMD_ARG_PARAMS(define_param_set);
02311 
02312   const string pathstr = arglist.next(string(""));
02313   PathSpecification path(pathstr);
02314 
02315   if (path.all().empty()) {
02316     ipc_notify(IPC_ONE,IPC_ERROR,"A parameter set must have a non-empty name");
02317     return CMD_PARAMETER_ERROR;
02318   }
02319 
02320   BlackboardType& parent = blackboard.lookup_map(path.allbutlast(),true,path.isrooted());
02321 
02322   /* Adds a new map if there isn't one by that name already */
02323   if (&parent == &(parent.lookup_map(path.last(),false)))
02324     parent.new_child(path.last(),true);
02325   
02326   return CMD_NO_ERROR;
02327 }
02328 
02329 
02330 
02331 cmdstat cmd_select_param_set( CMD_ARGS )
02332 {
02333   CMD_ARG_PARAMS(select_param_set);
02334   static std::vector<BlackboardType*> blackboardstack;
02335     
02336   const string pathstr = arglist.next(string(""));
02337   PathSpecification path(pathstr);
02338 
02339   /* Pop top of stack */
02340   if (!path.isrooted() && path.all().empty()) {
02341     if (blackboardstack.empty())
02342       ipc_notify(IPC_ONE,IPC_ERROR,"At the root level; cannot pop back to another parameter set");
02343     else {
02344       blackboard_ptr=blackboardstack.back();
02345       blackboardstack.pop_back();
02346     }
02347   }
02348 
02349   /* Push current param_set on stack and go to a new one */
02350   else {
02351     blackboardstack.push_back(blackboard_ptr);
02352 
02353     blackboard_ptr = &(blackboard.lookup_map(path.all(),true,path.isrooted()));
02354   }
02355 
02356   return CMD_NO_ERROR;
02357 }
02358 
02359 
02360 
02361 cmdstat cmd_param_default( CMD_ARGS )
02362 {
02363   (void)argc;
02364 
02365   blackboard.lookup(argv[0]).add_default_expr(argv[1]);
02366   
02367   return CMD_NO_ERROR;
02368 }
02369 
02370 
02371 
02372 cmdstat cmd_if( CMD_ARGS )
02373 {
02374   if (PARSE_I(argv[0]))
02375     return cmddefs_exec_by_name(argv[1],argc-2,&(argv[2]));
02376   
02377   return CMD_NO_ERROR;
02378 }
02379 
02380 
02381 
02382 cmdstat cmd_dotimes( CMD_ARGS )
02383 {
02384   int i;
02385   int status = CMD_NO_ERROR;
02386   
02387   for (i=0; i<PARSE_I(argv[0]); i++) {
02388     status = cmddefs_exec_by_name(argv[1],argc-2,&(argv[2]));
02389     if (status) return status;
02390   }
02391   
02392   return CMD_NO_ERROR;
02393 }
02394 
02395 
02396 
02397 cmdstat cmd_for( CMD_ARGS )
02398 {
02399   int status = CMD_NO_ERROR;
02400 
02401   for (cmd_set(1,&(argv[0])); PARSE_I(argv[1]); cmd_set(1,&(argv[2]))) {
02402     status = cmddefs_exec_by_name(argv[3],argc-4,&(argv[4]));
02403     if (status) return status;
02404   }
02405   
02406   return CMD_NO_ERROR;
02407 }
02408 
02409 
02410 
02411 cmdstat cmd_print( CMD_ARGS )
02412 {
02413   int i;
02414   
02415   for (i=0; i<argc; i++) {
02416     const string squeezed = String::replace_all(string(argv[i]),string("::"),string(""));
02417 
02418     /* Parameter name -- anything with all chars valid in a C identifier plus '::' */
02419     if (String::C_identifier_length(squeezed) == squeezed.length()) {
02420       const ParamType& param = blackboard.lookup_with_path(argv[i]);
02421       if (param.is_valid())
02422         ipc_notify(IPC_ONE,IPC_REQUESTED,"%s == %s",argv[i],param.stringrep().c_str());
02423     }
02424     /* String substitution */
02425     else if (*argv[i] == '$')
02426       ipc_notify(IPC_ONE,IPC_REQUESTED,"%s == %s",argv[i],PARSE_C(argv[i]));
02427     /* Numeric expression */
02428     else {
02429       double value = PARSE_F(argv[i]);
02430       ipc_notify(IPC_ONE,IPC_REQUESTED,"%s == %g",argv[i],value);
02431     }
02432   }
02433 
02434   return CMD_NO_ERROR;
02435 }
02436 
02437 
02438 
02439 cmdstat cmd_echo( CMD_ARGS )
02440 {
02441   for (int i=0; i<argc; i++) {
02442     const string str = String::replace_all(string(PARSE_C(argv[i])),string("\\n"),string("\n")); 
02443     ipc_notify(IPC_ONE,IPC_REQUESTED,"%s",str.c_str());
02444   }
02445   
02446   return CMD_NO_ERROR;
02447 }
02448 
02449 
02450 
02451 cmdstat cmd_system( CMD_ARGS )
02452 {
02453   char str[CMD_MAX_LINE_LENGTH];
02454   char *str_ptr=str;
02455   size_t charsleft=CMD_MAX_LINE_LENGTH-1;
02456   int i;
02457   int status=0;
02458   
02459   /* Print arguments as a command */
02460   for(i=0; i < argc; i++){
02461     SNPRINTF(str_ptr,charsleft,"%s%s",(i==0? "" : " "),PARSE_C(argv[i]));
02462     charsleft -= strlen(str_ptr);
02463     str_ptr   += strlen(str_ptr);
02464   }
02465 
02466   ipc_notify(IPC_ONE,IPC_VERBOSE,"Executing shell command `%s'",str);
02467   if (AMPARENTPE)
02468     status=system(str);
02469 
02470   return (status? CMD_MISC_ERROR : CMD_NO_ERROR);
02471 }

Generated on Mon Jan 20 02:35:44 2003 for RF-LISSOM by doxygen1.3-rc2