/* 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] = '"'; } break; } } } else excp->value = value; excp->type = type; excp->throw_name = vim_strsave(sourcing_name == NULL ? (char_u *)"" : sourcing_name); if (excp->throw_name == NULL) { if (type == ET_ERROR) vim_free(excp->value); goto nomem; } excp->throw_lnum = sourcing_lnum; if (p_verbose >= 13 || debug_break_level > 0) { int save_msg_silent = msg_silent; if (debug_break_level > 0) msg_silent = FALSE; /* display messages */ else verbose_enter(); ++no_wait_return; if (debug_break_level > 0 || *p_vfile == NUL) msg_scroll = TRUE; /* always scroll up, don't overwrite */ smsg((char_u *)_("Exception thrown: %s"), excp->value); msg_puts((char_u *)"\n"); /* don't overwrite this either */ if (debug_break_level > 0 || *p_vfile == NUL) cmdline_row = msg_row; --no_wait_return; if (debug_break_level > 0) msg_silent = save_msg_silent; else verbose_leave(); } current_exception = excp; return OK; nomem: vim_free(excp); suppress_errthrow = TRUE; EMSG(_(e_outofmem)); fail: current_exception = NULL; return FAIL; } /* * Discard an exception. "was_finished" is set when the exception has been * caught and the catch clause has been ended normally. */ static void discard_exception(excp, was_finished) except_T *excp; int was_finished; { char_u *saved_IObuff; if (excp == NULL) { EMSG(_(e_internal)); return; } if (p_verbose >= 13 || debug_break_level > 0) { int save_msg_silent = msg_silent; saved_IObuff = vim_strsave(IObuff); if (debug_break_level > 0) msg_silent = FALSE; /* display messages */ else verbose_enter(); ++no_wait_return; if (debug_break_level > 0 || *p_vfile == NUL) msg_scroll = TRUE; /* always scroll up, don't overwrite */ smsg(was_finished ? (char_u *)_("Exception finished: %s") : (char_u *)_("Exception discarded: %s"), excp->value); msg_puts((char_u *)"\n"); /* don't overwrite this either */ if (debug_break_level > 0 || *p_vfile == NUL) cmdline_row = msg_row; --no_wait_return; if (debug_break_level > 0) msg_silent = save_msg_silent; else verbose_leave(); STRCPY(IObuff, saved_IObuff); vim_free(saved_IObuff); } if (excp->type != ET_INTERRUPT) vim_free(excp->value); if (excp->type == ET_ERROR) free_msglist(excp->messages); vim_free(excp->throw_name); vim_free(excp); } /* * Discard the exception currently being thrown. */ void discard_current_exception() { discard_exception(current_exception, FALSE); current_exception = NULL; did_throw = FALSE; need_rethrow = FALSE; } /* * Put an exception on the caught stack. */ static void catch_exception(excp) except_T *excp; { excp->caught = caught_stack; caught_stack = excp; set_vim_var_string(VV_EXCEPTION, excp->value, -1); if (*excp->throw_name != NUL) { if (excp->throw_lnum != 0) vim_snprintf((char *)IObuff, IOSIZE, _("%s, line %ld"), excp->throw_name, (long)excp->throw_lnum); else vim_snprintf((char *)IObuff, IOSIZE, "%s", excp->throw_name); set_vim_var_string(VV_THROWPOINT, IObuff, -1); } else /* throw_name not set on an exception from a command that was typed. */ set_vim_var_string(VV_THROWPOINT, NULL, -1); if (p_verbose >= 13 || debug_break_level > 0) { int save_msg_silent = msg_silent; if (debug_break_level > 0) msg_silent = FALSE; /* display messages */ else verbose_enter(); ++no_wait_return; if (debug_break_level > 0 || *p_vfile == NUL) msg_scroll = TRUE; /* always scroll up, don't overwrite */ smsg((char_u *)_("Exception caught: %s"), excp->value); msg_puts((char_u *)"\n"); /* don't overwrite this either */ if (debug_break_level > 0 || *p_vfile == NUL) cmdline_row = msg_row; --no_wait_return; if (debug_break_level > 0) msg_silent = save_msg_silent; else verbose_leave(); } } /* * Remove an exception from the caught stack. */ static void finish_exception(excp) except_T *excp; { if (excp != caught_stack) EMSG(_(e_internal)); caught_stack = caught_stack->caught; if (caught_stack != NULL) { set_vim_var_string(VV_EXCEPTION, caught_stack->value, -1); if (*caught_stack->throw_name != NUL) { if (caught_stack->throw_lnum != 0) vim_snprintf((char *)IObuff, IOSIZE, _("%s, line %ld"), caught_stack->throw_name, (long)caught_stack->throw_lnum); else vim_snprintf((char *)IObuff, IOSIZE, "%s", caught_stack->throw_name); set_vim_var_string(VV_THROWPOINT, IObuff, -1); } else /* throw_name not set on an exception from a command that was * typed. */ set_vim_var_string(VV_THROWPOINT, NULL, -1); } else { set_vim_var_string(VV_EXCEPTION, NULL, -1); set_vim_var_string(VV_THROWPOINT, NULL, -1); } /* Discard the exception, but use the finish message for 'verbose'. */ discard_exception(excp, TRUE); } /* * Flags specifying the message displayed by report_pending. */ #define RP_MAKE 0 #define RP_RESUME 1 #define RP_DISCARD 2 /* * Report information about something pending in a finally clause if required by * the 'verbose' option or when debugging. "action" tells whether something is * made pending or something pending is resumed or discarded. "pending" tells * what is pending. "value" specifies the return value for a pending ":return" * or the exception value for a pending exception. */ static void report_pending(action, pending, value) int action; int pending; void *value; { char_u *mesg; char *s; int save_msg_silent; switch (action) { case RP_MAKE: mesg = (char_u *)_("%s made pending"); break; case RP_RESUME: mesg = (char_u *)_("%s resumed"); break; /* case RP_DISCARD: */ default: mesg = (char_u *)_("%s discarded"); break; } switch (pending) { case CSTP_NONE: return; case CSTP_CONTINUE: s = ":continue"; break; case CSTP_BREAK: s = ":break"; break; case CSTP_FINISH: s = ":finish"; break; case CSTP_RETURN: /* ":return" command producing value, allocated */ s = (char *)get_return_cmd(value); break; default: if (pending & CSTP_THROW) { vim_snprintf((char *)IObuff, IOSIZE, (char *)mesg, _("Exception")); mesg = vim_strnsave(IObuff, (int)STRLEN(IObuff) + 4); STRCAT(mesg, ": %s"); s = (char *)((except_T *)value)->value; } else if ((pending & CSTP_ERROR) && (pending & CSTP_INTERRUPT)) s = _("Error and interrupt"); else if (pending & CSTP_ERROR) s = _("Error"); else /* if (pending & CSTP_INTERRUPT) */ s = _("Interrupt"); } save_msg_silent = msg_silent; if (debug_break_level > 0) msg_silent = FALSE; /* display messages */ ++no_wait_return; msg_scroll = TRUE; /* always scroll up, don't overwrite */ smsg(mesg, (char_u *)s); msg_puts((char_u *)"\n"); /* don't overwrite this either */ cmdline_row = msg_row; --no_wait_return; if (debug_break_level > 0) msg_silent = save_msg_silent; if (pending == CSTP_RETURN) vim_free(s); else if (pending & CSTP_THROW) vim_free(mesg); } /* * If something is made pending in a finally clause, report it if required by * the 'verbose' option or when debugging. */ void report_make_pending(pending, value) int pending; void *value; { if (p_verbose >= 14 || debug_break_level > 0) { if (debug_break_level <= 0) verbose_enter(); report_pending(RP_MAKE, pending, value); if (debug_break_level <= 0) verbose_leave(); } } /* * If something pending in a finally clause is resumed at the ":endtry", report * it if required by the 'verbose' option or when debugging. */ void report_resume_pending(pending, value) int pending; void *value; { if (p_verbose >= 14 || debug_break_level > 0) { if (debug_break_level <= 0) verbose_enter(); report_pending(RP_RESUME, pending, value); if (debug_break_level <= 0) verbose_leave(); } } /* * If something pending in a finally clause is discarded, report it if required * by the 'verbose' option or when debugging. */ void report_discard_pending(pending, value) int pending; void *value; { if (p_verbose >= 14 || debug_break_level > 0) { if (debug_break_level <= 0) verbose_enter(); report_pending(RP_DISCARD, pending, value); if (debug_break_level <= 0) verbose_leave(); } } /* * ":if". */ void ex_if(eap) exarg_T *eap; { int error; int skip; int result; struct condstack *cstack = eap->cstack; if (cstack->cs_idx == CSTACK_LEN - 1) eap->errmsg = (char_u *)N_("E579: :if nesting too deep"); else { ++cstack->cs_idx; cstack->cs_flags[cstack->cs_idx] = 0; /* * Don't do something after an error, interrupt, or throw, or when there * is a surrounding conditional and it was not active. */ skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 && !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)); result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); if (!skip && !error) { if (result) cstack->cs_flags[cstack->cs_idx] = CSF_ACTIVE | CSF_TRUE; } else /* set TRUE, so this conditional will never get active */ cstack->cs_flags[cstack->cs_idx] = CSF_TRUE; } } /* * ":endif". */ void ex_endif(eap) exarg_T *eap; { did_endif = TRUE; if (eap->cstack->cs_idx < 0 || (eap->cstack->cs_flags[eap->cstack->cs_idx] & (CSF_WHILE | CSF_FOR | CSF_TRY))) eap->errmsg = (char_u *)N_("E580: :endif without :if"); else { /* * When debugging or a breakpoint was encountered, display the debug * prompt (if not already done). This shows the user that an ":endif" * is executed when the ":if" or a previous ":elseif" was not TRUE. * Handle a ">quit" debug command as if an interrupt had occurred before * the ":endif". That is, throw an interrupt exception if appropriate. * Doing this here prevents an exception for a parsing error being * discarded by throwing the interrupt exception later on. */ if (!(eap->cstack->cs_flags[eap->cstack->cs_idx] & CSF_TRUE) && dbg_check_skipped(eap)) (void)do_intthrow(eap->cstack); --eap->cstack->cs_idx; } } /* * ":else" and ":elseif". */ void ex_else(eap) exarg_T *eap; { int error; int skip; int result; struct condstack *cstack = eap->cstack; /* * Don't do something after an error, interrupt, or throw, or when there is * a surrounding conditional and it was not active. */ skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 && !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)); if (cstack->cs_idx < 0 || (cstack->cs_flags[cstack->cs_idx] & (CSF_WHILE | CSF_FOR | CSF_TRY))) { if (eap->cmdidx == CMD_else) { eap->errmsg = (char_u *)N_("E581: :else without :if"); return; } eap->errmsg = (char_u *)N_("E582: :elseif without :if"); skip = TRUE; } else if (cstack->cs_flags[cstack->cs_idx] & CSF_ELSE) { if (eap->cmdidx == CMD_else) { eap->errmsg = (char_u *)N_("E583: multiple :else"); return; } eap->errmsg = (char_u *)N_("E584: :elseif after :else"); skip = TRUE; } /* if skipping or the ":if" was TRUE, reset ACTIVE, otherwise set it */ if (skip || cstack->cs_flags[cstack->cs_idx] & CSF_TRUE) { if (eap->errmsg == NULL) cstack->cs_flags[cstack->cs_idx] = CSF_TRUE; skip = TRUE; /* don't evaluate an ":elseif" */ } else cstack->cs_flags[cstack->cs_idx] = CSF_ACTIVE; /* * When debugging or a breakpoint was encountered, display the debug prompt * (if not already done). This shows the user that an ":else" or ":elseif" * is executed when the ":if" or previous ":elseif" was not TRUE. Handle * a ">quit" debug command as if an interrupt had occurred before the * ":else" or ":elseif". That is, set "skip" and throw an interrupt * exception if appropriate. Doing this here prevents that an exception * for a parsing errors is discarded when throwing the interrupt exception * later on. */ if (!skip && dbg_check_skipped(eap) && got_int) { (void)do_intthrow(cstack); skip = TRUE; } if (eap->cmdidx == CMD_elseif) { result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); /* When throwing error exceptions, we want to throw always the first * of several errors in a row. This is what actually happens when * a conditional error was detected above and there is another failure * when parsing the expression. Since the skip flag is set in this * case, the parsing error will be ignored by emsg(). */ if (!skip && !error) { if (result) cstack->cs_flags[cstack->cs_idx] = CSF_ACTIVE | CSF_TRUE; else cstack->cs_flags[cstack->cs_idx] = 0; } else if (eap->errmsg == NULL) /* set TRUE, so this conditional will never get active */ cstack->cs_flags[cstack->cs_idx] = CSF_TRUE; } else cstack->cs_flags[cstack->cs_idx] |= CSF_ELSE; } /* * Handle ":while" and ":for". */ void ex_while(eap) exarg_T *eap; { int error; int skip; int result; struct condstack *cstack = eap->cstack; if (cstack->cs_idx == CSTACK_LEN - 1) eap->errmsg = (char_u *)N_("E585: :while/:for nesting too deep"); else { /* * The loop flag is set when we have jumped back from the matching * ":endwhile" or ":endfor". When not set, need to initialise this * cstack entry. */ if ((cstack->cs_lflags & CSL_HAD_LOOP) == 0) { ++cstack->cs_idx; ++cstack->cs_looplevel; cstack->cs_line[cstack->cs_idx] = -1; } cstack->cs_flags[cstack->cs_idx] = eap->cmdidx == CMD_while ? CSF_WHILE : CSF_FOR; /* * Don't do something after an error, interrupt, or throw, or when * there is a surrounding conditional and it was not active. */ skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 && !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)); if (eap->cmdidx == CMD_while) { /* * ":while bool-expr" */ result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); } else { void *fi; /* * ":for var in list-expr" */ if ((cstack->cs_lflags & CSL_HAD_LOOP) != 0) { /* Jumping here from a ":continue" or ":endfor": use the * previously evaluated list. */ fi = cstack->cs_forinfo[cstack->cs_idx]; error = FALSE; } else { /* Evaluate the argument and get the info in a structure. */ fi = eval_for_line(eap->arg, &error, &eap->nextcmd, skip); cstack->cs_forinfo[cstack->cs_idx] = fi; } /* use the element at the start of the list and advance */ if (!error && fi != NULL && !skip) result = next_for_item(fi, eap->arg); else result = FALSE; if (!result) { free_for_info(fi); cstack->cs_forinfo[cstack->cs_idx] = NULL; } } /* * If this cstack entry was just initialised and is active, set the * loop flag, so do_cmdline() will set the line number in cs_line[]. * If executing the command a second time, clear the loop flag. */ if (!skip && !error && result) { cstack->cs_flags[cstack->cs_idx] |= (CSF_ACTIVE | CSF_TRUE); cstack->cs_lflags ^= CSL_HAD_LOOP; } else { cstack->cs_lflags &= ~CSL_HAD_LOOP; /* If the ":while" evaluates to FALSE or ":for" is past the end of * the list, show the debug prompt at the ":endwhile"/":endfor" as * if there was a ":break" in a ":while"/":for" evaluating to * TRUE. */ if (!skip && !error) cstack->cs_flags[cstack->cs_idx] |= CSF_TRUE; } } } /* * ":continue" */ void ex_continue(eap) exarg_T *eap; { int idx; struct condstack *cstack = eap->cstack; if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0) eap->errmsg = (char_u *)N_("E586: :continue without :while or :for"); else { /* Try to find the matching ":while". This might stop at a try * conditional not in its finally clause (which is then to be executed * next). Therefor, inactivate all conditionals except the ":while" * itself (if reached). */ idx = cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, FALSE); if (idx >= 0 && (cstack->cs_flags[idx] & (CSF_WHILE | CSF_FOR))) { rewind_conditionals(cstack, idx, CSF_TRY, &cstack->cs_trylevel); /* * Set CSL_HAD_CONT, so do_cmdline() will jump back to the * matching ":while". */ cstack->cs_lflags |= CSL_HAD_CONT; /* let do_cmdline() handle it */ } else { /* If a try conditional not in its finally clause is reached first, * make the ":continue" pending for execution at the ":endtry". */ cstack->cs_pending[idx] = CSTP_CONTINUE; report_make_pending(CSTP_CONTINUE, NULL); } } } /* * ":break" */ void ex_break(eap) exarg_T *eap; { int idx; struct condstack *cstack = eap->cstack; if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0) eap->errmsg = (char_u *)N_("E587: :break without :while or :for"); else { /* Inactivate conditionals until the matching ":while" or a try * conditional not in its finally clause (which is then to be * executed next) is found. In the latter case, make the ":break" * pending for execution at the ":endtry". */ idx = cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, TRUE); if (idx >= 0 && !(cstack->cs_flags[idx] & (CSF_WHILE | CSF_FOR))) { cstack->cs_pending[idx] = CSTP_BREAK; report_make_pending(CSTP_BREAK, NULL); } } } /* * ":endwhile" and ":endfor" */ void ex_endwhile(eap) exarg_T *eap; { struct condstack *cstack = eap->cstack; int idx; char_u *err; int csf; int fl; if (eap->cmdidx == CMD_endwhile) { err = e_while; csf = CSF_WHILE; } else { err = e_for; csf = CSF_FOR; } if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0) eap->errmsg = err; else { fl = cstack->cs_flags[cstack->cs_idx]; if (!(fl & csf)) { /* If we are in a ":while" or ":for" but used the wrong endloop * command, do not rewind to the next enclosing ":for"/":while". */ if (fl & CSF_WHILE) eap->errmsg = (char_u *)_("E732: Using :endfor with :while"); else if (fl & CSF_FOR) eap->errmsg = (char_u *)_("E733: Using :endwhile with :for"); } if (!(fl & (CSF_WHILE | CSF_FOR))) { if (!(fl & CSF_TRY)) eap->errmsg = e_endif; else if (fl & CSF_FINALLY) eap->errmsg = e_endtry; /* Try to find the matching ":while" and report what's missing. */ for (idx = cstack->cs_idx; idx > 0; --idx) { fl = cstack->cs_flags[idx]; if ((fl & CSF_TRY) && !(fl & CSF_FINALLY)) { /* Give up at a try conditional not in its finally clause. * Ignore the ":endwhile"/":endfor". */ eap->errmsg = err; return; } if (fl & csf) break; } /* Cleanup and rewind all contained (and unclosed) conditionals. */ (void)cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, FALSE); rewind_conditionals(cstack, idx, CSF_TRY, &cstack->cs_trylevel); } /* * When debugging or a breakpoint was encountered, display the debug * prompt (if not already done). This shows the user that an * ":endwhile"/":endfor" is executed when the ":while" was not TRUE or * after a ":break". Handle a ">quit" debug command as if an * interrupt had occurred before the ":endwhile"/":endfor". That is, * throw an interrupt exception if appropriate. Doing this here * prevents that an exception for a parsing error is discarded when * throwing the interrupt exception later on. */ else if (cstack->cs_flags[cstack->cs_idx] & CSF_TRUE && !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE) && dbg_check_skipped(eap)) (void)do_intthrow(cstack); /* * Set loop flag, so do_cmdline() will jump back to the matching * ":while" or ":for". */ cstack->cs_lflags |= CSL_HAD_ENDLOOP; } } /* * ":throw expr" */ void ex_throw(eap) exarg_T *eap; { char_u *arg = eap->arg; char_u *value; if (*arg != NUL && *arg != '|' && *arg != '\n') value = eval_to_string_skip(arg, &eap->nextcmd, eap->skip); else { EMSG(_(e_argreq)); value = NULL; } /* On error or when an exception is thrown during argument evaluation, do * not throw. */ if (!eap->skip && value != NULL) { if (throw_exception(value, ET_USER, NULL) == FAIL) vim_free(value); else do_throw(eap->cstack); } } /* * Throw the current exception through the specified cstack. Common routine * for ":throw" (user exception) and error and interrupt exceptions. Also * used for rethrowing an uncaught exception. */ void do_throw(cstack) struct condstack *cstack; { int idx; int inactivate_try = FALSE; /* * Cleanup and inactivate up to the next surrounding try conditional that * is not in its finally clause. Normally, do not inactivate the try * conditional itself, so that its ACTIVE flag can be tested below. But * if a previous error or interrupt has not been converted to an exception, * inactivate the try conditional, too, as if the conversion had been done, * and reset the did_emsg or got_int flag, so this won't happen again at * the next surrounding try conditional. */ if (did_emsg && !THROW_ON_ERROR) { inactivate_try = TRUE; did_emsg = FALSE; } if (got_int && !THROW_ON_INTERRUPT) { inactivate_try = TRUE; got_int = FALSE; } idx = cleanup_conditionals(cstack, 0, inactivate_try); if (idx >= 0) { /* * If this try conditional is active and we are before its first * ":catch", set THROWN so that the ":catch" commands will check * whether the exception matches. When the exception came from any of * the catch clauses, it will be made pending at the ":finally" (if * present) and rethrown at the ":endtry". This will also happen if * the try conditional is inactive. This is the case when we are * throwing an exception due to an error or interrupt on the way from * a preceding ":continue", ":break", ":return", ":finish", error or * interrupt (not converted to an exception) to the finally clause or * from a preceding throw of a user or error or interrupt exception to * the matching catch clause or the finally clause. */ if (!(cstack->cs_flags[idx] & CSF_CAUGHT)) { if (cstack->cs_flags[idx] & CSF_ACTIVE) cstack->cs_flags[idx] |= CSF_THROWN; else /* THROWN may have already been set for a catchable exception * that has been discarded. Ensure it is reset for the new * exception. */ cstack->cs_flags[idx] &= ~CSF_THROWN; } cstack->cs_flags[idx] &= ~CSF_ACTIVE; cstack->cs_exception[idx] = current_exception; } #if 0 /* TODO: Add optimization below. Not yet done because of interface * problems to eval.c and ex_cmds2.c. (Servatius) */ else { /* * There are no catch clauses to check or finally clauses to execute. * End the current script or function. The exception will be rethrown * in the caller. */ if (getline_equal(eap->getline, eap->cookie, get_func_line)) current_funccal->returned = TRUE; elseif (eap->get_func_line == getsourceline) ((struct source_cookie *)eap->cookie)->finished = TRUE; } #endif did_throw = TRUE; } /* * ":try" */ void ex_try(eap) exarg_T *eap; { int skip; struct condstack *cstack = eap->cstack; if (cstack->cs_idx == CSTACK_LEN - 1) eap->errmsg = (char_u *)N_("E601: :try nesting too deep"); else { ++cstack->cs_idx; ++cstack->cs_trylevel; cstack->cs_flags[cstack->cs_idx] = CSF_TRY; cstack->cs_pending[cstack->cs_idx] = CSTP_NONE; /* * Don't do something after an error, interrupt, or throw, or when there * is a surrounding conditional and it was not active. */ skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 && !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)); if (!skip) { /* Set ACTIVE and TRUE. TRUE means that the corresponding ":catch" * commands should check for a match if an exception is thrown and * that the finally clause needs to be executed. */ cstack->cs_flags[cstack->cs_idx] |= CSF_ACTIVE | CSF_TRUE; /* * ":silent!", even when used in a try conditional, disables * displaying of error messages and conversion of errors to * exceptions. When the silent commands again open a try * conditional, save "emsg_silent" and reset it so that errors are * again converted to exceptions. The value is restored when that * try conditional is left. If it is left normally, the commands * following the ":endtry" are again silent. If it is left by * a ":continue", ":break", ":return", or ":finish", the commands * executed next are again silent. If it is left due to an * aborting error, an interrupt, or an exception, restoring * "emsg_silent" does not matter since we are already in the * aborting state and/or the exception has already been thrown. * The effect is then just freeing the memory that was allocated * to save the value. */ if (emsg_silent) { eslist_T *elem; elem = (eslist_T *)alloc((unsigned)sizeof(struct eslist_elem)); if (elem == NULL) EMSG(_(e_outofmem)); else { elem->saved_emsg_silent = emsg_silent; elem->next = cstack->cs_emsg_silent_list; cstack->cs_emsg_silent_list = elem; cstack->cs_flags[cstack->cs_idx] |= CSF_SILENT; emsg_silent = 0; } } } } } /* * ":catch /{pattern}/" and ":catch" */ void ex_catch(eap) exarg_T *eap; { int idx = 0; int give_up = FALSE; int skip = FALSE; int caught = FALSE; char_u *end; int save_char = 0; char_u *save_cpo; regmatch_T regmatch; int prev_got_int; struct condstack *cstack = eap->cstack; char_u *pat; if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) { eap->errmsg = (char_u *)N_("E603: :catch without :try"); give_up = TRUE; } else { if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { /* Report what's missing if the matching ":try" is not in its * finally clause. */ eap->errmsg = get_end_emsg(cstack); skip = TRUE; } for (idx = cstack->cs_idx; idx > 0; --idx) if (cstack->cs_flags[idx] & CSF_TRY) break; if (cstack->cs_flags[idx] & CSF_FINALLY) { /* Give up for a ":catch" after ":finally" and ignore it. * Just parse. */ eap->errmsg = (char_u *)N_("E604: :catch after :finally"); give_up = TRUE; } else rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, &cstack->cs_looplevel); } if (ends_excmd(*eap->arg)) /* no argument, catch all errors */ { pat = (char_u *)".*"; end = NULL; eap->nextcmd = find_nextcmd(eap->arg); } else { pat = eap->arg + 1; end = skip_regexp(pat, *eap->arg, TRUE, NULL); } if (!give_up) { /* * Don't do something when no exception has been thrown or when the * corresponding try block never got active (because of an inactive * surrounding conditional or after an error or interrupt or throw). */ if (!did_throw || !(cstack->cs_flags[idx] & CSF_TRUE)) skip = TRUE; /* * Check for a match only if an exception is thrown but not caught by * a previous ":catch". An exception that has replaced a discarded * exception is not checked (THROWN is not set then). */ if (!skip && (cstack->cs_flags[idx] & CSF_THROWN) && !(cstack->cs_flags[idx] & CSF_CAUGHT)) { if (end != NULL && *end != NUL && !ends_excmd(*skipwhite(end + 1))) { EMSG(_(e_trailing)); return; } /* When debugging or a breakpoint was encountered, display the * debug prompt (if not already done) before checking for a match. * This is a helpful hint for the user when the regular expression * matching fails. Handle a ">quit" debug command as if an * interrupt had occurred before the ":catch". That is, discard * the original exception, replace it by an interrupt exception, * and don't catch it in this try block. */ if (!dbg_check_skipped(eap) || !do_intthrow(cstack)) { /* Terminate the pattern and avoid the 'l' flag in 'cpoptions' * while compiling it. */ if (end != NULL) { save_char = *end; *end = NUL; } save_cpo = p_cpo; p_cpo = (char_u *)""; regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); regmatch.rm_ic = FALSE; if (end != NULL) *end = save_char; p_cpo = save_cpo; if (regmatch.regprog == NULL) EMSG2(_(e_invarg2), pat); else { /* * Save the value of got_int and reset it. We don't want * a previous interruption cancel matching, only hitting * CTRL-C while matching should abort it. */ prev_got_int = got_int; got_int = FALSE; caught = vim_regexec_nl(®match, current_exception->value, (colnr_T)0); got_int |= prev_got_int; vim_free(regmatch.regprog); } } } if (caught) { /* Make this ":catch" clause active and reset did_emsg, got_int, * and did_throw. Put the exception on the caught stack. */ cstack->cs_flags[idx] |= CSF_ACTIVE | CSF_CAUGHT; did_emsg = got_int = did_throw = FALSE; catch_exception((except_T *)cstack->cs_exception[idx]); /* It's mandatory that the current exception is stored in the cstack * so that it can be discarded at the next ":catch", ":finally", or * ":endtry" or when the catch clause is left by a ":continue", * ":break", ":return", ":finish", error, interrupt, or another * exception. */ if (cstack->cs_exception[cstack->cs_idx] != current_exception) EMSG(_(e_internal)); } else { /* * If there is a preceding catch clause and it caught the exception, * finish the exception now. This happens also after errors except * when this ":catch" was after the ":finally" or not within * a ":try". Make the try conditional inactive so that the * following catch clauses are skipped. On an error or interrupt * after the preceding try block or catch clause was left by * a ":continue", ":break", ":return", or ":finish", discard the * pending action. */ cleanup_conditionals(cstack, CSF_TRY, TRUE); } } if (end != NULL) eap->nextcmd = find_nextcmd(end); } /* * ":finally" */ void ex_finally(eap) exarg_T *eap; { int idx; int skip = FALSE; int pending = CSTP_NONE; struct condstack *cstack = eap->cstack; if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) eap->errmsg = (char_u *)N_("E606: :finally without :try"); else { if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { eap->errmsg = get_end_emsg(cstack); for (idx = cstack->cs_idx - 1; idx > 0; --idx) if (cstack->cs_flags[idx] & CSF_TRY) break; /* Make this error pending, so that the commands in the following * finally clause can be executed. This overrules also a pending * ":continue", ":break", ":return", or ":finish". */ pending = CSTP_ERROR; } else idx = cstack->cs_idx; if (cstack->cs_flags[idx] & CSF_FINALLY) { /* Give up for a multiple ":finally" and ignore it. */ eap->errmsg = (char_u *)N_("E607: multiple :finally"); return; } rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, &cstack->cs_looplevel); /* * Don't do something when the corresponding try block never got active * (because of an inactive surrounding conditional or after an error or * interrupt or throw) or for a ":finally" without ":try" or a multiple * ":finally". After every other error (did_emsg or the conditional * errors detected above) or after an interrupt (got_int) or an * exception (did_throw), the finally clause must be executed. */ skip = !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); if (!skip) { /* When debugging or a breakpoint was encountered, display the * debug prompt (if not already done). The user then knows that the * finally clause is executed. */ if (dbg_check_skipped(eap)) { /* Handle a ">quit" debug command as if an interrupt had * occurred before the ":finally". That is, discard the * original exception and replace it by an interrupt * exception. */ (void)do_intthrow(cstack); } /* * If there is a preceding catch clause and it caught the exception, * finish the exception now. This happens also after errors except * when this is a multiple ":finally" or one not within a ":try". * After an error or interrupt, this also discards a pending * ":continue", ":break", ":finish", or ":return" from the preceding * try block or catch clause. */ cleanup_conditionals(cstack, CSF_TRY, FALSE); /* * Make did_emsg, got_int, did_throw pending. If set, they overrule * a pending ":continue", ":break", ":return", or ":finish". Then * we have particularly to discard a pending return value (as done * by the call to cleanup_conditionals() above when did_emsg or * got_int is set). The pending values are restored by the * ":endtry", except if there is a new error, interrupt, exception, * ":continue", ":break", ":return", or ":finish" in the following * finally clause. A missing ":endwhile", ":endfor" or ":endif" * detected here is treated as if did_emsg and did_throw had * already been set, respectively in case that the error is not * converted to an exception, did_throw had already been unset. * We must not set did_emsg here since that would suppress the * error message. */ if (pending == CSTP_ERROR || did_emsg || got_int || did_throw) { if (cstack->cs_pending[cstack->cs_idx] == CSTP_RETURN) { report_discard_pending(CSTP_RETURN, cstack->cs_rettv[cstack->cs_idx]); discard_pending_return(cstack->cs_rettv[cstack->cs_idx]); } if (pending == CSTP_ERROR && !did_emsg) pending |= (THROW_ON_ERROR) ? CSTP_THROW : 0; else pending |= did_throw ? CSTP_THROW : 0; pending |= did_emsg ? CSTP_ERROR : 0; pending |= got_int ? CSTP_INTERRUPT : 0; cstack->cs_pending[cstack->cs_idx] = pending; /* It's mandatory that the current exception is stored in the * cstack so that it can be rethrown at the ":endtry" or be * discarded if the finally clause is left by a ":continue", * ":break", ":return", ":finish", error, interrupt, or another * exception. When emsg() is called for a missing ":endif" or * a missing ":endwhile"/":endfor" detected here, the * exception will be discarded. */ if (did_throw && cstack->cs_exception[cstack->cs_idx] != current_exception) EMSG(_(e_internal)); } /* * Set CSL_HAD_FINA, so do_cmdline() will reset did_emsg, * got_int, and did_throw and make the finally clause active. * This will happen after emsg() has been called for a missing * ":endif" or a missing ":endwhile"/":endfor" detected here, so * that the following finally clause will be executed even then. */ cstack->cs_lflags |= CSL_HAD_FINA; } } } /* * ":endtry" */ void ex_endtry(eap) exarg_T *eap; { int idx; int skip; int rethrow = FALSE; int pending = CSTP_NONE; void *rettv = NULL; struct condstack *cstack = eap->cstack; if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) eap->errmsg = (char_u *)N_("E602: :endtry without :try"); else { /* * Don't do something after an error, interrupt or throw in the try * block, catch clause, or finally clause preceding this ":endtry" or * when an error or interrupt occurred after a ":continue", ":break", * ":return", or ":finish" in a try block or catch clause preceding this * ":endtry" or when the try block never got active (because of an * inactive surrounding conditional or after an error or interrupt or * throw) or when there is a surrounding conditional and it has been * made inactive by a ":continue", ":break", ":return", or ":finish" in * the finally clause. The latter case need not be tested since then * anything pending has already been discarded. */ skip = did_emsg || got_int || did_throw || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { eap->errmsg = get_end_emsg(cstack); /* Find the matching ":try" and report what's missing. */ idx = cstack->cs_idx; do --idx; while (idx > 0 && !(cstack->cs_flags[idx] & CSF_TRY)); rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, &cstack->cs_looplevel); skip = TRUE; /* * If an exception is being thrown, discard it to prevent it from * being rethrown at the end of this function. It would be * discarded by the error message, anyway. Resets did_throw. * This does not affect the script termination due to the error * since "trylevel" is decremented after emsg() has been called. */ if (did_throw) discard_current_exception(); } else { idx = cstack->cs_idx; /* * If we stopped with the exception currently being thrown at this * try conditional since we didn't know that it doesn't have * a finally clause, we need to rethrow it after closing the try * conditional. */ if (did_throw && (cstack->cs_flags[idx] & CSF_TRUE) && !(cstack->cs_flags[idx] & CSF_FINALLY)) rethrow = TRUE; } /* If there was no finally clause, show the user when debugging or * a breakpoint was encountered that the end of the try conditional has * been reached: display the debug prompt (if not already done). Do * this on normal control flow or when an exception was thrown, but not * on an interrupt or error not converted to an exception or when * a ":break", ":continue", ":return", or ":finish" is pending. These * actions are carried out immediately. */ if ((rethrow || (!skip && !(cstack->cs_flags[idx] & CSF_FINALLY) && !cstack->cs_pending[idx])) && dbg_check_skipped(eap)) { /* Handle a ">quit" debug command as if an interrupt had occurred * before the ":endtry". That is, throw an interrupt exception and * set "skip" and "rethrow". */ if (got_int) { skip = TRUE; (void)do_intthrow(cstack); /* The do_intthrow() call may have reset did_throw or * cstack->cs_pending[idx].*/ rethrow = FALSE; if (did_throw && !(cstack->cs_flags[idx] & CSF_FINALLY)) rethrow = TRUE; } } /* * If a ":return" is pending, we need to resume it after closing the * try conditional; remember the return value. If there was a finally * clause making an exception pending, we need to rethrow it. Make it * the exception currently being thrown. */ if (!skip) { pending = cstack->cs_pending[idx]; cstack->cs_pending[idx] = CSTP_NONE; if (pending == CSTP_RETURN) rettv = cstack->cs_rettv[idx]; else if (pending & CSTP_THROW) current_exception = cstack->cs_exception[idx]; } /* * Discard anything pending on an error, interrupt, or throw in the * finally clause. If there was no ":finally", discard a pending * ":continue", ":break", ":return", or ":finish" if an error or * interrupt occurred afterwards, but before the ":endtry" was reached. * If an exception was caught by the last of the catch clauses and there * was no finally clause, finish the exception now. This happens also * after errors except when this ":endtry" is not within a ":try". * Restore "emsg_silent" if it has been reset by this try conditional. */ (void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, TRUE); --cstack->cs_idx; --cstack->cs_trylevel; if (!skip) { report_resume_pending(pending, (pending == CSTP_RETURN) ? rettv : (pending & CSTP_THROW) ? (void *)current_exception : NULL); switch (pending) { case CSTP_NONE: break; /* Reactivate a pending ":continue", ":break", ":return", * ":finish" from the try block or a catch clause of this try * conditional. This is skipped, if there was an error in an * (unskipped) conditional command or an interrupt afterwards * or if the finally clause is present and executed a new error, * interrupt, throw, ":continue", ":break", ":return", or * ":finish". */ case CSTP_CONTINUE: ex_continue(eap); break; case CSTP_BREAK: ex_break(eap); break; case CSTP_RETURN: do_return(eap, FALSE, FALSE, rettv); break; case CSTP_FINISH: do_finish(eap, FALSE); break; /* When the finally clause was entered due to an error, * interrupt or throw (as opposed to a ":continue", ":break", * ":return", or ":finish"), restore the pending values of * did_emsg, got_int, and did_throw. This is skipped, if there * was a new error, interrupt, throw, ":continue", ":break", * ":return", or ":finish". in the finally clause. */ default: if (pending & CSTP_ERROR) did_emsg = TRUE; if (pending & CSTP_INTERRUPT) got_int = TRUE; if (pending & CSTP_THROW) rethrow = TRUE; break; } } if (rethrow) /* Rethrow the current exception (within this cstack). */ do_throw(cstack); } } /* * enter_cleanup() and leave_cleanup() * * Functions to be called before/after invoking a sequence of autocommands for * cleanup for a failed command. (Failure means here that a call to emsg() * has been made, an interrupt occurred, or there is an uncaught exception * from a previous autocommand execution of the same command.) * * Call enter_cleanup() with a pointer to a cleanup_T and pass the same * pointer to leave_cleanup(). The cleanup_T structure stores the pending * error/interrupt/exception state. */ /* * This function works a bit like ex_finally() except that there was not * actually an extra try block around the part that failed and an error or * interrupt has not (yet) been converted to an exception. This function * saves the error/interrupt/ exception state and prepares for the call to * do_cmdline() that is going to be made for the cleanup autocommand * execution. */ void enter_cleanup(csp) cleanup_T *csp; { int pending = CSTP_NONE; /* * Postpone did_emsg, got_int, did_throw. The pending values will be * restored by leave_cleanup() except if there was an aborting error, * interrupt, or uncaught exception after this function ends. */ if (did_emsg || got_int || did_throw || need_rethrow) { csp->pending = (did_emsg ? CSTP_ERROR : 0) | (got_int ? CSTP_INTERRUPT : 0) | (did_throw ? CSTP_THROW : 0) | (need_rethrow ? CSTP_THROW : 0); /* If we are currently throwing an exception (did_throw), save it as * well. On an error not yet converted to an exception, update * "force_abort" and reset "cause_abort" (as do_errthrow() would do). * This is needed for the do_cmdline() call that is going to be made * for autocommand execution. We need not save *msg_list because * there is an extra instance for every call of do_cmdline(), anyway. */ if (did_throw || need_rethrow) csp->exception = current_exception; else { csp->exception = NULL; if (did_emsg) { force_abort |= cause_abort; cause_abort = FALSE; } } did_emsg = got_int = did_throw = need_rethrow = FALSE; /* Report if required by the 'verbose' option or when debugging. */ report_make_pending(pending, csp->exception); } else { csp->pending = CSTP_NONE; csp->exception = NULL; } } /* * See comment above enter_cleanup() for how this function is used. * * This function is a bit like ex_endtry() except that there was not actually * an extra try block around the part that failed and an error or interrupt * had not (yet) been converted to an exception when the cleanup autocommand * sequence was invoked. * * This function has to be called with the address of the cleanup_T structure * filled by enter_cleanup() as an argument; it restores the error/interrupt/ * exception state saved by that function - except there was an aborting * error, an interrupt or an uncaught exception during execution of the * cleanup autocommands. In the latter case, the saved error/interrupt/ * exception state is discarded. */ void leave_cleanup(csp) cleanup_T *csp; { int pending = csp->pending; if (pending == CSTP_NONE) /* nothing to do */ return; /* If there was an aborting error, an interrupt, or an uncaught exception * after the corresponding call to enter_cleanup(), discard what has been * made pending by it. Report this to the user if required by the * 'verbose' option or when debugging. */ if (aborting() || need_rethrow) { if (pending & CSTP_THROW) /* Cancel the pending exception (includes report). */ discard_exception((except_T *)csp->exception, FALSE); else report_discard_pending(pending, NULL); /* If an error was about to be converted to an exception when * enter_cleanup() was called, free the message list. */ if (msg_list != NULL) { free_msglist(*msg_list); *msg_list = NULL; } } /* * If there was no new error, interrupt, or throw between the calls * to enter_cleanup() and leave_cleanup(), restore the pending * error/interrupt/exception state. */ else { /* * If there was an exception being thrown when enter_cleanup() was * called, we need to rethrow it. Make it the exception currently * being thrown. */ if (pending & CSTP_THROW) current_exception = csp->exception; /* * If an error was about to be converted to an exception when * enter_cleanup() was called, let "cause_abort" take the part of * "force_abort" (as done by cause_errthrow()). */ else if (pending & CSTP_ERROR) { cause_abort = force_abort; force_abort = FALSE; } /* * Restore the pending values of did_emsg, got_int, and did_throw. */ if (pending & CSTP_ERROR) did_emsg = TRUE; if (pending & CSTP_INTERRUPT) got_int = TRUE; if (pending & CSTP_THROW) need_rethrow = TRUE; /* did_throw will be set by do_one_cmd() */ /* Report if required by the 'verbose' option or when debugging. */ report_resume_pending(pending, (pending & CSTP_THROW) ? (void *)current_exception : NULL); } } /* * Make conditionals inactive and discard what's pending in finally clauses * until the conditional type searched for or a try conditional not in its * finally clause is reached. If this is in an active catch clause, finish * the caught exception. * Return the cstack index where the search stopped. * Values used for "searched_cond" are (CSF_WHILE | CSF_FOR) or CSF_TRY or 0, * the latter meaning the innermost try conditional not in its finally clause. * "inclusive" tells whether the conditional searched for should be made * inactive itself (a try conditional not in its finally claused possibly find * before is always made inactive). If "inclusive" is TRUE and * "searched_cond" is CSF_TRY|CSF_SILENT, the saved former value of * "emsg_silent", if reset when the try conditional finally reached was * entered, is restored (unsed by ex_endtry()). This is normally done only * when such a try conditional is left. */ int cleanup_conditionals(cstack, searched_cond, inclusive) struct condstack *cstack; int searched_cond; int inclusive; { int idx; int stop = FALSE; for (idx = cstack->cs_idx; idx >= 0; --idx) { if (cstack->cs_flags[idx] & CSF_TRY) { /* * Discard anything pending in a finally clause and continue the * search. There may also be a pending ":continue", ":break", * ":return", or ":finish" before the finally clause. We must not * discard it, unless an error or interrupt occurred afterwards. */ if (did_emsg || got_int || (cstack->cs_flags[idx] & CSF_FINALLY)) { switch (cstack->cs_pending[idx]) { case CSTP_NONE: break; case CSTP_CONTINUE: case CSTP_BREAK: case CSTP_FINISH: report_discard_pending(cstack->cs_pending[idx], NULL); cstack->cs_pending[idx] = CSTP_NONE; break; case CSTP_RETURN: report_discard_pending(CSTP_RETURN, cstack->cs_rettv[idx]); discard_pending_return(cstack->cs_rettv[idx]); cstack->cs_pending[idx] = CSTP_NONE; break; default: if (cstack->cs_flags[idx] & CSF_FINALLY) { if (cstack->cs_pending[idx] & CSTP_THROW) { /* Cancel the pending exception. This is in the * finally clause, so that the stack of the * caught exceptions is not involved. */ discard_exception((except_T *) cstack->cs_exception[idx], FALSE); } else report_discard_pending(cstack->cs_pending[idx], NULL); cstack->cs_pending[idx] = CSTP_NONE; } break; } } /* * Stop at a try conditional not in its finally clause. If this try * conditional is in an active catch clause, finish the caught * exception. */ if (!(cstack->cs_flags[idx] & CSF_FINALLY)) { if ((cstack->cs_flags[idx] & CSF_ACTIVE) && (cstack->cs_flags[idx] & CSF_CAUGHT)) finish_exception((except_T *)cstack->cs_exception[idx]); /* Stop at this try conditional - except the try block never * got active (because of an inactive surrounding conditional * or when the ":try" appeared after an error or interrupt or * throw). */ if (cstack->cs_flags[idx] & CSF_TRUE) { if (searched_cond == 0 && !inclusive) break; stop = TRUE; } } } /* Stop on the searched conditional type (even when the surrounding * conditional is not active or something has been made pending). * If "inclusive" is TRUE and "searched_cond" is CSF_TRY|CSF_SILENT, * check first whether "emsg_silent" needs to be restored. */ if (cstack->cs_flags[idx] & searched_cond) { if (!inclusive) break; stop = TRUE; } cstack->cs_flags[idx] &= ~CSF_ACTIVE; if (stop && searched_cond != (CSF_TRY | CSF_SILENT)) break; /* * When leaving a try conditional that reset "emsg_silent" on its * entry after saving the original value, restore that value here and * free the memory used to store it. */ if ((cstack->cs_flags[idx] & CSF_TRY) && (cstack->cs_flags[idx] & CSF_SILENT)) { eslist_T *elem; elem = cstack->cs_emsg_silent_list; cstack->cs_emsg_silent_list = elem->next; emsg_silent = elem->saved_emsg_silent; vim_free(elem); cstack->cs_flags[idx] &= ~CSF_SILENT; } if (stop) break; } return idx; } /* * Return an appropriate error message for a missing endwhile/endfor/endif. */ static char_u * get_end_emsg(cstack) struct condstack *cstack; { if (cstack->cs_flags[cstack->cs_idx] & CSF_WHILE) return e_endwhile; if (cstack->cs_flags[cstack->cs_idx] & CSF_FOR) return e_endfor; return e_endif; } /* * Rewind conditionals until index "idx" is reached. "cond_type" and * "cond_level" specify a conditional type and the address of a level variable * which is to be decremented with each skipped conditional of the specified * type. * Also free "for info" structures where needed. */ void rewind_conditionals(cstack, idx, cond_type, cond_level) struct condstack *cstack; int idx; int cond_type; int *cond_level; { while (cstack->cs_idx > idx) { if (cstack->cs_flags[cstack->cs_idx] & cond_type) --*cond_level; if (cstack->cs_flags[cstack->cs_idx] & CSF_FOR) free_for_info(cstack->cs_forinfo[cstack->cs_idx]); --cstack->cs_idx; } } /* * ":endfunction" when not after a ":function" */ /*ARGSUSED*/ void ex_endfunction(eap) exarg_T *eap; { EMSG(_("E193: :endfunction not inside a function")); } /* * Return TRUE if the string "p" looks like a ":while" or ":for" command. */ int has_loop_cmd(p) char_u *p; { int len; /* skip modifiers, white space and ':' */ for (;;) { while (*p == ' ' || *p == '\t' || *p == ':') ++p; len = modifier_len(p); if (len == 0) break; p += len; } if ((p[0] == 'w' && p[1] == 'h') || (p[0] == 'f' && p[1] == 'o' && p[2] == 'r')) return TRUE; return FALSE; } #endif /* FEAT_EVAL */