1    | /*****
2    |  ** ** Module Header ******************************************************* **
3    |  ** 									     **
4    |  **   Modules Revision 3.0						     **
5    |  **   Providing a flexible user environment				     **
6    |  ** 									     **
7    |  **   File:		cmdPath.c					     **
8    |  **   First Edition:	91/10/23					     **
9    |  ** 									     **
10   |  **   Authors:	John Furlan, jlf@behere.com				     **
11   |  **		Jens Hamisch, jens@Strawberry.COM			     **
12   |  ** 									     **
13   |  **   Description:	The path manipulation routines. Much of the heart of **
14   |  **			Modules is contained in this file. These routines    **
15   |  **			are responsible for adding and removing directories  **
16   |  **			from given PATH-like variables.			     **
17   |  ** 									     **
18   |  **   Exports:		cmdSetPath					     **
19   |  **			cmdRemovePath					     **
20   |  ** 									     **
21   |  **   Notes:								     **
22   |  ** 									     **
23   |  ** ************************************************************************ **
24   |  ****/
25   | 
26   | /** ** Copyright *********************************************************** **
27   |  ** 									     **
28   |  ** Copyright 1991-1994 by John L. Furlan.                      	     **
29   |  ** see LICENSE.GPL, which must be provided, for details		     **
30   |  ** 									     ** 
31   |  ** ************************************************************************ **/
32   | 
33   | static char Id[] = "@(#)$Id: cmdPath.c.src.html,v 1.1 2005/11/15 03:43:35 rkowen Exp $";
34   | static void *UseId[] = { &UseId, Id };
35   | 
36   | /** ************************************************************************ **/
37   | /** 				      HEADERS				     **/
38   | /** ************************************************************************ **/
39   | 
40   | #include "modules_def.h"
41   | #ifdef	HAS_SYS_PARAM_H
42   | #include <sys/param.h>
43   | #endif
44   | 
45   | /** ************************************************************************ **/
46   | /** 				  LOCAL DATATYPES			     **/
47   | /** ************************************************************************ **/
48   | 
49   | /** not applicable **/
50   | 
51   | /** ************************************************************************ **/
52   | /** 				     CONSTANTS				     **/
53   | /** ************************************************************************ **/
54   | 
55   | #ifdef	MAXPATHLEN
56   | #define	PATH_BUFLEN	MAXPATHLEN
57   | #else
58   | #define	PATH_BUFLEN	1024
59   | #endif
60   | 
61   | /** ************************************************************************ **/
62   | /**				      MACROS				     **/
63   | /** ************************************************************************ **/
64   | 
65   | #define _TCLCHK(a)	\
66   | 	{if (*(a)->result) ErrorLogger(ERR_EXEC,LOC,(a)->result,NULL);}
67   | 
68   | /** ************************************************************************ **/
69   | /** 				    LOCAL DATA				     **/
70   | /** ************************************************************************ **/
71   | 
72   | static	char	module_name[] = "cmdPath.c";	/** File name of this module **/
73   | 
74   | #if WITH_DEBUGGING_CALLBACK
75   | static	char	_proc_cmdSetPath[] = "cmdSetPath";
76   | static	char	_proc_cmdRemovePath[] = "cmdRemovePath";
77   | static	char	_proc_Remove_Path[] = "Remove_Path";
78   | #endif
79   | 
80   | static char buffer[ PATH_BUFLEN];
81   | 
82   | /** ************************************************************************ **/
83   | /**				    PROTOTYPES				     **/
84   | /** ************************************************************************ **/
85   | 
86   | static int	Remove_Path( Tcl_Interp	*interp, char *variable, char *item, 
87   | 			char *sw_marker);
88   | 
89   | 
90   | /*++++
91   |  ** ** Function-Header ***************************************************** **
92   |  ** 									     **
93   |  **   Function:		cmdSetPath					     **
94   |  ** 									     **
95   |  **   Description:	Add the passed value (argv[2]) to the specified vari-**
96   |  **			able (argv[1]). argv[0] specifies, if the variable   **
97   |  **			is to be appended or prepended.  Each directory in   **
98   |  **			the path is checked to see whether it is already     **
99   |  **			in the path.  If so it is not added.		     **
100  |  ** 									     **
101  |  **   First Edition:	91/10/23					     **
102  |  ** 									     **
103  |  **   Parameters:	ClientData	 client_data			     **
104  |  **			Tcl_Interp	*interp		According Tcl interp.**
105  |  **			int		 argc		Number of arguments  **
106  |  **			char		*argv[]		Argument array	     **
107  |  ** 									     **
108  |  **   Result:		int	TCL_OK		Successfull completion	     **
109  |  **				TCL_ERROR	Any error		     **
110  |  ** 									     **
111  |  **   Attached Globals:	g_flags		These are set up accordingly before  **
112  |  **					this function is called in order to  **
113  |  **					control everything		     **
114  |  ** 									     **
115  |  ** ************************************************************************ **
116  |  ++++*/
117  | 
118  | int	cmdSetPath(	ClientData	 client_data,
119  | 	       		Tcl_Interp	*interp,
120  | 	       		int		 argc,
121  | 	       		char		*argv[])
122  | {
123  |     Tcl_RegExp	chkexpPtr;			/** Regular expression for   **/
124  | 						/** marker checking	     **/
125  |     char	*oldpath,			/** Old value of 'var'	     **/
126  | 		*newpath,			/** New value of 'var'	     **/
127  | 		*sw_marker = APP_SW_MARKER,	/** arbitrary default	     **/
128  | 		*startp=NULL, *endp=NULL,	/** regexp match endpts	     **/
129  | 		*qualifiedpath,			/** List of dirs which 
130  | 						    aren't already in path   **/
131  | 		**pathlist;			/** List of dirs	     **/
132  |     int		 append = 1,			/** append or prepend	     **/
133  | 		 numpaths,			/** number of dirs in path   **/
134  | 		 qpathlen,			/** qualifiedpath length     **/
135  | 		 x;				/** loop index		     **/
136  | 
137  | #if WITH_DEBUGGING_CALLBACK
138  |     ErrorLogger( NO_ERR_START, LOC, _proc_cmdSetPath, NULL);
139  | #endif
140  | 
141  |     /**
142  |      **  Whatis mode?
143  |      **/
144  |     if( g_flags & (M_WHATIS | M_HELP))
145  |         goto success0;
146  | 	
147  |     /**
148  |      **   Check arguments. There should be give 3 args:
149  |      **     argv[0]  -  prepend/append
150  |      **     argv[1]  -  varname
151  |      **     argv[2]  -  value
152  |      **/
153  |     if(argc != 3)
154  | 	if( OK != ErrorLogger(ERR_USAGE, LOC, argv[0],"path-variable directory",
155  | 	    NULL))
156  | 	    goto unwind0;
157  | 
158  |     /**
159  |      **  Should this guy be removed from the variable ... If yes, do so!
160  |      **/
161  |     if(g_flags & M_REMOVE) 
162  | 	return( cmdRemovePath(client_data, interp, argc, argv));   /** ----> **/
163  | 
164  |     /**
165  |      **  prepend or append. The default is append.
166  |      **/
167  |     if( !( append = !!strncmp( argv[0], "pre", 3)))
168  | 	sw_marker = PRE_SW_MARKER;
169  |   
170  |     /**
171  |      **  Display only ... ok, let's do so!
172  |      **/
173  |     if(g_flags & M_DISPLAY) {
174  | 	fprintf( stderr, "%s\t ", argv[ 0]);
175  | 	while( --argc)
176  | 	    fprintf( stderr, "%s ", *++argv);
177  | 	fprintf( stderr, "\n");
178  |         goto success0;
179  |     }
180  |   
181  |     /**
182  |      **  Get the old value of the variable. MANPATH defaults to "/usr/man".
183  |      **  Put a \ in front of each '.' and '+'.
184  |      **  (this is an intentional memory leak)
185  |      **/
186  |     oldpath = Tcl_GetVar2( interp, "env", argv[1], TCL_GLOBAL_ONLY);
187  |     _TCLCHK(interp)
188  | 
189  |     if( oldpath == NULL)
190  | 	oldpath = !strcmp( argv[1], "MANPATH") ? "/usr/man" : "";
191  | 
192  |     /**
193  |      **  Split the new path into its components directories so each
194  |      **  directory can be checked to see whether it is already in the 
195  |      **  existing path.
196  |      **/
197  |     if( !( pathlist = SplitIntoList( interp, argv[2], &numpaths)))
198  | 	goto unwind0;
199  | 
200  |     /**
201  |      **  Some space for the list of paths which
202  |      **  are not already in the existing path.
203  |      **/
204  |     if((char *) NULL == (qualifiedpath = stringer(NULL,0, argv[2], ":", NULL)))
205  | 	if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
206  | 	    goto unwind1;
207  | 
208  |     qpathlen = strlen(qualifiedpath)+1;
209  |     *qualifiedpath = '\0';		/** make sure null for later	     **/
210  | 
211  |     for( x = 0; x < numpaths; x++) {
212  | 
213  | 	cleanse_path( pathlist[x], buffer, PATH_BUFLEN);
214  | 
215  | 	/**
216  | 	 **  Check to see if path is already in this path variable.
217  | 	 **  It could be at the 
218  | 	 **     beginning ... ^path:
219  | 	 **     middle    ... :path:
220  | 	 **     end       ... :path$
221  | 	 **     only one  ... ^path$
222  | 	 **/
223  | 	if((char *) NULL == (newpath = stringer(NULL,0,
224  | 		"(^", buffer, ":)|(:", buffer, ":)|(:",
225  | 		buffer, "$)|(^", buffer, "$)",NULL)))
226  | 	    if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
227  | 		goto unwind2;
228  | 
229  | 	chkexpPtr = Tcl_RegExpCompile(interp, newpath);
230  | 	_TCLCHK(interp)
231  | 	null_free((void *) &newpath);
232  | 
233  | 	/**
234  | 	 **  If the directory is not already in the path, 
235  | 	 **  add it to the qualified path.
236  | 	 **/
237  | 	if( !Tcl_RegExpExec(interp, chkexpPtr, oldpath, oldpath))
238  | 	    if ((char *) NULL ==
239  | 		    stringer(qualifiedpath + strlen(qualifiedpath),
240  | 		    qpathlen - strlen(qualifiedpath),
241  | 		    pathlist[x], ":", NULL))
242  | 		if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
243  | 		    goto unwind2;
244  | 
245  |     }					/** End of loop that checks for 
246  | 					 ** already existent path
247  | 					 **/
248  |     /**
249  |      **  If all of the directories in the new path already exist,
250  |      **  exit doing nothing.
251  |      **/
252  |     if( ! *qualifiedpath)
253  | 	goto success1;
254  | 
255  |     /* remove trailing ':' */
256  |     qualifiedpath[strlen(qualifiedpath) - 1] = '\0';
257  | 
258  |     /**
259  |      **  Some space for our newly created path.
260  |      **  We size at the oldpath plus the addition.
261  |      **/
262  |     if((char *)NULL == (newpath = stringer(NULL, strlen( oldpath) +
263  | 	strlen(qualifiedpath) + 2,NULL)))
264  | 	if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
265  | 	    goto unwind2;
266  |     *newpath = '\0';
267  | 
268  |     /**
269  |      **  Easy job to do, if the old path has not been set up so far ...
270  |      **/
271  |     if( !strcmp( oldpath, "")) {
272  | 	strcpy( newpath, qualifiedpath);
273  | 
274  |     /**
275  |      **  Otherwise we have to take care on prepending vs. appending ...
276  |      **  If there is a append or prepend marker within the variable (see
277  |      **  modules_def.h) the changes are made according to this markers. Other-
278  |      **  wise append and prepend will be relative to the strings begin or end.
279  |      **/
280  | 
281  |     } else {
282  | 
283  | 	Tcl_RegExp	markexpPtr = Tcl_RegExpCompile(interp, sw_marker);
284  | 	_TCLCHK(interp)
285  | 
286  | 	strcpy( newpath, oldpath);
287  | 
288  | 	if( Tcl_RegExpExec(interp, markexpPtr, oldpath, oldpath)) {
289  | 	    _TCLCHK(interp)
290  | 	    Tcl_RegExpRange(markexpPtr, 0, &startp, &endp);
291  | 
292  | 	    /**
293  | 	     **  Append/Prepend marker found
294  | 	     **/
295  | 	    if( append) {
296  | 		char ch = *endp;
297  | 		*endp = '\0';
298  | 		strcpy(newpath, oldpath);
299  | 		if (newpath[strlen(newpath)-1] != ':')	strcat(newpath, ":");
300  | 		strcat(newpath, qualifiedpath);
301  | 		*endp = ch;
302  | 		strcat(newpath, endp);
303  | 	    } else {
304  | 		char ch = *startp;
305  | 		*startp = '\0';
306  | 		strcpy(newpath, oldpath);
307  | 		if (newpath[strlen(newpath)-1] != ':')	strcat(newpath, ":");
308  | 		strcpy(newpath, qualifiedpath);
309  | 		if (*oldpath != ':')	strcat(newpath, ":");
310  | 		strcat(newpath, oldpath);
311  | 		*startp = ch;
312  | 		strcat(newpath, startp);
313  | 	    }
314  | 
315  | 	} else {
316  | 
317  | 	    /**
318  | 	     **  No marker set
319  | 	     **/
320  | 	    if( !append) {
321  | 		strcpy(newpath, qualifiedpath);
322  | 		if (*oldpath != ':')	strcat(newpath, ":");
323  | 		strcat(newpath, oldpath);
324  | 	    } else {
325  | 		strcpy(newpath, oldpath);
326  | 		if (newpath[strlen(newpath)-1] != ':')	strcat(newpath, ":");
327  | 		strcat(newpath, qualifiedpath);
328  | 	    }
329  | 
330  | 	} /** if( marker) **/
331  | 
332  |     } /** if( strcmp) **/
333  | 
334  |     /**
335  |      **  Now the new value to be set resides in 'newpath'. Set it up.
336  |      **/
337  |     moduleSetenv( interp, argv[1], newpath, 1);
338  |     _TCLCHK(interp)
339  | 
340  | #if WITH_DEBUGGING_CALLBACK
341  |     ErrorLogger( NO_ERR_END, LOC, _proc_cmdSetPath, NULL);
342  | #endif
343  | 
344  |     /**
345  |      ** Free resources
346  |      **/
347  |     null_free((void *) &newpath);
348  | success1:
349  |     null_free((void *) &qualifiedpath);
350  |     FreeList( pathlist, numpaths);
351  | success0:
352  |     return( TCL_OK);			/** -------- EXIT (SUCCESS) -------> **/
353  | 
354  | unwind2:
355  |     null_free((void *) &qualifiedpath);
356  | unwind1:
357  |     FreeList( pathlist, numpaths);
358  | unwind0:
359  |     return( TCL_ERROR);			/** -------- EXIT (FAILURE) -------> **/
360  | 
361  | } /** End of 'cmdSetPath' **/
362  | 
363  | /*++++
364  |  ** ** Function-Header ***************************************************** **
365  |  ** 									     **
366  |  **   Function:		cmdRemovePath					     **
367  |  ** 									     **
368  |  **   Description:	Remove the passed value (argv[2]) from the specified **
369  |  **			variable (argv[1]). In case of switching this pro-   **
370  |  **			cedure removes markers from the path, too. argv[0]   **
371  |  **			specifies, if the append- or prepend-marker is af-   **
372  |  **			fected						     **
373  |  ** 									     **
374  |  **   First Edition:	91/10/23					     **
375  |  ** 									     **
376  |  **   Parameters:	ClientData	 client_data			     **
377  |  **			Tcl_Interp	*interp		According Tcl interp.**
378  |  **			int		 argc		Number of arguments  **
379  |  **			char		*argv[]		Argument array	     **
380  |  ** 									     **
381  |  **   Result:		int	TCL_OK		Successfull completion	     **
382  |  **				TCL_ERROR	Any error		     **
383  |  ** 									     **
384  |  **   Attached Globals:	g_flags		These are set up accordingly before  **
385  |  **					this function is called in order to  **
386  |  **					control everything		     **
387  |  ** 									     **
388  |  ** ************************************************************************ **
389  |  ++++*/
390  | 
391  | int	cmdRemovePath(	ClientData	 client_data,
392  | 	       		Tcl_Interp	*interp,
393  | 	       		int		 argc,
394  | 	       		char		*argv[])
395  | {
396  |     char	*sw_marker = APP_SW_MARKER;	/** arbitrary default	     **/
397  |     char	**pathlist;			/** List of dirs	     **/
398  |     int		 numpaths;			/** number of dirs in path   **/
399  |     int		 x;				/** loop index		     **/
400  | 
401  | 
402  | #if WITH_DEBUGGING_CALLBACK
403  |     ErrorLogger( NO_ERR_START, LOC, _proc_cmdRemovePath, NULL);
404  | #endif
405  | 
406  |     /**
407  |      **   Check arguments. There should be give 3 args:
408  |      **     argv[0]  -  prepend/append/remove-path
409  |      **     argv[1]  -  varname
410  |      **     argv[2]  -  value
411  |      **/
412  |     if(argc != 3)
413  | 	if( OK != ErrorLogger(ERR_USAGE,LOC,argv[0],"path-variable directory",
414  | 	    NULL))
415  | 	    goto unwind0;
416  | 
417  |     /**
418  |      **  Display only ... ok, let's do so!
419  |      **/
420  |     if(g_flags & M_DISPLAY) {
421  | 	fprintf( stderr, "%s\t ", argv[ 0]);
422  | 	while( --argc)
423  | 	    fprintf( stderr, "%s ", *++argv);
424  | 	fprintf( stderr, "\n");
425  |         goto success0;
426  |     }
427  |   
428  |     /**
429  |      **  prepend or append. The default is append.
430  |      **/
431  |     if( ! strncmp( argv[0], "pre", 3))
432  | 	sw_marker = PRE_SW_MARKER;
433  |   
434  |     /**
435  |      ** For switch state3, we're looking to remove the markers.
436  |      **/
437  |     if( g_flags & M_SWSTATE3) 
438  | 	argv[2] = sw_marker;
439  | 
440  |     /**
441  |      **  Split the path into its components so each item can be removed
442  |      **  individually from the variable.
443  |      **/
444  |     if( !( pathlist = SplitIntoList( interp, argv[2], &numpaths)))
445  | 	goto unwind0;
446  | 
447  |     /**
448  |      ** Remove each item individually
449  |      **/
450  |     for( x = 0; x < numpaths; x++)
451  | 	if( TCL_OK != Remove_Path( interp, argv[1], pathlist[x], sw_marker))
452  | 	    goto unwind1;
453  | 
454  | #if WITH_DEBUGGING_CALLBACK
455  |     ErrorLogger( NO_ERR_END, LOC, _proc_cmdRemovePath, NULL);
456  | #endif
457  | 
458  |     /**
459  |      ** Free resources
460  |      **/
461  |     FreeList(pathlist, numpaths);
462  | 
463  | success0:
464  |     return( TCL_OK);			/** -------- EXIT (SUCCESS) -------> **/
465  | 
466  | unwind1:
467  |     FreeList(pathlist, numpaths);
468  | unwind0:
469  |     return( TCL_ERROR);			/** -------- EXIT (FAILURE) -------> **/
470  | 
471  | } /** End of 'cmdRemovePath' **/
472  | 
473  | /*++++
474  |  ** ** Function-Header ***************************************************** **
475  |  ** 									     **
476  |  **   Function:		Remove_Path					     **
477  |  ** 									     **
478  |  **   Description:	This function actually does the work of removing     **
479  |  **			the item from the path.  It is done this way to      **
480  |  **			support multiple items (often directories)	     **
481  |  **			separated by colons in the variable value.	     **
482  |  ** 									     **
483  |  **   First Edition:	2001/08/08					     **
484  |  ** 									     **
485  |  **   Parameters:	Tcl_Interp	*interp		According Tcl interp.**
486  |  **			char		*variable	Variable from which  **
487  |  **							to remove item	     **
488  |  **			char		*item		Item to remove	     **
489  |  **			char		*sw_marker	Switch marker	     **
490  |  ** 									     **
491  |  **   Result:		int	TCL_OK		Successfull completion	     **
492  |  **				TCL_ERROR	Any error		     **
493  |  ** 									     **
494  |  **   Attached Globals:	g_flags		These are set up accordingly before  **
495  |  **					this function is called in order to  **
496  |  **					control everything		     **
497  |  ** 									     **
498  |  ** ************************************************************************ **
499  |  ++++*/
500  | 
501  | static int	Remove_Path(	Tcl_Interp	*interp,
502  | 	       			char		*variable,
503  | 	       			char		*item,
504  | 				char		*sw_marker)
505  | {
506  |     char	*oldpath,			/** Old value of 'var'	     **/
507  |     		*tmppath,			/** Temp. buffer for 'var'   **/
508  |     		*searchpath,
509  | 		*startp=NULL, *endp=NULL;	/** regexp match endpts	     **/
510  |     int  	 start_offset = 0,		/** match offsets	     **/
511  |     		 end_offset = 0,
512  | 		 path_len = 0;			/** length of unmatched path **/
513  |     Tcl_RegExp	regexpPtr  = (Tcl_RegExp) NULL,
514  |     		begexpPtr  = (Tcl_RegExp) NULL,
515  |     		midexpPtr  = (Tcl_RegExp) NULL,
516  |     		endexpPtr  = (Tcl_RegExp) NULL,
517  |     		onlyexpPtr = (Tcl_RegExp) NULL,
518  |     		markexpPtr = (Tcl_RegExp) NULL;
519  | 
520  | #if WITH_DEBUGGING_CALLBACK
521  |     ErrorLogger( NO_ERR_START, LOC, _proc_Remove_Path, NULL);
522  | #endif
523  | 
524  |     /**
525  |      **  Get the current value of the "PATH" environment variable
526  |      **/
527  |     if( NULL == (tmppath = Tcl_GetVar2( interp, "env", variable,
528  | 	TCL_GLOBAL_ONLY))) {
529  | 	_TCLCHK(interp)
530  | 	return( TCL_OK);		/** -------- EXIT (SUCCESS) -------> **/
531  |     }    
532  | 
533  |     /**
534  |      **  We want to make a local copy of old path because we're going
535  |      **  to be butchering it and the environ one doesn't need to be
536  |      **  changed in this manner.
537  |      **  Put a \ in front of each . and + in the passed value to remove.
538  |      **/
539  |     if((char *) NULL == (oldpath = stringer(NULL,0, tmppath, NULL)))
540  | 	if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
541  | 	    goto unwind0;
542  |     path_len = strlen(oldpath);
543  |     cleanse_path( item, buffer, PATH_BUFLEN);
544  | 
545  |     /**
546  |      **  Now we need to find it in the path variable.  So, we create
547  |      **  space enough to build search expressions.
548  |      **/
549  |     if((char *) NULL == (searchpath = stringer(NULL,0,"^", buffer, ":", NULL)))
550  | 	if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
551  | 	    goto unwind1;
552  | 
553  |     /**
554  |      **  This section searches for the oldpath in the PATH variable.
555  |      **  There are four cases because the colons must be used as markers
556  |      **  in order to guarantee that a partial match isn't being found.
557  |      ** 
558  |      **  The start_offset and end_offset variables indicate how much to 
559  |      **  cut or not to cut off of each end of the located
560  |      **  string.  They differ for each case...beginning of
561  |      **  the path, middle of the path, end of the path, 
562  |      **  and for the only path.
563  |      ** 
564  |      **  This is to ensure the colons get cut off properly.
565  |      **/
566  | 
567  |     begexpPtr = Tcl_RegExpCompile(interp,  searchpath);
568  |     _TCLCHK(interp)
569  | 
570  |     if( Tcl_RegExpExec(interp, begexpPtr, oldpath, oldpath) > 0) {
571  | 	_TCLCHK(interp)
572  | 	regexpPtr = begexpPtr;
573  | 
574  | 	/**
575  | 	 **  Copy from the beginning of the startp to one
576  | 	 **  character before endp
577  | 	 **/
578  | 
579  | 	Tcl_RegExpRange(begexpPtr, 0, &startp, &endp);
580  | 	start_offset = 0; end_offset = -1;
581  | 	path_len -= (endp - startp - 1);
582  | 
583  |     } else {
584  | 
585  | 	*searchpath = ':';	/* :buffer: */
586  | 	midexpPtr = Tcl_RegExpCompile(interp, searchpath);
587  | 	_TCLCHK(interp)
588  | 
589  | 	if( Tcl_RegExpExec(interp, midexpPtr, oldpath, oldpath) > 0) {
590  | 	    _TCLCHK(interp)
591  | 	    regexpPtr = midexpPtr;
592  | 
593  | 	    /**
594  | 	     **  Copy from one character after stringp[0] to one
595  | 	     **  character before endp[0]
596  | 	     **/
597  | 
598  | 	    Tcl_RegExpRange(midexpPtr, 0, &startp, &endp);
599  | 	    start_offset = 1; end_offset = -1;
600  | 	    path_len -= (endp - startp - 2);
601  | 
602  | 	} else {
603  | 
604  | 	    searchpath[strlen(searchpath)-1] = '$';	/* :buffer$ */
605  | 	    endexpPtr = Tcl_RegExpCompile(interp, searchpath);
606  | 	    _TCLCHK(interp)
607  | 
608  | 	    if( Tcl_RegExpExec(interp, endexpPtr, oldpath, oldpath) > 0) {
609  | 		_TCLCHK(interp)
610  | 		regexpPtr = endexpPtr;
611  | 
612  | 		/**
613  | 		 **  Copy from one character after stringp[0] 
614  | 		 **  through endp[0]
615  | 		 **/
616  | 
617  | 		Tcl_RegExpRange(endexpPtr, 0, &startp, &endp);
618  | 		start_offset = 0; end_offset = 0;
619  | 		path_len -= (endp - startp - 1);
620  | 
621  | 	    } else {
622  | 
623  | 		*searchpath = '^';	/* ^buffer$ */
624  | 		onlyexpPtr = Tcl_RegExpCompile(interp, searchpath);
625  | 		_TCLCHK(interp)
626  | 
627  | 		if(Tcl_RegExpExec(interp, onlyexpPtr, oldpath, oldpath) > 0) {
628  | 		    _TCLCHK(interp)
629  | 
630  | 		    /**
631  | 		     **  In this case, I should go ahead and unset the variable
632  | 		     **  from the environment because I'm removing the very last
633  | 		     **  path.
634  | 		     **
635  | 		     **  First I'm going to clear the variable from the
636  | 		     **  setenvHashTable just in case its already been altered
637  | 		     **  and had a significant value at the time. It's very
638  | 		     **  possible that I'm removing the only two or three paths
639  | 		     **  from this variable. If that's the case, then all the
640  | 		     **  earlier paths were marked for output in this hashTable.
641  | 		     **
642  | 		     **  Secondly, I actually mark the the environment variable
643  | 		     **  to be unset when output.
644  | 		     **/
645  | 		    clear_hash_value( setenvHashTable, variable);
646  | 		    moduleUnsetenv( interp, variable);
647  | 
648  | 		    /**
649  | 		     **  moduleUnsetenv doesn't unset the variable in the Tcl
650  | 		     **  space because the $env variable might need to be
651  | 		     **  used again in the modulefile for locating other
652  | 		     **  paths.  BUT, since this was a path-type environment
653  | 		     **  variable, the user is expecting this to be empty
654  | 		     **  after removing the only remaining path.  So, I set
655  | 		     **  the variable empty here.
656  | 		     **/
657  | 		    (void) Tcl_SetVar2( interp, "env", variable, "",
658  | 			TCL_GLOBAL_ONLY);
659  | 		    _TCLCHK(interp)
660  | 		    null_free((void *) &searchpath);
661  | 		    goto success1;
662  | 		}
663  | 	    }
664  | 	}
665  |     }
666  |     null_free((void *) &searchpath);
667  | 
668  |     /**
669  |      **  If I couldn't find it assume it wasn't there
670  |      **  and return.
671  |      **/
672  |     if( !regexpPtr)
673  | 	goto success1;
674  | 
675  |     markexpPtr = Tcl_RegExpCompile(interp, sw_marker);
676  |     _TCLCHK(interp)
677  | 
678  |     /**
679  |      **  In state1, we're actually replacing old paths with
680  |      **  the markers for future appends and prepends.
681  |      **  
682  |      **  We only want to do this once to mark the location
683  |      **  the module was formed around.
684  |      **/
685  |     if((g_flags & M_SWSTATE1) && ! (Tcl_RegExpExec(interp,
686  | 	 markexpPtr, oldpath, oldpath) > 0)) {
687  | 
688  | 	/**
689  | 	 **  If I don't have enough space to replace the oldpath with the
690  | 	 **  marker, then I must create space for the marker and thus
691  | 	 **  recreate the PATH variable.
692  | 	 **/
693  | 	char* newenv;
694  | 	int newlen = path_len + strlen(sw_marker) + 1;
695  |       
696  | 	if((char *) NULL==(newenv = stringer(NULL, newlen ,NULL) ))
697  | 	    if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
698  | 		goto unwind1;
699  | 
700  | 	*(startp + start_offset) = '\0';
701  | 
702  | 	if(*(endp + end_offset) == '\0') {
703  | 	    (void) stringer(newenv, newlen, oldpath,":",sw_marker,NULL);
704  | 	} else {
705  | 	    (void) stringer(newenv, newlen, oldpath,sw_marker,
706  | 		endp + end_offset, NULL);
707  | 	}
708  | 
709  | 	/**
710  | 	 **  Just store the value with the marker into the environment.  I'm not
711  | 	 **  checking if it changed because the only case that a PATH-type
712  | 	 **  variable will be unset is when I remove the only path remaining in
713  | 	 **  the variable.  So, using moduleSetenv is not necessary here.
714  | 	 **/
715  | 	Tcl_SetVar2( interp, "env", variable, newenv, TCL_GLOBAL_ONLY);
716  | 	_TCLCHK(interp)
717  | 	null_free((void *) &newenv);
718  | 
719  |     } else {  /** SW_STATE1 **/
720  | 
721  | 	/**
722  | 	 **  We must be in SW_STATE3 or not in SW_STATE at all.
723  | 	 **  Removing the marker should be just like removing any other path.
724  | 	 **/
725  | 	strcpy( startp + start_offset, endp);
726  | 
727  | 	/**
728  | 	 **  Cache the set.  Clear the variable from the unset table just
729  | 	 **  in case it was previously unset.
730  | 	 **/
731  | 	store_hash_value( setenvHashTable, variable, oldpath);
732  | 	clear_hash_value( unsetenvHashTable, variable);
733  | 
734  | 	/**
735  | 	 **  Store the new PATH value into the environment.
736  | 	 **/
737  | 	Tcl_SetVar2( interp, "env", variable, oldpath, TCL_GLOBAL_ONLY);
738  | 	_TCLCHK(interp)
739  | 
740  |     }  /** ! SW_STATE1 **/
741  | 
742  | #if WITH_DEBUGGING_CALLBACK
743  |     ErrorLogger( NO_ERR_END, LOC, _proc_cmdRemovePath, NULL);
744  | #endif
745  | 
746  |     /**
747  |      **  Free what has been used and return on success
748  |      **/
749  | success1:
750  |     null_free((void *) &oldpath);
751  | success0:
752  |     return( TCL_OK);		/** -------- EXIT (SUCCESS) -------> **/
753  | 
754  | unwind1:
755  |     null_free((void *) &oldpath);
756  | unwind0:
757  |     return( TCL_ERROR);		/** -------- EXIT (FAILURE) -------> **/
758  | 
759  | } /** End of 'Remove_Path' **/