/* 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. Load more
/* 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. */ int aborting() { 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. */ void update_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. */ int should_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. */ int aborted_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. */ int cause_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 void free_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()). */ void do_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. */ int do_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 int throw_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] = '"';