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