envlist.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. #include <sys/queue.h>
  2. #include <assert.h>
  3. #include <errno.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <unistd.h>
  7. #include "envlist.h"
  8. struct envlist_entry {
  9. const char *ev_var; /* actual env value */
  10. LIST_ENTRY(envlist_entry) ev_link;
  11. };
  12. struct envlist {
  13. LIST_HEAD(, envlist_entry) el_entries; /* actual entries */
  14. size_t el_count; /* number of entries */
  15. };
  16. static int envlist_parse(envlist_t *envlist,
  17. const char *env, int (*)(envlist_t *, const char *));
  18. /*
  19. * Allocates new envlist and returns pointer to that or
  20. * NULL in case of error.
  21. */
  22. envlist_t *
  23. envlist_create(void)
  24. {
  25. envlist_t *envlist;
  26. if ((envlist = malloc(sizeof (*envlist))) == NULL)
  27. return (NULL);
  28. LIST_INIT(&envlist->el_entries);
  29. envlist->el_count = 0;
  30. return (envlist);
  31. }
  32. /*
  33. * Releases given envlist and its entries.
  34. */
  35. void
  36. envlist_free(envlist_t *envlist)
  37. {
  38. struct envlist_entry *entry;
  39. assert(envlist != NULL);
  40. while (envlist->el_entries.lh_first != NULL) {
  41. entry = envlist->el_entries.lh_first;
  42. LIST_REMOVE(entry, ev_link);
  43. free((char *)entry->ev_var);
  44. free(entry);
  45. }
  46. free(envlist);
  47. }
  48. /*
  49. * Parses comma separated list of set/modify environment
  50. * variable entries and updates given enlist accordingly.
  51. *
  52. * For example:
  53. * envlist_parse(el, "HOME=foo,SHELL=/bin/sh");
  54. *
  55. * inserts/sets environment variables HOME and SHELL.
  56. *
  57. * Returns 0 on success, errno otherwise.
  58. */
  59. int
  60. envlist_parse_set(envlist_t *envlist, const char *env)
  61. {
  62. return (envlist_parse(envlist, env, &envlist_setenv));
  63. }
  64. /*
  65. * Parses comma separated list of unset environment variable
  66. * entries and removes given variables from given envlist.
  67. *
  68. * Returns 0 on success, errno otherwise.
  69. */
  70. int
  71. envlist_parse_unset(envlist_t *envlist, const char *env)
  72. {
  73. return (envlist_parse(envlist, env, &envlist_unsetenv));
  74. }
  75. /*
  76. * Parses comma separated list of set, modify or unset entries
  77. * and calls given callback for each entry.
  78. *
  79. * Returns 0 in case of success, errno otherwise.
  80. */
  81. static int
  82. envlist_parse(envlist_t *envlist, const char *env,
  83. int (*callback)(envlist_t *, const char *))
  84. {
  85. char *tmpenv, *envvar;
  86. char *envsave = NULL;
  87. assert(callback != NULL);
  88. if ((envlist == NULL) || (env == NULL))
  89. return (EINVAL);
  90. /*
  91. * We need to make temporary copy of the env string
  92. * as strtok_r(3) modifies it while it tokenizes.
  93. */
  94. if ((tmpenv = strdup(env)) == NULL)
  95. return (errno);
  96. envvar = strtok_r(tmpenv, ",", &envsave);
  97. while (envvar != NULL) {
  98. if ((*callback)(envlist, envvar) != 0) {
  99. free(tmpenv);
  100. return (errno);
  101. }
  102. envvar = strtok_r(NULL, ",", &envsave);
  103. }
  104. free(tmpenv);
  105. return (0);
  106. }
  107. /*
  108. * Sets environment value to envlist in similar manner
  109. * than putenv(3).
  110. *
  111. * Returns 0 in success, errno otherwise.
  112. */
  113. int
  114. envlist_setenv(envlist_t *envlist, const char *env)
  115. {
  116. struct envlist_entry *entry = NULL;
  117. const char *eq_sign;
  118. size_t envname_len;
  119. if ((envlist == NULL) || (env == NULL))
  120. return (EINVAL);
  121. /* find out first equals sign in given env */
  122. if ((eq_sign = strchr(env, '=')) == NULL)
  123. return (EINVAL);
  124. envname_len = eq_sign - env + 1;
  125. /*
  126. * If there already exists variable with given name
  127. * we remove and release it before allocating a whole
  128. * new entry.
  129. */
  130. for (entry = envlist->el_entries.lh_first; entry != NULL;
  131. entry = entry->ev_link.le_next) {
  132. if (strncmp(entry->ev_var, env, envname_len) == 0)
  133. break;
  134. }
  135. if (entry != NULL) {
  136. LIST_REMOVE(entry, ev_link);
  137. free((char *)entry->ev_var);
  138. free(entry);
  139. } else {
  140. envlist->el_count++;
  141. }
  142. if ((entry = malloc(sizeof (*entry))) == NULL)
  143. return (errno);
  144. if ((entry->ev_var = strdup(env)) == NULL) {
  145. free(entry);
  146. return (errno);
  147. }
  148. LIST_INSERT_HEAD(&envlist->el_entries, entry, ev_link);
  149. return (0);
  150. }
  151. /*
  152. * Removes given env value from envlist in similar manner
  153. * than unsetenv(3). Returns 0 in success, errno otherwise.
  154. */
  155. int
  156. envlist_unsetenv(envlist_t *envlist, const char *env)
  157. {
  158. struct envlist_entry *entry;
  159. size_t envname_len;
  160. if ((envlist == NULL) || (env == NULL))
  161. return (EINVAL);
  162. /* env is not allowed to contain '=' */
  163. if (strchr(env, '=') != NULL)
  164. return (EINVAL);
  165. /*
  166. * Find out the requested entry and remove
  167. * it from the list.
  168. */
  169. envname_len = strlen(env);
  170. for (entry = envlist->el_entries.lh_first; entry != NULL;
  171. entry = entry->ev_link.le_next) {
  172. if (strncmp(entry->ev_var, env, envname_len) == 0)
  173. break;
  174. }
  175. if (entry != NULL) {
  176. LIST_REMOVE(entry, ev_link);
  177. free((char *)entry->ev_var);
  178. free(entry);
  179. envlist->el_count--;
  180. }
  181. return (0);
  182. }
  183. /*
  184. * Returns given envlist as array of strings (in same form that
  185. * global variable environ is). Caller must free returned memory
  186. * by calling free(3) for each element and for the array. Returned
  187. * array and given envlist are not related (no common references).
  188. *
  189. * If caller provides count pointer, number of items in array is
  190. * stored there. In case of error, NULL is returned and no memory
  191. * is allocated.
  192. */
  193. char **
  194. envlist_to_environ(const envlist_t *envlist, size_t *count)
  195. {
  196. struct envlist_entry *entry;
  197. char **env, **penv;
  198. penv = env = malloc((envlist->el_count + 1) * sizeof (char *));
  199. if (env == NULL)
  200. return (NULL);
  201. for (entry = envlist->el_entries.lh_first; entry != NULL;
  202. entry = entry->ev_link.le_next) {
  203. *(penv++) = strdup(entry->ev_var);
  204. }
  205. *penv = NULL; /* NULL terminate the list */
  206. if (count != NULL)
  207. *count = envlist->el_count;
  208. return (env);
  209. }