1 /* $Id: envvar.c,v 1.3 2002/03/02 21:02:21 sverrehu Exp $ */
2 /**************************************************************************
3  *
4  *  FILE            envvar.c
5  *
6  *  DESCRIPTION     Routines to expand environment-variables into strings.
7  *                  Will understand both $ENV and ${ENV} -type variables.
8  *
9  *  WRITTEN BY      Sverre H. Huseby <shh@thathost.com>
10  *
11  *  CREATED         1995-10-03
12  *
13  **************************************************************************/
14 
15 #include <stdlib.h>
16 #include <string.h>
17 #include <ctype.h>
18 
19 #include "envvar.h"
20 
21 #ifndef NULL
22 #define NULL 0
23 #endif
24 
25 /**************************************************************************
26  *                                                                        *
27  *                   P R I V A T E    F U N C T I O N S                   *
28  *                                                                        *
29  **************************************************************************/
30 
31 /*-------------------------------------------------------------------------
32  *
33  *  NAME          strDel
34  *
35  *  FUNCTION      Delete characters from a string.
36  *
37  *  INPUT         s       the string to delete characters from.
38  *                idx     index of first character to delete.
39  *                n       number of characters to delete.
40  *
41  *  OUTPUT        s       string with characters deleted.
42  *
43  *  DESCRIPTION   Deletes characters from a string by moving following
44  *                characters back.
45  *
46  */
47 static void
strDel(char * s,int idx,int n)48 strDel(char *s, int idx, int n)
49 {
50     int  l;
51     char *p;
52 
53     if (idx >= (l = strlen(s)))
54 	return;
55     if (idx + n > l)
56 	n = l - idx;
57     s += idx;
58     p = s + n;
59     do {
60 	*s++ = *p;
61     } while (*p++);
62 }
63 
64 
65 
66 /*-------------------------------------------------------------------------
67  *
68  *  NAME          strIns
69  *
70  *  FUNCTION      Insert a string into a string.
71  *
72  *  INPUT         s       the string to insert into.
73  *                ins     the string to insert.
74  *                idx     index of where to insert the string.
75  *                max     max length of s, including '\0'.
76  *
77  *  OUTPUT        s       string with characters inserted.
78  *
79  *  DESCRIPTION   The insertion will be done even if the string gets to
80  *                long, but characters will be sacrificed at the end of s.
81  *                The string is always '\0'-terminated.
82  *
83  */
84 static void
strIns(char * s,const char * ins,int idx,int max)85 strIns(char *s, const char *ins, int idx, int max)
86 {
87     int  l, li, move;
88     char *p1, *p2;
89 
90     if (idx > (l = strlen(s)))
91 	idx = l;
92     li = strlen(ins);
93     move = l - idx + 2; /* include '\0' in move */
94     p1 = s + l;
95     p2 = p1 + li;
96     while (p2 >= s + max) {
97 	--p1;
98 	--p2;
99 	--move;
100     }
101     while (move-- > 0)
102 	*p2-- = *p1--;
103     p1 = s + idx;
104     if (idx + li >= max)
105 	li = max - idx - 1;
106     while (li--)
107 	*p1++ = *ins++;
108     s[max - 1] = '\0';
109 }
110 
111 
112 
113 /*-------------------------------------------------------------------------
114  *
115  *  NAME          findEnvVar
116  *
117  *  FUNCTION      Find first environment variable in a string.
118  *
119  *  INPUT         s       the string to scan.
120  *
121  *  OUTPUT        len     length of variable, including $ and { }.
122  *
123  *  RETURNS       Pointer to the $ that introduces the variable, or NULL
124  *                if no variable is found.
125  *
126  *  DESCRIPTION   Searches for matches like $NAME and ${NAME}, where NAME is
127  *                a sequence of characters, digits and underscores, of which
128  *                the first can not be a digit.
129  *
130  *  NOTE          This function will only return `legal' variables. There
131  *                may be $'s in the string that are not followed by what
132  *                is considered a legal variable name introducer. Such
133  *                occurrences are skipped.
134  *
135  */
136 static char *
findEnvVar(const char * s,int * len)137 findEnvVar(const char *s, int *len)
138 {
139     int   brace = 0;
140     char  *ret = NULL;
141     const char *next;
142 
143     if (!s)
144 	return NULL;
145     while (*s) {
146 	next = s + 1;
147 	if (*s == '$' && (isalpha(*next) || *next == '_' || *next == '{')) {
148 	    ret = (char *) s++;
149 	    if (*s == '{') {
150 		brace = 1;
151 		++s;
152 	    }
153 	    while (*s && (isalnum(*s) || *s == '_'))
154 		++s;
155 	    *len = s - ret;
156 	    if (brace) {
157 		if (*s == '}') {
158 		    ++*len;
159 		    break;
160 		}
161 		ret = NULL;
162 	    } else
163 		break;
164 	}
165 	++s;
166     }
167     return ret;
168 }
169 
170 
171 
172 /*-------------------------------------------------------------------------
173  *
174  *  NAME          getEnv
175  *
176  *  FUNCTION      Look up environment variable.
177  *
178  *  INPUT         name    name of environment variable to look up. This
179  *                        may include $ and { }.
180  *
181  *  RETURNS       The variable contents, or "" if not found.
182  *
183  */
184 static const char *
getEnv(const char * name)185 getEnv(const char *name)
186 {
187     static char *empty = "";
188     char   *ret, *tmp, *p, *p2;
189 
190     if ((tmp = strdup(name)) == NULL)
191 	return empty;  /* better than no test at all. */
192     p = tmp;
193     if (*p == '$')
194 	++p;
195     if (*p == '{') {
196 	++p;
197 	if ((p2 = strchr(p, '}')) != NULL)
198 	    *p2 = '\0';
199     }
200     if ((ret = getenv(p)) == NULL)
201 	ret = empty;
202     free(tmp);
203     return ret;
204 }
205 
206 
207 
208 /**************************************************************************
209  *                                                                        *
210  *                    P U B L I C    F U N C T I O N S                    *
211  *                                                                        *
212  **************************************************************************/
213 
214 /*-------------------------------------------------------------------------
215  *
216  *  NAME          envExpand
217  *
218  *  FUNCTION      Expand environment variables in a string.
219  *
220  *  SYNOPSIS      #include "envvar.h"
221  *                int envExpand(char *s, int max);
222  *
223  *  INPUT         s       string to expand environment variables in.
224  *                max     max length of string, including '\0'.
225  *
226  *  OUTPUT        s       the string with environment variables expanded.
227  *
228  *  RETURNS       Number of changes done.
229  *
230  *  NOTES         A non-existing variable is substituted with the empty
231  *                string.
232  *
233  */
234 int
envExpand(char * s,int max)235 envExpand(char *s, int max)
236 {
237     char  *var, *s2, save;
238     const char *env;
239     int   len, ret = 0;
240 
241     s2 = s;
242     while ((var = findEnvVar(s2, &len)) != NULL) {
243 	++ret;
244 	save = var[len];
245 	var[len] = '\0';
246 	env = getEnv(var);
247 	var[len] = save;
248 	strDel(s, var - s, len);
249 	strIns(s, env, var - s, max);
250 	s2 = var + strlen(env);
251     }
252     return ret;
253 }
254 
255 
256 
257 /*-------------------------------------------------------------------------
258  *
259  *  NAME          envDupExpand
260  *
261  *  FUNCTION      Expand environment variables into a new string.
262  *
263  *  SYNOPSIS      #include "envvar.h"
264  *                char *envDupExpand(const char *s, int extra);
265  *
266  *  INPUT         s       string to expand environment variables in.
267  *                extra   number of extra bytes to allocate in the
268  *                        string, in addition to the string contents
269  *                        and the terminating '\0'.
270  *
271  *  RETURNS       A dynamically allocated string with environment
272  *                variables expanded.
273  *                Use free() to deallocate the buffer when it is no
274  *                longer needed.
275  *                NULL is returned if there is not enough memory.
276  *
277  *  NOTES         A non-existing variable is substituted with the empty
278  *                string.
279  *
280  */
281 char *
envDupExpand(const char * s,int extra)282 envDupExpand(const char *s, int extra)
283 {
284     char  *var, *ret, save;
285     const char *env, *s2;
286     int   len, slen, elen, bufflen;
287 
288     /*
289      *  calculate length needed.
290      */
291     s2 = s;
292     slen = strlen(s);
293     bufflen = slen + 1 + extra;
294     while ((var = findEnvVar(s2, &len)) != NULL) {
295 	save = var[len];
296 	var[len] = '\0';
297 	env = getEnv(var);
298 	var[len] = save;
299 	elen = strlen(env);
300 	/* need to make a buffer the maximum possible size, else we
301 	 * may get trouble while expanding. */
302 	bufflen += len > elen ? len : elen;
303 	s2 = var + len;
304     }
305     if (bufflen < slen + 1)
306 	bufflen = slen + 1;
307 
308     if ((ret = malloc(bufflen)) == NULL)
309 	return ret;
310 
311     /*
312      *  now do the real expansion.
313      */
314     strcpy(ret, s);
315     envExpand(ret, bufflen - extra);
316 
317     return ret;
318 }
319