1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, see: <http://www.gnu.org/licenses/>
14 */
15
16 /*
17 * MODULE OF fvwm
18 *
19 * DESCRIPTION Routines to expand environment-variables into strings.
20 * Will understand both $ENV and ${ENV} -type variables.
21 *
22 * WRITTEN BY Sverre H. Huseby
23 * sverrehu@ifi.uio.no
24 *
25 * CREATED 1995/10/3
26 *
27 * UPDATED migo - 21/Jun/1999 - added getFirstEnv, some changes
28 *
29 */
30
31 /* ---------------------------- included header files ---------------------- */
32
33 #include "config.h"
34 #include <stdio.h>
35 #include <ctype.h>
36
37 #include "fvwmlib.h"
38 #include "envvar.h"
39
40 /* ---------------------------- local definitions -------------------------- */
41
42 #ifdef HAVE_UNSETENV
43 #define FHaveUnsetenv 1
44 #else
45 #define unsetenv(x) do { } while (0)
46 #define FHaveUnsetenv 0
47 #endif
48
49 /* ---------------------------- local macros ------------------------------- */
50
51 #define ENV_LIST_INC 10
52 #ifndef NULL
53 #define NULL 0
54 #endif
55
56 /* ---------------------------- imports ------------------------------------ */
57
58 /* ---------------------------- included code files ------------------------ */
59
60 /* ---------------------------- local types -------------------------------- */
61
62 typedef struct
63 {
64 char *var;
65 char *env;
66 } env_list_item;
67
68 /* ---------------------------- forward declarations ----------------------- */
69
70 /* ---------------------------- local variables ---------------------------- */
71
72 /* ---------------------------- exported variables (globals) --------------- */
73
74 /* ---------------------------- local functions ---------------------------- */
75
76 /*-------------------------------------------------------------------------
77 *
78 * NAME strDel
79 *
80 * FUNCTION Delete characters from a string.
81 *
82 * INPUT s the string to delete characters from.
83 * idx index of first character to delete.
84 * n number of characters to delete.
85 *
86 * OUTPUT s string with characters deleted.
87 *
88 * DESCRIPTION Deletes characters from a string by moving following
89 * characters back.
90 *
91 */
strDel(char * s,int idx,int n)92 static void strDel(char *s, int idx, int n)
93 {
94 int l;
95 char *p;
96
97 if (idx >= (l = strlen(s)))
98 return;
99 if (idx + n > l)
100 n = l - idx;
101 s += idx;
102 p = s + n;
103 do {
104 *s++ = *p;
105 } while (*p++);
106 }
107
108 /*-------------------------------------------------------------------------
109 *
110 * NAME strIns
111 *
112 * FUNCTION Insert a string into a string.
113 *
114 * INPUT s the string to insert into.
115 * ins the string to insert.
116 * idx index of where to insert the string.
117 * maxstrlen max length of s, including '\0'.
118 *
119 * OUTPUT s string with characters inserted.
120 *
121 * DESCRIPTION The insertion will be done even if the string gets to
122 * long, but characters will be sacrificed at the end of s.
123 * The string is always '\0'-terminated.
124 *
125 */
strIns(char * s,const char * ins,int idx,int maxstrlen)126 static void strIns(char *s, const char *ins, int idx, int maxstrlen)
127 {
128 int l, li, move;
129 char *p1, *p2;
130
131 if (idx > (l = strlen(s)))
132 {
133 idx = l;
134 }
135 li = strlen(ins);
136 move = l - idx + 1; /* include '\0' in move */
137 p1 = s + l;
138 p2 = p1 + li;
139 while (p2 >= s + maxstrlen) {
140 --p1;
141 --p2;
142 --move;
143 }
144 while (move-- > 0)
145 *p2-- = *p1--;
146 p1 = s + idx;
147 if (idx + li >= maxstrlen)
148 {
149 li = maxstrlen - idx - 1;
150 }
151 while (li-- > 0)
152 *p1++ = *ins++;
153 s[maxstrlen - 1] = '\0';
154 }
155
156 /*-------------------------------------------------------------------------
157 *
158 * NAME findEnvVar
159 *
160 * FUNCTION Find first environment variable in a string.
161 *
162 * INPUT s the string to scan.
163 *
164 * OUTPUT len length of variable, including $ and { }.
165 *
166 * RETURNS Pointer to the $ that introduces the variable, or NULL
167 * if no variable is found.
168 *
169 * DESCRIPTION Searches for matches like $NAME and ${NAME}, where NAME is
170 * a sequence of characters, digits and underscores, of which
171 * the first can not be a digit.
172 *
173 * NOTE This function will only return `legal' variables. There
174 * may be $'s in the string that are not followed by what
175 * is considered a legal variable name introducer. Such
176 * occurrences are skipped.
177 *
178 */
findEnvVar(const char * s,int * len)179 static char *findEnvVar(const char *s, int *len)
180 {
181 int brace = 0;
182 char *ret = NULL;
183 const char *next;
184
185 if (!s)
186 return NULL;
187 while (*s) {
188 next = s + 1;
189 if (*s == '$' &&
190 (isalpha(*next) || *next == '_' || *next == '{')) {
191 ret = (char *) s++;
192 if (*s == '{') {
193 brace = 1;
194 ++s;
195 }
196 while (*s && (isalnum(*s) || *s == '_'))
197 ++s;
198 *len = s - ret;
199 if (brace) {
200 if (*s == '}') {
201 ++*len;
202 break;
203 }
204 ret = NULL;
205 } else
206 break;
207 }
208 ++s;
209 }
210 return ret;
211 }
212
213 /*-------------------------------------------------------------------------
214 *
215 * FUNCTION Look up environment variable.
216 *
217 * INPUT name name of environment variable to look up. This
218 * may include $ and { }.
219 * len length for environment variable name (0 - ignore).
220 *
221 * RETURNS The variable contents, or "" if not found.
222 *
223 */
getEnv(const char * name,int len)224 static const char *getEnv(const char *name, int len)
225 {
226 static char *empty = "";
227 char *ret = NULL, *tmp, *p, *p2;
228
229 if ((tmp = fxstrdup(name)) == NULL)
230 return empty; /* better than no test at all. */
231 p = tmp;
232 if (*p == '$')
233 ++p;
234 if (*p == '{') {
235 ++p;
236 if ((p2 = strchr(p, '}')) != NULL)
237 *p2 = '\0';
238 }
239 if (len > 0 && len < strlen(tmp)) tmp[len] = '\0';
240 if ((ret = getenv(p)) == NULL)
241 ret = empty;
242 free(tmp);
243 return ret;
244 }
245
246 /* ---------------------------- interface functions ------------------------ */
247
248 /*
249 * FUNCTION Expand environment variables in a string.
250 *
251 * SYNOPSIS #include "envvar.h"
252 * int envExpand(char *s, int maxstrlen);
253 *
254 * INPUT s string to expand environment variables in.
255 * maxstrlen max length of string, including '\0'.
256 *
257 * OUTPUT s the string with environment variables expanded.
258 *
259 * RETURNS Number of changes done.
260 *
261 * NOTES A non-existing variable is substituted with the empty
262 * string.
263 *
264 */
envExpand(char * s,int maxstrlen)265 int envExpand(char *s, int maxstrlen)
266 {
267 char *var, *s2;
268 const char *env;
269 int len, ret = 0;
270
271 s2 = s;
272 while ((var = findEnvVar(s2, &len)) != NULL) {
273 ++ret;
274 env = getEnv(var, len);
275 strDel(s, var - s, len);
276 strIns(s, env, var - s, maxstrlen);
277 s2 = var + strlen(env);
278 }
279 return ret;
280 }
281
282 /*
283 * FUNCTION Expand environment variables into a new string.
284 *
285 * SYNOPSIS #include "envvar.h"
286 * char *envDupExpand(const char *s, int extra);
287 *
288 * INPUT s string to expand environment variables in.
289 * extra number of extra bytes to allocate in the
290 * string, in addition to the string contents
291 * and the terminating '\0'.
292 *
293 * RETURNS A dynamically allocated string with environment
294 * variables expanded.
295 * Use free() to deallocate the buffer when it is no
296 * longer needed.
297 * NULL is returned if there is not enough memory.
298 *
299 * NOTES A non-existing variable is substituted with the empty
300 * string.
301 *
302 */
envDupExpand(const char * s,int extra)303 char *envDupExpand(const char *s, int extra)
304 {
305 char *var, *ret;
306 const char *env, *s2;
307 int len, slen, elen, bufflen;
308
309 /*
310 * calculate length needed.
311 */
312 s2 = s;
313 slen = strlen(s);
314 bufflen = slen + 1 + extra;
315 while ((var = findEnvVar(s2, &len)) != NULL) {
316 env = getEnv(var, len);
317 elen = strlen(env);
318 /* need to make a buffer the maximum possible size, else we
319 * may get trouble while expanding. */
320 bufflen += len > elen ? len : elen;
321 s2 = var + len;
322 }
323 if (bufflen < slen + 1)
324 bufflen = slen + 1;
325
326 ret = fxmalloc(bufflen);
327 /* TA: FIXME! xasprintf() */
328
329 /*
330 * now do the real expansion.
331 */
332 strcpy(ret, s);
333 envExpand(ret, bufflen - extra);
334
335 return ret;
336 }
337
338 /*
339 * FUNCTION Search for the first environment variable and return
340 * its contents and coordinates in the given string.
341 *
342 * INPUT s the string to scan.
343 * may include $ and { } that introduce variable.
344 *
345 * OUTPUT beg index in the string of matching $.
346 * end index in the string, first after matching var.
347 *
348 * RETURNS The variable contents; "" if env variable has legal name,
349 * but does not exist; or NULL if no env variables found.
350 * Returned constant string must not be deallocated.
351 *
352 * NOTE This function will only return `legal' variables. There
353 * may be $'s in the string that are not followed by what
354 * is considered a legal variable name introducer. Such
355 * occurrences are skipped.
356 * If nothing is found returns NULL and sets beg and end to 0.
357 *
358 * EXAMPLE getFirstEnv("echo $HOME/.fvwm/config", &beg, &end)
359 * returns "/home/username" and beg=5, end=10.
360 *
361 */
getFirstEnv(const char * s,int * beg,int * end)362 const char* getFirstEnv(const char *s, int *beg, int *end)
363 {
364 char *var;
365 const char *env;
366 int len;
367
368 *beg = *end = 0;
369 if ((var = findEnvVar(s, &len)) == NULL) return NULL;
370 env = getEnv(var, len);
371
372 *beg = var - s;
373 *end = *beg + len;
374
375 return env;
376 }
377
378 /* If env is NULL, var is removed from the environment list */
add_to_envlist(char * var,char * env)379 static void add_to_envlist(char *var, char *env)
380 {
381 static env_list_item *env_list = NULL;
382 static unsigned int env_len = 0;
383 static unsigned int env_len_allocated = 0;
384 unsigned int i;
385
386 /* find string in list */
387 if (env_list && env_len)
388 {
389 for (i = 0; i < env_len; i++)
390 {
391 if (strcmp(var, env_list[i].var) != 0)
392 {
393 continue;
394 }
395 /* found it - replace old string */
396 free(env_list[i].var);
397 free(env_list[i].env);
398 if (env == NULL)
399 {
400 /* delete */
401 env_len--;
402 env_list[i].var =
403 env_list[env_len].var;
404 env_list[i].env =
405 env_list[env_len].env;
406 }
407 else
408 {
409 /* replace */
410 env_list[i].var = var;
411 env_list[i].env = env;
412 }
413
414 return;
415 }
416 }
417 if (env == NULL)
418 {
419 return;
420 }
421 /* not found */
422 if (env_list == NULL)
423 {
424 /* list is still empty */
425 env_len_allocated = ENV_LIST_INC;
426 env_list = fxcalloc(sizeof(env_list_item), env_len_allocated);
427 }
428 else if (env_len >= env_len_allocated && env != NULL)
429 {
430 /* need more memory */
431 env_len_allocated = env_len + ENV_LIST_INC;
432 env_list = fxrealloc((void *)env_list, (env_len_allocated),
433 sizeof(env_list_item));
434 }
435 env_list[env_len].var = var;
436 env_list[env_len].env = env;
437 env_len++;
438
439 return;
440 }
441
442 /* This function keeps a list of all strings that were set in the environment.
443 * If a variable is written again, the old memory is freed. This function
444 * should be called instead of putenv().
445 *
446 * var - environement variable name
447 * env - environment string ("variable=value")
448 *
449 * Both arguments are copied internally and should be freed after calling this
450 * function.
451 */
flib_putenv(char * var,char * env)452 void flib_putenv(char *var, char *env)
453 {
454 char *s;
455
456 s = fxstrdup(var);
457 var = s;
458 s = fxstrdup(env);
459 env = s;
460 putenv(env);
461 add_to_envlist(var, env);
462
463 return;
464 }
465
flib_unsetenv(const char * name)466 void flib_unsetenv(const char *name)
467 {
468 if (FHaveUnsetenv)
469 {
470 unsetenv(name);
471 }
472 else
473 {
474 int rc;
475
476 /* try putenv without '=' */
477 rc = putenv((char *)name);
478 if (rc == 0 || getenv(name) != NULL)
479 {
480 /* failed, write empty string */
481 flib_putenv((char *)name, "");
482 return;
483 }
484 }
485 add_to_envlist((char *)name, NULL);
486
487 return;
488 }
489