- char *input ;
- char *s ;
-
- assert (target);
-
- input = xmalloc(ds_length(target) + 1);
- s = input;
-
- strcpy(input, ds_c_str(target));
-
- if (NULL == strchr (ds_c_str(target), '$'))
- goto done;
-
- ds_clear(target);
-
- for (;;)
- {
- switch (*s)
- {
- case '\0':
- goto done ;
-
- case '$':
- s++;
-
- if (*s == '$')
- {
- ds_putc (target, '$');
- s++;
- }
- else
- {
- int stop;
- int start;
- const char *value;
-
- start = ds_length (target);
-
- if (*s == '(')
- {
- stop = ')';
- s++;
- }
- else if (*s == '{')
- {
- stop = '}';
- s++;
- }
- else
- stop = 0;
-
- while (*s && *s != stop
- && (stop || isalpha ((unsigned char) *s)))
- {
- ds_putc (target, *s++);
- }
-
- value = getenv (ds_c_str (target) + start);
- ds_truncate (target, start);
- ds_puts (target, value);
-
- if (stop && *s == stop)
- s++;
- }
- break;
-
- default:
- ds_putc (target, *s++);
- }
- }
-
- done:
- free(input);
- return;
+ struct string dst = DS_EMPTY_INITIALIZER;
+ int c;
+
+ while ((c = ss_get_char (&src)) != EOF)
+ if (c != '$')
+ ds_put_char (&dst, c);
+ else
+ {
+ if (ss_match_char (&src, '$') || ss_is_empty (src))
+ ds_put_char (&dst, '$');
+ else
+ {
+ struct substring var_name;
+ size_t start;
+ const char *value;
+
+ if (ss_match_char (&src, '('))
+ ss_get_until (&src, ')', &var_name);
+ else if (ss_match_char (&src, '{'))
+ ss_get_until (&src, '}', &var_name);
+ else
+ ss_get_chars (&src, MIN (1, ss_span (src, ss_cstr (CC_ALNUM))),
+ &var_name);
+
+ start = ds_length (&dst);
+ ds_put_substring (&dst, var_name);
+ value = getenv (ds_cstr (&dst) + start);
+ ds_truncate (&dst, start);
+
+ ds_put_cstr (&dst, value);
+ }
+ }
+
+ ds_swap (&dst, dst_);
+ ds_destroy (&dst);