Files
modules/cmdPath.c
rkowen b3c7011933 * Upgraded configure.in to autoconf 2.52.
* Changed  the configure to search for libTcl.sh to acquire all it's
necessary Tcl env.vars from it.

* created a utility function stringer() to handle most of the strcpy/strcat
operations and to automatically allocate string memory.

* Mostly changed the memory malloc/free to use stringer/null_free.

* rewrote some of the code logic to have a single point of exit
with unwinding of memory allocations.
2002-04-29 21:16:48 +00:00

762 lines
23 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*****
** ** Module Header ******************************************************* **
** **
** Modules Revision 3.0 **
** Providing a flexible user environment **
** **
** File: cmdPath.c **
** First Edition: 91/10/23 **
** **
** Authors: John Furlan, jlf@behere.com **
** Jens Hamisch, jens@Strawberry.COM **
** **
** Description: The path manipulation routines. Much of the heart of **
** Modules is contained in this file. These routines **
** are responsible for adding and removing directories **
** from given PATH-like variables. **
** **
** Exports: cmdSetPath **
** cmdRemovePath **
** **
** Notes: **
** **
** ************************************************************************ **
****/
/** ** Copyright *********************************************************** **
** **
** Copyright 1991-1994 by John L. Furlan. **
** see LICENSE.GPL, which must be provided, for details **
** **
** ************************************************************************ **/
static char Id[] = "@(#)$Id: cmdPath.c,v 1.6 2002/04/29 21:16:48 rkowen Exp $";
static void *UseId[] = { &UseId, Id };
/** ************************************************************************ **/
/** HEADERS **/
/** ************************************************************************ **/
#include "modules_def.h"
#ifdef HAS_SYS_PARAM_H
#include <sys/param.h>
#endif
/** ************************************************************************ **/
/** LOCAL DATATYPES **/
/** ************************************************************************ **/
/** not applicable **/
/** ************************************************************************ **/
/** CONSTANTS **/
/** ************************************************************************ **/
#ifdef MAXPATHLEN
#define PATH_BUFLEN MAXPATHLEN
#else
#define PATH_BUFLEN 1024
#endif
/** ************************************************************************ **/
/** MACROS **/
/** ************************************************************************ **/
#define _TCLCHK(a) \
{if (*(a)->result) ErrorLogger(ERR_EXEC,LOC,(a)->result,NULL);}
/** ************************************************************************ **/
/** LOCAL DATA **/
/** ************************************************************************ **/
static char module_name[] = "cmdPath.c"; /** File name of this module **/
#if WITH_DEBUGGING_CALLBACK
static char _proc_cmdSetPath[] = "cmdSetPath";
static char _proc_cmdRemovePath[] = "cmdRemovePath";
static char _proc_Remove_Path[] = "Remove_Path";
#endif
static char buffer[ PATH_BUFLEN];
/** ************************************************************************ **/
/** PROTOTYPES **/
/** ************************************************************************ **/
static int Remove_Path( Tcl_Interp *interp, char *variable, char *item,
char *sw_marker);
/*++++
** ** Function-Header ***************************************************** **
** **
** Function: cmdSetPath **
** **
** Description: Add the passed value (argv[2]) to the specified vari-**
** able (argv[1]). argv[0] specifies, if the variable **
** is to be appended or prepended. Each directory in **
** the path is checked to see whether it is already **
** in the path. If so it is not added. **
** **
** First Edition: 91/10/23 **
** **
** 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: g_flags These are set up accordingly before **
** this function is called in order to **
** control everything **
** **
** ************************************************************************ **
++++*/
int cmdSetPath( ClientData client_data,
Tcl_Interp *interp,
int argc,
char *argv[])
{
Tcl_RegExp chkexpPtr; /** Regular expression for **/
/** marker checking **/
char *oldpath, /** Old value of 'var' **/
*newpath, /** New value of 'var' **/
*sw_marker = APP_SW_MARKER, /** arbitrary default **/
*startp=NULL, *endp=NULL, /** regexp match endpts **/
*qualifiedpath, /** List of dirs which
aren't already in path **/
**pathlist; /** List of dirs **/
int append = 1, /** append or prepend **/
numpaths, /** number of dirs in path **/
qpathlen, /** qualifiedpath length **/
x; /** loop index **/
#if WITH_DEBUGGING_CALLBACK
ErrorLogger( NO_ERR_START, LOC, _proc_cmdSetPath, NULL);
#endif
/**
** Whatis mode?
**/
if( g_flags & (M_WHATIS | M_HELP))
goto success0;
/**
** Check arguments. There should be give 3 args:
** argv[0] - prepend/append
** argv[1] - varname
** argv[2] - value
**/
if(argc != 3)
if( OK != ErrorLogger(ERR_USAGE, LOC, argv[0],"path-variable directory",
NULL))
goto unwind0;
/**
** Should this guy be removed from the variable ... If yes, do so!
**/
if(g_flags & M_REMOVE)
return( cmdRemovePath(client_data, interp, argc, argv)); /** ----> **/
/**
** prepend or append. The default is append.
**/
if( !( append = !!strncmp( argv[0], "pre", 3)))
sw_marker = PRE_SW_MARKER;
/**
** Display only ... ok, let's do so!
**/
if(g_flags & M_DISPLAY) {
fprintf( stderr, "%s\t ", argv[ 0]);
while( --argc)
fprintf( stderr, "%s ", *++argv);
fprintf( stderr, "\n");
goto success0;
}
/**
** Get the old value of the variable. MANPATH defaults to "/usr/man".
** Put a \ in front of each '.' and '+'.
** (this is an intentional memory leak)
**/
oldpath = Tcl_GetVar2( interp, "env", argv[1], TCL_GLOBAL_ONLY);
_TCLCHK(interp)
if( oldpath == NULL)
oldpath = !strcmp( argv[1], "MANPATH") ? "/usr/man" : "";
/**
** Split the new path into its components directories so each
** directory can be checked to see whether it is already in the
** existing path.
**/
if( !( pathlist = SplitIntoList( interp, argv[2], &numpaths)))
goto unwind0;
/**
** Some space for the list of paths which
** are not already in the existing path.
**/
if((char *) NULL == (qualifiedpath = stringer(NULL,0, argv[2], ":", NULL)))
if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
goto unwind1;
qpathlen = strlen(qualifiedpath)+1;
*qualifiedpath = '\0'; /** make sure null for later **/
for( x = 0; x < numpaths; x++) {
cleanse_path( pathlist[x], buffer, PATH_BUFLEN);
/**
** Check to see if path is already in this path variable.
** It could be at the
** beginning ... ^path:
** middle ... :path:
** end ... :path$
** only one ... ^path$
**/
if((char *) NULL == (newpath = stringer(NULL,0,
"(^", buffer, ":)|(:", buffer, ":)|(:",
buffer, "$)|(^", buffer, "$)",NULL)))
if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
goto unwind2;
chkexpPtr = Tcl_RegExpCompile(interp, newpath);
_TCLCHK(interp)
null_free((void *) &newpath);
/**
** If the directory is not already in the path,
** add it to the qualified path.
**/
if( !Tcl_RegExpExec(interp, chkexpPtr, oldpath, oldpath))
if ((char *) NULL ==
stringer(qualifiedpath + strlen(qualifiedpath),
qpathlen - strlen(qualifiedpath),
pathlist[x], ":", NULL))
if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
goto unwind2;
} /** End of loop that checks for
** already existent path
**/
/**
** If all of the directories in the new path already exist,
** exit doing nothing.
**/
if( ! *qualifiedpath)
goto success1;
/* remove trailing ':' */
qualifiedpath[strlen(qualifiedpath) - 1] = '\0';
/**
** Some space for our newly created path.
** We size at the oldpath plus the addition.
**/
if((char *)NULL == (newpath = stringer(NULL, strlen( oldpath) +
strlen(qualifiedpath) + 2,NULL)))
if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
goto unwind2;
*newpath = '\0';
/**
** Easy job to do, if the old path has not been set up so far ...
**/
if( !strcmp( oldpath, "")) {
strcpy( newpath, qualifiedpath);
/**
** Otherwise we have to take care on prepending vs. appending ...
** If there is a append or prepend marker within the variable (see
** modules_def.h) the changes are made according to this markers. Other-
** wise append and prepend will be relative to the strings begin or end.
**/
} else {
Tcl_RegExp markexpPtr = Tcl_RegExpCompile(interp, sw_marker);
_TCLCHK(interp)
strcpy( newpath, oldpath);
if( Tcl_RegExpExec(interp, markexpPtr, oldpath, oldpath)) {
_TCLCHK(interp)
Tcl_RegExpRange(markexpPtr, 0, &startp, &endp);
/**
** Append/Prepend marker found
**/
if( append) {
char ch = *endp;
*endp = '\0';
strcpy(newpath, oldpath);
if (newpath[strlen(newpath)-1] != ':') strcat(newpath, ":");
strcat(newpath, qualifiedpath);
*endp = ch;
strcat(newpath, endp);
} else {
char ch = *startp;
*startp = '\0';
strcpy(newpath, oldpath);
if (newpath[strlen(newpath)-1] != ':') strcat(newpath, ":");
strcpy(newpath, qualifiedpath);
if (*oldpath != ':') strcat(newpath, ":");
strcat(newpath, oldpath);
*startp = ch;
strcat(newpath, startp);
}
} else {
/**
** No marker set
**/
if( !append) {
strcpy(newpath, qualifiedpath);
if (*oldpath != ':') strcat(newpath, ":");
strcat(newpath, oldpath);
} else {
strcpy(newpath, oldpath);
if (newpath[strlen(newpath)-1] != ':') strcat(newpath, ":");
strcat(newpath, qualifiedpath);
}
} /** if( marker) **/
null_free((void*) &markexpPtr);
} /** if( strcmp) **/
/**
** Now the new value to be set resides in 'newpath'. Set it up.
**/
moduleSetenv( interp, argv[1], newpath, 1);
_TCLCHK(interp)
#if WITH_DEBUGGING_CALLBACK
ErrorLogger( NO_ERR_END, LOC, _proc_cmdSetPath, NULL);
#endif
/**
** Free resources
**/
null_free((void *) &newpath);
success1:
null_free((void *) &qualifiedpath);
FreeList( pathlist, numpaths);
success0:
return( TCL_OK); /** -------- EXIT (SUCCESS) -------> **/
unwind2:
null_free((void *) &qualifiedpath);
unwind1:
FreeList( pathlist, numpaths);
unwind0:
return( TCL_ERROR); /** -------- EXIT (FAILURE) -------> **/
} /** End of 'cmdSetPath' **/
/*++++
** ** Function-Header ***************************************************** **
** **
** Function: cmdRemovePath **
** **
** Description: Remove the passed value (argv[2]) from the specified **
** variable (argv[1]). In case of switching this pro- **
** cedure removes markers from the path, too. argv[0] **
** specifies, if the append- or prepend-marker is af- **
** fected **
** **
** First Edition: 91/10/23 **
** **
** 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: g_flags These are set up accordingly before **
** this function is called in order to **
** control everything **
** **
** ************************************************************************ **
++++*/
int cmdRemovePath( ClientData client_data,
Tcl_Interp *interp,
int argc,
char *argv[])
{
char *sw_marker = APP_SW_MARKER; /** arbitrary default **/
char **pathlist; /** List of dirs **/
int numpaths; /** number of dirs in path **/
int x; /** loop index **/
#if WITH_DEBUGGING_CALLBACK
ErrorLogger( NO_ERR_START, LOC, _proc_cmdRemovePath, NULL);
#endif
/**
** Check arguments. There should be give 3 args:
** argv[0] - prepend/append/remove-path
** argv[1] - varname
** argv[2] - value
**/
if(argc != 3)
if( OK != ErrorLogger(ERR_USAGE,LOC,argv[0],"path-variable directory",
NULL))
goto unwind0;
/**
** Display only ... ok, let's do so!
**/
if(g_flags & M_DISPLAY) {
fprintf( stderr, "%s\t ", argv[ 0]);
while( --argc)
fprintf( stderr, "%s ", *++argv);
fprintf( stderr, "\n");
goto success0;
}
/**
** prepend or append. The default is append.
**/
if( ! strncmp( argv[0], "pre", 3))
sw_marker = PRE_SW_MARKER;
/**
** For switch state3, we're looking to remove the markers.
**/
if( g_flags & M_SWSTATE3)
argv[2] = sw_marker;
/**
** Split the path into its components so each item can be removed
** individually from the variable.
**/
if( !( pathlist = SplitIntoList( interp, argv[2], &numpaths)))
goto unwind0;
/**
** Remove each item individually
**/
for( x = 0; x < numpaths; x++)
if( TCL_OK != Remove_Path( interp, argv[1], pathlist[x], sw_marker))
goto unwind1;
#if WITH_DEBUGGING_CALLBACK
ErrorLogger( NO_ERR_END, LOC, _proc_cmdRemovePath, NULL);
#endif
/**
** Free resources
**/
FreeList(pathlist, numpaths);
success0:
return( TCL_OK); /** -------- EXIT (SUCCESS) -------> **/
unwind1:
FreeList(pathlist, numpaths);
unwind0:
return( TCL_ERROR); /** -------- EXIT (FAILURE) -------> **/
} /** End of 'cmdRemovePath' **/
/*++++
** ** Function-Header ***************************************************** **
** **
** Function: Remove_Path **
** **
** Description: This function actually does the work of removing **
** the item from the path. It is done this way to **
** support multiple items (often directories) **
** seperated by colons in the variable value. **
** **
** First Edition: 2001/08/08 **
** **
** Parameters: Tcl_Interp *interp According Tcl interp.**
** char *variable Variable from which **
** to remove item **
** char *item Item to remove **
** char *sw_marker Switch marker **
** **
** Result: int TCL_OK Successfull completion **
** TCL_ERROR Any error **
** **
** Attached Globals: g_flags These are set up accordingly before **
** this function is called in order to **
** control everything **
** **
** ************************************************************************ **
++++*/
static int Remove_Path( Tcl_Interp *interp,
char *variable,
char *item,
char *sw_marker)
{
char *oldpath, /** Old value of 'var' **/
*tmppath, /** Temp. buffer for 'var' **/
*searchpath,
*startp=NULL, *endp=NULL; /** regexp match endpts **/
int start_offset = 0, /** match offsets **/
end_offset = 0,
path_len = 0; /** length of unmatched path **/
Tcl_RegExp regexpPtr = (Tcl_RegExp) NULL,
begexpPtr = (Tcl_RegExp) NULL,
midexpPtr = (Tcl_RegExp) NULL,
endexpPtr = (Tcl_RegExp) NULL,
onlyexpPtr = (Tcl_RegExp) NULL,
markexpPtr = (Tcl_RegExp) NULL;
#if WITH_DEBUGGING_CALLBACK
ErrorLogger( NO_ERR_START, LOC, _proc_Remove_Path, NULL);
#endif
/**
** Get the current value of the "PATH" environment variable
**/
if( NULL == (tmppath = Tcl_GetVar2( interp, "env", variable,
TCL_GLOBAL_ONLY))) {
_TCLCHK(interp)
return( TCL_OK); /** -------- EXIT (SUCCESS) -------> **/
}
/**
** We want to make a local copy of old path because we're going
** to be butchering it and the environ one doesn't need to be
** changed in this manner.
** Put a \ in front of each . and + in the passed value to remove.
**/
if((char *) NULL == (oldpath = stringer(NULL,0, tmppath, NULL)))
if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
goto unwind0;
path_len = strlen(oldpath);
cleanse_path( item, buffer, PATH_BUFLEN);
/**
** Now we need to find it in the path variable. So, we create
** space enough to build search expressions.
**/
if((char *) NULL == (searchpath = stringer(NULL,0,"^", buffer, ":", NULL)))
if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
goto unwind1;
/**
** This section searches for the oldpath in the PATH variable.
** There are four cases because the colons must be used as markers
** in order to guarantee that a partial match isn't being found.
**
** The start_offset and end_offset variables indicate how much to
** cut or not to cut off of each end of the located
** string. They differ for each case...beginning of
** the path, middle of the path, end of the path,
** and for the only path.
**
** This is to ensure the colons get cut off properly.
**/
begexpPtr = Tcl_RegExpCompile(interp, searchpath);
_TCLCHK(interp)
if( Tcl_RegExpExec(interp, begexpPtr, oldpath, oldpath) > 0) {
_TCLCHK(interp)
regexpPtr = begexpPtr;
/**
** Copy from the beginning of the startp to one
** character before endp
**/
Tcl_RegExpRange(begexpPtr, 0, &startp, &endp);
start_offset = 0; end_offset = -1;
path_len -= (endp - startp - 1);
} else {
*searchpath = ':'; /* :buffer: */
midexpPtr = Tcl_RegExpCompile(interp, searchpath);
_TCLCHK(interp)
if( Tcl_RegExpExec(interp, midexpPtr, oldpath, oldpath) > 0) {
_TCLCHK(interp)
regexpPtr = midexpPtr;
/**
** Copy from one character after stringp[0] to one
** character before endp[0]
**/
Tcl_RegExpRange(midexpPtr, 0, &startp, &endp);
start_offset = 1; end_offset = -1;
path_len -= (endp - startp - 2);
} else {
searchpath[strlen(searchpath)-1] = '$'; /* :buffer$ */
endexpPtr = Tcl_RegExpCompile(interp, searchpath);
_TCLCHK(interp)
if( Tcl_RegExpExec(interp, endexpPtr, oldpath, oldpath) > 0) {
_TCLCHK(interp)
regexpPtr = endexpPtr;
/**
** Copy from one character after stringp[0]
** through endp[0]
**/
Tcl_RegExpRange(endexpPtr, 0, &startp, &endp);
start_offset = 0; end_offset = 0;
path_len -= (endp - startp - 1);
} else {
*searchpath = '^'; /* ^buffer$ */
onlyexpPtr = Tcl_RegExpCompile(interp, searchpath);
_TCLCHK(interp)
if(Tcl_RegExpExec(interp, onlyexpPtr, oldpath, oldpath) > 0) {
_TCLCHK(interp)
/**
** In this case, I should go ahead and unset the variable
** from the environment because I'm removing the very last
** path.
**
** First I'm going to clear the variable from the
** setenvHashTable just in case its already been altered
** and had a significant value at the time. It's very
** possible that I'm removing the only two or three paths
** from this variable. If that's the case, then all the
** earlier paths were marked for output in this hashTable.
**
** Secondly, I actually mark the the environment variable
** to be unset when output.
**/
clear_hash_value( setenvHashTable, variable);
moduleUnsetenv( interp, variable);
/**
** moduleUnsetenv doesn't unset the variable in the Tcl
** space because the $env variable might need to be
** used again in the modulefile for locating other
** paths. BUT, since this was a path-type environment
** variable, the user is expecting this to be empty
** after removing the only remaining path. So, I set
** the variable empty here.
**/
(void) Tcl_SetVar2( interp, "env", variable, "",
TCL_GLOBAL_ONLY);
_TCLCHK(interp)
null_free((void *) &searchpath);
goto success1;
}
}
}
}
null_free((void *) &searchpath);
/**
** If I couldn't find it assume it wasn't there
** and return.
**/
if( !regexpPtr)
goto success1;
markexpPtr = Tcl_RegExpCompile(interp, sw_marker);
_TCLCHK(interp)
/**
** In state1, we're actually replacing old paths with
** the markers for future appends and prepends.
**
** We only want to do this once to mark the location
** the module was formed around.
**/
if((g_flags & M_SWSTATE1) && ! (Tcl_RegExpExec(interp,
markexpPtr, oldpath, oldpath) > 0)) {
/**
** If I don't have enough space to replace the oldpath with the
** marker, then I must create space for the marker and thus
** recreate the PATH variable.
**/
char* newenv;
int newlen = path_len + strlen(sw_marker) + 1;
if((char *) NULL==(newenv = stringer(NULL, newlen ,NULL) ))
if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
goto unwind1;
*(startp + start_offset) = '\0';
if(*(endp + end_offset) == '\0') {
(void) stringer(newenv, newlen, oldpath,":",sw_marker,NULL);
} else {
(void) stringer(newenv, newlen, oldpath,sw_marker,
endp + end_offset, NULL);
}
/**
** Just store the value with the marker into the environment. I'm not
** checking if it changed because the only case that a PATH-type
** variable will be unset is when I remove the only path remaining in
** the variable. So, using moduleSetenv is not necessary here.
**/
Tcl_SetVar2( interp, "env", variable, newenv, TCL_GLOBAL_ONLY);
_TCLCHK(interp)
null_free((void *) &newenv);
} else { /** SW_STATE1 **/
/**
** We must be in SW_STATE3 or not in SW_STATE at all.
** Removing the marker should be just like removing any other path.
**/
strcpy( startp + start_offset, endp);
/**
** Cache the set. Clear the variable from the unset table just
** in case it was previously unset.
**/
store_hash_value( setenvHashTable, variable, oldpath);
clear_hash_value( unsetenvHashTable, variable);
/**
** Store the new PATH value into the environment.
**/
Tcl_SetVar2( interp, "env", variable, oldpath, TCL_GLOBAL_ONLY);
_TCLCHK(interp)
} /** ! SW_STATE1 **/
#if WITH_DEBUGGING_CALLBACK
ErrorLogger( NO_ERR_END, LOC, _proc_cmdRemovePath, NULL);
#endif
/**
** Free what has been used and return on success
**/
success1:
null_free((void *) &oldpath);
success0:
return( TCL_OK); /** -------- EXIT (SUCCESS) -------> **/
unwind1:
null_free((void *) &oldpath);
unwind0:
return( TCL_ERROR); /** -------- EXIT (FAILURE) -------> **/
} /** End of 'Remove_Path' **/