/***** ** ** Module Header ******************************************************* ** ** ** ** Modules Revision 3.0 ** ** Providing a flexible user environment ** ** ** ** File: cmdTrace.c ** ** First Edition: 95/12/26 ** ** ** ** Authors: Jens Hamisch, jens@Strawberry.COM ** ** ** ** Description: The Tcl module-trace routine which provides a con- ** ** trolling interface to the modulecmd tracing feature ** ** ** ** Exports: cmdModuleTrace ** ** GetTraceSel ** ** CheckTracing ** ** CheckTracingList ** ** ** ** Notes: ** ** ** ** ************************************************************************ ** ****/ /** ** Copyright *********************************************************** ** ** ** ** Copyright 1991-1994 by John L. Furlan. ** ** see LICENSE.GPL, which must be provided, for details ** ** ** ** ************************************************************************ **/ static char Id[] = "@(#)$Id: cmdTrace.c,v 1.2 2001/06/09 09:48:46 rkowen Exp $"; static void *UseId[] = { &UseId, Id }; /** ************************************************************************ **/ /** HEADERS **/ /** ************************************************************************ **/ #include "modules_def.h" /** ************************************************************************ **/ /** LOCAL DATATYPES **/ /** ************************************************************************ **/ typedef struct _mod_trace { char **re_ptr; /** Pointer to the reqular expr. **/ /** which identifies the command **/ char const *cmd; /** The spelled command for printing **/ char *tracing; /** The tracing setup **/ char alloc; /** Alloc flag **/ } ModTrace; /** ************************************************************************ **/ /** CONSTANTS **/ /** ************************************************************************ **/ #ifdef WITH_TRACE_LOAD # define MOD_TR_LOAD WITH_TRACE_LOAD #else # define MOD_TR_LOAD _all_off #endif #ifdef WITH_TRACE_UNLOAD # define MOD_TR_UNLOAD WITH_TRACE_UNLOAD #else # define MOD_TR_UNLOAD _all_off #endif #ifdef WITH_TRACE_SWITCH # define MOD_TR_SWITCH WITH_TRACE_SWITCH #else # define MOD_TR_SWITCH _all_off #endif #ifdef WITH_TRACE_DISP # define MOD_TR_DISP WITH_TRACE_DISP #else # define MOD_TR_DISP _all_off #endif #ifdef WITH_TRACE_LIST # define MOD_TR_LIST WITH_TRACE_LIST #else # define MOD_TR_LIST _all_off #endif #ifdef WITH_TRACE_AVAIL # define MOD_TR_AVAIL WITH_TRACE_AVAIL #else # define MOD_TR_AVAIL _all_off #endif #ifdef WITH_TRACE_HELP # define MOD_TR_HELP WITH_TRACE_HELP #else # define MOD_TR_HELP _all_off #endif #ifdef WITH_TRACE_INIT # define MOD_TR_INIT WITH_TRACE_INIT #else # define MOD_TR_INIT _all_off #endif #ifdef WITH_TRACE_USE # define MOD_TR_USE WITH_TRACE_USE #else # define MOD_TR_USE _all_off #endif #ifdef WITH_TRACE_UNUSE # define MOD_TR_UNUSE WITH_TRACE_UNUSE #else # define MOD_TR_UNUSE _all_off #endif #ifdef WITH_TRACE_UPDATE # define MOD_TR_UPDATE WITH_TRACE_UPDATE #else # define MOD_TR_UPDATE _all_off #endif #ifdef WITH_TRACE_PURGE # define MOD_TR_PURGE WITH_TRACE_PURGE #else # define MOD_TR_PURGE _all_off #endif #ifdef WITH_TRACE_CLEAR # define MOD_TR_CLEAR WITH_TRACE_CLEAR #else # define MOD_TR_CLEAR _all_off #endif #ifdef WITH_TRACE_APROPOS # define MOD_TR_APROPOS WITH_TRACE_APROPOS #else # define MOD_TR_APROPOS _all_off #endif #ifdef WITH_TRACE_WHATIS # define MOD_TR_WHATIS WITH_TRACE_WHATIS #else # define MOD_TR_WHATIS _all_off #endif /** ************************************************************************ **/ /** MACROS **/ /** ************************************************************************ **/ /** not applicable **/ /** ************************************************************************ **/ /** LOCAL DATA **/ /** ************************************************************************ **/ static char module_name[] = "cmdTrace.c"; /** File name of this module **/ #if WITH_DEBUGGING_CALLBACK static char _proc_cmdModuleTrace[] = "cmdModuleTrace"; #endif /** ** Tracing strings ** contain a colon separated list of 'switch on/switch off' selector for ** module files. Each selector beginning with a '+' means 'tracing on', '-' ** means 'tracing off'. ** The +/- switch is followed by the name of the modulefile to which it ** belongs. All valif TCL regexps may be specified here. **/ static char _all[] = ".*"; static char _all_on[] = "+.*"; static char _all_off[] = "-.*"; /** ** The tracing selection table **/ static ModTrace TraceSelect[] = { { &addRE, "load", MOD_TR_LOAD, 0 }, /** 0 **/ { &rmRE, "unload", MOD_TR_UNLOAD, 0 }, /** 1 **/ { &swRE, "switch", MOD_TR_SWITCH, 0 }, /** 2 **/ { &dispRE, "display", MOD_TR_DISP, 0 }, /** 3 **/ { &listRE, "list", MOD_TR_LIST, 0 }, /** 4 **/ { &availRE, "avail", MOD_TR_AVAIL, 0 }, /** 5 **/ { &helpRE, "help", MOD_TR_HELP, 0 }, /** 6 **/ { &initRE, "init", MOD_TR_INIT, 0 }, /** 7 **/ { &useRE, "use", MOD_TR_USE, 0 }, /** 8 **/ { &unuseRE, "unuse", MOD_TR_UNUSE, 0 }, /** 9 **/ { &updateRE, "update", MOD_TR_UPDATE, 0 }, /** 10 **/ { &purgeRE, "purge", MOD_TR_PURGE, 0 }, /** 11 **/ { &clearRE, "clear", MOD_TR_CLEAR, 0 }, /** 12 **/ { &whatisRE, "whatis", MOD_TR_WHATIS, 0 }, /** 13 **/ { &aproposRE, "apropos", MOD_TR_APROPOS, 0 } /** 14 **/ }; /** ************************************************************************ **/ /** PROTOTYPES **/ /** ************************************************************************ **/ static int GetTraceTable( Tcl_Interp *interp, char *cmd, int num); static int ChangeTraceSel( Tcl_Interp *interp, char *cmd_tab, int cmd_tab_size, char on_off, char *module_pat); static int CheckTracingPat( Tcl_Interp *interp, char *pattern, char *modulefile); /*++++ ** ** Function-Header ***************************************************** ** ** ** ** Function: cmdModuleTrace ** ** ** ** Description: Callback function for 'trace' ** ** ** ** First Edition: 95/12/26 ** ** ** ** Parameters: ClientData client_data ** ** Tcl_Interp *interp According Tcl interp.** ** int argc Number of arguments ** ** char *argv[] Argument array ** ** ** ** Result: int TCL_OK Successfull completion ** ** TCL_ERROR Any error ** ** ** ** Attached Globals: TraceSelect List containing all tracing settings ** ** g_flags These are set up accordingly before ** ** this function is called in order to ** ** control everything ** ** ** ** ************************************************************************ ** ++++*/ int cmdModuleTrace( ClientData client_data, Tcl_Interp *interp, int argc, char *argv[]) { char on_off = '+'; /** The first argument **/ char **args; /** Argument pointer for scanning **/ int i, k; /** Loop counter **/ int cmd_tab_size; char *cmd_table; /** Command table **/ int ret = TCL_OK; #if WITH_DEBUGGING_CALLBACK ErrorLogger( NO_ERR_START, LOC, _proc_cmdModuleTrace, NULL); #endif /** ** Whatis mode? **/ if( g_flags & (M_WHATIS | M_HELP)) return( TCL_OK); /** ------- EXIT PROCEDURE -------> **/ /** ** Parameter check **/ if( argc < 2) { if( OK != ErrorLogger( ERR_USAGE, LOC, argv[0], "on|off", "[cmd [cmd ...]]", "[ -module module [module ...]]", NULL)) return( TCL_ERROR); /** -------- EXIT (FAILURE) -------> **/ } /** ** On or off? **/ if( !strcmp( argv[ 1], "on")) on_off = '+'; else if( !strcmp( argv[ 1], "off")) on_off = '-'; else { if( OK != ErrorLogger( ERR_USAGE, LOC, argv[0], "on|off", "[cmd [cmd ...]]", "[ -module module [module ...]]", NULL)) return( TCL_ERROR); /** -------- EXIT (FAILURE) -------> **/ } /** ** Display mode? **/ if( g_flags & M_DISPLAY) { fprintf( stderr, "%s\t ", argv[ 0]); for( i=1; i **/ } /** ** We need a table of module command involved in this trace selection. ** Allocate one and initialize it. **/ cmd_tab_size = sizeof( TraceSelect) / sizeof( TraceSelect[ 0]); if((char *) NULL == (cmd_table = (char *) malloc( cmd_tab_size))) return((OK == ErrorLogger( ERR_ALLOC, LOC, NULL)) ? TCL_OK : TCL_ERROR); /** ** Scan all commands specified as options. The last option to be scanned ** is either the real last one, or the '-module' one **/ args = argv + 2; i = argc - 2; memset( cmd_table, !i, cmd_tab_size); while( i--) { char *tmp = *args++; /** ** Check for '-module' **/ if( !strncmp( tmp, "-module", strlen( tmp))) break; /** ** This should be a module command. ** Check it against the TraceSelect table **/ if( -1 != (k = GetTraceTable(interp, tmp, cmd_tab_size))) { cmd_table[ k] = 1; } else { if( OK != ErrorLogger( ERR_COMMAND, LOC, tmp, NULL)) { free( cmd_table); return( TCL_ERROR); } } } /** while( i--) **/ /** ** Now scan all Modulefiles concerned in the current command ** If we ran to the end of the argument list (i==0), ALL files are ** concerned in this ... **/ if( 0 >= i) { ret = ChangeTraceSel(interp, cmd_table, cmd_tab_size, on_off, _all); } else { while( i-- && TCL_OK == ret) ret = ChangeTraceSel(interp, cmd_table, cmd_tab_size, on_off, *args++); } /** ** Cleanup finally and return **/ free( cmd_table); #if WITH_DEBUGGING_CALLBACK ErrorLogger( NO_ERR_END, LOC, _proc_cmdModuleTrace, NULL); #endif return( ret); } /** End of 'cmdModuleTrace' **/ /*++++ ** ** Function-Header ***************************************************** ** ** ** ** Function: GetTraceTable ** ** ** ** Description: Returns the TraceSelect index for the passed module ** ** subcommand ** ** ** ** First Edition: 95/12/26 ** ** ** ** Parameters: char *cmd Subcommand to be checked ** ** int num Number of commands to be chk ** ** ** ** Result: int >= 0 Successfull completion ** ** -1 Any error ** ** ** ** ************************************************************************ ** ++++*/ static int GetTraceTable(Tcl_Interp *interp, char *cmd, int num) { int k; for( k=0; k < num; k++) { if( Tcl_RegExpMatch(interp, cmd, *(TraceSelect[k].re_ptr))) return( k); /** ----------- Got it ------------> **/ } /** for( k) **/ /** ** Not found .. **/ return( -1); } /** End of 'GetTraceTable' **/ /*++++ ** ** Function-Header ***************************************************** ** ** ** ** Function: GetTraceSel ** ** ** ** Description: Retrieve the trace selection pattern for the passed ** ** module command ** ** ** ** First Edition: 95/12/26 ** ** ** ** Parameters: char *cmd Subcommand to be checked ** ** ** ** Result: char * NULL Module subcommand not found ** ** Otherwise Assigned trace pattern ** ** ** ** ************************************************************************ ** ++++*/ char *GetTraceSel( Tcl_Interp *interp, char *cmd) { int k; if( -1 == (k = GetTraceTable(interp, cmd, (sizeof( TraceSelect) / sizeof( TraceSelect[ 0]) )))) return((char *) NULL); return( TraceSelect[ k].tracing); } /** End of 'GetTraceSel' **/ /*++++ ** ** Function-Header ***************************************************** ** ** ** ** Function: ChangeTraceSel ** ** ** ** Description: Change the trace selection for all commands speci- ** ** fied in the passed 'cmd_table'. The passed module- ** ** name has to be changed according 'on_off' ** ** ** ** First Edition: 95/12/26 ** ** ** ** Parameters: char *cmd_table Boolean array indicating all ** ** commands in the TraceSelect ** ** table to be changed ** ** int cmd_tab_size Size of this array ** ** char on_off '+' switch tracing on ** ** '-' switch tracing off ** ** char *module_pat Pattern for the affected ** ** module files ** ** ** ** Result: int TCL_OK Successfull completion ** ** TCL_ERROR Any error ** ** ** ** ************************************************************************ ** ++++*/ static int ChangeTraceSel( Tcl_Interp *interp, char *cmd_table, int cmd_tab_size, char on_off, char *module_pat) { char *pattern, *s, *t, *tmp; int len = strlen( module_pat); int i; int ret = TCL_OK; /** ** Need a buffer for to build the complete pattern **/ if((char *) NULL == (pattern = (char *) malloc( len + 2))) { ErrorLogger( ERR_ALLOC, LOC, NULL); return( TCL_ERROR); } /** ** Check if this is the ALL pattern. If it is, replace all affected ** entries with '_all_on' or '_all_off' **/ if( !strcmp( module_pat, _all)) { for( i=0; i < cmd_tab_size; i++) { if( cmd_table[ i]) { if( TraceSelect[ i].alloc) free( TraceSelect[ i].tracing) TraceSelect[ i].alloc = 0; TraceSelect[ i].tracing = ('+' == on_off) ? _all_on : _all_off; } } } else { /** if( ALL pattern) **/ /** ** Check the pattern for colons ... **/ if( strchr( module_pat, ':')) if( OK != ErrorLogger( ERR_COLON, LOC, module_pat, NULL)) ret = TCL_ERROR; if( TCL_OK == ret) { /** ** Build the complete pattern **/ *pattern = on_off; strcpy( pattern + 1, module_pat); len += 1; /** ** Loop for all entries to be changed **/ for( i=0; i < cmd_tab_size; i++) { if( cmd_table[ i]) { /** ** allocate a buffer for the new pattern **/ if((char *) NULL == (tmp = (char *) malloc( len + 2 + strlen( TraceSelect[ i].tracing)))) { if( OK == ErrorLogger( ERR_ALLOC, LOC, NULL)) { continue; } else { ret = TCL_ERROR; break; } } /** ** Copy the new pattern to the buffer at first **/ strcpy( tmp, pattern); t = tmp + len; /** ** Tokenize the old pattern and remove duplicates **/ for( s = strtok( TraceSelect[ i].tracing, ":"); s; s = strtok( NULL, ":") ) { if( strcmp( (s+1), module_pat)) { *t++ = ':'; while( *t++ = *s++); t--; } } /** for **/ /** ** Finally check if we have to dealloc and set up the ** new pattern **/ if( TraceSelect[ i].alloc) free( TraceSelect[ i].tracing); TraceSelect[ i].tracing = tmp; TraceSelect[ i].alloc = 1; } /** if **/ } /** for **/ } /** if( ret) **/ } /** if( ALL pattern) **/ /** ** Free what has been allocated an return **/ free( pattern); return( ret); } /** End of 'ChangeTraceSel' **/ /*++++ ** ** Function-Header ***************************************************** ** ** ** ** Function: CheckTracing ** ** ** ** Description: Check wheter thracing is turned on for the passed ** ** command and modulefile ** ** ** ** First Edition: 95/12/26 ** ** ** ** Parameters: char *cmd Subcommand to be checked ** ** char *modulefile Modulefile to be checked ** ** ** ** Result: int 0 No tracing ** ** 1 Tracing enabled ** ** ** ** ************************************************************************ ** ++++*/ int CheckTracing( Tcl_Interp *interp, char *cmd, char *modulefile) { int k; /** ** Get the TraceSelect table index **/ if( -1 == (k = GetTraceTable(interp, cmd, (sizeof( TraceSelect) / sizeof( TraceSelect[0]) )))) { ErrorLogger( ERR_COMMAND, LOC, cmd, NULL); return( 0); } /** ** Now check this guy ... **/ return( CheckTracingPat(interp, TraceSelect[ k].tracing, modulefile)); } /** End of 'CheckTracing' **/ /*++++ ** ** Function-Header ***************************************************** ** ** ** ** Function: CheckTracingPat ** ** ** ** Description: Check the passed pattern if it enables tracing for ** ** the passed module file ** ** ** ** First Edition: 95/12/26 ** ** ** ** Parameters: char *pattern Pattern to be checked ** ** char *modulefile Modulefile to be checked ** ** ** ** Result: int 0 No tracing ** ** 1 Tracing enabled ** ** ** ** ************************************************************************ ** ++++*/ static int CheckTracingPat( Tcl_Interp *interp, char *pattern, char *modulefile) { char *s; int ret; /** ** Tokenize the pattern and check if it matches ... **/ for( s = strtok( pattern, ":"); s; s = strtok( NULL, ":") ) { ret = ('+' == *s++); if( Tcl_RegExpMatch(interp, modulefile, s)) return( ret); } /** for **/ /** ** No pattern matched the module file name ... **/ return( 0); } /** End of 'CheckTracingPat' **/ /*++++ ** ** Function-Header ***************************************************** ** ** ** ** Function: CheckTracingList ** ** ** ** Description: Check wheter tracing is turned on for the passed ** ** command and at least one of the passed modulefiles ** ** ** ** First Edition: 95/12/26 ** ** ** ** Parameters: char *cmd Subcommand to be checked ** ** int count Number of passed modulefiles ** ** char **modules Modulefiles to be checked ** ** ** ** Result: int 0 No tracing ** ** 1 Tracing enabled ** ** ** ** ************************************************************************ ** ++++*/ int CheckTracingList( Tcl_Interp *interp, char *cmd, int count, char **modules) { int k; /** ** Get the TraceSelect table index **/ if( -1 == (k = GetTraceTable(interp, cmd, (sizeof( TraceSelect) / sizeof( TraceSelect[0]) )))) { ErrorLogger( ERR_COMMAND, LOC, cmd, NULL); return( 0); } /** ** Now check whether one of them requires tracing **/ while( count--) if( CheckTracingPat(interp, TraceSelect[ k].tracing, *modules++)) return( 1); return( 0); } /** End of 'CheckTracingList' **/