/*++
/* NAME
/*	dict_inline 3
/* SUMMARY
/*	dictionary manager interface for inline table
/* SYNOPSIS
/*	#include <dict_inline.h>
/*
/*	DICT	*dict_inline_open(name, open_flags, dict_flags)
/*	const char *name;
/*	int	open_flags;
/*	int	dict_flags;
/* DESCRIPTION
/*	dict_inline_open() opens a read-only, in-memory table.
/*	Example: "\fBinline:{\fIkey_1=value_1, ..., key_n=value_n\fR}".
/*	The longer form with { key = value } allows values that
/*	contain whitespace or comma.
/* 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 <stringops.h>
#include <dict.h>
#include <dict_ht.h>
#include <dict_inline.h>

/* Application-specific. */

/* dict_inline_open - open inline table */

DICT   *dict_inline_open(const char *name, int open_flags, int dict_flags)
{
    DICT   *dict;
    char   *cp, *saved_name = 0;
    size_t  len;
    char   *nameval, *vname, *value;
    const char *err = 0;
    char   *free_me = 0;
    int     count = 0;

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

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

    /*
     * UTF-8 syntax check.
     */
    if (DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags)
	&& allascii(name) == 0
	&& valid_utf8_stringz(name) == 0)
	DICT_INLINE_RETURN(dict_surrogate(DICT_TYPE_INLINE, name,
					  open_flags, dict_flags,
					  "bad UTF-8 syntax: \"%s:%s\"; "
					  "need \"%s:{name=value...}\"",
					  DICT_TYPE_INLINE, name,
					  DICT_TYPE_INLINE));

    /*
     * Parse the table into its constituent name=value pairs.
     */
    if ((len = balpar(name, CHARS_BRACE)) == 0 || name[len] != 0
	|| *(cp = saved_name = mystrndup(name + 1, len - 2)) == 0)
	DICT_INLINE_RETURN(dict_surrogate(DICT_TYPE_INLINE, name,
					  open_flags, dict_flags,
					  "bad syntax: \"%s:%s\"; "
					  "need \"%s:{name=value...}\"",
					  DICT_TYPE_INLINE, name,
					  DICT_TYPE_INLINE));

    /*
     * Reuse the "internal" dictionary type.
     */
    dict = dict_ht_open(name, open_flags, dict_flags);
    dict_type_override(dict, DICT_TYPE_INLINE);
    while ((nameval = mystrtokq(&cp, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
	if (nameval[0] == CHARS_BRACE[0])
	    err = free_me = extpar(&nameval, CHARS_BRACE, EXTPAR_FLAG_STRIP);
	if (err != 0 || (err = split_qnameval(nameval, &vname, &value)) != 0)
	    break;

	if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) != 0) {
	    VSTRING *base64_buf;

	    if ((base64_buf = dict_file_to_b64(dict, value)) == 0) {
		err = free_me = dict_file_get_error(dict);
		break;
	    }
	    value = vstring_str(base64_buf);
	}
	/* No duplicate checks. See comments in dict_thash.c. */
	dict->update(dict, vname, value);
	count += 1;
    }
    if (err != 0 || count == 0) {
	dict->close(dict);
	DICT_INLINE_RETURN(dict_surrogate(DICT_TYPE_INLINE, name,
					  open_flags, dict_flags,
					  "%s: \"%s:%s\"; "
					  "need \"%s:{name=%s...}\"",
					  err != 0 ? err : "empty table",
					  DICT_TYPE_INLINE, name,
					  DICT_TYPE_INLINE,
				  (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) ?
					  "filename" : "value"));
    }
    dict->owner.status = DICT_OWNER_TRUSTED;

    dict_file_purge_buffers(dict);
    DICT_INLINE_RETURN(dict);
}
