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.

207 lines
5.0 KiB

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