diff --git a/src/quickfix.c b/src/quickfix.c
index 19363d358..c76e69629 100644
--- a/src/quickfix.c
+++ b/src/quickfix.c
@@ -134,24 +134,7 @@ struct efm_S
 
 static efm_T	*fmt_start = NULL; /* cached across qf_parse_line() calls */
 
-static void	qf_new_list(qf_info_T *qi, char_u *qf_title);
-static int	qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname, char_u *module, int bufnum, char_u *mesg, long lnum, int col, int vis_col, char_u *pattern, int nr, int type, int valid);
-static void	qf_free(qf_list_T *qfl);
-static char_u	*qf_types(int, int);
-static int	qf_get_fnum(qf_info_T *qi, int qf_idx, char_u *, char_u *);
-static char_u	*qf_push_dir(char_u *, struct dir_stack_T **, int is_file_stack);
-static char_u	*qf_pop_dir(struct dir_stack_T **);
-static char_u	*qf_guess_filepath(qf_list_T *qfl, char_u *);
-static void	qf_fmt_text(char_u *text, char_u *buf, int bufsize);
-static int	qf_win_pos_update(qf_info_T *qi, int old_qf_index);
-static win_T	*qf_find_win(qf_info_T *qi);
-static buf_T	*qf_find_buf(qf_info_T *qi);
 static void	qf_update_buffer(qf_info_T *qi, qfline_T *old_last);
-static void	qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last);
-static buf_T	*load_dummy_buffer(char_u *fname, char_u *dirname_start, char_u *resulting_dir);
-static void	wipe_dummy_buffer(buf_T *buf, char_u *dirname_start);
-static void	unload_dummy_buffer(buf_T *buf, char_u *dirname_start);
-static qf_info_T *ll_get_or_alloc_list(win_T *);
 
 /* Quickfix window check helper macro */
 #define IS_QF_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref == NULL)
@@ -575,6 +558,248 @@ parse_efm_end:
     return fmt_first;
 }
 
+/*
+ * Push dirbuf onto the directory stack and return pointer to actual dir or
+ * NULL on error.
+ */
+    static char_u *
+qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr, int is_file_stack)
+{
+    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 && is_file_stack))
+	(*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(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(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 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(qf_list_T *qfl, 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 (qfl->qf_dir_stack == NULL)
+	return NULL;
+
+    ds_ptr = qfl->qf_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 (qfl->qf_dir_stack->next != ds_ptr)
+    {
+	ds_tmp = qfl->qf_dir_stack->next;
+	qfl->qf_dir_stack->next = qfl->qf_dir_stack->next->next;
+	vim_free(ds_tmp->dirname);
+	vim_free(ds_tmp);
+    }
+
+    return ds_ptr == NULL ? NULL : ds_ptr->dirname;
+}
+
+/*
+ * Get buffer number for file "directory/fname".
+ * Also sets the b_has_qf_entry flag.
+ */
+    static int
+qf_get_fnum(qf_info_T *qi, int qf_idx, char_u *directory, char_u *fname)
+{
+    char_u	*ptr = NULL;
+    buf_T	*buf;
+    char_u	*bufname;
+
+    if (fname == NULL || *fname == NUL)		/* no file name */
+	return 0;
+
+#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(&qi->qf_lists[qf_idx], fname);
+	    if (directory)
+		ptr = concat_fnames(directory, fname, TRUE);
+	    else
+		ptr = vim_strsave(fname);
+	}
+	/* Use concatenated directory name and file name */
+	bufname = ptr;
+    }
+    else
+	bufname = fname;
+
+    if (qf_last_bufname != NULL && STRCMP(bufname, qf_last_bufname) == 0
+	    && bufref_valid(&qf_last_bufref))
+    {
+	buf = qf_last_bufref.br_buf;
+	vim_free(ptr);
+    }
+    else
+    {
+	vim_free(qf_last_bufname);
+	buf = buflist_new(bufname, NULL, (linenr_T)0, BLN_NOOPT);
+	if (bufname == ptr)
+	    qf_last_bufname = bufname;
+	else
+	    qf_last_bufname = vim_strsave(bufname);
+	set_bufref(&qf_last_bufref, buf);
+    }
+    if (buf == NULL)
+	return 0;
+
+    buf->b_has_qf_entry =
+			IS_QF_STACK(qi) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY;
+    return buf->b_fnum;
+}
+
 enum {
     QF_FAIL = 0,
     QF_OK = 1,
@@ -1498,6 +1723,205 @@ restofline:
     return QF_OK;
 }
 
+/*
+ * Allocate a new location list stack
+ */
+    static qf_info_T *
+ll_new_list(void)
+{
+    qf_info_T *qi;
+
+    qi = (qf_info_T *)alloc_clear((unsigned)sizeof(qf_info_T));
+    if (qi != NULL)
+	qi->qf_refcount++;
+    return qi;
+}
+
+/*
+ * Free all the entries in the error list "idx". Note that other information
+ * associated with the list like context and title are not freed.
+ */
+    static void
+qf_free_items(qf_list_T *qfl)
+{
+    qfline_T	*qfp;
+    qfline_T	*qfpnext;
+    int		stop = FALSE;
+
+    while (qfl->qf_count && qfl->qf_start != NULL)
+    {
+	qfp = qfl->qf_start;
+	qfpnext = qfp->qf_next;
+	if (!stop)
+	{
+	    vim_free(qfp->qf_module);
+	    vim_free(qfp->qf_text);
+	    vim_free(qfp->qf_pattern);
+	    stop = (qfp == qfpnext);
+	    vim_free(qfp);
+	    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. */
+		qfl->qf_count = 1;
+	}
+	qfl->qf_start = qfpnext;
+	--qfl->qf_count;
+    }
+
+    qfl->qf_index = 0;
+    qfl->qf_start = NULL;
+    qfl->qf_last = NULL;
+    qfl->qf_ptr = NULL;
+    qfl->qf_nonevalid = TRUE;
+
+    qf_clean_dir_stack(&qfl->qf_dir_stack);
+    qfl->qf_directory = NULL;
+    qf_clean_dir_stack(&qfl->qf_file_stack);
+    qfl->qf_currfile = NULL;
+    qfl->qf_multiline = FALSE;
+    qfl->qf_multiignore = FALSE;
+    qfl->qf_multiscan = FALSE;
+}
+
+/*
+ * Free error list "idx". Frees all the entries in the quickfix list,
+ * associated context information and the title.
+ */
+    static void
+qf_free(qf_list_T *qfl)
+{
+    qf_free_items(qfl);
+
+    VIM_CLEAR(qfl->qf_title);
+    free_tv(qfl->qf_ctx);
+    qfl->qf_ctx = NULL;
+    qfl->qf_id = 0;
+    qfl->qf_changedtick = 0L;
+}
+
+/*
+ * Free a location list stack
+ */
+    static void
+ll_free_all(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->qf_lists[i]);
+	vim_free(qi);
+    }
+}
+
+/*
+ * Free all the quickfix/location lists in the stack.
+ */
+    void
+qf_free_all(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->qf_lists[i]);
+}
+
+/*
+ * Set the title of the specified quickfix list. Frees the previous title.
+ * Prepends ':' to the title.
+ */
+    static void
+qf_store_title(qf_list_T *qfl, char_u *title)
+{
+    VIM_CLEAR(qfl->qf_title);
+
+    if (title != NULL)
+    {
+	char_u *p = alloc((int)STRLEN(title) + 2);
+
+	qfl->qf_title = p;
+	if (p != NULL)
+	    STRCPY(p, title);
+    }
+}
+
+/*
+ * Prepare for adding a new quickfix list. If the current list is in the
+ * middle of the stack, then all the following lists are freed and then
+ * the new list is added.
+ */
+    static void
+qf_new_list(qf_info_T *qi, char_u *qf_title)
+{
+    int		i;
+
+    /*
+     * If the current entry is not the last entry, delete entries beyond
+     * 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->qf_lists[--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->qf_lists[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)));
+    qf_store_title(&qi->qf_lists[qi->qf_curlist], qf_title);
+    qi->qf_lists[qi->qf_curlist].qf_id = ++last_qf_id;
+}
+
+/*
+ * Return the location list stack for window 'wp'.
+ * If not present, allocate a location list stack
+ */
+    static qf_info_T *
+ll_get_or_alloc_list(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;
+}
+
 /*
  * Returns TRUE if the specified quickfix/location list is empty.
  */
@@ -1509,6 +1933,102 @@ qf_list_empty(qf_info_T *qi, int qf_idx)
     return qi->qf_lists[qf_idx].qf_count <= 0;
 }
 
+/*
+ * Add an entry to the end of the list of errors.
+ * Returns OK or FAIL.
+ */
+    static int
+qf_add_entry(
+    qf_info_T	*qi,		/* quickfix list */
+    int		qf_idx,		/* list index */
+    char_u	*dir,		/* optional directory name */
+    char_u	*fname,		/* file name or NULL */
+    char_u	*module,	/* module 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 */
+{
+    qf_list_T	*qfl = &qi->qf_lists[qf_idx];
+    qfline_T	*qfp;
+    qfline_T	**lastp;	/* pointer to qf_last or NULL */
+
+    if ((qfp = (qfline_T *)alloc((unsigned)sizeof(qfline_T))) == NULL)
+	return FAIL;
+    if (bufnum != 0)
+    {
+	buf_T *buf = buflist_findnr(bufnum);
+
+	qfp->qf_fnum = bufnum;
+	if (buf != NULL)
+	    buf->b_has_qf_entry |=
+		IS_QF_STACK(qi) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY;
+    }
+    else
+	qfp->qf_fnum = qf_get_fnum(qi, qf_idx, 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;
+    }
+    if (module == NULL || *module == NUL)
+	qfp->qf_module = NULL;
+    else if ((qfp->qf_module = vim_strsave(module)) == NULL)
+    {
+	vim_free(qfp->qf_text);
+	vim_free(qfp->qf_pattern);
+	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;
+
+    lastp = &qfl->qf_last;
+    if (qf_list_empty(qi, qf_idx))	/* first element in the list */
+    {
+	qfl->qf_start = qfp;
+	qfl->qf_ptr = qfp;
+	qfl->qf_index = 0;
+	qfp->qf_prev = NULL;
+    }
+    else
+    {
+	qfp->qf_prev = *lastp;
+	(*lastp)->qf_next = qfp;
+    }
+    qfp->qf_next = NULL;
+    qfp->qf_cleared = FALSE;
+    *lastp = qfp;
+    ++qfl->qf_count;
+    if (qfl->qf_index == 0 && qfp->qf_valid)
+				/* first valid entry */
+    {
+	qfl->qf_index = qfl->qf_count;
+	qfl->qf_ptr = qfp;
+    }
+
+    return OK;
+}
+
 /*
  * Allocate the fields used for parsing lines and populating a quickfix list.
  */
@@ -1795,25 +2315,6 @@ qf_init(win_T	    *wp,
 	    newlist, (linenr_T)0, (linenr_T)0, qf_title, enc);
 }
 
-/*
- * Set the title of the specified quickfix list. Frees the previous title.
- * Prepends ':' to the title.
- */
-    static void
-qf_store_title(qf_list_T *qfl, char_u *title)
-{
-    VIM_CLEAR(qfl->qf_title);
-
-    if (title != NULL)
-    {
-	char_u *p = alloc((int)STRLEN(title) + 2);
-
-	qfl->qf_title = p;
-	if (p != NULL)
-	    STRCPY(p, title);
-    }
-}
-
 /*
  * The title of a quickfix/location list is set, by default, to the command
  * that created the quickfix list with the ":" prefix.
@@ -1829,219 +2330,6 @@ qf_cmdtitle(char_u *cmd)
     return qftitle_str;
 }
 
-/*
- * Prepare for adding a new quickfix list. If the current list is in the
- * middle of the stack, then all the following lists are freed and then
- * the new list is added.
- */
-    static void
-qf_new_list(qf_info_T *qi, char_u *qf_title)
-{
-    int		i;
-
-    /*
-     * If the current entry is not the last entry, delete entries beyond
-     * 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->qf_lists[--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->qf_lists[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)));
-    qf_store_title(&qi->qf_lists[qi->qf_curlist], qf_title);
-    qi->qf_lists[qi->qf_curlist].qf_id = ++last_qf_id;
-}
-
-/*
- * Free a location list stack
- */
-    static void
-ll_free_all(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->qf_lists[i]);
-	vim_free(qi);
-    }
-}
-
-/*
- * Free all the quickfix/location lists in the stack.
- */
-    void
-qf_free_all(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->qf_lists[i]);
-}
-
-/*
- * Add an entry to the end of the list of errors.
- * Returns OK or FAIL.
- */
-    static int
-qf_add_entry(
-    qf_info_T	*qi,		/* quickfix list */
-    int		qf_idx,		/* list index */
-    char_u	*dir,		/* optional directory name */
-    char_u	*fname,		/* file name or NULL */
-    char_u	*module,	/* module 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 */
-{
-    qf_list_T	*qfl = &qi->qf_lists[qf_idx];
-    qfline_T	*qfp;
-    qfline_T	**lastp;	/* pointer to qf_last or NULL */
-
-    if ((qfp = (qfline_T *)alloc((unsigned)sizeof(qfline_T))) == NULL)
-	return FAIL;
-    if (bufnum != 0)
-    {
-	buf_T *buf = buflist_findnr(bufnum);
-
-	qfp->qf_fnum = bufnum;
-	if (buf != NULL)
-	    buf->b_has_qf_entry |=
-		IS_QF_STACK(qi) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY;
-    }
-    else
-	qfp->qf_fnum = qf_get_fnum(qi, qf_idx, 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;
-    }
-    if (module == NULL || *module == NUL)
-	qfp->qf_module = NULL;
-    else if ((qfp->qf_module = vim_strsave(module)) == NULL)
-    {
-	vim_free(qfp->qf_text);
-	vim_free(qfp->qf_pattern);
-	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;
-
-    lastp = &qfl->qf_last;
-    if (qf_list_empty(qi, qf_idx))	/* first element in the list */
-    {
-	qfl->qf_start = qfp;
-	qfl->qf_ptr = qfp;
-	qfl->qf_index = 0;
-	qfp->qf_prev = NULL;
-    }
-    else
-    {
-	qfp->qf_prev = *lastp;
-	(*lastp)->qf_next = qfp;
-    }
-    qfp->qf_next = NULL;
-    qfp->qf_cleared = FALSE;
-    *lastp = qfp;
-    ++qfl->qf_count;
-    if (qfl->qf_index == 0 && qfp->qf_valid)
-				/* first valid entry */
-    {
-	qfl->qf_index = qfl->qf_count;
-	qfl->qf_ptr = qfp;
-    }
-
-    return OK;
-}
-
-/*
- * Allocate a new location list stack
- */
-    static qf_info_T *
-ll_new_list(void)
-{
-    qf_info_T *qi;
-
-    qi = (qf_info_T *)alloc_clear((unsigned)sizeof(qf_info_T));
-    if (qi != NULL)
-	qi->qf_refcount++;
-    return qi;
-}
-
-/*
- * Return the location list stack for window 'wp'.
- * If not present, allocate a location list stack
- */
-    static qf_info_T *
-ll_get_or_alloc_list(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 location list entries from 'from_qfl' to 'to_qfl'.
  */
@@ -2175,248 +2463,6 @@ copy_loclist_stack(win_T *from, win_T *to)
     to->w_llist->qf_curlist = qi->qf_curlist;	// current list
 }
 
-/*
- * Get buffer number for file "directory/fname".
- * Also sets the b_has_qf_entry flag.
- */
-    static int
-qf_get_fnum(qf_info_T *qi, int qf_idx, char_u *directory, char_u *fname)
-{
-    char_u	*ptr = NULL;
-    buf_T	*buf;
-    char_u	*bufname;
-
-    if (fname == NULL || *fname == NUL)		/* no file name */
-	return 0;
-
-#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(&qi->qf_lists[qf_idx], fname);
-	    if (directory)
-		ptr = concat_fnames(directory, fname, TRUE);
-	    else
-		ptr = vim_strsave(fname);
-	}
-	/* Use concatenated directory name and file name */
-	bufname = ptr;
-    }
-    else
-	bufname = fname;
-
-    if (qf_last_bufname != NULL && STRCMP(bufname, qf_last_bufname) == 0
-	    && bufref_valid(&qf_last_bufref))
-    {
-	buf = qf_last_bufref.br_buf;
-	vim_free(ptr);
-    }
-    else
-    {
-	vim_free(qf_last_bufname);
-	buf = buflist_new(bufname, NULL, (linenr_T)0, BLN_NOOPT);
-	if (bufname == ptr)
-	    qf_last_bufname = bufname;
-	else
-	    qf_last_bufname = vim_strsave(bufname);
-	set_bufref(&qf_last_bufref, buf);
-    }
-    if (buf == NULL)
-	return 0;
-
-    buf->b_has_qf_entry =
-			IS_QF_STACK(qi) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY;
-    return buf->b_fnum;
-}
-
-/*
- * Push dirbuf onto the directory stack and return pointer to actual dir or
- * NULL on error.
- */
-    static char_u *
-qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr, int is_file_stack)
-{
-    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 && is_file_stack))
-	(*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(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(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 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(qf_list_T *qfl, 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 (qfl->qf_dir_stack == NULL)
-	return NULL;
-
-    ds_ptr = qfl->qf_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 (qfl->qf_dir_stack->next != ds_ptr)
-    {
-	ds_tmp = qfl->qf_dir_stack->next;
-	qfl->qf_dir_stack->next = qfl->qf_dir_stack->next->next;
-	vim_free(ds_tmp->dirname);
-	vim_free(ds_tmp);
-    }
-
-    return ds_ptr == NULL ? NULL : ds_ptr->dirname;
-}
-
 /*
  * Returns TRUE if a quickfix/location list with the given identifier exists.
  */
@@ -2857,6 +2903,188 @@ qf_goto_win_with_qfl_file(int qf_fnum)
     win_goto(win);
 }
 
+/*
+ * Remove newlines and leading whitespace from an error message.
+ * Put the result in "buf[bufsize]".
+ */
+    static void
+qf_fmt_text(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;
+}
+
+/*
+ * Check whether the given window is displaying the specified quickfix/location
+ * stack.
+ */
+    static int
+is_qf_win(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 ((IS_QF_STACK(qi) && win->w_llist_ref == NULL)
+		|| (IS_LL_STACK(qi) && win->w_llist_ref == qi))
+	    return TRUE;
+
+    return FALSE;
+}
+
+/*
+ * Find a window displaying the quickfix/location stack 'qi'
+ * Only searches in the current tabpage.
+ */
+    static win_T *
+qf_find_win(qf_info_T *qi)
+{
+    win_T	*win;
+
+    FOR_ALL_WINDOWS(win)
+	if (is_qf_win(win, qi))
+	    return win;
+    return NULL;
+}
+
+/*
+ * Find a quickfix buffer.
+ * Searches in windows opened in all the tabs.
+ */
+    static buf_T *
+qf_find_buf(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;
+}
+
+/*
+ * Move the cursor in the quickfix window to "lnum".
+ */
+    static void
+qf_win_goto(win_T *win, linenr_T lnum)
+{
+    win_T	*old_curwin = curwin;
+
+    curwin = win;
+    curbuf = win->w_buffer;
+    curwin->w_cursor.lnum = lnum;
+    curwin->w_cursor.col = 0;
+#ifdef FEAT_VIRTUALEDIT
+    curwin->w_cursor.coladd = 0;
+#endif
+    curwin->w_curswant = 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;
+}
+
+/*
+ * 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(
+    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)
+    {
+	if (qf_index > old_qf_index)
+	{
+	    win->w_redraw_top = old_qf_index;
+	    win->w_redraw_bot = qf_index;
+	}
+	else
+	{
+	    win->w_redraw_top = qf_index;
+	    win->w_redraw_bot = old_qf_index;
+	}
+	qf_win_goto(win, qf_index);
+    }
+    return win != NULL;
+}
+
+/*
+ * 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(int c, int 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;
+}
+
 /*
  * Find a suitable window for opening a file (qf_fnum) from the
  * quickfix/location list and jump to it.  If the file is already opened in a
@@ -3482,31 +3710,6 @@ qf_list(exarg_T *eap)
     }
 }
 
-/*
- * Remove newlines and leading whitespace from an error message.
- * Put the result in "buf[bufsize]".
- */
-    static void
-qf_fmt_text(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;
-}
-
 /*
  * Display information (list number, list size and the title) about a
  * quickfix/location list.
@@ -3609,69 +3812,6 @@ qf_history(exarg_T *eap)
 	    qf_msg(qi, i, i == qi->qf_curlist ? "> " : "  ");
 }
 
-/*
- * Free all the entries in the error list "idx". Note that other information
- * associated with the list like context and title are not freed.
- */
-    static void
-qf_free_items(qf_list_T *qfl)
-{
-    qfline_T	*qfp;
-    qfline_T	*qfpnext;
-    int		stop = FALSE;
-
-    while (qfl->qf_count && qfl->qf_start != NULL)
-    {
-	qfp = qfl->qf_start;
-	qfpnext = qfp->qf_next;
-	if (!stop)
-	{
-	    vim_free(qfp->qf_module);
-	    vim_free(qfp->qf_text);
-	    vim_free(qfp->qf_pattern);
-	    stop = (qfp == qfpnext);
-	    vim_free(qfp);
-	    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. */
-		qfl->qf_count = 1;
-	}
-	qfl->qf_start = qfpnext;
-	--qfl->qf_count;
-    }
-
-    qfl->qf_index = 0;
-    qfl->qf_start = NULL;
-    qfl->qf_last = NULL;
-    qfl->qf_ptr = NULL;
-    qfl->qf_nonevalid = TRUE;
-
-    qf_clean_dir_stack(&qfl->qf_dir_stack);
-    qfl->qf_directory = NULL;
-    qf_clean_dir_stack(&qfl->qf_file_stack);
-    qfl->qf_currfile = NULL;
-    qfl->qf_multiline = FALSE;
-    qfl->qf_multiignore = FALSE;
-    qfl->qf_multiscan = FALSE;
-}
-
-/*
- * Free error list "idx". Frees all the entries in the quickfix list,
- * associated context information and the title.
- */
-    static void
-qf_free(qf_list_T *qfl)
-{
-    qf_free_items(qfl);
-
-    VIM_CLEAR(qfl->qf_title);
-    free_tv(qfl->qf_ctx);
-    qfl->qf_ctx = NULL;
-    qfl->qf_id = 0;
-    qfl->qf_changedtick = 0L;
-}
-
 /*
  * qf_mark_adjust: adjust marks
  */
@@ -3722,51 +3862,6 @@ qf_mark_adjust(
 	curbuf->b_has_qf_entry &= ~buf_has_flag;
 }
 
-/*
- * 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(int c, int 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;
-}
-
 /*
  * When "split" is FALSE: Open the entry/result under the cursor.
  * When "split" is TRUE: Open the entry/result under the cursor in a new window.
@@ -3980,266 +4075,6 @@ qf_open_new_cwindow(qf_info_T *qi, int height)
     return OK;
 }
 
-/*
- * ":copen": open a window that shows the list of errors.
- * ":lopen": open a window that shows the location list.
- */
-    void
-ex_copen(exarg_T *eap)
-{
-    qf_info_T	*qi = &ql_info;
-    int		height;
-    int		status = FAIL;
-
-    if (is_loclist_cmd(eap->cmdidx))
-    {
-	qi = GET_LOC_LIST(curwin);
-	if (qi == NULL)
-	{
-	    EMSG(_(e_loclist));
-	    return;
-	}
-    }
-
-    if (eap->addr_count != 0)
-	height = eap->line2;
-    else
-	height = QF_WINHEIGHT;
-
-    reset_VIsual_and_resel();			// stop Visual mode
-#ifdef FEAT_GUI
-    need_mouse_correct = TRUE;
-#endif
-
-    // Find an existing quickfix window, or open a new one.
-    if (cmdmod.tab == 0)
-	status = qf_goto_cwindow(qi, eap->addr_count != 0, height,
-						cmdmod.split & WSP_VERT);
-    if (status == FAIL)
-	if (qf_open_new_cwindow(qi, height) == FAIL)
-	    return;
-
-    qf_set_title_var(&qi->qf_lists[qi->qf_curlist]);
-
-    // Fill the buffer with the quickfix list.
-    qf_fill_buffer(qi, curbuf, NULL);
-
-    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
-}
-
-/*
- * Move the cursor in the quickfix window to "lnum".
- */
-    static void
-qf_win_goto(win_T *win, linenr_T lnum)
-{
-    win_T	*old_curwin = curwin;
-
-    curwin = win;
-    curbuf = win->w_buffer;
-    curwin->w_cursor.lnum = lnum;
-    curwin->w_cursor.col = 0;
-#ifdef FEAT_VIRTUALEDIT
-    curwin->w_cursor.coladd = 0;
-#endif
-    curwin->w_curswant = 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;
-}
-
-/*
- * :cbottom/:lbottom commands.
- */
-    void
-ex_cbottom(exarg_T *eap)
-{
-    qf_info_T	*qi = &ql_info;
-    win_T	*win;
-
-    if (is_loclist_cmd(eap->cmdidx))
-    {
-	qi = GET_LOC_LIST(curwin);
-	if (qi == NULL)
-	{
-	    EMSG(_(e_loclist));
-	    return;
-	}
-    }
-
-    win = qf_find_win(qi);
-    if (win != NULL && win->w_cursor.lnum != win->w_buffer->b_ml.ml_line_count)
-	qf_win_goto(win, win->w_buffer->b_ml.ml_line_count);
-}
-
-/*
- * Return the number of the current entry (line number in the quickfix
- * window).
- */
-     linenr_T
-qf_current_entry(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(
-    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)
-    {
-	if (qf_index > old_qf_index)
-	{
-	    win->w_redraw_top = old_qf_index;
-	    win->w_redraw_bot = qf_index;
-	}
-	else
-	{
-	    win->w_redraw_top = qf_index;
-	    win->w_redraw_bot = old_qf_index;
-	}
-	qf_win_goto(win, qf_index);
-    }
-    return win != NULL;
-}
-
-/*
- * Check whether the given window is displaying the specified quickfix/location
- * stack.
- */
-    static int
-is_qf_win(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 ((IS_QF_STACK(qi) && win->w_llist_ref == NULL)
-		|| (IS_LL_STACK(qi) && win->w_llist_ref == qi))
-	    return TRUE;
-
-    return FALSE;
-}
-
-/*
- * Find a window displaying the quickfix/location stack 'qi'
- * Only searches in the current tabpage.
- */
-    static win_T *
-qf_find_win(qf_info_T *qi)
-{
-    win_T	*win;
-
-    FOR_ALL_WINDOWS(win)
-	if (is_qf_win(win, qi))
-	    return win;
-    return NULL;
-}
-
-/*
- * Find a quickfix buffer.
- * Searches in windows opened in all the tabs.
- */
-    static buf_T *
-qf_find_buf(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;
-}
-
-/*
- * Update the w:quickfix_title variable in the quickfix/location list window
- */
-    static void
-qf_update_win_titlevar(qf_info_T *qi)
-{
-    win_T	*win;
-    win_T	*curwin_save;
-
-    if ((win = qf_find_win(qi)) != NULL)
-    {
-	curwin_save = curwin;
-	curwin = win;
-	qf_set_title_var(&qi->qf_lists[qi->qf_curlist]);
-	curwin = curwin_save;
-    }
-}
-
-/*
- * Find the quickfix buffer.  If it exists, update the contents.
- */
-    static void
-qf_update_buffer(qf_info_T *qi, qfline_T *old_last)
-{
-    buf_T	*buf;
-    win_T	*win;
-    aco_save_T	aco;
-
-    /* Check if a buffer for the quickfix list exists.  Update it. */
-    buf = qf_find_buf(qi);
-    if (buf != NULL)
-    {
-	linenr_T	old_line_count = buf->b_ml.ml_line_count;
-
-	if (old_last == NULL)
-	    /* set curwin/curbuf to buf and save a few things */
-	    aucmd_prepbuf(&aco, buf);
-
-	qf_update_win_titlevar(qi);
-
-	qf_fill_buffer(qi, buf, old_last);
-	++CHANGEDTICK(buf);
-
-	if (old_last == NULL)
-	{
-	    (void)qf_win_pos_update(qi, 0);
-
-	    /* restore curwin/curbuf and a few other things */
-	    aucmd_restbuf(&aco);
-	}
-
-	/* Only redraw when added lines are visible.  This avoids flickering
-	 * when the added lines are not visible. */
-	if ((win = qf_find_win(qi)) != NULL && old_line_count < win->w_botline)
-	    redraw_buf_later(buf, NOT_VALID);
-    }
-}
-
 /*
  * Add an error line to the quickfix buffer.
  */
@@ -4403,6 +4238,154 @@ qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last)
     KeyTyped = old_KeyTyped;
 }
 
+/*
+ * ":copen": open a window that shows the list of errors.
+ * ":lopen": open a window that shows the location list.
+ */
+    void
+ex_copen(exarg_T *eap)
+{
+    qf_info_T	*qi = &ql_info;
+    int		height;
+    int		status = FAIL;
+
+    if (is_loclist_cmd(eap->cmdidx))
+    {
+	qi = GET_LOC_LIST(curwin);
+	if (qi == NULL)
+	{
+	    EMSG(_(e_loclist));
+	    return;
+	}
+    }
+
+    if (eap->addr_count != 0)
+	height = eap->line2;
+    else
+	height = QF_WINHEIGHT;
+
+    reset_VIsual_and_resel();			// stop Visual mode
+#ifdef FEAT_GUI
+    need_mouse_correct = TRUE;
+#endif
+
+    // Find an existing quickfix window, or open a new one.
+    if (cmdmod.tab == 0)
+	status = qf_goto_cwindow(qi, eap->addr_count != 0, height,
+						cmdmod.split & WSP_VERT);
+    if (status == FAIL)
+	if (qf_open_new_cwindow(qi, height) == FAIL)
+	    return;
+
+    qf_set_title_var(&qi->qf_lists[qi->qf_curlist]);
+
+    // Fill the buffer with the quickfix list.
+    qf_fill_buffer(qi, curbuf, NULL);
+
+    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
+}
+
+/*
+ * :cbottom/:lbottom commands.
+ */
+    void
+ex_cbottom(exarg_T *eap)
+{
+    qf_info_T	*qi = &ql_info;
+    win_T	*win;
+
+    if (is_loclist_cmd(eap->cmdidx))
+    {
+	qi = GET_LOC_LIST(curwin);
+	if (qi == NULL)
+	{
+	    EMSG(_(e_loclist));
+	    return;
+	}
+    }
+
+    win = qf_find_win(qi);
+    if (win != NULL && win->w_cursor.lnum != win->w_buffer->b_ml.ml_line_count)
+	qf_win_goto(win, win->w_buffer->b_ml.ml_line_count);
+}
+
+/*
+ * Return the number of the current entry (line number in the quickfix
+ * window).
+ */
+     linenr_T
+qf_current_entry(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 w:quickfix_title variable in the quickfix/location list window
+ */
+    static void
+qf_update_win_titlevar(qf_info_T *qi)
+{
+    win_T	*win;
+    win_T	*curwin_save;
+
+    if ((win = qf_find_win(qi)) != NULL)
+    {
+	curwin_save = curwin;
+	curwin = win;
+	qf_set_title_var(&qi->qf_lists[qi->qf_curlist]);
+	curwin = curwin_save;
+    }
+}
+
+/*
+ * Find the quickfix buffer.  If it exists, update the contents.
+ */
+    static void
+qf_update_buffer(qf_info_T *qi, qfline_T *old_last)
+{
+    buf_T	*buf;
+    win_T	*win;
+    aco_save_T	aco;
+
+    /* Check if a buffer for the quickfix list exists.  Update it. */
+    buf = qf_find_buf(qi);
+    if (buf != NULL)
+    {
+	linenr_T	old_line_count = buf->b_ml.ml_line_count;
+
+	if (old_last == NULL)
+	    /* set curwin/curbuf to buf and save a few things */
+	    aucmd_prepbuf(&aco, buf);
+
+	qf_update_win_titlevar(qi);
+
+	qf_fill_buffer(qi, buf, old_last);
+	++CHANGEDTICK(buf);
+
+	if (old_last == NULL)
+	{
+	    (void)qf_win_pos_update(qi, 0);
+
+	    /* restore curwin/curbuf and a few other things */
+	    aucmd_restbuf(&aco);
+	}
+
+	/* Only redraw when added lines are visible.  This avoids flickering
+	 * when the added lines are not visible. */
+	if ((win = qf_find_win(qi)) != NULL && old_line_count < win->w_botline)
+	    redraw_buf_later(buf, NOT_VALID);
+    }
+}
+
 /*
  * For every change made to the quickfix list, update the changed tick.
  */
@@ -5013,6 +4996,182 @@ ex_cfile(exarg_T *eap)
 	qf_jump_first(qi, save_qfid, eap->forceit);
 }
 
+/*
+ * 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(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);
+    }
+}
+
+/*
+ * 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_T *buf, char_u *dirname_start)
+{
+    if (curbuf != buf)		/* safety check */
+    {
+#if 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_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);
+    }
+}
+
+/*
+ * 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(
+    char_u	*fname,
+    char_u	*dirname_start,  /* in: old directory */
+    char_u	*resulting_dir)  /* out: new directory */
+{
+    buf_T	*newbuf;
+    bufref_T	newbufref;
+    bufref_T	newbuf_to_wipe;
+    int		failed = TRUE;
+    aco_save_T	aco;
+    int		readfile_result;
+
+    /* 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;
+    set_bufref(&newbufref, newbuf);
+
+    /* 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)
+    {
+	/* Make sure this buffer isn't wiped out by autocommands. */
+	++newbuf->b_locked;
+
+	/* 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;
+
+	newbuf_to_wipe.br_buf = NULL;
+	readfile_result = readfile(fname, NULL,
+		    (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
+		    NULL, READ_NEW | READ_DUMMY);
+	--newbuf->b_locked;
+	if (readfile_result == 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. */
+		set_bufref(&newbuf_to_wipe, newbuf);
+		newbuf = curbuf;
+	    }
+	}
+
+	/* restore curwin/curbuf and a few other things */
+	aucmd_restbuf(&aco);
+	if (newbuf_to_wipe.br_buf != NULL && bufref_valid(&newbuf_to_wipe))
+	    wipe_buffer(newbuf_to_wipe.br_buf, FALSE);
+
+	/* Add back the "dummy" flag, otherwise buflist_findname_stat() won't
+	 * skip it. */
+	newbuf->b_flags |= BF_DUMMY;
+    }
+
+    /*
+     * 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 (!bufref_valid(&newbufref))
+	return NULL;
+    if (failed)
+    {
+	wipe_dummy_buffer(newbuf, dirname_start);
+	return NULL;
+    }
+    return newbuf;
+}
+
+/*
+ * 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_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);
+    }
+}
+
 /*
  * Return the vimgrep autocmd name.
  */
@@ -5524,182 +5683,6 @@ theend:
     vim_regfree(regmatch.regprog);
 }
 
-/*
- * 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(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(
-    char_u	*fname,
-    char_u	*dirname_start,  /* in: old directory */
-    char_u	*resulting_dir)  /* out: new directory */
-{
-    buf_T	*newbuf;
-    bufref_T	newbufref;
-    bufref_T	newbuf_to_wipe;
-    int		failed = TRUE;
-    aco_save_T	aco;
-    int		readfile_result;
-
-    /* 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;
-    set_bufref(&newbufref, newbuf);
-
-    /* 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)
-    {
-	/* Make sure this buffer isn't wiped out by autocommands. */
-	++newbuf->b_locked;
-
-	/* 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;
-
-	newbuf_to_wipe.br_buf = NULL;
-	readfile_result = readfile(fname, NULL,
-		    (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
-		    NULL, READ_NEW | READ_DUMMY);
-	--newbuf->b_locked;
-	if (readfile_result == 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. */
-		set_bufref(&newbuf_to_wipe, newbuf);
-		newbuf = curbuf;
-	    }
-	}
-
-	/* restore curwin/curbuf and a few other things */
-	aucmd_restbuf(&aco);
-	if (newbuf_to_wipe.br_buf != NULL && bufref_valid(&newbuf_to_wipe))
-	    wipe_buffer(newbuf_to_wipe.br_buf, FALSE);
-
-	/* Add back the "dummy" flag, otherwise buflist_findname_stat() won't
-	 * skip it. */
-	newbuf->b_flags |= BF_DUMMY;
-    }
-
-    /*
-     * 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 (!bufref_valid(&newbufref))
-	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_T *buf, char_u *dirname_start)
-{
-    if (curbuf != buf)		/* safety check */
-    {
-#if 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_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_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.
