Skip to content
Snippets Groups Projects
ex_cmds2.c 127 KiB
Newer Older
			(size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T)));
	    ALIST(curwin)->al_ga.ga_len -= n;
	    if (curwin->w_arg_idx >= eap->line2)
		curwin->w_arg_idx -= n;
	    else if (curwin->w_arg_idx > eap->line1)
		curwin->w_arg_idx = eap->line1;
Bram Moolenaar's avatar
Bram Moolenaar committed
	    if (ARGCOUNT == 0)
		curwin->w_arg_idx = 0;
	    else if (curwin->w_arg_idx >= ARGCOUNT)
		curwin->w_arg_idx = ARGCOUNT - 1;
	}
    }
    else if (*eap->arg == NUL)
	EMSG(_(e_argreq));
    else
	do_arglist(eap->arg, AL_DEL, 0);
#ifdef FEAT_TITLE
    maketitle();
#endif
}

/*
Bram Moolenaar's avatar
Bram Moolenaar committed
 * ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo"
Bram Moolenaar's avatar
Bram Moolenaar committed
ex_listdo(exarg_T *eap)
    win_T	*wp;
    tabpage_T	*tp;
    buf_T	*buf = curbuf;
    int		next_fnum = 0;
#if defined(FEAT_SYN_HL)
    char_u	*save_ei = NULL;
#endif
    char_u	*p_shm_save;
Bram Moolenaar's avatar
Bram Moolenaar committed
#ifdef FEAT_QUICKFIX
Bram Moolenaar's avatar
Bram Moolenaar committed
    int		qf_size = 0;
Bram Moolenaar's avatar
Bram Moolenaar committed
    int		qf_idx;
#endif
Bram Moolenaar's avatar
Bram Moolenaar committed
#ifndef FEAT_QUICKFIX
    if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo ||
	    eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
    {
	ex_ni(eap);
	return;
    }
#endif

#if defined(FEAT_SYN_HL)
    if (eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo)
	/* Don't do syntax HL autocommands.  Skipping the syntax file is a
	 * great speed improvement. */
	save_ei = au_event_disable(",Syntax");
#endif
#ifdef FEAT_CLIPBOARD
    start_global_changes();
#endif

    if (eap->cmdidx == CMD_windo
	    || eap->cmdidx == CMD_tabdo
	    || !check_changed(curbuf, CCGD_AW
				    | (eap->forceit ? CCGD_FORCEIT : 0)
				    | CCGD_EXCMD))
	/* start at the eap->line1 argument/window/buffer */
	wp = firstwin;
	tp = first_tabpage;
	switch (eap->cmdidx)
	{
	    case CMD_windo:
		for ( ; wp != NULL && i + 1 < eap->line1; wp = wp->w_next)
		    i++;
		break;
	    case CMD_tabdo:
		for( ; tp != NULL && i + 1 < eap->line1; tp = tp->tp_next)
		    i++;
		break;
	    case CMD_argdo:
		i = eap->line1 - 1;
		break;
	    default:
		break;
	}
	/* set pcmark now */
	if (eap->cmdidx == CMD_bufdo)
Bram Moolenaar's avatar
Bram Moolenaar committed
	{
	    /* Advance to the first listed buffer after "eap->line1". */
Bram Moolenaar's avatar
Bram Moolenaar committed
	    for (buf = firstbuf; buf != NULL && (buf->b_fnum < eap->line1
					  || !buf->b_p_bl); buf = buf->b_next)
		if (buf->b_fnum > eap->line2)
		{
		    buf = NULL;
		    break;
		}
Bram Moolenaar's avatar
Bram Moolenaar committed
	    if (buf != NULL)
		goto_buffer(eap, DOBUF_FIRST, FORWARD, buf->b_fnum);
Bram Moolenaar's avatar
Bram Moolenaar committed
	}
#ifdef FEAT_QUICKFIX
	else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
		|| eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
	{
	    qf_size = qf_get_size(eap);
	    if (qf_size <= 0 || eap->line1 > qf_size)
		buf = NULL;
	    else
	    {
		ex_cc(eap);

		buf = curbuf;
		i = eap->line1 - 1;
		if (eap->addr_count <= 0)
		    /* default is all the quickfix/location list entries */
		    eap->line2 = qf_size;
	    }
	}
#endif
	else
	    setpcmark();
	listcmd_busy = TRUE;	    /* avoids setting pcmark below */

	while (!got_int && buf != NULL)
	{
	    if (eap->cmdidx == CMD_argdo)
	    {
		/* go to argument "i" */
		if (i == ARGCOUNT)
		    break;
		/* Don't call do_argfile() when already there, it will try
		 * reloading the file. */
		if (curwin->w_arg_idx != i || !editing_arg_idx(curwin))
		{
		    /* Clear 'shm' to avoid that the file message overwrites
		     * any output from the command. */
		    p_shm_save = vim_strsave(p_shm);
		    set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
		    do_argfile(eap, i);
		    set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
		    vim_free(p_shm_save);
		}
		if (curwin->w_arg_idx != i)
		    break;
	    }
	    else if (eap->cmdidx == CMD_windo)
	    {
		/* go to window "wp" */
		if (!win_valid(wp))
		    break;
		win_goto(wp);
		if (curwin != wp)
		    break;  /* something must be wrong */
		wp = curwin->w_next;
	    }
	    else if (eap->cmdidx == CMD_tabdo)
	    {
		/* go to window "tp" */
		if (!valid_tabpage(tp))
		    break;
		goto_tabpage_tp(tp, TRUE, TRUE);
		tp = tp->tp_next;
	    }
	    else if (eap->cmdidx == CMD_bufdo)
	    {
		/* Remember the number of the next listed buffer, in case
		 * ":bwipe" is used or autocommands do something strange. */
		next_fnum = -1;
		for (buf = curbuf->b_next; buf != NULL; buf = buf->b_next)
		    if (buf->b_p_bl)
		    {
			next_fnum = buf->b_fnum;
			break;
		    }
	    }

	    /* execute the command */
	    do_cmdline(eap->arg, eap->getline, eap->cookie,
						DOCMD_VERBOSE + DOCMD_NOWAIT);

	    if (eap->cmdidx == CMD_bufdo)
	    {
		/* Done? */
		if (next_fnum < 0 || next_fnum > eap->line2)
		    break;
		/* Check if the buffer still exists. */
Bram Moolenaar's avatar
Bram Moolenaar committed
		FOR_ALL_BUFFERS(buf)
		    if (buf->b_fnum == next_fnum)
			break;
		if (buf == NULL)
		    break;

		/* Go to the next buffer.  Clear 'shm' to avoid that the file
		 * message overwrites any output from the command. */
		p_shm_save = vim_strsave(p_shm);
		set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
		goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum);
		set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
		vim_free(p_shm_save);

Bram Moolenaar's avatar
Bram Moolenaar committed
		/* If autocommands took us elsewhere, quit here. */
		if (curbuf->b_fnum != next_fnum)
		    break;
	    }

Bram Moolenaar's avatar
Bram Moolenaar committed
#ifdef FEAT_QUICKFIX
	    if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
		    || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
	    {
		if (i >= qf_size || i >= eap->line2)
		    break;

		qf_idx = qf_get_cur_idx(eap);

		ex_cnext(eap);

		/* If jumping to the next quickfix entry fails, quit here */
		if (qf_get_cur_idx(eap) == qf_idx)
		    break;
	    }
#endif

	    if (eap->cmdidx == CMD_windo)
	    {
		validate_cursor();	/* cursor may have moved */
		/* required when 'scrollbind' has been set */
		if (curwin->w_p_scb)
		    do_check_scrollbind(TRUE);
	    }

	    if (eap->cmdidx == CMD_windo || eap->cmdidx == CMD_tabdo)
		if (i+1 > eap->line2)
		    break;
	    if (eap->cmdidx == CMD_argdo && i >= eap->line2)
		break;
	}
	listcmd_busy = FALSE;
    }

#if defined(FEAT_SYN_HL)
    if (save_ei != NULL)
    {
	au_event_restore(save_ei);
	apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn,
					       curbuf->b_fname, TRUE, curbuf);
    }
#endif
#ifdef FEAT_CLIPBOARD
    end_global_changes();
#endif
}

/*
 * Add files[count] to the arglist of the current window after arg "after".
 * The file names in files[count] must have been allocated and are taken over.
 * Files[] itself is not taken over.
 * Returns index of first added argument.  Returns -1 when failed (out of mem).
 */
    static int
Bram Moolenaar's avatar
Bram Moolenaar committed
alist_add_list(
    int		count,
    char_u	**files,
    int		after)	    /* where to add: 0 = before first one */
Bram Moolenaar's avatar
Bram Moolenaar committed
    int		old_argcount = ARGCOUNT;

    if (ga_grow(&ALIST(curwin)->al_ga, count) == OK)
    {
	if (after < 0)
	    after = 0;
	if (after > ARGCOUNT)
	    after = ARGCOUNT;
	if (after < ARGCOUNT)
	    mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
				       (ARGCOUNT - after) * sizeof(aentry_T));
	for (i = 0; i < count; ++i)
	{
	    ARGLIST[after + i].ae_fname = files[i];
	    ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED);
	}
	ALIST(curwin)->al_ga.ga_len += count;
Bram Moolenaar's avatar
Bram Moolenaar committed
	if (old_argcount > 0 && curwin->w_arg_idx >= after)
	    curwin->w_arg_idx += count;
	return after;
    }

    for (i = 0; i < count; ++i)
	vim_free(files[i]);
    return -1;
}

#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
/*
 * Function given to ExpandGeneric() to obtain the possible arguments of the
 * argedit and argdelete commands.
 */
    char_u *
get_arglist_name(expand_T *xp UNUSED, int idx)
{
    if (idx >= ARGCOUNT)
	return NULL;

    return alist_name(&ARGLIST[idx]);
}
#endif

#ifdef FEAT_EVAL
/*
 * ":compiler[!] {name}"
 */
    void
Bram Moolenaar's avatar
Bram Moolenaar committed
ex_compiler(exarg_T *eap)
{
    char_u	*buf;
    char_u	*old_cur_comp = NULL;
    char_u	*p;

    if (*eap->arg == NUL)
    {
	/* List all compiler scripts. */
	do_cmdline_cmd((char_u *)"echo globpath(&rtp, 'compiler/*.vim')");
					/* ) keep the indenter happy... */
    }
    else
    {
	buf = alloc((unsigned)(STRLEN(eap->arg) + 14));
	if (buf != NULL)
	{
	    if (eap->forceit)
	    {
		/* ":compiler! {name}" sets global options */
		do_cmdline_cmd((char_u *)
				   "command -nargs=* CompilerSet set <args>");
	    }
	    else
	    {
		/* ":compiler! {name}" sets local options.
		 * To remain backwards compatible "current_compiler" is always
		 * used.  A user's compiler plugin may set it, the distributed
		 * plugin will then skip the settings.  Afterwards set
		 * "b:current_compiler" and restore "current_compiler".
		 * Explicitly prepend "g:" to make it work in a function. */
		old_cur_comp = get_var_value((char_u *)"g:current_compiler");
		if (old_cur_comp != NULL)
		    old_cur_comp = vim_strsave(old_cur_comp);
		do_cmdline_cmd((char_u *)
			      "command -nargs=* CompilerSet setlocal <args>");
	    }
	    do_unlet((char_u *)"g:current_compiler", TRUE);
	    do_unlet((char_u *)"b:current_compiler", TRUE);

	    sprintf((char *)buf, "compiler/%s.vim", eap->arg);
Bram Moolenaar's avatar
Bram Moolenaar committed
	    if (source_runtime(buf, DIP_ALL) == FAIL)
		EMSG2(_("E666: compiler not supported: %s"), eap->arg);
	    vim_free(buf);

	    do_cmdline_cmd((char_u *)":delcommand CompilerSet");

	    /* Set "b:current_compiler" from "current_compiler". */
	    p = get_var_value((char_u *)"g:current_compiler");
	    if (p != NULL)
		set_internal_string_var((char_u *)"b:current_compiler", p);

	    /* Restore "current_compiler" for ":compiler {name}". */
	    if (!eap->forceit)
	    {
		if (old_cur_comp != NULL)
		{
		    set_internal_string_var((char_u *)"g:current_compiler",
								old_cur_comp);
		    vim_free(old_cur_comp);
		}
		else
		    do_unlet((char_u *)"g:current_compiler", TRUE);
Bram Moolenaar's avatar
Bram Moolenaar committed
 * ":runtime [what] {name}"
Bram Moolenaar's avatar
Bram Moolenaar committed
ex_runtime(exarg_T *eap)
Bram Moolenaar's avatar
Bram Moolenaar committed
    char_u  *arg = eap->arg;
    char_u  *p = skiptowhite(arg);
    int	    len = (int)(p - arg);
    int	    flags = eap->forceit ? DIP_ALL : 0;

    if (STRNCMP(arg, "START", len) == 0)
    {
	flags += DIP_START + DIP_NORTP;
	arg = skipwhite(arg + len);
    }
    else if (STRNCMP(arg, "OPT", len) == 0)
    {
	flags += DIP_OPT + DIP_NORTP;
	arg = skipwhite(arg + len);
    }
    else if (STRNCMP(arg, "PACK", len) == 0)
    {
	flags += DIP_START + DIP_OPT + DIP_NORTP;
	arg = skipwhite(arg + len);
    }
    else if (STRNCMP(arg, "ALL", len) == 0)
    {
	flags += DIP_START + DIP_OPT;
	arg = skipwhite(arg + len);
    }

    source_runtime(arg, flags);
Bram Moolenaar's avatar
Bram Moolenaar committed
source_callback(char_u *fname, void *cookie UNUSED)
    (void)do_source(fname, FALSE, DOSO_NONE);
Bram Moolenaar's avatar
Bram Moolenaar committed
/*
 * Find the file "name" in all directories in "path" and invoke
 * "callback(fname, cookie)".
 * "name" can contain wildcards.
 * When "flags" has DIP_ALL: source all files, otherwise only the first one.
 * When "flags" has DIP_DIR: find directories instead of files.
 * When "flags" has DIP_ERR: give an error message if there is no match.
 *
 * return FAIL when no file could be sourced, OK otherwise.
 */
Bram Moolenaar's avatar
Bram Moolenaar committed
    int
Bram Moolenaar's avatar
Bram Moolenaar committed
do_in_path(
    char_u	*path,
Bram Moolenaar's avatar
Bram Moolenaar committed
    char_u	*name,
Bram Moolenaar's avatar
Bram Moolenaar committed
    int		flags,
Bram Moolenaar's avatar
Bram Moolenaar committed
    void	(*callback)(char_u *fname, void *ck),
    void	*cookie)
{
    char_u	*rtp;
    char_u	*np;
    char_u	*buf;
    char_u	*rtp_copy;
    char_u	*tail;
    int		num_files;
    char_u	**files;
    int		i;
    int		did_one = FALSE;
#ifdef AMIGA
    struct Process	*proc = (struct Process *)FindTask(0L);
    APTR		save_winptr = proc->pr_WindowPtr;

    /* Avoid a requester here for a volume that doesn't exist. */
    proc->pr_WindowPtr = (APTR)-1L;
#endif

    /* Make a copy of 'runtimepath'.  Invoking the callback may change the
     * value. */
Bram Moolenaar's avatar
Bram Moolenaar committed
    rtp_copy = vim_strsave(path);
    buf = alloc(MAXPATHL);
    if (buf != NULL && rtp_copy != NULL)
    {
	if (p_verbose > 1 && name != NULL)
	{
	    verbose_enter();
	    smsg((char_u *)_("Searching for \"%s\" in \"%s\""),
Bram Moolenaar's avatar
Bram Moolenaar committed
						 (char *)name, (char *)path);
	    verbose_leave();
	}
	/* Loop over all entries in 'runtimepath'. */
	rtp = rtp_copy;
Bram Moolenaar's avatar
Bram Moolenaar committed
	while (*rtp != NUL && ((flags & DIP_ALL) || !did_one))
Bram Moolenaar's avatar
Bram Moolenaar committed
	    size_t buflen;

	    /* Copy the path from 'runtimepath' to buf[]. */
	    copy_option_part(&rtp, buf, MAXPATHL, ",");
Bram Moolenaar's avatar
Bram Moolenaar committed
	    buflen = STRLEN(buf);

	    /* Skip after or non-after directories. */
	    if (flags & (DIP_NOAFTER | DIP_AFTER))
	    {
		int is_after = buflen >= 5
				     && STRCMP(buf + buflen - 5, "after") == 0;

		if ((is_after && (flags & DIP_NOAFTER))
			|| (!is_after && (flags & DIP_AFTER)))
		    continue;
	    }

	    if (name == NULL)
	    {
		(*callback)(buf, (void *) &cookie);
		if (!did_one)
		    did_one = (cookie == NULL);
	    }
Bram Moolenaar's avatar
Bram Moolenaar committed
	    else if (buflen + STRLEN(name) + 2 < MAXPATHL)
	    {
		add_pathsep(buf);
		tail = buf + STRLEN(buf);

		/* Loop over all patterns in "name" */
		np = name;
Bram Moolenaar's avatar
Bram Moolenaar committed
		while (*np != NUL && ((flags & DIP_ALL) || !did_one))
		{
		    /* Append the pattern from "name" to buf[]. */
		    copy_option_part(&np, tail, (int)(MAXPATHL - (tail - buf)),
								       "\t ");

		    if (p_verbose > 2)
		    {
			verbose_enter();
			smsg((char_u *)_("Searching for \"%s\""), buf);
			verbose_leave();
		    }

		    /* Expand wildcards, invoke the callback for each match. */
		    if (gen_expand_wildcards(1, &buf, &num_files, &files,
Bram Moolenaar's avatar
Bram Moolenaar committed
				  (flags & DIP_DIR) ? EW_DIR : EW_FILE) == OK)
		    {
			for (i = 0; i < num_files; ++i)
			{
			    (*callback)(files[i], cookie);
			    did_one = TRUE;
Bram Moolenaar's avatar
Bram Moolenaar committed
			    if (!(flags & DIP_ALL))
				break;
			}
			FreeWild(num_files, files);
		    }
		}
	    }
	}
    }
    vim_free(buf);
    vim_free(rtp_copy);
Bram Moolenaar's avatar
Bram Moolenaar committed
    if (!did_one && name != NULL)
Bram Moolenaar's avatar
Bram Moolenaar committed
	char *basepath = path == p_rtp ? "runtimepath" : "packpath";

	if (flags & DIP_ERR)
	    EMSG3(_(e_dirnotf), basepath, name);
	else if (p_verbose > 0)
	{
	    verbose_enter();
	    smsg((char_u *)_("not found in '%s': \"%s\""), basepath, name);
	    verbose_leave();
	}

#ifdef AMIGA
    proc->pr_WindowPtr = save_winptr;
#endif

    return did_one ? OK : FAIL;
}

Bram Moolenaar's avatar
Bram Moolenaar committed
/*
 * Find "name" in "path".  When found, invoke the callback function for
Bram Moolenaar's avatar
Bram Moolenaar committed
 * it: callback(fname, "cookie")
Bram Moolenaar's avatar
Bram Moolenaar committed
 * When "flags" has DIP_ALL repeat for all matches, otherwise only the first
 * one is used.
Bram Moolenaar's avatar
Bram Moolenaar committed
 * Returns OK when at least one match found, FAIL otherwise.
 *
 * If "name" is NULL calls callback for each entry in "path". Cookie is
Bram Moolenaar's avatar
Bram Moolenaar committed
 * passed by reference in this case, setting it to NULL indicates that callback
 * has done its job.
 */
    static int
do_in_path_and_pp(
    char_u	*path,
Bram Moolenaar's avatar
Bram Moolenaar committed
    char_u	*name,
Bram Moolenaar's avatar
Bram Moolenaar committed
    int		flags,
Bram Moolenaar's avatar
Bram Moolenaar committed
    void	(*callback)(char_u *fname, void *ck),
    void	*cookie)
{
Bram Moolenaar's avatar
Bram Moolenaar committed
    int		done = FAIL;
Bram Moolenaar's avatar
Bram Moolenaar committed
    char_u	*s;
    int		len;
    char	*start_dir = "pack/*/start/*/%s";
    char	*opt_dir = "pack/*/opt/*/%s";

Bram Moolenaar's avatar
Bram Moolenaar committed
    if ((flags & DIP_NORTP) == 0)
	done = do_in_path(path, name, flags, callback, cookie);
Bram Moolenaar's avatar
Bram Moolenaar committed

Bram Moolenaar's avatar
Bram Moolenaar committed
    if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START))
Bram Moolenaar's avatar
Bram Moolenaar committed
    {
Bram Moolenaar's avatar
Bram Moolenaar committed
	len = (int)(STRLEN(start_dir) + STRLEN(name));
Bram Moolenaar's avatar
Bram Moolenaar committed
	s = alloc(len);
	if (s == NULL)
	    return FAIL;
	vim_snprintf((char *)s, len, start_dir, name);
	done = do_in_path(p_pp, s, flags, callback, cookie);
	vim_free(s);
    }

Bram Moolenaar's avatar
Bram Moolenaar committed
    if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_OPT))
Bram Moolenaar's avatar
Bram Moolenaar committed
    {
Bram Moolenaar's avatar
Bram Moolenaar committed
	len = (int)(STRLEN(opt_dir) + STRLEN(name));
Bram Moolenaar's avatar
Bram Moolenaar committed
	s = alloc(len);
	if (s == NULL)
	    return FAIL;
	vim_snprintf((char *)s, len, opt_dir, name);
	done = do_in_path(p_pp, s, flags, callback, cookie);
	vim_free(s);
    }

    return done;
/*
 * Just like do_in_path_and_pp(), using 'runtimepath' for "path".
 */
    int
do_in_runtimepath(
    char_u	*name,
    int		flags,
    void	(*callback)(char_u *fname, void *ck),
    void	*cookie)
{
    return do_in_path_and_pp(p_rtp, name, flags, callback, cookie);
}

/*
 * Source the file "name" from all directories in 'runtimepath'.
 * "name" can contain wildcards.
 * When "flags" has DIP_ALL: source all files, otherwise only the first one.
 *
 * return FAIL when no file could be sourced, OK otherwise.
 */
    int
source_runtime(char_u *name, int flags)
{
    return source_in_path(p_rtp, name, flags);
}

/*
 * Just like source_runtime(), but use "path" instead of 'runtimepath'.
 */
    int
source_in_path(char_u *path, char_u *name, int flags)
{
    return do_in_path_and_pp(path, name, flags, source_callback, NULL);
}


#if defined(FEAT_EVAL) || defined(PROTO)

Bram Moolenaar's avatar
Bram Moolenaar committed
/*
Bram Moolenaar's avatar
Bram Moolenaar committed
 * Expand wildcards in "pat" and invoke do_source() for each match.
Bram Moolenaar's avatar
Bram Moolenaar committed
 */
    static void
Bram Moolenaar's avatar
Bram Moolenaar committed
source_all_matches(char_u *pat)
Bram Moolenaar's avatar
Bram Moolenaar committed
{
Bram Moolenaar's avatar
Bram Moolenaar committed
    int	    num_files;
    char_u  **files;
    int	    i;
Bram Moolenaar's avatar
Bram Moolenaar committed

Bram Moolenaar's avatar
Bram Moolenaar committed
    if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) == OK)
Bram Moolenaar's avatar
Bram Moolenaar committed
    {
Bram Moolenaar's avatar
Bram Moolenaar committed
	for (i = 0; i < num_files; ++i)
	    (void)do_source(files[i], FALSE, DOSO_NONE);
	FreeWild(num_files, files);
/*
 * Add the package directory to 'runtimepath'.
 */
    static int
add_pack_dir_to_rtp(char_u *fname)
Bram Moolenaar's avatar
Bram Moolenaar committed
{
Bram Moolenaar's avatar
Bram Moolenaar committed
    char_u  *p4, *p3, *p2, *p1, *p;
    char_u  *insp;
Bram Moolenaar's avatar
Bram Moolenaar committed
    int	    c;
    char_u  *new_rtp;
    int	    keep;
Bram Moolenaar's avatar
Bram Moolenaar committed
    size_t  oldlen;
    size_t  addlen;
Bram Moolenaar's avatar
Bram Moolenaar committed
    size_t  afterlen = 0;
Bram Moolenaar's avatar
Bram Moolenaar committed
    size_t  fname_len;
    char_u  *buf = NULL;
    char_u  *rtp_ffname;
    int	    match;
Bram Moolenaar's avatar
Bram Moolenaar committed

    p4 = p3 = p2 = p1 = get_past_head(fname);
    for (p = p1; *p; MB_PTR_ADV(p))
	if (vim_ispathsep_nocolon(*p))
Bram Moolenaar's avatar
Bram Moolenaar committed
	{
    /* now we have:
     * rtp/pack/name/start/name
     *    p4   p3   p2    p1
     *
     * find the part up to "pack" in 'runtimepath' */
    c = *++p4; /* append pathsep in order to expand symlink */
    *p4 = NUL;
    ffname = fix_fname(fname);
    *p4 = c;
    if (ffname == NULL)
	return FAIL;

    /* Find "ffname" in "p_rtp", ignoring '/' vs '\' differences. */
    fname_len = STRLEN(ffname);
    insp = p_rtp;
    buf = alloc(MAXPATHL);
    if (buf == NULL)
	goto theend;
    while (*insp != NUL)
    {
	copy_option_part(&insp, buf, MAXPATHL, ",");
	add_pathsep(buf);
	rtp_ffname = fix_fname(buf);
	if (rtp_ffname == NULL)
Bram Moolenaar's avatar
Bram Moolenaar committed
	    goto theend;
	match = vim_fnamencmp(rtp_ffname, ffname, fname_len) == 0;
	vim_free(rtp_ffname);
	if (match)
	    break;
    if (*insp == NUL)
	/* not found, append at the end */
	insp = p_rtp + STRLEN(p_rtp);
    else
	/* append after the matching directory. */
	--insp;

    /* check if rtp/pack/name/start/name/after exists */
    afterdir = concat_fnames(fname, (char_u *)"after", TRUE);
    if (afterdir != NULL && mch_isdir(afterdir))
	afterlen = STRLEN(afterdir) + 1; /* add one for comma */

    oldlen = STRLEN(p_rtp);
    addlen = STRLEN(fname) + 1; /* add one for comma */
    new_rtp = alloc((int)(oldlen + addlen + afterlen + 1));
    /* add one for NUL */
    if (new_rtp == NULL)
	goto theend;
    keep = (int)(insp - p_rtp);
    mch_memmove(new_rtp, p_rtp, keep);
    new_rtp[keep] = ',';
    mch_memmove(new_rtp + keep + 1, fname, addlen);
    if (p_rtp[keep] != NUL)
	mch_memmove(new_rtp + keep + addlen, p_rtp + keep, oldlen - keep + 1);
    if (afterlen > 0)
Bram Moolenaar's avatar
Bram Moolenaar committed
    {
	STRCAT(new_rtp, ",");
	STRCAT(new_rtp, afterdir);
    }
    set_option_value((char_u *)"rtp", 0L, new_rtp, 0);
    vim_free(new_rtp);
    retval = OK;
Bram Moolenaar's avatar
Bram Moolenaar committed

theend:
    vim_free(buf);
    vim_free(ffname);
    vim_free(afterdir);
    return retval;
}

/*
 * Load scripts in "plugin" and "ftdetect" directories of the package.
 */
    static int
load_pack_plugin(char_u *fname)
{
    static char *plugpat = "%s/plugin/**/*.vim";
    static char *ftpat = "%s/ftdetect/*.vim";
    int		len;
    char_u	*ffname = fix_fname(fname);
    char_u	*pat = NULL;
    int		retval = FAIL;

    if (ffname == NULL)
	return FAIL;
    len = (int)STRLEN(ffname) + (int)STRLEN(ftpat);
    pat = alloc(len);
    if (pat == NULL)
	goto theend;
    vim_snprintf((char *)pat, len, plugpat, ffname);
    source_all_matches(pat);
Bram Moolenaar's avatar
Bram Moolenaar committed

    {
	char_u *cmd = vim_strsave((char_u *)"g:did_load_filetypes");
Bram Moolenaar's avatar
Bram Moolenaar committed

	/* If runtime/filetype.vim wasn't loaded yet, the scripts will be
	 * found when it loads. */
	if (cmd != NULL && eval_to_number(cmd) > 0)
	{
	    do_cmdline_cmd((char_u *)"augroup filetypedetect");
	    vim_snprintf((char *)pat, len, ftpat, ffname);
	    source_all_matches(pat);
	    do_cmdline_cmd((char_u *)"augroup END");
Bram Moolenaar's avatar
Bram Moolenaar committed
	}
Bram Moolenaar's avatar
Bram Moolenaar committed
    }
Bram Moolenaar's avatar
Bram Moolenaar committed

theend:
    vim_free(ffname);
    return retval;
}

/* used for "cookie" of add_pack_plugin() */
static int APP_ADD_DIR;
static int APP_LOAD;
static int APP_BOTH;

    static void
add_pack_plugin(char_u *fname, void *cookie)
{
    if (cookie != &APP_LOAD)
    {
	char_u	*buf = alloc(MAXPATHL);
	char_u	*p;
	int	found = FALSE;

	if (buf == NULL)
	p = p_rtp;
	while (*p != NUL)
	{
	    copy_option_part(&p, buf, MAXPATHL, ",");
	    if (pathcmp((char *)buf, (char *)fname, -1) == 0)
	    {
		found = TRUE;
		break;
	    }
	}
	vim_free(buf);
	if (!found)
	    /* directory is not yet in 'runtimepath', add it */
	    if (add_pack_dir_to_rtp(fname) == FAIL)
		return;
    }

    if (cookie != &APP_ADD_DIR)
	load_pack_plugin(fname);
/*
 * Add all packages in the "start" directory to 'runtimepath'.
 */
    void
add_pack_start_dirs(void)
{
    do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR,
					       add_pack_plugin, &APP_ADD_DIR);
}

/*
 * Load plugins from all packages in the "start" directory.
 */
    void
load_start_packages(void)
{
    did_source_packages = TRUE;
    do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR,
						  add_pack_plugin, &APP_LOAD);
}
Bram Moolenaar's avatar
Bram Moolenaar committed

Bram Moolenaar's avatar
Bram Moolenaar committed
/*
Bram Moolenaar's avatar
Bram Moolenaar committed
 * ":packloadall"
Bram Moolenaar's avatar
Bram Moolenaar committed
 * Find plugins in the package directories and source them.
Bram Moolenaar's avatar
Bram Moolenaar committed
 */
    void
Bram Moolenaar's avatar
Bram Moolenaar committed
ex_packloadall(exarg_T *eap)
Bram Moolenaar's avatar
Bram Moolenaar committed
{
    if (!did_source_packages || eap->forceit)
Bram Moolenaar's avatar
Bram Moolenaar committed
    {
Bram Moolenaar's avatar
Bram Moolenaar committed
	/* First do a round to add all directories to 'runtimepath', then load
	 * the plugins. This allows for plugins to use an autoload directory
	 * of another plugin. */
	add_pack_start_dirs();
	load_start_packages();
Bram Moolenaar's avatar
Bram Moolenaar committed
    }
Bram Moolenaar's avatar
Bram Moolenaar committed
/*
Bram Moolenaar's avatar
Bram Moolenaar committed
 * ":packadd[!] {name}"
Bram Moolenaar's avatar
Bram Moolenaar committed
 */
    void
ex_packadd(exarg_T *eap)
{
    static char *plugpat = "pack/*/%s/%s";
Bram Moolenaar's avatar
Bram Moolenaar committed
    int		len;
    char	*pat;
Bram Moolenaar's avatar
Bram Moolenaar committed

    /* Round 1: use "start", round 2: use "opt". */
    for (round = 1; round <= 2; ++round)
    {
	/* Only look under "start" when loading packages wasn't done yet. */
	if (round == 1 && did_source_packages)
	    continue;

	len = (int)STRLEN(plugpat) + (int)STRLEN(eap->arg) + 5;
	pat = (char *)alloc(len);
	if (pat == NULL)
	    return;
	vim_snprintf(pat, len, plugpat, round == 1 ? "start" : "opt", eap->arg);
	/* The first round don't give a "not found" error, in the second round
	 * only when nothing was found in the first round. */
	res = do_in_path(p_pp, (char_u *)pat,
		DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0),
		add_pack_plugin, eap->forceit ? &APP_ADD_DIR : &APP_BOTH);
	vim_free(pat);
    }
Bram Moolenaar's avatar
Bram Moolenaar committed
}
Bram Moolenaar's avatar
Bram Moolenaar committed

#if defined(FEAT_EVAL) || defined(PROTO)
/*
 * ":options"
 */
    void
Bram Moolenaar's avatar
Bram Moolenaar committed
ex_options(
    exarg_T	*eap UNUSED)
    vim_setenv((char_u *)"OPTWIN_CMD", (char_u *)(cmdmod.tab ? "tab" : ""));
    cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
}
#endif

#if defined(FEAT_PYTHON3) || defined(FEAT_PYTHON) || defined(PROTO)

# if (defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)) || defined(PROTO)
/*
 * Detect Python 3 or 2, and initialize 'pyxversion'.
 */
    void
init_pyxversion(void)
{
    if (p_pyx == 0)
    {
	if (python3_enabled(FALSE))
	    p_pyx = 3;
	else if (python_enabled(FALSE))
	    p_pyx = 2;
    }
}
# endif

/*
 * Does a file contain one of the following strings at the beginning of any
 * line?
 * "#!(any string)python2"  => returns 2
 * "#!(any string)python3"  => returns 3
 * "# requires python 2.x"  => returns 2
 * "# requires python 3.x"  => returns 3
 * otherwise return 0.
 */
    static int
requires_py_version(char_u *filename)
{
    FILE    *file;
    int	    requires_py_version = 0;
    int	    i, lines;

    lines = (int)p_mls;
    if (lines < 0)
	lines = 5;

    file = mch_fopen((char *)filename, "r");
    if (file != NULL)
    {
	for (i = 0; i < lines; i++)
	{
	    if (vim_fgets(IObuff, IOSIZE, file))
		break;
	    if (i == 0 && IObuff[0] == '#' && IObuff[1] == '!')
	    {
		/* Check shebang. */
		if (strstr((char *)IObuff + 2, "python2") != NULL)
		{
		    requires_py_version = 2;
		    break;
		}
		if (strstr((char *)IObuff + 2, "python3") != NULL)
		{
		    requires_py_version = 3;
		    break;
		}
	    }
	    IObuff[21] = '\0';
	    if (STRCMP("# requires python 2.x", IObuff) == 0)