view src/quickfix.c @ 4444:ccecb03e5e8b v7.3.970

updated for version 7.3.970 Problem: Syntax highlighting can be slow. Solution: Include the NFA regexp engine. Add the 'regexpengine' option to select which one is used. (various authors, including Ken Takata, Andrei Aiordachioaie, Russ Cox, Xiaozhou Liua, Ian Young)
author Bram Moolenaar <bram@vim.org>
date Sun, 19 May 2013 19:40:29 +0200
parents 000bb500208d
children 66803af09906
line wrap: on
line source

/* vi:set ts=8 sts=4 sw=4:
 *
 * VIM - Vi IMproved	by Bram Moolenaar
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 * See README.txt for an overview of the Vim source code.
 */

/*
 * quickfix.c: functions for quickfix mode, using a file with error messages
 */

#include "vim.h"

#if defined(FEAT_QUICKFIX) || defined(PROTO)

struct dir_stack_T
{
    struct dir_stack_T	*next;
    char_u		*dirname;
};

static struct dir_stack_T   *dir_stack = NULL;

/*
 * For each error the next struct is allocated and linked in a list.
 */
typedef struct qfline_S qfline_T;
struct qfline_S
{
    qfline_T	*qf_next;	/* pointer to next error in the list */
    qfline_T	*qf_prev;	/* pointer to previous error in the list */
    linenr_T	qf_lnum;	/* line number where the error occurred */
    int		qf_fnum;	/* file number for the line */
    int		qf_col;		/* column where the error occurred */
    int		qf_nr;		/* error number */
    char_u	*qf_pattern;	/* search pattern for the error */
    char_u	*qf_text;	/* description of the error */
    char_u	qf_viscol;	/* set to TRUE if qf_col is screen column */
    char_u	qf_cleared;	/* set to TRUE if line has been deleted */
    char_u	qf_type;	/* type of the error (mostly 'E'); 1 for
				   :helpgrep */
    char_u	qf_valid;	/* valid error message detected */
};

/*
 * There is a stack of error lists.
 */
#define LISTCOUNT   10

typedef struct qf_list_S
{
    qfline_T	*qf_start;	/* pointer to the first error */
    qfline_T	*qf_ptr;	/* pointer to the current error */
    int		qf_count;	/* number of errors (0 means no error list) */
    int		qf_index;	/* current index in the error list */
    int		qf_nonevalid;	/* TRUE if not a single valid entry found */
    char_u	*qf_title;	/* title derived from the command that created
				 * the error list */
} qf_list_T;

struct qf_info_S
{
    /*
     * Count of references to this list. Used only for location lists.
     * When a location list window reference this list, qf_refcount
     * will be 2. Otherwise, qf_refcount will be 1. When qf_refcount
     * reaches 0, the list is freed.
     */
    int		qf_refcount;
    int		qf_listcount;	    /* current number of lists */
    int		qf_curlist;	    /* current error list */
    qf_list_T	qf_lists[LISTCOUNT];
};

static qf_info_T ql_info;	/* global quickfix list */

#define FMT_PATTERNS 10		/* maximum number of % recognized */

/*
 * Structure used to hold the info of one part of 'errorformat'
 */
typedef struct efm_S efm_T;
struct efm_S
{
    regprog_T	    *prog;	/* pre-formatted part of 'errorformat' */
    efm_T	    *next;	/* pointer to next (NULL if last) */
    char_u	    addr[FMT_PATTERNS]; /* indices of used % patterns */
    char_u	    prefix;	/* prefix of this format line: */
				/*   'D' enter directory */
				/*   'X' leave directory */
				/*   'A' start of multi-line message */
				/*   'E' error message */
				/*   'W' warning message */
				/*   'I' informational message */
				/*   'C' continuation line */
				/*   'Z' end of multi-line message */
				/*   'G' general, unspecific message */
				/*   'P' push file (partial) message */
				/*   'Q' pop/quit file (partial) message */
				/*   'O' overread (partial) message */
    char_u	    flags;	/* additional flags given in prefix */
				/*   '-' do not include this line */
				/*   '+' include whole line in message */
    int		    conthere;	/* %> used */
};

static int	qf_init_ext __ARGS((qf_info_T *qi, char_u *efile, buf_T *buf, typval_T *tv, char_u *errorformat, int newlist, linenr_T lnumfirst, linenr_T lnumlast, char_u *qf_title));
static void	qf_new_list __ARGS((qf_info_T *qi, char_u *qf_title));
static void	ll_free_all __ARGS((qf_info_T **pqi));
static int	qf_add_entry __ARGS((qf_info_T *qi, qfline_T **prevp, char_u *dir, char_u *fname, int bufnum, char_u *mesg, long lnum, int col, int vis_col, char_u *pattern, int nr, int type, int valid));
static qf_info_T *ll_new_list __ARGS((void));
static void	qf_msg __ARGS((qf_info_T *qi));
static void	qf_free __ARGS((qf_info_T *qi, int idx));
static char_u	*qf_types __ARGS((int, int));
static int	qf_get_fnum __ARGS((char_u *, char_u *));
static char_u	*qf_push_dir __ARGS((char_u *, struct dir_stack_T **));
static char_u	*qf_pop_dir __ARGS((struct dir_stack_T **));
static char_u	*qf_guess_filepath __ARGS((char_u *));
static void	qf_fmt_text __ARGS((char_u *text, char_u *buf, int bufsize));
static void	qf_clean_dir_stack __ARGS((struct dir_stack_T **));
#ifdef FEAT_WINDOWS
static int	qf_win_pos_update __ARGS((qf_info_T *qi, int old_qf_index));
static int	is_qf_win __ARGS((win_T *win, qf_info_T *qi));
static win_T	*qf_find_win __ARGS((qf_info_T *qi));
static buf_T	*qf_find_buf __ARGS((qf_info_T *qi));
static void	qf_update_buffer __ARGS((qf_info_T *qi));
static void	qf_set_title __ARGS((qf_info_T *qi));
static void	qf_fill_buffer __ARGS((qf_info_T *qi));
#endif
static char_u	*get_mef_name __ARGS((void));
static void	restore_start_dir __ARGS((char_u *dirname_start));
static buf_T	*load_dummy_buffer __ARGS((char_u *fname, char_u *dirname_start, char_u *resulting_dir));
static void	wipe_dummy_buffer __ARGS((buf_T *buf, char_u *dirname_start));
static void	unload_dummy_buffer __ARGS((buf_T *buf, char_u *dirname_start));
static qf_info_T *ll_get_or_alloc_list __ARGS((win_T *));

/* Quickfix window check helper macro */
#define IS_QF_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref == NULL)
/* Location list window check helper macro */
#define IS_LL_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)
/*
 * Return location list for window 'wp'
 * For location list window, return the referenced location list
 */
#define GET_LOC_LIST(wp) (IS_LL_WINDOW(wp) ? wp->w_llist_ref : wp->w_llist)

/*
 * Read the errorfile "efile" into memory, line by line, building the error
 * list. Set the error list's title to qf_title.
 * Return -1 for error, number of errors for success.
 */
    int
qf_init(wp, efile, errorformat, newlist, qf_title)
    win_T	    *wp;
    char_u	    *efile;
    char_u	    *errorformat;
    int		    newlist;		/* TRUE: start a new error list */
    char_u	    *qf_title;
{
    qf_info_T	    *qi = &ql_info;

    if (efile == NULL)
	return FAIL;

    if (wp != NULL)
    {
	qi = ll_get_or_alloc_list(wp);
	if (qi == NULL)
	    return FAIL;
    }

    return qf_init_ext(qi, efile, curbuf, NULL, errorformat, newlist,
						    (linenr_T)0, (linenr_T)0,
						    qf_title);
}

/*
 * Read the errorfile "efile" into memory, line by line, building the error
 * list.
 * Alternative: when "efile" is null read errors from buffer "buf".
 * Always use 'errorformat' from "buf" if there is a local value.
 * Then "lnumfirst" and "lnumlast" specify the range of lines to use.
 * Set the title of the list to "qf_title".
 * Return -1 for error, number of errors for success.
 */
    static int
qf_init_ext(qi, efile, buf, tv, errorformat, newlist, lnumfirst, lnumlast,
								     qf_title)
    qf_info_T	    *qi;
    char_u	    *efile;
    buf_T	    *buf;
    typval_T	    *tv;
    char_u	    *errorformat;
    int		    newlist;		/* TRUE: start a new error list */
    linenr_T	    lnumfirst;		/* first line number to use */
    linenr_T	    lnumlast;		/* last line number to use */
    char_u	    *qf_title;
{
    char_u	    *namebuf;
    char_u	    *errmsg;
    char_u	    *pattern;
    char_u	    *fmtstr = NULL;
    int		    col = 0;
    char_u	    use_viscol = FALSE;
    int		    type = 0;
    int		    valid;
    linenr_T	    buflnum = lnumfirst;
    long	    lnum = 0L;
    int		    enr = 0;
    FILE	    *fd = NULL;
    qfline_T	    *qfprev = NULL;	/* init to make SASC shut up */
    char_u	    *efmp;
    efm_T	    *fmt_first = NULL;
    efm_T	    *fmt_last = NULL;
    efm_T	    *fmt_ptr;
    efm_T	    *fmt_start = NULL;
    char_u	    *efm;
    char_u	    *ptr;
    char_u	    *srcptr;
    int		    len;
    int		    i;
    int		    round;
    int		    idx = 0;
    int		    multiline = FALSE;
    int		    multiignore = FALSE;
    int		    multiscan = FALSE;
    int		    retval = -1;	/* default: return error flag */
    char_u	    *directory = NULL;
    char_u	    *currfile = NULL;
    char_u	    *tail = NULL;
    char_u	    *p_str = NULL;
    listitem_T	    *p_li = NULL;
    struct dir_stack_T  *file_stack = NULL;
    regmatch_T	    regmatch;
    static struct fmtpattern
    {
	char_u	convchar;
	char	*pattern;
    }		    fmt_pat[FMT_PATTERNS] =
		    {
			{'f', ".\\+"},	    /* only used when at end */
			{'n', "\\d\\+"},
			{'l', "\\d\\+"},
			{'c', "\\d\\+"},
			{'t', "."},
			{'m', ".\\+"},
			{'r', ".*"},
			{'p', "[- 	.]*"},
			{'v', "\\d\\+"},
			{'s', ".\\+"}
		    };

    namebuf = alloc(CMDBUFFSIZE + 1);
    errmsg = alloc(CMDBUFFSIZE + 1);
    pattern = alloc(CMDBUFFSIZE + 1);
    if (namebuf == NULL || errmsg == NULL || pattern == NULL)
	goto qf_init_end;

    if (efile != NULL && (fd = mch_fopen((char *)efile, "r")) == NULL)
    {
	EMSG2(_(e_openerrf), efile);
	goto qf_init_end;
    }

    if (newlist || qi->qf_curlist == qi->qf_listcount)
	/* make place for a new list */
	qf_new_list(qi, qf_title);
    else if (qi->qf_lists[qi->qf_curlist].qf_count > 0)
	/* Adding to existing list, find last entry. */
	for (qfprev = qi->qf_lists[qi->qf_curlist].qf_start;
			  qfprev->qf_next != qfprev; qfprev = qfprev->qf_next)
	    ;

/*
 * Each part of the format string is copied and modified from errorformat to
 * regex prog.  Only a few % characters are allowed.
 */
    /* Use the local value of 'errorformat' if it's set. */
    if (errorformat == p_efm && tv == NULL && *buf->b_p_efm != NUL)
	efm = buf->b_p_efm;
    else
	efm = errorformat;
    /*
     * Get some space to modify the format string into.
     */
    i = (FMT_PATTERNS * 3) + ((int)STRLEN(efm) << 2);
    for (round = FMT_PATTERNS; round > 0; )
	i += (int)STRLEN(fmt_pat[--round].pattern);
#ifdef COLON_IN_FILENAME
    i += 12; /* "%f" can become twelve chars longer */
#else
    i += 2; /* "%f" can become two chars longer */
#endif
    if ((fmtstr = alloc(i)) == NULL)
	goto error2;

    while (efm[0] != NUL)
    {
	/*
	 * Allocate a new eformat structure and put it at the end of the list
	 */
	fmt_ptr = (efm_T *)alloc_clear((unsigned)sizeof(efm_T));
	if (fmt_ptr == NULL)
	    goto error2;
	if (fmt_first == NULL)	    /* first one */
	    fmt_first = fmt_ptr;
	else
	    fmt_last->next = fmt_ptr;
	fmt_last = fmt_ptr;

	/*
	 * Isolate one part in the 'errorformat' option
	 */
	for (len = 0; efm[len] != NUL && efm[len] != ','; ++len)
	    if (efm[len] == '\\' && efm[len + 1] != NUL)
		++len;

	/*
	 * Build regexp pattern from current 'errorformat' option
	 */
	ptr = fmtstr;
	*ptr++ = '^';
	round = 0;
	for (efmp = efm; efmp < efm + len; ++efmp)
	{
	    if (*efmp == '%')
	    {
		++efmp;
		for (idx = 0; idx < FMT_PATTERNS; ++idx)
		    if (fmt_pat[idx].convchar == *efmp)
			break;
		if (idx < FMT_PATTERNS)
		{
		    if (fmt_ptr->addr[idx])
		    {
			sprintf((char *)errmsg,
				_("E372: Too many %%%c in format string"), *efmp);
			EMSG(errmsg);
			goto error2;
		    }
		    if ((idx
				&& idx < 6
				&& vim_strchr((char_u *)"DXOPQ",
						     fmt_ptr->prefix) != NULL)
			    || (idx == 6
				&& vim_strchr((char_u *)"OPQ",
						    fmt_ptr->prefix) == NULL))
		    {
			sprintf((char *)errmsg,
				_("E373: Unexpected %%%c in format string"), *efmp);
			EMSG(errmsg);
			goto error2;
		    }
		    fmt_ptr->addr[idx] = (char_u)++round;
		    *ptr++ = '\\';
		    *ptr++ = '(';
#ifdef BACKSLASH_IN_FILENAME
		    if (*efmp == 'f')
		    {
			/* Also match "c:" in the file name, even when
			 * checking for a colon next: "%f:".
			 * "\%(\a:\)\=" */
			STRCPY(ptr, "\\%(\\a:\\)\\=");
			ptr += 10;
		    }
#endif
		    if (*efmp == 'f' && efmp[1] != NUL)
		    {
			if (efmp[1] != '\\' && efmp[1] != '%')
			{
			    /* A file name may contain spaces, but this isn't
			     * in "\f".  For "%f:%l:%m" there may be a ":" in
			     * the file name.  Use ".\{-1,}x" instead (x is
			     * the next character), the requirement that :999:
			     * follows should work. */
			    STRCPY(ptr, ".\\{-1,}");
			    ptr += 7;
			}
			else
			{
			    /* File name followed by '\\' or '%': include as
			     * many file name chars as possible. */
			    STRCPY(ptr, "\\f\\+");
			    ptr += 4;
			}
		    }
		    else
		    {
			srcptr = (char_u *)fmt_pat[idx].pattern;
			while ((*ptr = *srcptr++) != NUL)
			    ++ptr;
		    }
		    *ptr++ = '\\';
		    *ptr++ = ')';
		}
		else if (*efmp == '*')
		{
		    if (*++efmp == '[' || *efmp == '\\')
		    {
			if ((*ptr++ = *efmp) == '[')	/* %*[^a-z0-9] etc. */
			{
			    if (efmp[1] == '^')
				*ptr++ = *++efmp;
			    if (efmp < efm + len)
			    {
				*ptr++ = *++efmp;	    /* could be ']' */
				while (efmp < efm + len
					&& (*ptr++ = *++efmp) != ']')
				    /* skip */;
				if (efmp == efm + len)
				{
				    EMSG(_("E374: Missing ] in format string"));
				    goto error2;
				}
			    }
			}
			else if (efmp < efm + len)	/* %*\D, %*\s etc. */
			    *ptr++ = *++efmp;
			*ptr++ = '\\';
			*ptr++ = '+';
		    }
		    else
		    {
			/* TODO: scanf()-like: %*ud, %*3c, %*f, ... ? */
			sprintf((char *)errmsg,
				_("E375: Unsupported %%%c in format string"), *efmp);
			EMSG(errmsg);
			goto error2;
		    }
		}
		else if (vim_strchr((char_u *)"%\\.^$~[", *efmp) != NULL)
		    *ptr++ = *efmp;		/* regexp magic characters */
		else if (*efmp == '#')
		    *ptr++ = '*';
		else if (*efmp == '>')
		    fmt_ptr->conthere = TRUE;
		else if (efmp == efm + 1)		/* analyse prefix */
		{
		    if (vim_strchr((char_u *)"+-", *efmp) != NULL)
			fmt_ptr->flags = *efmp++;
		    if (vim_strchr((char_u *)"DXAEWICZGOPQ", *efmp) != NULL)
			fmt_ptr->prefix = *efmp;
		    else
		    {
			sprintf((char *)errmsg,
				_("E376: Invalid %%%c in format string prefix"), *efmp);
			EMSG(errmsg);
			goto error2;
		    }
		}
		else
		{
		    sprintf((char *)errmsg,
			    _("E377: Invalid %%%c in format string"), *efmp);
		    EMSG(errmsg);
		    goto error2;
		}
	    }
	    else			/* copy normal character */
	    {
		if (*efmp == '\\' && efmp + 1 < efm + len)
		    ++efmp;
		else if (vim_strchr((char_u *)".*^$~[", *efmp) != NULL)
		    *ptr++ = '\\';	/* escape regexp atoms */
		if (*efmp)
		    *ptr++ = *efmp;
	    }
	}
	*ptr++ = '$';
	*ptr = NUL;
	if ((fmt_ptr->prog = vim_regcomp(fmtstr, RE_MAGIC + RE_STRING)) == NULL)
	    goto error2;
	/*
	 * Advance to next part
	 */
	efm = skip_to_option_part(efm + len);	/* skip comma and spaces */
    }
    if (fmt_first == NULL)	/* nothing found */
    {
	EMSG(_("E378: 'errorformat' contains no pattern"));
	goto error2;
    }

    /*
     * got_int is reset here, because it was probably set when killing the
     * ":make" command, but we still want to read the errorfile then.
     */
    got_int = FALSE;

    /* Always ignore case when looking for a matching error. */
    regmatch.rm_ic = TRUE;

    if (tv != NULL)
    {
	if (tv->v_type == VAR_STRING)
	    p_str = tv->vval.v_string;
	else if (tv->v_type == VAR_LIST)
	    p_li = tv->vval.v_list->lv_first;
    }

    /*
     * Read the lines in the error file one by one.
     * Try to recognize one of the error formats in each line.
     */
    while (!got_int)
    {
	/* Get the next line. */
	if (fd == NULL)
	{
	    if (tv != NULL)
	    {
		if (tv->v_type == VAR_STRING)
		{
		    /* Get the next line from the supplied string */
		    char_u *p;

		    if (!*p_str) /* Reached the end of the string */
			break;

		    p = vim_strchr(p_str, '\n');
		    if (p)
			len = (int)(p - p_str + 1);
		    else
			len = (int)STRLEN(p_str);

		    if (len > CMDBUFFSIZE - 2)
			vim_strncpy(IObuff, p_str, CMDBUFFSIZE - 2);
		    else
			vim_strncpy(IObuff, p_str, len);

		    p_str += len;
		}
		else if (tv->v_type == VAR_LIST)
		{
		    /* Get the next line from the supplied list */
		    while (p_li && p_li->li_tv.v_type != VAR_STRING)
			p_li = p_li->li_next;	/* Skip non-string items */

		    if (!p_li)			/* End of the list */
			break;

		    len = (int)STRLEN(p_li->li_tv.vval.v_string);
		    if (len > CMDBUFFSIZE - 2)
			len = CMDBUFFSIZE - 2;

		    vim_strncpy(IObuff, p_li->li_tv.vval.v_string, len);

		    p_li = p_li->li_next;	/* next item */
		}
	    }
	    else
	    {
		/* Get the next line from the supplied buffer */
		if (buflnum > lnumlast)
		    break;
		vim_strncpy(IObuff, ml_get_buf(buf, buflnum++, FALSE),
			    CMDBUFFSIZE - 2);
	    }
	}
	else if (fgets((char *)IObuff, CMDBUFFSIZE - 2, fd) == NULL)
	    break;

	IObuff[CMDBUFFSIZE - 2] = NUL;  /* for very long lines */
#ifdef FEAT_MBYTE
	remove_bom(IObuff);
#endif

	if ((efmp = vim_strrchr(IObuff, '\n')) != NULL)
	    *efmp = NUL;
#ifdef USE_CRNL
	if ((efmp = vim_strrchr(IObuff, '\r')) != NULL)
	    *efmp = NUL;
#endif

	/* If there was no %> item start at the first pattern */
	if (fmt_start == NULL)
	    fmt_ptr = fmt_first;
	else
	{
	    fmt_ptr = fmt_start;
	    fmt_start = NULL;
	}

	/*
	 * Try to match each part of 'errorformat' until we find a complete
	 * match or no match.
	 */
	valid = TRUE;
restofline:
	for ( ; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next)
	{
	    idx = fmt_ptr->prefix;
	    if (multiscan && vim_strchr((char_u *)"OPQ", idx) == NULL)
		continue;
	    namebuf[0] = NUL;
	    pattern[0] = NUL;
	    if (!multiscan)
		errmsg[0] = NUL;
	    lnum = 0;
	    col = 0;
	    use_viscol = FALSE;
	    enr = -1;
	    type = 0;
	    tail = NULL;

	    regmatch.regprog = fmt_ptr->prog;
	    if (vim_regexec(&regmatch, IObuff, (colnr_T)0))
	    {
		if ((idx == 'C' || idx == 'Z') && !multiline)
		    continue;
		if (vim_strchr((char_u *)"EWI", idx) != NULL)
		    type = idx;
		else
		    type = 0;
		/*
		 * Extract error message data from matched line.
		 * We check for an actual submatch, because "\[" and "\]" in
		 * the 'errorformat' may cause the wrong submatch to be used.
		 */
		if ((i = (int)fmt_ptr->addr[0]) > 0)		/* %f */
		{
		    int c;

		    if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL)
			continue;

		    /* Expand ~/file and $HOME/file to full path. */
		    c = *regmatch.endp[i];
		    *regmatch.endp[i] = NUL;
		    expand_env(regmatch.startp[i], namebuf, CMDBUFFSIZE);
		    *regmatch.endp[i] = c;

		    if (vim_strchr((char_u *)"OPQ", idx) != NULL
						&& mch_getperm(namebuf) == -1)
			continue;
		}
		if ((i = (int)fmt_ptr->addr[1]) > 0)		/* %n */
		{
		    if (regmatch.startp[i] == NULL)
			continue;
		    enr = (int)atol((char *)regmatch.startp[i]);
		}
		if ((i = (int)fmt_ptr->addr[2]) > 0)		/* %l */
		{
		    if (regmatch.startp[i] == NULL)
			continue;
		    lnum = atol((char *)regmatch.startp[i]);
		}
		if ((i = (int)fmt_ptr->addr[3]) > 0)		/* %c */
		{
		    if (regmatch.startp[i] == NULL)
			continue;
		    col = (int)atol((char *)regmatch.startp[i]);
		}
		if ((i = (int)fmt_ptr->addr[4]) > 0)		/* %t */
		{
		    if (regmatch.startp[i] == NULL)
			continue;
		    type = *regmatch.startp[i];
		}
		if (fmt_ptr->flags == '+' && !multiscan)	/* %+ */
		    STRCPY(errmsg, IObuff);
		else if ((i = (int)fmt_ptr->addr[5]) > 0)	/* %m */
		{
		    if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL)
			continue;
		    len = (int)(regmatch.endp[i] - regmatch.startp[i]);
		    vim_strncpy(errmsg, regmatch.startp[i], len);
		}
		if ((i = (int)fmt_ptr->addr[6]) > 0)		/* %r */
		{
		    if (regmatch.startp[i] == NULL)
			continue;
		    tail = regmatch.startp[i];
		}
		if ((i = (int)fmt_ptr->addr[7]) > 0)		/* %p */
		{
		    char_u	*match_ptr;

		    if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL)
			continue;
		    col = 0;
		    for (match_ptr = regmatch.startp[i];
				   match_ptr != regmatch.endp[i]; ++match_ptr)
		    {
			++col;
			if (*match_ptr == TAB)
			{
			    col += 7;
			    col -= col % 8;
			}
		    }
		    ++col;
		    use_viscol = TRUE;
		}
		if ((i = (int)fmt_ptr->addr[8]) > 0)		/* %v */
		{
		    if (regmatch.startp[i] == NULL)
			continue;
		    col = (int)atol((char *)regmatch.startp[i]);
		    use_viscol = TRUE;
		}
		if ((i = (int)fmt_ptr->addr[9]) > 0)		/* %s */
		{
		    if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL)
			continue;
		    len = (int)(regmatch.endp[i] - regmatch.startp[i]);
		    if (len > CMDBUFFSIZE - 5)
			len = CMDBUFFSIZE - 5;
		    STRCPY(pattern, "^\\V");
		    STRNCAT(pattern, regmatch.startp[i], len);
		    pattern[len + 3] = '\\';
		    pattern[len + 4] = '$';
		    pattern[len + 5] = NUL;
		}
		break;
	    }
	}
	multiscan = FALSE;

	if (fmt_ptr == NULL || idx == 'D' || idx == 'X')
	{
	    if (fmt_ptr != NULL)
	    {
		if (idx == 'D')				/* enter directory */
		{
		    if (*namebuf == NUL)
		    {
			EMSG(_("E379: Missing or empty directory name"));
			goto error2;
		    }
		    if ((directory = qf_push_dir(namebuf, &dir_stack)) == NULL)
			goto error2;
		}
		else if (idx == 'X')			/* leave directory */
		    directory = qf_pop_dir(&dir_stack);
	    }
	    namebuf[0] = NUL;		/* no match found, remove file name */
	    lnum = 0;			/* don't jump to this line */
	    valid = FALSE;
	    STRCPY(errmsg, IObuff);	/* copy whole line to error message */
	    if (fmt_ptr == NULL)
		multiline = multiignore = FALSE;
	}
	else if (fmt_ptr != NULL)
	{
	    /* honor %> item */
	    if (fmt_ptr->conthere)
		fmt_start = fmt_ptr;

	    if (vim_strchr((char_u *)"AEWI", idx) != NULL)
		multiline = TRUE;	/* start of a multi-line message */
	    else if (vim_strchr((char_u *)"CZ", idx) != NULL)
	    {				/* continuation of multi-line msg */
		if (qfprev == NULL)
		    goto error2;
		if (*errmsg && !multiignore)
		{
		    len = (int)STRLEN(qfprev->qf_text);
		    if ((ptr = alloc((unsigned)(len + STRLEN(errmsg) + 2)))
								    == NULL)
			goto error2;
		    STRCPY(ptr, qfprev->qf_text);
		    vim_free(qfprev->qf_text);
		    qfprev->qf_text = ptr;
		    *(ptr += len) = '\n';
		    STRCPY(++ptr, errmsg);
		}
		if (qfprev->qf_nr == -1)
		    qfprev->qf_nr = enr;
		if (vim_isprintc(type) && !qfprev->qf_type)
		    qfprev->qf_type = type;  /* only printable chars allowed */
		if (!qfprev->qf_lnum)
		    qfprev->qf_lnum = lnum;
		if (!qfprev->qf_col)
		    qfprev->qf_col = col;
		qfprev->qf_viscol = use_viscol;
		if (!qfprev->qf_fnum)
		    qfprev->qf_fnum = qf_get_fnum(directory,
					*namebuf || directory ? namebuf
					  : currfile && valid ? currfile : 0);
		if (idx == 'Z')
		    multiline = multiignore = FALSE;
		line_breakcheck();
		continue;
	    }
	    else if (vim_strchr((char_u *)"OPQ", idx) != NULL)
	    {
		/* global file names */
		valid = FALSE;
		if (*namebuf == NUL || mch_getperm(namebuf) >= 0)
		{
		    if (*namebuf && idx == 'P')
			currfile = qf_push_dir(namebuf, &file_stack);
		    else if (idx == 'Q')
			currfile = qf_pop_dir(&file_stack);
		    *namebuf = NUL;
		    if (tail && *tail)
		    {
			STRMOVE(IObuff, skipwhite(tail));
			multiscan = TRUE;
			goto restofline;
		    }
		}
	    }
	    if (fmt_ptr->flags == '-')	/* generally exclude this line */
	    {
		if (multiline)
		    multiignore = TRUE;	/* also exclude continuation lines */
		continue;
	    }
	}

	if (qf_add_entry(qi, &qfprev,
			directory,
			(*namebuf || directory)
			    ? namebuf
			    : ((currfile && valid) ? currfile : (char_u *)NULL),
			0,
			errmsg,
			lnum,
			col,
			use_viscol,
			pattern,
			enr,
			type,
			valid) == FAIL)
	    goto error2;
	line_breakcheck();
    }
    if (fd == NULL || !ferror(fd))
    {
	if (qi->qf_lists[qi->qf_curlist].qf_index == 0)
	{
	    /* no valid entry found */
	    qi->qf_lists[qi->qf_curlist].qf_ptr =
		qi->qf_lists[qi->qf_curlist].qf_start;
	    qi->qf_lists[qi->qf_curlist].qf_index = 1;
	    qi->qf_lists[qi->qf_curlist].qf_nonevalid = TRUE;
	}
	else
	{
	    qi->qf_lists[qi->qf_curlist].qf_nonevalid = FALSE;
	    if (qi->qf_lists[qi->qf_curlist].qf_ptr == NULL)
		qi->qf_lists[qi->qf_curlist].qf_ptr =
		    qi->qf_lists[qi->qf_curlist].qf_start;
	}
	/* return number of matches */
	retval = qi->qf_lists[qi->qf_curlist].qf_count;
	goto qf_init_ok;
    }
    EMSG(_(e_readerrf));
error2:
    qf_free(qi, qi->qf_curlist);
    qi->qf_listcount--;
    if (qi->qf_curlist > 0)
	--qi->qf_curlist;
qf_init_ok:
    if (fd != NULL)
	fclose(fd);
    for (fmt_ptr = fmt_first; fmt_ptr != NULL; fmt_ptr = fmt_first)
    {
	fmt_first = fmt_ptr->next;
	vim_free(fmt_ptr->prog);
	vim_free(fmt_ptr);
    }
    qf_clean_dir_stack(&dir_stack);
    qf_clean_dir_stack(&file_stack);
qf_init_end:
    vim_free(namebuf);
    vim_free(errmsg);
    vim_free(pattern);
    vim_free(fmtstr);

#ifdef FEAT_WINDOWS
    qf_update_buffer(qi);
#endif

    return retval;
}

/*
 * Prepare for adding a new quickfix list.
 */
    static void
qf_new_list(qi, qf_title)
    qf_info_T	*qi;
    char_u	*qf_title;
{
    int		i;

    /*
     * If the current entry is not the last entry, delete entries below
     * the current entry.  This makes it possible to browse in a tree-like
     * way with ":grep'.
     */
    while (qi->qf_listcount > qi->qf_curlist + 1)
	qf_free(qi, --qi->qf_listcount);

    /*
     * When the stack is full, remove to oldest entry
     * Otherwise, add a new entry.
     */
    if (qi->qf_listcount == LISTCOUNT)
    {
	qf_free(qi, 0);
	for (i = 1; i < LISTCOUNT; ++i)
	    qi->qf_lists[i - 1] = qi->qf_lists[i];
	qi->qf_curlist = LISTCOUNT - 1;
    }
    else
	qi->qf_curlist = qi->qf_listcount++;
    vim_memset(&qi->qf_lists[qi->qf_curlist], 0, (size_t)(sizeof(qf_list_T)));
    if (qf_title != NULL)
    {
	char_u *p = alloc((int)STRLEN(qf_title) + 2);

	qi->qf_lists[qi->qf_curlist].qf_title = p;
	if (p != NULL)
	    sprintf((char *)p, ":%s", (char *)qf_title);
    }
}

/*
 * Free a location list
 */
    static void
ll_free_all(pqi)
    qf_info_T	**pqi;
{
    int		i;
    qf_info_T	*qi;

    qi = *pqi;
    if (qi == NULL)
	return;
    *pqi = NULL;	/* Remove reference to this list */

    qi->qf_refcount--;
    if (qi->qf_refcount < 1)
    {
	/* No references to this location list */
	for (i = 0; i < qi->qf_listcount; ++i)
	    qf_free(qi, i);
	vim_free(qi);
    }
}

    void
qf_free_all(wp)
    win_T	*wp;
{
    int		i;
    qf_info_T	*qi = &ql_info;

    if (wp != NULL)
    {
	/* location list */
	ll_free_all(&wp->w_llist);
	ll_free_all(&wp->w_llist_ref);
    }
    else
	/* quickfix list */
	for (i = 0; i < qi->qf_listcount; ++i)
	    qf_free(qi, i);
}

/*
 * Add an entry to the end of the list of errors.
 * Returns OK or FAIL.
 */
    static int
qf_add_entry(qi, prevp, dir, fname, bufnum, mesg, lnum, col, vis_col, pattern,
	     nr, type, valid)
    qf_info_T	*qi;		/* quickfix list */
    qfline_T	**prevp;	/* pointer to previously added entry or NULL */
    char_u	*dir;		/* optional directory name */
    char_u	*fname;		/* file name or NULL */
    int		bufnum;		/* buffer number or zero */
    char_u	*mesg;		/* message */
    long	lnum;		/* line number */
    int		col;		/* column */
    int		vis_col;	/* using visual column */
    char_u	*pattern;	/* search pattern */
    int		nr;		/* error number */
    int		type;		/* type character */
    int		valid;		/* valid entry */
{
    qfline_T	*qfp;

    if ((qfp = (qfline_T *)alloc((unsigned)sizeof(qfline_T))) == NULL)
	return FAIL;
    if (bufnum != 0)
	qfp->qf_fnum = bufnum;
    else
	qfp->qf_fnum = qf_get_fnum(dir, fname);
    if ((qfp->qf_text = vim_strsave(mesg)) == NULL)
    {
	vim_free(qfp);
	return FAIL;
    }
    qfp->qf_lnum = lnum;
    qfp->qf_col = col;
    qfp->qf_viscol = vis_col;
    if (pattern == NULL || *pattern == NUL)
	qfp->qf_pattern = NULL;
    else if ((qfp->qf_pattern = vim_strsave(pattern)) == NULL)
    {
	vim_free(qfp->qf_text);
	vim_free(qfp);
	return FAIL;
    }
    qfp->qf_nr = nr;
    if (type != 1 && !vim_isprintc(type)) /* only printable chars allowed */
	type = 0;
    qfp->qf_type = type;
    qfp->qf_valid = valid;

    if (qi->qf_lists[qi->qf_curlist].qf_count == 0)
				/* first element in the list */
    {
	qi->qf_lists[qi->qf_curlist].qf_start = qfp;
	qfp->qf_prev = qfp;	/* first element points to itself */
    }
    else
    {
	qfp->qf_prev = *prevp;
	(*prevp)->qf_next = qfp;
    }
    qfp->qf_next = qfp;	/* last element points to itself */
    qfp->qf_cleared = FALSE;
    *prevp = qfp;
    ++qi->qf_lists[qi->qf_curlist].qf_count;
    if (qi->qf_lists[qi->qf_curlist].qf_index == 0 && qfp->qf_valid)
				/* first valid entry */
    {
	qi->qf_lists[qi->qf_curlist].qf_index =
	    qi->qf_lists[qi->qf_curlist].qf_count;
	qi->qf_lists[qi->qf_curlist].qf_ptr = qfp;
    }

    return OK;
}

/*
 * Allocate a new location list
 */
    static qf_info_T *
ll_new_list()
{
    qf_info_T *qi;

    qi = (qf_info_T *)alloc((unsigned)sizeof(qf_info_T));
    if (qi != NULL)
    {
	vim_memset(qi, 0, (size_t)(sizeof(qf_info_T)));
	qi->qf_refcount++;
    }

    return qi;
}

/*
 * Return the location list for window 'wp'.
 * If not present, allocate a location list
 */
    static qf_info_T *
ll_get_or_alloc_list(wp)
    win_T   *wp;
{
    if (IS_LL_WINDOW(wp))
	/* For a location list window, use the referenced location list */
	return wp->w_llist_ref;

    /*
     * For a non-location list window, w_llist_ref should not point to a
     * location list.
     */
    ll_free_all(&wp->w_llist_ref);

    if (wp->w_llist == NULL)
	wp->w_llist = ll_new_list();	    /* new location list */
    return wp->w_llist;
}

/*
 * Copy the location list from window "from" to window "to".
 */
    void
copy_loclist(from, to)
    win_T	*from;
    win_T	*to;
{
    qf_info_T	*qi;
    int		idx;
    int		i;

    /*
     * When copying from a location list window, copy the referenced
     * location list. For other windows, copy the location list for
     * that window.
     */
    if (IS_LL_WINDOW(from))
	qi = from->w_llist_ref;
    else
	qi = from->w_llist;

    if (qi == NULL)		    /* no location list to copy */
	return;

    /* allocate a new location list */
    if ((to->w_llist = ll_new_list()) == NULL)
	return;

    to->w_llist->qf_listcount = qi->qf_listcount;

    /* Copy the location lists one at a time */
    for (idx = 0; idx < qi->qf_listcount; idx++)
    {
	qf_list_T   *from_qfl;
	qf_list_T   *to_qfl;

	to->w_llist->qf_curlist = idx;

	from_qfl = &qi->qf_lists[idx];
	to_qfl = &to->w_llist->qf_lists[idx];

	/* Some of the fields are populated by qf_add_entry() */
	to_qfl->qf_nonevalid = from_qfl->qf_nonevalid;
	to_qfl->qf_count = 0;
	to_qfl->qf_index = 0;
	to_qfl->qf_start = NULL;
	to_qfl->qf_ptr = NULL;
	if (from_qfl->qf_title != NULL)
	    to_qfl->qf_title = vim_strsave(from_qfl->qf_title);
	else
	    to_qfl->qf_title = NULL;

	if (from_qfl->qf_count)
	{
	    qfline_T    *from_qfp;
	    qfline_T    *prevp = NULL;

	    /* copy all the location entries in this list */
	    for (i = 0, from_qfp = from_qfl->qf_start; i < from_qfl->qf_count;
		 ++i, from_qfp = from_qfp->qf_next)
	    {
		if (qf_add_entry(to->w_llist, &prevp,
				 NULL,
				 NULL,
				 0,
				 from_qfp->qf_text,
				 from_qfp->qf_lnum,
				 from_qfp->qf_col,
				 from_qfp->qf_viscol,
				 from_qfp->qf_pattern,
				 from_qfp->qf_nr,
				 0,
				 from_qfp->qf_valid) == FAIL)
		{
		    qf_free_all(to);
		    return;
		}
		/*
		 * qf_add_entry() will not set the qf_num field, as the
		 * directory and file names are not supplied. So the qf_fnum
		 * field is copied here.
		 */
		prevp->qf_fnum = from_qfp->qf_fnum; /* file number */
		prevp->qf_type = from_qfp->qf_type; /* error type */
		if (from_qfl->qf_ptr == from_qfp)
		    to_qfl->qf_ptr = prevp;	    /* current location */
	    }
	}

	to_qfl->qf_index = from_qfl->qf_index;	/* current index in the list */

	/* When no valid entries are present in the list, qf_ptr points to
	 * the first item in the list */
	if (to_qfl->qf_nonevalid)
	    to_qfl->qf_ptr = to_qfl->qf_start;
    }

    to->w_llist->qf_curlist = qi->qf_curlist;	/* current list */
}

/*
 * get buffer number for file "dir.name"
 */
    static int
qf_get_fnum(directory, fname)
    char_u   *directory;
    char_u   *fname;
{
    if (fname == NULL || *fname == NUL)		/* no file name */
	return 0;
    {
	char_u	    *ptr;
	int	    fnum;

#ifdef VMS
	vms_remove_version(fname);
#endif
#ifdef BACKSLASH_IN_FILENAME
	if (directory != NULL)
	    slash_adjust(directory);
	slash_adjust(fname);
#endif
	if (directory != NULL && !vim_isAbsName(fname)
		&& (ptr = concat_fnames(directory, fname, TRUE)) != NULL)
	{
	    /*
	     * Here we check if the file really exists.
	     * This should normally be true, but if make works without
	     * "leaving directory"-messages we might have missed a
	     * directory change.
	     */
	    if (mch_getperm(ptr) < 0)
	    {
		vim_free(ptr);
		directory = qf_guess_filepath(fname);
		if (directory)
		    ptr = concat_fnames(directory, fname, TRUE);
		else
		    ptr = vim_strsave(fname);
	    }
	    /* Use concatenated directory name and file name */
	    fnum = buflist_add(ptr, 0);
	    vim_free(ptr);
	    return fnum;
	}
	return buflist_add(fname, 0);
    }
}

/*
 * push dirbuf onto the directory stack and return pointer to actual dir or
 * NULL on error
 */
    static char_u *
qf_push_dir(dirbuf, stackptr)
    char_u		*dirbuf;
    struct dir_stack_T	**stackptr;
{
    struct dir_stack_T  *ds_new;
    struct dir_stack_T  *ds_ptr;

    /* allocate new stack element and hook it in */
    ds_new = (struct dir_stack_T *)alloc((unsigned)sizeof(struct dir_stack_T));
    if (ds_new == NULL)
	return NULL;

    ds_new->next = *stackptr;
    *stackptr = ds_new;

    /* store directory on the stack */
    if (vim_isAbsName(dirbuf)
	    || (*stackptr)->next == NULL
	    || (*stackptr && dir_stack != *stackptr))
	(*stackptr)->dirname = vim_strsave(dirbuf);
    else
    {
	/* Okay we don't have an absolute path.
	 * dirbuf must be a subdir of one of the directories on the stack.
	 * Let's search...
	 */
	ds_new = (*stackptr)->next;
	(*stackptr)->dirname = NULL;
	while (ds_new)
	{
	    vim_free((*stackptr)->dirname);
	    (*stackptr)->dirname = concat_fnames(ds_new->dirname, dirbuf,
		    TRUE);
	    if (mch_isdir((*stackptr)->dirname) == TRUE)
		break;

	    ds_new = ds_new->next;
	}

	/* clean up all dirs we already left */
	while ((*stackptr)->next != ds_new)
	{
	    ds_ptr = (*stackptr)->next;
	    (*stackptr)->next = (*stackptr)->next->next;
	    vim_free(ds_ptr->dirname);
	    vim_free(ds_ptr);
	}

	/* Nothing found -> it must be on top level */
	if (ds_new == NULL)
	{
	    vim_free((*stackptr)->dirname);
	    (*stackptr)->dirname = vim_strsave(dirbuf);
	}
    }

    if ((*stackptr)->dirname != NULL)
	return (*stackptr)->dirname;
    else
    {
	ds_ptr = *stackptr;
	*stackptr = (*stackptr)->next;
	vim_free(ds_ptr);
	return NULL;
    }
}


/*
 * pop dirbuf from the directory stack and return previous directory or NULL if
 * stack is empty
 */
    static char_u *
qf_pop_dir(stackptr)
    struct dir_stack_T	**stackptr;
{
    struct dir_stack_T  *ds_ptr;

    /* TODO: Should we check if dirbuf is the directory on top of the stack?
     * What to do if it isn't? */

    /* pop top element and free it */
    if (*stackptr != NULL)
    {
	ds_ptr = *stackptr;
	*stackptr = (*stackptr)->next;
	vim_free(ds_ptr->dirname);
	vim_free(ds_ptr);
    }

    /* return NEW top element as current dir or NULL if stack is empty*/
    return *stackptr ? (*stackptr)->dirname : NULL;
}

/*
 * clean up directory stack
 */
    static void
qf_clean_dir_stack(stackptr)
    struct dir_stack_T	**stackptr;
{
    struct dir_stack_T  *ds_ptr;

    while ((ds_ptr = *stackptr) != NULL)
    {
	*stackptr = (*stackptr)->next;
	vim_free(ds_ptr->dirname);
	vim_free(ds_ptr);
    }
}

/*
 * Check in which directory of the directory stack the given file can be
 * found.
 * Returns a pointer to the directory name or NULL if not found
 * Cleans up intermediate directory entries.
 *
 * TODO: How to solve the following problem?
 * If we have the this directory tree:
 *     ./
 *     ./aa
 *     ./aa/bb
 *     ./bb
 *     ./bb/x.c
 * and make says:
 *     making all in aa
 *     making all in bb
 *     x.c:9: Error
 * Then qf_push_dir thinks we are in ./aa/bb, but we are in ./bb.
 * qf_guess_filepath will return NULL.
 */
    static char_u *
qf_guess_filepath(filename)
    char_u *filename;
{
    struct dir_stack_T     *ds_ptr;
    struct dir_stack_T     *ds_tmp;
    char_u		   *fullname;

    /* no dirs on the stack - there's nothing we can do */
    if (dir_stack == NULL)
	return NULL;

    ds_ptr = dir_stack->next;
    fullname = NULL;
    while (ds_ptr)
    {
	vim_free(fullname);
	fullname = concat_fnames(ds_ptr->dirname, filename, TRUE);

	/* If concat_fnames failed, just go on. The worst thing that can happen
	 * is that we delete the entire stack.
	 */
	if ((fullname != NULL) && (mch_getperm(fullname) >= 0))
	    break;

	ds_ptr = ds_ptr->next;
    }

    vim_free(fullname);

    /* clean up all dirs we already left */
    while (dir_stack->next != ds_ptr)
    {
	ds_tmp = dir_stack->next;
	dir_stack->next = dir_stack->next->next;
	vim_free(ds_tmp->dirname);
	vim_free(ds_tmp);
    }

    return ds_ptr==NULL? NULL: ds_ptr->dirname;

}

/*
 * jump to a quickfix line
 * if dir == FORWARD go "errornr" valid entries forward
 * if dir == BACKWARD go "errornr" valid entries backward
 * if dir == FORWARD_FILE go "errornr" valid entries files backward
 * if dir == BACKWARD_FILE go "errornr" valid entries files backward
 * else if "errornr" is zero, redisplay the same line
 * else go to entry "errornr"
 */
    void
qf_jump(qi, dir, errornr, forceit)
    qf_info_T	*qi;
    int		dir;
    int		errornr;
    int		forceit;
{
    qf_info_T		*ll_ref;
    qfline_T		*qf_ptr;
    qfline_T		*old_qf_ptr;
    int			qf_index;
    int			old_qf_fnum;
    int			old_qf_index;
    int			prev_index;
    static char_u	*e_no_more_items = (char_u *)N_("E553: No more items");
    char_u		*err = e_no_more_items;
    linenr_T		i;
    buf_T		*old_curbuf;
    linenr_T		old_lnum;
    colnr_T		screen_col;
    colnr_T		char_col;
    char_u		*line;
#ifdef FEAT_WINDOWS
    char_u		*old_swb = p_swb;
    unsigned		old_swb_flags = swb_flags;
    int			opened_window = FALSE;
    win_T		*win;
    win_T		*altwin;
    int			flags;
#endif
    win_T		*oldwin = curwin;
    int			print_message = TRUE;
    int			len;
#ifdef FEAT_FOLDING
    int			old_KeyTyped = KeyTyped; /* getting file may reset it */
#endif
    int			ok = OK;
    int			usable_win;

    if (qi == NULL)
	qi = &ql_info;

    if (qi->qf_curlist >= qi->qf_listcount
	|| qi->qf_lists[qi->qf_curlist].qf_count == 0)
    {
	EMSG(_(e_quickfix));
	return;
    }

    qf_ptr = qi->qf_lists[qi->qf_curlist].qf_ptr;
    old_qf_ptr = qf_ptr;
    qf_index = qi->qf_lists[qi->qf_curlist].qf_index;
    old_qf_index = qf_index;
    if (dir == FORWARD || dir == FORWARD_FILE)	    /* next valid entry */
    {
	while (errornr--)
	{
	    old_qf_ptr = qf_ptr;
	    prev_index = qf_index;
	    old_qf_fnum = qf_ptr->qf_fnum;
	    do
	    {
		if (qf_index == qi->qf_lists[qi->qf_curlist].qf_count
						   || qf_ptr->qf_next == NULL)
		{
		    qf_ptr = old_qf_ptr;
		    qf_index = prev_index;
		    if (err != NULL)
		    {
			EMSG(_(err));
			goto theend;
		    }
		    errornr = 0;
		    break;
		}
		++qf_index;
		qf_ptr = qf_ptr->qf_next;
	    } while ((!qi->qf_lists[qi->qf_curlist].qf_nonevalid
		      && !qf_ptr->qf_valid)
		  || (dir == FORWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum));
	    err = NULL;
	}
    }
    else if (dir == BACKWARD || dir == BACKWARD_FILE)  /* prev. valid entry */
    {
	while (errornr--)
	{
	    old_qf_ptr = qf_ptr;
	    prev_index = qf_index;
	    old_qf_fnum = qf_ptr->qf_fnum;
	    do
	    {
		if (qf_index == 1 || qf_ptr->qf_prev == NULL)
		{
		    qf_ptr = old_qf_ptr;
		    qf_index = prev_index;
		    if (err != NULL)
		    {
			EMSG(_(err));
			goto theend;
		    }
		    errornr = 0;
		    break;
		}
		--qf_index;
		qf_ptr = qf_ptr->qf_prev;
	    } while ((!qi->qf_lists[qi->qf_curlist].qf_nonevalid
		      && !qf_ptr->qf_valid)
		  || (dir == BACKWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum));
	    err = NULL;
	}
    }
    else if (errornr != 0)	/* go to specified number */
    {
	while (errornr < qf_index && qf_index > 1 && qf_ptr->qf_prev != NULL)
	{
	    --qf_index;
	    qf_ptr = qf_ptr->qf_prev;
	}
	while (errornr > qf_index && qf_index <
				    qi->qf_lists[qi->qf_curlist].qf_count
						   && qf_ptr->qf_next != NULL)
	{
	    ++qf_index;
	    qf_ptr = qf_ptr->qf_next;
	}
    }

#ifdef FEAT_WINDOWS
    qi->qf_lists[qi->qf_curlist].qf_index = qf_index;
    if (qf_win_pos_update(qi, old_qf_index))
	/* No need to print the error message if it's visible in the error
	 * window */
	print_message = FALSE;

    /*
     * For ":helpgrep" find a help window or open one.
     */
    if (qf_ptr->qf_type == 1 && (!curwin->w_buffer->b_help || cmdmod.tab != 0))
    {
	win_T	*wp;

	if (cmdmod.tab != 0)
	    wp = NULL;
	else
	    for (wp = firstwin; wp != NULL; wp = wp->w_next)
		if (wp->w_buffer != NULL && wp->w_buffer->b_help)
		    break;
	if (wp != NULL && wp->w_buffer->b_nwindows > 0)
	    win_enter(wp, TRUE);
	else
	{
	    /*
	     * Split off help window; put it at far top if no position
	     * specified, the current window is vertically split and narrow.
	     */
	    flags = WSP_HELP;
# ifdef FEAT_VERTSPLIT
	    if (cmdmod.split == 0 && curwin->w_width != Columns
						      && curwin->w_width < 80)
		flags |= WSP_TOP;
# endif
	    if (qi != &ql_info)
		flags |= WSP_NEWLOC;  /* don't copy the location list */

	    if (win_split(0, flags) == FAIL)
		goto theend;
	    opened_window = TRUE;	/* close it when fail */

	    if (curwin->w_height < p_hh)
		win_setheight((int)p_hh);

	    if (qi != &ql_info)	    /* not a quickfix list */
	    {
		/* The new window should use the supplied location list */
		curwin->w_llist = qi;
		qi->qf_refcount++;
	    }
	}

	if (!p_im)
	    restart_edit = 0;	    /* don't want insert mode in help file */
    }

    /*
     * If currently in the quickfix window, find another window to show the
     * file in.
     */
    if (bt_quickfix(curbuf) && !opened_window)
    {
	/*
	 * If there is no file specified, we don't know where to go.
	 * But do advance, otherwise ":cn" gets stuck.
	 */
	if (qf_ptr->qf_fnum == 0)
	    goto theend;

	/* Locate a window showing a normal buffer */
	usable_win = 0;
	FOR_ALL_WINDOWS(win)
	    if (win->w_buffer->b_p_bt[0] == NUL)
	    {
		usable_win = 1;
		break;
	    }

	/*
	 * If no usable window is found and 'switchbuf' contains "usetab"
	 * then search in other tabs.
	 */
	if (!usable_win && (swb_flags & SWB_USETAB))
	{
	    tabpage_T	*tp;
	    win_T	*wp;

	    FOR_ALL_TAB_WINDOWS(tp, wp)
	    {
		if (wp->w_buffer->b_fnum == qf_ptr->qf_fnum)
		{
		    goto_tabpage_win(tp, wp);
		    usable_win = 1;
		    goto win_found;
		}
	    }
	}
win_found:

	/*
	 * If there is only one window and it is the quickfix window, create a
	 * new one above the quickfix window.
	 */
	if (((firstwin == lastwin) && bt_quickfix(curbuf)) || !usable_win)
	{
	    ll_ref = curwin->w_llist_ref;

	    flags = WSP_ABOVE;
	    if (ll_ref != NULL)
		flags |= WSP_NEWLOC;
	    if (win_split(0, flags) == FAIL)
		goto failed;		/* not enough room for window */
	    opened_window = TRUE;	/* close it when fail */
	    p_swb = empty_option;	/* don't split again */
	    swb_flags = 0;
	    RESET_BINDING(curwin);
	    if (ll_ref != NULL)
	    {
		/* The new window should use the location list from the
		 * location list window */
		curwin->w_llist = ll_ref;
		ll_ref->qf_refcount++;
	    }
	}
	else
	{
	    if (curwin->w_llist_ref != NULL)
	    {
		/* In a location window */
		ll_ref = curwin->w_llist_ref;

		/* Find the window with the same location list */
		FOR_ALL_WINDOWS(win)
		    if (win->w_llist == ll_ref)
			break;
		if (win == NULL)
		{
		    /* Find the window showing the selected file */
		    FOR_ALL_WINDOWS(win)
			if (win->w_buffer->b_fnum == qf_ptr->qf_fnum)
			    break;
		    if (win == NULL)
		    {
			/* Find a previous usable window */
			win = curwin;
			do
			{
			    if (win->w_buffer->b_p_bt[0] == NUL)
				break;
			    if (win->w_prev == NULL)
				win = lastwin;	/* wrap around the top */
			    else
				win = win->w_prev; /* go to previous window */
			} while (win != curwin);
		    }
		}
		win_goto(win);

		/* If the location list for the window is not set, then set it
		 * to the location list from the location window */
		if (win->w_llist == NULL)
		{
		    win->w_llist = ll_ref;
		    ll_ref->qf_refcount++;
		}
	    }
	    else
	    {

	    /*
	     * Try to find a window that shows the right buffer.
	     * Default to the window just above the quickfix buffer.
	     */
	    win = curwin;
	    altwin = NULL;
	    for (;;)
	    {
		if (win->w_buffer->b_fnum == qf_ptr->qf_fnum)
		    break;
		if (win->w_prev == NULL)
		    win = lastwin;	/* wrap around the top */
		else
		    win = win->w_prev;	/* go to previous window */

		if (IS_QF_WINDOW(win))
		{
		    /* Didn't find it, go to the window before the quickfix
		     * window. */
		    if (altwin != NULL)
			win = altwin;
		    else if (curwin->w_prev != NULL)
			win = curwin->w_prev;
		    else
			win = curwin->w_next;
		    break;
		}

		/* Remember a usable window. */
		if (altwin == NULL && !win->w_p_pvw
					   && win->w_buffer->b_p_bt[0] == NUL)
		    altwin = win;
	    }

	    win_goto(win);
	    }
	}
    }
#endif

    /*
     * If there is a file name,
     * read the wanted file if needed, and check autowrite etc.
     */
    old_curbuf = curbuf;
    old_lnum = curwin->w_cursor.lnum;

    if (qf_ptr->qf_fnum != 0)
    {
	if (qf_ptr->qf_type == 1)
	{
	    /* Open help file (do_ecmd() will set b_help flag, readfile() will
	     * set b_p_ro flag). */
	    if (!can_abandon(curbuf, forceit))
	    {
		EMSG(_(e_nowrtmsg));
		ok = FALSE;
	    }
	    else
		ok = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1,
					   ECMD_HIDE + ECMD_SET_HELP,
					   oldwin == curwin ? curwin : NULL);
	}
	else
	    ok = buflist_getfile(qf_ptr->qf_fnum,
			    (linenr_T)1, GETF_SETMARK | GETF_SWITCH, forceit);
    }

    if (ok == OK)
    {
	/* When not switched to another buffer, still need to set pc mark */
	if (curbuf == old_curbuf)
	    setpcmark();

	if (qf_ptr->qf_pattern == NULL)
	{
	    /*
	     * Go to line with error, unless qf_lnum is 0.
	     */
	    i = qf_ptr->qf_lnum;
	    if (i > 0)
	    {
		if (i > curbuf->b_ml.ml_line_count)
		    i = curbuf->b_ml.ml_line_count;
		curwin->w_cursor.lnum = i;
	    }
	    if (qf_ptr->qf_col > 0)
	    {
		curwin->w_cursor.col = qf_ptr->qf_col - 1;
		if (qf_ptr->qf_viscol == TRUE)
		{
		    /*
		     * Check each character from the beginning of the error
		     * line up to the error column.  For each tab character
		     * found, reduce the error column value by the length of
		     * a tab character.
		     */
		    line = ml_get_curline();
		    screen_col = 0;
		    for (char_col = 0; char_col < curwin->w_cursor.col; ++char_col)
		    {
			if (*line == NUL)
			    break;
			if (*line++ == '\t')
			{
			    curwin->w_cursor.col -= 7 - (screen_col % 8);
			    screen_col += 8 - (screen_col % 8);
			}
			else
			    ++screen_col;
		    }
		}
		check_cursor();
	    }
	    else
		beginline(BL_WHITE | BL_FIX);
	}
	else
	{
	    pos_T save_cursor;

	    /* Move the cursor to the first line in the buffer */
	    save_cursor = curwin->w_cursor;
	    curwin->w_cursor.lnum = 0;
	    if (!do_search(NULL, '/', qf_ptr->qf_pattern, (long)1,
							   SEARCH_KEEP, NULL))
		curwin->w_cursor = save_cursor;
	}

#ifdef FEAT_FOLDING
	if ((fdo_flags & FDO_QUICKFIX) && old_KeyTyped)
	    foldOpenCursor();
#endif
	if (print_message)
	{
	    /* Update the screen before showing the message, unless the screen
	     * scrolled up. */
	    if (!msg_scrolled)
		update_topline_redraw();
	    sprintf((char *)IObuff, _("(%d of %d)%s%s: "), qf_index,
		    qi->qf_lists[qi->qf_curlist].qf_count,
		    qf_ptr->qf_cleared ? _(" (line deleted)") : "",
		    (char *)qf_types(qf_ptr->qf_type, qf_ptr->qf_nr));
	    /* Add the message, skipping leading whitespace and newlines. */
	    len = (int)STRLEN(IObuff);
	    qf_fmt_text(skipwhite(qf_ptr->qf_text), IObuff + len, IOSIZE - len);

	    /* Output the message.  Overwrite to avoid scrolling when the 'O'
	     * flag is present in 'shortmess'; But when not jumping, print the
	     * whole message. */
	    i = msg_scroll;
	    if (curbuf == old_curbuf && curwin->w_cursor.lnum == old_lnum)
		msg_scroll = TRUE;
	    else if (!msg_scrolled && shortmess(SHM_OVERALL))
		msg_scroll = FALSE;
	    msg_attr_keep(IObuff, 0, TRUE);
	    msg_scroll = i;
	}
    }
    else
    {
#ifdef FEAT_WINDOWS
	if (opened_window)
	    win_close(curwin, TRUE);    /* Close opened window */
#endif
	if (qf_ptr->qf_fnum != 0)
	{
	    /*
	     * Couldn't open file, so put index back where it was.  This could
	     * happen if the file was readonly and we changed something.
	     */
#ifdef FEAT_WINDOWS
failed:
#endif
	    qf_ptr = old_qf_ptr;
	    qf_index = old_qf_index;
	}
    }
theend:
    qi->qf_lists[qi->qf_curlist].qf_ptr = qf_ptr;
    qi->qf_lists[qi->qf_curlist].qf_index = qf_index;
#ifdef FEAT_WINDOWS
    if (p_swb != old_swb && opened_window)
    {
	/* Restore old 'switchbuf' value, but not when an autocommand or
	 * modeline has changed the value. */
	if (p_swb == empty_option)
	{
	    p_swb = old_swb;
	    swb_flags = old_swb_flags;
	}
	else
	    free_string_option(old_swb);
    }
#endif
}

/*
 * ":clist": list all errors
 * ":llist": list all locations
 */
    void
qf_list(eap)
    exarg_T	*eap;
{
    buf_T	*buf;
    char_u	*fname;
    qfline_T	*qfp;
    int		i;
    int		idx1 = 1;
    int		idx2 = -1;
    char_u	*arg = eap->arg;
    int		all = eap->forceit;	/* if not :cl!, only show
						   recognised errors */
    qf_info_T	*qi = &ql_info;

    if (eap->cmdidx == CMD_llist)
    {
	qi = GET_LOC_LIST(curwin);
	if (qi == NULL)
	{
	    EMSG(_(e_loclist));
	    return;
	}
    }

    if (qi->qf_curlist >= qi->qf_listcount
	|| qi->qf_lists[qi->qf_curlist].qf_count == 0)
    {
	EMSG(_(e_quickfix));
	return;
    }
    if (!get_list_range(&arg, &idx1, &idx2) || *arg != NUL)
    {
	EMSG(_(e_trailing));
	return;
    }
    i = qi->qf_lists[qi->qf_curlist].qf_count;
    if (idx1 < 0)
	idx1 = (-idx1 > i) ? 0 : idx1 + i + 1;
    if (idx2 < 0)
	idx2 = (-idx2 > i) ? 0 : idx2 + i + 1;

    if (qi->qf_lists[qi->qf_curlist].qf_nonevalid)
	all = TRUE;
    qfp = qi->qf_lists[qi->qf_curlist].qf_start;
    for (i = 1; !got_int && i <= qi->qf_lists[qi->qf_curlist].qf_count; )
    {
	if ((qfp->qf_valid || all) && idx1 <= i && i <= idx2)
	{
	    msg_putchar('\n');
	    if (got_int)
		break;

	    fname = NULL;
	    if (qfp->qf_fnum != 0
			      && (buf = buflist_findnr(qfp->qf_fnum)) != NULL)
	    {
		fname = buf->b_fname;
		if (qfp->qf_type == 1)	/* :helpgrep */
		    fname = gettail(fname);
	    }
	    if (fname == NULL)
		sprintf((char *)IObuff, "%2d", i);
	    else
		vim_snprintf((char *)IObuff, IOSIZE, "%2d %s",
							    i, (char *)fname);
	    msg_outtrans_attr(IObuff, i == qi->qf_lists[qi->qf_curlist].qf_index
					   ? hl_attr(HLF_L) : hl_attr(HLF_D));
	    if (qfp->qf_lnum == 0)
		IObuff[0] = NUL;
	    else if (qfp->qf_col == 0)
		sprintf((char *)IObuff, ":%ld", qfp->qf_lnum);
	    else
		sprintf((char *)IObuff, ":%ld col %d",
						   qfp->qf_lnum, qfp->qf_col);
	    sprintf((char *)IObuff + STRLEN(IObuff), "%s:",
				  (char *)qf_types(qfp->qf_type, qfp->qf_nr));
	    msg_puts_attr(IObuff, hl_attr(HLF_N));
	    if (qfp->qf_pattern != NULL)
	    {
		qf_fmt_text(qfp->qf_pattern, IObuff, IOSIZE);
		STRCAT(IObuff, ":");
		msg_puts(IObuff);
	    }
	    msg_puts((char_u *)" ");

	    /* Remove newlines and leading whitespace from the text.  For an
	     * unrecognized line keep the indent, the compiler may mark a word
	     * with ^^^^. */
	    qf_fmt_text((fname != NULL || qfp->qf_lnum != 0)
				     ? skipwhite(qfp->qf_text) : qfp->qf_text,
							      IObuff, IOSIZE);
	    msg_prt_line(IObuff, FALSE);
	    out_flush();		/* show one line at a time */
	}

	qfp = qfp->qf_next;
	++i;
	ui_breakcheck();
    }
}

/*
 * Remove newlines and leading whitespace from an error message.
 * Put the result in "buf[bufsize]".
 */
    static void
qf_fmt_text(text, buf, bufsize)
    char_u	*text;
    char_u	*buf;
    int		bufsize;
{
    int		i;
    char_u	*p = text;

    for (i = 0; *p != NUL && i < bufsize - 1; ++i)
    {
	if (*p == '\n')
	{
	    buf[i] = ' ';
	    while (*++p != NUL)
		if (!vim_iswhite(*p) && *p != '\n')
		    break;
	}
	else
	    buf[i] = *p++;
    }
    buf[i] = NUL;
}

/*
 * ":colder [count]": Up in the quickfix stack.
 * ":cnewer [count]": Down in the quickfix stack.
 * ":lolder [count]": Up in the location list stack.
 * ":lnewer [count]": Down in the location list stack.
 */
    void
qf_age(eap)
    exarg_T	*eap;
{
    qf_info_T	*qi = &ql_info;
    int		count;

    if (eap->cmdidx == CMD_lolder || eap->cmdidx == CMD_lnewer)
    {
	qi = GET_LOC_LIST(curwin);
	if (qi == NULL)
	{
	    EMSG(_(e_loclist));
	    return;
	}
    }

    if (eap->addr_count != 0)
	count = eap->line2;
    else
	count = 1;
    while (count--)
    {
	if (eap->cmdidx == CMD_colder || eap->cmdidx == CMD_lolder)
	{
	    if (qi->qf_curlist == 0)
	    {
		EMSG(_("E380: At bottom of quickfix stack"));
		break;
	    }
	    --qi->qf_curlist;
	}
	else
	{
	    if (qi->qf_curlist >= qi->qf_listcount - 1)
	    {
		EMSG(_("E381: At top of quickfix stack"));
		break;
	    }
	    ++qi->qf_curlist;
	}
    }
    qf_msg(qi);
}

    static void
qf_msg(qi)
    qf_info_T	*qi;
{
    smsg((char_u *)_("error list %d of %d; %d errors"),
	    qi->qf_curlist + 1, qi->qf_listcount,
	    qi->qf_lists[qi->qf_curlist].qf_count);
#ifdef FEAT_WINDOWS
    qf_update_buffer(qi);
#endif
}

/*
 * Free error list "idx".
 */
    static void
qf_free(qi, idx)
    qf_info_T	*qi;
    int		idx;
{
    qfline_T	*qfp;
    int		stop = FALSE;

    while (qi->qf_lists[idx].qf_count)
    {
	qfp = qi->qf_lists[idx].qf_start->qf_next;
	if (qi->qf_lists[idx].qf_title != NULL && !stop)
	{
	    vim_free(qi->qf_lists[idx].qf_start->qf_text);
	    stop = (qi->qf_lists[idx].qf_start == qfp);
	    vim_free(qi->qf_lists[idx].qf_start->qf_pattern);
	    vim_free(qi->qf_lists[idx].qf_start);
	    if (stop)
		/* Somehow qf_count may have an incorrect value, set it to 1
		 * to avoid crashing when it's wrong.
		 * TODO: Avoid qf_count being incorrect. */
		qi->qf_lists[idx].qf_count = 1;
	}
	qi->qf_lists[idx].qf_start = qfp;
	--qi->qf_lists[idx].qf_count;
    }
    vim_free(qi->qf_lists[idx].qf_title);
    qi->qf_lists[idx].qf_title = NULL;
}

/*
 * qf_mark_adjust: adjust marks
 */
   void
qf_mark_adjust(wp, line1, line2, amount, amount_after)
    win_T	*wp;
    linenr_T	line1;
    linenr_T	line2;
    long	amount;
    long	amount_after;
{
    int		i;
    qfline_T	*qfp;
    int		idx;
    qf_info_T	*qi = &ql_info;

    if (wp != NULL)
    {
	if (wp->w_llist == NULL)
	    return;
	qi = wp->w_llist;
    }

    for (idx = 0; idx < qi->qf_listcount; ++idx)
	if (qi->qf_lists[idx].qf_count)
	    for (i = 0, qfp = qi->qf_lists[idx].qf_start;
		       i < qi->qf_lists[idx].qf_count; ++i, qfp = qfp->qf_next)
		if (qfp->qf_fnum == curbuf->b_fnum)
		{
		    if (qfp->qf_lnum >= line1 && qfp->qf_lnum <= line2)
		    {
			if (amount == MAXLNUM)
			    qfp->qf_cleared = TRUE;
			else
			    qfp->qf_lnum += amount;
		    }
		    else if (amount_after && qfp->qf_lnum > line2)
			qfp->qf_lnum += amount_after;
		}
}

/*
 * Make a nice message out of the error character and the error number:
 *  char    number	message
 *  e or E    0		" error"
 *  w or W    0		" warning"
 *  i or I    0		" info"
 *  0	      0		""
 *  other     0		" c"
 *  e or E    n		" error n"
 *  w or W    n		" warning n"
 *  i or I    n		" info n"
 *  0	      n		" error n"
 *  other     n		" c n"
 *  1	      x		""	:helpgrep
 */
    static char_u *
qf_types(c, nr)
    int c, nr;
{
    static char_u	buf[20];
    static char_u	cc[3];
    char_u		*p;

    if (c == 'W' || c == 'w')
	p = (char_u *)" warning";
    else if (c == 'I' || c == 'i')
	p = (char_u *)" info";
    else if (c == 'E' || c == 'e' || (c == 0 && nr > 0))
	p = (char_u *)" error";
    else if (c == 0 || c == 1)
	p = (char_u *)"";
    else
    {
	cc[0] = ' ';
	cc[1] = c;
	cc[2] = NUL;
	p = cc;
    }

    if (nr <= 0)
	return p;

    sprintf((char *)buf, "%s %3d", (char *)p, nr);
    return buf;
}

#if defined(FEAT_WINDOWS) || defined(PROTO)
/*
 * ":cwindow": open the quickfix window if we have errors to display,
 *	       close it if not.
 * ":lwindow": open the location list window if we have locations to display,
 *	       close it if not.
 */
    void
ex_cwindow(eap)
    exarg_T	*eap;
{
    qf_info_T	*qi = &ql_info;
    win_T	*win;

    if (eap->cmdidx == CMD_lwindow)
    {
	qi = GET_LOC_LIST(curwin);
	if (qi == NULL)
	    return;
    }

    /* Look for an existing quickfix window.  */
    win = qf_find_win(qi);

    /*
     * If a quickfix window is open but we have no errors to display,
     * close the window.  If a quickfix window is not open, then open
     * it if we have errors; otherwise, leave it closed.
     */
    if (qi->qf_lists[qi->qf_curlist].qf_nonevalid
	    || qi->qf_lists[qi->qf_curlist].qf_count == 0
	    || qi->qf_curlist >= qi->qf_listcount)
    {
	if (win != NULL)
	    ex_cclose(eap);
    }
    else if (win == NULL)
	ex_copen(eap);
}

/*
 * ":cclose": close the window showing the list of errors.
 * ":lclose": close the window showing the location list
 */
    void
ex_cclose(eap)
    exarg_T	*eap;
{
    win_T	*win = NULL;
    qf_info_T	*qi = &ql_info;

    if (eap->cmdidx == CMD_lclose || eap->cmdidx == CMD_lwindow)
    {
	qi = GET_LOC_LIST(curwin);
	if (qi == NULL)
	    return;
    }

    /* Find existing quickfix window and close it. */
    win = qf_find_win(qi);
    if (win != NULL)
	win_close(win, FALSE);
}

/*
 * ":copen": open a window that shows the list of errors.
 * ":lopen": open a window that shows the location list.
 */
    void
ex_copen(eap)
    exarg_T	*eap;
{
    qf_info_T	*qi = &ql_info;
    int		height;
    win_T	*win;
    tabpage_T	*prevtab = curtab;
    buf_T	*qf_buf;
    win_T	*oldwin = curwin;

    if (eap->cmdidx == CMD_lopen || eap->cmdidx == CMD_lwindow)
    {
	qi = GET_LOC_LIST(curwin);
	if (qi == NULL)
	{
	    EMSG(_(e_loclist));
	    return;
	}
    }

    if (eap->addr_count != 0)
	height = eap->line2;
    else
	height = QF_WINHEIGHT;

#ifdef FEAT_VISUAL
    reset_VIsual_and_resel();			/* stop Visual mode */
#endif
#ifdef FEAT_GUI
    need_mouse_correct = TRUE;
#endif

    /*
     * Find existing quickfix window, or open a new one.
     */
    win = qf_find_win(qi);

    if (win != NULL && cmdmod.tab == 0)
	win_goto(win);
    else
    {
	qf_buf = qf_find_buf(qi);

	/* The current window becomes the previous window afterwards. */
	win = curwin;

	if ((eap->cmdidx == CMD_copen || eap->cmdidx == CMD_cwindow)
		&& cmdmod.split == 0)
	    /* Create the new window at the very bottom, except when
	     * :belowright or :aboveleft is used. */
	    win_goto(lastwin);
	if (win_split(height, WSP_BELOW | WSP_NEWLOC) == FAIL)
	    return;		/* not enough room for window */
	RESET_BINDING(curwin);

	if (eap->cmdidx == CMD_lopen || eap->cmdidx == CMD_lwindow)
	{
	    /*
	     * For the location list window, create a reference to the
	     * location list from the window 'win'.
	     */
	    curwin->w_llist_ref = win->w_llist;
	    win->w_llist->qf_refcount++;
	}

	if (oldwin != curwin)
	    oldwin = NULL;  /* don't store info when in another window */
	if (qf_buf != NULL)
	    /* Use the existing quickfix buffer */
	    (void)do_ecmd(qf_buf->b_fnum, NULL, NULL, NULL, ECMD_ONE,
					     ECMD_HIDE + ECMD_OLDBUF, oldwin);
	else
	{
	    /* Create a new quickfix buffer */
	    (void)do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, oldwin);
	    /* switch off 'swapfile' */
	    set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL);
	    set_option_value((char_u *)"bt", 0L, (char_u *)"quickfix",
								   OPT_LOCAL);
	    set_option_value((char_u *)"bh", 0L, (char_u *)"wipe", OPT_LOCAL);
	    RESET_BINDING(curwin);
#ifdef FEAT_DIFF
	    curwin->w_p_diff = FALSE;
#endif
#ifdef FEAT_FOLDING
	    set_option_value((char_u *)"fdm", 0L, (char_u *)"manual",
								   OPT_LOCAL);
#endif
	}

	/* Only set the height when still in the same tab page and there is no
	 * window to the side. */
	if (curtab == prevtab
#ifdef FEAT_VERTSPLIT
		&& curwin->w_width == Columns
#endif
	   )
	    win_setheight(height);
	curwin->w_p_wfh = TRUE;	    /* set 'winfixheight' */
	if (win_valid(win))
	    prevwin = win;
    }

    /*
     * Fill the buffer with the quickfix list.
     */
    qf_fill_buffer(qi);

    if (qi->qf_lists[qi->qf_curlist].qf_title != NULL)
	qf_set_title(qi);

    curwin->w_cursor.lnum = qi->qf_lists[qi->qf_curlist].qf_index;
    curwin->w_cursor.col = 0;
    check_cursor();
    update_topline();		/* scroll to show the line */
}

/*
 * Return the number of the current entry (line number in the quickfix
 * window).
 */
     linenr_T
qf_current_entry(wp)
    win_T	*wp;
{
    qf_info_T	*qi = &ql_info;

    if (IS_LL_WINDOW(wp))
	/* In the location list window, use the referenced location list */
	qi = wp->w_llist_ref;

    return qi->qf_lists[qi->qf_curlist].qf_index;
}

/*
 * Update the cursor position in the quickfix window to the current error.
 * Return TRUE if there is a quickfix window.
 */
    static int
qf_win_pos_update(qi, old_qf_index)
    qf_info_T	*qi;
    int		old_qf_index;	/* previous qf_index or zero */
{
    win_T	*win;
    int		qf_index = qi->qf_lists[qi->qf_curlist].qf_index;

    /*
     * Put the cursor on the current error in the quickfix window, so that
     * it's viewable.
     */
    win = qf_find_win(qi);
    if (win != NULL
	    && qf_index <= win->w_buffer->b_ml.ml_line_count
	    && old_qf_index != qf_index)
    {
	win_T	*old_curwin = curwin;

	curwin = win;
	curbuf = win->w_buffer;
	if (qf_index > old_qf_index)
	{
	    curwin->w_redraw_top = old_qf_index;
	    curwin->w_redraw_bot = qf_index;
	}
	else
	{
	    curwin->w_redraw_top = qf_index;
	    curwin->w_redraw_bot = old_qf_index;
	}
	curwin->w_cursor.lnum = qf_index;
	curwin->w_cursor.col = 0;
	update_topline();		/* scroll to show the line */
	redraw_later(VALID);
	curwin->w_redr_status = TRUE;	/* update ruler */
	curwin = old_curwin;
	curbuf = curwin->w_buffer;
    }
    return win != NULL;
}

/*
 * Check whether the given window is displaying the specified quickfix/location
 * list buffer
 */
    static int
is_qf_win(win, qi)
    win_T	*win;
    qf_info_T	*qi;
{
    /*
     * A window displaying the quickfix buffer will have the w_llist_ref field
     * set to NULL.
     * A window displaying a location list buffer will have the w_llist_ref
     * pointing to the location list.
     */
    if (bt_quickfix(win->w_buffer))
	if ((qi == &ql_info && win->w_llist_ref == NULL)
		|| (qi != &ql_info && win->w_llist_ref == qi))
	    return TRUE;

    return FALSE;
}

/*
 * Find a window displaying the quickfix/location list 'qi'
 * Searches in only the windows opened in the current tab.
 */
    static win_T *
qf_find_win(qi)
    qf_info_T	*qi;
{
    win_T	*win;

    FOR_ALL_WINDOWS(win)
	if (is_qf_win(win, qi))
	    break;

    return win;
}

/*
 * Find a quickfix buffer.
 * Searches in windows opened in all the tabs.
 */
    static buf_T *
qf_find_buf(qi)
    qf_info_T	*qi;
{
    tabpage_T	*tp;
    win_T	*win;

    FOR_ALL_TAB_WINDOWS(tp, win)
	if (is_qf_win(win, qi))
	    return win->w_buffer;

    return NULL;
}

/*
 * Find the quickfix buffer.  If it exists, update the contents.
 */
    static void
qf_update_buffer(qi)
    qf_info_T	*qi;
{
    buf_T	*buf;
    win_T	*win;
    win_T	*curwin_save;
    aco_save_T	aco;

    /* Check if a buffer for the quickfix list exists.  Update it. */
    buf = qf_find_buf(qi);
    if (buf != NULL)
    {
	/* set curwin/curbuf to buf and save a few things */
	aucmd_prepbuf(&aco, buf);

	qf_fill_buffer(qi);

	if (qi->qf_lists[qi->qf_curlist].qf_title != NULL
	    && (win = qf_find_win(qi)) != NULL)
	{
	    curwin_save = curwin;
	    curwin = win;
	    qf_set_title(qi);
	    curwin = curwin_save;

	}

	/* restore curwin/curbuf and a few other things */
	aucmd_restbuf(&aco);

	(void)qf_win_pos_update(qi, 0);
    }
}

    static void
qf_set_title(qi)
    qf_info_T	*qi;
{
    set_internal_string_var((char_u *)"w:quickfix_title",
				    qi->qf_lists[qi->qf_curlist].qf_title);
}

/*
 * Fill current buffer with quickfix errors, replacing any previous contents.
 * curbuf must be the quickfix buffer!
 */
    static void
qf_fill_buffer(qi)
    qf_info_T	*qi;
{
    linenr_T	lnum;
    qfline_T	*qfp;
    buf_T	*errbuf;
    int		len;
    int		old_KeyTyped = KeyTyped;

    /* delete all existing lines */
    while ((curbuf->b_ml.ml_flags & ML_EMPTY) == 0)
	(void)ml_delete((linenr_T)1, FALSE);

    /* Check if there is anything to display */
    if (qi->qf_curlist < qi->qf_listcount)
    {
	/* Add one line for each error */
	qfp = qi->qf_lists[qi->qf_curlist].qf_start;
	for (lnum = 0; lnum < qi->qf_lists[qi->qf_curlist].qf_count; ++lnum)
	{
	    if (qfp->qf_fnum != 0
		    && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL
		    && errbuf->b_fname != NULL)
	    {
		if (qfp->qf_type == 1)	/* :helpgrep */
		    STRCPY(IObuff, gettail(errbuf->b_fname));
		else
		    STRCPY(IObuff, errbuf->b_fname);
		len = (int)STRLEN(IObuff);
	    }
	    else
		len = 0;
	    IObuff[len++] = '|';

	    if (qfp->qf_lnum > 0)
	    {
		sprintf((char *)IObuff + len, "%ld", qfp->qf_lnum);
		len += (int)STRLEN(IObuff + len);

		if (qfp->qf_col > 0)
		{
		    sprintf((char *)IObuff + len, " col %d", qfp->qf_col);
		    len += (int)STRLEN(IObuff + len);
		}

		sprintf((char *)IObuff + len, "%s",
				  (char *)qf_types(qfp->qf_type, qfp->qf_nr));
		len += (int)STRLEN(IObuff + len);
	    }
	    else if (qfp->qf_pattern != NULL)
	    {
		qf_fmt_text(qfp->qf_pattern, IObuff + len, IOSIZE - len);
		len += (int)STRLEN(IObuff + len);
	    }
	    IObuff[len++] = '|';
	    IObuff[len++] = ' ';

	    /* Remove newlines and leading whitespace from the text.
	     * For an unrecognized line keep the indent, the compiler may
	     * mark a word with ^^^^. */
	    qf_fmt_text(len > 3 ? skipwhite(qfp->qf_text) : qfp->qf_text,
						  IObuff + len, IOSIZE - len);

	    if (ml_append(lnum, IObuff, (colnr_T)STRLEN(IObuff) + 1, FALSE)
								      == FAIL)
		break;
	    qfp = qfp->qf_next;
	}
	/* Delete the empty line which is now at the end */
	(void)ml_delete(lnum + 1, FALSE);
    }

    /* correct cursor position */
    check_lnums(TRUE);

    /* Set the 'filetype' to "qf" each time after filling the buffer.  This
     * resembles reading a file into a buffer, it's more logical when using
     * autocommands. */
    set_option_value((char_u *)"ft", 0L, (char_u *)"qf", OPT_LOCAL);
    curbuf->b_p_ma = FALSE;

#ifdef FEAT_AUTOCMD
    keep_filetype = TRUE;		/* don't detect 'filetype' */
    apply_autocmds(EVENT_BUFREADPOST, (char_u *)"quickfix", NULL,
							       FALSE, curbuf);
    apply_autocmds(EVENT_BUFWINENTER, (char_u *)"quickfix", NULL,
							       FALSE, curbuf);
    keep_filetype = FALSE;
#endif

    /* make sure it will be redrawn */
    redraw_curbuf_later(NOT_VALID);

    /* Restore KeyTyped, setting 'filetype' may reset it. */
    KeyTyped = old_KeyTyped;
}

#endif /* FEAT_WINDOWS */

/*
 * Return TRUE if "buf" is the quickfix buffer.
 */
    int
bt_quickfix(buf)
    buf_T	*buf;
{
    return buf != NULL && buf->b_p_bt[0] == 'q';
}

/*
 * Return TRUE if "buf" is a "nofile" or "acwrite" buffer.
 * This means the buffer name is not a file name.
 */
    int
bt_nofile(buf)
    buf_T	*buf;
{
    return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
	    || buf->b_p_bt[0] == 'a');
}

/*
 * Return TRUE if "buf" is a "nowrite" or "nofile" buffer.
 */
    int
bt_dontwrite(buf)
    buf_T	*buf;
{
    return buf != NULL && buf->b_p_bt[0] == 'n';
}

    int
bt_dontwrite_msg(buf)
    buf_T	*buf;
{
    if (bt_dontwrite(buf))
    {
	EMSG(_("E382: Cannot write, 'buftype' option is set"));
	return TRUE;
    }
    return FALSE;
}

/*
 * Return TRUE if the buffer should be hidden, according to 'hidden', ":hide"
 * and 'bufhidden'.
 */
    int
buf_hide(buf)
    buf_T	*buf;
{
    /* 'bufhidden' overrules 'hidden' and ":hide", check it first */
    switch (buf->b_p_bh[0])
    {
	case 'u':		    /* "unload" */
	case 'w':		    /* "wipe" */
	case 'd': return FALSE;	    /* "delete" */
	case 'h': return TRUE;	    /* "hide" */
    }
    return (p_hid || cmdmod.hide);
}

/*
 * Return TRUE when using ":vimgrep" for ":grep".
 */
    int
grep_internal(cmdidx)
    cmdidx_T	cmdidx;
{
    return ((cmdidx == CMD_grep
		|| cmdidx == CMD_lgrep
		|| cmdidx == CMD_grepadd
		|| cmdidx == CMD_lgrepadd)
	    && STRCMP("internal",
			*curbuf->b_p_gp == NUL ? p_gp : curbuf->b_p_gp) == 0);
}

/*
 * Used for ":make", ":lmake", ":grep", ":lgrep", ":grepadd", and ":lgrepadd"
 */
    void
ex_make(eap)
    exarg_T	*eap;
{
    char_u	*fname;
    char_u	*cmd;
    unsigned	len;
    win_T	*wp = NULL;
    qf_info_T	*qi = &ql_info;
    int		res;
#ifdef FEAT_AUTOCMD
    char_u	*au_name = NULL;

    /* Redirect ":grep" to ":vimgrep" if 'grepprg' is "internal". */
    if (grep_internal(eap->cmdidx))
    {
	ex_vimgrep(eap);
	return;
    }

    switch (eap->cmdidx)
    {
	case CMD_make:	    au_name = (char_u *)"make"; break;
	case CMD_lmake:	    au_name = (char_u *)"lmake"; break;
	case CMD_grep:	    au_name = (char_u *)"grep"; break;
	case CMD_lgrep:	    au_name = (char_u *)"lgrep"; break;
	case CMD_grepadd:   au_name = (char_u *)"grepadd"; break;
	case CMD_lgrepadd:  au_name = (char_u *)"lgrepadd"; break;
	default: break;
    }
    if (au_name != NULL)
    {
	apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
					       curbuf->b_fname, TRUE, curbuf);
# ifdef FEAT_EVAL
	if (did_throw || force_abort)
	    return;
# endif
    }
#endif

    if (eap->cmdidx == CMD_lmake || eap->cmdidx == CMD_lgrep
	|| eap->cmdidx == CMD_lgrepadd)
	wp = curwin;

    autowrite_all();
    fname = get_mef_name();
    if (fname == NULL)
	return;
    mch_remove(fname);	    /* in case it's not unique */

    /*
     * If 'shellpipe' empty: don't redirect to 'errorfile'.
     */
    len = (unsigned)STRLEN(p_shq) * 2 + (unsigned)STRLEN(eap->arg) + 1;
    if (*p_sp != NUL)
	len += (unsigned)STRLEN(p_sp) + (unsigned)STRLEN(fname) + 3;
    cmd = alloc(len);
    if (cmd == NULL)
	return;
    sprintf((char *)cmd, "%s%s%s", (char *)p_shq, (char *)eap->arg,
							       (char *)p_shq);
    if (*p_sp != NUL)
	append_redir(cmd, len, p_sp, fname);
    /*
     * Output a newline if there's something else than the :make command that
     * was typed (in which case the cursor is in column 0).
     */
    if (msg_col == 0)
	msg_didout = FALSE;
    msg_start();
    MSG_PUTS(":!");
    msg_outtrans(cmd);		/* show what we are doing */

    /* let the shell know if we are redirecting output or not */
    do_shell(cmd, *p_sp != NUL ? SHELL_DOOUT : 0);

#ifdef AMIGA
    out_flush();
		/* read window status report and redraw before message */
    (void)char_avail();
#endif

    res = qf_init(wp, fname, (eap->cmdidx != CMD_make
			    && eap->cmdidx != CMD_lmake) ? p_gefm : p_efm,
					   (eap->cmdidx != CMD_grepadd
					    && eap->cmdidx != CMD_lgrepadd),
					   *eap->cmdlinep);
    if (wp != NULL)
	qi = GET_LOC_LIST(wp);
#ifdef FEAT_AUTOCMD
    if (au_name != NULL)
    {
	apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
					       curbuf->b_fname, TRUE, curbuf);
	if (qi->qf_curlist < qi->qf_listcount)
	    res = qi->qf_lists[qi->qf_curlist].qf_count;
	else
	    res = 0;
    }
#endif
    if (res > 0 && !eap->forceit)
	qf_jump(qi, 0, 0, FALSE);		/* display first error */

    mch_remove(fname);
    vim_free(fname);
    vim_free(cmd);
}

/*
 * Return the name for the errorfile, in allocated memory.
 * Find a new unique name when 'makeef' contains "##".
 * Returns NULL for error.
 */
    static char_u *
get_mef_name()
{
    char_u	*p;
    char_u	*name;
    static int	start = -1;
    static int	off = 0;
#ifdef HAVE_LSTAT
    struct stat	sb;
#endif

    if (*p_mef == NUL)
    {
	name = vim_tempname('e');
	if (name == NULL)
	    EMSG(_(e_notmp));
	return name;
    }

    for (p = p_mef; *p; ++p)
	if (p[0] == '#' && p[1] == '#')
	    break;

    if (*p == NUL)
	return vim_strsave(p_mef);

    /* Keep trying until the name doesn't exist yet. */
    for (;;)
    {
	if (start == -1)
	    start = mch_get_pid();
	else
	    off += 19;

	name = alloc((unsigned)STRLEN(p_mef) + 30);
	if (name == NULL)
	    break;
	STRCPY(name, p_mef);
	sprintf((char *)name + (p - p_mef), "%d%d", start, off);
	STRCAT(name, p + 2);
	if (mch_getperm(name) < 0
#ifdef HAVE_LSTAT
		    /* Don't accept a symbolic link, its a security risk. */
		    && mch_lstat((char *)name, &sb) < 0
#endif
		)
	    break;
	vim_free(name);
    }
    return name;
}

/*
 * ":cc", ":crewind", ":cfirst" and ":clast".
 * ":ll", ":lrewind", ":lfirst" and ":llast".
 */
    void
ex_cc(eap)
    exarg_T	*eap;
{
    qf_info_T	*qi = &ql_info;

    if (eap->cmdidx == CMD_ll
	    || eap->cmdidx == CMD_lrewind
	    || eap->cmdidx == CMD_lfirst
	    || eap->cmdidx == CMD_llast)
    {
	qi = GET_LOC_LIST(curwin);
	if (qi == NULL)
	{
	    EMSG(_(e_loclist));
	    return;
	}
    }

    qf_jump(qi, 0,
	    eap->addr_count > 0
	    ? (int)eap->line2
	    : (eap->cmdidx == CMD_cc || eap->cmdidx == CMD_ll)
		? 0
		: (eap->cmdidx == CMD_crewind || eap->cmdidx == CMD_lrewind
		   || eap->cmdidx == CMD_cfirst || eap->cmdidx == CMD_lfirst)
		    ? 1
		    : 32767,
	    eap->forceit);
}

/*
 * ":cnext", ":cnfile", ":cNext" and ":cprevious".
 * ":lnext", ":lNext", ":lprevious", ":lnfile", ":lNfile" and ":lpfile".
 */
    void
ex_cnext(eap)
    exarg_T	*eap;
{
    qf_info_T	*qi = &ql_info;

    if (eap->cmdidx == CMD_lnext
	    || eap->cmdidx == CMD_lNext
	    || eap->cmdidx == CMD_lprevious
	    || eap->cmdidx == CMD_lnfile
	    || eap->cmdidx == CMD_lNfile
	    || eap->cmdidx == CMD_lpfile)
    {
	qi = GET_LOC_LIST(curwin);
	if (qi == NULL)
	{
	    EMSG(_(e_loclist));
	    return;
	}
    }

    qf_jump(qi, (eap->cmdidx == CMD_cnext || eap->cmdidx == CMD_lnext)
	    ? FORWARD
	    : (eap->cmdidx == CMD_cnfile || eap->cmdidx == CMD_lnfile)
		? FORWARD_FILE
		: (eap->cmdidx == CMD_cpfile || eap->cmdidx == CMD_lpfile
		   || eap->cmdidx == CMD_cNfile || eap->cmdidx == CMD_lNfile)
		    ? BACKWARD_FILE
		    : BACKWARD,
	    eap->addr_count > 0 ? (int)eap->line2 : 1, eap->forceit);
}

/*
 * ":cfile"/":cgetfile"/":caddfile" commands.
 * ":lfile"/":lgetfile"/":laddfile" commands.
 */
    void
ex_cfile(eap)
    exarg_T	*eap;
{
    win_T	*wp = NULL;
    qf_info_T	*qi = &ql_info;
#ifdef FEAT_AUTOCMD
    char_u	*au_name = NULL;
#endif

    if (eap->cmdidx == CMD_lfile || eap->cmdidx == CMD_lgetfile
					       || eap->cmdidx == CMD_laddfile)
	wp = curwin;

#ifdef FEAT_AUTOCMD
    switch (eap->cmdidx)
    {
	case CMD_cfile:	    au_name = (char_u *)"cfile"; break;
	case CMD_cgetfile:  au_name = (char_u *)"cgetfile"; break;
	case CMD_caddfile:  au_name = (char_u *)"caddfile"; break;
	case CMD_lfile:	    au_name = (char_u *)"lfile"; break;
	case CMD_lgetfile:  au_name = (char_u *)"lgetfile"; break;
	case CMD_laddfile:  au_name = (char_u *)"laddfile"; break;
	default: break;
    }
    if (au_name != NULL)
	apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, NULL, FALSE, curbuf);
#endif
#ifdef FEAT_BROWSE
    if (cmdmod.browse)
    {
	char_u *browse_file = do_browse(0, (char_u *)_("Error file"), eap->arg,
				   NULL, NULL, BROWSE_FILTER_ALL_FILES, NULL);
	if (browse_file == NULL)
	    return;
	set_string_option_direct((char_u *)"ef", -1, browse_file, OPT_FREE, 0);
	vim_free(browse_file);
    }
    else
#endif
    if (*eap->arg != NUL)
	set_string_option_direct((char_u *)"ef", -1, eap->arg, OPT_FREE, 0);

    /*
     * This function is used by the :cfile, :cgetfile and :caddfile
     * commands.
     * :cfile always creates a new quickfix list and jumps to the
     * first error.
     * :cgetfile creates a new quickfix list but doesn't jump to the
     * first error.
     * :caddfile adds to an existing quickfix list. If there is no
     * quickfix list then a new list is created.
     */
    if (qf_init(wp, p_ef, p_efm, (eap->cmdidx != CMD_caddfile
				  && eap->cmdidx != CMD_laddfile),
							   *eap->cmdlinep) > 0
				  && (eap->cmdidx == CMD_cfile
					     || eap->cmdidx == CMD_lfile))
    {
#ifdef FEAT_AUTOCMD
	if (au_name != NULL)
	    apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, FALSE, curbuf);
#endif
	if (wp != NULL)
	    qi = GET_LOC_LIST(wp);
	qf_jump(qi, 0, 0, eap->forceit);	/* display first error */
    }

    else
    {
#ifdef FEAT_AUTOCMD
	if (au_name != NULL)
	    apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, FALSE, curbuf);
#endif
    }
}

/*
 * ":vimgrep {pattern} file(s)"
 * ":vimgrepadd {pattern} file(s)"
 * ":lvimgrep {pattern} file(s)"
 * ":lvimgrepadd {pattern} file(s)"
 */
    void
ex_vimgrep(eap)
    exarg_T	*eap;
{
    regmmatch_T	regmatch;
    int		fcount;
    char_u	**fnames;
    char_u	*fname;
    char_u	*s;
    char_u	*p;
    int		fi;
    qf_info_T	*qi = &ql_info;
#ifdef FEAT_AUTOCMD
    qfline_T	*cur_qf_start;
#endif
    qfline_T	*prevp = NULL;
    long	lnum;
    buf_T	*buf;
    int		duplicate_name = FALSE;
    int		using_dummy;
    int		redraw_for_dummy = FALSE;
    int		found_match;
    buf_T	*first_match_buf = NULL;
    time_t	seconds = 0;
    int		save_mls;
#if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
    char_u	*save_ei = NULL;
#endif
    aco_save_T	aco;
    int		flags = 0;
    colnr_T	col;
    long	tomatch;
    char_u	*dirname_start = NULL;
    char_u	*dirname_now = NULL;
    char_u	*target_dir = NULL;
#ifdef FEAT_AUTOCMD
    char_u	*au_name =  NULL;

    switch (eap->cmdidx)
    {
	case CMD_vimgrep:     au_name = (char_u *)"vimgrep"; break;
	case CMD_lvimgrep:    au_name = (char_u *)"lvimgrep"; break;
	case CMD_vimgrepadd:  au_name = (char_u *)"vimgrepadd"; break;
	case CMD_lvimgrepadd: au_name = (char_u *)"lvimgrepadd"; break;
	case CMD_grep:	      au_name = (char_u *)"grep"; break;
	case CMD_lgrep:	      au_name = (char_u *)"lgrep"; break;
	case CMD_grepadd:     au_name = (char_u *)"grepadd"; break;
	case CMD_lgrepadd:    au_name = (char_u *)"lgrepadd"; break;
	default: break;
    }
    if (au_name != NULL)
    {
	apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
					       curbuf->b_fname, TRUE, curbuf);
	if (did_throw || force_abort)
	    return;
    }
#endif

    if (eap->cmdidx == CMD_lgrep
	    || eap->cmdidx == CMD_lvimgrep
	    || eap->cmdidx == CMD_lgrepadd
	    || eap->cmdidx == CMD_lvimgrepadd)
    {
	qi = ll_get_or_alloc_list(curwin);
	if (qi == NULL)
	    return;
    }

    if (eap->addr_count > 0)
	tomatch = eap->line2;
    else
	tomatch = MAXLNUM;

    /* Get the search pattern: either white-separated or enclosed in // */
    regmatch.regprog = NULL;
    p = skip_vimgrep_pat(eap->arg, &s, &flags);
    if (p == NULL)
    {
	EMSG(_(e_invalpat));
	goto theend;
    }

    if (s != NULL && *s == NUL)
    {
	/* Pattern is empty, use last search pattern. */
	if (last_search_pat() == NULL)
	{
	    EMSG(_(e_noprevre));
	    goto theend;
	}
	regmatch.regprog = vim_regcomp(last_search_pat(), RE_MAGIC);
    }
    else
	regmatch.regprog = vim_regcomp(s, RE_MAGIC);

    if (regmatch.regprog == NULL)
	goto theend;
    regmatch.rmm_ic = p_ic;
    regmatch.rmm_maxcol = 0;

    p = skipwhite(p);
    if (*p == NUL)
    {
	EMSG(_("E683: File name missing or invalid pattern"));
	goto theend;
    }

    if ((eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd &&
	 eap->cmdidx != CMD_vimgrepadd && eap->cmdidx != CMD_lvimgrepadd)
					|| qi->qf_curlist == qi->qf_listcount)
	/* make place for a new list */
	qf_new_list(qi, *eap->cmdlinep);
    else if (qi->qf_lists[qi->qf_curlist].qf_count > 0)
	/* Adding to existing list, find last entry. */
	for (prevp = qi->qf_lists[qi->qf_curlist].qf_start;
			    prevp->qf_next != prevp; prevp = prevp->qf_next)
	    ;

    /* parse the list of arguments */
    if (get_arglist_exp(p, &fcount, &fnames, TRUE) == FAIL)
	goto theend;
    if (fcount == 0)
    {
	EMSG(_(e_nomatch));
	goto theend;
    }

    dirname_start = alloc(MAXPATHL);
    dirname_now = alloc(MAXPATHL);
    if (dirname_start == NULL || dirname_now == NULL)
	goto theend;

    /* Remember the current directory, because a BufRead autocommand that does
     * ":lcd %:p:h" changes the meaning of short path names. */
    mch_dirname(dirname_start, MAXPATHL);

#ifdef FEAT_AUTOCMD
     /* Remember the value of qf_start, so that we can check for autocommands
      * changing the current quickfix list. */
    cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start;
#endif

    seconds = (time_t)0;
    for (fi = 0; fi < fcount && !got_int && tomatch > 0; ++fi)
    {
	fname = shorten_fname1(fnames[fi]);
	if (time(NULL) > seconds)
	{
	    /* Display the file name every second or so, show the user we are
	     * working on it. */
	    seconds = time(NULL);
	    msg_start();
	    p = msg_strtrunc(fname, TRUE);
	    if (p == NULL)
		msg_outtrans(fname);
	    else
	    {
		msg_outtrans(p);
		vim_free(p);
	    }
	    msg_clr_eos();
	    msg_didout = FALSE;	    /* overwrite this message */
	    msg_nowait = TRUE;	    /* don't wait for this message */
	    msg_col = 0;
	    out_flush();
	}

	buf = buflist_findname_exp(fnames[fi]);
	if (buf == NULL || buf->b_ml.ml_mfp == NULL)
	{
	    /* Remember that a buffer with this name already exists. */
	    duplicate_name = (buf != NULL);
	    using_dummy = TRUE;
	    redraw_for_dummy = TRUE;

#if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
	    /* Don't do Filetype autocommands to avoid loading syntax and
	     * indent scripts, a great speed improvement. */
	    save_ei = au_event_disable(",Filetype");
#endif
	    /* Don't use modelines here, it's useless. */
	    save_mls = p_mls;
	    p_mls = 0;

	    /* Load file into a buffer, so that 'fileencoding' is detected,
	     * autocommands applied, etc. */
	    buf = load_dummy_buffer(fname, dirname_start, dirname_now);

	    p_mls = save_mls;
#if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
	    au_event_restore(save_ei);
#endif
	}
	else
	    /* Use existing, loaded buffer. */
	    using_dummy = FALSE;

#ifdef FEAT_AUTOCMD
	if (cur_qf_start != qi->qf_lists[qi->qf_curlist].qf_start)
	{
	    int idx;

	    /* Autocommands changed the quickfix list.  Find the one we were
	     * using and restore it. */
	    for (idx = 0; idx < LISTCOUNT; ++idx)
		if (cur_qf_start == qi->qf_lists[idx].qf_start)
		{
		    qi->qf_curlist = idx;
		    break;
		}
	    if (idx == LISTCOUNT)
	    {
		/* List cannot be found, create a new one. */
		qf_new_list(qi, *eap->cmdlinep);
		cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start;
	    }
	}
#endif

	if (buf == NULL)
	{
	    if (!got_int)
		smsg((char_u *)_("Cannot open file \"%s\""), fname);
	}
	else
	{
	    /* Try for a match in all lines of the buffer.
	     * For ":1vimgrep" look for first match only. */
	    found_match = FALSE;
	    for (lnum = 1; lnum <= buf->b_ml.ml_line_count && tomatch > 0;
								       ++lnum)
	    {
		col = 0;
		while (vim_regexec_multi(&regmatch, curwin, buf, lnum,
							       col, NULL) > 0)
		{
		    ;
		    if (qf_add_entry(qi, &prevp,
				NULL,       /* dir */
				fname,
				0,
				ml_get_buf(buf,
				     regmatch.startpos[0].lnum + lnum, FALSE),
				regmatch.startpos[0].lnum + lnum,
				regmatch.startpos[0].col + 1,
				FALSE,      /* vis_col */
				NULL,	    /* search pattern */
				0,	    /* nr */
				0,	    /* type */
				TRUE	    /* valid */
				) == FAIL)
		    {
			got_int = TRUE;
			break;
		    }
		    found_match = TRUE;
		    if (--tomatch == 0)
			break;
		    if ((flags & VGR_GLOBAL) == 0
					       || regmatch.endpos[0].lnum > 0)
			break;
		    col = regmatch.endpos[0].col
					    + (col == regmatch.endpos[0].col);
		    if (col > (colnr_T)STRLEN(ml_get_buf(buf, lnum, FALSE)))
			break;
		}
		line_breakcheck();
		if (got_int)
		    break;
	    }
#ifdef FEAT_AUTOCMD
	    cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start;
#endif

	    if (using_dummy)
	    {
		if (found_match && first_match_buf == NULL)
		    first_match_buf = buf;
		if (duplicate_name)
		{
		    /* Never keep a dummy buffer if there is another buffer
		     * with the same name. */
		    wipe_dummy_buffer(buf, dirname_start);
		    buf = NULL;
		}
		else if (!cmdmod.hide
			    || buf->b_p_bh[0] == 'u'	/* "unload" */
			    || buf->b_p_bh[0] == 'w'	/* "wipe" */
			    || buf->b_p_bh[0] == 'd')	/* "delete" */
		{
		    /* When no match was found we don't need to remember the
		     * buffer, wipe it out.  If there was a match and it
		     * wasn't the first one or we won't jump there: only
		     * unload the buffer.
		     * Ignore 'hidden' here, because it may lead to having too
		     * many swap files. */
		    if (!found_match)
		    {
			wipe_dummy_buffer(buf, dirname_start);
			buf = NULL;
		    }
		    else if (buf != first_match_buf || (flags & VGR_NOJUMP))
		    {
			unload_dummy_buffer(buf, dirname_start);
			buf = NULL;
		    }
		}

		if (buf != NULL)
		{
		    /* If the buffer is still loaded we need to use the
		     * directory we jumped to below. */
		    if (buf == first_match_buf
			    && target_dir == NULL
			    && STRCMP(dirname_start, dirname_now) != 0)
			target_dir = vim_strsave(dirname_now);

		    /* The buffer is still loaded, the Filetype autocommands
		     * need to be done now, in that buffer.  And the modelines
		     * need to be done (again).  But not the window-local
		     * options! */
		    aucmd_prepbuf(&aco, buf);
#if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
		    apply_autocmds(EVENT_FILETYPE, buf->b_p_ft,
						     buf->b_fname, TRUE, buf);
#endif
		    do_modelines(OPT_NOWIN);
		    aucmd_restbuf(&aco);
		}
	    }
	}
    }

    FreeWild(fcount, fnames);

    qi->qf_lists[qi->qf_curlist].qf_nonevalid = FALSE;
    qi->qf_lists[qi->qf_curlist].qf_ptr = qi->qf_lists[qi->qf_curlist].qf_start;
    qi->qf_lists[qi->qf_curlist].qf_index = 1;

#ifdef FEAT_WINDOWS
    qf_update_buffer(qi);
#endif

#ifdef FEAT_AUTOCMD
    if (au_name != NULL)
	apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
					       curbuf->b_fname, TRUE, curbuf);
#endif

    /* Jump to first match. */
    if (qi->qf_lists[qi->qf_curlist].qf_count > 0)
    {
	if ((flags & VGR_NOJUMP) == 0)
	{
	    buf = curbuf;
	    qf_jump(qi, 0, 0, eap->forceit);
	    if (buf != curbuf)
		/* If we jumped to another buffer redrawing will already be
		 * taken care of. */
		redraw_for_dummy = FALSE;

	    /* Jump to the directory used after loading the buffer. */
	    if (curbuf == first_match_buf && target_dir != NULL)
	    {
		exarg_T ea;

		ea.arg = target_dir;
		ea.cmdidx = CMD_lcd;
		ex_cd(&ea);
	    }
	}
    }
    else
	EMSG2(_(e_nomatch2), s);

    /* If we loaded a dummy buffer into the current window, the autocommands
     * may have messed up things, need to redraw and recompute folds. */
    if (redraw_for_dummy)
    {
#ifdef FEAT_FOLDING
	foldUpdateAll(curwin);
#else
	redraw_later(NOT_VALID);
#endif
    }

theend:
    vim_free(dirname_now);
    vim_free(dirname_start);
    vim_free(target_dir);
    vim_free(regmatch.regprog);
}

/*
 * Skip over the pattern argument of ":vimgrep /pat/[g][j]".
 * Put the start of the pattern in "*s", unless "s" is NULL.
 * If "flags" is not NULL put the flags in it: VGR_GLOBAL, VGR_NOJUMP.
 * If "s" is not NULL terminate the pattern with a NUL.
 * Return a pointer to the char just past the pattern plus flags.
 */
    char_u *
skip_vimgrep_pat(p, s, flags)
    char_u  *p;
    char_u  **s;
    int	    *flags;
{
    int		c;

    if (vim_isIDc(*p))
    {
	/* ":vimgrep pattern fname" */
	if (s != NULL)
	    *s = p;
	p = skiptowhite(p);
	if (s != NULL && *p != NUL)
	    *p++ = NUL;
    }
    else
    {
	/* ":vimgrep /pattern/[g][j] fname" */
	if (s != NULL)
	    *s = p + 1;
	c = *p;
	p = skip_regexp(p + 1, c, TRUE, NULL);
	if (*p != c)
	    return NULL;

	/* Truncate the pattern. */
	if (s != NULL)
	    *p = NUL;
	++p;

	/* Find the flags */
	while (*p == 'g' || *p == 'j')
	{
	    if (flags != NULL)
	    {
		if (*p == 'g')
		    *flags |= VGR_GLOBAL;
		else
		    *flags |= VGR_NOJUMP;
	    }
	    ++p;
	}
    }
    return p;
}

/*
 * Restore current working directory to "dirname_start" if they differ, taking
 * into account whether it is set locally or globally.
 */
    static void
restore_start_dir(dirname_start)
    char_u	*dirname_start;
{
    char_u *dirname_now = alloc(MAXPATHL);

    if (NULL != dirname_now)
    {
	mch_dirname(dirname_now, MAXPATHL);
	if (STRCMP(dirname_start, dirname_now) != 0)
	{
	    /* If the directory has changed, change it back by building up an
	     * appropriate ex command and executing it. */
	    exarg_T ea;

	    ea.arg = dirname_start;
	    ea.cmdidx = (curwin->w_localdir == NULL) ? CMD_cd : CMD_lcd;
	    ex_cd(&ea);
	}
	vim_free(dirname_now);
    }
}

/*
 * Load file "fname" into a dummy buffer and return the buffer pointer,
 * placing the directory resulting from the buffer load into the
 * "resulting_dir" pointer. "resulting_dir" must be allocated by the caller
 * prior to calling this function. Restores directory to "dirname_start" prior
 * to returning, if autocmds or the 'autochdir' option have changed it.
 *
 * If creating the dummy buffer does not fail, must call unload_dummy_buffer()
 * or wipe_dummy_buffer() later!
 *
 * Returns NULL if it fails.
 */
    static buf_T *
load_dummy_buffer(fname, dirname_start, resulting_dir)
    char_u	*fname;
    char_u	*dirname_start;  /* in: old directory */
    char_u	*resulting_dir;  /* out: new directory */
{
    buf_T	*newbuf;
    buf_T	*newbuf_to_wipe = NULL;
    int		failed = TRUE;
    aco_save_T	aco;

    /* Allocate a buffer without putting it in the buffer list. */
    newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY);
    if (newbuf == NULL)
	return NULL;

    /* Init the options. */
    buf_copy_options(newbuf, BCO_ENTER | BCO_NOHELP);

    /* need to open the memfile before putting the buffer in a window */
    if (ml_open(newbuf) == OK)
    {
	/* set curwin/curbuf to buf and save a few things */
	aucmd_prepbuf(&aco, newbuf);

	/* Need to set the filename for autocommands. */
	(void)setfname(curbuf, fname, NULL, FALSE);

	/* Create swap file now to avoid the ATTENTION message. */
	check_need_swap(TRUE);

	/* Remove the "dummy" flag, otherwise autocommands may not
	 * work. */
	curbuf->b_flags &= ~BF_DUMMY;

	if (readfile(fname, NULL,
		    (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
		    NULL, READ_NEW | READ_DUMMY) == OK
		&& !got_int
		&& !(curbuf->b_flags & BF_NEW))
	{
	    failed = FALSE;
	    if (curbuf != newbuf)
	    {
		/* Bloody autocommands changed the buffer!  Can happen when
		 * using netrw and editing a remote file.  Use the current
		 * buffer instead, delete the dummy one after restoring the
		 * window stuff. */
		newbuf_to_wipe = newbuf;
		newbuf = curbuf;
	    }
	}

	/* restore curwin/curbuf and a few other things */
	aucmd_restbuf(&aco);
	if (newbuf_to_wipe != NULL && buf_valid(newbuf_to_wipe))
	    wipe_buffer(newbuf_to_wipe, FALSE);
    }

    /*
     * When autocommands/'autochdir' option changed directory: go back.
     * Let the caller know what the resulting dir was first, in case it is
     * important.
     */
    mch_dirname(resulting_dir, MAXPATHL);
    restore_start_dir(dirname_start);

    if (!buf_valid(newbuf))
	return NULL;
    if (failed)
    {
	wipe_dummy_buffer(newbuf, dirname_start);
	return NULL;
    }
    return newbuf;
}

/*
 * Wipe out the dummy buffer that load_dummy_buffer() created. Restores
 * directory to "dirname_start" prior to returning, if autocmds or the
 * 'autochdir' option have changed it.
 */
    static void
wipe_dummy_buffer(buf, dirname_start)
    buf_T	*buf;
    char_u	*dirname_start;
{
    if (curbuf != buf)		/* safety check */
    {
#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
	cleanup_T   cs;

	/* Reset the error/interrupt/exception state here so that aborting()
	 * returns FALSE when wiping out the buffer.  Otherwise it doesn't
	 * work when got_int is set. */
	enter_cleanup(&cs);
#endif

	wipe_buffer(buf, FALSE);

#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
	/* Restore the error/interrupt/exception state if not discarded by a
	 * new aborting error, interrupt, or uncaught exception. */
	leave_cleanup(&cs);
#endif
	/* When autocommands/'autochdir' option changed directory: go back. */
	restore_start_dir(dirname_start);
    }
}

/*
 * Unload the dummy buffer that load_dummy_buffer() created. Restores
 * directory to "dirname_start" prior to returning, if autocmds or the
 * 'autochdir' option have changed it.
 */
    static void
unload_dummy_buffer(buf, dirname_start)
    buf_T	*buf;
    char_u	*dirname_start;
{
    if (curbuf != buf)		/* safety check */
    {
	close_buffer(NULL, buf, DOBUF_UNLOAD, FALSE);

	/* When autocommands/'autochdir' option changed directory: go back. */
	restore_start_dir(dirname_start);
    }
}

#if defined(FEAT_EVAL) || defined(PROTO)
/*
 * Add each quickfix error to list "list" as a dictionary.
 */
    int
get_errorlist(wp, list)
    win_T	*wp;
    list_T	*list;
{
    qf_info_T	*qi = &ql_info;
    dict_T	*dict;
    char_u	buf[2];
    qfline_T	*qfp;
    int		i;
    int		bufnum;

    if (wp != NULL)
    {
	qi = GET_LOC_LIST(wp);
	if (qi == NULL)
	    return FAIL;
    }

    if (qi->qf_curlist >= qi->qf_listcount
	    || qi->qf_lists[qi->qf_curlist].qf_count == 0)
	return FAIL;

    qfp = qi->qf_lists[qi->qf_curlist].qf_start;
    for (i = 1; !got_int && i <= qi->qf_lists[qi->qf_curlist].qf_count; ++i)
    {
	/* Handle entries with a non-existing buffer number. */
	bufnum = qfp->qf_fnum;
	if (bufnum != 0 && (buflist_findnr(bufnum) == NULL))
	    bufnum = 0;

	if ((dict = dict_alloc()) == NULL)
	    return FAIL;
	if (list_append_dict(list, dict) == FAIL)
	    return FAIL;

	buf[0] = qfp->qf_type;
	buf[1] = NUL;
	if ( dict_add_nr_str(dict, "bufnr", (long)bufnum, NULL) == FAIL
	  || dict_add_nr_str(dict, "lnum",  (long)qfp->qf_lnum, NULL) == FAIL
	  || dict_add_nr_str(dict, "col",   (long)qfp->qf_col, NULL) == FAIL
	  || dict_add_nr_str(dict, "vcol",  (long)qfp->qf_viscol, NULL) == FAIL
	  || dict_add_nr_str(dict, "nr",    (long)qfp->qf_nr, NULL) == FAIL
	  || dict_add_nr_str(dict, "pattern",  0L,
	     qfp->qf_pattern == NULL ? (char_u *)"" : qfp->qf_pattern) == FAIL
	  || dict_add_nr_str(dict, "text",  0L,
		   qfp->qf_text == NULL ? (char_u *)"" : qfp->qf_text) == FAIL
	  || dict_add_nr_str(dict, "type",  0L, buf) == FAIL
	  || dict_add_nr_str(dict, "valid", (long)qfp->qf_valid, NULL) == FAIL)
	    return FAIL;

	qfp = qfp->qf_next;
    }
    return OK;
}

/*
 * Populate the quickfix list with the items supplied in the list
 * of dictionaries. "title" will be copied to w:quickfix_title
 */
    int
set_errorlist(wp, list, action, title)
    win_T	*wp;
    list_T	*list;
    int		action;
    char_u	*title;
{
    listitem_T	*li;
    dict_T	*d;
    char_u	*filename, *pattern, *text, *type;
    int		bufnum;
    long	lnum;
    int		col, nr;
    int		vcol;
    qfline_T	*prevp = NULL;
    int		valid, status;
    int		retval = OK;
    qf_info_T	*qi = &ql_info;
    int		did_bufnr_emsg = FALSE;

    if (wp != NULL)
    {
	qi = ll_get_or_alloc_list(wp);
	if (qi == NULL)
	    return FAIL;
    }

    if (action == ' ' || qi->qf_curlist == qi->qf_listcount)
	/* make place for a new list */
	qf_new_list(qi, title);
    else if (action == 'a' && qi->qf_lists[qi->qf_curlist].qf_count > 0)
	/* Adding to existing list, find last entry. */
	for (prevp = qi->qf_lists[qi->qf_curlist].qf_start;
	     prevp->qf_next != prevp; prevp = prevp->qf_next)
	    ;
    else if (action == 'r')
	qf_free(qi, qi->qf_curlist);

    for (li = list->lv_first; li != NULL; li = li->li_next)
    {
	if (li->li_tv.v_type != VAR_DICT)
	    continue; /* Skip non-dict items */

	d = li->li_tv.vval.v_dict;
	if (d == NULL)
	    continue;

	filename = get_dict_string(d, (char_u *)"filename", TRUE);
	bufnum = get_dict_number(d, (char_u *)"bufnr");
	lnum = get_dict_number(d, (char_u *)"lnum");
	col = get_dict_number(d, (char_u *)"col");
	vcol = get_dict_number(d, (char_u *)"vcol");
	nr = get_dict_number(d, (char_u *)"nr");
	type = get_dict_string(d, (char_u *)"type", TRUE);
	pattern = get_dict_string(d, (char_u *)"pattern", TRUE);
	text = get_dict_string(d, (char_u *)"text", TRUE);
	if (text == NULL)
	    text = vim_strsave((char_u *)"");

	valid = TRUE;
	if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL))
	    valid = FALSE;

	/* Mark entries with non-existing buffer number as not valid. Give the
	 * error message only once. */
	if (bufnum != 0 && (buflist_findnr(bufnum) == NULL))
	{
	    if (!did_bufnr_emsg)
	    {
		did_bufnr_emsg = TRUE;
		EMSGN(_("E92: Buffer %ld not found"), bufnum);
	    }
	    valid = FALSE;
	    bufnum = 0;
	}

	status =  qf_add_entry(qi, &prevp,
			       NULL,	    /* dir */
			       filename,
			       bufnum,
			       text,
			       lnum,
			       col,
			       vcol,	    /* vis_col */
			       pattern,	    /* search pattern */
			       nr,
			       type == NULL ? NUL : *type,
			       valid);

	vim_free(filename);
	vim_free(pattern);
	vim_free(text);
	vim_free(type);

	if (status == FAIL)
	{
	    retval = FAIL;
	    break;
	}
    }

    if (qi->qf_lists[qi->qf_curlist].qf_index == 0)
	/* no valid entry */
	qi->qf_lists[qi->qf_curlist].qf_nonevalid = TRUE;
    else
	qi->qf_lists[qi->qf_curlist].qf_nonevalid = FALSE;
    qi->qf_lists[qi->qf_curlist].qf_ptr = qi->qf_lists[qi->qf_curlist].qf_start;
    qi->qf_lists[qi->qf_curlist].qf_index = 1;

#ifdef FEAT_WINDOWS
    qf_update_buffer(qi);
#endif

    return retval;
}
#endif

/*
 * ":[range]cbuffer [bufnr]" command.
 * ":[range]caddbuffer [bufnr]" command.
 * ":[range]cgetbuffer [bufnr]" command.
 * ":[range]lbuffer [bufnr]" command.
 * ":[range]laddbuffer [bufnr]" command.
 * ":[range]lgetbuffer [bufnr]" command.
 */
    void
ex_cbuffer(eap)
    exarg_T   *eap;
{
    buf_T	*buf = NULL;
    qf_info_T	*qi = &ql_info;

    if (eap->cmdidx == CMD_lbuffer || eap->cmdidx == CMD_lgetbuffer
	    || eap->cmdidx == CMD_laddbuffer)
    {
	qi = ll_get_or_alloc_list(curwin);
	if (qi == NULL)
	    return;
    }

    if (*eap->arg == NUL)
	buf = curbuf;
    else if (*skipwhite(skipdigits(eap->arg)) == NUL)
	buf = buflist_findnr(atoi((char *)eap->arg));
    if (buf == NULL)
	EMSG(_(e_invarg));
    else if (buf->b_ml.ml_mfp == NULL)
	EMSG(_("E681: Buffer is not loaded"));
    else
    {
	if (eap->addr_count == 0)
	{
	    eap->line1 = 1;
	    eap->line2 = buf->b_ml.ml_line_count;
	}
	if (eap->line1 < 1 || eap->line1 > buf->b_ml.ml_line_count
		|| eap->line2 < 1 || eap->line2 > buf->b_ml.ml_line_count)
	    EMSG(_(e_invrange));
	else
	{
	    char_u *qf_title = *eap->cmdlinep;

	    if (buf->b_sfname)
	    {
		vim_snprintf((char *)IObuff, IOSIZE, "%s (%s)",
				     (char *)qf_title, (char *)buf->b_sfname);
		qf_title = IObuff;
	    }

	    if (qf_init_ext(qi, NULL, buf, NULL, p_efm,
			    (eap->cmdidx != CMD_caddbuffer
			     && eap->cmdidx != CMD_laddbuffer),
						   eap->line1, eap->line2,
						   qf_title) > 0
		    && (eap->cmdidx == CMD_cbuffer
			|| eap->cmdidx == CMD_lbuffer))
		qf_jump(qi, 0, 0, eap->forceit);  /* display first error */
	}
    }
}

#if defined(FEAT_EVAL) || defined(PROTO)
/*
 * ":cexpr {expr}", ":cgetexpr {expr}", ":caddexpr {expr}" command.
 * ":lexpr {expr}", ":lgetexpr {expr}", ":laddexpr {expr}" command.
 */
    void
ex_cexpr(eap)
    exarg_T	*eap;
{
    typval_T	*tv;
    qf_info_T	*qi = &ql_info;

    if (eap->cmdidx == CMD_lexpr || eap->cmdidx == CMD_lgetexpr
	    || eap->cmdidx == CMD_laddexpr)
    {
	qi = ll_get_or_alloc_list(curwin);
	if (qi == NULL)
	    return;
    }

    /* Evaluate the expression.  When the result is a string or a list we can
     * use it to fill the errorlist. */
    tv = eval_expr(eap->arg, NULL);
    if (tv != NULL)
    {
	if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL)
		|| (tv->v_type == VAR_LIST && tv->vval.v_list != NULL))
	{
	    if (qf_init_ext(qi, NULL, NULL, tv, p_efm,
			    (eap->cmdidx != CMD_caddexpr
			     && eap->cmdidx != CMD_laddexpr),
				 (linenr_T)0, (linenr_T)0, *eap->cmdlinep) > 0
		    && (eap->cmdidx == CMD_cexpr
			|| eap->cmdidx == CMD_lexpr))
		qf_jump(qi, 0, 0, eap->forceit);  /* display first error */
	}
	else
	    EMSG(_("E777: String or List expected"));
	free_tv(tv);
    }
}
#endif

/*
 * ":helpgrep {pattern}"
 */
    void
ex_helpgrep(eap)
    exarg_T	*eap;
{
    regmatch_T	regmatch;
    char_u	*save_cpo;
    char_u	*p;
    int		fcount;
    char_u	**fnames;
    FILE	*fd;
    int		fi;
    qfline_T	*prevp = NULL;
    long	lnum;
#ifdef FEAT_MULTI_LANG
    char_u	*lang;
#endif
    qf_info_T	*qi = &ql_info;
    int		new_qi = FALSE;
    win_T	*wp;
#ifdef FEAT_AUTOCMD
    char_u	*au_name =  NULL;
#endif

#ifdef FEAT_MULTI_LANG
    /* Check for a specified language */
    lang = check_help_lang(eap->arg);
#endif

#ifdef FEAT_AUTOCMD
    switch (eap->cmdidx)
    {
	case CMD_helpgrep:  au_name = (char_u *)"helpgrep"; break;
	case CMD_lhelpgrep: au_name = (char_u *)"lhelpgrep"; break;
	default: break;
    }
    if (au_name != NULL)
    {
	apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
					       curbuf->b_fname, TRUE, curbuf);
	if (did_throw || force_abort)
	    return;
    }
#endif

    /* Make 'cpoptions' empty, the 'l' flag should not be used here. */
    save_cpo = p_cpo;
    p_cpo = empty_option;

    if (eap->cmdidx == CMD_lhelpgrep)
    {
	/* Find an existing help window */
	FOR_ALL_WINDOWS(wp)
	    if (wp->w_buffer != NULL && wp->w_buffer->b_help)
		break;

	if (wp == NULL)	    /* Help window not found */
	    qi = NULL;
	else
	    qi = wp->w_llist;

	if (qi == NULL)
	{
	    /* Allocate a new location list for help text matches */
	    if ((qi = ll_new_list()) == NULL)
		return;
	    new_qi = TRUE;
	}
    }

    regmatch.regprog = vim_regcomp(eap->arg, RE_MAGIC + RE_STRING);
    regmatch.rm_ic = FALSE;
    if (regmatch.regprog != NULL)
    {
#ifdef FEAT_MBYTE
	vimconv_T vc;

	/* Help files are in utf-8 or latin1, convert lines when 'encoding'
	 * differs. */
	vc.vc_type = CONV_NONE;
	if (!enc_utf8)
	    convert_setup(&vc, (char_u *)"utf-8", p_enc);
#endif

	/* create a new quickfix list */
	qf_new_list(qi, *eap->cmdlinep);

	/* Go through all directories in 'runtimepath' */
	p = p_rtp;
	while (*p != NUL && !got_int)
	{
	    copy_option_part(&p, NameBuff, MAXPATHL, ",");

	    /* Find all "*.txt" and "*.??x" files in the "doc" directory. */
	    add_pathsep(NameBuff);
	    STRCAT(NameBuff, "doc/*.\\(txt\\|??x\\)");
	    if (gen_expand_wildcards(1, &NameBuff, &fcount,
					     &fnames, EW_FILE|EW_SILENT) == OK
		    && fcount > 0)
	    {
		for (fi = 0; fi < fcount && !got_int; ++fi)
		{
#ifdef FEAT_MULTI_LANG
		    /* Skip files for a different language. */
		    if (lang != NULL
			    && STRNICMP(lang, fnames[fi]
					    + STRLEN(fnames[fi]) - 3, 2) != 0
			    && !(STRNICMP(lang, "en", 2) == 0
				&& STRNICMP("txt", fnames[fi]
					   + STRLEN(fnames[fi]) - 3, 3) == 0))
			    continue;
#endif
		    fd = mch_fopen((char *)fnames[fi], "r");
		    if (fd != NULL)
		    {
			lnum = 1;
			while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int)
			{
			    char_u    *line = IObuff;
#ifdef FEAT_MBYTE
			    /* Convert a line if 'encoding' is not utf-8 and
			     * the line contains a non-ASCII character. */
			    if (vc.vc_type != CONV_NONE
						   && has_non_ascii(IObuff)) {
				line = string_convert(&vc, IObuff, NULL);
				if (line == NULL)
				    line = IObuff;
			    }
#endif

			    if (vim_regexec(&regmatch, line, (colnr_T)0))
			    {
				int	l = (int)STRLEN(line);

				/* remove trailing CR, LF, spaces, etc. */
				while (l > 0 && line[l - 1] <= ' ')
				     line[--l] = NUL;

				if (qf_add_entry(qi, &prevp,
					    NULL,	/* dir */
					    fnames[fi],
					    0,
					    line,
					    lnum,
					    (int)(regmatch.startp[0] - line)
								+ 1, /* col */
					    FALSE,	/* vis_col */
					    NULL,	/* search pattern */
					    0,		/* nr */
					    1,		/* type */
					    TRUE	/* valid */
					    ) == FAIL)
				{
				    got_int = TRUE;
#ifdef FEAT_MBYTE
				    if (line != IObuff)
					vim_free(line);
#endif
				    break;
				}
			    }
#ifdef FEAT_MBYTE
			    if (line != IObuff)
				vim_free(line);
#endif
			    ++lnum;
			    line_breakcheck();
			}
			fclose(fd);
		    }
		}
		FreeWild(fcount, fnames);
	    }
	}

	vim_free(regmatch.regprog);
#ifdef FEAT_MBYTE
	if (vc.vc_type != CONV_NONE)
	    convert_setup(&vc, NULL, NULL);
#endif

	qi->qf_lists[qi->qf_curlist].qf_nonevalid = FALSE;
	qi->qf_lists[qi->qf_curlist].qf_ptr =
	    qi->qf_lists[qi->qf_curlist].qf_start;
	qi->qf_lists[qi->qf_curlist].qf_index = 1;
    }

    if (p_cpo == empty_option)
	p_cpo = save_cpo;
    else
	/* Darn, some plugin changed the value. */
	free_string_option(save_cpo);

#ifdef FEAT_WINDOWS
    qf_update_buffer(qi);
#endif

#ifdef FEAT_AUTOCMD
    if (au_name != NULL)
    {
	apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
					       curbuf->b_fname, TRUE, curbuf);
	if (!new_qi && qi != &ql_info && qf_find_buf(qi) == NULL)
	    /* autocommands made "qi" invalid */
	    return;
    }
#endif

    /* Jump to first match. */
    if (qi->qf_lists[qi->qf_curlist].qf_count > 0)
	qf_jump(qi, 0, 0, FALSE);
    else
	EMSG2(_(e_nomatch2), eap->arg);

    if (eap->cmdidx == CMD_lhelpgrep)
    {
	/* If the help window is not opened or if it already points to the
	 * correct location list, then free the new location list. */
	if (!curwin->w_buffer->b_help || curwin->w_llist == qi)
	{
	    if (new_qi)
		ll_free_all(&qi);
	}
	else if (curwin->w_llist == NULL)
	    curwin->w_llist = qi;
    }
}

#endif /* FEAT_QUICKFIX */