예제 #1
0
static void tmate_client_exec_cmd(struct tmate_unpacker *uk)
{
	struct cmd_q *cmd_q;
	struct cmd_list *cmdlist;
	char *cause;

	int client_id = unpack_int(uk);
	char *cmd_str = unpack_string(uk);

	if (cmd_string_parse(cmd_str, &cmdlist, NULL, 0, &cause) != 0) {
		tmate_failed_cmd(client_id, cause);
		free(cause);
		goto out;
	}

	/* error messages land in cfg_causes */
	ARRAY_FREE(&cfg_causes);

	cmd_q = cmdq_new(NULL);
	cmdq_run(cmd_q, cmdlist);
	cmd_list_free(cmdlist);
	cmdq_free(cmd_q);

	if (!ARRAY_EMPTY(&cfg_causes)) {
		cause = ARRAY_ITEM(&cfg_causes, 0);
		tmate_failed_cmd(client_id, cause);
		free(cause);
		ARRAY_FREE(&cfg_causes);
	}

out:
	free(cmd_str);
}
예제 #2
0
// 调用函数
FAKE_API void fkrunps(fake * fk, const char * func)
{
	FKLOG("fkrunps %p %s", fk, func);

	fk->rn.rundeps++;

	// 清空运行环境	
	fk->clearerr();

	// 分配个
	pool<processor>::node * n = 0;
	if (UNLIKE(POOL_EMPTY(fk->pp)))
	{
		POOL_GROW(fk->pp, pool<processor>::node, n);
		PROCESS_INI(n->t, fk);
	}
	POOL_POP(fk->pp, n);
	assert(ARRAY_EMPTY(n->t.m_pl.l));
	assert(n->t.m_routine_num == 0);
	processor & pro = n->t;
	PROCESS_CLEAR(pro);
	variant funcv;
	V_SET_STRING(&funcv, func);
	routine * r = pro.start_routine(funcv, 0, 0);

	PUSH_CUR_PROCESSOR(n, fk->rn);
	// 单独执行,下次再跑run
	if (UNLIKE(fk->rn.stepmod))
	{
		variant * ret = 0;
		bool err = false;
		PS_PUSH_AND_GET(fk->ps, ret);
		*ret = NILV;
		FKLOG("fkrunps %p %s yield", fk, func);
		CHECK_ERR(err);
		return;
	}
	
	pro.run();
	POP_CUR_PROCESSOR(fk->rn);
	
	variant * ret = 0;
	bool err = false;
	PS_PUSH_AND_GET(fk->ps, ret);
	*ret = ROUTINE_GETRET(*r);
	CHECK_ERR(err);

	POOL_PUSH(fk->pp, n);
	
	fk->rn.rundeps--;
	
	FKLOG("fkrunps %p %s OK", fk, func);
}
예제 #3
0
int
include_finish(void)
{
	if (ARRAY_EMPTY(&parse_filestack))
		return (1);
	log_debug2("finished file %s", parse_file->path);

	xfree(parse_file);
	parse_file = ARRAY_LAST(&parse_filestack);
	ARRAY_TRUNC(&parse_filestack, 1);

	return (0);
}
예제 #4
0
파일: fetch-mbox.c 프로젝트: mbeck-/fdm
/* Initial state. */
int
fetch_mbox_state_init(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_mbox_data	*data = a->data;

	if (fetch_mbox_make(a) != 0)
		return (FETCH_ERROR);
	if (ARRAY_EMPTY(&data->fmboxes)) {
		log_warnx("%s: no mboxes found", a->name);
		return (-1);
	}

	data->index = 0;

	TAILQ_INIT(&data->kept);

	fctx->state = fetch_mbox_state_open;
	return (FETCH_AGAIN);
}
예제 #5
0
파일: cfg.c 프로젝트: JonAWhite/tmux
void
cfg_show_causes(struct session *s)
{
	struct window_pane	*wp;
	char			*cause;
	u_int			 i;

	if (s == NULL || ARRAY_EMPTY(&cfg_causes))
		return;
	wp = s->curw->window->active;

	window_pane_set_mode(wp, &window_copy_mode);
	window_copy_init_for_output(wp);
	for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) {
		cause = ARRAY_ITEM(&cfg_causes, i);
		window_copy_add(wp, "%s", cause);
		free(cause);
	}
	ARRAY_FREE(&cfg_causes);
}
예제 #6
0
파일: mail.c 프로젝트: avkrotov/fdm
/*
 * Sometimes mail has wrapped header lines, this undoubtedly looks neat but
 * makes them a pain to match using regexps. We build a list of the newlines
 * in all the wrapped headers in m->wrapped, and can then quickly unwrap them
 * for regexp matching and wrap them again for delivery.
 */
u_int
fill_wrapped(struct mail *m)
{
	char		*ptr;
	size_t		 end, off;
	u_int		 n;

	if (!ARRAY_EMPTY(&m->wrapped))
		fatalx("already wrapped");

	ARRAY_INIT(&m->wrapped);
	m->wrapchar = '\0';

	end = m->body;
	ptr = m->data;

	n = 0;
	for (;;) {
		ptr = memchr(ptr, '\n', m->size - (ptr - m->data));
		if (ptr == NULL)
			break;
		ptr++;
		off = ptr - m->data;
		if (off >= end)
			break;

		/* Check if the line starts with whitespace. */
		if (!isblank((u_char) *ptr))
			continue;

		/* Save the position. */
		ARRAY_ADD(&m->wrapped, off - 1);
		n++;
	}

	return (n);
}
예제 #7
0
int
cmd_source_file_exec(struct cmd *self, struct cmd_ctx *ctx)
{
	struct args		*args = self->args;
	struct causelist	 causes;
	char			*cause;
	struct window_pane	*wp;
	int			 retval;
	u_int			 i;

	ARRAY_INIT(&causes);

	retval = load_cfg(args->argv[0], ctx, &causes);
	if (ARRAY_EMPTY(&causes))
		return (retval);

	if (retval == 1 && !RB_EMPTY(&sessions) && ctx->cmdclient != NULL) {
		wp = RB_MIN(sessions, &sessions)->curw->window->active;
		window_pane_set_mode(wp, &window_copy_mode);
		window_copy_init_for_output(wp);
		for (i = 0; i < ARRAY_LENGTH(&causes); i++) {
			cause = ARRAY_ITEM(&causes, i);
			window_copy_add(wp, "%s", cause);
			xfree(cause);
		}
	} else {
		for (i = 0; i < ARRAY_LENGTH(&causes); i++) {
			cause = ARRAY_ITEM(&causes, i);
			ctx->print(ctx, "%s", cause);
			xfree(cause);
		}
	}
	ARRAY_FREE(&causes);

	return (retval);
}
예제 #8
0
int
cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
{
	struct args		*args = self->args;
	struct session		*s, *old_s, *groupwith;
	struct window		*w;
	struct window_pane	*wp;
	struct environ		 env;
	struct termios		 tio, *tiop;
	struct passwd		*pw;
	const char		*newname, *target, *update, *cwd;
	char			*overrides, *cmd, *cause;
	int			 detached, idx;
	u_int			 sx, sy, i;

	newname = args_get(args, 's');
	if (newname != NULL && session_find(newname) != NULL) {
		ctx->error(ctx, "duplicate session: %s", newname);
		return (-1);
	}

	target = args_get(args, 't');
	if (target != NULL) {
		groupwith = cmd_find_session(ctx, target);
		if (groupwith == NULL)
			return (-1);
	} else
		groupwith = NULL;

	/*
	 * There are three cases:
	 *
	 * 1. If cmdclient is non-NULL, new-session has been called from the
	 *    command-line - cmdclient is to become a new attached, interactive
	 *    client. Unless -d is given, the terminal must be opened and then
	 *    the client sent MSG_READY.
	 *
	 * 2. If cmdclient is NULL, new-session has been called from an
	 *    existing client (such as a key binding).
	 *
	 * 3. Both are NULL, the command was in the configuration file. Treat
	 *    this as if -d was given even if it was not.
	 *
	 * In all cases, a new additional session needs to be created and
	 * (unless -d) set as the current session for the client.
	 */

	/* Set -d if no client. */
	detached = args_has(args, 'd');
	if (ctx->cmdclient == NULL && ctx->curclient == NULL)
		detached = 1;

	/*
	 * Save the termios settings, part of which is used for new windows in
	 * this session.
	 *
	 * This is read again with tcgetattr() rather than using tty.tio as if
	 * detached, tty_open won't be called. Because of this, it must be done
	 * before opening the terminal as that calls tcsetattr() to prepare for
	 * tmux taking over.
	 */
	if (ctx->cmdclient != NULL && ctx->cmdclient->tty.fd != -1) {
		if (tcgetattr(ctx->cmdclient->tty.fd, &tio) != 0)
			fatal("tcgetattr failed");
		tiop = &tio;
	} else
		tiop = NULL;

	/* Open the terminal if necessary. */
	if (!detached && ctx->cmdclient != NULL) {
		if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) {
			ctx->error(ctx, "not a terminal");
			return (-1);
		}

		overrides =
		    options_get_string(&global_s_options, "terminal-overrides");
		if (tty_open(&ctx->cmdclient->tty, overrides, &cause) != 0) {
			ctx->error(ctx, "open terminal failed: %s", cause);
			xfree(cause);
			return (-1);
		}
	}

	/* Get the new session working directory. */
	if (ctx->cmdclient != NULL && ctx->cmdclient->cwd != NULL)
		cwd = ctx->cmdclient->cwd;
	else {
		pw = getpwuid(getuid());
		if (pw->pw_dir != NULL && *pw->pw_dir != '\0')
			cwd = pw->pw_dir;
		else
			cwd = "/";
	}

	/* Find new session size. */
	if (detached) {
		sx = 80;
		sy = 24;
	} else if (ctx->cmdclient != NULL) {
		sx = ctx->cmdclient->tty.sx;
		sy = ctx->cmdclient->tty.sy;
	} else {
		sx = ctx->curclient->tty.sx;
		sy = ctx->curclient->tty.sy;
	}
	if (sy > 0 && options_get_number(&global_s_options, "status"))
		sy--;
	if (sx == 0)
		sx = 1;
	if (sy == 0)
		sy = 1;

	/* Figure out the command for the new window. */
	if (target != NULL)
		cmd = NULL;
	else if (args->argc != 0)
		cmd = args->argv[0];
	else
		cmd = options_get_string(&global_s_options, "default-command");

	/* Construct the environment. */
	environ_init(&env);
	update = options_get_string(&global_s_options, "update-environment");
	if (ctx->cmdclient != NULL)
		environ_update(update, &ctx->cmdclient->environ, &env);

	/* Create the new session. */
	idx = -1 - options_get_number(&global_s_options, "base-index");
	s = session_create(newname, cmd, cwd, &env, tiop, idx, sx, sy, &cause);
	if (s == NULL) {
		ctx->error(ctx, "create session failed: %s", cause);
		xfree(cause);
		return (-1);
	}
	environ_free(&env);

	/* Set the initial window name if one given. */
	if (cmd != NULL && args_has(args, 'n')) {
		w = s->curw->window;

		xfree(w->name);
		w->name = xstrdup(args_get(args, 'n'));

		options_set_number(&w->options, "automatic-rename", 0);
	}

	/*
	 * If a target session is given, this is to be part of a session group,
	 * so add it to the group and synchronize.
	 */
	if (groupwith != NULL) {
		session_group_add(groupwith, s);
		session_group_synchronize_to(s);
		session_select(s, RB_ROOT(&s->windows)->idx);
	}

	/*
	 * Set the client to the new session. If a command client exists, it is
	 * taking this session and needs to get MSG_READY and stay around.
	 */
	if (!detached) {
		if (ctx->cmdclient != NULL) {
			server_write_client(ctx->cmdclient, MSG_READY, NULL, 0);

			old_s = ctx->cmdclient->session;
			if (old_s != NULL)
				ctx->cmdclient->last_session = old_s;
			ctx->cmdclient->session = s;
			session_update_activity(s);
			server_redraw_client(ctx->cmdclient);
		} else {
			old_s = ctx->curclient->session;
			if (old_s != NULL)
				ctx->curclient->last_session = old_s;
			ctx->curclient->session = s;
			session_update_activity(s);
			server_redraw_client(ctx->curclient);
		}
	}
	recalculate_sizes();
	server_update_socket();

	/*
	 * If there are still configuration file errors to display, put the new
	 * session's current window into more mode and display them now.
	 */
	if (cfg_finished && !ARRAY_EMPTY(&cfg_causes)) {
		wp = s->curw->window->active;
		window_pane_set_mode(wp, &window_copy_mode);
		window_copy_init_for_output(wp);
		for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) {
			cause = ARRAY_ITEM(&cfg_causes, i);
			window_copy_add(wp, "%s", cause);
			xfree(cause);
		}
		ARRAY_FREE(&cfg_causes);
	}

	return (!detached);	/* 1 means don't tell command client to exit */
}
예제 #9
0
파일: imap-common.c 프로젝트: upwhere/fdm
/*
 * Next state. Get next mail. This is also the idle state when completed, so
 * check for finished mail, exiting, and so on.
 */
int
imap_state_next(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;

	/* Handle dropped and kept mail. */
	if (!ARRAY_EMPTY(&data->dropped)) {
		if (imap_putln(a, "%u UID STORE %u +FLAGS.SILENT (\\Deleted)",
		    ++data->tag, ARRAY_FIRST(&data->dropped)) != 0)
			return (FETCH_ERROR);
		ARRAY_REMOVE(&data->dropped, 0);
		fctx->state = imap_state_commit;
		return (FETCH_BLOCK);
	}
	if (!ARRAY_EMPTY(&data->kept)) {
		/*
		 * GMail is broken and does not set the \Seen flag after mail
		 * is fetched, so set it explicitly for kept mail.
		 */
		if (imap_putln(a, "%u UID STORE %u +FLAGS.SILENT (\\Seen)",
		    ++data->tag, ARRAY_FIRST(&data->kept)) != 0)
			return (FETCH_ERROR);
		ARRAY_REMOVE(&data->kept, 0);
		fctx->state = imap_state_commit;
		return (FETCH_BLOCK);
	}

	/* Need to purge, switch to purge state. */
	if (fctx->flags & FETCH_PURGE) {
		/*
		 * If can't purge now, loop through this state until there is
		 * no mail on the dropped queue and FETCH_EMPTY is set. Can't
		 * have a seperate state to loop through without returning
		 * here: mail could potentially be added to the dropped list
		 * while in that state.
		 */
		if (fctx->flags & FETCH_EMPTY) {
			fctx->flags &= ~FETCH_PURGE;

			if (imap_putln(a, "%u EXPUNGE", ++data->tag) != 0)
				return (FETCH_ERROR);
			fctx->state = imap_state_expunge;
			return (FETCH_BLOCK);
		}

		/*
		 * Must be waiting for delivery, so permit blocking even though
		 * we (fetch) aren't waiting for any data.
		 */
		return (FETCH_BLOCK);
	}

	/* If last mail, wait for everything to be committed then close down. */
	if (ARRAY_EMPTY(&data->wanted)) {
		if (data->committed != data->total)
			return (FETCH_BLOCK);
		if (imap_putln(a, "%u CLOSE", ++data->tag) != 0)
			return (FETCH_ERROR);
		fctx->state = imap_state_close;
		return (FETCH_BLOCK);
	}

	/* Fetch the next mail. */
	if (imap_putln(a, "%u "
	    "UID FETCH %u BODY[]",++data->tag, ARRAY_FIRST(&data->wanted)) != 0)
		return (FETCH_ERROR);
	fctx->state = imap_state_body;
	return (FETCH_BLOCK);
}
예제 #10
0
void interpreter::call(const variant & func)
{
    fake * fk = m_fk;
	const funcunion * f = m_fk->fm.get_func(func);
	if (UNLIKE(!f))
	{
		FKERR("fkrun no func %s fail", vartostring(&func).c_str());
		seterror(m_fk, efk_run_no_func_error, "fkrun no func %s fail", vartostring(&func).c_str());
		m_isend = true;
		return;
	}

	// 常规函数
	if (f->havefb)
	{
		const func_binary * fb = &f->fb;

		// 空函数处理
		if (UNLIKE(!FUNC_BINARY_CMDSIZE(*fb)))
		{
			// 所有都完
			if (ARRAY_EMPTY(m_stack_list))
			{
				FKLOG("call stack empty end");
				m_isend = true;
			}
			return;
		}

		// 压栈
		if (UNLIKE(ARRAY_SIZE(m_stack_list) >= ARRAY_MAX_SIZE(m_stack_list)))
		{
			int newsize = ARRAY_MAX_SIZE(m_stack_list) + 1 + ARRAY_MAX_SIZE(m_stack_list) * m_fk->cfg.array_grow_speed / 100;
			ARRAY_GROW(m_stack_list, newsize, stack);
		}
		ARRAY_PUSH_BACK(m_stack_list);
		stack & s = ARRAY_BACK(m_stack_list);
		m_cur_stack = &s;
		STACK_INI(s, m_fk, fb);
		if (UNLIKE(FUNC_BINARY_MAX_STACK(*fb) > (int)ARRAY_MAX_SIZE(s.m_stack_variant_list)))
		{
			ARRAY_GROW(s.m_stack_variant_list, FUNC_BINARY_MAX_STACK(*fb), variant);
		}

		// 记录profile
		beginfuncprofile();
		
		paramstack * ps = getps(m_fk);

		if (UNLIKE((int)ps->m_variant_list_num != FUNC_BINARY_PARAMNUM(*fb)))
		{
			FKERR("call func %s param not match", vartostring(&func).c_str());
			seterror(m_fk, efk_run_param_error, "call func %s param not match", vartostring(&func).c_str());
			m_isend = true;
			return;
		}

		assert(FUNC_BINARY_PARAMNUM(*fb) <= (int)ARRAY_MAX_SIZE(s.m_stack_variant_list));

		// 分配栈空间
		for (int i = 0; i < (int)FUNC_BINARY_PARAMNUM(*fb); i++)
		{
			SET_STACK(&(ps->m_variant_list[i]), s, i);
			FKLOG("call set %s to pos %d", (vartostring(&(ps->m_variant_list[i]))).c_str(), i);
		}
		
		ps->clear();

		// 重置ret
		V_SET_NIL(&m_ret[0]);

		// 标记
		FUNC_BINARY_USE(*fb)++;

		return;
	}

	// 记录profile
	uint32_t s = 0;
	if (m_fk->pf.isopen())
	{
		s = fkgetmstick();
	}

	// 绑定函数
	if (f->haveff)
	{
		BIND_FUNC_CALL(f, this);
		FKLOG("call C func %s", vartostring(&func).c_str());
	}
	// 内置函数
	else if (f->havebif)
	{
		BUILDIN_FUNC_CALL(f, this);
		FKLOG("call buildin func %s", vartostring(&func).c_str());
	}
	else
	{
		assert(0);
		FKERR("fkrun no inter func %s fail", vartostring(&func).c_str());
		seterror(m_fk, efk_run_no_func_error, "fkrun no inter func %s fail", vartostring(&func).c_str());
		m_isend = true;
		return;
	}

	// 返回值
	paramstack * theps = getps(m_fk);
    bool err = false;
	USE(err);

	// 这种情况是直接跳过脚本调用了C函数
	if (UNLIKE(ARRAY_EMPTY(m_stack_list)))
	{
    	variant * cret;
    	PS_POP_AND_GET(*theps, cret);
		m_isend = true;
		// 直接塞返回值
		m_ret[0] = *cret;
	}
	// 否则塞到当前堆栈上
	else
	{
		// 塞返回值
		m_cur_stack = &ARRAY_BACK(m_stack_list);
		const func_binary & fb = *m_cur_stack->m_fb;
		for (int i = 0; i < m_cur_stack->m_retnum; i++)
		{
			variant * ret;
			GET_VARIANT(*m_cur_stack, fb, ret, m_cur_stack->m_retvpos[i]);
			
        	variant * cret;
        	PS_GET(*theps, cret, i);
        	
			*ret = *cret;
		}
	}
    if (UNLIKE(err))
    {
        m_isend = true;
    }
    
	if (m_fk->pf.isopen())
	{
	    bool err = false;
		const char * name = 0;
		V_GET_STRING(&func, name);
		m_fk->pf.add_func_sample(name, fkgetmstick() - s);
	}

	return;
}
예제 #11
0
/* Check mail against next rule or part of expression. */
int
mail_match(struct mail_ctx *mctx, struct msg *msg, struct msgbuf *msgbuf)
{
	struct account	*a = mctx->account;
	struct mail	*m = mctx->mail;
	struct expritem	*ei;
	struct replstrs	*users;
	int		 should_free, this = -1, error = MAIL_CONTINUE;
	char		 desc[DESCBUFSIZE];

	set_wrapped(m, ' ');

	/* If blocked, check for msgs from parent. */
	if (mctx->msgid != 0) {
		if (msg == NULL || msg->id != mctx->msgid)
			return (MAIL_BLOCKED);
		mctx->msgid = 0;

		if (msg->type != MSG_DONE)
			fatalx("unexpected message");
		if (msgbuf->buf != NULL && msgbuf->len != 0) {
			strb_destroy(&m->tags);
			m->tags = msgbuf->buf;
			reset_tags(&m->tags);
		}

		ei = mctx->expritem;
		switch (msg->data.error) {
		case MATCH_ERROR:
			return (MAIL_ERROR);
		case MATCH_TRUE:
			this = 1;
			break;
		case MATCH_FALSE:
			this = 0;
			break;
		default:
			fatalx("unexpected response");
		}
		apply_result(ei, &mctx->result, this);

		goto next_expritem;
	}

	/* Check for completion and end of ruleset. */
	if (mctx->done)
		return (MAIL_DONE);
	if (mctx->rule == NULL) {
		switch (conf.impl_act) {
		case DECISION_NONE:
			log_warnx("%s: reached end of ruleset. no "
			    "unmatched-mail option; keeping mail",  a->name);
			m->decision = DECISION_KEEP;
			break;
		case DECISION_KEEP:
			log_debug2("%s: reached end of ruleset. keeping mail",
			    a->name);
			m->decision = DECISION_KEEP;
			break;
		case DECISION_DROP:
			log_debug2("%s: reached end of ruleset. dropping mail",
			    a->name);
			m->decision = DECISION_DROP;
			break;
		}
		return (MAIL_DONE);
	}

	/* Expression not started. Start it. */
	if (mctx->expritem == NULL) {
		/* Start the expression. */
		mctx->result = 0;
		mctx->expritem = TAILQ_FIRST(mctx->rule->expr);
	}

	/* Check this expression item and adjust the result. */
	ei = mctx->expritem;

	/* Handle short-circuit evaluation. */
	switch (ei->op) {
	case OP_NONE:
		break;
	case OP_AND:
		/* And and the result is already false. */
		if (!mctx->result)
			goto next_expritem;
		break;
	case OP_OR:
		/* Or and the result is already true. */
		if (mctx->result)
			goto next_expritem;
		break;
	}

	switch (ei->match->match(mctx, ei)) {
	case MATCH_ERROR:
		return (MAIL_ERROR);
	case MATCH_PARENT:
		return (MAIL_BLOCKED);
	case MATCH_TRUE:
		this = 1;
		break;
	case MATCH_FALSE:
		this = 0;
		break;
	default:
		fatalx("unexpected op");
	}
	apply_result(ei, &mctx->result, this);

	ei->match->desc(ei, desc, sizeof desc);
	log_debug3("%s: tried %s, result now %d", a->name, desc, mctx->result);

next_expritem:
	/* Move to the next item. If there is one, then return. */
	mctx->expritem = TAILQ_NEXT(mctx->expritem, entry);
	if (mctx->expritem != NULL)
		return (MAIL_CONTINUE);

	log_debug3("%s: finished rule %u, result %d", a->name, mctx->rule->idx,
	    mctx->result);

	/* If the result was false, skip to find the next rule. */
	if (!mctx->result)
		goto next_rule;
	log_debug2("%s: matched to rule %u", a->name, mctx->rule->idx);

	/*
	 * If this rule is stop, mark the context so when we get back after
	 * delivery we know to stop.
	 */
	if (mctx->rule->stop)
		mctx->done = 1;

	/* Handle nested rules. */
	if (!TAILQ_EMPTY(&mctx->rule->rules)) {
		log_debug2("%s: entering nested rules", a->name);

		/*
		 * Stack the current rule (we are at the end of it so the
		 * the expritem must be NULL already).
		 */
		ARRAY_ADD(&mctx->stack, mctx->rule);

		/* Continue with the first rule of the nested list. */
		mctx->rule = TAILQ_FIRST(&mctx->rule->rules);
		return (MAIL_CONTINUE);
	}
	mctx->matched = 1;

	/* Handle lambda actions. */
	if (mctx->rule->lambda != NULL) {
		users = find_delivery_users(mctx, NULL, &should_free);

		chained = MAXACTIONCHAIN;
		if (fill_from_action(mctx,
		    mctx->rule, mctx->rule->lambda, users) != 0) {
			if (should_free)
				ARRAY_FREEALL(users);
			return (MAIL_ERROR);
		}

		if (should_free)
			ARRAY_FREEALL(users);
		error = MAIL_DELIVER;
	}

	/* Fill the delivery action queue. */
	if (!ARRAY_EMPTY(mctx->rule->actions)) {
		chained = MAXACTIONCHAIN;
		if (fill_from_strings(mctx,
		    mctx->rule, mctx->rule->actions) != 0)
			return (MAIL_ERROR);
		error = MAIL_DELIVER;
	}

next_rule:
	/* Move to the next rule. */
	mctx->ruleidx = mctx->rule->idx;	/* save last index */
	mctx->rule = TAILQ_NEXT(mctx->rule, entry);

	/* If no more rules, try to move up the stack. */
	while (mctx->rule == NULL) {
		if (ARRAY_EMPTY(&mctx->stack))
			break;
		log_debug2("%s: exiting nested rules", a->name);
		mctx->rule = ARRAY_LAST(&mctx->stack);
		mctx->rule = TAILQ_NEXT(mctx->rule, entry);
		ARRAY_TRUNC(&mctx->stack, 1);
	}

	return (error);
}