1/*
2 * Copyright (c) 1990 Carnegie Mellon University
3 * All Rights Reserved.
4 *
5 * Permission to use, copy, modify and distribute this software and its
6 * documentation is hereby granted, provided that both the copyright
7 * notice and this permission notice appear in all copies of the
8 * software, derivative works or modified versions, and any portions
9 * thereof, and that both notices appear in supporting documentation.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND CARNEGIE MELLON UNIVERSITY
12 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
13 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT
14 * SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY SPECIAL, DIRECT,
15 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
16 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
17 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 *
20 * Users of this software agree to return to Carnegie Mellon any
21 * improvements or extensions that they make and grant Carnegie the
22 * rights to redistribute these changes.
23 *
24 * Export of this software is permitted only after complying with the
25 * regulations of the U.S. Deptartment of Commerce relating to the
26 * Export of Technical Data.
27 */
28/*
29 *  setpath --- smart interface for setting path variables
30 *
31 *  usage:	setpath(paths, cmds, localsyspath, dosuffix, printerrors)
32 *		char **paths, **cmds, *localsyspath;
33 *		int dosuffix, printerrors;
34 *
35 *  The 'paths' argument is a list of pointers to path lists of the
36 *  form "name=value" where name is the name of the path and value
37 *  is a colon separated list of directories.  There can never be
38 *  more than MAXDIRS (64) directories in a path.
39 *
40 *  The 'cmds' argument may be a sequence of any of the following:
41 *	-r			reset path to default
42 *	-i newpath		insert newpath before localsyspath
43 *	-ia oldpath newpath	insert newpath after oldpath
44 *	-ib oldpath newpath	insert newpath before oldpath
45 *	-i# newpath		insert newpath at position #
46 *	-d oldpath		delete oldpath
47 *	-d#			delete path at position #
48 *	-c oldpath newpath	change oldpath to newpath
49 *	-c# newpath		change position # to newpath
50 *
51 *  The "-i newpath" command is equivilent to "-ib 'localsyspath' newpath".
52 *
53 *  If 'dosuffix' is true, the appropriate suffix will be added to
54 *  all command operands for any system path in 'paths'.
55 *
56 *  Both of the 'paths' and 'cmds' lists are terminated by a NULL
57 *  entry.
58 *
59 *  if 'printerrors' is true, setpath will printf error diagnostics.
60 *
61 *  WARNING !!!: Under no circumstances should anyone change this
62 *  module without fully understanding the impact on the C shell.
63 *  The C shell has it's own malloc and printf routines and this
64 *  module was carefully written taking that into account.  Do not
65 *  use any stdio routines from this module except printf.
66 *
67 **********************************************************************
68 * HISTORY
69 *
70 * Revision 1.4  90/12/11  17:58:44  mja
71 * 	Add copyright/disclaimer for distribution.
72 *
73 * 05-Jun-88  Glenn Marcy (gm0w) at Carnegie-Mellon University
74 *	Make all non-entry points static.
75 *
76 * 30-Apr-88  Glenn Marcy (gm0w) at Carnegie-Mellon University
77 *	Added -r switch to reset paths to their default values.
78 *
79 * 06-Jan-86  Glenn Marcy (gm0w) at Carnegie-Mellon University
80 *	Created from old setpath program for the shell.
81 *
82 **********************************************************************
83 */
84#include "sh.h"
85
86#ifdef MACH
87
88#define MAXDIRS 64		/* max directories on a path */
89#ifndef NULL
90# define NULL 0
91#endif
92
93static int npaths;		/* # pathlist arguments */
94
95static struct pelem {
96    struct pelem *pnext;	/* pointer to next path */
97    char *pname;		/* name of pathlist */
98    char *psuf;			/* suffix for pathlist */
99    char *pdef;			/* default for pathlist */
100    int pdirs;			/* # directories on each pathlist */
101    char *pdir[MAXDIRS];	/* directory names for each pathlist */
102} *pathhead = NULL;
103
104static struct {
105    char *name;
106    char *suffix;
107    char *defalt;
108} syspath[] = {
109    "PATH",	"/bin",		":/usr/ucb:/bin:/usr/bin",
110    "CPATH",	"/include",	":/usr/include",
111    "LPATH",	"/lib",		":/lib:/usr/lib",
112    "MPATH",	"/man",		":/usr/man",
113    "EPATH",	"/maclib",	"",
114    0, 0, 0
115};
116
117static int sflag;
118static int eflag;
119
120#define INVALID { \
121	if (eflag) xprintf(CGETS(10, 1, \
122				 "setpath: invalid command '%s'.\n"), cmd); \
123	freepaths(); \
124	return(-1); \
125}
126
127#define TOOFEW { \
128	if (eflag) xprintf(CGETS(10, 2, \
129		 "setpath: insufficient arguments to command '%s'.\n"), cmd); \
130	freepaths(); \
131	return(-1); \
132}
133
134static int initpaths	(char **);
135static void savepaths	(char **);
136static void freepaths	(void);
137static void tcsh_rcmd	(char *);
138static void icmd	(char *, char *);
139static void iacmd	(char *, char *);
140static void ibcmd	(char *, char *);
141static void incmd	(char *, int);
142static void insert	(struct pelem *, int, char *);
143static void dcmd	(char *);
144static void dncmd	(int);
145static void delete	(struct pelem *, int);
146static void ccmd	(char *, char *);
147static void cncmd	(char *, int);
148static void change	(struct pelem *, int, char *);
149static int locate	(struct pelem *, char *);
150
151
152
153int
154setpath(char **paths, char **cmds, char *localsyspath, int dosuffix,
155	int printerrors)
156{
157    char *cmd, *cmd1, *cmd2;
158    int ncmd;
159
160    sflag = dosuffix;
161    eflag = printerrors;
162    if (initpaths(paths) < 0)
163	return(-1);
164    if (npaths == 0)
165	return(0);
166    for (ncmd = 0; cmd = cmds[ncmd]; ncmd++) {
167	if (cmd[0] != '-')
168	    INVALID;
169	cmd1 = cmds[ncmd+1];
170	cmd2 = cmds[ncmd+2];
171	switch (cmd[1]) {
172	case 'r':
173	    if (cmd[2] != '\0')
174		INVALID;
175	    tcsh_rcmd(localsyspath);
176	    break;
177	case 'i':
178	    if (cmd[2] == '\0') {
179		ncmd++;
180		if (cmd1 == NULL) TOOFEW;
181		icmd(cmd1, localsyspath);
182	    } else if (isdigit(cmd[2])) {
183		ncmd++;
184		if (cmd1 == NULL) TOOFEW;
185		incmd(cmd1, atoi(cmd+2));
186	    } else if (cmd[3] != '\0' || (cmd[2] != 'a' && cmd[2] != 'b')) {
187		INVALID;
188	    } else {
189		ncmd += 2;
190		if (cmd1 == NULL || cmd2 == NULL) TOOFEW;
191		if (cmd[2] == 'a')
192		    iacmd(cmd1, cmd2);
193		else
194		    ibcmd(cmd1, cmd2);
195	    }
196	    break;
197	case 'd':
198	    if (cmd[2] == '\0') {
199		ncmd++;
200		if (cmd1 == NULL) TOOFEW;
201		dcmd(cmd1);
202	    } else if (isdigit(cmd[2]))
203		dncmd(atoi(cmd+2));
204	    else {
205		INVALID;
206	    }
207	    break;
208	case 'c':
209	    if (cmd[2] == '\0') {
210		ncmd += 2;
211		if (cmd1 == NULL || cmd2 == NULL) TOOFEW;
212		ccmd(cmd1, cmd2);
213	    } else if (isdigit(cmd[2])) {
214		ncmd++;
215		if (cmd1 == NULL) TOOFEW;
216		cncmd(cmd1, atoi(cmd+2));
217	    } else {
218		INVALID;
219	    }
220	    break;
221	default:
222	    INVALID;
223	}
224    }
225    savepaths(paths);
226    freepaths();
227    return(0);
228}
229
230static int
231initpaths(char **paths)
232{
233    char *path, *val, *p, *q;
234    int i, done;
235    struct pelem *pe, *pathend;
236
237    freepaths();
238    for (npaths = 0; path = paths[npaths]; npaths++) {
239	val = index(path, '=');
240	if (val == NULL) {
241	    if (eflag)
242		xprintf(CGETS(10, 3,
243			      "setpath: value missing in path '%s'\n"), path);
244	    freepaths();
245	    return(-1);
246	}
247	*val++ = '\0';
248	pe = xmalloc(sizeof(struct pelem));
249	setzero(pe, sizeof(struct pelem));
250	if (pathhead == NULL)
251	    pathhead = pathend = pe;
252	else {
253	    pathend->pnext = pe;
254	    pathend = pe;
255	}
256	p = strsave(path);
257	pe->pname = p;
258	pe->psuf = "";
259	pe->pdef = "";
260	for (i = 0; syspath[i].name; i++)
261	    if (strcmp(pe->pname, syspath[i].name) == 0) {
262		pe->psuf = syspath[i].suffix;
263		pe->pdef = syspath[i].defalt;
264		break;
265	    }
266	q = val;
267	for (;;) {
268	    q = index(p = q, ':');
269	    done = (q == NULL);
270	    if (!done)
271		*q++ = '\0';
272	    p = strsave(p);
273	    pe->pdir[pe->pdirs] = p;
274	    pe->pdirs++;
275	    if (done)
276		break;
277	}
278    }
279    return(0);
280}
281
282static void
283savepaths(char **paths)
284{
285    char *p, *q;
286    int npath, i, len;
287    struct pelem *pe;
288
289    for (npath = 0, pe = pathhead; pe; npath++, pe = pe->pnext) {
290	len = strlen(pe->pname) + 1;
291	if (pe->pdirs == 0)
292	    len++;
293	else for (i = 0; i < pe->pdirs; i++)
294	    len += strlen(pe->pdir[i]) + 1;
295	p = xmalloc((unsigned)len);
296	paths[npath] = p;
297	for (q = pe->pname; *p = *q; p++, q++);
298	*p++ = '=';
299	if (pe->pdirs != 0) {
300	    for (i = 0; i < pe->pdirs; i++) {
301		for (q = pe->pdir[i]; *p = *q; p++, q++);
302		*p++ = ':';
303	    }
304	    p--;
305	}
306	*p = '\0';
307    }
308}
309
310static void
311freepaths(void)
312{
313    char *p;
314    int i;
315    struct pelem *pe;
316
317    if (npaths == 0 || pathhead == NULL)
318	return;
319    while (pe = pathhead) {
320	if (pe->pname) {
321	    for (i = 0; i < pe->pdirs; i++) {
322		if (pe->pdir[i] == NULL)
323		    continue;
324		p = pe->pdir[i];
325		pe->pdir[i] = NULL;
326		xfree((ptr_t) p);
327	    }
328	    pe->pdirs = 0;
329	    p = pe->pname;
330	    pe->pname = NULL;
331	    xfree((ptr_t) p);
332	}
333	pathhead = pe->pnext;
334	xfree((ptr_t) pe);
335    }
336    npaths = 0;
337}
338
339/***********************************************
340 ***    R E S E T   A   P A T H N A M E    ***
341 ***********************************************/
342
343static void
344tcsh_rcmd(char *localsyspath)	/* reset path with localsyspath */
345{
346    int n, done;
347    char *new, *p;
348    struct pelem *pe;
349    char newbuf[MAXPATHLEN+1];/*FIXBUF*/
350
351    for (pe = pathhead; pe; pe = pe->pnext) {
352	new = newbuf;
353	*new = '\0';
354	if (localsyspath != NULL) {
355	    *new = ':';
356	    (void) strcpy(new + 1, localsyspath);
357	    (void) strcat(new, pe->psuf);
358	}
359	(void) strcat(new, pe->pdef);
360	for (n = 0; n < pe->pdirs; n++) {
361	    if (pe->pdir[n] == NULL)
362		continue;
363	    p = pe->pdir[n];
364	    pe->pdir[n] = NULL;
365	    xfree((ptr_t) p);
366	}
367	pe->pdirs = 0;
368	for (;;) {
369	    new = index(p = new, ':');
370	    done = (new == NULL);
371	    if (!done)
372		*new++ = '\0';
373	    p = strsave(p);
374	    pe->pdir[pe->pdirs] = p;
375	    pe->pdirs++;
376	    if (done)
377		break;
378	}
379    }
380}
381
382/***********************************************
383 ***    I N S E R T   A   P A T H N A M E    ***
384 ***********************************************/
385
386static void
387icmd(char *path, char *localsyspath)	/* insert path before localsyspath */
388{
389    int n;
390    char *new;
391    struct pelem *pe;
392    char newbuf[MAXPATHLEN+1];/*FIXBUF*/
393
394    for (pe = pathhead; pe; pe = pe->pnext) {
395	if (sflag)
396	    new = localsyspath;
397	else {
398	    new = newbuf;
399	    (void) strcpy(new, localsyspath);
400	    (void) strcat(new, pe->psuf);
401	}
402	n = locate(pe, new);
403	if (n >= 0)
404	    insert(pe, n, path);
405	else
406	    insert(pe, 0, path);
407    }
408}
409
410static void
411iacmd(char *inpath, char *path)	/* insert path after inpath */
412{
413    int n;
414    struct pelem *pe;
415
416    for (pe = pathhead; pe; pe = pe->pnext) {
417	n = locate(pe, inpath);
418	if (n >= 0)
419	    insert(pe, n + 1, path);
420	else
421	    xprintf(CGETS(10, 4, "setpath: %s not found in %s\n"),
422		    inpath, pe->pname);
423    }
424}
425
426static void
427ibcmd(char *inpath, char *path)	/* insert path before inpath */
428{
429    int n;
430    struct pelem *pe;
431
432    for (pe = pathhead; pe; pe = pe->pnext) {
433	n = locate(pe, inpath);
434	if (n >= 0)
435	    insert(pe, n, path);
436	else
437	    xprintf(CGETS(10, 4, "setpath: %s not found in %s\n"),
438		    inpath, pe->pname);
439    }
440}
441
442static void
443incmd(char *path, int n)	/* insert path at position n */
444{
445    struct pelem *pe;
446
447    for (pe = pathhead; pe; pe = pe->pnext)
448	insert(pe, n, path);
449}
450
451static void
452insert(struct pelem *pe, int loc, char *key)
453{
454    int i;
455    char *new;
456    char newbuf[2000];/*FIXBUF*/
457
458    if (sflag) {		/* add suffix */
459	new = newbuf;
460	(void) strcpy(new, key);
461	(void) strcat(new, pe->psuf);
462    } else
463	new = key;
464    new = strsave(new);
465    for (i = pe->pdirs; i > loc; --i)
466	pe->pdir[i] = pe->pdir[i-1];
467    if (loc > pe->pdirs)
468	loc = pe->pdirs;
469    pe->pdir[loc] = new;
470    pe->pdirs++;
471}
472
473/***********************************************
474 ***    D E L E T E   A   P A T H N A M E    ***
475 ***********************************************/
476
477static void
478dcmd(char *path)		/* delete path */
479{
480    int n;
481    struct pelem *pe;
482
483    for (pe = pathhead; pe; pe = pe->pnext) {
484	n = locate(pe, path);
485	if (n >= 0)
486	    delete(pe, n);
487	else
488	    xprintf(CGETS(10, 4, "setpath: %s not found in %s\n"),
489		    path, pe->pname);
490    }
491}
492
493static void
494dncmd(int n)			/* delete at position n */
495{
496    struct pelem *pe;
497
498    for (pe = pathhead; pe; pe = pe->pnext) {
499	if (n < pe->pdirs)
500	    delete(pe, n);
501	else
502	    xprintf(CGETS(10, 5,
503			    "setpath: %d not valid position in %s\n"),
504		    n, pe->pname);
505    }
506}
507
508static void
509delete(struct pelem *pe, int n)
510{
511    int d;
512
513    xfree((ptr_t) (pe->pdir[n]));
514    for (d = n; d < pe->pdirs - 1; d++)
515	pe->pdir[d] = pe->pdir[d+1];
516    --pe->pdirs;
517}
518
519/***********************************************
520 ***    C H A N G E   A   P A T H N A M E    ***
521 ***********************************************/
522
523static void
524ccmd(char *inpath, char *path)	/* change inpath to path */
525{
526    int n;
527    struct pelem *pe;
528
529    for (pe = pathhead; pe; pe = pe->pnext) {
530	n = locate(pe, inpath);
531	if (n >= 0)
532	    change(pe, n, path);
533	else
534	    xprintf(CGETS(10, 4, "setpath: %s not found in %s\n"),
535		    inpath, pe->pname);
536    }
537}
538
539static void
540cncmd(char *path, int n)	/* change at position n to path */
541{
542    struct pelem *pe;
543
544    for (pe = pathhead; pe; pe = pe->pnext) {
545	if (n < pe->pdirs)
546	    change(pe, n, path);
547	else
548	    xprintf(CGETS(10, 5,
549			    "setpath: %d not valid position in %s\n"),
550		    n, pe->pname);
551    }
552}
553
554static void
555change(struct pelem *pe, int loc, char *key)
556{
557    char *new;
558    char newbuf[MAXPATHLEN+1];/*FIXBUF*/
559
560    if (sflag) {		/* append suffix */
561	new = newbuf;
562	(void) strcpy(new, key);
563	(void) strcat(new, pe->psuf);
564    } else
565	new = key;
566    new = strsave(new);
567    xfree((ptr_t) (pe->pdir[loc]));
568    pe->pdir[loc] = new;
569}
570
571/***************************************
572 ***    F I N D   P A T H N A M E    ***
573 ***************************************/
574
575static int
576locate(struct pelem *pe, char *key)
577{
578    int i;
579    char *realkey;
580    char keybuf[MAXPATHLEN+1];/*FIXBUF*/
581
582    if (sflag) {
583	realkey = keybuf;
584	(void) strcpy(realkey, key);
585	(void) strcat(realkey, pe->psuf);
586    } else
587	realkey = key;
588    for (i = 0; i < pe->pdirs; i++)
589	if (strcmp(pe->pdir[i], realkey) == 0)
590	    break;
591    return((i < pe->pdirs) ? i : -1);
592}
593#endif
594