2281 lines · 67.0 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. */ /* * ex_eval.c: functions for Ex command line for the +eval feature. */ #include "vim.h" #if defined(FEAT_EVAL) || defined(PROTO) static void	free_msglist __ARGS((struct msglist *l));static int	throw_exception __ARGS((void *, int, char_u *));static char_u	*get_end_emsg __ARGS((struct condstack *cstack)); /* * Exception handling terms: * *	:try		":try" command		\ *	    ...		try block		| *	:catch RE	":catch" command	| *	    ...		catch clause		|- try conditional *	:finally	":finally" command	| *	    ...		finally clause		| *	:endtry		":endtry" command	/ * * The try conditional may have any number of catch clauses and at most one * finally clause.  A ":throw" command can be inside the try block, a catch * clause, the finally clause, or in a function called or script sourced from * there or even outside the try conditional.  Try conditionals may be nested. */ /* * Configuration whether an exception is thrown on error or interrupt.  When * the preprocessor macros below evaluate to FALSE, an error (did_emsg) or * interrupt (got_int) under an active try conditional terminates the script * after the non-active finally clauses of all active try conditionals have been * executed.  Otherwise, errors and/or interrupts are converted into catchable * exceptions (did_throw additionally set), which terminate the script only if * not caught.  For user exceptions, only did_throw is set.  (Note: got_int can * be set asyncronously afterwards by a SIGINT, so did_throw && got_int is not * a reliant test that the exception currently being thrown is an interrupt * exception.  Similarly, did_emsg can be set afterwards on an error in an * (unskipped) conditional command inside an inactive conditional, so did_throw * && did_emsg is not a reliant test that the exception currently being thrown * is an error exception.)  -  The macros can be defined as expressions checking * for a variable that is allowed to be changed during execution of a script. */#if 0/* Expressions used for testing during the development phase. */# define THROW_ON_ERROR		(!eval_to_number("$VIMNOERRTHROW"))# define THROW_ON_INTERRUPT	(!eval_to_number("$VIMNOINTTHROW"))# define THROW_TEST#else/* Values used for the Vim release. */# define THROW_ON_ERROR		TRUE# define THROW_ON_INTERRUPT	TRUE#endif static void	catch_exception __ARGS((except_T *excp));static void	finish_exception __ARGS((except_T *excp));static void	discard_exception __ARGS((except_T *excp, int was_finished));static void	report_pending __ARGS((int action, int pending, void *value)); /* * When several errors appear in a row, setting "force_abort" is delayed until * the failing command returned.  "cause_abort" is set to TRUE meanwhile, in * order to indicate that situation.  This is useful when "force_abort" was set * during execution of a function call from an expression: the aborting of the * expression evaluation is done without producing any error messages, but all * error messages on parsing errors during the expression evaluation are given * (even if a try conditional is active). */static int cause_abort = FALSE; /* * Return TRUE when immediately aborting on error, or when an interrupt * occurred or an exception was thrown but not caught.  Use for ":{range}call" * to check whether an aborted function that does not handle a range itself * should be called again for the next line in the range.  Also used for * cancelling expression evaluation after a function call caused an immediate * abort.  Note that the first emsg() call temporarily resets "force_abort" * until the throw point for error messages has been reached.  That is, during * cancellation of an expression evaluation after an aborting function call or * due to a parsing error, aborting() always returns the same value. */    intaborting(){    return (did_emsg && force_abort) || got_int || did_throw;} /* * The value of "force_abort" is temporarily reset by the first emsg() call * during an expression evaluation, and "cause_abort" is used instead.  It might * be necessary to restore "force_abort" even before the throw point for the * error message has been reached.  update_force_abort() should be called then. */    voidupdate_force_abort(){    if (cause_abort)	force_abort = TRUE;} /* * Return TRUE if a command with a subcommand resulting in "retcode" should * abort the script processing.  Can be used to suppress an autocommand after * execution of a failing subcommand as long as the error message has not been * displayed and actually caused the abortion. */    intshould_abort(retcode)    int		retcode;{    return ((retcode == FAIL && trylevel != 0 && !emsg_silent) || aborting());} /* * Return TRUE if a function with the "abort" flag should not be considered * ended on an error.  This means that parsing commands is continued in order * to find finally clauses to be executed, and that some errors in skipped * commands are still reported. */    intaborted_in_try(){    /* This function is only called after an error.  In this case, "force_abort"     * determines whether searching for finally clauses is necessary. */    return force_abort;} /* * cause_errthrow(): Cause a throw of an error exception if appropriate. * Return TRUE if the error message should not be displayed by emsg(). * Sets "ignore", if the emsg() call should be ignored completely. * * When several messages appear in the same command, the first is usually the * most specific one and used as the exception value.  The "severe" flag can be * set to TRUE, if a later but severer message should be used instead. */    intcause_errthrow(mesg, severe, ignore)    char_u	*mesg;    int		severe;    int		*ignore;{    struct msglist *elem;    struct msglist **plist;     /*     * Do nothing when displaying the interrupt message or reporting an     * uncaught exception (which has already been discarded then) at the top     * level.  Also when no exception can be thrown.  The message will be     * displayed by emsg().     */    if (suppress_errthrow)	return FALSE;     /*     * If emsg() has not been called previously, temporarily reset     * "force_abort" until the throw point for error messages has been     * reached.  This ensures that aborting() returns the same value for all     * errors that appear in the same command.  This means particularly that     * for parsing errors during expression evaluation emsg() will be called     * multiply, even when the expression is evaluated from a finally clause     * that was activated due to an aborting error, interrupt, or exception.     */    if (!did_emsg)    {	cause_abort = force_abort;	force_abort = FALSE;    }     /*     * If no try conditional is active and no exception is being thrown and     * there has not been an error in a try conditional or a throw so far, do     * nothing (for compatibility of non-EH scripts).  The message will then     * be displayed by emsg().  When ":silent!" was used and we are not     * currently throwing an exception, do nothing.  The message text will     * then be stored to v:errmsg by emsg() without displaying it.     */    if (((trylevel == 0 && !cause_abort) || emsg_silent) && !did_throw)	return FALSE;     /*     * Ignore an interrupt message when inside a try conditional or when an     * exception is being thrown or when an error in a try conditional or     * throw has been detected previously.  This is important in order that an     * interrupt exception is catchable by the innermost try conditional and     * not replaced by an interrupt message error exception.     */    if (mesg == (char_u *)_(e_interr))    {	*ignore = TRUE;	return TRUE;    }     /*     * Ensure that all commands in nested function calls and sourced files     * are aborted immediately.     */    cause_abort = TRUE;     /*     * When an exception is being thrown, some commands (like conditionals) are     * not skipped.  Errors in those commands may affect what of the subsequent     * commands are regarded part of catch and finally clauses.  Catching the     * exception would then cause execution of commands not intended by the     * user, who wouldn't even get aware of the problem.  Therefor, discard the     * exception currently being thrown to prevent it from being caught.  Just     * execute finally clauses and terminate.     */    if (did_throw)    {	/* When discarding an interrupt exception, reset got_int to prevent the	 * same interrupt being converted to an exception again and discarding	 * the error exception we are about to throw here. */	if (current_exception->type == ET_INTERRUPT)	    got_int = FALSE;	discard_current_exception();    } #ifdef THROW_TEST    if (!THROW_ON_ERROR)    {	/*	 * Print error message immediately without searching for a matching	 * catch clause; just finally clauses are executed before the script	 * is terminated.	 */	return FALSE;    }    else#endif    {	/*	 * Prepare the throw of an error exception, so that everything will	 * be aborted (except for executing finally clauses), until the error	 * exception is caught; if still uncaught at the top level, the error	 * message will be displayed and the script processing terminated	 * then.  -  This function has no access to the conditional stack.	 * Thus, the actual throw is made after the failing command has	 * returned.  -  Throw only the first of several errors in a row, except	 * a severe error is following.	 */	if (msg_list != NULL)	{	    plist = msg_list;	    while (*plist != NULL)		plist = &(*plist)->next; 	    elem = (struct msglist *)alloc((unsigned)sizeof(struct msglist));	    if (elem == NULL)	    {		suppress_errthrow = TRUE;		EMSG(_(e_outofmem));	    }	    else	    {		elem->msg = vim_strsave(mesg);		if (elem->msg == NULL)		{		    vim_free(elem);		    suppress_errthrow = TRUE;		    EMSG(_(e_outofmem));		}		else		{		    elem->next = NULL;		    elem->throw_msg = NULL;		    *plist = elem;		    if (plist == msg_list || severe)		    {			char_u	    *tmsg; 			/* Skip the extra "Vim " prefix for message "E458". */			tmsg = elem->msg;			if (STRNCMP(tmsg, "Vim E", 5) == 0				&& VIM_ISDIGIT(tmsg[5])				&& VIM_ISDIGIT(tmsg[6])				&& VIM_ISDIGIT(tmsg[7])				&& tmsg[8] == ':'				&& tmsg[9] == ' ')			    (*msg_list)->throw_msg = &tmsg[4];			else			    (*msg_list)->throw_msg = tmsg;		    }		}	    }	}	return TRUE;    }} /* * Free a "msg_list" and the messages it contains. */    static voidfree_msglist(l)    struct msglist  *l;{    struct msglist  *messages, *next;     messages = l;    while (messages != NULL)    {	next = messages->next;	vim_free(messages->msg);	vim_free(messages);	messages = next;    }} /* * Throw the message specified in the call to cause_errthrow() above as an * error exception.  If cstack is NULL, postpone the throw until do_cmdline() * has returned (see do_one_cmd()). */    voiddo_errthrow(cstack, cmdname)    struct condstack	*cstack;    char_u		*cmdname;{    /*     * Ensure that all commands in nested function calls and sourced files     * are aborted immediately.     */    if (cause_abort)    {	cause_abort = FALSE;	force_abort = TRUE;    }     /* If no exception is to be thrown or the conversion should be done after     * returning to a previous invocation of do_one_cmd(), do nothing. */    if (msg_list == NULL || *msg_list == NULL)	return;     if (throw_exception(*msg_list, ET_ERROR, cmdname) == FAIL)	free_msglist(*msg_list);    else    {	if (cstack != NULL)	    do_throw(cstack);	else	    need_rethrow = TRUE;    }    *msg_list = NULL;} /* * do_intthrow(): Replace the current exception by an interrupt or interrupt * exception if appropriate.  Return TRUE if the current exception is discarded, * FALSE otherwise. */    intdo_intthrow(cstack)    struct condstack	*cstack;{    /*     * If no interrupt occurred or no try conditional is active and no exception     * is being thrown, do nothing (for compatibility of non-EH scripts).     */    if (!got_int || (trylevel == 0 && !did_throw))	return FALSE; #ifdef THROW_TEST	/* avoid warning for condition always true */    if (!THROW_ON_INTERRUPT)    {	/*	 * The interrupt aborts everything except for executing finally clauses.	 * Discard any user or error or interrupt exception currently being	 * thrown.	 */	if (did_throw)	    discard_current_exception();    }    else#endif    {	/*	 * Throw an interrupt exception, so that everything will be aborted	 * (except for executing finally clauses), until the interrupt exception	 * is caught; if still uncaught at the top level, the script processing	 * will be terminated then.  -  If an interrupt exception is already	 * being thrown, do nothing.	 *	 */	if (did_throw)	{	    if (current_exception->type == ET_INTERRUPT)		return FALSE; 	    /* An interrupt exception replaces any user or error exception. */	    discard_current_exception();	}	if (throw_exception("Vim:Interrupt", ET_INTERRUPT, NULL) != FAIL)	    do_throw(cstack);    }     return TRUE;}  /* * Throw a new exception.  Return FAIL when out of memory or it was tried to * throw an illegal user exception.  "value" is the exception string for a user * or interrupt exception, or points to a message list in case of an error * exception. */    static intthrow_exception(value, type, cmdname)    void	*value;    int		type;    char_u	*cmdname;{    except_T	*excp;    char_u	*p, *mesg, *val;    int		cmdlen;     /*     * Disallow faking Interrupt or error exceptions as user exceptions.  They     * would be treated differently from real interrupt or error exceptions when     * no active try block is found, see do_cmdline().     */    if (type == ET_USER)    {	if (STRNCMP((char_u *)value, "Vim", 3) == 0 &&		(((char_u *)value)[3] == NUL || ((char_u *)value)[3] == ':' ||		 ((char_u *)value)[3] == '('))	{	    EMSG(_("E608: Cannot :throw exceptions with 'Vim' prefix"));	    goto fail;	}    }     excp = (except_T *)alloc((unsigned)sizeof(except_T));    if (excp == NULL)	goto nomem;     if (type == ET_ERROR)    {	/* Store the original message and prefix the exception value with	 * "Vim:" or, if a command name is given, "Vim(cmdname):". */	excp->messages = (struct msglist *)value;	mesg = excp->messages->throw_msg;	if (cmdname != NULL && *cmdname != NUL)	{	    cmdlen = (int)STRLEN(cmdname);	    excp->value = vim_strnsave((char_u *)"Vim(",					   4 + cmdlen + 2 + (int)STRLEN(mesg));	    if (excp->value == NULL)		goto nomem;	    STRCPY(&excp->value[4], cmdname);	    STRCPY(&excp->value[4 + cmdlen], "):");	    val = excp->value + 4 + cmdlen + 2;	}	else	{	    excp->value = vim_strnsave((char_u *)"Vim:", 4 + (int)STRLEN(mesg));	    if (excp->value == NULL)		goto nomem;	    val = excp->value + 4;	} 	/* msg_add_fname may have been used to prefix the message with a file	 * name in quotes.  In the exception value, put the file name in	 * parentheses and move it to the end. */	for (p = mesg; ; p++)	{	    if (*p == NUL		    || (*p == 'E'			&& VIM_ISDIGIT(p[1])			&& (p[2] == ':'			    || (VIM_ISDIGIT(p[2])				&& (p[3] == ':'				    || (VIM_ISDIGIT(p[3])					&& p[4] == ':'))))))	    {		if (*p == NUL || p == mesg)		    STRCAT(val, mesg);  /* 'E123' missing or at beginning */		else		{		    /* '"filename" E123: message text' */		    if (mesg[0] != '"' || p-2 < &mesg[1] ||			    p[-2] != '"' || p[-1] != ' ')			/* "E123:" is part of the file name. */			continue; 		    STRCAT(val, p);		    p[-2] = NUL;		    sprintf((char *)(val + STRLEN(p)), " (%s)", &mesg[1]);		    p[-2] = '"';

Lines 1–500 of 2281.