static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::vector<completion_t> &out, long last_idx) { int is_ok= 1; int empty=0; wcstring var_tmp; std::vector<long> var_idx_list; // CHECK( out, 0 ); for (long i=last_idx; (i>=0) && is_ok && !empty; i--) { const wchar_t c = in[i]; if ((c == VARIABLE_EXPAND) || (c == VARIABLE_EXPAND_SINGLE)) { long start_pos = i+1; long stop_pos; long var_len; int is_single = (c==VARIABLE_EXPAND_SINGLE); stop_pos = start_pos; while (1) { if (!(in[stop_pos ])) break; if (!(iswalnum(in[stop_pos]) || (wcschr(L"_", in[stop_pos])!= 0))) break; stop_pos++; } /* printf( "Stop for '%c'\n", in[stop_pos]);*/ var_len = stop_pos - start_pos; if (var_len == 0) { expand_variable_error(parser, in, stop_pos-1, -1); is_ok = 0; break; } var_tmp.append(in + start_pos, var_len); env_var_t var_val = expand_var(var_tmp.c_str()); if (! var_val.missing()) { int all_vars=1; wcstring_list_t var_item_list; if (is_ok) { tokenize_variable_array(var_val.c_str(), var_item_list); if (in[stop_pos] == L'[') { wchar_t *slice_end; all_vars=0; if (parse_slice(in + stop_pos, &slice_end, var_idx_list, var_item_list.size())) { parser.error(SYNTAX_ERROR, -1, L"Invalid index value"); is_ok = 0; break; } stop_pos = (slice_end-in); } if (!all_vars) { wcstring_list_t string_values(var_idx_list.size()); for (size_t j=0; j<var_idx_list.size(); j++) { long tmp = var_idx_list.at(j); /* Check that we are within array bounds. If not, truncate the list to exit. */ if (tmp < 1 || (size_t)tmp > var_item_list.size()) { parser.error(SYNTAX_ERROR, -1, ARRAY_BOUNDS_ERR); is_ok=0; var_idx_list.resize(j); break; } else { /* Replace each index in var_idx_list inplace with the string value at the specified index */ //al_set( var_idx_list, j, wcsdup((const wchar_t *)al_get( &var_item_list, tmp-1 ) ) ); string_values.at(j) = var_item_list.at(tmp-1); } } // string_values is the new var_item_list var_item_list.swap(string_values); } } if (is_ok) { if (is_single) { in[i]=0; wcstring res = in; res.push_back(INTERNAL_SEPARATOR); for (size_t j=0; j<var_item_list.size(); j++) { const wcstring &next = var_item_list.at(j); if (is_ok) { if (j != 0) res.append(L" "); res.append(next); } } res.append(in + stop_pos); is_ok &= expand_variables2(parser, res, out, i); } else { for (size_t j=0; j<var_item_list.size(); j++) { const wcstring &next = var_item_list.at(j); if (is_ok && (i == 0) && (!in[stop_pos])) { append_completion(out, next); } else { if (is_ok) { wcstring new_in; if (start_pos > 0) new_in.append(in, start_pos - 1); // at this point new_in.size() is start_pos - 1 if (start_pos>1 && new_in[start_pos-2]!=VARIABLE_EXPAND) { new_in.push_back(INTERNAL_SEPARATOR); } new_in.append(next); new_in.append(in + stop_pos); is_ok &= expand_variables2(parser, new_in, out, i); } } } } } return is_ok; } else { /* Expand a non-existing variable */ if (c == VARIABLE_EXPAND) { /* Regular expansion, i.e. expand this argument to nothing */ empty = 1; } else { /* Expansion to single argument. */ wcstring res; in[i] = 0; res.append(in); res.append(in + stop_pos); is_ok &= expand_variables2(parser, res, out, i); return is_ok; } } } } if (!empty) { append_completion(out, in); } return is_ok; }
/** Perform cmdsubst expansion */ static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector<completion_t> &outList) { wchar_t *paran_begin=0, *paran_end=0; std::vector<wcstring> sub_res; size_t i, j; wchar_t *tail_begin = 0; const wchar_t * const in = input.c_str(); int parse_ret; switch (parse_ret = parse_util_locate_cmdsubst(in, ¶n_begin, ¶n_end, 0)) { case -1: parser.error(SYNTAX_ERROR, -1, L"Mismatched parenthesis"); return 0; case 0: outList.push_back(completion_t(input)); return 1; case 1: break; } const wcstring subcmd(paran_begin + 1, paran_end-paran_begin - 1); if (exec_subshell(subcmd, sub_res) == -1) { parser.error(CMDSUBST_ERROR, -1, L"Unknown error while evaulating command substitution"); return 0; } tail_begin = paran_end + 1; if (*tail_begin == L'[') { std::vector<long> slice_idx; wchar_t *slice_end; if (parse_slice(tail_begin, &slice_end, slice_idx, sub_res.size())) { parser.error(SYNTAX_ERROR, -1, L"Invalid index value"); return 0; } else { std::vector<wcstring> sub_res2; tail_begin = slice_end; for (i=0; i < slice_idx.size(); i++) { long idx = slice_idx.at(i); if (idx < 1 || (size_t)idx > sub_res.size()) { parser.error(SYNTAX_ERROR, -1, ARRAY_BOUNDS_ERR); return 0; } idx = idx-1; sub_res2.push_back(sub_res.at(idx)); // debug( 0, L"Pushing item '%ls' with index %d onto sliced result", al_get( sub_res, idx ), idx ); //sub_res[idx] = 0; // ?? } sub_res = sub_res2; } } /* Recursively call ourselves to expand any remaining command substitutions. The result of this recursive call using the tail of the string is inserted into the tail_expand array list */ std::vector<completion_t> tail_expand; expand_cmdsubst(parser, tail_begin, tail_expand); /* Combine the result of the current command substitution with the result of the recursive tail expansion */ for (i=0; i<sub_res.size(); i++) { wcstring sub_item = sub_res.at(i); wcstring sub_item2 = escape_string(sub_item, 1); for (j=0; j < tail_expand.size(); j++) { wcstring whole_item; wcstring tail_item = tail_expand.at(j).completion; //sb_append_substring( &whole_item, in, len1 ); whole_item.append(in, paran_begin-in); //sb_append_char( &whole_item, INTERNAL_SEPARATOR ); whole_item.push_back(INTERNAL_SEPARATOR); //sb_append_substring( &whole_item, sub_item2, item_len ); whole_item.append(sub_item2); //sb_append_char( &whole_item, INTERNAL_SEPARATOR ); whole_item.push_back(INTERNAL_SEPARATOR); //sb_append( &whole_item, tail_item ); whole_item.append(tail_item); //al_push( out, whole_item.buff ); outList.push_back(completion_t(whole_item)); } } return 1; }
void expand_variable_error(parser_t &parser, const wchar_t *token, size_t token_pos, int error_pos) { size_t stop_pos = token_pos+1; switch (token[stop_pos]) { case BRACKET_BEGIN: { wchar_t *cpy = wcsdup(token); *(cpy+token_pos)=0; wchar_t *name = &cpy[stop_pos+1]; wchar_t *end = wcschr(name, BRACKET_END); wchar_t *post; int is_var=0; if (end) { post = end+1; *end = 0; if (!wcsvarname(name)) { is_var = 1; } } if (is_var) { parser.error(SYNTAX_ERROR, error_pos, COMPLETE_VAR_BRACKET_DESC, cpy, name, post); } else { parser.error(SYNTAX_ERROR, error_pos, COMPLETE_VAR_BRACKET_DESC, L"", L"VARIABLE", L""); } free(cpy); break; } case INTERNAL_SEPARATOR: { parser.error(SYNTAX_ERROR, error_pos, COMPLETE_VAR_PARAN_DESC); break; } case 0: { parser.error(SYNTAX_ERROR, error_pos, COMPLETE_VAR_NULL_DESC); break; } default: { wchar_t token_stop_char = token[stop_pos]; // Unescape (see http://github.com/fish-shell/fish-shell/issues/50) if (token_stop_char == ANY_CHAR) token_stop_char = L'?'; else if (token_stop_char == ANY_STRING || token_stop_char == ANY_STRING_RECURSIVE) token_stop_char = L'*'; parser.error(SYNTAX_ERROR, error_pos, (token_stop_char == L'?' ? COMPLETE_YOU_WANT_STATUS : COMPLETE_VAR_DESC), token_stop_char); break; } } }
/** Perform bracket expansion */ static int expand_brackets(parser_t &parser, const wcstring &instr, int flags, std::vector<completion_t> &out) { bool syntax_error = false; int bracket_count=0; const wchar_t *bracket_begin = NULL, *bracket_end = NULL; const wchar_t *last_sep = NULL; const wchar_t *item_begin; size_t length_preceding_brackets, length_following_brackets, tot_len; const wchar_t * const in = instr.c_str(); /* Locate the first non-nested bracket pair */ for (const wchar_t *pos = in; (*pos) && !syntax_error; pos++) { switch (*pos) { case BRACKET_BEGIN: { if (bracket_count == 0) bracket_begin = pos; bracket_count++; break; } case BRACKET_END: { bracket_count--; if (bracket_count < 0) { syntax_error = true; } else if (bracket_count == 0) { bracket_end = pos; break; } } case BRACKET_SEP: { if (bracket_count == 1) last_sep = pos; } } } if (bracket_count > 0) { if (!(flags & ACCEPT_INCOMPLETE)) { syntax_error = true; } else { /* The user hasn't typed an end bracket yet; make one up and append it, then expand that. */ wcstring mod; if (last_sep) { mod.append(in, bracket_begin-in+1); mod.append(last_sep+1); mod.push_back(BRACKET_END); } else { mod.append(in); mod.push_back(BRACKET_END); } return expand_brackets(parser, mod, 1, out); } } if (syntax_error) { parser.error(SYNTAX_ERROR, -1, _(L"Mismatched brackets")); return 0; } if (bracket_begin == NULL) { append_completion(out, instr); return 1; } length_preceding_brackets = (bracket_begin-in); length_following_brackets = wcslen(bracket_end)-1; tot_len = length_preceding_brackets+length_following_brackets; item_begin = bracket_begin+1; for (const wchar_t *pos =(bracket_begin+1); true; pos++) { if (bracket_count == 0) { if ((*pos == BRACKET_SEP) || (pos==bracket_end)) { assert(pos >= item_begin); size_t item_len = pos-item_begin; wcstring whole_item; whole_item.reserve(tot_len + item_len + 2); whole_item.append(in, length_preceding_brackets); whole_item.append(item_begin, item_len); whole_item.append(bracket_end + 1); expand_brackets(parser, whole_item, flags, out); item_begin = pos+1; if (pos == bracket_end) break; } } if (*pos == BRACKET_BEGIN) { bracket_count++; } if (*pos == BRACKET_END) { bracket_count--; } } return 1; }