+ WORD_DESC td;
+ WORD_LIST *list, *tlist;
+ size_t slen;
+ int i, saw_quote;
+ char *ret;
+ DECLARE_MBSTATE;
+
+ /* Don't need string length for ADVANCE_CHAR unless multibyte chars possible. */
+ slen = (MB_CUR_MAX > 1) ? strlen (string) : 0;
+ i = saw_quote = 0;
+ while (string[i])
+ {
+ if (EXP_CHAR (string[i]))
+ break;
+ else if (string[i] == '\'' || string[i] == '\\' || string[i] == '"')
+ saw_quote = 1;
+ ADVANCE_CHAR (string, slen, i);
+ }
+
+ if (string[i])
+ {
+ /* This is expanded version of expand_string_internal as it's called by
+ expand_string_leave_quoted */
+ td.flags = W_NOPROCSUB; /* don't want process substitution */
+ td.word = savestring (string);
+ list = call_expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL);
+ /* This takes care of the calls from expand_string_leave_quoted and
+ expand_string */
+ if (list)
+ {
+ tlist = word_list_split (list);
+ dispose_words (list);
+ list = tlist;
+ if (list)
+ dequote_list (list);
+ }
+ /* This comes from expand_string_if_necessary */
+ if (list)
+ {
+ ret = string_list (list);
+ dispose_words (list);
+ }
+ else
+ ret = (char *)NULL;
+ FREE (td.word);
+ }
+ else if (saw_quote && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0))
+ ret = string_quote_removal (string, quoted);
+ else
+ ret = savestring (string);
+
+ return ret;