/** Test if this argument contains any errors. Detected errors include syntax errors in command substitutions, improperly escaped characters and improper use of the variable expansion operator. */ parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t &node, const wcstring &arg_src, parse_error_list_t *out_errors) { assert(node.type == symbol_argument); int err=0; wchar_t *paran_begin, *paran_end; int do_loop = 1; wcstring working_copy = arg_src; while (do_loop) { const wchar_t *working_copy_cstr = working_copy.c_str(); switch (parse_util_locate_cmdsubst(working_copy_cstr, ¶n_begin, ¶n_end, false)) { case -1: { err=1; if (out_errors) { append_syntax_error(out_errors, node, L"Mismatched parenthesis"); } return err; } case 0: { do_loop = 0; break; } case 1: { const wcstring subst(paran_begin + 1, paran_end); // Replace the command substitution with just INTERNAL_SEPARATOR size_t cmd_sub_start = paran_begin - working_copy_cstr; size_t cmd_sub_len = paran_end + 1 - paran_begin; working_copy.replace(cmd_sub_start, cmd_sub_len, wcstring(1, INTERNAL_SEPARATOR)); parse_error_list_t subst_errors; err |= parse_util_detect_errors(subst, &subst_errors, false /* do not accept incomplete */); /* Our command substitution produced error offsets relative to its source. Tweak the offsets of the errors in the command substitution to account for both its offset within the string, and the offset of the node */ size_t error_offset = cmd_sub_start + 1 + node.source_start; parse_error_offset_source_start(&subst_errors, error_offset); if (out_errors != NULL) { out_errors->insert(out_errors->end(), subst_errors.begin(), subst_errors.end()); } break; } } } wcstring unesc; if (! unescape_string(working_copy, &unesc, UNESCAPE_SPECIAL)) { if (out_errors) { append_syntax_error(out_errors, node, L"Invalid token '%ls'", working_copy.c_str()); } return 1; } else { /* Check for invalid variable expansions */ const size_t unesc_size = unesc.size(); for (size_t idx = 0; idx < unesc_size; idx++) { switch (unesc.at(idx)) { case VARIABLE_EXPAND: case VARIABLE_EXPAND_SINGLE: { wchar_t next_char = (idx + 1 < unesc_size ? unesc.at(idx + 1) : L'\0'); if (next_char != VARIABLE_EXPAND && next_char != VARIABLE_EXPAND_SINGLE && ! wcsvarchr(next_char)) { err=1; if (out_errors) { parse_util_expand_variable_error(node, unesc, idx, node.source_start, out_errors); } } break; } } } } return err; }
/** Highlight operators (such as $, ~, %, as well as escaped characters. */ static void highlight_param( const wcstring &buffstr, std::vector<int> &colors, int pos, wcstring_list_t *error ) { const wchar_t * const buff = buffstr.c_str(); enum {e_unquoted, e_single_quoted, e_double_quoted} mode = e_unquoted; size_t in_pos, len = buffstr.size(); int bracket_count=0; int normal_status = colors.at(0); for (in_pos=0; in_pos<len; in_pos++) { wchar_t c = buffstr.at(in_pos); switch( mode ) { /* Mode 0 means unquoted string */ case e_unquoted: { if( c == L'\\' ) { size_t start_pos = in_pos; in_pos++; if( wcschr( L"~%", buff[in_pos] ) ) { if( in_pos == 1 ) { colors.at(start_pos) = HIGHLIGHT_ESCAPE; colors.at(in_pos+1) = normal_status; } } else if( buff[in_pos]==L',' ) { if( bracket_count ) { colors.at(start_pos) = HIGHLIGHT_ESCAPE; colors.at(in_pos+1) = normal_status; } } else if( wcschr( L"abefnrtv*?$(){}[]'\"<>^ \\#;|&", buff[in_pos] ) ) { colors.at(start_pos)=HIGHLIGHT_ESCAPE; colors.at(in_pos+1)=normal_status; } else if( wcschr( L"c", buff[in_pos] ) ) { colors.at(start_pos)=HIGHLIGHT_ESCAPE; if (in_pos+2 < colors.size()) colors.at(in_pos+2)=normal_status; } else if( wcschr( L"uUxX01234567", buff[in_pos] ) ) { int i; long long res=0; int chars=2; int base=16; wchar_t max_val = ASCII_MAX; switch( buff[in_pos] ) { case L'u': { chars=4; max_val = UCS2_MAX; break; } case L'U': { chars=8; max_val = WCHAR_MAX; break; } case L'x': { break; } case L'X': { max_val = BYTE_MAX; break; } default: { base=8; chars=3; in_pos--; break; } } for( i=0; i<chars; i++ ) { int d = convert_digit( buff[++in_pos],base); if( d < 0 ) { in_pos--; break; } res=(res*base)|d; } if( (res <= max_val) ) { colors.at(start_pos) = HIGHLIGHT_ESCAPE; colors.at(in_pos+1) = normal_status; } else { colors.at(start_pos) = HIGHLIGHT_ERROR; colors.at(in_pos+1) = normal_status; } } } else { switch( buff[in_pos]){ case L'~': case L'%': { if( in_pos == 0 ) { colors.at(in_pos) = HIGHLIGHT_OPERATOR; colors.at(in_pos+1) = normal_status; } break; } case L'$': { wchar_t n = buff[in_pos+1]; colors.at(in_pos) = (n==L'$'||wcsvarchr(n))? HIGHLIGHT_OPERATOR:HIGHLIGHT_ERROR; colors.at(in_pos+1) = normal_status; break; } case L'*': case L'?': case L'(': case L')': { colors.at(in_pos) = HIGHLIGHT_OPERATOR; colors.at(in_pos+1) = normal_status; break; } case L'{': { colors.at(in_pos) = HIGHLIGHT_OPERATOR; colors.at(in_pos+1) = normal_status; bracket_count++; break; } case L'}': { colors.at(in_pos) = HIGHLIGHT_OPERATOR; colors.at(in_pos+1) = normal_status; bracket_count--; break; } case L',': { if( bracket_count ) { colors.at(in_pos) = HIGHLIGHT_OPERATOR; colors.at(in_pos+1) = normal_status; } break; } case L'\'': { colors.at(in_pos) = HIGHLIGHT_QUOTE; mode = e_single_quoted; break; } case L'\"': { colors.at(in_pos) = HIGHLIGHT_QUOTE; mode = e_double_quoted; break; } } } break; } /* Mode 1 means single quoted string, i.e 'foo' */ case e_single_quoted: { if( c == L'\\' ) { int start_pos = in_pos; switch( buff[++in_pos] ) { case '\\': case L'\'': { colors.at(start_pos) = HIGHLIGHT_ESCAPE; colors.at(in_pos+1) = HIGHLIGHT_QUOTE; break; } case 0: { return; } } } if( c == L'\'' ) { mode = e_unquoted; colors.at(in_pos+1) = normal_status; } break; } /* Mode 2 means double quoted string, i.e. "foo" */ case e_double_quoted: { switch( c ) { case '"': { mode = e_unquoted; colors.at(in_pos+1) = normal_status; break; } case '\\': { int start_pos = in_pos; switch( buff[++in_pos] ) { case L'\0': { return; } case '\\': case L'$': case '"': { colors.at(start_pos) = HIGHLIGHT_ESCAPE; colors.at(in_pos+1) = HIGHLIGHT_QUOTE; break; } } break; } case '$': { wchar_t n = buff[in_pos+1]; colors.at(in_pos) = (n==L'$'||wcsvarchr(n))? HIGHLIGHT_OPERATOR:HIGHLIGHT_ERROR; colors.at(in_pos+1) = HIGHLIGHT_QUOTE; break; } } break; } } } }