diff options
Diffstat (limited to 'src/ex_eval.c')
| -rw-r--r-- | src/ex_eval.c | 2073 |
1 files changed, 0 insertions, 2073 deletions
diff --git a/src/ex_eval.c b/src/ex_eval.c deleted file mode 100644 index fc0887ca03..0000000000 --- a/src/ex_eval.c +++ /dev/null @@ -1,2073 +0,0 @@ -/* - * 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" -#include "ex_eval.h" -#include "charset.h" -#include "eval.h" -#include "ex_cmds2.h" -#include "ex_docmd.h" -#include "message.h" -#include "misc2.h" -#include "memory.h" -#include "regexp.h" - - -static void free_msglist(struct msglist *l); -static int throw_exception(void *, int, char_u *); -static char_u *get_end_emsg(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 asynchronously 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. - */ -/* Values used for the Vim release. */ -# define THROW_ON_ERROR TRUE -# define THROW_ON_ERROR_TRUE -# define THROW_ON_INTERRUPT TRUE -# define THROW_ON_INTERRUPT_TRUE - -static void catch_exception(except_T *excp); -static void finish_exception(except_T *excp); -static void discard_exception(except_T *excp, int was_finished); -static void report_pending(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(void) -{ - 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(void) -{ - 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(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(void) -{ - /* 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(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) { - 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(struct msglist *l) -{ - struct msglist *messages, *next; - - messages = l; - while (messages != NULL) { - next = messages->next; - free(messages->msg); - free(messages); - messages = next; - } -} - -/* - * Free global "*msg_list" and the messages it contains, then set "*msg_list" - * to NULL. - */ -void free_global_msglist(void) -{ - free_msglist(*msg_list); - *msg_list = NULL; -} - -/* - * 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(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(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; -} - -/* - * Get an exception message that is to be stored in current_exception->value. - */ -char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should_free) -{ - char_u *ret, *mesg; - int cmdlen; - char_u *p, *val; - - if (type == ET_ERROR) { - *should_free = FALSE; - mesg = ((struct msglist *)value)->throw_msg; - if (cmdname != NULL && *cmdname != NUL) { - cmdlen = (int)STRLEN(cmdname); - ret = vim_strnsave((char_u *)"Vim(", - 4 + cmdlen + 2 + (int)STRLEN(mesg)); - if (ret == NULL) - return ret; - STRCPY(&ret[4], cmdname); - STRCPY(&ret[4 + cmdlen], "):"); - val = ret + 4 + cmdlen + 2; - } else { - ret = vim_strnsave((char_u *)"Vim:", 4 + (int)STRLEN(mesg)); - if (ret == NULL) - return ret; - val = ret + 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 { - *should_free = FALSE; - ret = (char_u *) value; - } - - return ret; -} - - -/* - * 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(void *value, int type, char_u *cmdname) -{ - except_T *excp; - int should_free; - - /* - * 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; - - excp->value = get_exception_string(value, type, cmdname, &should_free); - if (excp->value == NULL && should_free) - goto nomem; - - excp->type = type; - excp->throw_name = vim_strsave(sourcing_name == NULL - ? (char_u *)"" : sourcing_name); - if (excp->throw_name == NULL) { - if (should_free) - 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: - 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(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); - free(saved_IObuff); - } - if (excp->type != ET_INTERRUPT) - free(excp->value); - if (excp->type == ET_ERROR) - free_msglist(excp->messages); - free(excp->throw_name); - free(excp); -} - -/* - * Discard the exception currently being thrown. - */ -void discard_current_exception(void) -{ - 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(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 %" PRId64), - excp->throw_name, (int64_t)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(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 %" PRId64), caught_stack->throw_name, - (int64_t)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(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) - free(s); - else if (pending & CSTP_THROW) - 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(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(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(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(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(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(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(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(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); - - /* |