/*
 * Copyright (c) 2003-2011
 * Distributed Systems Software.  All rights reserved.
 * See the file LICENSE for redistribution information.
 *
 * $Id: kwv.h 2521 2011-09-23 18:44:00Z brachman $
 */

/*****************************************************************************
 * COPYRIGHT AND PERMISSION NOTICE
 * 
 * Copyright (c) 2001-2003 The Queen in Right of Canada
 * 
 * All rights reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, and/or sell
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, provided that the above copyright notice(s) and this
 * permission notice appear in all copies of the Software and that both the
 * above copyright notice(s) and this permission notice appear in supporting
 * documentation.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE 
 * BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
 * SOFTWARE.
 * 
 * Except as contained in this notice, the name of a copyright holder shall not
 * be used in advertising or otherwise to promote the sale, use or other
 * dealings in this Software without prior written authorization of the
 * copyright holder.
 ***************************************************************************/

#ifndef _KWV_H_
#define _KWV_H_

#include <string.h>
#include <sys/types.h>
#include <regex.h>
#include <stdlib.h>
#include <stdio.h>

#include "str.h"
#include "ds.h"

typedef struct Kwv_conf Kwv_conf;

/* Bit flags */
typedef enum Kwv_conf_mode {
  KWV_CONF_DEFAULT  = 0x00,
  KWV_CONF_KEEPQ    = 0x01,	/* Retain quotes around the value */
  KWV_CONF_QUOTED   = 0x02,	/* Quotes are required around the value */
  KWV_CONF_MULTISEP = 0x04	/* Ignore repeated sep characters */
} Kwv_conf_mode;

struct Kwv_conf {
  char *sep;			/* Set of chars that separates keyword from value */
  char *quote_chars;	/* Optional quote characters to surround value */
  char *ws_pad_chars;	/* Optional padding characters to surround sep */
  unsigned int mode;
  char *multi_mode;		/* Any given chars separates one pair from next */
  unsigned int init_nalloc;	/* If non-zero, initial vector allocation to use */
  int (*eval_func)(char *name, char *val, const Kwv_conf *conf, void *eval_arg,
				   char **new_val);
  void *eval_arg;
};

/* Bit flags */
typedef enum Kwv_parse_flags {
  KWV_PARSE_DEFAULT         = 0x00, /* Use all defaults */
  KWV_PARSE_NEED_VALUE      = 0x01,	/* A null value is invalid */
  KWV_PARSE_NEED_QUOTES     = 0x02,	/* A quoted value is mandatory */
  KWV_PARSE_KEEP_QUOTES     = 0x04,	/* If present, retain quotes around value */
  KWV_PARSE_INTERNAL_QUOTES = 0x08	/* Internal quotes are not special */
} Kwv_parse_flags;

typedef struct Kwv_parse_conf {
  Kwv_parse_flags flags;	/* ORed Kwv_parse_flags */
  int sep_char;				/* If non-zero, the separator to use */
  int end_char;				/* If non-zero, an additional value terminator */
  char *errmsg;
} Kwv_parse_conf;

typedef struct Kwv_pair {
  char *name;					/* The key */
  char *val;					/* Its value */
  void *xval;					/* Could be conditionally included... */
  struct Kwv_pair *next;		/* The next value having the same key */
} Kwv_pair;

/*
 * 8 7 6 5 4 3 2 1 0
 * - - - D D D C I R
 *
 * XXX this mode selection code should be simplified
 */
enum {
  KWV_REGEX_MODE       = 0x0001,	/* R */
  KWV_ICASE_MODE       = 0x0002,	/* I */
  KWV_CLEAR_MODE       = 0x0004,	/* C */
  KWV_NODUPS_MODE      = 0x0008,	/* D */
  KWV_ALLOWDUPS_MODE   = 0x0010,	/* D */
  KWV_REPLACEDUPS_MODE = 0x0020,	/* D */
  KWV_DUPS_MODE_MASK   = 0x0038,
  KWV_MODE_MASK        = 0x003f,
  KWV_PREPEND_MODE     = 0x0040		/* P */
};

/* How to handle a duplicate key */
typedef enum {
  KWV_NO_DUPS      = 0,		/* Duplicates not allowed - flag an error */
  KWV_ALLOW_DUPS   = 1,		/* Duplicates allowed */
  KWV_REPLACE_DUPS = 2		/* Duplicates not allowed - replace */
} Kwv_dup_mode;

typedef struct Kwv {
  Kwv_pair **pairs;
  unsigned int npairs;			/* Total number of pairs in data structure */
  unsigned int nalloc;			/* Total number of vector slots allocated */
  unsigned int nused;			/* Total number of vector slots used */
  unsigned int dup_mode    : 2;	/* How to handle duplicate keys */
  unsigned int clear_flag  : 1;	/* If set, zap strings when freeing */
  unsigned int icase : 1;		/* If set, keys are case insensitive */
  unsigned int prepend_flag : 1;	/* If set, dup keys are prepended */
  unsigned int regex_flag  : 1;	/* If set, keys are looked up by regex */
  char *error_msg;				/* If non-NULL, the most recent error */
} Kwv;

typedef struct Kwv_var {
  char *name;
  char **varp;
} Kwv_var;

typedef enum {
  KWV_REQUIRED    = 1,		/* One or more keys must exist */
  KWV_REQUIRED1   = 2,		/* Exactly one key must exist */
  KWV_OPTIONAL    = 3,		/* One or more keys may exist */
  KWV_OPTIONAL1   = 4,		/* Exactly one key may exist */
  KWV_RESERVED    = 5,		/* May not be set by kwv_vartab_init() */
  KWV_UNUSED      = 6,		/* A key must not exist */
  KWV_NULL        = 7,		/* End-of-table marker type */
  KWV_STACK       = 8,		/* Like KWV_OPTIONAL; see conf_merge() */
  KWV_SCAN        = 9,		/* Like KWV_STACK; see conf_merge() */
  KWV_TYPE_MASK   = 0xf,
  KWV_DERIVABLE   = 16		/* A modifier bit */
} Kwv_vartab_type;

enum {
  KWV_VARTAB_END_ID = -1
};

#define KWV_VARTAB_ENTRY(ID, STRNAME, TYPE)		\
  { ID, STRNAME, TYPE, NULL }

#define KWV_VARTAB_END()		\
  { KWV_VARTAB_END_ID, NULL, KWV_NULL, NULL }

typedef struct Kwv_vartab {
  int id;
  char *name;
  Kwv_vartab_type type;
  Kwv_pair *pair;
} Kwv_vartab;

typedef struct Kwv_iter {
  Kwv *kwv;
  int current_index;
  Kwv_pair *next_linked_pair;
  int key_only;
  regex_t regex;
} Kwv_iter;

typedef enum {
  KWV_XML_START_TAG   = 0,
  KWV_XML_END_TAG     = 1,
  KWV_XML_EMPTY_TAG   = 2,
  KWV_XML_COMMENT     = 3,
  KWV_XML_UNKNOWN_TAG = -1,
} Kwv_xml_type;

/* An XML element. */
typedef struct Kwv_xml {
  Kwv_xml_type type;
  char *el_name;
  Kwv *kwv_attrs;
} Kwv_xml;

#ifdef __cplusplus
extern "C" {
#endif

extern Kwv *kwv_init(unsigned int n);
extern Kwv *kwv_reset(Kwv *kwv);
extern int kwv_parse_str(char *str, char **name, char **value);
extern Kwv_parse_conf *kwv_parse_init(Kwv_parse_conf *c, int flags,
									  int sep_char, int end_char);
extern int kwv_parse_qstr(char *str, char **name, char **value, char **endp,
						  Kwv_parse_conf *conf);

extern Kwv_xml *kwv_xml_init(Kwv_xml *xml);
extern Kwv_xml *kwv_xml_parse(Kwv_xml *x, char *str, char **endptr);
extern Ds *kwv_xml_format(Kwv *kwv, char *el_name);

extern Kwv *kwv_make_new(char *str, Kwv_conf *conf);
extern Kwv *kwv_make_add(Kwv *k, char *str, Kwv_conf *conf);
extern Kwv *kwv_make_sep(Kwv *k, char *str, Kwv_conf *conf);
extern Kwv *kwv_add(Kwv *k, char *key, char *val);
extern Kwv *kwv_add_pair(Kwv *k, Kwv_pair *pair);
extern Kwv *kwv_add_str(Kwv *k, char *str);
extern Kwv *kwv_add_qstr(Kwv *k, char *str, char **endp, Kwv_parse_conf *conf);
extern Kwv *kwv_replace(Kwv *kwv, char *key, char *val);
extern Kwv *kwv_replace_pair(Kwv *kwv, Kwv_pair *pair);
extern Kwv_pair *kwv_new_pair(char *key, char *val, void *xval);
extern Kwv_pair *kwv_set_pair(Kwv_pair *pair, char *key, char *val,
							  void *xval);
extern Kwv_pair *kwv_lookup(Kwv *kwv, char *key);
extern char *kwv_lookup_value(Kwv *kwv, char *key);
extern int kwv_lookup_strnum(Kwv *kwv, char *key, Strnum type, void *value);
extern char *kwv_lookup_value_null(Kwv *kwv, char *key);
extern char *kwv_str(Kwv *kwv);
extern Kwv *kwv_merge(Kwv *primary, Kwv *secondary, Kwv_dup_mode mode);
extern void kwv_free(Kwv *kwv);
extern int kwv_count(Kwv *kwv, char *key);
extern int kwv_dups(Kwv *kwv, char *key);
extern Kwv *kwv_add_nocopy(Kwv *k, char *key, char *val);
extern Kwv *kwv_add_pair_nocopy(Kwv *k, Kwv_pair *pair);
extern Kwv *kwv_copy(Kwv *kwv);
extern int kwv_delete(Kwv *k, char *key);
extern int kwv_get_mode(Kwv *kwv);
extern int kwv_set_mode(Kwv *kwv, char *modestr);

extern Kwv_iter *kwv_iter_begin(Kwv *kwv, char *key);
extern Kwv_pair *kwv_iter_first(Kwv_iter *iter);
extern Kwv_pair *kwv_iter_next(Kwv_iter *iter);
extern void kwv_iter_end(Kwv_iter *iter);

extern int kwv_vartab_init(Kwv *kwv, Kwv_vartab *vartab,
						   Kwv_pair *(*derive)(Kwv_vartab *, void *), void *);
extern Kwv_vartab *kwv_vartab_lookup(Kwv_vartab *vartab, char *name);
extern int kwv_vartab_check(Kwv *kwv, Kwv_vartab *vartab, Kwv_pair **kw,
							Kwv_vartab **vt, char **errmsg);
extern Kwv_vartab *kwv_vartab_new(Kwv_vartab *vartab);
extern void kwv_vartab_text(FILE *fp, Kwv_vartab *vartab);

extern char **kwv_env(Kwv *kwv);
extern void kwv_html(FILE *fp, Kwv *kwv);
extern void kwv_text(FILE *fp, Kwv *kwv);
extern char *kwv_buf(Kwv *kwv, int sep, int quote_ch);
extern int kwv_validate(Kwv *kwv, char *op, int verbose);
extern Dsvec *kwv_keys(Kwv *kwv);

#ifdef __cplusplus
}
#endif

#endif
