示例#1
0
void parse_util_cmdsubst_extent(const wchar_t *buff, size_t cursor_pos, const wchar_t **a, const wchar_t **b)
{
    const wchar_t * const cursor = buff + cursor_pos;

    CHECK(buff,);

    const size_t bufflen = wcslen(buff);
    assert(cursor_pos <= bufflen);

    /* ap and bp are the beginning and end of the tightest command substitition found so far */
    const wchar_t *ap = buff, *bp = buff + bufflen;
    const wchar_t *pos = buff;
    for (;;)
    {
        wchar_t *begin = NULL, *end = NULL;
        if (parse_util_locate_cmdsubst(pos, &begin, &end, true) <= 0)
        {
            /* No subshell found, all done */
            break;
        }
        /* Interpret NULL to mean the end */
        if (end == NULL)
        {
            end = const_cast<wchar_t *>(buff) + bufflen;
        }

        if (begin < cursor && end >= cursor)
        {
            /* This command substitution surrounds the cursor, so it's a tighter fit */
            begin++;
            ap = begin;
            bp = end;
            /* pos is where to begin looking for the next one. But if we reached the end there's no next one. */
            if (begin >= end)
                break;
            pos = begin + 1;
        }
        else if (begin >= cursor)
        {
            /* This command substitution starts at or after the cursor. Since it was the first command substitution in the string, we're done. */
            break;
        }
        else
        {
            /* This command substitution ends before the cursor. Skip it. */
            assert(end < cursor);
            pos = end + 1;
            assert(pos <= buff + bufflen);
        }
    }

    if (a != NULL) *a = ap;
    if (b != NULL) *b = bp;
}
示例#2
0
int expand_string(const wcstring &input, std::vector<completion_t> &output, expand_flags_t flags)
{
    parser_t parser(PARSER_TYPE_ERRORS_ONLY, true /* show errors */);
    std::vector<completion_t> list1, list2;
    std::vector<completion_t> *in, *out;

    size_t i;
    int res = EXPAND_OK;

    if ((!(flags & ACCEPT_INCOMPLETE)) && expand_is_clean(input.c_str()))
    {
        output.push_back(completion_t(input));
        return EXPAND_OK;
    }

    if (EXPAND_SKIP_CMDSUBST & flags)
    {
        wchar_t *begin, *end;

        if (parse_util_locate_cmdsubst(input.c_str(),
                                       &begin,
                                       &end,
                                       1) != 0)
        {
            parser.error(CMDSUBST_ERROR, -1, L"Command substitutions not allowed");
            return EXPAND_ERROR;
        }
        list1.push_back(completion_t(input));
    }
    else
    {
        int cmdsubst_ok = expand_cmdsubst(parser, input, list1);
        if (! cmdsubst_ok)
            return EXPAND_ERROR;
    }

    in = &list1;
    out = &list2;

    for (i=0; i < in->size(); i++)
    {
        /*
         We accept incomplete strings here, since complete uses
         expand_string to expand incomplete strings from the
         commandline.
         */
        int unescape_flags = UNESCAPE_SPECIAL | UNESCAPE_INCOMPLETE;
        wcstring next = expand_unescape_string(in->at(i).completion, unescape_flags);

        if (EXPAND_SKIP_VARIABLES & flags)
        {
            for (size_t i=0; i < next.size(); i++)
            {
                if (next.at(i) == VARIABLE_EXPAND)
                {
                    next[i] = L'$';
                }
            }
            out->push_back(completion_t(next));
        }
        else
        {
            if (!expand_variables2(parser, next, *out, next.size() - 1))
            {
                return EXPAND_ERROR;
            }
        }
    }

    in->clear();

    in = &list2;
    out = &list1;

    for (i=0; i < in->size(); i++)
    {
        wcstring next = in->at(i).completion;

        if (!expand_brackets(parser, next, flags, *out))
        {
            return EXPAND_ERROR;
        }
    }
    in->clear();

    in = &list1;
    out = &list2;

    for (i=0; i < in->size(); i++)
    {
        wcstring next = in->at(i).completion;

        expand_home_directory(next);


        if (flags & ACCEPT_INCOMPLETE)
        {
            if (next[0] == PROCESS_EXPAND)
            {
                /*
                 If process expansion matches, we are not
                 interested in other completions, so we
                 short-circut and return
                 */
                if (!(flags & EXPAND_SKIP_PROCESS))
                    expand_pid(next, flags, output);
                return EXPAND_OK;
            }
            else
            {
                out->push_back(completion_t(next));
            }
        }
        else
        {
            if (!(flags & EXPAND_SKIP_PROCESS) && ! expand_pid(next, flags, *out))
            {
                return EXPAND_ERROR;
            }
        }
    }

    in->clear();

    in = &list2;
    out = &list1;

    for (i=0; i < in->size(); i++)
    {
        wcstring next_str = in->at(i).completion;
        int wc_res;

        remove_internal_separator(next_str, (EXPAND_SKIP_WILDCARDS & flags) ? true : false);
        const wchar_t *next = next_str.c_str();

        if (((flags & ACCEPT_INCOMPLETE) && (!(flags & EXPAND_SKIP_WILDCARDS))) ||
                wildcard_has(next, 1))
        {
            const wchar_t *start, *rest;
            std::vector<completion_t> *list = out;

            if (next[0] == '/')
            {
                start = L"/";
                rest = &next[1];
            }
            else
            {
                start = L"";
                rest = next;
            }

            if (flags & ACCEPT_INCOMPLETE)
            {
                list = &output;
            }

            wc_res = wildcard_expand_string(rest, start, flags, *list);

            if (!(flags & ACCEPT_INCOMPLETE))
            {

                switch (wc_res)
                {
                    case 0:
                    {
                        if (!(flags & ACCEPT_INCOMPLETE))
                        {
                            if (res == EXPAND_OK)
                                res = EXPAND_WILDCARD_NO_MATCH;
                            break;
                        }
                    }

                    case 1:
                    {
                        size_t j;
                        res = EXPAND_WILDCARD_MATCH;
                        sort_completions(*out);

                        for (j=0; j< out->size(); j++)
                        {
                            output.push_back(out->at(j));
                        }
                        out->clear();
                        break;
                    }

                    case -1:
                    {
                        return EXPAND_ERROR;
                    }

                }
            }

        }
        else
        {
            if (flags & ACCEPT_INCOMPLETE)
            {
            }
            else
            {
                output.push_back(completion_t(next));
            }
        }

    }

    return res;
}
示例#3
0
/**
 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,
                        &paran_begin,
                        &paran_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;
}
示例#4
0
/**
   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,
                                           &paran_begin,
                                           &paran_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;
}
示例#5
0
void parse_util_cmdsubst_extent( const wchar_t *buff,
								 int cursor_pos,
								 const wchar_t **a, 
								 const wchar_t **b )
{
	wchar_t *begin, *end;
	wchar_t *pos;
	const wchar_t *cursor = buff + cursor_pos;
	
	CHECK( buff, );

	if( a )
	{
		*a = (wchar_t *)buff;
	}

	if( b )
	{
		*b = (wchar_t *)buff+wcslen(buff);
	}
	
	pos = (wchar_t *)buff;
	
	while( 1 )
	{
		if( parse_util_locate_cmdsubst( pos,
										&begin,
										&end,
										1 ) <= 0)
		{
			/*
			  No subshell found
			*/
			break;
		}

		if( !end )
		{
			end = (wchar_t *)buff + wcslen(buff);
		}

		if(( begin < cursor ) && (end >= cursor) )
		{
			begin++;

			if( a )
			{
				*a = begin;
			}

			if( b )
			{
				*b = end;
			}

			break;
		}

		if( !*end )
		{
			break;
		}
		
		pos = end+1;
	}
	
}
示例#6
0
// PCA This function does I/O, (calls is_potential_path, path_get_path, maybe others) and so ought to only run on a background thread
void highlight_shell( const wcstring &buff, std::vector<int> &color, int pos, wcstring_list_t *error, const env_vars_snapshot_t &vars )
{
    ASSERT_IS_BACKGROUND_THREAD();
    
    const size_t length = buff.size();
    assert(buff.size() == color.size());


	if( length == 0 )
		return;
	
    std::fill(color.begin(), color.end(), -1);

    /* Do something sucky and get the current working directory on this background thread. This should really be passed in. Note that we also need this as a vector (of one directory). */
    const wcstring working_directory = get_working_directory();

    /* Tokenize the string */
    tokenize(buff.c_str(), color, pos, error, working_directory, vars);

	/*
	  Locate and syntax highlight cmdsubsts recursively
	*/

	wchar_t * const subbuff = wcsdup(buff.c_str());
    wchar_t * subpos = subbuff;
	int done=0;
	
	while( 1 )
	{
		wchar_t *begin, *end;
    
		if( parse_util_locate_cmdsubst(subpos, &begin, &end, 1) <= 0)
		{
			break;
		}
		
		if( !*end )
			done=1;
		else
			*end=0;
		
        //our subcolors start at color + (begin-subbuff)+1
        size_t start = begin - subbuff + 1, len = wcslen(begin + 1);
        std::vector<int> subcolors(len, -1);
        
		highlight_shell( begin+1, subcolors, -1, error, vars );
        
        // insert subcolors
        std::copy(subcolors.begin(), subcolors.end(), color.begin() + start);
        
        // highlight the end of the subcommand
        assert(end >= subbuff);
        if ((size_t)(end - subbuff) < length) {
            color.at(end-subbuff)=HIGHLIGHT_OPERATOR;
        }
		
		if( done )
			break;
		
		subpos = end+1;
	}
    free(subbuff);

	/*
	  The highlighting code only changes the first element when the
	  color changes. This fills in the rest.
	*/
	int last_val=0;
	for( size_t i=0; i < buff.size(); i++ )
	{
		if( color.at(i) >= 0 )
			last_val = color.at(i);
		else
			color.at(i) = last_val;
	}
    
	/*
	  Color potentially valid paths in a special path color if they
	  are the current token.
      For reasons that I don't yet understand, it's required that pos be allowed to be length (e.g. when backspacing).
	*/
	if( pos >= 0 && (size_t)pos <= length )
	{
		
        const wchar_t *cbuff = buff.c_str();
		const wchar_t *tok_begin, *tok_end;
		parse_util_token_extent( cbuff, pos, &tok_begin, &tok_end, 0, 0 );
		if( tok_begin && tok_end )
		{
			wcstring token(tok_begin, tok_end-tok_begin);
			const wcstring_list_t working_directory_list(1, working_directory);
			if (unescape_string(token, 1) && is_potential_path(token, working_directory_list, PATH_EXPAND_TILDE))
			{
				for( ptrdiff_t i=tok_begin-cbuff; i < (tok_end-cbuff); i++ )
				{
                    // Don't color HIGHLIGHT_ERROR because it looks dorky. For example, trying to cd into a non-directory would show an underline and also red.
                    if (! (color.at(i) & HIGHLIGHT_ERROR)) {
                        color.at(i) |= HIGHLIGHT_VALID_PATH;
                    }
				}
			}
		}
	}
	

	highlight_universal_internal( buff, color, pos );

	/*
	  Spaces should not be highlighted at all, since it makes cursor look funky in some terminals
	*/
	for( size_t i=0; i < buff.size(); i++ )
	{
		if( iswspace(buff.at(i)) )
		{
			color.at(i)=0;
		}
	}
}