Example #1
0
void split_about(ITER haystack_start, ITER haystack_end,
                 ITER needle_start, ITER needle_end,
                 wcstring_list_t *output, long max)
{
    long remaining = max;
    ITER haystack_cursor = haystack_start;
    while (remaining > 0 && haystack_cursor != haystack_end)
    {
        ITER split_point;
        if (needle_start == needle_end)
        {
            // empty needle, we split on individual elements
            split_point = haystack_cursor + 1;
        }
        else
        {
            split_point = std::search(haystack_cursor, haystack_end, needle_start, needle_end);
        }
        if (split_point == haystack_end)
        {
            // not found
            break;
        }
        output->push_back(wcstring(haystack_cursor, split_point));
        remaining--;
        // need to skip over the needle for the next search
        // note that the needle may be empty
        haystack_cursor = split_point + std::distance(needle_start, needle_end);
    }
    // trailing component, possibly empty
    output->push_back(wcstring(haystack_cursor, haystack_end));
}
/// Silly function.
static void builtin_complete_add2(const wchar_t *cmd, int cmd_type, const wchar_t *short_opt,
                                  const wcstring_list_t &gnu_opt, const wcstring_list_t &old_opt,
                                  int result_mode, const wchar_t *condition, const wchar_t *comp,
                                  const wchar_t *desc, int flags) {
    size_t i;
    const wchar_t *s;

    for (s = short_opt; *s; s++) {
        complete_add(cmd, cmd_type, wcstring(1, *s), option_type_short, result_mode, condition,
                     comp, desc, flags);
    }

    for (i = 0; i < gnu_opt.size(); i++) {
        complete_add(cmd, cmd_type, gnu_opt.at(i), option_type_double_long, result_mode, condition,
                     comp, desc, flags);
    }

    for (i = 0; i < old_opt.size(); i++) {
        complete_add(cmd, cmd_type, old_opt.at(i), option_type_single_long, result_mode, condition,
                     comp, desc, flags);
    }

    if (old_opt.empty() && gnu_opt.empty() && wcslen(short_opt) == 0) {
        complete_add(cmd, cmd_type, wcstring(), option_type_args_only, result_mode, condition, comp,
                     desc, flags);
    }
}
Example #3
0
wcstring parser_t::current_line()
{
    if (execution_contexts.empty())
    {
        return wcstring();
    }
    const parse_execution_context_t *context = execution_contexts.back();
    assert(context != NULL);

    int source_offset = context->get_current_source_offset();
    if (source_offset < 0)
    {
        return wcstring();
    }

    const int lineno = this->get_lineno();
    const wchar_t *file = this->current_filename();

    wcstring prefix;

    /* If we are not going to print a stack trace, at least print the line number and filename */
    if (!get_is_interactive() || is_function())
    {
        if (file)
        {
            append_format(prefix, _(L"%ls (line %d): "), user_presentable_path(file).c_str(), lineno);
        }
        else if (is_within_fish_initialization)
        {
            append_format(prefix, L"%ls: ", _(L"Startup"), lineno);
        }
        else
        {
            append_format(prefix, L"%ls: ", _(L"Standard input"), lineno);
        }
    }

    bool is_interactive = get_is_interactive();
    bool skip_caret = is_interactive && ! is_function();

    /* Use an error with empty text */
    assert(source_offset >= 0);
    parse_error_t empty_error = {};
    empty_error.source_start = source_offset;

    wcstring line_info = empty_error.describe_with_prefix(context->get_source(), prefix, is_interactive, skip_caret);
    if (! line_info.empty())
    {
        line_info.push_back(L'\n');
    }

    line_info.append(this->stack_trace());
    return line_info;
}
Example #4
0
int wildcard_expand( const wchar_t *wc, 
					 const wchar_t *base_dir,
					 expand_flags_t flags,
					 std::vector<completion_t> &out )
{
	size_t c = out.size();
	int res = wildcard_expand_internal( wc, base_dir, flags, out );
			
	if( flags & ACCEPT_INCOMPLETE )
	{
        wcstring wc_base;
		const wchar_t *wc_base_ptr = wcsrchr( wc, L'/' );
		if( wc_base_ptr )
		{
            wc_base = wcstring(wc, (wc_base_ptr-wc)+1);
		}

		for( size_t i=c; i<out.size(); i++ )
		{
			completion_t &c = out.at( i );
			
			if( c.flags & COMPLETE_NO_CASE )
			{
				c.completion = format_string(L"%ls%ls%ls", base_dir, wc_base.c_str(), c.completion.c_str());
			}
		}		
	}
	return res;
}
 void make_pipe(const wchar_t *test_path)
 {
     wcstring vars_path = test_path ? wcstring(test_path) : default_vars_path();
     vars_path.append(L".notifier");
     const std::string narrow_path = wcs2string(vars_path);
     
     int fd = wopen_cloexec(vars_path, O_RDWR | O_NONBLOCK, 0600);
     if (fd < 0 && errno == ENOENT)
     {
         /* File doesn't exist, try creating it */
         if (mkfifo(narrow_path.c_str(), 0600) >= 0)
         {
             fd = wopen_cloexec(vars_path, O_RDWR | O_NONBLOCK, 0600);
         }
     }
     if (fd < 0)
     {
         // Maybe open failed, maybe mkfifo failed
         int err = errno;
         report_error(err, L"Unable to make or open a FIFO for universal variables with path '%ls'", vars_path.c_str());
     }
     else
     {
         pipe_fd = fd;
     }
 }
int env_universal_remove( const wchar_t *name )
{
	int res;
	
	message_t *msg;
	if( !init )
		return 1;
		
	CHECK( name, 1 );

	res = !env_universal_common_get( name );
	debug( 3,
		   L"env_universal_remove( \"%ls\" )",
		   name );
		
	if( is_dead() )
	{
		env_universal_common_remove( wcstring(name) );
	}
	else
	{
		msg= create_message( ERASE, name, 0);
		msg->count=1;
        env_universal_server.unsent->push(msg);
		env_universal_barrier();
	}
	
	return res;
}
Example #7
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;
}
Example #8
0
static void builtin_complete_remove_cmd(const wcstring &cmd,
                                        int cmd_type,
                                        const wchar_t *short_opt,
                                        const wcstring_list_t &gnu_opt,
                                        const wcstring_list_t &old_opt)
{
    bool removed = false;
    size_t i;
    for (i=0; short_opt[i] != L'\0'; i++)
    {
        complete_remove(cmd, cmd_type, wcstring(1, short_opt[i]), option_type_short);
        removed = true;
    }
    
    for (i=0; i < old_opt.size(); i++)
    {
        complete_remove(cmd, cmd_type, old_opt.at(i), option_type_single_long);
        removed = true;
    }
    
    for (i=0; i < gnu_opt.size(); i++)
    {
        complete_remove(cmd, cmd_type, gnu_opt.at(i), option_type_double_long);
        removed = true;
    }
    
    if (! removed)
    {
        // This means that all loops were empty
        complete_remove_all(cmd, cmd_type);
    }
}
Example #9
0
history_item_t history_t::item_at_index(size_t idx) {
    scoped_lock locker(lock);
    
    /* 0 is considered an invalid index */
    assert(idx > 0);
    idx--;
    
    /* idx=0 corresponds to last item in new_items */
    size_t new_item_count = new_items.size();
    if (idx < new_item_count) {
        return new_items.at(new_item_count - idx - 1);
    }
    
    /* Now look in our old items */
    idx -= new_item_count;
    load_old_if_needed();
    size_t old_item_count = old_item_offsets.size();
    if (idx < old_item_count) {
        /* idx=0 corresponds to last item in old_item_offsets */
        size_t offset = old_item_offsets.at(old_item_count - idx - 1);
        return history_t::decode_item(mmap_start + offset, mmap_length - offset);
    }
    
    /* Index past the valid range, so return an empty history item */
    return history_item_t(wcstring(), 0);
}
Example #10
0
/* Given a command like "cat file", truncate it to a reasonable length */
static wcstring truncate_command(const wcstring &cmd)
{
    const size_t max_len = 32;
    if (cmd.size() <= max_len)
    {
        // No truncation necessary
        return cmd;
    }
    
    // Truncation required
    const bool ellipsis_is_unicode = (ellipsis_char == L'\x2026');
    const size_t ellipsis_length = ellipsis_is_unicode ? 1 : 3;
    size_t trunc_length = max_len - ellipsis_length;
    // Eat trailing whitespace
    while (trunc_length > 0 && iswspace(cmd.at(trunc_length - 1)))
    {
        trunc_length -= 1;
    }
    wcstring result = wcstring(cmd, 0, trunc_length);
    // Append ellipsis
    if (ellipsis_is_unicode)
    {
        result.push_back(ellipsis_char);
    }
    else
    {
        result.append(L"...");
    }
    return result;
}
Example #11
0
bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid)
{
    wcstring cmd;
    pid_t pid = 0;
    while (cmd.empty())
    {
        wcstring name;
        if (! dir || ! wreaddir(dir, name))
            break;

        if (!iswnumeric(name.c_str()))
            continue;

        wcstring path = wcstring(L"/proc/") + name;
        struct stat buf;
        if (wstat(path, &buf))
            continue;

        if (buf.st_uid != getuid())
            continue;

        /* remember the pid */
        pid = fish_wcstoi(name.c_str(), NULL, 10);

        /* the 'cmdline' file exists, it should contain the commandline */
        FILE *cmdfile;
        if ((cmdfile=wfopen(path + L"/cmdline", "r")))
        {
            wcstring full_command_line;
            signal_block();
            fgetws2(&full_command_line, cmdfile);
            signal_unblock();

            /* The command line needs to be escaped */
            cmd = tok_first(full_command_line.c_str());
        }
#ifdef SunOS
        else if ((cmdfile=wfopen(path + L"/psinfo", "r")))
        {
            psinfo_t info;
            if (fread(&info, sizeof(info), 1, cmdfile))
            {
                /* The filename is unescaped */
                cmd = str2wcstring(info.pr_fname);
            }
        }
#endif
        if (cmdfile)
            fclose(cmdfile);
    }

    bool result = ! cmd.empty();
    if (result)
    {
        *out_str = cmd;
        *out_pid = pid;
    }
    return result;
}
/**
   Output the specified selection.

   \param begin start of selection
   \param end  end of selection
   \param cut_at_cursor whether printing should stop at the surrent cursor position
   \param tokenize whether the string should be tokenized, printing one string token on every line and skipping non-string tokens
*/
static void write_part(const wchar_t *begin,
                       const wchar_t *end,
                       int cut_at_cursor,
                       int tokenize,
                       io_streams_t &streams)
{
    size_t pos = get_cursor_pos()-(begin-get_buffer());

    if (tokenize)
    {
        wchar_t *buff = wcsndup(begin, end-begin);
//    fwprintf( stderr, L"Subshell: %ls, end char %lc\n", buff, *end );
        wcstring out;
        tokenizer_t tok(buff, TOK_ACCEPT_UNFINISHED);
        tok_t token;
        while (tok.next(&token))
        {
            if ((cut_at_cursor) &&
                    (token.offset + token.text.size() >= pos))
                break;

            switch (token.type)
            {
                case TOK_STRING:
                {
                    wcstring tmp = token.text;
                    unescape_string_in_place(&tmp, UNESCAPE_INCOMPLETE);
                    out.append(tmp);
                    out.push_back(L'\n');
                    break;
                }

                default:
                {
                    break;
                }
            }
        }

        streams.out.append(out);

        free(buff);
    }
    else
    {
        if (cut_at_cursor)
        {
            end = begin+pos;
        }

//    debug( 0, L"woot2 %ls -> %ls", buff, esc );
        wcstring tmp = wcstring(begin, end - begin);
        unescape_string_in_place(&tmp, UNESCAPE_INCOMPLETE);
        streams.out.append(tmp);
        streams.out.append(L"\n");

    }
}
Example #13
0
void RendererD3D::pushGroupMarker(GLsizei length, const char *marker)
{
    std::vector<wchar_t> wcstring(length + 1);
    size_t convertedChars = 0;
    errno_t err = mbstowcs_s(&convertedChars, wcstring.data(), length + 1, marker, _TRUNCATE);
    if (err == 0)
    {
        getAnnotator()->beginEvent(wcstring.data());
    }
}
Example #14
0
// Check if any pair of mutually exclusive options was seen. Note that since every option must have
// a short name we only need to check those.
static int check_for_mutually_exclusive_flags(argparse_cmd_opts_t &opts, io_streams_t &streams) {
    for (auto it : opts.options) {
        auto opt_spec = it.second;
        if (opt_spec->num_seen == 0) continue;

        // We saw this option at least once. Check all the sets of mutually exclusive options to see
        // if this option appears in any of them.
        for (auto xarg_set : opts.exclusive_flag_sets) {
            auto found = std::find(xarg_set.begin(), xarg_set.end(), opt_spec->short_flag);
            if (found != xarg_set.end()) {
                // Okay, this option is in a mutually exclusive set of options. Check if any of the
                // other mutually exclusive options have been seen.
                for (auto xflag : xarg_set) {
                    auto xopt_spec = opts.options[xflag];
                    // Ignore this flag in the list of mutually exclusive flags.
                    if (xopt_spec->short_flag == opt_spec->short_flag) continue;

                    // If it is a different flag check if it has been seen.
                    if (xopt_spec->num_seen) {
                        wcstring flag1;
                        if (opt_spec->short_flag_valid) flag1 = wcstring(1, opt_spec->short_flag);
                        if (!opt_spec->long_flag.empty()) {
                            if (opt_spec->short_flag_valid) flag1 += L"/";
                            flag1 += opt_spec->long_flag;
                        }
                        wcstring flag2;
                        if (xopt_spec->short_flag_valid) flag2 = wcstring(1, xopt_spec->short_flag);
                        if (!xopt_spec->long_flag.empty()) {
                            if (xopt_spec->short_flag_valid) flag2 += L"/";
                            flag2 += xopt_spec->long_flag;
                        }
                        streams.err.append_format(
                            _(L"%ls: Mutually exclusive flags '%ls' and `%ls` seen\n"),
                            opts.name.c_str(), flag1.c_str(), flag2.c_str());
                        return STATUS_CMD_ERROR;
                    }
                }
            }
        }
    }

    return STATUS_CMD_OK;
}
Example #15
0
wcstring vformat_string(const wchar_t *format, va_list va_orig)
{    
    const int saved_err = errno;
    /*
      As far as I know, there is no way to check if a
      vswprintf-call failed because of a badly formated string
      option or because the supplied destination string was to
      small. In GLIBC, errno seems to be set to EINVAL either way. 

      Because of this, on failiure we try to
      increase the buffer size until the free space is
      larger than max_size, at which point it will
      conclude that the error was probably due to a badly
      formated string option, and return an error. Make
      sure to null terminate string before that, though.
    */
    const size_t max_size = (128*1024*1024);
    wchar_t static_buff[256];
    size_t size = 0;
    wchar_t *buff = NULL;
    int status = -1;
    while (status < 0) {
        /* Reallocate if necessary */
        if (size == 0) {
            buff = static_buff;
            size = sizeof static_buff;
        } else {
            size *= 2;
            if (size >= max_size) {
                buff[0] = '\0';
                break;
            }
            buff = (wchar_t *)realloc( (buff == static_buff ? NULL : buff), size);
            if (buff == NULL) {
                DIE_MEM();
            }
        }
                
        /* Try printing */
		va_list va;
		va_copy( va, va_orig );
        status = vswprintf(buff, size / sizeof(wchar_t), format, va);
        va_end(va);   
    }
    
    wcstring result = wcstring(buff);
    
    if (buff != static_buff)
        free(buff);
    
    errno = saved_err;
    return result;
}
Example #16
0
const wcstring wgetcwd() {
    wcstring retval;

    char *res = getcwd(NULL, 0);
    if (res) {
        retval = str2wcstring(res);
        free(res);
    } else {
        debug(0, _(L"getcwd() failed with errno %d/%s"), errno, strerror(errno));
        retval = wcstring();
    }

    return retval;
}
Example #17
0
    int report_match(const wchar_t *arg, int pcre2_rc) {
        // Return values: -1 = error, 0 = no match, 1 = match.
        if (pcre2_rc == PCRE2_ERROR_NOMATCH) {
            if (opts.invert_match && !opts.quiet) {
                if (opts.index) {
                    streams.out.append_format(L"1 %lu\n", wcslen(arg));
                } else {
                    streams.out.append(arg);
                    streams.out.push_back(L'\n');
                }
            }

            return opts.invert_match ? 1 : 0;
        } else if (pcre2_rc < 0) {
            string_error(streams, _(L"%ls: Regular expression match error: %ls\n"), argv0,
                         pcre2_strerror(pcre2_rc).c_str());
            return -1;
        } else if (pcre2_rc == 0) {
            // The output vector wasn't big enough. Should not happen.
            string_error(streams, _(L"%ls: Regular expression internal error\n"), argv0);
            return -1;
        } else if (opts.invert_match) {
            return 0;
        }

        if (opts.entire) {
            streams.out.append(arg);
            streams.out.push_back(L'\n');
        }

        PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(regex.match);
        for (int j = (opts.entire ? 1 : 0); j < pcre2_rc; j++) {
            PCRE2_SIZE begin = ovector[2 * j];
            PCRE2_SIZE end = ovector[2 * j + 1];

            if (begin != PCRE2_UNSET && end != PCRE2_UNSET && !opts.quiet) {
                if (opts.index) {
                    streams.out.append_format(L"%lu %lu", (unsigned long)(begin + 1),
                                              (unsigned long)(end - begin));
                } else if (end > begin) {
                    // May have end < begin if \K is used.
                    streams.out.append(wcstring(&arg[begin], end - begin));
                }
                streams.out.push_back(L'\n');
            }
        }

        return opts.invert_match ? 0 : 1;
    }
Example #18
0
/**
   Replace completion strings with a comp_t structure
*/
static std::vector<comp_t *> mangle_completions( wcstring_list_t &lst, const wchar_t *prefix )
{
    std::vector<comp_t *> result;
	for( size_t i=0; i<lst.size(); i++ )
	{
        wcstring &next = lst.at(i);
		size_t start, end;
        
        comp_t zerod = {};
		comp_t *comp = new comp_t(zerod);
		
		for( start=end=0; 1; end++ )
		{
			wchar_t c = next.c_str()[end];
			
			if( (c == COMPLETE_ITEM_SEP) || (c==COMPLETE_SEP) || !c)
			{
                wcstring start2 = wcstring(next, start, end - start);
                wcstring str = escape_string(start2, ESCAPE_ALL | ESCAPE_NO_QUOTED);
				comp->comp_width += my_wcswidth( str.c_str() );
				comp->comp.push_back(str);
				start = end+1;
			}

			if( c == COMPLETE_SEP )
			{
				comp->desc = next.c_str() + start;
				break;
			}	
			
			if( !c )
				break;
			
		}

		comp->comp_width  += (int)(my_wcswidth(prefix)*comp->comp.size() + 2*(comp->comp.size()-1));
		comp->desc_width = comp->desc.empty()?0:my_wcswidth( comp->desc.c_str() );
		
		comp->pref_width = comp->comp_width + comp->desc_width + (comp->desc_width?4:0);
		
        result.push_back(comp);
	}
	
	recalc_width( result, prefix );
    return result;
}
Example #19
0
/// Returns the number of characters in the escape code starting at 'code'. We only handle sequences
/// that begin with \x1B. If it doesn't we return zero. We also return zero if we don't recognize the
/// escape sequence based on querying terminfo and other heuristics.
size_t escape_code_length(const wchar_t *code) {
    assert(code != NULL);
    if (*code != L'\x1B') return 0;

    size_t esc_seq_len = cached_layouts.find_escape_code(code);
    if (esc_seq_len) return esc_seq_len;

    bool found = is_color_escape_seq(code, &esc_seq_len);
    if (!found) found = is_visual_escape_seq(code, &esc_seq_len);
    if (!found) found = is_screen_name_escape_seq(code, &esc_seq_len);
    if (!found) found = is_iterm2_escape_seq(code, &esc_seq_len);
    if (!found) found = is_single_byte_escape_seq(code, &esc_seq_len);
    if (!found) found = is_csi_style_escape_seq(code, &esc_seq_len);
    if (!found) found = is_two_byte_escape_seq(code, &esc_seq_len);
    if (found) cached_layouts.add_escape_code(wcstring(code, esc_seq_len));
    return esc_seq_len;
}
Example #20
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;
}
Example #21
0
static int string_trim(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) {
    options_t opts;
    opts.chars_valid = true;
    opts.left_valid = true;
    opts.right_valid = true;
    opts.quiet_valid = true;
    int optind;
    int retval = parse_opts(&opts, &optind, 0, argc, argv, parser, streams);
    if (retval != STATUS_CMD_OK) return retval;

    // If neither left or right is specified, we do both.
    if (!opts.left && !opts.right) {
        opts.left = opts.right = true;
    }

    const wchar_t *arg;
    size_t ntrim = 0;

    wcstring argstr;
    wcstring storage;
    while ((arg = string_get_arg(&optind, argv, &storage, streams)) != 0) {
        argstr = arg;
        // Begin and end are respectively the first character to keep on the left, and first
        // character to trim on the right. The length is thus end - start.
        size_t begin = 0, end = argstr.size();
        if (opts.right) {
            size_t last_to_keep = argstr.find_last_not_of(opts.chars_to_trim);
            end = (last_to_keep == wcstring::npos) ? 0 : last_to_keep + 1;
        }
        if (opts.left) {
            size_t first_to_keep = argstr.find_first_not_of(opts.chars_to_trim);
            begin = (first_to_keep == wcstring::npos ? end : first_to_keep);
        }
        assert(begin <= end && end <= argstr.size());
        ntrim += argstr.size() - (end - begin);
        if (!opts.quiet) {
            streams.out.append(wcstring(argstr, begin, end - begin));
            streams.out.append(L'\n');
        }
    }

    return ntrim > 0 ? STATUS_CMD_OK : STATUS_CMD_ERROR;
}
Example #22
0
/// Given a command like "cat file", truncate it to a reasonable length.
static wcstring truncate_command(const wcstring &cmd) {
    const size_t max_len = 32;
    if (cmd.size() <= max_len) {
        // No truncation necessary.
        return cmd;
    }

    // Truncation required.
    const size_t ellipsis_length = wcslen(ellipsis_str); //no need for wcwidth
    size_t trunc_length = max_len - ellipsis_length;
    // Eat trailing whitespace.
    while (trunc_length > 0 && iswspace(cmd.at(trunc_length - 1))) {
        trunc_length -= 1;
    }
    wcstring result = wcstring(cmd, 0, trunc_length);
    // Append ellipsis.
    result.append(ellipsis_str);
    return result;
}
Example #23
0
static int wildcard_expand(const wchar_t *wc,
                           const wchar_t *base_dir,
                           expand_flags_t flags,
                           std::vector<completion_t> *out)
{
    assert(out != NULL);
    size_t c = out->size();

    /* Make a set of used completion strings so we can do fast membership tests inside wildcard_expand_internal. Otherwise wildcards like '**' are very slow, because we end up with an N^2 membership test.
    */
    std::set<wcstring> completion_set;
    for (std::vector<completion_t>::const_iterator iter = out->begin(); iter != out->end(); ++iter)
    {
        completion_set.insert(iter->completion);
    }

    std::set<file_id_t> visited_files;
    int res = wildcard_expand_internal(wc, base_dir, flags, out, completion_set, visited_files);

    if (flags & ACCEPT_INCOMPLETE)
    {
        wcstring wc_base;
        const wchar_t *wc_base_ptr = wcsrchr(wc, L'/');
        if (wc_base_ptr)
        {
            wc_base = wcstring(wc, (wc_base_ptr-wc)+1);
        }

        for (size_t i=c; i<out->size(); i++)
        {
            completion_t &c = out->at(i);

            if (c.flags & COMPLETE_REPLACES_TOKEN)
            {
                // completion = base_dir + wc_base + completion
                c.completion.insert(0, wc_base);
                c.completion.insert(0, base_dir);
            }
        }
    }
    return res;
}
Example #24
0
/**
   Merge multiple completions with the same description to the same line
*/
static void join_completions( wcstring_list_t lst )
{
    std::map<wcstring, long> desc_table;

	for( size_t i=0; i<lst.size(); i++ )
	{
		const wchar_t *item = lst.at(i).c_str();
		const wchar_t *desc = wcschr( item, COMPLETE_SEP );
		long prev_idx;
		
		if( !desc )
			continue;
		desc++;
        prev_idx = desc_table[desc] - 1;
		if( prev_idx == -1 )
		{
            desc_table[desc] = (long)(i+1);
		}
		else
		{
			const wchar_t *old = lst.at(i).c_str();
			const wchar_t *old_end = wcschr( old, COMPLETE_SEP );
			
			if( old_end )
			{
				
                wcstring foo;
                foo.append(old, old_end - old);
                foo.push_back(COMPLETE_ITEM_SEP);
                foo.append(item);
                
                lst.at(prev_idx) = foo;
                lst.at(i).clear();
			}
			
		}
		
	}	

    /* Remove empty strings */
    lst.erase(remove(lst.begin(), lst.end(), wcstring()), lst.end());
}
Example #25
0
wcstring wsetlocale(int category, const wchar_t *locale)
{

	char *lang = NULL;
	if (locale){
		lang = wcs2str( locale );
	}
	char * res = setlocale(category,lang);
	free( lang );

	/*
	  Use ellipsis if on known unicode system, otherwise use $
	*/
	char *ctype = setlocale( LC_CTYPE, NULL );
	ellipsis_char = (strstr( ctype, ".UTF")||strstr( ctype, ".utf") )?L'\x2026':L'$';	
		
	if( !res )
		return wcstring();
    else
		return format_string(L"%s", res);
}
Example #26
0
int wildcard_expand( const wchar_t *wc,
					 const wchar_t *base_dir,
					 expand_flags_t flags,
					 std::vector<completion_t> &out )
{
	size_t c = out.size();
    
    /* Make a set of used completion strings so we can do fast membership tests inside wildcard_expand_internal. Otherwise wildcards like '**' are very slow, because we end up with an N^2 membership test.
    */
	std::set<wcstring> completion_set;
	for (std::vector<completion_t>::const_iterator iter = out.begin(); iter != out.end(); ++iter)
	{
        completion_set.insert(iter->completion);
	}
    
	std::set<file_id_t> visited_files;
	int res = wildcard_expand_internal( wc, base_dir, flags, out, completion_set, visited_files );
    
	if( flags & ACCEPT_INCOMPLETE )
	{
        wcstring wc_base;
		const wchar_t *wc_base_ptr = wcsrchr( wc, L'/' );
		if( wc_base_ptr )
		{
            wc_base = wcstring(wc, (wc_base_ptr-wc)+1);
		}

		for( size_t i=c; i<out.size(); i++ )
		{
			completion_t &c = out.at( i );
			
			if( c.flags & COMPLETE_NO_CASE )
			{
				c.completion = format_string(L"%ls%ls%ls", base_dir, wc_base.c_str(), c.completion.c_str());
			}
		}		
	}
	return res;
}
Example #27
0
/**
   The real implementation of wildcard expansion is in this
   function. Other functions are just wrappers around this one.

   This function traverses the relevant directory tree looking for
   matches, and recurses when needed to handle wildcrards spanning
   multiple components and recursive wildcards.

   Because this function calls itself recursively with substrings,
   it's important that the parameters be raw pointers instead of wcstring,
   which would be too expensive to construct for all substrings.
 */
static int wildcard_expand_internal(const wchar_t *wc,
                                    const wchar_t * const base_dir,
                                    expand_flags_t flags,
                                    std::vector<completion_t> *out,
                                    std::set<wcstring> &completion_set,
                                    std::set<file_id_t> &visited_files)
{
    /* Variables for traversing a directory */
    DIR *dir;

    /* The result returned */
    int res = 0;


    //  debug( 3, L"WILDCARD_EXPAND %ls in %ls", wc, base_dir );

    if (is_main_thread() ? reader_interrupted() : reader_thread_job_is_stale())
    {
        return -1;
    }

    if (!wc || !base_dir)
    {
        debug(2, L"Got null string on line %d of file %s", __LINE__, __FILE__);
        return 0;
    }

    const size_t base_dir_len = wcslen(base_dir);
    const size_t wc_len = wcslen(wc);

    if (flags & ACCEPT_INCOMPLETE)
    {
        /*
           Avoid excessive number of returned matches for wc ending with a *
        */
        if (wc_len > 0 && (wc[wc_len-1]==ANY_STRING))
        {
            wchar_t * foo = wcsdup(wc);
            foo[wc_len-1]=0;
            int res = wildcard_expand_internal(foo, base_dir, flags, out, completion_set, visited_files);
            free(foo);
            return res;
        }
    }

    /* Determine if we are the last segment */
    const wchar_t * const next_slash = wcschr(wc,L'/');
    const bool is_last_segment = (next_slash == NULL);
    const size_t wc_segment_len = next_slash ? next_slash - wc : wc_len;
    const wcstring wc_segment = wcstring(wc, wc_segment_len);
    
    /* Maybe this segment has no wildcards at all. If this is not the last segment, and it has no wildcards, then we don't need to match against the directory contents, and in fact we don't want to match since we may not be able to read it anyways (#2099). Don't even open the directory! */
    const bool segment_has_wildcards = wildcard_has(wc_segment, true /* internal, i.e. look for ANY_CHAR instead of ? */);
    if (! segment_has_wildcards && ! is_last_segment)
    {
        wcstring new_base_dir = make_path(base_dir, wc_segment);
        new_base_dir.push_back(L'/');
        
        /* Skip multiple separators */
        assert(next_slash != NULL);
        const wchar_t *new_wc = next_slash;
        while (*new_wc==L'/')
        {
            new_wc++;
        }
        /* Early out! */
        return wildcard_expand_internal(new_wc, new_base_dir.c_str(), flags, out, completion_set, visited_files);
    }
    
    /* Test for recursive match string in current segment */
    const bool is_recursive = (wc_segment.find(ANY_STRING_RECURSIVE) != wcstring::npos);
    

    const wchar_t *base_dir_or_cwd = (base_dir[0] == L'\0') ? L"." : base_dir;
    if (!(dir = wopendir(base_dir_or_cwd)))
    {
        return 0;
    }
    
    /*
      Is this segment of the wildcard the last?
    */
    if (is_last_segment)
    {
        /*
          Wildcard segment is the last segment,

          Insert all matching files/directories
        */
        if (wc[0]=='\0')
        {
            /*
              The last wildcard segment is empty. Insert everything if
              completing, the directory itself otherwise.
            */
            if (flags & ACCEPT_INCOMPLETE)
            {
                wcstring next;
                while (wreaddir(dir, next))
                {
                    if (next[0] != L'.')
                    {
                        wcstring long_name = make_path(base_dir, next);

                        if (test_flags(long_name.c_str(), flags))
                        {
                            wildcard_completion_allocate(out, long_name, next, L"", flags);
                        }
                    }
                }
            }
            else
            {
                res = 1;
                insert_completion_if_missing(base_dir, out, &completion_set);
            }
        }
        else
        {
            /* This is the last wildcard segment, and it is not empty. Match files/directories. */
            wcstring name_str;
            while (wreaddir(dir, name_str))
            {
                if (flags & ACCEPT_INCOMPLETE)
                {

                    const wcstring long_name = make_path(base_dir, name_str);

                    /* Test for matches before stating file, so as to minimize the number of calls to the much slower stat function. The only expand flag we care about is EXPAND_FUZZY_MATCH; we have no complete flags. */
                    std::vector<completion_t> test;
                    if (wildcard_complete(name_str, wc, L"", NULL, &test, flags & EXPAND_FUZZY_MATCH, 0))
                    {
                        if (test_flags(long_name.c_str(), flags))
                        {
                            wildcard_completion_allocate(out, long_name, name_str, wc, flags);

                        }
                    }
                }
                else
                {
                    if (wildcard_match(name_str, wc, true /* skip files with leading dots */))
                    {
                        const wcstring long_name = make_path(base_dir, name_str);
                        int skip = 0;

                        if (is_recursive)
                        {
                            /*
                              In recursive mode, we are only
                              interested in adding files -directories
                              will be added in the next pass.
                            */
                            struct stat buf;
                            if (!wstat(long_name, &buf))
                            {
                                skip = S_ISDIR(buf.st_mode);
                            }
                        }
                        if (! skip)
                        {
                            insert_completion_if_missing(long_name, out, &completion_set);
                        }
                        res = 1;
                    }
                }
            }
        }
    }

    if ((! is_last_segment) || is_recursive)
    {
        /*
          Wilcard segment is not the last segment.  Recursively call
          wildcard_expand for all matching subdirectories.
        */

        /*
          In recursive mode, we look through the directory twice. If
          so, this rewind is needed.
        */
        rewinddir(dir);

        /* new_dir is a scratch area containing the full path to a file/directory we are iterating over */
        wcstring new_dir = base_dir;

        wcstring name_str;
        while (wreaddir(dir, name_str))
        {
            /*
              Test if the file/directory name matches the whole
              wildcard element, i.e. regular matching.
            */
            bool whole_match = wildcard_match(name_str, wc_segment, true /* ignore leading dots */);
            bool partial_match = false;

            /*
               If we are doing recursive matching, also check if this
               directory matches the part up to the recusrive
               wildcard, if so, then we can search all subdirectories
               for matches.
            */
            if (is_recursive)
            {
                const wchar_t *end = wcschr(wc, ANY_STRING_RECURSIVE);
                wchar_t *wc_sub = wcsndup(wc, end-wc+1);
                partial_match = wildcard_match(name_str, wc_sub, true /* ignore leading dots */);
                free(wc_sub);
            }

            if (whole_match || partial_match)
            {
                struct stat buf;
                int new_res;

                // new_dir is base_dir + some other path components
                // Replace everything after base_dir with the new path component
                new_dir.replace(base_dir_len, wcstring::npos, name_str);

                int stat_res = wstat(new_dir, &buf);

                if (!stat_res)
                {
                    // Insert a "file ID" into visited_files
                    // If the insertion fails, we've already visited this file (i.e. a symlink loop)
                    // If we're not recursive, insert anyways (in case we loop back around in a future recursive segment), but continue on; the idea being that literal path components should still work
                    const file_id_t file_id = file_id_t::file_id_from_stat(&buf);
                    if (S_ISDIR(buf.st_mode) && (visited_files.insert(file_id).second || ! is_recursive))
                    {
                        new_dir.push_back(L'/');

                        /*
                          Regular matching
                        */
                        if (whole_match)
                        {
                            const wchar_t *new_wc = L"";
                            if (next_slash)
                            {
                                new_wc=next_slash+1;
                                /*
                                  Accept multiple '/' as a single directory separator
                                */
                                while (*new_wc==L'/')
                                {
                                    new_wc++;
                                }
                            }

                            new_res = wildcard_expand_internal(new_wc,
                                                               new_dir.c_str(),
                                                               flags,
                                                               out,
                                                               completion_set,
                                                               visited_files);

                            if (new_res == -1)
                            {
                                res = -1;
                                break;
                            }
                            res |= new_res;

                        }

                        /*
                          Recursive matching
                        */
                        if (partial_match)
                        {

                            new_res = wildcard_expand_internal(wcschr(wc, ANY_STRING_RECURSIVE),
                                                               new_dir.c_str(),
                                                               flags | WILDCARD_RECURSIVE,
                                                               out,
                                                               completion_set,
                                                               visited_files);

                            if (new_res == -1)
                            {
                                res = -1;
                                break;
                            }
                            res |= new_res;

                        }
                    }
                }
            }
        }
    }
    closedir(dir);

    return res;
}
Example #28
0
/*
    Give a more condensed description of \c event compared to \c event_get_desc.
    It includes what function will fire if the \c event is an event handler.
 */
static wcstring event_desc_compact(const event_t &event)
{
    wcstring res;
    wchar_t const *temp;
    int sig;
    switch (event.type)
    {
        case EVENT_ANY:
            res = L"EVENT_ANY";
            break;
        case EVENT_VARIABLE:
            if (event.str_param1.c_str())
            {
                res = format_string(L"EVENT_VARIABLE($%ls)", event.str_param1.c_str());
            }
            else
            {
                res = L"EVENT_VARIABLE([any])";
            }
            break;
        case EVENT_SIGNAL:
            sig = event.param1.signal;
            if (sig == EVENT_ANY_SIGNAL)
            {
                temp = L"[all signals]";
            }
            else if (sig == 0)
            {
                temp = L"not set";
            }
            else
            {
                temp = sig2wcs(sig);
            }
            res = format_string(L"EVENT_SIGNAL(%ls)", temp);
            break;
        case EVENT_EXIT:
            if (event.param1.pid == EVENT_ANY_PID)
            {
                res = wcstring(L"EVENT_EXIT([all child processes])");
            }
            else if (event.param1.pid > 0)
            {
                res = format_string(L"EVENT_EXIT(pid %d)", event.param1.pid);
            }
            else
            {
                job_t *j = job_get_from_pid(-event.param1.pid);
                if (j)
                    res = format_string(L"EVENT_EXIT(jobid %d: \"%ls\")", j->job_id, j->command_wcstr());
                else
                    res = format_string(L"EVENT_EXIT(pgid %d)", -event.param1.pid);
            }
            break;
        case EVENT_JOB_ID:
        {
            job_t *j = job_get(event.param1.job_id);
            if (j)
                res = format_string(L"EVENT_JOB_ID(job %d: \"%ls\")", j->job_id, j->command_wcstr());
            else
                res = format_string(L"EVENT_JOB_ID(jobid %d)", event.param1.job_id);
            break;
        }
        case EVENT_GENERIC:
            res = format_string(L"EVENT_GENERIC(%ls)", event.str_param1.c_str());
            break;
        default:
            res = format_string(L"unknown/illegal event(%x)", event.type);
    }
    if (event.function_name.size())
    {
        return format_string(L"%ls: \"%ls\"", res.c_str(), event.function_name.c_str());
    }
    else
    {
        return res;
    }
}
Example #29
0
static int find_process(const wchar_t *proc,
                        expand_flags_t flags,
                        std::vector<completion_t> &out)
{
    int found = 0;

    if (!(flags & EXPAND_SKIP_JOBS))
    {
        ASSERT_IS_MAIN_THREAD();
        const job_t *j;

        if (iswnumeric(proc) || (wcslen(proc)==0))
        {
            /*
              This is a numeric job string, like '%2'
            */

            if (flags & ACCEPT_INCOMPLETE)
            {
                job_iterator_t jobs;
                while ((j = jobs.next()))
                {
                    wchar_t jid[16];
                    if (j->command_is_empty())
                        continue;

                    swprintf(jid, 16, L"%d", j->job_id);

                    if (wcsncmp(proc, jid, wcslen(proc))==0)
                    {
                        wcstring desc_buff = format_string(COMPLETE_JOB_DESC_VAL, j->command_wcstr());
                        append_completion(out,
                                          jid+wcslen(proc),
                                          desc_buff,
                                          0);
                    }
                }

            }
            else
            {

                int jid;
                wchar_t *end;

                errno = 0;
                jid = fish_wcstoi(proc, &end, 10);
                if (jid > 0 && !errno && !*end)
                {
                    j = job_get(jid);
                    if ((j != 0) && (j->command_wcstr() != 0))
                    {
                        {
                            append_completion(out, to_string<long>(j->pgid));
                            found = 1;
                        }
                    }
                }
            }
        }
        if (found)
            return 1;

        job_iterator_t jobs;
        while ((j = jobs.next()))
        {

            if (j->command_is_empty())
                continue;

            size_t offset;
            if (match_pid(j->command(), proc, flags, &offset))
            {
                if (flags & ACCEPT_INCOMPLETE)
                {
                    append_completion(out,
                                      j->command_wcstr() + offset + wcslen(proc),
                                      COMPLETE_JOB_DESC,
                                      0);
                }
                else
                {
                    append_completion(out, to_string<long>(j->pgid));
                    found = 1;
                }
            }
        }

        if (found)
        {
            return 1;
        }

        jobs.reset();
        while ((j = jobs.next()))
        {
            process_t *p;
            if (j->command_is_empty())
                continue;
            for (p=j->first_process; p; p=p->next)
            {
                if (p->actual_cmd.empty())
                    continue;

                size_t offset;
                if (match_pid(p->actual_cmd, proc, flags, &offset))
                {
                    if (flags & ACCEPT_INCOMPLETE)
                    {
                        append_completion(out,
                                          wcstring(p->actual_cmd, offset + wcslen(proc)),
                                          COMPLETE_CHILD_PROCESS_DESC,
                                          0);
                    }
                    else
                    {
                        append_completion(out,
                                          to_string<long>(p->pid),
                                          L"",
                                          0);
                        found = 1;
                    }
                }
            }
        }

        if (found)
        {
            return 1;
        }
    }

    /* Iterate over all processes */
    wcstring process_name;
    pid_t process_pid;
    process_iterator_t iterator;
    while (iterator.next_process(&process_name, &process_pid))
    {
        size_t offset;
        if (match_pid(process_name, proc, flags, &offset))
        {
            if (flags & ACCEPT_INCOMPLETE)
            {
                append_completion(out,
                                  process_name.c_str() + offset + wcslen(proc),
                                  COMPLETE_PROCESS_DESC,
                                  0);
            }
            else
            {
                append_completion(out, to_string<long>(process_pid));
            }
        }
    }

    return 1;
}
Example #30
0
void parse_util_token_extent(const wchar_t *buff,
                             size_t cursor_pos,
                             const wchar_t **tok_begin,
                             const wchar_t **tok_end,
                             const wchar_t **prev_begin,
                             const wchar_t **prev_end)
{
    const wchar_t *a = NULL, *b = NULL, *pa = NULL, *pb = NULL;

    CHECK(buff,);

    assert(cursor_pos >= 0);

    const wchar_t *cmdsubst_begin, *cmdsubst_end;
    parse_util_cmdsubst_extent(buff, cursor_pos, &cmdsubst_begin, &cmdsubst_end);

    if (!cmdsubst_end || !cmdsubst_begin)
    {
        return;
    }

    /* pos is equivalent to cursor_pos within the range of the command substitution {begin, end} */
    long offset_within_cmdsubst = cursor_pos - (cmdsubst_begin - buff);

    a = cmdsubst_begin + offset_within_cmdsubst;
    b = a;
    pa = cmdsubst_begin + offset_within_cmdsubst;
    pb = pa;

    assert(cmdsubst_begin >= buff);
    assert(cmdsubst_begin <= (buff+wcslen(buff)));
    assert(cmdsubst_end >= cmdsubst_begin);
    assert(cmdsubst_end <= (buff+wcslen(buff)));

    const wcstring buffcpy = wcstring(cmdsubst_begin, cmdsubst_end-cmdsubst_begin);

    tokenizer_t tok(buffcpy.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS);
    for (; tok_has_next(&tok); tok_next(&tok))
    {
        size_t tok_begin = tok_get_pos(&tok);
        size_t tok_end = tok_begin;

        /*
          Calculate end of token
        */
        if (tok_last_type(&tok) == TOK_STRING)
        {
            tok_end += wcslen(tok_last(&tok));
        }

        /*
          Cursor was before beginning of this token, means that the
          cursor is between two tokens, so we set it to a zero element
          string and break
        */
        if (tok_begin > offset_within_cmdsubst)
        {
            a = b = cmdsubst_begin + offset_within_cmdsubst;
            break;
        }

        /*
          If cursor is inside the token, this is the token we are
          looking for. If so, set a and b and break
        */
        if ((tok_last_type(&tok) == TOK_STRING) && (tok_end >= offset_within_cmdsubst))
        {
            a = cmdsubst_begin + tok_get_pos(&tok);
            b = a + wcslen(tok_last(&tok));
            break;
        }

        /*
          Remember previous string token
        */
        if (tok_last_type(&tok) == TOK_STRING)
        {
            pa = cmdsubst_begin + tok_get_pos(&tok);
            pb = pa + wcslen(tok_last(&tok));
        }
    }

    if (tok_begin)
    {
        *tok_begin = a;
    }

    if (tok_end)
    {
        *tok_end = b;
    }

    if (prev_begin)
    {
        *prev_begin = pa;
    }

    if (prev_end)
    {
        *prev_end = pb;
    }

    assert(pa >= buff);
    assert(pa <= (buff+wcslen(buff)));
    assert(pb >= pa);
    assert(pb <= (buff+wcslen(buff)));

}