You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
5.0 KiB

10 years ago
  1. /* -*- coding: utf-8 -*-
  2. * ----------------------------------------------------------------------
  3. * Copyright © 2013, RedJack, LLC.
  4. * All rights reserved.
  5. *
  6. * Please see the COPYING file in this distribution for license
  7. * details.
  8. * ----------------------------------------------------------------------
  9. */
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <unistd.h>
  13. #include "libcork/core.h"
  14. #include "libcork/ds.h"
  15. #include "libcork/os/subprocess.h"
  16. #include "libcork/helpers/errors.h"
  17. #if defined(__APPLE__)
  18. /* Apple doesn't provide access to the "environ" variable from a shared library.
  19. * There's a workaround function to grab the environ pointer described at [1].
  20. *
  21. * [1] http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man7/environ.7.html
  22. */
  23. #include <crt_externs.h>
  24. #define environ (*_NSGetEnviron())
  25. #else
  26. /* On all other POSIX platforms, we assume that environ is available in shared
  27. * libraries. */
  28. extern char **environ;
  29. #endif
  30. struct cork_env_var {
  31. const char *name;
  32. const char *value;
  33. };
  34. static struct cork_env_var *
  35. cork_env_var_new(const char *name, const char *value)
  36. {
  37. struct cork_env_var *var = cork_new(struct cork_env_var);
  38. var->name = cork_strdup(name);
  39. var->value = cork_strdup(value);
  40. return var;
  41. }
  42. static void
  43. cork_env_var_free(void *vvar)
  44. {
  45. struct cork_env_var *var = vvar;
  46. cork_strfree(var->name);
  47. cork_strfree(var->value);
  48. free(var);
  49. }
  50. struct cork_env {
  51. struct cork_hash_table *variables;
  52. struct cork_buffer buffer;
  53. };
  54. struct cork_env *
  55. cork_env_new(void)
  56. {
  57. struct cork_env *env = cork_new(struct cork_env);
  58. env->variables = cork_string_hash_table_new(0, 0);
  59. cork_hash_table_set_free_value(env->variables, cork_env_var_free);
  60. cork_buffer_init(&env->buffer);
  61. return env;
  62. }
  63. static void
  64. cork_env_add_internal(struct cork_env *env, const char *name, const char *value)
  65. {
  66. if (env == NULL) {
  67. setenv(name, value, true);
  68. } else {
  69. struct cork_env_var *var = cork_env_var_new(name, value);
  70. void *old_var;
  71. cork_hash_table_put
  72. (env->variables, (void *) var->name, var, NULL, NULL, &old_var);
  73. if (old_var != NULL) {
  74. cork_env_var_free(old_var);
  75. }
  76. }
  77. }
  78. struct cork_env *
  79. cork_env_clone_current(void)
  80. {
  81. char **curr;
  82. struct cork_env *env = cork_env_new();
  83. for (curr = environ; *curr != NULL; curr++) {
  84. const char *entry = *curr;
  85. const char *equal;
  86. equal = strchr(entry, '=');
  87. if (CORK_UNLIKELY(equal == NULL)) {
  88. /* This environment entry is malformed; skip it. */
  89. continue;
  90. }
  91. /* Make a copy of the name so that it's NUL-terminated rather than
  92. * equal-terminated. */
  93. cork_buffer_set(&env->buffer, entry, equal - entry);
  94. cork_env_add_internal(env, env->buffer.buf, equal + 1);
  95. }
  96. return env;
  97. }
  98. void
  99. cork_env_free(struct cork_env *env)
  100. {
  101. cork_hash_table_free(env->variables);
  102. cork_buffer_done(&env->buffer);
  103. free(env);
  104. }
  105. const char *
  106. cork_env_get(struct cork_env *env, const char *name)
  107. {
  108. if (env == NULL) {
  109. return getenv(name);
  110. } else {
  111. struct cork_env_var *var =
  112. cork_hash_table_get(env->variables, (void *) name);
  113. return (var == NULL)? NULL: var->value;
  114. }
  115. }
  116. void
  117. cork_env_add(struct cork_env *env, const char *name, const char *value)
  118. {
  119. cork_env_add_internal(env, name, value);
  120. }
  121. void
  122. cork_env_add_vprintf(struct cork_env *env, const char *name,
  123. const char *format, va_list args)
  124. {
  125. cork_buffer_vprintf(&env->buffer, format, args);
  126. cork_env_add_internal(env, name, env->buffer.buf);
  127. }
  128. void
  129. cork_env_add_printf(struct cork_env *env, const char *name,
  130. const char *format, ...)
  131. {
  132. va_list args;
  133. va_start(args, format);
  134. cork_env_add_vprintf(env, name, format, args);
  135. va_end(args);
  136. }
  137. void
  138. cork_env_remove(struct cork_env *env, const char *name)
  139. {
  140. if (env == NULL) {
  141. unsetenv(name);
  142. } else {
  143. void *old_var;
  144. cork_hash_table_delete(env->variables, (void *) name, NULL, &old_var);
  145. if (old_var != NULL) {
  146. cork_env_var_free(old_var);
  147. }
  148. }
  149. }
  150. static enum cork_hash_table_map_result
  151. cork_env_set_vars(void *user_data, struct cork_hash_table_entry *entry)
  152. {
  153. struct cork_env_var *var = entry->value;
  154. setenv(var->name, var->value, false);
  155. return CORK_HASH_TABLE_MAP_CONTINUE;
  156. }
  157. #if defined(__APPLE__) || (defined(BSD) && (BSD >= 199103))
  158. /* A handful of platforms [1] don't provide clearenv(), so we must implement our
  159. * own version that clears the environ array directly.
  160. *
  161. * [1] http://www.gnu.org/software/gnulib/manual/html_node/clearenv.html
  162. */
  163. static void
  164. clearenv(void)
  165. {
  166. *environ = NULL;
  167. }
  168. #else
  169. /* Otherwise assume that we have clearenv available. */
  170. #endif
  171. void
  172. cork_env_replace_current(struct cork_env *env)
  173. {
  174. clearenv();
  175. cork_hash_table_map(env->variables, NULL, cork_env_set_vars);
  176. }