expand_env.c 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. #include <stdlib.h>
  2. #include <string.h>
  3. #include <stdio.h>
  4. #include <assert.h>
  5. #include <ctype.h>
  6. #include <stdbool.h>
  7. #include "expand_env.h"
  8. static bool allowed_env_var_name(char c)
  9. {
  10. return c != '\0' &&
  11. !isblank(c) &&
  12. !iscntrl(c) &&
  13. c != '/' &&
  14. c != '\\' &&
  15. c != '=' &&
  16. c != '$';
  17. }
  18. #define MAX_LEN (128 * 1024) /* Longest a result can expand to */
  19. /* Very basic expansion that looks for variable references like $NAME and expands them
  20. *
  21. */
  22. char *expand_environment(const char *input, const char *src_name, int src_line_no)
  23. {
  24. char *result = malloc(MAX_LEN);
  25. char *out = result;
  26. const char *in = input;
  27. while (*in != '\0') {
  28. // check for buffer overflow
  29. if (out >= result + MAX_LEN - 1) {
  30. goto too_long;
  31. }
  32. if (*in != '$') {
  33. // not part of an environment variable name, copy directly
  34. *out++ = *in++;
  35. continue;
  36. }
  37. // *in points to start of an environment variable reference
  38. in++;
  39. const char *env_start = in;
  40. while (allowed_env_var_name(*in)) { // scan to the end of the name
  41. in++;
  42. }
  43. size_t env_len = in - env_start;
  44. // make a buffer to hold the environment variable name
  45. //
  46. // strndup is not available on mingw32, apparently.
  47. char *env_name = calloc(1, env_len + 1);
  48. assert(env_name != NULL);
  49. strncpy(env_name, env_start, env_len);
  50. const char *value = getenv(env_name);
  51. if (value == NULL || strlen(value) == 0) {
  52. printf("%s:%d: undefined environment variable \"%s\"\n",
  53. src_name, src_line_no, env_name);
  54. exit(1);
  55. }
  56. free(env_name);
  57. if (out + strlen(value) >= result + MAX_LEN - 1) {
  58. goto too_long;
  59. }
  60. strcpy(out, value); // append the value to the result (range checked in previous statement)
  61. out += strlen(value);
  62. }
  63. *out = '\0'; // null terminate the result string
  64. return result;
  65. too_long:
  66. printf("%s:%d: Expansion is longer than %d bytes\n",
  67. src_name, src_line_no, MAX_LEN);
  68. free(result);
  69. exit(1);
  70. }
  71. void free_expanded(char *expanded)
  72. {
  73. free(expanded);
  74. }