示例#1
0
/**
   Return a description of a file based on its suffix. This function
   does not perform any caching, it directly calls the mimedb command
   to do a lookup.
 */
static wcstring complete_get_desc_suffix_internal( const wcstring &suff )
{

    wcstring cmd = wcstring(SUFFIX_CMD_STR) + suff;
    
    wcstring_list_t lst;
    wcstring desc;
	
	if( exec_subshell( cmd, lst ) != -1 )
	{
		if( lst.size()>0 )
		{
            const wcstring & ln = lst.at(0);
			if( ln.size() > 0 && ln != L"unknown" )
			{
				desc = ln;
				/*
				  I have decided I prefer to have the description
				  begin in uppercase and the whole universe will just
				  have to accept it. Hah!
				*/
				desc[0]=towupper(desc[0]);
			}
		}
	}
	
	if( desc.empty() )
	{
		desc = COMPLETE_FILE_DESC;
	}
	
    suffix_map[suff] = desc.c_str();
	return desc;
}
示例#2
0
void kill_add(const wcstring &str)
{
    ASSERT_IS_MAIN_THREAD();
    if (str.empty())
        return;

    wcstring cmd;
    wcstring escaped_str;
    kill_list.push_front(str);

    /*
       Check to see if user has set the FISH_CLIPBOARD_CMD variable,
       and, if so, use it instead of checking the display, etc.

       I couldn't think of a safe way to allow overide of the echo
       command too, so, the command used must accept the input via stdin.
    */

    const env_var_t clipboard_wstr = env_get_string(L"FISH_CLIPBOARD_CMD");
    if (!clipboard_wstr.missing())
    {
        escaped_str = escape(str.c_str(), ESCAPE_ALL);
        cmd.assign(L"echo -n ");
        cmd.append(escaped_str);
        cmd.append(clipboard_wstr);
    }
    else
    {
        /* This is for sending the kill to the X copy-and-paste buffer */
        if (!has_xsel())
        {
            return;
        }

        const env_var_t disp_wstr = env_get_string(L"DISPLAY");
        if (!disp_wstr.missing())
        {
            escaped_str = escape(str.c_str(), ESCAPE_ALL);
            cmd.assign(L"echo -n ");
            cmd.append(escaped_str);
            cmd.append(L" | xsel -i -b");
        }
    }

    if (! cmd.empty())
    {
        if (exec_subshell(cmd, false /* do not apply exit status */) == -1)
        {
            /*
               Do nothing on failiure
            */
        }

        cut_buffer = escaped_str;
    }
}
示例#3
0
/**
   Test if the specified script returns zero. The result is cached, so
   that if multiple completions use the same condition, it needs only
   be evaluated once. condition_cache_clear must be called after a
   completion run to make sure that there are no stale completions.
*/
static int condition_test( const wchar_t *condition )
{
	const void *test_res;

	if( !condition || !wcslen(condition) )
	{
//		fwprintf( stderr, L"No condition specified\n" );
		return 1;
	}

	if( !condition_cache )
	{
		condition_cache = malloc( sizeof( hash_table_t ) );
		if( !condition_cache )
		{
			DIE_MEM();

		}

		hash_init( condition_cache,
				   &hash_wcs_func,
				   &hash_wcs_cmp );


	}

	test_res = hash_get( condition_cache, condition );

	if (test_res == CC_NOT_TESTED )
	{
		test_res = exec_subshell( condition, 0 )?CC_FALSE:CC_TRUE;
		hash_put( condition_cache, condition, test_res );

		/*
		  Restore previous status information
		*/
	}

	if( wcscmp( test_res, CC_TRUE ) == 0 )
	{
		return 1;
	}

	return 0;
}
示例#4
0
/// Obtain help/usage information for the specified builtin from manpage in subshell
///
/// @param  name
///    builtin name to get up help for
///
/// @return
///    A wcstring with a formatted manpage.
///
wcstring builtin_help_get(parser_t &parser, io_streams_t &streams, const wchar_t *name) {
    UNUSED(parser);
    UNUSED(streams);
    // This won't ever work if no_exec is set.
    if (no_exec) return wcstring();

    wcstring_list_t lst;
    wcstring out;
    const wcstring name_esc = escape_string(name, 1);
    wcstring cmd = format_string(L"__fish_print_help %ls", name_esc.c_str());
    if (exec_subshell(cmd, parser, lst, false /* don't apply exit status */) >= 0) {
        for (size_t i = 0; i < lst.size(); i++) {
            out.append(lst.at(i));
            out.push_back(L'\n');
        }
    }
    return out;
}
示例#5
0
/**
   Check the X clipboard. If it has been changed, add the new
   clipboard contents to the fish killring.
*/
static void kill_check_x_buffer()
{
    if (!has_xsel())
        return;

    const env_var_t disp = env_get_string(L"DISPLAY");
    if (! disp.missing())
    {
        size_t i;
        wcstring cmd = L"xsel -t 500 -b";
        wcstring new_cut_buffer=L"";
        wcstring_list_t list;
        if (exec_subshell(cmd, list, false /* do not apply exit status */) != -1)
        {

            for (i=0; i<list.size(); i++)
            {
                wcstring next_line = escape_string(list.at(i), 0);
                if (i > 0) new_cut_buffer += L"\\n";
                new_cut_buffer += next_line;
            }

            if (new_cut_buffer.size() > 0)
            {
                /*
                  The buffer is inserted with backslash escapes,
                  since we don't really like tabs, newlines,
                  etc. anyway.
                */

                if (cut_buffer != new_cut_buffer)
                {
                    cut_buffer = new_cut_buffer;
                    kill_list.push_front(new_cut_buffer);
                }
            }
        }
    }
}
示例#6
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;
}
示例#7
0
/**
   This internal helper function does all the real work. By using two
   functions, the internal function can return on various places in
   the code, and the caller can take care of various cleanup work.

     cmd: the command name ('grep')
     really_load: whether to actually parse it as a function, or just check it it exists
     reload: whether to reload it if it's already loaded
     path_list: the set of paths to check

     Result: if really_load is true, returns whether the function was loaded. Otherwise returns whether the function existed.
*/
bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list)
{
    /* Note that we are NOT locked in this function! */
    bool reloaded = 0;

    /* Try using a cached function. If we really want the function to be loaded, require that it be really loaded. If we're not reloading, allow stale functions. */
    {
        bool allow_stale_functions = ! reload;

        /* Take a lock */
        scoped_lock locker(lock);

        /* Get the function */
        autoload_function_t * func = this->get_node(cmd);

        /* Determine if we can use this cached function */
        bool use_cached;
        if (! func)
        {
            /* Can't use a function that doesn't exist */
            use_cached = false;
        }
        else if (really_load && ! func->is_placeholder && ! func->is_loaded)
        {
            /* Can't use an unloaded function */
            use_cached = false;
        }
        else if (! allow_stale_functions && is_stale(func))
        {
            /* Can't use a stale function */
            use_cached = false;
        }
        else
        {
            /* I guess we can use it */
            use_cached = true;
        }

        /* If we can use this function, return whether we were able to access it */
        if (use_cached)
        {
            return func->is_internalized || func->access.accessible;
        }
    }
    /* The source of the script will end up here */
    wcstring script_source;
    bool has_script_source = false;

    /* Whether we found an accessible file */
    bool found_file = false;

    /* Look for built-in scripts via a binary search */
    const builtin_script_t *matching_builtin_script = NULL;
    if (builtin_script_count > 0)
    {
        const builtin_script_t test_script = {cmd.c_str(), NULL};
        const builtin_script_t *array_end = builtin_scripts + builtin_script_count;
        const builtin_script_t *found = std::lower_bound(builtin_scripts, array_end, test_script, script_name_precedes_script_name);
        if (found != array_end && ! wcscmp(found->name, test_script.name))
        {
            /* We found it */
            matching_builtin_script = found;
        }
    }
    if (matching_builtin_script)
    {
        has_script_source = true;
        script_source = str2wcstring(matching_builtin_script->def);

        /* Make a node representing this function */
        scoped_lock locker(lock);
        autoload_function_t *func = this->get_autoloaded_function_with_creation(cmd, really_load);

        /* This function is internalized */
        func->is_internalized = true;

        /* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */
        if (really_load) func->is_loaded = true;
    }

    if (! has_script_source)
    {
        /* Iterate over path searching for suitable completion files */
        for (size_t i=0; i<path_list.size(); i++)
        {
            wcstring next = path_list.at(i);
            wcstring path = next + L"/" + cmd + L".fish";

            const file_access_attempt_t access = access_file(path, R_OK);
            if (access.accessible)
            {
                /* Found it! */
                found_file = true;

                /* Now we're actually going to take the lock. */
                scoped_lock locker(lock);
                autoload_function_t *func = this->get_node(cmd);

                /* Generate the source if we need to load it */
                bool need_to_load_function = really_load && (func == NULL || func->access.mod_time != access.mod_time || ! func->is_loaded);
                if (need_to_load_function)
                {

                    /* Generate the script source */
                    wcstring esc = escape_string(path, 1);
                    script_source = L". " + esc;
                    has_script_source = true;

                    /* Remove any loaded command because we are going to reload it. Note that this will deadlock if command_removed calls back into us. */
                    if (func && func->is_loaded)
                    {
                        command_removed(cmd);
                        func->is_placeholder = false;
                    }

                    /* Mark that we're reloading it */
                    reloaded = true;
                }

                /* Create the function if we haven't yet. This does not load it. Do not trigger eviction unless we are actually loading, because we don't want to evict off of the main thread. */
                if (! func)
                {
                    func = get_autoloaded_function_with_creation(cmd, really_load);
                }

                /* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */
                if (need_to_load_function) func->is_loaded = true;

                /* Unconditionally record our access time */
                func->access = access;

                break;
            }
        }

        /*
          If no file or builtin script was found we insert a placeholder function.
          Later we only research if the current time is at least five seconds later.
          This way, the files won't be searched over and over again.
        */
        if (! found_file && ! has_script_source)
        {
            scoped_lock locker(lock);
            /* Generate a placeholder */
            autoload_function_t *func = this->get_node(cmd);
            if (! func)
            {
                func = new autoload_function_t(cmd);
                func->is_placeholder = true;
                if (really_load)
                {
                    this->add_node(func);
                }
                else
                {
                    this->add_node_without_eviction(func);
                }
            }
            func->access.last_checked = time(NULL);
        }
    }

    /* If we have a script, either built-in or a file source, then run it */
    if (really_load && has_script_source)
    {
        if (exec_subshell(script_source, false /* do not apply exit status */) == -1)
        {
            /* Do nothing on failure */
        }

    }

    if (really_load)
    {
        return reloaded;
    }
    else
    {
        return found_file || has_script_source;
    }
}
示例#8
0
/**
   If command to complete is short enough, substitute
   the description with the whatis information for the executable.
*/
static void complete_cmd_desc( const wchar_t *cmd, array_list_t *comp )
{
	int i;
	const wchar_t *cmd_start;
	int cmd_len;
	wchar_t *lookup_cmd=0;
	array_list_t list;
	hash_table_t lookup;
	wchar_t *esc;
	int skip;

	if( !cmd )
		return;

	cmd_start=wcsrchr(cmd, L'/');

	if( cmd_start )
		cmd_start++;
	else
		cmd_start = cmd;

	cmd_len = wcslen(cmd_start);

	/*
	  Using apropos with a single-character search term produces far
	  to many results - require at least two characters if we don't
	  know the location of the whatis-database.
	*/
	if(cmd_len < 2 )
		return;

	if( wildcard_has( cmd_start, 0 ) )
	{
		return;
	}

	skip = 1;

	for( i=0; i<al_get_count( comp ); i++ )
	{
		completion_t *c = (completion_t *)al_get( comp, i );

		if( !wcslen( c->completion) || (c->completion[wcslen(c->completion)-1] != L'/' ))
		{
			skip = 0;
			break;
		}

	}

	if( skip )
	{
		return;
	}


	esc = escape( cmd_start, 1 );

	lookup_cmd = wcsdupcat( L"__fish_describe_command ", esc );
	free(esc);

	al_init( &list );
	hash_init( &lookup, &hash_wcs_func, &hash_wcs_cmp );


	/*
	  First locate a list of possible descriptions using a single
	  call to apropos or a direct search if we know the location
	  of the whatis database. This can take some time on slower
	  systems with a large set of manuals, but it should be ok
	  since apropos is only called once.
	*/
	if( exec_subshell( lookup_cmd, &list ) != -1 )
	{

		/*
		  Then discard anything that is not a possible completion and put
		  the result into a hashtable with the completion as key and the
		  description as value.

		  Should be reasonably fast, since no memory allocations are needed.
		*/
		for( i=0; i<al_get_count( &list); i++ )
		{
			wchar_t *el = (wchar_t *)al_get( &list, i );
			wchar_t *key, *key_end, *val_begin;

			if( !el )
				continue;

			key = el+wcslen(cmd_start);
			key_end = wcschr( key, L'\t' );

			if( !key_end )
				continue;

			*key_end = 0;
			val_begin = key_end+1;

			/*
			  And once again I make sure the first character is uppercased
			  because I like it that way, and I get to decide these
			  things.
			*/
			val_begin[0]=towupper(val_begin[0]);

			hash_put( &lookup, key, val_begin );
		}

		/*
		  Then do a lookup on every completion and if a match is found,
		  change to the new description.

		  This needs to do a reallocation for every description added, but
		  there shouldn't be that many completions, so it should be ok.
		*/
		for( i=0; i<al_get_count(comp); i++ )
		{
			completion_t *c = (completion_t *)al_get( comp, i );
			const wchar_t *el = c->completion;

			wchar_t *new_desc;

			new_desc = (wchar_t *)hash_get( &lookup,
											el );

			if( new_desc )
			{
				c->description = halloc_wcsdup( comp, new_desc );
			}
		}
	}

	hash_destroy( &lookup );
	al_foreach( &list,
				&free );
	al_destroy( &list );
	free( lookup_cmd );
}