1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1982-2011 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                  David Korn <dgk@research.att.com>                   *
18*                                                                      *
19***********************************************************************/
20#pragma prototyped
21
22#include	<ast.h>
23#include	<cdt.h>
24
25#define	env_change()		(++ast.env_serial)
26
27typedef struct _venv_ Evar_t;
28struct _venv_
29{
30	union
31	{
32		Evar_t		*next;
33		char		*ptr;
34	}	un;
35	Dtlink_t	link;
36	int		index;
37};
38
39typedef  struct _env_
40{
41	Dt_t	*dt;
42	Evar_t	*freelist;
43	char	**env;
44	int	count;
45	int	extra;
46	int	max;
47	int	flags;
48} Env_t;
49
50#define _BLD_env	1
51#include	<env.h>
52
53#define ENV_VALID	2		/* set if env is valid */
54#define ENV_PMALLOC	1		/* set if Evar_t->un.ptr  *s malloced */
55#define ENV_VMALLOC	2		/* set of Evar_t was malloced */
56#define ENV_BITS	3
57
58/*
59 * Compares the name portion of name=... only.
60 */
61static int compare(Dt_t *dt, Void_t* key1, Void_t* key2, Dtdisc_t* disc)
62{
63	register int c,d;
64	const unsigned char *s1=(unsigned const char*)key1;
65	const unsigned char *s2=(unsigned const char*)key2;
66	while((c= *s1++) && c!='=' && c==*s2)
67		s2++;
68	if(c=='=')
69		c = 0;
70	if((d=*s2)=='=')
71		d = 0;
72	return(c-d);
73}
74
75static Dtdisc_t env_disc =
76{
77	0, -1,
78	sizeof(char*),
79	0,
80	0,
81	compare
82};
83
84/*
85 *  return a pointer to the environment in sorted order
86 *  NULL is returned if there if there is nospace
87 */
88char **env_get(Env_t* ep)
89{
90	register Evar_t *vp;
91	register int n=ep->extra;
92	if(ep->flags&ENV_VALID)
93		return(ep->env+n);
94	if(ep->count > ep->max)
95	{
96		if(ep->flags&ENV_MALLOCED)
97			free((void*)ep->env);
98		if(!(ep->env = (char**)malloc(sizeof(char*)*(ep->count+1))))
99			return(0);
100		ep->flags |= ENV_MALLOCED;
101		ep->max = ep->count;
102	}
103	for(vp=(Evar_t*)dtfirst(ep->dt);vp; vp=(Evar_t*)dtnext(ep->dt,vp))
104	{
105		vp->index = (n<<ENV_BITS) | (vp->index&((1<<ENV_BITS)-1));
106		ep->env[n++] = vp->un.ptr;
107	}
108	ep->env[n] = 0;
109	ep->flags |= ENV_VALID;
110	environ = ep->env+ep->extra;
111	return(ep->env+ep->extra);
112}
113
114/*
115 *  add name=value pair given by <str> to <ep>
116 *  if malloced is set, the variable will be freed when reassigned
117 *  The environment list may become invalidated
118 *  Returns 1 for success, 0 for failure
119 */
120int env_add(Env_t *ep, const char *str, int flags)
121{
122	Evar_t *vp = (Evar_t*)dtmatch(ep->dt,(void*)str);
123	if(vp && strcmp(str,vp->un.ptr)==0)
124		return(1);
125	if(flags&ENV_STRDUP)
126		str = strdup(str);
127	if(vp)
128	{
129		if(vp->index&ENV_PMALLOC)
130			free((void*)vp->un.ptr);
131		vp->un.ptr = (char*)str;
132		if(ep->env && (ep->flags&ENV_VALID))
133			ep->env[vp->index>>ENV_BITS] = vp->un.ptr;
134	}
135	else
136	{
137		ep->flags &= ~ENV_VALID;
138		if(vp = ep->freelist)
139			ep->freelist = vp->un.next;
140		else if(vp = newof((Evar_t*)0,Evar_t,2,0))
141		{
142			vp->index = ENV_VMALLOC;
143			ep->freelist = (vp+1);
144			ep->freelist->un.next = 0;
145		}
146		else
147			return(0);
148		vp->un.ptr = (void*)str;
149		if(!(vp=dtinsert(ep->dt,vp)))
150			return(0);
151		ep->count++;
152	}
153	if(flags)
154		vp->index |= ENV_PMALLOC;
155	else
156		vp->index &= ~ENV_PMALLOC;
157	env_change();
158	return(1);
159}
160
161/*
162 *  delete name  from <ep>
163 *  The environment list may become invalidated
164 *  Returns 1 for success, 0 for if name is not present
165 */
166int env_delete(Env_t *ep, const char *str)
167{
168	Evar_t *vp = (Evar_t*)dtmatch(ep->dt,(void*)str);
169	if(!vp)
170		return(0);
171	ep->flags &= ~ENV_VALID;
172	if(vp->index&ENV_PMALLOC)
173		free((void*)vp->un.ptr);
174	dtdelete(ep->dt,vp);
175	vp->un.next = ep->freelist;
176	ep->freelist = vp;
177	env_change();
178	return(1);
179}
180
181/*
182 * open up a structure to support environment variables
183 * initialize with environment give by <envp>
184 * If <extra> > 0, <extra> slots will be left at beginning of
185 *    environment list when env_get() is involed.
186 * If <extra>==ENV_USABLE, then the original environ can be
187 *   used and returned.  Otherwise, a new one will be returned
188 */
189Env_t *env_open(char **envp, int extra)
190{
191	char **env;
192	Env_t *ep;
193	Evar_t *vp;
194	int n=2;
195	if(!(ep = newof((Env_t*)0,Env_t,1,0)))
196		return(0);
197	if(!(ep->dt = dtopen(&env_disc,Dtoset)))
198		return(0);
199	if(env=envp)
200	{
201		while(*env++);
202		n = (env+2)-envp;
203	}
204	if(extra==ENV_STABLE)
205	{
206		ep->env = envp;
207		ep->max = n-1;
208	}
209	else
210		ep->count = ep->extra = extra;
211	ep->freelist = vp = newof((Evar_t*)0,Evar_t,n,0);
212	vp->index = ENV_VMALLOC;
213	while(--n>0)
214	{
215		vp->un.next = (vp+1);
216		vp++;
217	}
218	vp->un.next = 0;
219	if(env)
220	{
221		for(env=envp; *env; env++)
222			env_add(ep,*env,0);
223	}
224	return(ep);
225}
226
227/*
228 * close <ep> and free up all space used by it
229 */
230void env_close(Env_t *ep)
231{
232	Evar_t *vp, *vpnext,*top;
233	if(ep->env && (ep->flags&ENV_MALLOCED))
234		free((void*)ep->env);
235	for(vp=(Evar_t*)dtfirst(ep->dt);vp; vp=vpnext)
236	{
237		vpnext = (Evar_t*)dtnext(ep->dt,vp);
238		env_delete(ep,vp->un.ptr);
239	}
240	for(top=0,vp = ep->freelist; vp; vp = vpnext)
241	{
242		vpnext = vp->un.next;
243		if(vp->index&ENV_VMALLOC)
244		{
245			vp->un.next = top;
246			top = vp;
247		}
248	}
249	for(vp=top; vp; vp = vpnext)
250	{
251		vpnext = vp->un.next;
252		free((void*)vp);
253	}
254	dtclose(ep->dt);
255}
256