static struct mail_search_arg *
imap_search_modseq(struct mail_search_build_context *ctx)
{
	struct mail_search_arg *sarg;
	const char *value;
	int ret;

	/* [<name> <type>] <modseq> */
	sarg = mail_search_build_new(ctx, SEARCH_MODSEQ);
	sarg->value.modseq = p_new(ctx->pool, struct mail_search_modseq, 1);

	if (mail_search_parse_string(ctx->parser, &value) < 0)
		return NULL;

	if ((ret = arg_modseq_set_ext(ctx, sarg, value)) < 0)
		return NULL;
	if (ret > 0) {
		/* extension data used */
		if (mail_search_parse_string(ctx->parser, &value) < 0)
			return NULL;
	}

	if (str_to_uint64(value, &sarg->value.modseq->modseq) < 0) {
		ctx->_error = "Invalid MODSEQ value";
		return NULL;
	}
	return sarg;
}
static struct mail_search_arg *
arg_new_human_date(struct mail_search_build_context *ctx,
		   enum mail_search_arg_type type,
		   enum mail_search_date_type date_type)
{
	struct mail_search_arg *sarg;
	const char *value;

	sarg = mail_search_build_new(ctx, type);
	if (mail_search_parse_string(ctx->parser, &value) < 0)
		return NULL;

	if (mail_parse_human_timestamp(value, &sarg->value.time) < 0)
		sarg->value.time = (time_t)-1;

	sarg->value.search_flags = MAIL_SEARCH_ARG_FLAG_USE_TZ;

	if (sarg->value.time == (time_t)-1) {
		ctx->_error = p_strconcat(ctx->pool,
			"Invalid search date parameter: ", value, NULL);
		return NULL;
	}
	sarg->value.date_type = date_type;
	return sarg;
}
static int
arg_modseq_set_ext(struct mail_search_build_context *ctx,
		   struct mail_search_arg *sarg, const char *name)
{
	const char *value;

	name = t_str_lcase(name);
	if (strncmp(name, "/flags/", 7) != 0)
		return 0;
	name += 7;

	/* set name */
	if (*name == '\\') {
		/* system flag */
		sarg->value.flags = imap_parse_system_flag(name);
		if (sarg->value.flags == 0 ||
		    sarg->value.flags == MAIL_RECENT) {
			ctx->_error = "Invalid MODSEQ system flag";
			return -1;
		}
	} else {
		sarg->value.str = p_strdup(ctx->pool, name);
	}

	/* set type */
	if (mail_search_parse_string(ctx->parser, &value) < 0)
		return -1;
	if (arg_modseq_set_type(ctx, sarg->value.modseq, value) < 0)
		return -1;
	return 1;
}
struct mail_search_arg *
mail_search_build_str(struct mail_search_build_context *ctx,
		      enum mail_search_arg_type type)
{
	struct mail_search_arg *sarg;
	const char *value;

	sarg = mail_search_build_new(ctx, type);
	if (mail_search_parse_string(ctx->parser, &value) < 0)
		return NULL;
	sarg->value.str = p_strdup(ctx->pool, value);
	return sarg;
}
static struct mail_search_arg *
imap_search_header(struct mail_search_build_context *ctx)
{
	const char *hdr_name;

	/* <hdr-name> <string> */
	if (mail_search_parse_string(ctx->parser, &hdr_name) < 0)
		return NULL;
	if (mail_search_build_get_utf8(ctx, hdr_name, &hdr_name) < 0)
		return NULL;

	return arg_new_header(ctx, SEARCH_HEADER, t_str_ucase(hdr_name));
}
static struct mail_search_arg *
arg_new_human_size(struct mail_search_build_context *ctx,
		   enum mail_search_arg_type type)
{
	struct mail_search_arg *sarg;
	const char *value, *error;

	sarg = mail_search_build_new(ctx, type);
	if (mail_search_parse_string(ctx->parser, &value) < 0)
		return NULL;

	if (settings_get_size(value, &sarg->value.size, &error) < 0) {
		ctx->_error = p_strdup(ctx->pool, error);
		return NULL;
	}
	return sarg;
}
static struct mail_search_arg *
arg_new_header(struct mail_search_build_context *ctx,
	       enum mail_search_arg_type type, const char *hdr_name)
{
	struct mail_search_arg *sarg;
	const char *value;

	sarg = mail_search_build_new(ctx, type);
	if (mail_search_parse_string(ctx->parser, &value) < 0)
		return NULL;

	if (mail_search_build_get_utf8(ctx, value, &sarg->value.str) < 0)
		return NULL;

	sarg->hdr_field_name = p_strdup(ctx->pool, hdr_name);
	return sarg;
}
static struct mail_search_arg *
arg_new_size(struct mail_search_build_context *ctx,
	     enum mail_search_arg_type type)
{
	struct mail_search_arg *sarg;
	const char *value;

	sarg = mail_search_build_new(ctx, type);
	if (mail_search_parse_string(ctx->parser, &value) < 0)
		return NULL;

	if (str_to_uoff(value, &sarg->value.size) < 0) {
		ctx->_error = "Invalid search size parameter";
		return NULL;
	}
	return sarg;
}
static struct mail_search_arg *
arg_new_date(struct mail_search_build_context *ctx,
	     enum mail_search_arg_type type,
	     enum mail_search_date_type date_type)
{
	struct mail_search_arg *sarg;
	const char *value;

	sarg = mail_search_build_new(ctx, type);
	if (mail_search_parse_string(ctx->parser, &value) < 0)
		return NULL;
	if (!imap_parse_date(value, &sarg->value.time)) {
		ctx->_error = "Invalid search date parameter";
		return NULL;
	}
	sarg->value.date_type = date_type;
	return sarg;
}
static struct mail_search_arg *
arg_new_interval(struct mail_search_build_context *ctx,
		 enum mail_search_arg_type type)
{
	struct mail_search_arg *sarg;
	const char *value;
	uint32_t interval;

	sarg = mail_search_build_new(ctx, type);
	if (mail_search_parse_string(ctx->parser, &value) < 0)
		return NULL;

	if (str_to_uint32(value, &interval) < 0 || interval == 0) {
		ctx->_error = "Invalid search interval parameter";
		return NULL;
	}
	sarg->value.search_flags = MAIL_SEARCH_ARG_FLAG_USE_TZ;
	sarg->value.time = ioloop_time - interval;
	sarg->value.date_type = MAIL_SEARCH_DATE_TYPE_RECEIVED;
	return sarg;
}
static struct mail_search_arg *
imap_search_inthread(struct mail_search_build_context *ctx)
{
	struct mail_search_arg *sarg;

	/* <algorithm> <search key> */
	enum mail_thread_type thread_type;
	const char *algorithm;

	if (mail_search_parse_string(ctx->parser, &algorithm) < 0)
		return NULL;
	if (!mail_thread_type_parse(algorithm, &thread_type)) {
		ctx->_error = "Unknown thread algorithm";
		return NULL;
	}

	sarg = mail_search_build_new(ctx, SEARCH_INTHREAD);
	sarg->value.thread_type = thread_type;
	if (mail_search_build_key(ctx, sarg, &sarg->value.subargs) < 0)
		return NULL;
	return sarg;
}