Exemplo n.º 1
0
void history_t::get_string_representation(wcstring &result, const wcstring &separator)
{
    scoped_lock locker(lock);
    
    bool first = true;
    
    /* Append new items */
    for (std::vector<history_item_t>::const_reverse_iterator iter=new_items.rbegin(); iter < new_items.rend(); ++iter) {
        if (! first)
            result.append(separator);
        result.append(iter->str());
        first = false;
    }
    
    /* Append old items */
    load_old_if_needed();
    for (std::deque<size_t>::const_reverse_iterator iter = old_item_offsets.rbegin(); iter != old_item_offsets.rend(); ++iter) {        
        size_t offset = *iter;
        const history_item_t item = history_t::decode_item(mmap_start + offset, mmap_length - offset);
        if (! first)
            result.append(separator);
        result.append(item.str());
        first = false;
    }
}
Exemplo n.º 2
0
bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_directory, wcstring &outSuggestion) {
    if (str.empty())
        return false;
        
    ASSERT_IS_BACKGROUND_THREAD();
    
    /* Parse the string */
    wcstring parsed_command;
    wcstring_list_t parsed_arguments;
    int parsed_last_arg_pos = -1;
    if (! autosuggest_parse_command(str, &parsed_command, &parsed_arguments, &parsed_last_arg_pos))
        return false;
    
    bool result = false;
    if (parsed_command == L"cd" && ! parsed_arguments.empty()) {        
        /* We can possibly handle this specially */
        wcstring dir = parsed_arguments.back();
        wcstring suggested_path;
        
        /* We always return true because we recognized the command. This prevents us from falling back to dumber algorithms; for example we won't suggest a non-directory for the cd command. */
        result = true;
        outSuggestion.clear();

        if (is_potential_cd_path(dir, working_directory, &suggested_path)) {
            /* Success */
            outSuggestion = str;
            outSuggestion.erase(parsed_last_arg_pos);
            outSuggestion.append(suggested_path);
        }
    } else {
        /* Either an error or some other command, so we don't handle it specially */
    }
    return result;
}
Exemplo n.º 3
0
void append_path_component(wcstring &path, const wcstring &component)
{
    if (path.empty() || component.empty()) {
        path.append(component);
    } else {
        size_t path_len = path.size();
        bool path_slash = path.at(path_len-1) == L'/';
        bool comp_slash = component.at(0) == L'/';
        if (! path_slash && ! comp_slash) {
            // Need a slash
            path.push_back(L'/');
        } else if (path_slash && comp_slash) {
            // Too many slashes
            path.erase(path_len - 1, 1);
        }
        path.append(component);
    }
}
Exemplo n.º 4
0
void parser_t::get_backtrace(const wcstring &src, const parse_error_list_t &errors,
                             wcstring &output) const {
    if (!errors.empty()) {
        const parse_error_t &err = errors.at(0);

        const bool is_interactive = shell_is_interactive();

        // Determine if we want to try to print a caret to point at the source error. The
        // err.source_start <= src.size() check is due to the nasty way that slices work, which is
        // by rewriting the source.
        size_t which_line = 0;
        bool skip_caret = true;
        if (err.source_start != SOURCE_LOCATION_UNKNOWN && err.source_start <= src.size()) {
            // Determine which line we're on.
            which_line = 1 + std::count(src.begin(), src.begin() + err.source_start, L'\n');

            // Don't include the caret if we're interactive, this is the first line of text, and our
            // source is at its beginning, because then it's obvious.
            skip_caret = (is_interactive && which_line == 1 && err.source_start == 0);
        }

        wcstring prefix;
        const wchar_t *filename = this->current_filename();
        if (filename) {
            if (which_line > 0) {
                prefix = format_string(_(L"%ls (line %lu): "),
                                       user_presentable_path(filename).c_str(), which_line);
            } else {
                prefix = format_string(_(L"%ls: "), user_presentable_path(filename).c_str());
            }
        } else {
            prefix = L"fish: ";
        }

        const wcstring description =
            err.describe_with_prefix(src, prefix, is_interactive, skip_caret);
        if (!description.empty()) {
            output.append(description);
            output.push_back(L'\n');
        }
        output.append(this->stack_trace());
    }
}
Exemplo n.º 5
0
static void print_colors(void)
{
    const wcstring_list_t result = rgb_color_t::named_color_names();
    size_t i;
    for (i=0; i < result.size(); i++)
    {
        stdout_buffer.append(result.at(i));
        stdout_buffer.push_back(L'\n');
    }
}
Exemplo n.º 6
0
void append_format(wcstring &str, const wchar_t *format, ...)
{
    /* Preserve errno across this call since it likes to stomp on it */
    int err = errno;
	va_list va;
	va_start( va, format );
    str.append(vformat_string(format, va));
	va_end( va );
    errno = err;
}
Exemplo n.º 7
0
/**
   Print the names of all environment variables in the scope, with or without shortening,
   with or without values, with or without escaping
*/
static void print_variables(int include_values, int esc, bool shorten_ok, int scope) 
{
    wcstring_list_t names = env_get_names(scope);
    sort(names.begin(), names.end());
	
	for( size_t i = 0; i < names.size(); i++ )
	{
		const wcstring key = names.at(i);
        const wcstring e_key = escape_string(key, 0);

		stdout_buffer.append(e_key);
		
		if( include_values ) 
		{
			env_var_t value = env_get_string(key);
			if( !value.missing() )
			{
				int shorten = 0;
				
				if( shorten_ok && value.length() > 64 )
				{
					shorten = 1;
					value.resize(60);
				}
				
				wcstring e_value = esc ? expand_escape_variable(value) : value;
				
                stdout_buffer.append(L" ");
                stdout_buffer.append(e_value);
				
				if( shorten )
				{
					stdout_buffer.append(L"\u2026");
				}

			}
		}
		
		stdout_buffer.append(L"\n");
	}
}
Exemplo n.º 8
0
/* We have to return an escaped string here */
bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_directory, wcstring &outSuggestion) {
    if (str.empty())
        return false;
        
    ASSERT_IS_BACKGROUND_THREAD();
    
    /* Parse the string */
    wcstring parsed_command;
    wcstring_list_t parsed_arguments;
    int parsed_last_arg_pos = -1;
    if (! autosuggest_parse_command(str, &parsed_command, &parsed_arguments, &parsed_last_arg_pos))
        return false;
    
    bool result = false;
    if (parsed_command == L"cd" && ! parsed_arguments.empty()) {        
        /* We can possibly handle this specially */
        const wcstring escaped_dir = parsed_arguments.back();
        wcstring suggested_path;
        
        /* We always return true because we recognized the command. This prevents us from falling back to dumber algorithms; for example we won't suggest a non-directory for the cd command. */
        result = true;
        outSuggestion.clear();
        
        /* Unescape the parameter */
        wcstring unescaped_dir = escaped_dir;
        bool unescaped = unescape_string(unescaped_dir, UNESCAPE_INCOMPLETE);
        
        /* Determine the quote type we got from the input directory. */
        wchar_t quote = L'\0';
        parse_util_get_parameter_info(escaped_dir, 0, &quote, NULL, NULL);
        
        /* Big hack to avoid expanding a tilde inside quotes */
        path_flags_t path_flags = (quote == L'\0') ? PATH_EXPAND_TILDE : 0;
        if (unescaped && is_potential_cd_path(unescaped_dir, working_directory, path_flags, &suggested_path)) {
                    
            /* Note: this looks really wrong for strings that have an "unescapable" character in them, e.g. a \t, because parse_util_escape_string_with_quote will insert that character */
            wcstring escaped_suggested_path = parse_util_escape_string_with_quote(suggested_path, quote);

            /* Return it */
            outSuggestion = str;
            outSuggestion.erase(parsed_last_arg_pos);
            if (quote != L'\0') outSuggestion.push_back(quote);
            outSuggestion.append(escaped_suggested_path);
            if (quote != L'\0') outSuggestion.push_back(quote);
        }
    } else {
        /* Either an error or some other command, so we don't handle it specially */
    }
    return result;
}
Exemplo n.º 9
0
/**
   set_color builtin
*/
static int builtin_set_color(parser_t &parser, wchar_t **argv)
{
    /** Variables used for parsing the argument list */
    const struct woption long_options[] =
    {
        { L"background", required_argument, 0, 'b'},
        { L"help", no_argument, 0, 'h' },
        { L"bold", no_argument, 0, 'o' },
        { L"underline", no_argument, 0, 'u' },
        { L"version", no_argument, 0, 'v' },
        { L"print-colors", no_argument, 0, 'c' },
        { 0, 0, 0, 0 }
    };

    const wchar_t *short_options = L"b:hvocu";

    int argc = builtin_count_args(argv);

    /* Some code passes variables to set_color that don't exist, like $fish_user_whatever. As a hack, quietly return failure. */
    if (argc <= 1)
    {
        return EXIT_FAILURE;
    }

    const wchar_t *bgcolor = NULL;
    bool bold = false, underline=false;
    int errret;

    /* Parse options to obtain the requested operation and the modifiers */
    woptind = 0;
    while (1)
    {
        int c = wgetopt_long(argc, argv, short_options, long_options, 0);

        if (c == -1)
        {
            break;
        }

        switch (c)
        {
            case 0:
                break;

            case 'b':
                bgcolor = woptarg;
                break;

            case 'h':
                builtin_print_help(parser, argv[0], stdout_buffer);
                return STATUS_BUILTIN_OK;

            case 'o':
                bold = true;
                break;

            case 'u':
                underline = true;
                break;

            case 'c':
                print_colors();
                return STATUS_BUILTIN_OK;

            case '?':
                return STATUS_BUILTIN_ERROR;
        }
    }

    /* Remaining argument is foreground color */
    const wchar_t *fgcolor = NULL;
    if (woptind < argc)
    {
        if (woptind + 1 == argc)
        {
            fgcolor = argv[woptind];
        }
        else
        {
            append_format(stderr_buffer,
                          _(L"%ls: Too many arguments\n"),
                          argv[0]);
            return STATUS_BUILTIN_ERROR;
        }
    }

    if (fgcolor == NULL && bgcolor == NULL && !bold && !underline)
    {
        append_format(stderr_buffer,
                      _(L"%ls: Expected an argument\n"),
                      argv[0]);
        return STATUS_BUILTIN_ERROR;
    }

    const rgb_color_t fg = rgb_color_t(fgcolor ? fgcolor : L"");
    if (fgcolor && (fg.is_none() || fg.is_ignore()))
    {
        append_format(stderr_buffer, _(L"%ls: Unknown color '%ls'\n"), argv[0], fgcolor);
        return STATUS_BUILTIN_ERROR;
    }

    const rgb_color_t bg = rgb_color_t(bgcolor ? bgcolor : L"");
    if (bgcolor && (bg.is_none() || bg.is_ignore()))
    {
        append_format(stderr_buffer, _(L"%ls: Unknown color '%ls'\n"), argv[0], bgcolor);
        return STATUS_BUILTIN_ERROR;
    }

    /* Make sure that the term exists */
    if (cur_term == NULL && setupterm(0, STDOUT_FILENO, &errret) == ERR)
    {
        append_format(stderr_buffer, _(L"%ls: Could not set up terminal\n"), argv[0]);
        return STATUS_BUILTIN_ERROR;
    }

    /*
       Test if we have at least basic support for setting fonts, colors
       and related bits - otherwise just give up...
    */
    if (! exit_attribute_mode)
    {
        return STATUS_BUILTIN_ERROR;
    }


    /* Save old output function so we can restore it */
    int (* const saved_writer_func)(char) = output_get_writer();

    /* Set our output function, which writes to a std::string */
    builtin_set_color_output.clear();
    output_set_writer(set_color_builtin_outputter);

    if (bold)
    {
        if (enter_bold_mode)
            writembs(tparm(enter_bold_mode));
    }

    if (underline)
    {
        if (enter_underline_mode)
            writembs(enter_underline_mode);
    }

    if (bgcolor != NULL)
    {
        if (bg.is_normal())
        {
            write_color(rgb_color_t::black(), false /* not is_fg */);
            writembs(tparm(exit_attribute_mode));
        }
    }

    if (fgcolor != NULL)
    {
        if (fg.is_normal() || fg.is_reset())
        {
            write_color(rgb_color_t::black(), true /* is_fg */);
            writembs(tparm(exit_attribute_mode));
        }
        else
        {
            write_color(fg, true /* is_fg */);
        }
    }

    if (bgcolor != NULL)
    {
        if (! bg.is_normal() && ! bg.is_reset())
        {
            write_color(bg, false /* not is_fg */);
        }
    }

    /* Restore saved writer function */
    output_set_writer(saved_writer_func);

    /* Output the collected string */
    stdout_buffer.append(str2wcstring(builtin_set_color_output));
    builtin_set_color_output.clear();

    return STATUS_BUILTIN_OK;
}
Exemplo n.º 10
0
void parser_t::stack_trace(size_t block_idx, wcstring &buff) const
{
    /*
      Check if we should end the recursion
    */
    if (block_idx >= this->block_count())
        return;

    const block_t *b = this->block_at_index(block_idx);

    if (b->type()==EVENT)
    {
        /*
          This is an event handler
        */
        const event_block_t *eb = static_cast<const event_block_t *>(b);
        wcstring description = event_get_desc(eb->event);
        append_format(buff, _(L"in event handler: %ls\n"), description.c_str());
        buff.append(L"\n");

        /*
          Stop recursing at event handler. No reason to believe that
          any other code is relevant.

          It might make sense in the future to continue printing the
          stack trace of the code that invoked the event, if this is a
          programmatic event, but we can't currently detect that.
        */
        return;
    }

    if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW || b->type()==SOURCE || b->type()==SUBST)
    {
        /*
          These types of blocks should be printed
        */

        int i;

        switch (b->type())
        {
            case SOURCE:
            {
                const source_block_t *sb = static_cast<const source_block_t*>(b);
                const wchar_t *source_dest = sb->source_file;
                append_format(buff, _(L"from sourcing file %ls\n"), user_presentable_path(source_dest).c_str());
                break;
            }
            case FUNCTION_CALL:
            case FUNCTION_CALL_NO_SHADOW:
            {
                const function_block_t *fb = static_cast<const function_block_t*>(b);
                append_format(buff, _(L"in function '%ls'\n"), fb->name.c_str());
                break;
            }
            case SUBST:
            {
                append_format(buff, _(L"in command substitution\n"));
                break;
            }

            default: /* Can't get here */
                break;
        }

        const wchar_t *file = b->src_filename;

        if (file)
        {
            append_format(buff,
                          _(L"\tcalled on line %d of file %ls\n"),
                          b->src_lineno,
                          user_presentable_path(file).c_str());
        }
        else if (is_within_fish_initialization)
        {
            append_format(buff, _(L"\tcalled during startup\n"));
        }
        else
        {
            append_format(buff, _(L"\tcalled on standard input\n"));
        }

        if (b->type() == FUNCTION_CALL)
        {
            const function_block_t *fb = static_cast<const function_block_t *>(b);
            const process_t * const process = fb->process;
            if (process->argv(1))
            {
                wcstring tmp;

                for (i=1; process->argv(i); i++)
                {
                    if (i > 1)
                        tmp.push_back(L' ');
                    tmp.append(process->argv(i));
                }
                append_format(buff, _(L"\twith parameter list '%ls'\n"), tmp.c_str());
            }
        }

        append_format(buff, L"\n");
    }

    /*
      Recursively print the next block
    */
    parser_t::stack_trace(block_idx + 1, buff);
}
Exemplo n.º 11
0
/**
   Indent the specified input
 */
static int indent(wcstring &out, const wcstring &in, int flags)
{
    int res=0;
    int is_command = 1;
    int indent = 0;
    int do_indent = 1;
    int prev_type = 0;
    int prev_prev_type = 0;

    tokenizer_t tok(in.c_str(), TOK_SHOW_COMMENTS);
    for (; tok_has_next(&tok); tok_next(&tok))
    {
        int type = tok_last_type(&tok);
        const wchar_t *last = tok_last(&tok);

        switch (type)
        {
            case TOK_STRING:
            {
                if (is_command)
                {
                    int next_indent = indent;
                    is_command = 0;

                    wcstring unesc;
                    unescape_string(last, &unesc, UNESCAPE_SPECIAL);

                    if (parser_keywords_is_block(unesc))
                    {
                        next_indent++;
                    }
                    else if (unesc == L"else")
                    {
                        indent--;
                    }
                    /* case should have the same indent level as switch*/
                    else if (unesc == L"case")
                    {
                        indent--;
                    }
                    else if (unesc == L"end")
                    {
                        indent--;
                        next_indent--;
                    }


                    if (do_indent && flags && prev_type != TOK_PIPE)
                    {
                        insert_tabs(out, indent);
                    }

                    append_format(out, L"%ls", last);

                    indent = next_indent;

                }
                else
                {
                    if (prev_type != TOK_REDIRECT_FD)
                        out.append(L" ");
                    out.append(last);
                }

                break;
            }

            case TOK_END:
            {
                if (prev_type != TOK_END || prev_prev_type != TOK_END)
                    out.append(L"\n");
                do_indent = 1;
                is_command = 1;
                break;
            }

            case TOK_PIPE:
            {
                out.append(L" ");
                if (last[0] == '2' && !last[1])
                {
                    out.append(L"^");
                }
                else if (last[0] != '1' || last[1])
                {
                    out.append(last);
                    out.append(L">");
                }
                out.append(L" | ");
                is_command = 1;
                break;
            }

            case TOK_REDIRECT_OUT:
            {
                out.append(L" ");
                if (wcscmp(last, L"2") == 0)
                {
                    out.append(L"^");
                }
                else
                {
                    if (wcscmp(last, L"1") != 0)
                        out.append(last);
                    out.append(L"> ");
                }
                break;
            }

            case TOK_REDIRECT_APPEND:
            {
                out.append(L" ");
                if (wcscmp(last, L"2") == 0)
                {
                    out.append(L"^^");
                }
                else
                {
                    if (wcscmp(last, L"1") != 0)
                        out.append(last);
                    out.append(L">> ");
                }
                break;
            }

            case TOK_REDIRECT_IN:
            {
                out.append(L" ");
                if (wcscmp(last, L"0") != 0)
                    out.append(last);
                out.append(L"< ");
                break;
            }

            case TOK_REDIRECT_FD:
            {
                out.append(L" ");
                if (wcscmp(last, L"1") != 0)
                    out.append(last);
                out.append(L">& ");
                break;
            }

            case TOK_BACKGROUND:
            {
                out.append(L"&\n");
                do_indent = 1;
                is_command = 1;
                break;
            }

            case TOK_COMMENT:
            {
                if (do_indent && flags)
                {
                    insert_tabs(out, indent);
                }

                append_format(out, L"%ls", last);
                do_indent = 1;
                break;
            }

            default:
            {
                debug(0, L"Unknown token '%ls'", last);
                exit(1);
            }
        }

        prev_prev_type = prev_type;
        prev_type = type;

    }

    return res;
}
Exemplo n.º 12
0
/**
   Insert the specified number of tabs into the output buffer
 */
static void insert_tabs(wcstring &out, int indent)
{
    if (indent > 0)
        out.append((size_t)indent, L'\t');

}
Exemplo n.º 13
0
void write_screen( const wcstring &msg, wcstring &buff )
{
	const wchar_t *start, *pos;
	int line_width = 0;
	int tok_width = 0;
	int screen_width = common_get_width();
	
	if( screen_width )
	{
		start = pos = msg.c_str();
		while( 1 )
		{
			int overflow = 0;
		
			tok_width=0;

			/*
			  Tokenize on whitespace, and also calculate the width of the token
			*/
			while( *pos && ( !wcschr( L" \n\r\t", *pos ) ) )
			{
				
				/*
				  Check is token is wider than one line.
				  If so we mark it as an overflow and break the token.
				*/
				if((tok_width + wcwidth(*pos)) > (screen_width-1))
				{
					overflow = 1;
					break;				
				}
			
				tok_width += wcwidth( *pos );
				pos++;
			}

			/*
			  If token is zero character long, we don't do anything
			*/
			if( pos == start )
			{
				start = pos = pos+1;
			}
			else if( overflow )
			{
				/*
				  In case of overflow, we print a newline, except if we already are at position 0
				*/
				wchar_t *token = wcsndup( start, pos-start );
				if( line_width != 0 )
                    buff.push_back(L'\n');
                buff.append(format_string(L"%ls-\n", token));
				free( token );
				line_width=0;
			}
			else
			{
				/*
				  Print the token
				*/
				wchar_t *token = wcsndup( start, pos-start );
				if( (line_width + (line_width!=0?1:0) + tok_width) > screen_width )
				{
                    buff.push_back(L'\n');
					line_width=0;
				}
                buff.append(format_string(L"%ls%ls", line_width?L" ":L"", token ));
				free( token );
				line_width += (line_width!=0?1:0) + tok_width;
			}
			
			/*
			  Break on end of string
			*/
			if( !*pos )
			{
				break;
			}
			
			start=pos;
		}
	}
	else
	{
        buff.append(msg);
	}
    buff.push_back(L'\n');
}