Close fd in CMD_Exec
[fvwm.git] / libs / envvar.c
blob996f75b33be9d892d000c3b5786802fb244bd156
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.
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.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * MODULE OF fvwm
20 * DESCRIPTION Routines to expand environment-variables into strings.
21 * Will understand both $ENV and ${ENV} -type variables.
23 * WRITTEN BY Sverre H. Huseby
24 * sverrehu@ifi.uio.no
26 * CREATED 1995/10/3
28 * UPDATED migo - 21/Jun/1999 - added getFirstEnv, some changes
32 /* ---------------------------- included header files ---------------------- */
34 #include "config.h"
35 #include <stdio.h>
36 #include <ctype.h>
38 #include "fvwmlib.h"
39 #include "envvar.h"
41 /* ---------------------------- local definitions -------------------------- */
43 #ifdef HAVE_UNSETENV
44 #define FHaveUnsetenv 1
45 #else
46 #define unsetenv(x) do { } while (0)
47 #define FHaveUnsetenv 0
48 #endif
50 /* ---------------------------- local macros ------------------------------- */
52 #define ENV_LIST_INC 10
53 #ifndef NULL
54 #define NULL 0
55 #endif
57 /* ---------------------------- imports ------------------------------------ */
59 /* ---------------------------- included code files ------------------------ */
61 /* ---------------------------- local types -------------------------------- */
63 typedef struct
65 char *var;
66 char *env;
67 } env_list_item;
69 /* ---------------------------- forward declarations ----------------------- */
71 /* ---------------------------- local variables ---------------------------- */
73 /* ---------------------------- exported variables (globals) --------------- */
75 /* ---------------------------- local functions ---------------------------- */
77 /*-------------------------------------------------------------------------
79 * NAME strDel
81 * FUNCTION Delete characters from a string.
83 * INPUT s the string to delete characters from.
84 * idx index of first character to delete.
85 * n number of characters to delete.
87 * OUTPUT s string with characters deleted.
89 * DESCRIPTION Deletes characters from a string by moving following
90 * characters back.
93 static void strDel(char *s, int idx, int n)
95 int l;
96 char *p;
98 if (idx >= (l = strlen(s)))
99 return;
100 if (idx + n > l)
101 n = l - idx;
102 s += idx;
103 p = s + n;
104 do {
105 *s++ = *p;
106 } while (*p++);
109 /*-------------------------------------------------------------------------
111 * NAME strIns
113 * FUNCTION Insert a string into a string.
115 * INPUT s the string to insert into.
116 * ins the string to insert.
117 * idx index of where to insert the string.
118 * maxstrlen max length of s, including '\0'.
120 * OUTPUT s string with characters inserted.
122 * DESCRIPTION The insertion will be done even if the string gets to
123 * long, but characters will be sacrificed at the end of s.
124 * The string is always '\0'-terminated.
127 static void strIns(char *s, const char *ins, int idx, int maxstrlen)
129 int l, li, move;
130 char *p1, *p2;
132 if (idx > (l = strlen(s)))
134 idx = l;
136 li = strlen(ins);
137 move = l - idx + 1; /* include '\0' in move */
138 p1 = s + l;
139 p2 = p1 + li;
140 while (p2 >= s + maxstrlen) {
141 --p1;
142 --p2;
143 --move;
145 while (move-- > 0)
146 *p2-- = *p1--;
147 p1 = s + idx;
148 if (idx + li >= maxstrlen)
150 li = maxstrlen - idx - 1;
152 while (li-- > 0)
153 *p1++ = *ins++;
154 s[maxstrlen - 1] = '\0';
157 /*-------------------------------------------------------------------------
159 * NAME findEnvVar
161 * FUNCTION Find first environment variable in a string.
163 * INPUT s the string to scan.
165 * OUTPUT len length of variable, including $ and { }.
167 * RETURNS Pointer to the $ that introduces the variable, or NULL
168 * if no variable is found.
170 * DESCRIPTION Searches for matches like $NAME and ${NAME}, where NAME is
171 * a sequence of characters, digits and underscores, of which
172 * the first can not be a digit.
174 * NOTE This function will only return `legal' variables. There
175 * may be $'s in the string that are not followed by what
176 * is considered a legal variable name introducer. Such
177 * occurrences are skipped.
180 static char *findEnvVar(const char *s, int *len)
182 int brace = 0;
183 char *ret = NULL;
184 const char *next;
186 if (!s)
187 return NULL;
188 while (*s) {
189 next = s + 1;
190 if (*s == '$' &&
191 (isalpha(*next) || *next == '_' || *next == '{')) {
192 ret = (char *) s++;
193 if (*s == '{') {
194 brace = 1;
195 ++s;
197 while (*s && (isalnum(*s) || *s == '_'))
198 ++s;
199 *len = s - ret;
200 if (brace) {
201 if (*s == '}') {
202 ++*len;
203 break;
205 ret = NULL;
206 } else
207 break;
209 ++s;
211 return ret;
214 /*-------------------------------------------------------------------------
216 * FUNCTION Look up environment variable.
218 * INPUT name name of environment variable to look up. This
219 * may include $ and { }.
220 * len length for environment variable name (0 - ignore).
222 * RETURNS The variable contents, or "" if not found.
225 static const char *getEnv(const char *name, int len)
227 static char *empty = "";
228 char *ret = NULL, *tmp, *p, *p2;
230 if ((tmp = safestrdup(name)) == NULL)
231 return empty; /* better than no test at all. */
232 p = tmp;
233 if (*p == '$')
234 ++p;
235 if (*p == '{') {
236 ++p;
237 if ((p2 = strchr(p, '}')) != NULL)
238 *p2 = '\0';
240 if (len > 0 && len < strlen(tmp)) tmp[len] = '\0';
241 if ((ret = getenv(p)) == NULL)
242 ret = empty;
243 free(tmp);
244 return ret;
247 /* ---------------------------- interface functions ------------------------ */
250 * FUNCTION Expand environment variables in a string.
252 * SYNOPSIS #include "envvar.h"
253 * int envExpand(char *s, int maxstrlen);
255 * INPUT s string to expand environment variables in.
256 * maxstrlen max length of string, including '\0'.
258 * OUTPUT s the string with environment variables expanded.
260 * RETURNS Number of changes done.
262 * NOTES A non-existing variable is substituted with the empty
263 * string.
266 int envExpand(char *s, int maxstrlen)
268 char *var, *s2;
269 const char *env;
270 int len, ret = 0;
272 s2 = s;
273 while ((var = findEnvVar(s2, &len)) != NULL) {
274 ++ret;
275 env = getEnv(var, len);
276 strDel(s, var - s, len);
277 strIns(s, env, var - s, maxstrlen);
278 s2 = var + strlen(env);
280 return ret;
284 * FUNCTION Expand environment variables into a new string.
286 * SYNOPSIS #include "envvar.h"
287 * char *envDupExpand(const char *s, int extra);
289 * INPUT s string to expand environment variables in.
290 * extra number of extra bytes to allocate in the
291 * string, in addition to the string contents
292 * and the terminating '\0'.
294 * RETURNS A dynamically allocated string with environment
295 * variables expanded.
296 * Use free() to deallocate the buffer when it is no
297 * longer needed.
298 * NULL is returned if there is not enough memory.
300 * NOTES A non-existing variable is substituted with the empty
301 * string.
304 char *envDupExpand(const char *s, int extra)
306 char *var, *ret;
307 const char *env, *s2;
308 int len, slen, elen, bufflen;
311 * calculate length needed.
313 s2 = s;
314 slen = strlen(s);
315 bufflen = slen + 1 + extra;
316 while ((var = findEnvVar(s2, &len)) != NULL) {
317 env = getEnv(var, len);
318 elen = strlen(env);
319 /* need to make a buffer the maximum possible size, else we
320 * may get trouble while expanding. */
321 bufflen += len > elen ? len : elen;
322 s2 = var + len;
324 if (bufflen < slen + 1)
325 bufflen = slen + 1;
327 ret = safemalloc(bufflen);
330 * now do the real expansion.
332 strcpy(ret, s);
333 envExpand(ret, bufflen - extra);
335 return ret;
339 * FUNCTION Search for the first environment variable and return
340 * its contents and coordinates in the given string.
342 * INPUT s the string to scan.
343 * may include $ and { } that introduce variable.
345 * OUTPUT beg index in the string of matching $.
346 * end index in the string, first after matching var.
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.
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.
358 * EXAMPLE getFirstEnv("echo $HOME/.fvwm/config", &beg, &end)
359 * returns "/home/username" and beg=5, end=10.
362 const char* getFirstEnv(const char *s, int *beg, int *end)
364 char *var;
365 const char *env;
366 int len;
368 *beg = *end = 0;
369 if ((var = findEnvVar(s, &len)) == NULL) return NULL;
370 env = getEnv(var, len);
372 *beg = var - s;
373 *end = *beg + len;
375 return env;
378 /* If env is NULL, var is removed from the environment list */
379 static void add_to_envlist(char *var, char *env)
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;
386 /* find string in list */
387 if (env_list && env_len)
389 for (i = 0; i < env_len; i++)
391 if (strcmp(var, env_list[i].var) != 0)
393 continue;
395 /* found it - replace old string */
396 free(env_list[i].var);
397 free(env_list[i].env);
398 if (env == NULL)
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;
407 else
409 /* replace */
410 env_list[i].var = var;
411 env_list[i].env = env;
414 return;
417 if (env == NULL)
419 return;
421 /* not found */
422 if (env_list == NULL)
424 /* list is still empty */
425 env_len_allocated = ENV_LIST_INC;
426 env_list = (env_list_item *)safecalloc(
427 sizeof(env_list_item), env_len_allocated);
429 else if (env_len >= env_len_allocated && env != NULL)
431 /* need more memory */
432 env_len_allocated = env_len + ENV_LIST_INC;
433 env_list = (env_list_item *)saferealloc(
434 (void *)env_list, (env_len_allocated) *
435 sizeof(env_list_item));
437 env_list[env_len].var = var;
438 env_list[env_len].env = env;
439 env_len++;
441 return;
444 /* This function keeps a list of all strings that were set in the environment.
445 * If a variable is written again, the old memory is freed. This function
446 * should be called instead of putenv().
448 * var - environement variable name
449 * env - environment string ("variable=value")
451 * Both arguments are copied internally and should be freed after calling this
452 * function.
454 void flib_putenv(char *var, char *env)
456 char *s;
458 s = safestrdup(var);
459 var = s;
460 s = safestrdup(env);
461 env = s;
462 putenv(env);
463 add_to_envlist(var, env);
465 return;
468 void flib_unsetenv(const char *name)
470 if (FHaveUnsetenv)
472 unsetenv(name);
474 else
476 int rc;
478 /* try putenv without '=' */
479 rc = putenv((char *)name);
480 if (rc == 0 || getenv(name) != NULL)
482 /* failed, write empty string */
483 flib_putenv((char *)name, "");
484 return;
487 add_to_envlist((char *)name, NULL);
489 return;