/*++
/* NAME
/*	dict_random 3
/* SUMMARY
/*	dictionary manager interface for randomized tables
/* SYNOPSIS
/*	#include <dict_random.h>
/*
/*	DICT	*dict_random_open(name, open_flags, dict_flags)
/*	const char *name;
/*	int	open_flags;
/*	int	dict_flags;
/* DESCRIPTION
/*	dict_random_open() opens an in-memory, read-only, table.
/*	Example: "\fBrandmap:{\fIresult_1, ... ,result_n}\fR".
/*
/*	Each table query returns a random choice from the specified
/*	results. Other table access methods are not supported.
/*
/*	The first and last characters of the "randmap:" table name
/*	must be '{' and '}'. Within these, individual maps are
/*	separated with comma or whitespace.
/* SEE ALSO
/*	dict(3) generic dictionary manager
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

/* System library. */

#include <sys_defs.h>
#include <string.h>

/* Utility library. */

#include <msg.h>
#include <mymalloc.h>
#include <myrand.h>
#include <stringops.h>
#include <dict_random.h>

/* Application-specific. */

typedef struct {
    DICT    dict;			/* generic members */
    ARGV   *replies;			/* reply values */
} DICT_RANDOM;

#define STR(x) vstring_str(x)

/* dict_random_lookup - find randomized-table entry */

static const char *dict_random_lookup(DICT *dict, const char *unused_query)
{
    DICT_RANDOM *dict_random = (DICT_RANDOM *) dict;

    DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE,
	 dict_random->replies->argv[myrand() % dict_random->replies->argc]);
}

/* dict_random_close - disassociate from randomized table */

static void dict_random_close(DICT *dict)
{
    DICT_RANDOM *dict_random = (DICT_RANDOM *) dict;

    if (dict_random->replies)
	argv_free(dict_random->replies);
    dict_free(dict);
}

static char *dict_random_parse_name(DICT *dict, ARGV **argv,
				            const char *string,
				            const char *delim,
				            const char *parens)
{
    ARGV   *argvp = argv_alloc(1);
    char   *saved_string = mystrdup(string);
    char   *bp = saved_string;
    char   *arg;
    VSTRING *b64 = 0;
    char   *err = 0;

    while ((arg = mystrtokq(&bp, delim, parens)) != 0) {
	if (dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) {
	    if ((b64 = dict_file_to_b64(dict, arg)) != 0) {
		argv_add(argvp, vstring_str(b64), (char *) 0);
	    } else {
		err = dict_file_get_error(dict);
		break;
	    }
	} else {
	    argv_add(argvp, arg, (char *) 0);
	}
    }
    argv_terminate(argvp);
    myfree(saved_string);
    *argv = argvp;
    return (err);
}

/* dict_random_open - open a randomized table */

DICT   *dict_random_open(const char *name, int open_flags, int dict_flags)
{
    DICT_RANDOM *dict_random;
    char   *saved_name = 0;
    size_t  len;
    char   *err = 0;

    /*
     * Clarity first. Let the optimizer worry about redundant code.
     */
#define DICT_RANDOM_RETURN(x) do { \
	DICT *__d = (x); \
	if (saved_name != 0) \
	    myfree(saved_name); \
	if (err != 0) \
	    myfree(err); \
	return (__d); \
    } while (0)

    /*
     * Sanity checks.
     */
    if (open_flags != O_RDONLY)
	DICT_RANDOM_RETURN(dict_surrogate(DICT_TYPE_RANDOM, name,
					  open_flags, dict_flags,
				  "%s:%s map requires O_RDONLY access mode",
					  DICT_TYPE_RANDOM, name));

    /*
     * Bundle up preliminary results.
     */
    dict_random =
	(DICT_RANDOM *) dict_alloc(DICT_TYPE_RANDOM, name, sizeof(*dict_random));
    dict_random->dict.lookup = dict_random_lookup;
    dict_random->dict.close = dict_random_close;
    dict_random->dict.flags = dict_flags | DICT_FLAG_PATTERN;
    dict_random->replies = 0;
    dict_random->dict.owner.status = DICT_OWNER_TRUSTED;
    dict_random->dict.owner.uid = 0;

    /*
     * Split the table name into its constituent parts.
     */
    if ((len = balpar(name, CHARS_BRACE)) == 0 || name[len] != 0
	|| *(saved_name = mystrndup(name + 1, len - 2)) == 0
	|| (err = dict_random_parse_name(&dict_random->dict,
					 &dict_random->replies, saved_name,
					 CHARS_COMMA_SP, CHARS_BRACE)) != 0
	|| dict_random->replies->argc == 0) {
	dict_random_close(&dict_random->dict);
	DICT_RANDOM_RETURN(err == 0 ?
			   dict_surrogate(DICT_TYPE_RANDOM, name,
					  open_flags, dict_flags,
					  "bad syntax: \"%s:%s\"; "
					  "need \"%s:{value...}\"",
					  DICT_TYPE_RANDOM, name,
					  DICT_TYPE_RANDOM) :
			   dict_surrogate(DICT_TYPE_RANDOM, name,
					  open_flags, dict_flags,
					  "%s", err));
    }
    dict_file_purge_buffers(&dict_random->dict);
    DICT_RANDOM_RETURN(&dict_random->dict);
}
