1 /*****
2  ** ** Module Header ******************************************************* **
3  ** 									     **
4  **   Modules Revision 3.0						     **
5  **   Providing a flexible user environment				     **
6  ** 									     **
7  **   File:		ModuleCmd_Init.c				     **
8  **   First Edition:	1991/10/23					     **
9  ** 									     **
10  **   Authors:	John Furlan, jlf@behere.com				     **
11  **		Jens Hamisch, jens@Strawberry.COM			     **
12  ** 									     **
13  **   Description:	Routines that act on a user's "dot" startup files to **
14  **			add, remove, and list modulefiles to/from/in their   **
15  **			startup files.					     **
16  ** 									     **
17  **   Exports:		ModuleCmd_Init					     **
18  ** 									     **
19  **   Notes:								     **
20  ** 									     **
21  ** ************************************************************************ **
22  ****/
23 
24 /** ** Copyright *********************************************************** **
25  ** 									     **
26  ** Copyright 1991-1994 by John L. Furlan.                      	     **
27  ** see LICENSE.GPL, which must be provided, for details		     **
28  ** 									     **
29  ** ************************************************************************ **/
30 
31 static char Id[] = "@(#)$Id: cb3bc99e1a3ddb1538804d806b0579e6fffda19e $";
32 static void *UseId[] = { &UseId, Id };
33 
34 /** ************************************************************************ **/
35 /** 				      HEADERS				     **/
36 /** ************************************************************************ **/
37 
38 #include "modules_def.h"
39 
40 /** ************************************************************************ **/
41 /** 				  LOCAL DATATYPES			     **/
42 /** ************************************************************************ **/
43 
44 /** not applicable **/
45 
46 /** ************************************************************************ **/
47 /** 				     CONSTANTS				     **/
48 /** ************************************************************************ **/
49 
50 /** not applicable **/
51 
52 /** ************************************************************************ **/
53 /**				      MACROS				     **/
54 /** ************************************************************************ **/
55 
56 /** not applicable **/
57 
58 /** ************************************************************************ **/
59 /** 				    LOCAL DATA				     **/
60 /** ************************************************************************ **/
61 
62 static	char	module_name[] = "ModuleCmd_Init.c";	/** File name of this module **/
63 
64 #if WITH_DEBUGGING_MODULECMD
65 static	char	_proc_ModuleCmd_Init[] = "ModuleCmd_Init";
66 #endif
67 
68 /** ************************************************************************ **/
69 /**				    PROTOTYPES				     **/
70 /** ************************************************************************ **/
71 
72 /** not applicable **/
73 
74 /** ************************************************************************ **/
75 /**				    STATIC FUNCTIONS			     **/
76 /** ************************************************************************ **/
77 
78 /* Handles the output of a substring where the start & ending positions
79  * are given - if either is NULL then just do nothing and return -1
80  * all other cases it returns 0
81  */
out_substr(FILE * stream,char * start,char * end)82 static int out_substr(FILE *stream, char *start, char *end) {
83 	char save;
84 
85 	if (!start || !end) return -1;
86 
87 	save = *end;
88 	*end = '\0';
89 	fputs(start, stream);
90 	*end = save;
91 	return 0;
92 }
93 
94 
95 /*++++
96  ** ** Function-Header ***************************************************** **
97  ** 									     **
98  **   Function:		ModuleCmd_Init					     **
99  ** 									     **
100  **   Description:	Execution of the module-command 'init'		     **
101  ** 									     **
102  **   First Edition:	1991/10/23					     **
103  ** 									     **
104  **   Parameters:	Tcl_Interp	*interp		Attached Tcl Interp. **
105  **			int		 argc		Number of arguments  **
106  **			char 		*argv[]		Argument list	     **
107  ** 									     **
108  **   Result:		int	TCL_ERROR	Failure			     **
109  **				TCL_OK		Successful operation	     **
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 
ModuleCmd_Init(Tcl_Interp * interp,int argc,char * argv[])118 int	ModuleCmd_Init(	Tcl_Interp	*interp,
119 	       		int            	 argc,
120 	       		char		*argv[])
121 {
122     char	 *home_pathname,
123 		 *home_pathname2,
124 		 **shell_startups;	/** A list of all startup files our **/
125 					/** invoking shell will source       **/
126     int		  max_home_path = MOD_BUFSIZE + 40;
127     char	**modlist,
128 		 *home,
129 		 *buffer,
130 		  ch,
131 		 *startp, *endp,
132 		 *Modcmd =
133 	"^([ \t]*module[ \t]+)(load|add)[ \t]+([^#\n]*)([#.\n]*)";
134     static Tcl_Obj	 *modcmdObj;
135     static Tcl_RegExp	  modcmdPtr;
136     FILE	 *fileptr, *newfileptr;
137     int		  i, j,
138 		  found_module_command = 0,
139 		  found_modload_flag = 0,
140 		  shell_num = 0,
141 		  final_list_num = 0,
142 		  nummods, bufsiz = 8192,
143 		  new_file,
144 		  homelen, home_end, path_end;
145 
146 #if WITH_DEBUGGING_MODULECMD
147     ErrorLogger(NO_ERR_START, LOC, _proc_ModuleCmd_Init, NULL);
148 #endif
149 
150     /**
151      **  If called with no arguments and the flags don't say that there's some-
152      **  thing to do - exit now!
153      **/
154     if (argc < 1 && !(g_flags & (M_DISPLAY | M_CLEAR)))
155 	goto success0;
156 
157     if (!modcmdObj)
158 	modcmdObj = Tcl_NewStringObj(Modcmd,strlen(Modcmd));
159     if (!modcmdPtr)
160 	modcmdPtr = Tcl_GetRegExpFromObj(interp,modcmdObj,TCL_REG_ADVANCED);
161     /**
162      **  Parameter check for the initswitch command
163      **/
164     if (g_flags & M_SWITCH) {
165 	argc--;
166 	if (argc != 1)
167 	    if (OK != ErrorLogger(ERR_USAGE, LOC,
168 				  "initswitch oldmodule newmodule", NULL))
169 		goto unwind0;
170     }
171 
172     /**
173      **  Where's my HOME?
174      **/
175     if ((char *) NULL == (home = (char *) getenv("HOME")))
176 	if (OK != ErrorLogger(ERR_HOME, LOC, NULL))
177 	    goto unwind1;
178 
179     /**
180      **  Put HOME into a buffer and store a slash where the end of HOME is
181      **  for quick concatination of the shell startup files.
182      **/
183     homelen = strlen(home) + 40;
184     if ((char *) NULL ==
185 	(home_pathname = stringer(NULL, homelen, home, "/", NULL)))
186 	if (OK != ErrorLogger(ERR_STRING, LOC, NULL))
187 	    goto unwind0;
188 
189     if ((char *) NULL == (home_pathname2 = stringer(NULL, homelen, NULL)))
190 	if (OK != ErrorLogger(ERR_STRING, LOC, NULL))
191 	    goto unwind1;
192 
193     home_end = strlen(home_pathname);
194 
195     /**
196      **  Allocate a buffer for fgets ...
197      **/
198     if (NULL == (buffer = stringer(NULL, bufsiz, NULL)))
199 	if (OK != ErrorLogger(ERR_STRING, LOC, NULL))
200 	    goto unwind2;
201 
202     /**
203      **  Scan all startup files related to the current invoking shell
204      **/
205     if ((char **) NULL == (shell_startups = SetStartupFiles(shell_name)))
206 	goto unwind3;
207 
208     while (shell_startups[shell_num]) {
209 	new_file = 1;
210 	found_modload_flag = 0;
211 
212 	if ((char *) NULL == stringer(home_pathname + home_end, 40,
213 				      shell_startups[shell_num], NULL))
214 	    if (OK != ErrorLogger(ERR_STRING, LOC, NULL))
215 		goto unwind3;
216 
217 	if (NULL == (fileptr = fopen(home_pathname, "r")))
218 	    goto unwhile0; 	/** while( shell_startups) ...	     **/
219 
220 	/**
221 	 **  ... when the startup file exists ...
222 	 **  open a new startupfile with the extension -NEW for output
223 	 **/
224 	path_end = strlen(home_pathname);
225 	if ((char *) NULL == stringer(home_pathname + path_end,
226 				      homelen - path_end, "-NEW", NULL))
227 	    if (OK != ErrorLogger(ERR_STRING, LOC, NULL))
228 		goto unwind3;
229 
230 	if (!(g_flags & M_DISPLAY) &&
231 	    ((FILE *) NULL == (newfileptr = fopen(home_pathname, "w")))) {
232 	    (void) ErrorLogger(ERR_OPEN, LOC, home_pathname, "init", NULL);
233 	    goto unwhile0; 	/** while( shell_startups) ...	     **/
234 	}
235 
236 	/**
237 	 **  Seek for a modules load|add command within the shell startup file
238 	 **  Copy the shell input file to the new one until the magic cookie
239 	 **  is found.
240 	 **/
241 	while (fgets(buffer, bufsiz, fileptr)) {
242 	    if (Tcl_RegExpExec(interp, modcmdPtr, buffer, buffer)) {
243 		found_modload_flag = 1;
244 		/**
245 		 **  ... module load|add found ...
246 		 **/
247 		found_module_command = 1;
248 
249 		/* print out the "module" part */
250 		(void) Tcl_RegExpRange(modcmdPtr, 1,
251 				       (CONST84 char **) &startp,
252 				       (CONST84 char **) &endp);
253 		if (!(g_flags & M_DISPLAY))
254 		    (void) out_substr(newfileptr, startp, endp);
255 
256 		/* print out the "add/load" part */
257 		(void) Tcl_RegExpRange(modcmdPtr, 2,
258 				       (CONST84 char **) &startp,
259 				       (CONST84 char **) &endp);
260 		if (!(g_flags & M_DISPLAY))
261 		    (void) out_substr(newfileptr, startp, endp);
262 
263 		if (!(g_flags & M_CLEAR)) {
264 		    /* look at the "module list" part */
265 		    (void) Tcl_RegExpRange(modcmdPtr, 3,
266 					   (CONST84 char **) &startp,
267 					   (CONST84 char **) &endp);
268 		    /* save the end character & set to 0 */
269 		    if (endp) {
270 			ch = *endp;
271 			*endp = '\0';
272 		    }
273 
274 		    if ((char **) NULL ==
275 		    (modlist = SplitIntoList(interp, startp, &nummods," \t")))
276 			continue; /** while(fgets) **/
277 
278 		    /* restore the list end character */
279 		    if (endp)
280 			*endp = ch;
281 
282 		    if (g_flags & M_DISPLAY) {
283 			if (modlist[0] == NULL) {
284 			    fprintf(stderr,
285 	    "\nNo modules are loaded in %s's initialization file "
286 				    "$HOME/%s\n", shell_name,
287 				    shell_startups[shell_num]);
288 			} else {
289 			    if (new_file) {
290 				fprintf(stderr,
291 	    "\n%s initialization file $HOME/%s loads modules:\n\t",
292 				    shell_name, shell_startups[shell_num]);
293 		    		(void) out_substr(stderr, startp, endp);
294 				fputs("\n",stderr);
295 				new_file = 0;
296 			    } else {
297 				fputs("\t",stderr);
298 		    		(void) out_substr(stderr, startp, endp);
299 				fputs("\n",stderr);
300 			    }
301 			}
302 
303 			FreeList(modlist, nummods);
304 			continue; /** while(fgets) **/
305 		    }
306 
307 		    for (i = 0; i < argc; i++) {
308 		    /**
309 		     ** Search through the modlist of modules that are currently
310 		     ** in the ~/.startup.  If one is found, it handles removing
311 		     ** it, switching it, etc.
312 		     **/
313 			for (j = 0; j < nummods; j++) {
314 			    if (modlist[j] && !strcmp(modlist[j], argv[i])) {
315 				if (g_flags & (M_LOAD | M_REMOVE)) {
316 				/**
317 				 **  If removing, adding, prepending it,
318 				 **  NULL it off the list.
319 				 **/
320 				    if (g_flags & M_REMOVE)
321 					fprintf(stderr, "Removed %s\n",
322 						modlist[j]);
323 				    else if ((g_flags & M_LOAD)
324 					     && !(g_flags & M_PREPEND))
325 					fprintf(stderr, "Moving %s to end\n",
326 						modlist[j]);
327 				    else if (g_flags & M_PREPEND)
328 					fprintf(stderr,
329 						"Moving %s to beginning\n",
330 						modlist[j]);
331 				    null_free((void *) (modlist + j));
332 
333 				} else if (g_flags & M_SWITCH) {
334 				/**
335 				 **  If switching it, swap the old string with
336 				 **  the new string in the list.
337 				 **/
338 				    fprintf(stderr, "Switching %s to %s\n",
339 					    modlist[j], argv[i + 1]);
340 				    null_free((void *) (modlist + j));
341 				    modlist[j] = strdup(argv[i + 1]);
342 				}
343 			    } /** if **/
344 			} /** for(j) **/
345 		    } /** for(i) **/
346 		/**
347 		 **  Ok, if we're removing it, prepending it, or switching it,
348 		 **  the modlist contains what needs to be put where...
349 		 **/
350 		    if ((new_file) && (g_flags & M_PREPEND)) {
351 		    /**
352 		     **  PREPENDING
353 		     **/
354 			for (i = 0; i < argc; i++) {
355 			    fprintf(newfileptr, " %s", argv[i]);
356 			    final_list_num++;
357 			}
358 		    }
359 
360 		    if ((g_flags & (M_LOAD | M_REMOVE | M_SWITCH))) {
361 		    /**
362 		     **  DUMP LIST
363 		     **/
364 			for (j = 0; j < nummods; j++) {
365 			    if (modlist[j]) {
366 				fprintf(newfileptr, " %s", modlist[j]);
367 				final_list_num++;
368 			    }
369 			}
370 		    }
371 		    if ((new_file) && (g_flags & M_LOAD)
372 		    && !(g_flags & M_PREPEND)) {
373 		    /**
374 		     **  ADDING
375 		     **/
376 			for (i = 0; i < argc; i++) {
377 			    fprintf(newfileptr, " %s", argv[i]);
378 			    final_list_num++;
379 			}
380 		    }
381 		    /* always place a null if an empty list */
382 		    if (!final_list_num)
383 			fprintf(newfileptr, " %s", "null");
384 
385 		    FreeList(modlist, nummods);
386 
387 		} else { /** if( M_CLEAR) **/
388 		/**
389 		 ** Clear out the list, but leave a "null"
390 		 **/
391 		    fprintf(newfileptr, " %s", "null");
392 		}
393 	/**
394 	 **  Restore any comments at the end of the line...
395 	 **/
396 		(void) Tcl_RegExpRange(modcmdPtr, 4,
397 				       (CONST84 char **) &startp,
398 				       (CONST84 char **) &endp);
399 		(void) out_substr(newfileptr, startp, endp);
400 		new_file = 0;
401 	    } else {		/* not module load line */
402 		if (!(g_flags & M_DISPLAY))
403 		    fputs(buffer, newfileptr);
404 	    }
405 	} /** while (fgets) **/
406 	if (g_flags & M_DISPLAY) {
407 	    fputs("\n",stderr);
408 	}
409 
410 	if (!found_modload_flag) {
411 	    /**
412 	     **  If not found...
413 	     **/
414 	    if (EOF == fclose(fileptr))
415 		if (OK != ErrorLogger(ERR_CLOSE, LOC, home_pathname, NULL))
416 		    goto unwind3;
417 
418 	    if (!(g_flags & M_DISPLAY)) {
419 		if (EOF == fclose(newfileptr))
420 		    if (OK != ErrorLogger(ERR_CLOSE, LOC, home_pathname, NULL))
421 			goto unwind3;
422 
423 		if (0 > unlink(home_pathname))
424 		    if (OK != ErrorLogger(ERR_UNLINK, LOC, home_pathname, NULL))
425 			goto unwind3;
426 	    }
427 	} else {		/* found_modload_flag */
428 	    /**
429 	     **  Don't need these any more
430 	     **/
431 	    if (EOF == fclose(fileptr))
432 		if (OK != ErrorLogger(ERR_CLOSE, LOC, home_pathname, NULL))
433 		    goto unwind3;
434 
435 	    if (g_flags & M_DISPLAY)
436 	        goto unwhile0; 	/** while( shell_startups) ...	     **/
437 
438 	    if (EOF == fclose(newfileptr))
439 		if (OK != ErrorLogger(ERR_CLOSE, LOC, home_pathname, NULL))
440 		    goto unwind3;
441 
442 	    /**
443 	     **  Truncate -NEW from home_pathname and Create a -OLD name
444 	     **  Move ~/.startup to ~/.startup-OLD
445 	     **/
446 	    home_pathname[path_end] = '\0';
447 
448 	    if ((char *) NULL == stringer(home_pathname2, homelen,
449 					  home_pathname, "-OLD", NULL))
450 		if (OK != ErrorLogger(ERR_STRING, LOC, NULL))
451 		    goto unwind3;
452 
453 	    if (0 > rename(home_pathname, home_pathname2))
454 		if (OK !=
455 		    ErrorLogger(ERR_RENAME, LOC, home_pathname, home_pathname2,
456 				NULL))
457 		    goto unwind3;
458 
459 	    /**
460 	     **  Create a -NEW name
461 	     **  Move ~/.startup-NEW to ~/.startup
462 	     **/
463 	    if ((char *) NULL == stringer(home_pathname2, homelen,
464 					  home_pathname, "-NEW", NULL))
465 		if (OK != ErrorLogger(ERR_STRING, LOC, NULL))
466 		    goto unwind3;
467 
468 	    if (0 > rename(home_pathname2, home_pathname)) {
469 		if (OK !=
470 		    ErrorLogger(ERR_RENAME, LOC, home_pathname2, home_pathname,
471 				NULL)) {
472 		    /**
473 		     **  Put the -OLD one back if I can't rename it
474 		     **/
475 		    if ((char *) NULL == stringer(home_pathname2, homelen,
476 						  home_pathname, "-OLD", NULL))
477 			if (OK != ErrorLogger(ERR_STRING, LOC, NULL))
478 			    goto unwind3;
479 
480 		    if (0 > rename(home_pathname2, home_pathname))
481 			ErrorLogger(ERR_RENAME, LOC, home_pathname2,
482 				    home_pathname, NULL);
483 
484 		    goto unwind3;
485 		}
486 	    }
487 
488 	    /**
489 	     **  So far we're successful so
490 	     **  Create a -OLD name
491 	     **  Unlink ~/.startup-OLD
492 	     **/
493 	    if ((char *) NULL == stringer(home_pathname2, homelen,
494 					  home_pathname, "-OLD", NULL))
495 		if (OK != ErrorLogger(ERR_STRING, LOC, NULL))
496 		    goto unwind3;
497 
498 	    if ((g_flags & (M_CLEAR | M_LOAD | M_REMOVE | M_SWITCH)))
499 		if (0 > unlink(home_pathname2)) {
500 		    ErrorLogger(ERR_UNLINK, LOC, home_pathname2, NULL);
501 		    goto unwind3;
502 		}
503 	}
504     unwhile0:
505 	shell_num++;
506     } /** while( shell_startups) **/
507 
508     /**
509      **  Free up internal I/O buffers
510      **/
511     null_free((void *) &buffer);
512 
513     if (!found_module_command)
514 	if (OK != ErrorLogger(ERR_INIT_STUP, LOC, shell_name, NULL))
515 	    goto unwind2;
516 
517 #if WITH_DEBUGGING_MODULECMD
518     ErrorLogger(NO_ERR_END, LOC, _proc_ModuleCmd_Init, NULL);
519 #endif
520 
521     /**
522      **  Free up memory
523      **/
524     null_free((void *) &home_pathname2);
525     null_free((void *) &home_pathname);
526 
527 success0:
528     return (TCL_OK);			/** -------- EXIT (SUCCESS) -------> **/
529 
530 unwind3:
531     null_free((void *) &buffer);
532 unwind2:
533     null_free((void *) &home_pathname2);
534 unwind1:
535     null_free((void *) &home_pathname);
536 unwind0:
537     return (TCL_ERROR);			/** -------- EXIT (FAILURE) -------> **/
538 
539 } /** end of 'ModuleCmd_Init' **/
540