+#ifdef HUSH_BRACE_EXP
+/* There in a GNU extension, GLOB_BRACE, but it is not usable:
+ * first, it processes even {a} (no commas), second,
+ * I didn't manage to make it return strings when they don't match
+ * existing files. Need to re-implement it.
+ */
+
+/* Helper */
+static int glob_needed(const char *s)
+{
+ while (*s) {
+ if (*s == '\\') {
+ if (!s[1])
+ return 0;
+ s += 2;
+ continue;
+ }
+ if (*s == '*' || *s == '[' || *s == '?' || *s == '{')
+ return 1;
+ s++;
+ }
+ return 0;
+}
+/* Return pointer to next closing brace or to comma */
+static const char *next_brace_sub(const char *cp)
+{
+ unsigned depth = 0;
+ cp++;
+ while (*cp != '\0') {
+ if (*cp == '\\') {
+ if (*++cp == '\0')
+ break;
+ cp++;
+ continue;
+ }
+ /*{*/ if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0))
+ break;
+ if (*cp++ == '{') /*}*/
+ depth++;
+ }
+
+ return *cp != '\0' ? cp : NULL;
+}
+/* Recursive brace globber. Note: may garble pattern[]. */
+static int glob_brace(char *pattern, o_string *o, int n)
+{
+ char *new_pattern_buf;
+ const char *begin;
+ const char *next;
+ const char *rest;
+ const char *p;
+ size_t rest_len;
+
+ debug_printf_glob("glob_brace('%s')\n", pattern);
+
+ begin = pattern;
+ while (1) {
+ if (*begin == '\0')
+ goto simple_glob;
+ if (*begin == '{') /*}*/ {
+ /* Find the first sub-pattern and at the same time
+ * find the rest after the closing brace */
+ next = next_brace_sub(begin);
+ if (next == NULL) {
+ /* An illegal expression */
+ goto simple_glob;
+ }
+ /*{*/ if (*next == '}') {
+ /* "{abc}" with no commas - illegal
+ * brace expr, disregard and skip it */
+ begin = next + 1;
+ continue;
+ }
+ break;
+ }
+ if (*begin == '\\' && begin[1] != '\0')
+ begin++;
+ begin++;
+ }
+ debug_printf_glob("begin:%s\n", begin);
+ debug_printf_glob("next:%s\n", next);
+
+ /* Now find the end of the whole brace expression */
+ rest = next;
+ /*{*/ while (*rest != '}') {
+ rest = next_brace_sub(rest);
+ if (rest == NULL) {
+ /* An illegal expression */
+ goto simple_glob;
+ }
+ debug_printf_glob("rest:%s\n", rest);
+ }
+ rest_len = strlen(++rest) + 1;
+
+ /* We are sure the brace expression is well-formed */
+
+ /* Allocate working buffer large enough for our work */
+ new_pattern_buf = xmalloc(strlen(pattern));
+
+ /* We have a brace expression. BEGIN points to the opening {,
+ * NEXT points past the terminator of the first element, and REST
+ * points past the final }. We will accumulate result names from
+ * recursive runs for each brace alternative in the buffer using
+ * GLOB_APPEND. */
+
+ p = begin + 1;
+ while (1) {
+ /* Construct the new glob expression */
+ memcpy(
+ mempcpy(
+ mempcpy(new_pattern_buf,
+ /* We know the prefix for all sub-patterns */
+ pattern, begin - pattern),
+ p, next - p),
+ rest, rest_len);
+
+ /* Note: glob_brace() may garble new_pattern_buf[].
+ * That's why we re-copy prefix every time (1st memcpy above).
+ */
+ n = glob_brace(new_pattern_buf, o, n);
+ /*{*/ if (*next == '}') {
+ /* We saw the last entry */
+ break;
+ }
+ p = next + 1;
+ next = next_brace_sub(next);
+ }
+ free(new_pattern_buf);
+ return n;
+
+ simple_glob:
+ {
+ int gr;
+ glob_t globdata;
+
+ memset(&globdata, 0, sizeof(globdata));
+ gr = glob(pattern, 0, NULL, &globdata);
+ debug_printf_glob("glob('%s'):%d\n", pattern, gr);
+ if (gr != 0) {
+ if (gr == GLOB_NOMATCH) {
+ globfree(&globdata);
+ /* NB: garbles parameter */
+ unbackslash(pattern);
+ o_addstr_with_NUL(o, pattern);
+ debug_printf_glob("glob pattern '%s' is literal\n", pattern);
+ return o_save_ptr_helper(o, n);
+ }
+ if (gr == GLOB_NOSPACE)
+ bb_error_msg_and_die(bb_msg_memory_exhausted);
+ /* GLOB_ABORTED? Only happens with GLOB_ERR flag,
+ * but we didn't specify it. Paranoia again. */
+ bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
+ }
+ if (globdata.gl_pathv && globdata.gl_pathv[0]) {
+ char **argv = globdata.gl_pathv;
+ while (1) {
+ o_addstr_with_NUL(o, *argv);
+ n = o_save_ptr_helper(o, n);
+ argv++;
+ if (!*argv)
+ break;
+ }
+ }
+ globfree(&globdata);
+ }
+ return n;
+}
+/* Performs globbing on last list[],
+ * saving each result as a new list[].
+ */
+static int o_glob(o_string *o, int n)
+{
+ char *pattern, *copy;
+
+ debug_printf_glob("start o_glob: n:%d o->data:%p\n", n, o->data);
+ if (!o->data)
+ return o_save_ptr_helper(o, n);
+ pattern = o->data + o_get_last_ptr(o, n);
+ debug_printf_glob("glob pattern '%s'\n", pattern);
+ if (!glob_needed(pattern)) {
+ /* unbackslash last string in o in place, fix length */
+ o->length = unbackslash(pattern) - o->data;
+ debug_printf_glob("glob pattern '%s' is literal\n", pattern);
+ return o_save_ptr_helper(o, n);
+ }
+
+ copy = xstrdup(pattern);
+ /* "forget" pattern in o */
+ o->length = pattern - o->data;
+ n = glob_brace(copy, o, n);
+ free(copy);
+ if (DEBUG_GLOB)
+ debug_print_list("o_glob returning", o, n);
+ return n;
+}
+
+#else
+
+/* Helper */
+static int glob_needed(const char *s)
+{
+ while (*s) {
+ if (*s == '\\') {
+ if (!s[1])
+ return 0;
+ s += 2;
+ continue;
+ }
+ if (*s == '*' || *s == '[' || *s == '?')
+ return 1;
+ s++;
+ }
+ return 0;
+}
+/* Performs globbing on last list[],
+ * saving each result as a new list[].
+ */