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 
00065 #include <string.h>
00066 #include <stdlib.h>
00067 #include <time.h>
00068 #include <ctype.h>
00069 #include <math.h>
00070 #include <string>
00071 #include <vector>
00072 #include <strstream.h>
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 
00084 
00085 
00086 /******************************************************************************/
00087 /* Defines and typedefs                                                       */
00088 /******************************************************************************/
00089 
00090 /* Determine which PE is in charge of file handling */
00091 #define PARENTPE (0)
00092 #define AMPARENTPE (ipc_my_process()==PARENTPE)
00093 
00094 
00095 /* Parameter definitions */
00096 
00097 #define PARAM_MAX_NAME_LENGTH 64
00098 
00099 typedef   union { char *c; int i; double f; } bound;  /* upper or lower bound */
00100 
00101 /* Holds a single parameter definition. */
00102 typedef struct
00103 { 
00104   char name[PARAM_MAX_NAME_LENGTH]; 
00105   int data_type ;                   
00106   int is_constant;                  
00111   union t { char *c; int *i; double *f; } data_ptr;
00112   bound lower_bound;                
00113   int enforce_lower;                
00114   bound upper_bound;                
00115   int enforce_upper;                
00116   SetFnWrapper* setfn;              
00117   const char *doc;                  
00118   int been_set;                     
00119   const char *default_expr;         
00120 } ParamInterface;     
00121 
00122 #define PARAM_MAX_ENTRIES 275
00123 
00124 /* Holds all the parameter definitions. */
00125 typedef struct{
00126   int num;
00127   ParamInterface list[PARAM_MAX_ENTRIES];
00128 }Blackboard;
00129 
00130 
00131 #ifndef LONG_DOUBLE_UNAVAILABLE
00132 typedef long double exprtype;
00133 #else
00134 typedef double exprtype;
00135 #endif
00136 
00137 /* Command definitions */
00138 
00139 #define CMDDEF_MAX_NUM         64   
00140 #define CMDDEF_MAX_NAME_LENGTH 64   
00141 #define CMDDEF_MAX_PREREQS      2   
00143 /* Holds a single command definition. */
00144 typedef struct{                     
00145   char name[CMDDEF_MAX_NAME_LENGTH+1]; 
00146   CmdWrapper* function;             
00147   int minargs;                      
00148   int catchup;                      
00149   int num_prereqs;                  
00150   int prereqs[CMDDEF_MAX_PREREQS];  
00151   int callstatus;                   
00152   const char *usage;                
00153   const char *doc;                  
00154 }CommandDefinition;
00155 
00156 /* Holds all the command definitions. */
00157 typedef struct{                     
00158   int num;
00159   CommandDefinition list[CMDDEF_MAX_NUM];
00160 }CommandDefinitionBlackboard;
00161 
00162 /* Holds a single command instance. */
00163 typedef struct{
00164   int  argc;                            
00165   const char *argv[CMD_MAX_ARGUMENTS];  
00166   CommandDefinition* def;               
00167 }Command;
00168 
00169 #define CMD_CALL(cmd) (*((cmd).def->function))((cmd).argc, (cmd).argv)
00170 
00171 
00172 /* Counter hook definitions */
00173 
00174 /* Holds a command to be executed at a specified counter value. */
00175 typedef struct
00176 {
00177   int  start;     
00178   int  end;       
00179   int  step;      
00181   int  order;     
00182   char *argstofree[CMD_MAX_ARGUMENTS+1];  
00183   Command cmd;    
00184 } CounterHookDefinition;
00185 
00186 #define HOOKLIST_MAX_NUM      128  
00187 #define HOOKLIST_MAX_NAME_LENGTH  32
00188 
00189 /* Holds a sorted list of the specified counter hooks of a given type. */
00190 typedef struct
00191 {
00192   char name[HOOKLIST_MAX_NAME_LENGTH]; 
00193   char countername[HOOKLIST_MAX_NAME_LENGTH]; 
00194   int current;                         
00195   int num;                             
00196   CounterHookDefinition defs[HOOKLIST_MAX_NUM]; 
00197 } CounterHooklist;
00198 
00199 #define HOOKLISTS_MAX_NUM 4
00200 
00201 #define HAS_CURRENT_HOOK(hooklist) ((hooklist).current < (hooklist).num)
00202 #define CURRENT_HOOK(hooklist)     ((hooklist).defs[(hooklist).current])
00203 
00204 /* Holds all the counter Hooklists. */
00205 typedef struct
00206 {
00207   int num;        
00208   CounterHooklist lists[HOOKLISTS_MAX_NUM]; 
00209 } CounterHooklists;
00210 
00211 
00212 /* Temporary list of command names and corresponding prerequisites,
00213    to store the info until the commands have all been defined.
00214 */
00215 typedef struct
00216 {
00217   int num;        
00218   const char* names[CMDDEF_MAX_NUM*CMDDEF_MAX_PREREQS];
00219   const char* prereqs[CMDDEF_MAX_NUM*CMDDEF_MAX_PREREQS];
00220 } PrereqList;
00221 
00222 
00223 
00224 /******************************************************************************/
00225 /* Global variables                                                           */
00226 /******************************************************************************/
00227 
00228 CounterHooklists hooklists;
00229 
00230 Blackboard blackboard;                            
00231 CommandDefinitionBlackboard command_definitions;  
00233 int cmdparam_warn_unknown    = True;              
00234 int cmdparam_changes_verbose = True;              
00236 int commands_succeeded = 0;                       /* So far                 */
00237 int commands_failed    = 0;                       /* So far                 */
00238 int command_num_called = 0;                       /* unique global counter  */
00239 
00240 
00241 int hook_definition_counter=0;                    /* unique global counter  */
00242 
00243 
00244 FILE *cmddefs_file=NULL;
00245 char commandpath[CMD_MAX_LINE_LENGTH] = "../command/ ../../command/ ../samples/";
00246 
00247 
00248 int  cmddefs_exec_file_argc=0;                    
00249 const char **cmddefs_exec_file_argv=NULL;          
00250 
00251 char cmddefs_line_buffer[CMD_MAX_LINE_LENGTH+1+sizeof(int)];
00252 
00253 PrereqList prereq_storage;
00254 
00255 const char cmdparam_help_separator[]=
00256 "_______________________________________________________________________________\n";
00257 
00258 
00259 #ifndef NO_SIGNAL_HANDLING
00260 typedef void (*signal_handler_type)(int);
00261 jmp_buf jmp_env;
00262 #endif
00263 
00264 
00265 
00266 /******************************************************************************/
00267 /* Prototypes for private functions                                           */
00268 /******************************************************************************/
00269 
00270 CMD_DECLARE(call);
00271 CMD_DECLARE(clear_hooks);
00272 CMD_DECLARE(define_param);
00273 CMD_DECLARE(dotimes);
00274 CMD_DECLARE(exec);
00275 CMD_DECLARE(exec_file);
00276 CMD_DECLARE(for);
00277 CMD_DECLARE(hook);
00278 CMD_DECLARE(if);
00279 CMD_DECLARE(print);
00280 CMD_DECLARE(quit);
00281 CMD_DECLARE(read_args);
00282 CMD_DECLARE(save_params);
00283 CMD_DECLARE(set);
00284 CMD_DECLARE(system);
00285 
00286 size_t  cmdparam_identifier_length(const char* str);
00287 void    ipc_init_hook( void );
00288 
00289 exprtype params_apply_op(exprtype lval, string op, exprtype rval);
00290 exprtype params_getnumericval(const char **remaining );
00291 const char *params_getstringval(const char **remaining );
00292 void*    params_getval(int* type, const char **remaining );
00293 exprtype params_next_term(const char **remaining, string previous_op="");
00294 ParamInterface* params_lookup(const char *name, size_t len);
00295 string   params_next_op(const char **remaining);
00296 exprtype params_parse_expr(const char **remaining);
00297 exprtype params_read_float(const char* str);
00298      
00299 int     cmddef_compare(const void* hook1, const void* hook2);
00300 void    cmddefs_sort(void);
00301 int     cmddefs_get_line_from_file( void );
00302 
00303 int     command_check_usage(Command* command);
00304 cmdstat command_exec(Command *cmd);
00305 CommandDefinition* cmddefs_lookup(const char *name);
00306 int     command_parse_line(Command *command, char *string);
00307 const char* command_parse_token(char** remaining);
00308 void    command_print_to_str( const Command *cmd, char *str, size_t nchars);
00309 void    cmddef_print_usage(FILE *fp, const CommandDefinition* cmddef);
00310 void    cmddef_print_usage_to_str(const CommandDefinition* cmddef, char *str, size_t nchars);
00311 void    cmddefs_activate_prereqs( void );
00312 void    cmddefs_activate_command_prereq( const char* name, const char *prereq );
00313 
00314 int     hook_compare(const void* hook1, const void* hook2);
00315 void    hook_copy( CounterHookDefinition *source, CounterHookDefinition *dest);
00316 void    hooklists_log(void);
00317 int     hook_parse_specifier( CounterHookDefinition *hook, const char **specifier );
00318 void    hook_print_to_str( CounterHookDefinition *hook, char *hooklistname, char *str, size_t nchars);
00319 void    hooklists_sort(HooklistNum hooklist);
00320 
00321 int     param_check( const ParamInterface* param, const void * valueptr);
00322 int     param_compare(const void* param1, const void* param2);
00323 const char *param_pp_boolean(int value);
00324 const char *param_pp_float(float value);
00325 const char *param_pp_int(int value);
00326 const char *param_pp_scalar(const ParamInterface* def, void *value);
00327 int     param_print_range(FILE *fp, ParamInterface* param);
00328 int     param_print_type(FILE *fp, ParamInterface* param);
00329 int     param_print_usage(FILE *fp, ParamInterface* param);
00330 int     param_print_value(FILE *fp, ParamInterface* param);
00331 void    params_sort(void);
00332 
00333 
00334 #ifndef NO_SIGNAL_HANDLING
00335 void command_exec_sigint_handler( int sig );
00336 #endif
00337 
00338 /******************************************************************************/
00339 /* Initialization hook                                                        */
00340 /******************************************************************************/
00341 
00346 void  cmdparam_init_hook( void )
00347 {
00348   ipc_init_hook();
00349 
00350   /* Commands provided by this file */
00351   
00352   CMD_DOC(clear_hooks,NULL,0,"%s [<hook_list>]*",
00353           "Delete all the currently-defined hooks in the given list(s), or delete\n"
00354           "all hooks in all lists if no lists are specified.");
00355 
00356   CMD_DOC(define_param,NULL,2,"%s <name> <type> [<lowbound> [<highbound> [<helpstring>] [<initialvalue>]]]",
00357           "Define a new parameter of the given type.  Space for the parameter is\n"
00358           "immediately allocated from the heap, but it is not initialized until\n"
00359           "the parameter is set unless an initial value is supplied.  The type name\n"
00360           "should be prefixed with Param_, e.g. Param_Integer,Param_Float,Param_Boolean,\n"
00361           "or Param_String.  Calling this command multiple times for the same name is not\n"
00362           "an error unless the type differs, but the bounds, helpstring, and initialvalue\n"
00363           "are only installed on the first call.");
00364 
00365   CMD_DOC(dotimes,NULL,2,"%s <expr> <command> [<argument>]*",
00366           "Execute the given command with the given arguments, the number of times\n"
00367           "specified by the given numeric expression.");
00368 
00369   CMD_DOC(for,NULL,8,"%s <param>=<initvalue> <test-expr> <param>=<incrementvalue> <command> [<argument>]*",
00370           "Execute the given command with the given arguments a number of times.\n"
00371           "First, the first param-value pair is passed to the set command, then as\n"
00372           "long as the numeric <test-expr> evaluates to a true value, the given command\n"
00373           "is executed and the second param-value pair is passed to the set command.\n"
00374           "Example: `define_param angle Param_Integer', then\n"
00375           "`for angle=0 angle<180 angle=angle+10 print angle'.");
00376 
00377   CMD_DOC(if,NULL,2,"%s <expr> <command> [<argument>]*",
00378           "If the given numeric expression evaluates to a non-zero value, execute\n"
00379           "the given command with the given arguments.");
00380 
00381   CMD_DOC(call,NULL,1,"%s <filename> [<param>=<value>]*",
00382           "Same as exec_file, but suppresses verbose parameter change and other messages.\n"
00383           "This is intended for calling a file as a procedure; only error reporting is\n"
00384           "usually desired since the file may contain a long loop or may call other files\n"
00385           "repeatedly.");
00386 
00387   CMD_DOC(exec_file,NULL,1,"%s <filename> [<param>=<value>]*",
00388           "Execute the given command file.  The parameter settings included on the\n"
00389           "command line will be saved but not evaluated until the next call to the\n"
00390           "read_args command, which will presumably be from inside the command file.\n"
00391           "See the help for read_args.");
00392 
00393   CMD_DOC(exec,NULL,1,"%s [<command-string>]*",
00394           "Execute the commands specified by the given strings, in order.  Before being\n"
00395           "executed, each string is interpreted for string substitutions as for the set\n"
00396           "command for string parameters, so it's possible to use constructions like\n"
00397           "`exec \"${command}\" \"set x=y\"'.");
00398 
00399   CMD_DOC(print,NULL,1,"%s <expression>*",
00400           "Displays the value of the given expression(s), which can either be the name of\n"
00401           "any parameter, preceded by a dollar sign (`$'), or an arbitrary numeric\n"
00402           "expression allowed as a <value> by the set command.  Examples: \n"
00403           "`print $filename', `print 2*radius-1'.");
00404 
00405   CMD_DOC(read_args,NULL,0,"%s",
00406           "If called within a file executed using the exec_file command, evaluates (using\n"
00407           "the set command) the parameter values defined by that call.  This provides\n"
00408           "a rudimentary form of argument passing to allow command files to be used like\n"
00409           "procedures.  Trivial example (e.g. in a file named `countup.command'):\n\n"
00410           
00411           "  define_param n Param_Integer # Declare argument(s)\n"
00412           "  set n=10                     # Default value\n"
00413           "  read_args                    # Read argument(s)\n\n"
00414 
00415           "  define_param i Param_Integer # Declare global variable to be used as a local\n"
00416           "  for i=0 i<n i=i+1 print i    # Actually do something\n\n"
00417 
00418           "If this file is called as `exec_file countup.command n=5', it will print\n"
00419           "out the numbers less than 5 (amidst various \"helpful\" debugging messages).\n"
00420           "Of course, since all parameters are currently globals, be careful\n"
00421           "not to assume the procedures act like safer ones offered by a more powerful\n"
00422           "language.");
00423 
00424   CMD_DOC(save_params,NULL,1,"%s <filename>",
00425           "Save current values of parameters and currently-defined hooks in a format\n"
00426           "suitable for exec_file.");
00427 
00428   CMD_DEFINE_CATCHUP(3,set,"%s <name>=<value> [<name>=<value>]*",
00429                      "Allows any parameter to be set to an arbitrary value, within the type\n"
00430                      "and range of that parameter.  It can accept multiple pairs of names and\n"
00431                      "values as long as the CMD_MAX_ARGUMENTS limit is not reached.  Values\n"
00432                      "may not contain spaces unless they are enclosed in quotes.\n\n"
00433 
00434                      "The <value> for a parameter of String type may contain references to\n"
00435                      "other parameters if preceded by a dollar sign (`$') and (optionally)\n"
00436                      "enclosed in braces.  Non-string parameter values are printed to a\n"
00437                      "simple string representation.  Example: `set filename=\n"
00438                      "${filename}_${angle}_A' might set it to `oldname_76.5_A'.\n\n"
00439                      
00440                      "The <value> for numeric types (Integer, Float, and Boolean) may be an\n"
00441                      "expression matching the following (recursive) grammar:\n\n"
00442                      
00443                      "   <term>  == <number>|<paramname>|(<value>)\n\n"
00444                      "   <value> == <term>[+|-|>|<|<?|>?|*|/|%<value>]\n\n"
00445                      
00446                      "Thus a literal numeric value will suffice, or else an infix arithmetic\n"
00447                      "expression like `RN' or `i/2' or `RN/2.5*(N-1)' or `RN%8' can be used,\n"
00448                      "where i, RN, and N are parameters whose current value will be substituted\n"
00449                      "before evaluation.  The >? and <? operators are borrowed from GNU C++;\n"
00450                      "they take the maximum and the minimum of their arguments, respectively.\n"
00451                      "The rest of the arguments are defined as in ANSI C, except as noted below.\n\n"
00452 
00453                      "The `*', `/', and `%%' operators are equal in precedence and are greater\n"
00454                      "in precedence than all the others; otherwise evaluation occurs from left\n"
00455                      "to right.  Thus simple arithmetic expressions will have the same\n"
00456                      "interpretation as in C, but parentheses should be used liberally on\n"
00457                      "anything complicated (particularly for comparisons) to ensure that they\n"
00458                      "are calculated correctly.\n\n"
00459                      
00460                      "All intermediate numeric results are of type double (or long double,\n"
00461                      "if available), which is then rounded (not truncated) to an integer.\n"
00462                      "Thus expression values involving e.g. integer division may differ from\n"
00463                      "their C equivalents, but this is usually an improvement.\n\n"
00464 
00465                      "Boolean variables have numeric types, but the symbolic values True,\n"
00466                      "False, Yes, No, and Uninitialized are accepted as standing for 1,0,1,0,\n"
00467                      "and -1, respectively.  Boolean expressions like ((x<0)||(x>N))&&C can be\n"
00468                      "computed by writing their arithmetic equivalent, e.g. `((x<0)+(x>N))*C',\n"
00469                      "as long as none of the Boolean variables are Uninitialized.");
00470 
00471   CMD_DOC(system,NULL,1,"%s <shell-command> [<shell-command-arg>]*",
00472                      "Execute the given system shell command (e.g. `ls -l file').  Arguments\n"
00473                      "which require quoting should be enclosed in single quotes surrounded by\n"
00474                      "double quotes, e.g. `ls -l \"'file with spaces'\"', or vice versa.\n"
00475                      "Parameter values may be used in arguments if preceded by a dollar sign, as\n"
00476                      "for string parameters in the `set' command.  Consequently, shell variables\n"
00477                      "cannot be used in arguments; if this were to present a problem, \n"
00478                      "the system command (or cmdi) could be rewritten to suppress variable\n"
00479                      "substitution if it detects embedded single quotes.");
00480 
00481   CMD_DOC(quit,NULL,0,"%s [<exit-message>]",
00482                      "Exit from the program with no error condition.");
00483 
00484   CMD_DEFINE_CATCHUP(3,hook,"%s <hook_list> <counter_specifier> <command> [<argument>]*",
00485                      "This command allows you to specify that the given command should be\n"
00486                      "executed at location <hook_list> when a counter reaches the value\n"
00487                      "specified by the <counter_specifier>, where the <counter_specifier>\n"
00488                      "is of the (recursive) form:\n\n"
00489 
00490                      "   <counter_start>[-<counter_end>[%<step>]][,<counter_specifier>]\n\n"
00491 
00492                      "For example, the <counter_specifier> can be\n"
00493                      "a single value ((e.g.  1000), a range of values (e.g.  1-1000), a\n"
00494                      "range with a stepsize (e.g.  1-1000%%10), or a list of any of these\n"
00495                      "(e.g. 1,10,100-200%%20,7,50-60).\n\n"
00496 
00497                      "For a given hook_list and counter value, all the hooks that apply\n"
00498                      "are executed in the order of their counter_start and (if those\n"
00499                      "match) the order in which they were defined.");
00500 
00501 
00502   /* Declare symbolic constants */
00503   CONST_F(PI,           M_PI,         False,"Symbol representing PI.");
00504   CONST_B(True,         True,         False,"Symbol representing Boolean `True'.");
00505   CONST_B(False,        False,        False,"Symbol representing Boolean `False'.");
00506   CONST_B(Yes,          True,         False,"Symbol representing Boolean `True'.");
00507   CONST_B(No,           False,        False,"Symbol representing Boolean `False'.");
00508   CONST_I(Uninitialized,Uninitialized,False,"Symbol representing an uninitialized or default value.");
00509   CONST_I(MaxInt,       INT_MAX-1,    False,"Symbol representing the maximum integer value.");
00510   CONST_I(MinInt,       INT_MIN+1,    False,"Symbol representing the minimum integer value.");
00511 
00512   
00513   CONST_I(Param_String,  PARAM_STRING,False,"Symbol representing the string parameter type.");
00514   CONST_I(Param_Boolean, PARAM_BOOL,  False,"Symbol representing the Boolean parameter type.");
00515   CONST_I(Param_Integer, PARAM_INT,   False,"Symbol representing the integer parameter type.");
00516   CONST_I(Param_Float,   PARAM_DOUBLE,False,"Symbol representing the floating-point parameter type.");
00517 
00518   PARAM_I(PARAM_STRING,commandpath,1,CMD_MAX_LINE_LENGTH,
00519           "Space-separated list of paths to search for command files.  The current\n"
00520           "directory is always searched first.");
00521   
00522   /* All source-code-level params and commands must have been defined by this point */
00523   params_sort();  /* For pretty and to make user-defined ones separate */
00524   cmddefs_sort(); /* For pretty */
00525   cmddefs_activate_prereqs(); /* No more cmddefs sorting allowed after this */
00526 
00527   /* Any parameters defined after this point must be defined using cmd_define_param */
00528 }
00529 
00530 
00531 
00536 void  ipc_init_hook( void )
00537 {
00538   PARAM_I(PARAM_INT, ipc_msg_level,          IPC_NONE,IPC_OVERWHELM,          ipc_msg_level_docstring);
00539   PARAM_I(PARAM_INT, ipc_msg_forceall_level, IPC_NONE,IPC_OVERWHELM, ipc_msg_forceall_level_docstring);
00540   PARAM_I(PARAM_INT, ipc_msg_synch_level,    IPC_NONE,IPC_OVERWHELM,    ipc_msg_synch_level_docstring);
00541   PARAM_L(PARAM_INT, ipc_exit_on_error_num,0,                         ipc_exit_on_error_num_docstring);
00542   PARAM_L(PARAM_INT, ipc_max_warnings,0,                                   ipc_max_warnings_docstring);
00543   PARAM_L(PARAM_INT, ipc_max_errors,0,                                       ipc_max_errors_docstring);
00544 
00545   CONST_I(IPC_None,      IPC_NONE,     False,
00546           "Symbol representing `no message'.  Ordinarily, no message should be declared\n"
00547           "with a level this high, and then this level may be used to turn off all\n"
00548           "messages.");
00549 
00550   CONST_I(IPC_Error,     IPC_ERROR,    False,
00551           "Symbol representing `error message'.  It should be used for a fairly serious\n"
00552           "problem, e.g. one which causes the current computation to be aborted.");
00553 
00554   CONST_I(IPC_Warning,   IPC_WARNING,  False,
00555           "Symbol representing `warning message'.  It should be used for a condition\n"
00556           "that is clearly bad but which won't stop computation from proceeding, e.g.\n"
00557           "if a meaningful default value can be used in place of an out-of-range\n"
00558           "parameter.");
00559 
00560   CONST_I(IPC_Caution,   IPC_CAUTION,  False,
00561           "Symbol representing `caution message'.  It should be used for a condition that\n"
00562           "is probably bad but which is common enough not to be worth tabulating in total\n"
00563           "warning counts.");
00564   
00565   CONST_I(IPC_Alert,     IPC_ALERT,    False,
00566           "Symbol representing `alert message'.  It is primarily a placeholder, marking\n"
00567           "all higher message levels as being in the `alert' category instead of merely\n"
00568           "informational.  It may also be used for any particularly important message\n"
00569           "which should almost never be suppressed.");
00570 
00571   CONST_I(IPC_Summary,   IPC_SUMMARY,  False,
00572           "Symbol representing `summary message'.  It should be used for messages that\n"
00573           "are particularly important but which do not report an anomalous condition.");
00574 
00575   CONST_I(IPC_Std,       IPC_STD,      False,
00576           "Symbol representing `standard message'.  It should be used for messages that\n"
00577           "a user will probably want to see for every run.");
00578 
00579   CONST_I(IPC_Verbose,   IPC_VERBOSE,  False,
00580           "Symbol representing `verbose message'.  It should be used for messages which\n"
00581           "a user will probably want to see only when requested.");
00582 
00583   CONST_I(IPC_Overwhelm, IPC_OVERWHELM,False,
00584           "Symbol representing `overwhelming amount of messages'.  It should be used\n"
00585           "for debugging messages and similar trivia that may generate a lot of output.");
00586 }
00587 
00588 
00589 
00590 /******************************************************************************/
00591 /* Parameter-handling routines                                                */
00592 /******************************************************************************/
00593 
00598 int params_define_param(const char *name, int type, int is_constant, void *pointer)
00599 {
00600   const ParamInterface* existing = params_lookup(name, strlen(name));
00601 
00602   if (existing) {
00603     if      (existing->data_type   != type)
00604       ipc_notify(IPC_ALL,IPC_ERROR,"Parameter %s already exists with a different type; can't redefine it",name);
00605     else if (existing->is_constant != is_constant)
00606       ipc_notify(IPC_ALL,IPC_ERROR,"%s is constant; can't redefine it",name);
00607     else {
00608       /* ipc_notify(IPC_ALL,IPC_STD,"Parameter %s already exists; not redefining it",name);  */
00609       return 2;
00610     }
00611   }
00612   else if (blackboard.num >= PARAM_MAX_ENTRIES)
00613     ipc_notify(IPC_ALL,IPC_WARNING,"Blackboard is full; can't add entry for %s",name);
00614   else if (strlen(name)!=cmdparam_identifier_length(name))
00615     ipc_notify(IPC_ALL,IPC_WARNING,"Invalid characters in parameter name %s; can't define it",name);
00616   else {
00617     ParamInterface* param=&(blackboard.list[blackboard.num]);
00618     
00619     strncpy(param->name, name,PARAM_MAX_NAME_LENGTH);
00620     param->data_type     = type;
00621     param->is_constant   = is_constant;
00622     param->enforce_upper = False;
00623     param->enforce_lower = False;
00624     param->setfn         = NULL;
00625     param->been_set      = False;
00626     param->default_expr  = NULL;
00627 
00628     switch(type){
00629     case PARAM_STRING: param->data_ptr.c  = (char*)pointer;    break;
00630     case PARAM_BOOL:
00631     case PARAM_INT:    param->data_ptr.i  = (int*)pointer;     break;
00632     case PARAM_DOUBLE: param->data_ptr.f  = (double*)pointer;  break;
00633     default:
00634       ipc_notify(IPC_ONE,IPC_ERROR,"Unknown datatype %d; can't define param",type);
00635       return 0;
00636     }
00637     blackboard.num++;
00638 
00639     return 1;
00640   }
00641 
00642   return 0;
00643 }
00644 
00645 
00646 
00652 void params_add_lower_bound_int(const char *name, int lower_bound)
00653 {
00654   int idx = 0;
00655   int changed = False;
00656 
00657   /* Counts backward, which is very quick if this parameter was just added */
00658   for (idx=blackboard.num-1; !changed && idx >= 0; idx--)
00659     if (strncmp(name,blackboard.list[idx].name,PARAM_MAX_NAME_LENGTH) == 0)
00660       switch(blackboard.list[idx].data_type) {
00661 
00662       case PARAM_STRING:  /* String length bounds */
00663       case PARAM_BOOL:    /* Boolean bounds as integers */
00664       case PARAM_INT:     /* Integer value bounds */
00665         blackboard.list[idx].lower_bound.i = lower_bound;
00666         blackboard.list[idx].enforce_lower = True;
00667         changed = True;
00668         break;
00669 
00670       case PARAM_DOUBLE: 
00671         blackboard.list[idx].lower_bound.f = lower_bound;
00672         blackboard.list[idx].enforce_lower = True;
00673         changed = True;
00674         break;
00675       }
00676 
00677   if (!changed) 
00678     ipc_notify(IPC_ONE,IPC_ERROR,"Failed to add lower bound to parameter %s",name);
00679 }
00680 
00681 
00682 
00688 void params_add_upper_bound_int(const char *name, int upper_bound)
00689 {
00690   int idx = 0;
00691   int changed = False;
00692 
00693   /* Counts backward, which is very quick if this parameter was just added */
00694   for (idx=blackboard.num-1; !changed && idx >= 0; idx--)
00695     if (strncmp(name,blackboard.list[idx].name,PARAM_MAX_NAME_LENGTH) == 0)
00696       switch(blackboard.list[idx].data_type) {
00697 
00698       case PARAM_STRING:  /* String length bounds */
00699       case PARAM_BOOL:    /* Boolean bounds as integers */
00700       case PARAM_INT:     /* Integer value bounds */
00701         blackboard.list[idx].upper_bound.i = upper_bound;
00702         blackboard.list[idx].enforce_upper = True;
00703         changed = True;
00704         break;
00705 
00706       case PARAM_DOUBLE: 
00707         blackboard.list[idx].upper_bound.f = upper_bound;
00708         blackboard.list[idx].enforce_upper = True;
00709         changed = True;
00710         break;
00711       }
00712 
00713   if (!changed) 
00714     ipc_notify(IPC_ONE,IPC_ERROR,"Failed to add upper bound to parameter %s",name);
00715 }
00716 
00717 
00718 
00726 void params_define_default_expr(const char *name, const char *expr)
00727 {
00728   int idx = 0;
00729   int changed = False;
00730 
00731   /* Counts backward, which is very quick if this parameter was just added */
00732   for (idx=blackboard.num-1; !changed && idx >= 0; idx--)
00733     if (strncmp(name,blackboard.list[idx].name,PARAM_MAX_NAME_LENGTH) == 0) {
00734       blackboard.list[idx].default_expr = expr;
00735       changed = True;
00736     }
00737   
00738   if (!changed)
00739     ipc_notify(IPC_ONE,IPC_ERROR,"Failed to add default expression to parameter %s",name);
00740 }
00741 
00742 
00743 
00749 void params_update_default_value(int idx)
00750 {
00751   ParamInterface* param = &(blackboard.list[idx]);
00752   if (!param->been_set && param->default_expr) {
00753     cmdparam_set( param->name, param->default_expr );
00754     /* This doesn't count as an explicit set, so continue declaring param unset */
00755     param->been_set = False; 
00756   }
00757 }
00758 
00759 
00760 
00762 void params_update_default_value(const char* name)
00763 {
00764   int idx;
00765   int changed=False;
00766   
00767   for (idx=blackboard.num-1; !changed && idx >= 0; idx--)
00768     if (strncmp(name,blackboard.list[idx].name,PARAM_MAX_NAME_LENGTH) == 0) {
00769       params_update_default_value(idx);
00770       changed = True;
00771     }
00772   
00773   if (!changed)
00774     ipc_notify(IPC_ONE,IPC_ERROR,"Can't find parameter %s to update its default value",name);
00775 }
00776 
00777 
00778 
00780 void params_update_all_default_values( void )
00781 {
00782   for (int idx=0; idx<blackboard.num; idx++)
00783     params_update_default_value(idx);
00784 }
00785 
00786 
00787 
00792 int param_check( const ParamInterface* param, const void * valueptr)
00793 {
00794   switch(param->data_type) {
00795             
00796   case PARAM_STRING:
00797     {
00798       int value = strlen((const char *)valueptr);
00799       
00800       if ( param->enforce_lower && value < param->lower_bound.i ) {
00801         ipc_notify(IPC_ONE,IPC_ERROR,"%s too short (%d < %d)",param->name,value,param->lower_bound.i);
00802         return 1;
00803       }
00804       if ( param->enforce_upper && value > param->upper_bound.i ) {
00805         ipc_notify(IPC_ONE,IPC_ERROR,"%s too long (%d > %d)", param->name,value,param->upper_bound.i);
00806         return 1;
00807       }
00808       return 0;
00809     }
00810     
00811     
00812   case PARAM_BOOL:
00813     {
00814       int value = *((const int *)valueptr);
00815       
00816       if ( (param->enforce_lower && value < param->lower_bound.i)  ||
00817            (param->enforce_upper && value > param->upper_bound.i) ) {
00818         ipc_notify(IPC_ONE,IPC_ERROR,"%s is invalid (%d)",param->name,value);
00819         return 1;
00820       }
00821       return 0;
00822     }
00823     
00824     
00825   case PARAM_INT:
00826     {
00827       int value = *((const int *)valueptr);
00828       
00829       if ( param->enforce_lower && value < param->lower_bound.i ) {
00830         ipc_notify(IPC_ONE,IPC_ERROR,"%s too small (%s < %d)",param->name,
00831                    param_pp_int(value),(int)(param->lower_bound.i));
00832         return 1;
00833       }
00834       if ( param->enforce_upper && value > param->upper_bound.i ) {
00835         ipc_notify(IPC_ONE,IPC_ERROR,"%s too large (%s > %d)",param->name,
00836                    param_pp_int(value),(int)(param->upper_bound.i));
00837         return 1;
00838       }
00839       return 0;
00840     }
00841     
00842     
00843   case PARAM_DOUBLE:
00844     {
00845       double value = *((const double *)valueptr);
00846       
00847       if ( param->enforce_lower && value < param->lower_bound.f ) {
00848         ipc_notify(IPC_ONE,IPC_ERROR,"%s too small (%s < %g)",param->name,
00849                    param_pp_float(value),(double)(param->lower_bound.f));
00850         return 1;
00851       }
00852       if ( param->enforce_upper && value > param->upper_bound.f ) {
00853         ipc_notify(IPC_ONE,IPC_ERROR,"%s too large (%s > %g)",param->name,
00854                    param_pp_float(value),(double)(param->upper_bound.f));
00855         return 1;
00856       }
00857       return 0;
00858     }
00859     
00860   default:
00861     ipc_notify(IPC_ONE,IPC_WARNING,"Datatype of %s unknown; can't check bounds",param->name);
00862     return 0;
00863   }
00864 }
00865 
00866 
00867 
00874 void params_define_doc( const char* name, const char *doc )
00875 {
00876   int idx = 0;
00877   int changed = False;
00878 
00879   /* Counts backward, which is very quick if this param was just added */
00880   for (idx=blackboard.num-1; !changed && idx >= 0; idx--)
00881     if (strncmp(name,blackboard.list[idx].name,PARAM_MAX_NAME_LENGTH) == 0) {
00882       blackboard.list[idx].doc = doc;
00883       changed = True;
00884     }
00885   
00886   if (!changed) 
00887     ipc_notify(IPC_ONE,IPC_ERROR,"Failed to add documentation to param %s",name);
00888 }
00889 
00890 
00891 
00893 void params_define_setfn( const char* name, SetFnWrapper* setfn )
00894 {
00895   int idx = 0;
00896   int changed = False;
00897 
00898   /* Counts backward, which is very quick if this param was just added */
00899   for (idx=blackboard.num-1; !changed && idx >= 0; idx--)
00900     if (strncmp(name,blackboard.list[idx].name,PARAM_MAX_NAME_LENGTH) == 0) {
00901       blackboard.list[idx].setfn = setfn;
00902       changed = True;
00903     }
00904   
00905   if (!changed) 
00906     ipc_notify(IPC_ONE,IPC_ERROR,"Failed to add setting function to param %s",name);
00907 }
00908 
00909 
00910 
00914 int params_check_all( void )
00915 {
00916   int idx, numerrors=0;
00917   
00918   for (idx=0; idx < blackboard.num; idx++)
00919     numerrors += param_check( &(blackboard.list[idx]),
00920                               (void *)(blackboard.list[idx].data_ptr.i) );
00921   return numerrors;
00922 }
00923 
00924 
00925 
00926 
00928 int params_num_total(void)
00929 {  return blackboard.num;  }
00930 
00931 
00932 
00937 int params_print_to_str( int number, char *str, size_t nchars)
00938 {
00939   char *str_ptr=str;
00940   size_t charsleft=nchars-1;
00941   ParamInterface* param;
00942 
00943   if (number >= blackboard.num) {
00944     ipc_notify(IPC_ONE,IPC_ERROR,"Unknown parameter number: %d",number);
00945     return 0;
00946   }
00947   param = &(blackboard.list[number]);
00948   
00949   snprintf(str_ptr,charsleft,"set %s=",param->name);
00950   charsleft -= strlen(str_ptr);
00951   str_ptr   += strlen(str_ptr);
00952 
00953   if (param->data_type == PARAM_STRING)
00954     snprintf(str_ptr,charsleft,"\"%s\"", (param->data_ptr.c));
00955   else
00956     snprintf(str_ptr,charsleft,"%s", param_pp_scalar(param, param->data_ptr.i));
00957 
00958   return 1;
00959 }
00960 
00961 
00962 
00964 void params_log(void)
00965 {
00966   int i;
00967   char buf[CMD_MAX_LINE_LENGTH];
00968   
00969   for(i=0; i< blackboard.num; i++)
00970     if (params_print_to_str( i, buf, CMD_MAX_LINE_LENGTH))
00971       ipc_log(IPC_ALL,"%s\n",buf);
00972   
00973   ipc_log(IPC_ALL,"\n");
00974 
00975   hooklists_log();
00976 }
00977 
00978 
00980 void params_sort(void)
00981 {
00982   qsort(blackboard.list,
00983         blackboard.num,
00984         sizeof(ParamInterface),
00985         param_compare);
00986 }
00987 
00988 
00989 
00991 int param_compare(const void* param1, const void* param2)
00992 {
00993   return( strncasecmp(((const ParamInterface*)param1)->name,
00994                       ((const ParamInterface*)param2)->name,
00995                       CMDDEF_MAX_NAME_LENGTH) );
00996 }
00997 
00998 
00999 
01001 void params_print_all(FILE *fp)
01002 {
01003   int i;
01004   for(i=0; i< blackboard.num; i++){
01005     param_print_usage(fp,&(blackboard.list[i]));
01006     fprintf(fp,"\n");
01007   }
01008 }
01009 
01010 
01011 
01013 void params_print_parameters(FILE *fp)
01014 {
01015   int i;
01016   for(i=0; i< blackboard.num; i++)
01017     if (!blackboard.list[i].is_constant) {
01018       param_print_usage(fp,&(blackboard.list[i]));
01019       fprintf(fp,"\n");
01020     }
01021 }
01022 
01023 
01024 
01026 void params_print_constants(FILE *fp)
01027 {
01028   int i;
01029   for(i=0; i< blackboard.num; i++)
01030     if (blackboard.list[i].is_constant) {
01031       param_print_usage(fp,&(blackboard.list[i]));
01032       fprintf(fp,"\n");
01033     }
01034 }
01035 
01036 
01037 
01038 const char *param_pp_boolean(int value)
01039 {
01040   if (value == Uninitialized)
01041     return "Uninitialized";
01042   else if (value)
01043     return "True";
01044   else  return "False";
01045 }
01046 
01047 
01048 
01049 const char *param_pp_int(int value)
01050 {
01051   static char buf[20];
01052   
01053   if (value == Uninitialized)
01054     return "Uninitialized";
01055 
01056   snprintf(buf,20,"%d",value);
01057   return buf;
01058 }
01059 
01060 
01061 
01062 const char *param_pp_float(float value)
01063 {
01064   static char buf[20];
01065   
01066   if (value == Uninitialized)
01067     return "Uninitialized";
01068 
01069   snprintf(buf,20,"%g",value);
01070   return buf;
01071 }
01072 
01073 
01074 
01075 const char *param_pp_scalar(const ParamInterface* def, void *value)
01076 {
01077   switch(def->data_type){
01078   case PARAM_BOOL :  return param_pp_boolean( *(int *)value);
01079   case PARAM_INT  :  return param_pp_int(     *(int *)value);
01080   case PARAM_DOUBLE: return param_pp_float(*(double *)value);
01081   default:
01082     ipc_notify(IPC_ONE,IPC_WARNING,"Datatype of %s unknown; can't print value",def->name);
01083   }
01084   return "";
01085 }
01086 
01087 
01088 
01089 int param_print_value(FILE *fp, ParamInterface* param)
01090 {
01091   int pos=0;
01092   
01093   if (param->data_type == PARAM_STRING)
01094     pos += fprintf(fp,"\"%s\"", (param->data_ptr.c));
01095   else
01096     pos += fprintf(fp,"%s", param_pp_scalar(param, param->data_ptr.i));
01097 
01098   return pos;
01099 }
01100 
01101 
01102 
01103 int param_print_type(FILE *fp, ParamInterface* param)
01104 {
01105   int pos=0;
01106   
01107   switch(param->data_type){
01108   case PARAM_STRING: pos += fprintf(fp,"String");  break;
01109   case PARAM_BOOL  : pos += fprintf(fp,"Boolean"); break;
01110   case PARAM_INT   : pos += fprintf(fp,"Integer"); break;
01111   case PARAM_DOUBLE: pos += fprintf(fp,"Float");   break;
01112   default:
01113     ipc_notify(IPC_ONE,IPC_WARNING,"Datatype of %s unknown; can't print type",param->name);
01114   }
01115 
01116   return pos;
01117 }
01118 
01119   
01120 int param_print_range(FILE *fp, ParamInterface* param)
01121 {
01122   int pos=0;
01123   
01124   pos += fprintf(fp,"[");  
01125   if (param->enforce_lower) {
01126     if (param->data_type == PARAM_STRING)
01127       pos += fprintf(fp,"%d",param->lower_bound.i);
01128     else
01129       pos += fprintf(fp,"%s", param_pp_scalar(param, &(param->lower_bound))); 
01130   }
01131   else
01132     pos += fprintf(fp,"*");      
01133 
01134   pos += fprintf(fp,",");
01135   
01136   if (param->enforce_upper) {
01137     if (param->data_type == PARAM_STRING)
01138       pos += fprintf(fp,"%d",param->upper_bound.i);
01139     else
01140       pos += fprintf(fp,"%s", param_pp_scalar(param, &(param->upper_bound))); 
01141   }
01142   else
01143     pos += fprintf(fp,"*");      
01144 
01145   pos += fprintf(fp,"]");  
01146 
01147   return pos;
01148 }
01149 
01150 
01151 
01152 int param_print_usage(FILE *fp, ParamInterface* param)
01153 {
01154   int pos=0;
01155   
01156   pos += fprintf(fp,"%s=",param->name);
01157   pos += param_print_value(fp,param);
01158   
01159   pos += fprintf(fp,"%*s(",MAX(0,39-pos)," ");
01160   pos += param_print_type(fp,param);
01161   
01162   if (param->is_constant)
01163     pos += fprintf(fp," constant");
01164   else
01165     if (param->enforce_lower || param->enforce_upper) {
01166       pos += fprintf(fp," with %srange ",
01167                      (param->data_type==PARAM_STRING ? "length " : ""));
01168       pos += param_print_range(fp,param);
01169     }
01170 
01171   pos += fprintf(fp,")");
01172 
01173   return pos;
01174 }
01175 
01176 
01177 
01178 void params_print_doc_for_index(FILE *fp, int idx)
01179 {
01180   ParamInterface* param = &(blackboard.list[idx]);
01181   
01182   param_print_usage(fp,param);
01183   fprintf(fp,"\n");
01184   
01185   if (param->doc) {
01186     fprintf(fp,"\n");
01187     fprintf(fp,param->doc,param->name);
01188     fprintf(fp,"\n");
01189   }
01190 }
01191 
01192 
01193 
01194 int params_is_constant(int idx)
01195 {
01196   return blackboard.list[idx].is_constant;
01197 }
01198 
01199 
01200 
01202 void params_print_doc(FILE *fp, const char* name)
01203 {
01204   int idx = 0;
01205   int found = False;
01206 
01207   for (idx=blackboard.num-1; !found && idx >= 0; idx--)
01208     if (strncmp(name,blackboard.list[idx].name,PARAM_MAX_NAME_LENGTH) == 0) {
01209       params_print_doc_for_index(fp,idx);
01210       found = True;
01211     }
01212   
01213   if (!found) 
01214     ipc_notify(IPC_ONE,IPC_ERROR,"Don't know anything about parameter %s",name);
01215 }
01216 
01217 
01218 
01219 void params_print_constants_doc(FILE *file)
01220 {
01221   int i;
01222   fprintf(file,cmdparam_help_separator);
01223   for (i=0; i< params_num_total(); i++)
01224     if (params_is_constant(i)) {
01225       params_print_doc_for_index(file,i);
01226       fprintf(file,cmdparam_help_separator);
01227       fprintf(file,"\n");
01228     }
01229 }
01230 
01231 
01232 
01233 void params_print_parameters_doc(FILE *file)
01234 {
01235   int i;
01236   fprintf(file,cmdparam_help_separator);
01237   for (i=0; i< params_num_total(); i++)
01238     if (!params_is_constant(i)) {
01239       params_print_doc_for_index(file,i);
01240       fprintf(file,cmdparam_help_separator);
01241       fprintf(file,"\n");
01242     }
01243 }
01244 
01245 
01246 
01253 char* params_completion_generator (char *text, int state)
01254 {
01255   static int idx, len;
01256   char *name;
01257 
01258   /*
01259     If this is a new word to complete, initialize the index counters
01260     to the beginning and save the length of text for efficiency
01261   */
01262   if (!state) {
01263     idx = 0;
01264     len = strlen(text);
01265   }
01266   
01267   /* Return the next parameter which partially matches, if any. */
01268   while (idx < blackboard.num) {
01269     name = blackboard.list[idx++].name;
01270     if (strncmp (name, text, len) == 0)
01271       return (cmdparam_dupstr(name));
01272   }
01273   
01274   return ((char *)NULL); /* No more matches */
01275 }
01276 
01277 
01278 
01284 double* params_lookup_double(const char *name)
01285 {
01286   ParamInterface* param = params_lookup(name,strlen(name));
01287 
01288   if (param && param->data_type==PARAM_DOUBLE)
01289     return param->data_ptr.f;
01290   else
01291     return NULL;
01292 }
01293 
01294 
01295 
01301 ParamInterface* params_lookup(const char *name, size_t len)
01302 {
01303   int idx=0;
01304   
01305   /* Look up parameter value */
01306   while (idx < blackboard.num && 
01307          (strncmp(name,blackboard.list[idx].name, len) != 0 ||
01308           strlen(blackboard.list[idx].name) != len))
01309     idx++;
01310 
01311   return (idx < blackboard.num ? &(blackboard.list[idx]) : NULL);
01312 }
01313 
01314 
01315 
01329 void* params_getval( int* type, const char **remaining )
01330 {
01331   const char* str      = *remaining;
01332   int identlen         = cmdparam_identifier_length(str);
01333   size_t checklen      = MIN(identlen,PARAM_MAX_NAME_LENGTH);
01334 
01335   static double numval;
01336   ParamInterface* paramptr;
01337   
01338   
01339   if (!identlen) { /* Must be a numeric literal, then */
01340     char *rem;
01341     numval = strtod(str,&rem);
01342     *remaining=rem;
01343     
01344     if (str == *remaining) {
01345       ipc_notify(IPC_ONE,IPC_ERROR, "Can't parse expression: `%s'",str);
01346       return NULL;
01347     }
01348     *type=PARAM_DOUBLE;
01349     return &numval;
01350   }
01351 
01352   /* Look up parameter value */
01353   paramptr = params_lookup(str, checklen);
01354   *remaining=str+identlen;
01355   if (paramptr) {
01356     *type = paramptr->data_type;
01357     return paramptr->data_ptr.i;
01358   }
01359   else {
01360     /* Nothing found */
01361     ipc_notify(IPC_ONE,IPC_ERROR,"No parameter or constant %.*s is defined",checklen,str);
01362     *type=PARAM_INT;
01363     return NULL;
01364   }
01365   
01366 }
01367 
01368 
01369 
01380 const char *params_getstringval(const char **remaining )
01381 {
01382   const ParamInterface* paramptr;
01383   const char* str = *remaining;
01384   static char buf[CMD_MAX_LINE_LENGTH];
01385 
01386   char* bufptr=buf;
01387   int bracefound=False;
01388   int identlen;
01389 
01390   while (*remaining && **remaining && (bufptr-buf < CMD_MAX_LINE_LENGTH)) {
01391     while (**remaining && **remaining != '$' && (bufptr-buf < CMD_MAX_LINE_LENGTH))
01392       *(bufptr++) = *(*remaining)++;
01393 
01394     if (**remaining == '$') {
01395       (*remaining)++;
01396       if (**remaining == '$')
01397         *(bufptr++) = *(*remaining)++;
01398       else {
01399         if (**remaining == '{') {
01400           bracefound=True;
01401           (*remaining)++;
01402         }
01403         identlen = cmdparam_identifier_length(*remaining);
01404       
01405         /* Look up parameter value */
01406         paramptr = params_lookup(*remaining, identlen);
01407         if (paramptr) {
01408           if (paramptr->data_type == PARAM_STRING)
01409             bufptr += sprintf(bufptr,"%s", paramptr->data_ptr.c);
01410           else
01411             bufptr += sprintf(bufptr,"%s", param_pp_scalar(paramptr, paramptr->data_ptr.i));
01412         }
01413         else
01414           ipc_notify(IPC_ONE,IPC_ERROR,"No parameter or constant %.*s is defined",identlen,*remaining);
01415         *remaining +=identlen;
01416 
01417         if (bracefound) {
01418           if (**remaining == '}')
01419             (*remaining)++;
01420           else
01421             ipc_notify(IPC_ONE,IPC_WARNING,"Missing right brace in %s",str);
01422         }
01423       }
01424     }
01425   }
01426   *bufptr='\0';
01427     
01428   return buf;
01429 }
01430 
01431 
01432 
01438 exprtype params_getnumericval(const char **remaining )
01439 {
01440   int type;
01441   const void* paramptr;
01442   const char* str = *remaining;
01443   
01444   if (**remaining == '(') {
01445     exprtype subexpr;
01446     (*remaining)++;    
01447     subexpr=(params_parse_expr(remaining));
01448     
01449     if (**remaining != ')')
01450       ipc_notify(IPC_ONE,IPC_ERROR,"Missing right parenthesis");
01451     else
01452       (*remaining)++;
01453     return subexpr;
01454   }
01455 
01456   if (**remaining == '-') {
01457     (*remaining)++;        
01458     return -1.0*params_next_term(remaining);
01459   }
01460   
01461   paramptr = params_getval(&type,remaining);
01462 
01463   if (paramptr != NULL)
01464     switch (type) {
01465     case PARAM_BOOL  : return (exprtype)    *(const int*)paramptr;
01466     case PARAM_INT   : return (exprtype)    *(const int*)paramptr;
01467     case PARAM_DOUBLE: return (exprtype) *(const double*)paramptr;
01468     default:
01469       ipc_notify(IPC_ONE,IPC_ERROR,"Invalid param type in expression `%s'",str);
01470     }
01471 
01472   return Uninitialized;
01473 }
01474 
01475 
01477 exprtype params_apply_op(exprtype lval, string op, exprtype rval)
01478 {
01479   if      (op == "+")  return lval +  rval;
01480   else if (op == "-")  return lval -  rval;
01481   else if (op == "*")  return lval *  rval;
01482   else if (op == "<")  return lval <  rval;
01483   else if (op == ">")  return lval >  rval;
01484 
01485   /*
01486     Currently can't implement these because of the hack in
01487     command_parse_token used to implement set x=y; if we remove the
01488     special handling then these can be implemented.  No need to rush,
01489     though.
01490   */
01491   /*
01492   else if (op == "==") return lval == rval;
01493   else if (op == "!=") return lval != rval;
01494   else if (op == "<=") return lval <= rval;
01495   else if (op == ">=") return lval >= rval;
01496   */
01497   
01498   else if (op == "<?") return MIN(lval,rval);
01499   else if (op == ">?") return MAX(lval,rval);
01500   
01501   else if (op == "%" || op == "/") {
01502     if (rval==0) {
01503       ipc_notify(IPC_ONE,IPC_ERROR,"Division by zero");
01504       return Uninitialized;
01505     }
01506     if (op == "/") return lval / rval;
01507     else           return fmod(lval,rval);
01508   }
01509   
01510   ipc_notify(IPC_ONE,IPC_ERROR,"Unknown operator %s",op.c_str());
01511   return Uninitialized;
01512 }
01513 
01514 
01515 int params_op_has_precedence(const string& previous_op, const string& op)
01516 {
01517   (void) previous_op; /* Currently ignored */
01518 
01519   return (op=="*" || op=="/" | op=="%");
01520 }
01521 
01522 
01523 string params_next_op(const char **remaining)
01524 {
01525   const char* start=*remaining;
01526 
01527   while (ispunct(**remaining) && !(**remaining=='('))
01528     (*remaining)++;
01529 
01530   const char* end=*remaining;
01531 
01532   return string(start,end-start);
01533 }
01534 
01535 
01536 
01537 exprtype params_next_term(const char **remaining, string previous_op)
01538 {
01539   exprtype lval = params_getnumericval(remaining);
01540   string op = params_next_op(remaining);
01541   
01542   /* Reduce strings of operators with precedence */
01543   while (params_op_has_precedence(previous_op, op)) {
01544     if (! **remaining) {
01545       ipc_notify(IPC_ONE,IPC_ERROR,"Missing right value in `%s'",*remaining);
01546       return lval;
01547     }
01548     lval=params_apply_op(lval,op,params_getnumericval(remaining));
01549     previous_op = op;
01550     op = params_next_op(remaining);
01551   }
01552   *remaining -= op.length(); /* Push back unused operator */
01553 
01554   return lval;
01555 }
01556 
01557 
01558 
01559 exprtype params_parse_expr(const char **remaining)
01560 {
01561   const char* str = *remaining;
01562   exprtype lval = params_next_term(remaining);
01563 
01564   while (**remaining && **remaining != ')') {
01565     string op= params_next_op(remaining);
01566     
01567     if (! **remaining) {
01568       ipc_notify(IPC_ONE,IPC_ERROR,"Missing right value in `%s'",str);
01569       return lval;
01570     }
01571     
01572     lval = params_apply_op(lval,op,params_next_term(remaining,op));
01573   }
01574 
01575   return lval;
01576 }
01577 
01578 
01579 
01581 exprtype params_read_float(const char* str)
01582 {
01583   const char* remaining=str;
01584   exprtype val = params_parse_expr(&remaining);
01585 
01586   if (str == remaining) {
01587     ipc_notify(IPC_ONE,IPC_ERROR, "No number found in string: `%s'",str);
01588     return Uninitialized;
01589   }
01590   
01591   if (*remaining)
01592     ipc_notify(IPC_ONE,IPC_WARNING,
01593                "Trailing characters in expression ignored: `%s'",remaining);
01594   return val;
01595 }
01596 
01597 
01598 
01599 /******************************************************************************/
01600 /* Command-argument processing (called within the commands)                   */
01601 /******************************************************************************/
01602 
01603 
01605 double cmdf(const char* str)
01606 {  return (double)params_read_float(str);  }
01607 
01608 
01609 
01611 long cmdi(const char* str)
01612 {
01613   exprtype result = params_read_float(str);
01614   if (result >= INT_MAX)
01615     ipc_notify(IPC_ONE,IPC_WARNING,"Value %g greater than integer type can contain (%d); may cause overflow",
01616                (double)result,(int)INT_MAX);
01617 
01618   else if (result <= INT_MIN)
01619     ipc_notify(IPC_ONE,IPC_WARNING,"Value %g smaller than integer type can contain (%d); may cause underflow",
01620                (double)result,(int)(INT_MIN));
01621 
01622   return (long)ROUND(result);
01623 }
01624 
01625 
01627 const char* cmds(const char* str)
01628 {  return params_getstringval(&str);  }
01629 
01630 
01631 
01632 
01633 /******************************************************************************/
01634 /* Command-file processing                                                    */
01635 /******************************************************************************/
01636 
01637 
01639 void cmddefs_define_command( const char* name, CmdWrapper* function, int minargs, int exec_for_catchups )
01640 {
01641   if (command_definitions.num >= CMDDEF_MAX_NUM)
01642     ipc_notify(IPC_ONE,IPC_WARNING,"Command blackboard is full; can't add entry for %s",name);
01643   else if (strlen(name)!=cmdparam_identifier_length(name))
01644     ipc_notify(IPC_ALL,IPC_WARNING,"Invalid characters in command name; can't define it");
01645   else if (minargs > CMD_MAX_ARGUMENTS) 
01646     ipc_notify(IPC_ONE,IPC_ERROR,"Command %s expects too many arguments (%d > %d); can't define it",
01647                name, minargs,(int)CMD_MAX_ARGUMENTS);
01648   else {
01649     strncpy(command_definitions.list[command_definitions.num].name, name,CMDDEF_MAX_NAME_LENGTH);
01650     command_definitions.list[command_definitions.num].function    = function;
01651     command_definitions.list[command_definitions.num].minargs     = minargs;
01652     command_definitions.list[command_definitions.num].catchup     = exec_for_catchups;
01653     command_definitions.list[command_definitions.num].usage       = NULL;
01654     command_definitions.list[command_definitions.num].doc         = NULL;
01655     command_definitions.list[command_definitions.num].num_prereqs = 0;
01656     command_definitions.list[command_definitions.num].callstatus  = CMD_NEVER_CALLED;
01657     command_definitions.num++;
01658   }
01659 }
01660 
01661 
01662 
01664 void cmddefs_define_command_doc( const char* name, const char *usage, const char *doc )
01665 {
01666   int idx = 0;
01667   int changed = False;
01668 
01669   /* Counts backward, which is very quick if this command was just added */
01670   for (idx=command_definitions.num-1; !changed && idx >= 0; idx--)
01671     if (strncmp(name,command_definitions.list[idx].name,CMDDEF_MAX_NAME_LENGTH) == 0) {
01672       command_definitions.list[idx].usage = usage;
01673       command_definitions.list[idx].doc = doc;
01674       changed = True;
01675     }
01676   
01677   if (!changed) 
01678     ipc_notify(IPC_ONE,IPC_ERROR,"Failed to add documentation to command %s",name);
01679 }
01680 
01681 
01682 
01688 void cmddefs_define_command_prereq( const char* name, const char *prereq )
01689 {
01690   if (!prereq)
01691     return;
01692   
01693   if (prereq_storage.num >= CMDDEF_MAX_NUM*CMDDEF_MAX_PREREQS) {
01694     ipc_notify(IPC_ONE,IPC_ERROR,"Prerequisite storage area full; can't add prerequisite %s for command %s",
01695               prereq,name);
01696     return;
01697   }
01698 
01699   prereq_storage.names[prereq_storage.num]=name;
01700   prereq_storage.prereqs[prereq_storage.num]=prereq;
01701   prereq_storage.num++;
01702 }
01703 
01704 
01705 
01713 void cmddefs_declare_prereq_status( const char * name, cmdstat status )
01714 {
01715   CommandDefinition* def=cmddefs_lookup(name);
01716   if (def) def->callstatus = status;
01717 }
01718 
01719 
01720 
01721 void cmddefs_activate_prereqs( void )
01722 {
01723   int i;
01724 
01725   for (i=0; i< prereq_storage.num; i++)
01726     cmddefs_activate_command_prereq(prereq_storage.names[i],prereq_storage.prereqs[i]);
01727 }
01728 
01729 
01730 
01735 void cmddefs_activate_command_prereq( const char* name, const char *prereq )
01736 {
01737   int idx,pidx;
01738   int changed = False;
01739   int found = False;
01740 
01741   if (!prereq)
01742     return;
01743 
01744   /* Counts backward, which is very quick if this command was just added */
01745   for (idx=command_definitions.num-1; !changed && idx >= 0; idx--)
01746     if (strncmp(name,command_definitions.list[idx].name,CMDDEF_MAX_NAME_LENGTH) == 0) {
01747       CommandDefinition* cmddef = &(command_definitions.list[idx]);
01748         
01749       for (pidx=command_definitions.num-1; !found && pidx >= 0; pidx--)
01750         if (strncmp(prereq,command_definitions.list[pidx].name,CMDDEF_MAX_NAME_LENGTH) == 0) {
01751           found=True;
01752           if (cmddef->num_prereqs >= CMDDEF_MAX_PREREQS)
01753             ipc_notify(IPC_ONE,IPC_ERROR,"Maximum limit on command prerequisites (%d) reached; can't add another",
01754                       CMDDEF_MAX_PREREQS);
01755           else {
01756             cmddef->prereqs[cmddef->num_prereqs++]=pidx;
01757             /* ipc_notify(IPC_ONE,IPC_STD,"Added prerequisite %s (%d) for command %s (%d)",prereq,pidx,name,idx); */
01758           }
01759         }
01760       changed = True;
01761     }
01762   
01763   if (!changed) 
01764     ipc_notify(IPC_ONE,IPC_ERROR,"Failed to add prerequisite to command %s",name);
01765 }
01766 
01767 
01768 
01773 CommandDefinition* cmddefs_lookup(const char *name)
01774 {
01775   int  command_def=0;
01776   
01777   /* Iterate through commands array looking for this command */
01778   while ((command_def < command_definitions.num) &&
01779          (strncmp(name,command_definitions.list[command_def].name,CMDDEF_MAX_NAME_LENGTH) != 0))
01780     command_def++;
01781 
01782   /* If found, return a pointer to its definition */
01783   if (command_def < command_definitions.num)
01784     return &(command_definitions.list[command_def]);
01785   else {
01786     if (cmdparam_warn_unknown)
01787       ipc_notify(IPC_ONE,IPC_ERROR,"Unknown command: %s",name);
01788     return NULL;
01789   }
01790 }
01791 
01792 
01793 
01794 cmdstat cmddefs_exec_by_name(const char* name, CMD_ARGS)
01795 {
01796   CommandDefinition* def = cmddefs_lookup(name);
01797   
01798   if (!def)
01799     return CMD_PARAMETER_ERROR;
01800 
01801   else {
01802     Command cmd;
01803     int i;
01804     
01805     cmd.def=def;
01806     cmd.argc=argc;
01807     for (i=0; i<argc; i++)
01808       cmd.argv[i]=argv[i];
01809     
01810     return command_exec(&cmd);
01811   }
01812 }
01813 
01814 
01815 
01839 const char* command_parse_token(char** remaining)
01840 {
01841   char* start;
01842   char* end;
01843   static const char eq[]="=";
01844   static bool nexttokenis_eq=false;
01845 
01846   /* Fake a '=' if needed */
01847   if (nexttokenis_eq) {
01848     nexttokenis_eq=false;
01849     return eq;
01850   }
01851   
01852   while (isspace(**remaining)) (*remaining)++;
01853   start=*remaining;
01854   end=*remaining;
01855   
01856   switch (**remaining) {
01857 
01858   case '\0':
01859     break;
01860 
01861   case  '#':
01862     while (*(++(*remaining)));
01863     start=end=(*remaining);
01864     break;
01865     
01866   case '\"':
01867     while (*(++(*remaining)) && (**remaining!='\"'));
01868     start++;
01869     end=(*remaining);
01870     if (!**remaining) ipc_notify(IPC_ONE,IPC_ERROR,"Unmatched \"");
01871     else (*remaining)++;
01872     break;
01873     
01874   case '\'':
01875     while (*(++(*remaining)) && (**remaining!='\''));
01876     start++;
01877     end=(*remaining); 
01878     if (!**remaining) ipc_notify(IPC_ONE,IPC_ERROR,"Unmatched \'");
01879     else (*remaining)++;
01880     break;
01881     
01882   default:
01883     while (*(++(*remaining)) && !isspace(**remaining) && **remaining!='=' && **remaining!='#');
01884     end=*remaining;
01885     break;
01886   }
01887 
01888   /* Prepare to fake a '=' next time since it will be overwritten now */
01889   if (end && *end=='=') {
01890     nexttokenis_eq=true;
01891     *end=' ';
01892   }
01893   
01894   /* Swallow trailing garbage until we're sure there's something interesting left */
01895   while (isspace(**remaining)) (*remaining)++;
01896   if (**remaining=='#') while (*(++(*remaining)));
01897 
01898   /* Return pointer to token */
01899   if (end) *end='\0';  /* Overwrites delimiter */
01900   return start;
01901 }
01902 
01903 
01904 
01915 int command_parse_line(Command *command, char* const cmdtxt)
01916 {
01917   char* remaining=cmdtxt;
01918   
01919   /* First token is name of command */
01920   const char* name = command_parse_token(&remaining);
01921   if (!*name) return 0; /* Must be blank line or comment */
01922   
01923   /* Look up command definition and link it in */
01924   command->def = cmddefs_lookup(name);
01925   if (!command->def) {
01926     commands_failed++;
01927     return 0;
01928   }
01929   
01930   /* Read the command's arguments, if any */
01931   command->argc=0;
01932   while ( (command->argc < CMD_MAX_ARGUMENTS) && *remaining )
01933     command->argv[(command->argc)++]=command_parse_token(&remaining);
01934 
01935   
01936   if (!command_check_usage(command)) {
01937     commands_failed++;
01938     return 0;
01939   }
01940 
01941   return 1;
01942 }
01943 
01944 
01945 
01946 int command_check_usage(Command* command)
01947 {
01948   /* Make sure right number of arguments were supplied */
01949   if (command->argc >= CMD_MAX_ARGUMENTS)
01950     ipc_notify(IPC_ONE,IPC_WARNING,"Argument list may be truncated (limit is %d)",CMD_MAX_ARGUMENTS);
01951   if (command->argc < command->def->minargs) {
01952     char buf[CMD_MAX_LINE_LENGTH];
01953     cmddef_print_usage_to_str(command->def, buf, CMD_MAX_LINE_LENGTH);
01954     ipc_notify(IPC_ONE,IPC_ERROR,"Command %s requires %d arguments but %d supplied",
01955                command->def->name,command->def->minargs,command->argc);
01956     ipc_notify(IPC_ONE,IPC_STD,"Usage: %s",buf);
01957     return 0;
01958   }
01959 
01960   return 1;
01961 }
01962 
01963 
01964 
01965 #ifndef NO_SIGNAL_HANDLING
01966 void command_exec_sigint_handler( int sig)
01967 {
01968   (void)sig; /* Unused */
01969   
01970   ipc_notify(IPC_ALL,IPC_ERROR,
01971              "Aborting command due to SIGINT; program may now be in an undefined state");
01972   longjmp(jmp_env,CMD_SIGINT_ERROR); /* Return to command_exec */
01973 }
01974 #endif
01975 
01976 
01977 
01979 cmdstat command_exec(Command *cmd)
01980 {
01981   time_t end_of_command;
01982   time_t start_of_command;
01983   double elapsed_time;
01984   int command_number=command_num_called++; /* local copy for recursive calls */
01985   char str[CMD_MAX_LINE_LENGTH];
01986   int i;
01987   int status = CMD_NO_ERROR;  /* Any non-negative integer also accepted */
01988   
01989   if (ipc_msg_level >= IPC_VERBOSE) {
01990     command_print_to_str( cmd, str, CMD_MAX_LINE_LENGTH);
01991     ipc_notify(IPC_ONE,IPC_VERBOSE,"Command %2d (%s) executing",
01992                command_number,str);
01993   }
01994 
01995   /* Call any prerequisites this command defines */
01996   for (i=0; i< cmd->def->num_prereqs && status >= CMD_NO_ERROR; i++)
01997     if (command_definitions.list[cmd->def->prereqs[i]].callstatus == CMD_NEVER_CALLED) {
01998       Command temp;
01999       temp.def=cmddefs_lookup(command_definitions.list[cmd->def->prereqs[i]].name);
02000       temp.argc=0;
02001       status = command_exec(&temp);
02002     }
02003     else status = command_definitions.list[cmd->def->prereqs[i]].callstatus;
02004   
02005 
02006   /* Call command unless already in an error state */
02007   start_of_command = time(NULL);
02008   if (status >= CMD_NO_ERROR) {
02009 
02010 #ifdef NO_SIGNAL_HANDLING
02011     status = CMD_CALL(*cmd);
02012 #else
02013     /* Set up interrupt handler */
02014     signal_handler_type original_handler = 
02015       signal(SIGINT,command_exec_sigint_handler);
02016 
02017     /* Save state in case of interrupt */
02018     if (sigsetjmp(jmp_env,True)==0)
02019       /* Enter region that might have an interrupt */
02020       status = CMD_CALL(*cmd);
02021     else
02022       /* Continue from interrupt, if there was one */
02023       status = CMD_SIGINT_ERROR; 
02024 
02025     /* Restore original interrupt handler */
02026     signal(SIGINT,original_handler);
02027 #endif
02028     
02029     ipc_notify(IPC_ONE,IPC_VERBOSE,"Command %2d exited with return code %d",
02030                command_number,status);
02031     cmd->def->callstatus = status;
02032   }
02033   else {
02034     ipc_notify(IPC_ONE,IPC_WARNING,"Command %2d not being called due to unsatisfiable prerequisite: %s",
02035              command_number,command_definitions.list[cmd->def->prereqs[i-1]].name);
02036     status = CMD_PREREQ_ERROR;
02037   }
02038 
02039   
02040   if (status >= CMD_NO_ERROR) { 
02041     commands_succeeded++;
02042   }
02043   else
02044     commands_failed++;
02045 
02046   ipc_barrier(); /* Sync after each command ; should add bcast of error status here */
02047   
02048   end_of_command = time(NULL);
02049   elapsed_time = difftime(end_of_command, start_of_command);
02050   
02051   if (elapsed_time != 0 || ipc_msg_level >= IPC_OVERWHELM) {
02052     if (ipc_msg_level >= IPC_VERBOSE) {
02053       command_print_to_str( &(*cmd), str, CMD_MAX_LINE_LENGTH);
02054       ipc_notify(IPC_ONE,IPC_VERBOSE,"Command %2d (%s) took %g seconds, completing %.24s",
02055                  command_number, str,
02056                  (double)elapsed_time, ctime(&end_of_command));
02057     }
02058     else
02059       ipc_notify(IPC_ONE,IPC_STD,"Command %2d (%s) took %g seconds, completing %.24s",
02060                  command_number, cmd->def->name,
02061                  (double)elapsed_time, ctime(&end_of_command));
02062   }
02063   
02064   return status;
02065 }
02066 
02067 
02068 
02070 cmdstat cmddefs_exec_str(const char* cmdtxt)
02071 {
02072   Command cmd;
02073   char buf[CMD_MAX_LINE_LENGTH];
02074   strncpy(buf,cmdtxt,CMD_MAX_LINE_LENGTH);
02075 
02076   if (!command_parse_line(&cmd, buf))
02077     return CMD_EMPTY;
02078 
02079   return command_exec(&cmd);
02080 }
02081 
02082 
02083 
02099 int cmddefs_exec_batch(CmdDefs_LineGenerator fn, const char * description)
02100 {
02101   int command_count;
02102   int orig_commands_succeeded=commands_succeeded;
02103   int orig_commands_failed=commands_failed;
02104   static int done;
02105   static unsigned linelength;
02106   time_t current_time;
02107 
02108   command_count=0;
02109   done=False;
02110 
02111   if (cmdparam_changes_verbose){ 
02112     current_time = time(NULL);
02113     ipc_notify(IPC_ONE,IPC_SUMMARY,"Command %s execution started %.24s",
02114                description,ctime(&current_time));
02115   }
02116 
02117   
02118   /* Read a line, broadcast it to all PEs, execute it on all PEs,
02119      and repeat until the commands have all been read.
02120   */
02121   while (!done) {
02122 
02123     /* One PE gets the next line */
02124     if (AMPARENTPE){
02125       linelength=0;
02126       cmddefs_line_buffer[0]='\0';
02127       done=(*fn)();
02128       
02129       if (!done && *cmddefs_line_buffer) {
02130         linelength=strlen(cmddefs_line_buffer);
02131         
02132         /* Strip trailing newline, if any */
02133         if (cmddefs_line_buffer[linelength-1] == '\n') {
02134           cmddefs_line_buffer[linelength-1]='\0';
02135           linelength--;
02136         }
02137         
02138         if (linelength >= CMD_MAX_LINE_LENGTH)
02139           ipc_notify(IPC_ALL,IPC_WARNING,"Command line may be truncated (limit is %d)",CMD_MAX_LINE_LENGTH);
02140       }
02141     }
02142 
02143     /* Synchronize and copy flags (and cmd line, if any) */
02144     ipc_barrier(); 
02145     ipc_get(&done,       IPC_INT,      1, PARENTPE);
02146     ipc_get(&linelength, IPC_UNSIGNED, 1, PARENTPE);
02147     if (!done && linelength>0)
02148       ipc_get((int *)(&cmddefs_line_buffer[0]), IPC_INT, (linelength+1)/sizeof(int)+1, PARENTPE);
02149 
02150     /* Make sure every PE has a local copy of the cmd line */
02151     ipc_barrier(); 
02152 
02153     /* If there's a command line present, parse and execute it (on all PEs) */
02154     if (!done && linelength>0) {
02155       ipc_notify(IPC_ONE,IPC_OVERWHELM,"Parsing command string `%s'",cmddefs_line_buffer);
02156       if (cmddefs_exec_str(cmddefs_line_buffer) != CMD_EMPTY)
02157         command_count++;
02158     }
02159   }
02160   /* To allow multiply-nested calls to this function */
02161   done=False;
02162 
02163   current_time = time(NULL);
02164   if (cmdparam_changes_verbose)
02165     ipc_notify(IPC_ONE,IPC_SUMMARY,"Command %s execution completed %.24s; %d succeeded, %d failed",
02166                description,ctime(&current_time),
02167                commands_succeeded-orig_commands_succeeded,
02168                commands_failed-orig_commands_failed);
02169 
02170   return commands_failed-orig_commands_failed;
02171 }
02172 
02173 
02174 
02177 int cmddefs_get_line_from_file( void )
02178 {
02179   if (!cmddefs_file)
02180     return true;
02181   
02182   bool done=false;
02183   char* lastchar=cmddefs_line_buffer;
02184   *lastchar='\\';
02185 
02186   while (!done && *lastchar=='\\') {
02187     done = (fgets(lastchar,CMD_MAX_LINE_LENGTH+1,cmddefs_file) == NULL);
02188     lastchar += strlen(lastchar)-2;
02189     if (lastchar < cmddefs_line_buffer) lastchar = cmddefs_line_buffer;
02190   }
02191   return done;
02192 }
02193 
02194 
02195 
02199 int cmddefs_exec_file(const char *filename)
02200 {
02201   char description[255];
02202   int status,nofile=False;
02203   FILE *orig_cmddefs_file=cmddefs_file;
02204   
02205   cmddefs_file=0;
02206   
02207   /* Only one PE opens the file */
02208   if (AMPARENTPE){
02209     /* First try the current directory */
02210     cmddefs_file=fopen(filename,"r");
02211     
02212     /* Then try all the possible paths */
02213     if (!cmddefs_file){
02214       StringParser p; /* Parse commandpath to get list of paths */
02215       StringParser::arglist words;
02216       p.parse(commandpath,words);
02217       StringParser::argptr i=words.begin();
02218       while (i!=words.end() && !cmddefs_file) {
02219         const string name = (*i)+filename;
02220         cmddefs_file=fopen(name.c_str(),"r");
02221         i++;
02222       }
02223     }
02224     if (!cmddefs_file){ 
02225       ipc_notify(IPC_ALL,IPC_ERROR,"Couldn't open command file %s",filename);
02226       nofile=True;
02227     }
02228   }
02229   
02230   snprintf(description,255,"file %s",filename);
02231   status = cmddefs_exec_batch(&cmddefs_get_line_from_file,description);
02232   
02233   if (cmddefs_file)
02234     fclose(cmddefs_file);
02235   
02236   cmddefs_file=orig_cmddefs_file;
02237   
02238   return (nofile? CMD_FILE_ERROR : status);
02239 }
02240 
02241 
02242 
02244 int cmddefs_num_total(void)
02245 {  return command_definitions.num;  }
02246 
02247 
02248 
02253 void command_print_to_str( const Command *cmd, char *str, size_t nchars)
02254 {
02255   int i;
02256   char *str_ptr=str;
02257   size_t charsleft=nchars-1;
02258 
02259   /* Print command name */
02260   snprintf(str_ptr,charsleft,"%s",cmd->def->name);
02261   charsleft -= strlen(str_ptr);
02262   str_ptr   += strlen(str_ptr);
02263 
02264   /* Print arguments */
02265   for(i=0; i < cmd->argc; i++){
02266     snprintf(str_ptr,charsleft," %s",cmd->argv[i]);
02267     charsleft -= strlen(str_ptr);
02268     str_ptr   += strlen(str_ptr);
02269   }
02270 }
02271 
02272 
02273 
02274 void cmddefs_sort(void)
02275 {
02276   qsort(command_definitions.list, command_definitions.num,
02277         sizeof(CommandDefinition), cmddef_compare);
02278 }
02279 
02280 
02281 
02283 int cmddef_compare(const void* def1, const void* def2)
02284 {
02285   return( strncasecmp(((const CommandDefinition*)def1)->name,
02286                       ((const CommandDefinition*)def2)->name,
02287                       CMDDEF_MAX_NAME_LENGTH) );
02288 }
02289 
02290 
02292 void cmddef_print_usage(FILE *fp, const CommandDefinition* cmddef)
02293 {
02294   if (cmddef->usage) 
02295     fprintf(fp,cmddef->usage,cmddef->name);
02296   else {
02297     int i;
02298     fprintf(fp,"%s",cmddef->name);
02299     for (i=0; i< cmddef->minargs; i++)
02300         fprintf(fp," <arg>");
02301     fprintf(fp," [<optional-args>]");
02302   }
02303 }
02304 
02305 
02307 void cmddef_print_usage_to_str(const CommandDefinition* cmddef, char *str, size_t nchars)
02308 {
02309   if (cmddef->usage)
02310     snprintf(str,nchars,cmddef->usage,cmddef->name);
02311   else {
02312     char *str_ptr=str;
02313     size_t charsleft=nchars-1;
02314     int i;
02315     
02316     /* Print command name */
02317     snprintf(str_ptr,charsleft,"%s",cmddef->name);
02318     charsleft -= strlen(str_ptr);
02319     str_ptr   += strlen(str_ptr);
02320     
02321     /* Print arguments */
02322     for (i=0; i< cmddef->minargs; i++) {
02323       snprintf(str_ptr,charsleft," <arg%d>",i+1);
02324       charsleft -= strlen(str_ptr);
02325       str_ptr   += strlen(str_ptr);
02326     }
02327     snprintf(str_ptr,charsleft," [<optional-args>]");
02328     charsleft -= strlen(str_ptr);
02329     str_ptr   += strlen(str_ptr);
02330   }
02331 }
02332 
02333 
02334 
02336 void cmddefs_print_all(FILE *fp)
02337 {
02338   int i;
02339   
02340   for(i=0; i< command_definitions.num; i++) {
02341     cmddef_print_usage(fp,&(command_definitions.list[i]));
02342     fprintf(fp,"\n");
02343   }
02344 }
02345 
02346 
02347 
02348 void cmddefs_print_doc_for_index(FILE *fp, int idx)
02349 {
02350   cmddef_print_usage(fp,&(command_definitions.list[idx]));
02351   fprintf(fp,"\n\n");
02352   
02353   if (command_definitions.list[idx].doc) {
02354     fprintf(fp,command_definitions.list[idx].doc,
02355             command_definitions.list[idx].name);
02356     fprintf(fp,"\n");
02357   }
02358   else
02359     fprintf(fp,"No further help available.\n");
02360 }
02361 
02362 
02363 
02365 void cmddefs_print_doc(FILE *fp, const char* name)
02366 {
02367   int idx = 0;
02368   int found = False;
02369 
02370   for (idx=command_definitions.num-1; !found && idx >= 0; idx--)
02371     if (strncmp(name,command_definitions.list[idx].name,CMDDEF_MAX_NAME_LENGTH) == 0) {
02372       cmddefs_print_doc_for_index(fp,idx);
02373       found = True;
02374     }
02375   
02376   if (!found) 
02377     ipc_notify(IPC_ONE,IPC_ERROR,"Don't know anything about command %s",name);
02378 }
02379 
02380 
02381 
02382 void cmddefs_print_all_doc(FILE *file)
02383 {
02384   int i;
02385   fprintf(file,cmdparam_help_separator);
02386   for (i=0; i< cmddefs_num_total(); i++) {
02387     cmddefs_print_doc_for_index(file,i);
02388     fprintf(file,cmdparam_help_separator);
02389     fprintf(file,"\n");
02390   }
02391 }
02392 
02393 
02394 
02401 char* cmddefs_completion_generator (char *text, int state)
02402 {
02403   static int idx, len;
02404   char *name;
02405 
02406   /*
02407     If this is a new word to complete, initialize the index counters
02408     to the beginning and save the length of text for efficiency
02409   */
02410   if (!state) {
02411     idx = 0;
02412     len = strlen(text);
02413   }
02414   
02415   /* Return the next command which partially matches, if any. */
02416   while (idx < command_definitions.num) {
02417     name = command_definitions.list[idx++].name;
02418     if (strncmp (name, text, len) == 0)
02419       return (cmdparam_dupstr(name));
02420   }
02421   
02422   return ((char *)NULL); /* No more matches */
02423 }
02424 
02425 
02426 
02427 /******************************************************************************/
02428 /* Counter hook handling                                                    */
02429 /******************************************************************************/
02430 
02431 
02453 int hooklists_define_list( const char * listname, const char * countername )
02454 {
02455   if (hooklists.num >= HOOKLISTS_MAX_NUM) {
02456     ipc_notify(IPC_ONE,IPC_ERROR,"Maximum number of hook lists reached");
02457     return CMD_PARAMETER_ERROR;
02458   }
02459   
02460   strncpy(hooklists.lists[hooklists.num].name,listname,HOOKLIST_MAX_NAME_LENGTH);
02461   strncpy(hooklists.lists[hooklists.num].countername,countername,HOOKLIST_MAX_NAME_LENGTH);
02462   hooklists.lists[hooklists.num].current=0;
02463   hooklists.num++;
02464   
02465   return hooklists.num - 1; /* Returns the HooklistNum for the new list */
02466 }
02467 
02468 
02469 
02470 /*
02471   Define a command to execute at a specfied counter.
02472 
02473   First argument is the name of the Hooklist, the second is the
02474   counter number or range to which it applies, second is the command
02475   name, and rest are arguments to be given to the command.  A counter
02476   range may be of the form "b" (a single counter value, b), "b-e" (a
02477   range of counter values b through e, inclusive), or "b-e%s", an
02478   inclusive range where the command is executed only when the counter
02479   is evenly divisible by the given step s.
02480 
02481   Examples:
02482 
02483      hook before_input        5 set exc_rad 1
02484       (Sets the param "exc_rad" to 1.0 at counter value 5)
02485 
02486      hook after_learning    6-9 plot_activity
02487       (Calls command "plot_activity" at counter values 6, 7, 8 and 9)
02488 
02489      hook after_learning  6-9%3 plot_activity
02490       (Calls command "plot_activity" at counter values 6 and 9)
02491 
02492   Hooks with the same start counter are executed in the order
02493   defined.  If desired, could add a way to set the "order" struct 
02494   elem explicitly to allow full user control over the order of
02495   execution, but it's probably not necessary.
02496 */
02497 #define nexthooknum (hooklists.lists[hooklist].num)
02498 cmdstat cmd_hook( CMD_ARGS )
02499 {
02500   int i;
02501   HooklistNum hooklist=Uninitialized;
02502   CounterHookDefinition* hook = NULL;
02503   const char *specifier=argv[1];
02504   
02505   /* Look up hook list index */
02506   for (i=0; i<hooklists.num; i++)
02507     if (!strncmp(hooklists.lists[i].name,argv[0],HOOKLIST_MAX_NAME_LENGTH))
02508       hooklist=i;
02509   
02510   if (hooklist==Uninitialized) {
02511     ipc_notify(IPC_ONE,IPC_ERROR,"Hook list is not defined: %s",argv[0]);
02512     return CMD_PARAMETER_ERROR;
02513   }
02514 
02515   if (nexthooknum >= HOOKLIST_MAX_NUM) {
02516     ipc_notify(IPC_ONE,IPC_ERROR,"Reached limit for number of counter hooks");
02517     return CMD_PARAMETER_ERROR;
02518   }
02519 
02520   hook = &(hooklists.lists[hooklist].defs[nexthooknum]);
02521   hook->order=hook_definition_counter++;
02522   
02523   /* Look up the command and link in its definition */
02524   {
02525     CommandDefinition* def = cmddefs_lookup(argv[2]);
02526     if (!def) return CMD_PARAMETER_ERROR;
02527     hook->cmd.def = def;
02528   }
02529   
02530   /*  Copy arguments to this hook's command slot  */
02531   hook->cmd.argc = argc-3;
02532   for (i=0; i< argc-3; i++) {
02533     hook->argstofree[i] = cmdparam_dupstr(argv[i+3]);
02534     hook->cmd.argv[i] = hook->argstofree[i];
02535   }
02536   hook->argstofree[i]=NULL;
02537   
02538   if (!command_check_usage(&(hook->cmd)))
02539     return CMD_PARAMETER_ERROR;
02540   
02541   if (hook->cmd.def->minargs > argc-3)
02542     ipc_notify(IPC_ONE,IPC_WARNING,"Command %s in hook requires %d args but only %d supplied");
02543   
02544   /* Create one or more hooks, as appropriate for the counter specifier */
02545   while (*specifier) {
02546     
02547     /* Parse counter specifier */
02548     if (!hook_parse_specifier( hook, &specifier )) {
02549       ipc_notify(IPC_ONE,IPC_ERROR,"Could not parse counter specifier");
02550       return CMD_PARAMETER_ERROR;
02551     }
02552     nexthooknum++;
02553     
02554     /* Display result of parsing, if desired */
02555     if (ipc_msg_level >= IPC_VERBOSE) {
02556       char str[CMD_MAX_LINE_LENGTH];
02557       command_print_to_str(&(hook->cmd),str,CMD_MAX_LINE_LENGTH);
02558       ipc_notify(IPC_ONE,IPC_VERBOSE,"Defined counter_hook %d-%d%%%d %s",
02559                  hook->start,hook->end,hook->step,str);
02560     }
02561       
02562     /* Define another hook with same parameters, if requested */
02563     if (*specifier) {
02564       if (nexthooknum >= HOOKLIST_MAX_NUM) {
02565         ipc_notify(IPC_ONE,IPC_ERROR,"Reached limit for number of counter hooks");
02566         return CMD_PARAMETER_ERROR;
02567       }
02568       
02569       hook_copy( hook, &(hooklists.lists[hooklist].defs[nexthooknum]));
02570       hook =           &(hooklists.lists[hooklist].defs[nexthooknum]);
02571     }
02572   }
02573 
02574 
02575   /* Keep the list sorted */
02576   hooklists_sort(hooklist);
02577 
02578   return CMD_NO_ERROR;
02579 }
02580 
02581 
02582 
02584 int hook_parse_specifier( CounterHookDefinition *hook, const char **remaining )
02585 {
02586   hook->step = 1; /* standard default */
02587   
02588   /* First token is required -- the counter start value */
02589   hook->start = hook->end = (int)ROUND(params_getnumericval(remaining));
02590   if (hook->start < 0) return False;
02591   if (! (**remaining)) return True;
02592   
02593   /* Next token, if any, is the counter end value */
02594   if (**remaining == '-') {
02595     if (! ++(*remaining)) return False;
02596     hook->end = (int)ROUND(params_getnumericval(remaining));
02597     if (hook->end < 0) return False;
02598   }
02599   if (! (**remaining)) return True;
02600   
02601   /* Next token, if any, is the counter step specifier */
02602   if (**remaining == '%') {
02603     int num; 
02604     if (! ++(*remaining)) return False;
02605     num = (int)ROUND(params_getnumericval(remaining));
02606     if (num < 1) return False;
02607     hook->step = num;
02608   }
02609   if (! (**remaining)) return True;
02610 
02611   /* String should either be empty now, or have a comma followed by more specs */
02612   if (**remaining == ',') {
02613     if (! ++(*remaining)) return False;
02614   }
02615   else
02616     if (**remaining) 
02617       return False;
02618     
02619   return True;
02620 }
02621 
02622 
02623     
02625 void hook_copy( CounterHookDefinition *source, CounterHookDefinition *dest)
02626 {
02627   int i;
02628   
02629   /* Command */
02630   dest->cmd.def = source->cmd.def; 
02631 
02632   /* Arguments (pointers copied only, not the strings) */
02633   dest->cmd.argc = source->cmd.argc;
02634   for (i=0; i< source->cmd.argc; i++)
02635     dest->cmd.argv[i] = source->cmd.argv[i];
02636   dest->argstofree[0]=NULL;
02637   
02638   /* Counter specifier */
02639   dest->start = source->start;
02640   dest->end   = source->end;
02641   dest->step  = source->step;
02642 
02643   /* Execution order */
02644   dest->order = source->order;
02645 }
02646 
02647 
02648 
02653 void hooklists_sort(HooklistNum hooklist)
02654 {
02655   qsort(&(CURRENT_HOOK(hooklists.lists[hooklist])),
02656         hooklists.lists[hooklist].num - hooklists.lists[hooklist].current,
02657         sizeof(CounterHookDefinition),
02658         hook_compare);
02659 }
02660 
02661 
02662 
02665 int hook_compare(const void* hook1, const void* hook2)
02666 {
02667   const CounterHookDefinition* h1=(const CounterHookDefinition*)hook1;
02668   const CounterHookDefinition* h2=(const CounterHookDefinition*)hook2;
02669 
02670   return ( h1->start == h2->start ?
02671            h1->order - h2->order :
02672            h1->start - h2->start );
02673 }
02674 
02675 
02676 
02686 void hooklists_run_list(HooklistNum hooklist, int counter, int catchuponly)
02687 {
02688   int i;
02689 
02690   /* Abort early if no more hooks available */
02691   if (!HAS_CURRENT_HOOK(hooklists.lists[hooklist]))
02692     return;
02693   
02694   /* Abort early if next valid hook not yet reached */
02695   if (counter < CURRENT_HOOK(hooklists.lists[hooklist]).start)
02696     return;
02697   
02698   /*
02699     Clear out expired hooks, calling only the ones that specify to do so,
02700     and those at most once.  No command should ever end up being called
02701     here if this procedure is called once for every counter value, since
02702     commands expiring on the previous counter value are not called.  Note
02703     that the latter policy may open up a potential odd bug in some weird
02704     situations; since the behavior differs when a counter is called every
02705     other time compared to being called every time.  It might be better
02706     to totally dispense with the automatic calls of catchup commands,
02707     using a separate call (for each missing iteration, with catchuponly
02708     true) to force catchup.
02709   */
02710   for (; (HAS_CURRENT_HOOK(hooklists.lists[hooklist])) && 
02711          (counter > CURRENT_HOOK(hooklists.lists[hooklist]).end);
02712        hooklists.lists[hooklist].current++)
02713     if ( ( CURRENT_HOOK(hooklists.lists[hooklist]).end != counter-1) && 
02714          ( CURRENT_HOOK(hooklists.lists[hooklist]).cmd.def->catchup ) )
02715       command_exec(&(CURRENT_HOOK(hooklists.lists[hooklist]).cmd));
02716 
02717   /* Check every hook starting at before this counter value */
02718   for (i=hooklists.lists[hooklist].current;
02719        (i<hooklists.lists[hooklist].num) &&
02720          (counter >= hooklists.lists[hooklist].defs[i].start);
02721        i++) {
02722     CounterHookDefinition* hookdef= &(hooklists.lists[hooklist].defs[i]);
02723   
02724     /* If counter is in specified range and is at the appropriate step, call the command */
02725     if ( (counter <= hookdef->end) &&
02726          ((counter % hookdef->step) == 0) &&
02727          (!catchuponly || hookdef->cmd.def->catchup ) ) {
02728       /* For continuous hooks, only notify user when started */
02729       if (hookdef->step !=1 || counter == hookdef->start)
02730         ipc_notify(IPC_ONE,IPC_STD,"Running hook `%s' %s at %s %d",
02731                    hookdef->cmd.def->name,
02732                    hooklists.lists[hooklist].name,
02733                    hooklists.lists[hooklist].countername,
02734                    counter);
02735       if (hookdef->step==1 && counter==hookdef->start && hookdef->end > hookdef->start)
02736         ipc_notify(IPC_ONE,IPC_STD,"(Hook `%s' will run at every %s until %s %d)",
02737                    hookdef->cmd.def->name,
02738                    hooklists.lists[hooklist].countername,
02739                    hooklists.lists[hooklist].countername,
02740                    hookdef->end);
02741       command_exec(&(hookdef->cmd));
02742     }
02743   }
02744 }   
02745 
02746 
02747 
02749 void hooklists_reset_list(HooklistNum hooklist)
02750 {
02751   hooklists.lists[hooklist].current=0;
02752   hooklists_sort(hooklist);
02753   
02754   return;
02755 }   
02756 
02757 
02758 
02760 void hooklists_empty_list(HooklistNum hooklistnum)
02761 {
02762   int i;
02763   CounterHooklist* hooklist = &(hooklists.lists[hooklistnum]);
02764 
02765   /* Free allocated items */
02766   for (i=0; i<hooklist->num; i++) {
02767     int arg=0;
02768     while (hooklist->defs[i].argstofree[arg])
02769       free(hooklist->defs[i].argstofree[arg++]);
02770   }
02771   
02772   hooklist->num=0;
02773   hooklist->current=0;
02774   
02775   return;
02776 }   
02777 
02778 
02779 
02784 void hook_print_to_str( CounterHookDefinition *hook, char *hooklistname, char *str, size_t nchars)
02785 {
02786   char *str_ptr=str;
02787   size_t charsleft=nchars-1;
02788 
02789   snprintf(str_ptr,charsleft,"hook %-15s %6d", hooklistname, hook->start);
02790   charsleft -= strlen(str_ptr); str_ptr += strlen(str_ptr);
02791 
02792   /* Only print counter end if different */
02793   if (hook->end != hook->start) {
02794     snprintf(str_ptr,charsleft,"-%d",hook->end);
02795     charsleft -= strlen(str_ptr); str_ptr += strlen(str_ptr);
02796   }
02797   
02798   /* Only print non-single steps */
02799   if (hook->step != 1) {
02800     snprintf(str_ptr,charsleft,"%%%d", hook->step);
02801     charsleft -= strlen(str_ptr); str_ptr += strlen(str_ptr);
02802   }
02803 
02804   /* Print command text */
02805   snprintf(str_ptr,charsleft,"  ");
02806   charsleft -= strlen(str_ptr); str_ptr += strlen(str_ptr);
02807   command_print_to_str(&(hook->cmd), str_ptr,charsleft);
02808   charsleft -= strlen(str_ptr); str_ptr += strlen(str_ptr);
02809 }
02810 
02811 
02812 
02817 void hooklists_log(void) 
02818 {
02819   int i,hooklist;
02820   char str[CMD_MAX_LINE_LENGTH];
02821   
02822   for (hooklist=0; hooklist<hooklists.num; hooklist++)
02823     for (i=0; i<hooklists.lists[hooklist].num; i++) {
02824       hook_print_to_str( &(hooklists.lists[hooklist].defs[i]),
02825                          hooklists.lists[hooklist].name,
02826                          str, CMD_MAX_LINE_LENGTH);
02827       ipc_log(IPC_ONE,"%s\n",str);
02828     }
02829               
02830   ipc_log(IPC_ONE,"\n");
02831 }
02832 
02833 
02834 
02835 /******************************************************************************/
02836 /* Routines that work with parameters and commands both                       */
02837 /******************************************************************************/
02838 
02839 
02843 size_t  cmdparam_identifier_length(const char* str)
02844 {
02845   const char * rem;
02846 
02847   /* 1st char must be alphabetical */
02848   if (!str || !isalpha(*str))  return 0;
02849 
02850   /* Rest must be alphanumeric or an underscore */
02851   for (rem=str; isalnum(*rem) || *rem == '_'; rem++);
02852   
02853   return rem-str;
02854 }
02855 
02856 
02857 
02859 char* cmdparam_dupstr (const char *str)
02860 {
02861   char *newstr = (char *)malloc(strlen(str)+1);
02862   if (!newstr)
02863     ipc_abort(IPC_EXIT_OUT_OF_MEMORY,"Cannot allocate string");
02864   strcpy (newstr, str);
02865   return (newstr);
02866 }
02867 
02868 
02869 
02872 int cmdparams_print_doc(FILE *fp, const char* name)
02873 {
02874   int idx = 0;
02875   int found = False;
02876 
02877   for (idx=blackboard.num-1; !found && idx >= 0; idx--)
02878     if (strncmp(name,blackboard.list[idx].name,PARAM_MAX_NAME_LENGTH) == 0) {
02879       params_print_doc_for_index(fp,idx);
02880       found = True;
02881     }
02882   
02883   if (!found) 
02884     for (idx=command_definitions.num-1; !found && idx >= 0; idx--)
02885       if (strncmp(name,command_definitions.list[idx].name,CMDDEF_MAX_NAME_LENGTH) == 0) {
02886         cmddefs_print_doc_for_index(fp,idx);
02887         found = True;
02888       }
02889 
02890   return found;
02891 }
02892 
02893 
02894 
02896 char* cmdparams_completion_generator (char *text, int state)
02897 {
02898   char *name = cmddefs_completion_generator (text, state);
02899   if (name) return name;
02900   return params_completion_generator (text, state);
02901 }
02902 
02903 
02904 int cmdparam_save_current_state(const char * filename)
02905 {
02906   int i,hooklist;
02907   FILE *file;
02908   char str[CMD_MAX_LINE_LENGTH];
02909 
02910   file=fopen(filename,"w+");
02911   if(file==NULL) {
02912     ipc_notify(IPC_ALL,IPC_ERROR,"Can't open %s file", filename);
02913     return CMD_FILE_ERROR;
02914   }
02915   
02916   fprintf(file,"# Parameters:\n\n");
02917   
02918   for(i=0; i< blackboard.num; i++)
02919     if (!blackboard.list[i].is_constant && 
02920         params_print_to_str( i, str, CMD_MAX_LINE_LENGTH))
02921       fprintf(file,"%s\n",str);
02922 
02923   fprintf(file,"\n# Hooks:\n\n");
02924   
02925   for (hooklist=0; hooklist<hooklists.num; hooklist++)
02926     for (i=0; i<hooklists.lists[hooklist].num; i++) {
02927       hook_print_to_str( &(hooklists.lists[hooklist].defs[i]),
02928                          hooklists.lists[hooklist].name,
02929                          str, CMD_MAX_LINE_LENGTH);
02930       fprintf(file,"%s\n",str);
02931     }
02932 
02933   fprintf(file,"\n");
02934   fclose(file);
02935 
02936   return CMD_NO_ERROR;
02937 }
02938 
02939 
02940 
02942 cmdstat cmdparam_set( const char *name, const char *expr )
02943 {
02944   int status = CMD_NO_ERROR;
02945   int idx = 0;
02946   int found  = False;
02947 
02948   for (idx=0; !found && idx < blackboard.num; idx++)
02949     if (strncmp(name,blackboard.list[idx].name,PARAM_MAX_NAME_LENGTH) == 0) {
02950       ParamInterface* def = &(blackboard.list[idx]);
02951       found = True;
02952       
02953       if (def->is_constant) {
02954         ipc_notify(IPC_ONE,IPC_ERROR,"Can't change value of symbolic constant");
02955         status = CMD_PARAMETER_ERROR;
02956       }
02957       else
02958         switch(def->data_type) {
02959           
02960           
02961         case PARAM_STRING:
02962           {
02963             if (param_check( def, expr))
02964               status = CMD_PARAMETER_ERROR;
02965             else {
02966               const char* str=cmds(expr);
02967               int differs=strcmp(def->data_ptr.c,str);
02968               
02969               if (def->setfn)
02970                 status = (*def->setfn)(def->name,def->data_ptr.c,str);
02971               else
02972                 strcpy(def->data_ptr.c,str);
02973               
02974               if (differs && cmdparam_changes_verbose) /* Accepts resetting to same as a "change" */
02975                 ipc_notify(IPC_ONE,IPC_STD,"Changed parameter %s to \"%s\"",def->name,str);
02976             }
02977             break;
02978           }
02979           
02980           
02981         case PARAM_BOOL:
02982           {
02983             int oldvalue    = *(def->data_ptr.i);
02984             int newvalue    = cmdi(expr);
02985             
02986             /* Force value to be one of the three canonical Boolean values */
02987             if (newvalue!=Uninitialized && newvalue!=False)
02988               newvalue=True;
02989             
02990             if (param_check( def, &newvalue))
02991               status = CMD_PARAMETER_ERROR;
02992             else {
02993               if (def->setfn) 
02994                 status = (*def->setfn)(def->name,def->data_ptr.i,&newvalue);
02995               else
02996                 *(def->data_ptr.i)  = newvalue;
02997               
02998               if (cmdparam_changes_verbose && *(def->data_ptr.i) != oldvalue)
02999                 ipc_notify(IPC_ONE,IPC_STD,"Changed parameter %s to %s",def->name,
03000                            param_pp_boolean(*(def->data_ptr.i)));
03001             }
03002             break;
03003           }
03004           
03005           
03006         case PARAM_INT:
03007           {
03008             int oldvalue    = *(def->data_ptr.i);
03009             int newvalue    = cmdi(expr); 
03010             
03011             if (param_check( def, &newvalue))
03012               status = CMD_PARAMETER_ERROR;
03013             else {
03014               if (def->setfn) 
03015                 status = (*def->setfn)(def->name,def->data_ptr.i,&newvalue);
03016               else
03017                 *(def->data_ptr.i)  = newvalue;
03018               
03019               if (cmdparam_changes_verbose && *(def->data_ptr.i) != oldvalue)
03020                 ipc_notify(IPC_ONE,IPC_STD,"Changed parameter %s to %s",
03021                            def->name,param_pp_int(*(def->data_ptr.i)));
03022             }
03023             break;
03024           }
03025           
03026           
03027         case PARAM_DOUBLE:
03028           {
03029             double oldvalue = *(def->data_ptr.f);    
03030             double newvalue = cmdf(expr); 
03031             
03032             if (param_check( def, &newvalue))
03033               status = CMD_PARAMETER_ERROR;
03034             else {
03035               if (def->setfn) 
03036                 status = (*def->setfn)(def->name, def->data_ptr.f, &newvalue);
03037               else 
03038                 *(def->data_ptr.f)  = newvalue;
03039               if (cmdparam_changes_verbose && *(def->data_ptr.f) != oldvalue)
03040                 ipc_notify(IPC_ONE,IPC_STD,"Changed parameter %s to %s",
03041                            def->name,param_pp_float(*(def->data_ptr.f)));
03042             }
03043             break;
03044           }
03045           
03046           
03047         default:
03048           ipc_notify(IPC_ONE,IPC_WARNING,"Datatype of %s unknown; can't change it",name);
03049           status = CMD_PARAMETER_ERROR;
03050         }
03051       def->been_set = True;
03052     }
03053   
03054   if (!found) {
03055     if (cmdparam_warn_unknown)
03056       ipc_notify(IPC_ONE,IPC_ERROR,"Unknown parameter: %s", name);
03057     status = CMD_PARAMETER_ERROR;
03058   }
03059 
03060   return status;
03061 }
03062 
03063 
03064 
03065 /******************************************************************************/
03066 /* Commands                                                                   */
03067 /******************************************************************************/
03068 
03069 
03071 cmdstat cmd_set( CMD_ARGS )
03072 {
03073   if (argc < 3 || argc%3 != 0) {
03074     ipc_notify(IPC_ONE,IPC_ERROR,"Wrong number of arguments to set (%d)",argc);
03075     return CMD_PARAMETER_ERROR;
03076   }
03077 
03078   int status = CMD_NO_ERROR;
03079 
03080   for (int namearg=0,pivotarg=1,valuearg=2; valuearg<argc; namearg+=3,pivotarg+=3,valuearg+=3) {
03081     if (*argv[pivotarg] != '=') {
03082       ipc_notify(IPC_ONE,IPC_ERROR,"Syntax error in set arguments ('%s' is not '=')",argv[pivotarg]);
03083       return CMD_PARAMETER_ERROR;
03084     }
03085 
03086     /* Keeps only the most negative error status, which is probably the most severe */
03087     status = std::min(status,cmdparam_set( argv[namearg], argv[valuearg] ));
03088   }
03089   
03090   return status;
03091 }
03092 
03093 
03094 
03095 cmdstat cmd_quit( CMD_ARGS )
03096 {
03097   time_t current_time = time(NULL);
03098   ipc_notify(IPC_ONE,IPC_SUMMARY,"Exiting at %.24s", ctime(&current_time));
03099   
03100   if (argc>0)
03101     ipc_exit(IPC_EXIT_NORMAL,argv[0]);
03102   else
03103     ipc_exit(IPC_EXIT_NORMAL,"Exited via quit command");
03104 
03105   return CMD_NO_ERROR;
03106 }
03107 
03108 
03109 
03110 cmdstat cmd_clear_hooks( CMD_ARGS )
03111 {
03112   int status=CMD_NO_ERROR;
03113   HooklistNum hooklist;
03114   
03115   if (argc<1) /* Clear all lists by default */
03116     for (hooklist=0; hooklist<hooklists.num; hooklist++) {
03117       if (hooklists.lists[hooklist].num) {
03118         hooklists_empty_list(hooklist);
03119         ipc_notify(IPC_ONE,IPC_STD,"Emptied hooklist %s",hooklists.lists[hooklist].name);
03120       }
03121     }
03122   else {
03123     int i,arg;
03124 
03125     for (arg=0; arg<argc; arg++) {
03126       hooklist = Uninitialized;
03127       
03128       /* Look up hook list index */
03129       for (i=0; i<hooklists.num; i++)
03130         if (!strncmp(hooklists.lists[i].name,argv[arg],HOOKLIST_MAX_NAME_LENGTH))
03131           hooklist=i;
03132   
03133       if (hooklist==Uninitialized) {
03134         ipc_notify(IPC_ONE,IPC_ERROR,"Hook list is not defined: %s",argv[0]);
03135         status= CMD_PARAMETER_ERROR;
03136       }
03137       else if (hooklists.lists[hooklist].num) {
03138         hooklists_empty_list(hooklist);
03139         ipc_notify(IPC_ONE,IPC_STD,"Emptied hooklist %s",hooklists.lists[hooklist].name);
03140       }
03141     }
03142   }
03143   
03144   return status;
03145 }
03146 
03147 
03148 
03149 cmdstat cmd_save_params( CMD_ARGS )
03150 {
03151   (void)argc; /* Unused */
03152   return cmdparam_save_current_state(cmds(argv[0]));
03153 }
03154 
03155 
03156 
03157 cmdstat cmd_exec( CMD_ARGS )
03158 {
03159   int status=0;
03160   
03161   for (int i=0; i<argc; i++) {
03162     int newstatus = cmddefs_exec_str(cmds(argv[i]));
03163     if (newstatus>=0) status=newstatus;
03164   }
03165 
03166   return status;
03167 }
03168 
03169 
03170 
03171 cmdstat cmd_exec_file( CMD_ARGS )
03172 {
03173   int status;
03174   
03175   cmddefs_exec_file_argc = argc-1;
03176   cmddefs_exec_file_argv = (argc>1 ? &argv[1] : NULL);
03177   
03178   status=cmddefs_exec_file(cmds(argv[0]));
03179   cmddefs_exec_file_argc=0;
03180   
03181   return (status? CMD_FILE_ERROR : CMD_NO_ERROR);
03182 }
03183 
03184 
03185 
03186 cmdstat cmd_call( CMD_ARGS )
03187 {
03188   int status;
03189   const int oldipcverbosity = ipc_msg_level;
03190   const int newipcverbosity = IPC_ALERT;
03191 
03192   if (ipc_msg_level > newipcverbosity)
03193     ipc_msg_level = newipcverbosity;
03194   
03195   cmddefs_exec_file_argc    = argc-1;
03196   cmddefs_exec_file_argv    = (argc>1 ? &argv[1] : NULL);
03197 
03198 
03199   status=cmddefs_exec_file(cmds(argv[0]));
03200   cmddefs_exec_file_argc=0;
03201   if (ipc_msg_level==newipcverbosity) /* If unchanged by file */
03202     ipc_msg_level=oldipcverbosity;
03203     
03204   return (status? CMD_FILE_ERROR : CMD_NO_ERROR);
03205 }
03206 
03207 
03208 
03210 cmdstat cmd_read_args( CMD_ARGS )
03211 {
03212   (void)argc; /* Unused */
03213   (void)argv; /* Unused */
03214 
03215   return (cmddefs_exec_file_argc<1 ? CMD_NO_ERROR : 
03216           cmd_set(cmddefs_exec_file_argc,cmddefs_exec_file_argv));
03217 }
03218 
03219 
03220 
03221 cmdstat cmd_define_param( CMD_ARGS )
03222 {
03223   const char* name        = argv[0];           
03224   const int   type        = (argc<=1 ? PARAM_DOUBLE : cmdi(argv[1]));
03225   const bool  has_lbound  = (type == PARAM_STRING ? True : argc>2);
03226   const int   lower_bound = (argc>2 ? cmdi(argv[2]) : (type == PARAM_STRING ? 0 : Uninitialized));
03227   const bool  has_ubound  = (type == PARAM_STRING ? True : argc>3);
03228   const int   ub          = (argc>3 ? cmdi(argv[3]) : Uninitialized);
03229   const int   upper_bound = (type==PARAM_STRING && ub==Uninitialized ? CMD_MAX_LINE_LENGTH : ub);
03230   const char* helpstring  = (argc>4 ? cmds(argv[4]) : NULL);
03231   const bool  has_initval = argc>5;
03232   const char* initval     = (argc>5 ? argv[5] : NULL);
03233   
03234   size_t size             = (type == PARAM_STRING ? upper_bound : MAX(sizeof(double),sizeof(int)));
03235   void *pointer           = (void *)malloc(size);
03236 
03237 
03238   if (params_define_param(name, type, False, pointer) == 1) {
03239     if (has_lbound) params_add_lower_bound_int(name,lower_bound);
03240     if (has_ubound) params_add_upper_bound_int(name,upper_bound);
03241     if (helpstring) params_define_doc(name,cmdparam_dupstr(helpstring));
03242     if (cmdparam_changes_verbose)
03243       ipc_notify(IPC_ONE,IPC_STD,"Defined parameter %s",name);
03244     if (has_initval)
03245       cmdparam_set( name, initval );
03246   }
03247   
03248   return CMD_NO_ERROR;
03249 }
03250 
03251 
03252 
03253 cmdstat cmd_if( CMD_ARGS )
03254 {
03255   if (cmdi(argv[0]))
03256     return cmddefs_exec_by_name(argv[1],argc-2,&(argv[2]));
03257   
03258   return CMD_NO_ERROR;
03259 }
03260 
03261 
03262 
03263 cmdstat cmd_dotimes( CMD_ARGS )
03264 {
03265   int i;
03266   int status = CMD_NO_ERROR;
03267   
03268   for (i=0; i<cmdi(argv[0]); i++) {
03269     status = cmddefs_exec_by_name(argv[1],argc-2,&(argv[2]));
03270     if (status) return status;
03271   }
03272   
03273   return CMD_NO_ERROR;
03274 }
03275 
03276 
03277 
03278 cmdstat cmd_for( CMD_ARGS )
03279 {
03280   int status = CMD_NO_ERROR;
03281 
03282   for (cmd_set(3,&(argv[0])); cmdi(argv[3]); cmd_set(3,&(argv[4]))) {
03283     status = cmddefs_exec_by_name(argv[7],argc-8,&(argv[8]));
03284     if (status) return status;
03285   }
03286   
03287   return CMD_NO_ERROR;
03288 }
03289 
03290 
03291 
03292 cmdstat cmd_print( CMD_ARGS )
03293 {
03294   int i;
03295 
03296   for (i=0; i<argc; i++)
03297     if (*argv[i] == '$')
03298       ipc_notify(IPC_ONE,IPC_SUMMARY,"%s == %s",argv[i],cmds(argv[i]));    
03299     else {
03300       double value = cmdf(argv[i]);
03301       ipc_notify(IPC_ONE,IPC_SUMMARY,"%s == %g",argv[i],value);
03302     }
03303   
03304   return CMD_NO_ERROR;
03305 }
03306 
03307 
03308 
03309 cmdstat cmd_system( CMD_ARGS )
03310 {
03311   char str[CMD_MAX_LINE_LENGTH];
03312   char *str_ptr=str;
03313   size_t charsleft=CMD_MAX_LINE_LENGTH-1;
03314   int i;
03315   int status=0;
03316   
03317   /* Print arguments as a command */
03318   for(i=0; i < argc; i++){
03319     snprintf(str_ptr,charsleft,"%s%s",(i==0? "" : " "),cmds(argv[i]));
03320     charsleft -= strlen(str_ptr);
03321     str_ptr   += strlen(str_ptr);
03322   }
03323 
03324   ipc_notify(IPC_ONE,IPC_VERBOSE,"Executing shell command `%s'",str);
03325   if (AMPARENTPE)
03326     status=system(str);
03327 
03328   return (status? CMD_MISC_ERROR : CMD_NO_ERROR);
03329 }

Generated at Mon Aug 21 00:30:54 2000 for RF-LISSOM by doxygen1.2.1 written by Dimitri van Heesch, © 1997-2000