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