3849 lines · 92.2 KB · C
RawDownload
/* 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 */} 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));static void	qf_new_list __ARGS((qf_info_T *qi));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 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_WINDOWSstatic 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_fill_buffer __ARGS((qf_info_T *qi));#endifstatic char_u	*get_mef_name __ARGS((void));static buf_T	*load_dummy_buffer __ARGS((char_u *fname));static void	wipe_dummy_buffer __ARGS((buf_T *buf));static void	unload_dummy_buffer __ARGS((buf_T *buf));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. * Return -1 for error, number of errors for success. */    intqf_init(wp, efile, errorformat, newlist)    win_T	    *wp;    char_u	    *efile;    char_u	    *errorformat;    int		    newlist;		/* TRUE: start a new error list */{    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);} /* * 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. * Return -1 for error, number of errors for success. */    static intqf_init_ext(qi, efile, buf, tv, errorformat, newlist, lnumfirst, lnumlast)    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	    *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);    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)	{

Lines 1–500 of 3849.