/*  -
   Copyright (C) 2006 Weongyo Jeong (weongyo@gmail.com)

This file is part of ROVM.

ROVM is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.

ROVM is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.  */

#ifndef _ROVM_CONFIG_H
#define _ROVM_CONFIG_H

typedef struct command_struct command_rec;
typedef struct rc_directive_t rc_directive_t;

/* Just in case your linefeed isn't the one the other end is expecting. */
#if !APR_CHARSET_EBCDIC
/** linefeed */
#define LF 10
/** carrige return */
#define CR 13
/** carrige return /Line Feed Combo */
#define CRLF "\015\012"
#else /* APR_CHARSET_EBCDIC */
/* For platforms using the EBCDIC charset, the transition ASCII->EBCDIC is done
 * in the buff package (bread/bputs/bwrite).  Everywhere else, we use
 * "native EBCDIC" CR and NL characters. These are therefore
 * defined as
 * '\r' and '\n'.
 */
#define CR '\r'
#define LF '\n'
#define CRLF "\r\n"
#endif /* APR_CHARSET_EBCDIC */

/** Common structure for reading of config files / passwd files etc. */
typedef struct rc_configfile_t rc_configfile_t;
struct rc_configfile_t 
{
  int (*getch) (void *param);     /**< a getc()-like function */
  /**< a fgets()-like function */
  void *(*getstr) (void *buf, size_t bufsiz, void *param);
  int (*close) (void *param);     /**< a close handler function */
  void *param;                    /**< the argument passed to getch/getstr/close */
  const char *name;               /**< the filename / description */
  unsigned line_number;           /**< current line number, starting at 1 */
};

/**
 * This can be returned by a function if they don't wish to handle
 * a command. Make it something not likely someone will actually use
 * as an error code.
 */
#define DECLINE_CMD "\a\b"

#define CMD_ROVM(NODE)          ((NODE)->r)

typedef struct cmd_parms_struct cmd_parms;
struct cmd_parms_struct 
{
  /** Argument to command from cmd_table */
  void *info;
  /** configuration command */
  const command_rec *cmd;

  /** Pool to allocate new storage in */
  apr_pool_t *pool;
  /** Pool for scratch memory; persists during configuration, but
   *  wiped before the first request is served...  */
  apr_pool_t *temp_pool;
 
  struct rovm *r;

  /** Which allow-override bits are set */
  int override;
  /** Which allow-override-opts bits are set */
  int override_opts;

  /** Config file structure. */
  rc_configfile_t *config_file;
  /** directive with syntax error */
  const rc_directive_t *err_directive;
  /** the directive specifying this command */
  rc_directive_t *directive;
};

/**
 * All the types of functions that can be used in directives
 * @internal
 */
typedef union 
{
  /** function to call for a no-args */
  const char *(*no_args) (cmd_parms *parms, void *mconfig);
  /** function to call for a raw-args */
  const char *(*raw_args) (cmd_parms *parms, void *mconfig, const char *args);
  /** function to call for a argv/argc */
  const char *(*take_argv) (cmd_parms *parms, void *mconfig, int argc, char *const argv[]);
  /** function to call for a take1 */
  const char *(*take1) (cmd_parms *parms, void *mconfig, const char *w);
  /** function to call for a take2 */
  const char *(*take2) (cmd_parms *parms, void *mconfig, const char *w, const char *w2);
  /** function to call for a take3 */
  const char *(*take3) (cmd_parms *parms, void *mconfig, const char *w, const char *w2, const char *w3);
  /** function to call for a flag */
  const char *(*flag) (cmd_parms *parms, void *mconfig, int on);
} cmd_func;

/** This configuration directive does not take any arguments */
# define RC_NO_ARGS     func.no_args
/** This configuration directive will handle it's own parsing of arguments*/
# define RC_RAW_ARGS    func.raw_args
/** This configuration directive will handle it's own parsing of arguments*/
# define RC_TAKE_ARGV   func.take_argv
/** This configuration directive takes 1 argument*/
# define RC_TAKE1       func.take1
/** This configuration directive takes 2 arguments */
# define RC_TAKE2       func.take2
/** This configuration directive takes 3 arguments */
# define RC_TAKE3       func.take3
/** This configuration directive takes a flag (on/off) as a argument*/
# define RC_FLAG        func.flag

/** method of declaring a directive with no arguments */
# define RC_INIT_NO_ARGS(directive, func, mconfig, where, help) \
    { directive, { .no_args=func }, mconfig, where, RAW_ARGS, help }
/** method of declaring a directive with raw argument parsing */
# define RC_INIT_RAW_ARGS(directive, func, mconfig, where, help) \
    { directive, { .raw_args=func }, mconfig, where, RAW_ARGS, help }
/** method of declaring a directive with raw argument parsing */
# define RC_INIT_TAKE_ARGV(directive, func, mconfig, where, help) \
    { directive, { .take_argv=func }, mconfig, where, TAKE_ARGV, help }
/** method of declaring a directive which takes 1 argument */
# define RC_INIT_TAKE1(directive, func, mconfig, where, help) \
    { directive, { .take1=func }, mconfig, where, TAKE1, help }
/** method of declaring a directive which takes multiple arguments */
# define RC_INIT_ITERATE(directive, func, mconfig, where, help) \
    { directive, { .take1=func }, mconfig, where, ITERATE, help }
/** method of declaring a directive which takes 2 arguments */
# define RC_INIT_TAKE2(directive, func, mconfig, where, help) \
    { directive, { .take2=func }, mconfig, where, TAKE2, help }
/** method of declaring a directive which takes 1 or 2 arguments */
# define RC_INIT_TAKE12(directive, func, mconfig, where, help) \
    { directive, { .take2=func }, mconfig, where, TAKE12, help }
/** method of declaring a directive which takes multiple 2 arguments */
# define RC_INIT_ITERATE2(directive, func, mconfig, where, help) \
    { directive, { .take2=func }, mconfig, where, ITERATE2, help }
/** method of declaring a directive which takes 1 or 3 arguments */
# define RC_INIT_TAKE13(directive, func, mconfig, where, help) \
    { directive, { .take3=func }, mconfig, where, TAKE13, help }
/** method of declaring a directive which takes 2 or 3 arguments */
# define RC_INIT_TAKE23(directive, func, mconfig, where, help) \
    { directive, { .take3=func }, mconfig, where, TAKE23, help }
/** method of declaring a directive which takes 1 to 3 arguments */
# define RC_INIT_TAKE123(directive, func, mconfig, where, help) \
    { directive, { .take3=func }, mconfig, where, TAKE123, help }
/** method of declaring a directive which takes 3 arguments */
# define RC_INIT_TAKE3(directive, func, mconfig, where, help) \
    { directive, { .take3=func }, mconfig, where, TAKE3, help }
/** method of declaring a directive which takes a flag (on/off) as a argument*/
# define RC_INIT_FLAG(directive, func, mconfig, where, help) \
    { directive, { .flag=func }, mconfig, where, FLAG, help }

/**
 * How the directives arguments should be parsed.
 * @remark Note that for all of these except RAW_ARGS, the config routine is
 *      passed a freshly allocated string which can be modified or stored
 *      or whatever...
 */
enum cmd_how 
  {
    RAW_ARGS,                   /**< cmd_func parses command line itself */
    TAKE1,                      /**< one argument only */
    TAKE2,                      /**< two arguments only */
    ITERATE,                    /**< one argument, occuring multiple times
                                 * (e.g., IndexIgnore)
                                 */
    ITERATE2,                   /**< two arguments, 2nd occurs multiple times
                                 * (e.g., AddIcon)
                                 */
    FLAG,                       /**< One of 'On' or 'Off' */
    NO_ARGS,                    /**< No args at all, e.g. </Directory> */
    TAKE12,                     /**< one or two arguments */
    TAKE3,                      /**< three arguments only */
    TAKE23,                     /**< two or three arguments */
    TAKE123,                    /**< one, two or three arguments */
    TAKE13,                     /**< one or three arguments */
    TAKE_ARGV                   /**< an argc and argv are passed */
  };

/**
 * The command record structure.  Each modules can define a table of these
 * to define the directives it will implement.
 */
struct command_struct 
{
  /** Name of this command */
  const char *name;
  /** The function to be called when this directive is parsed */
  cmd_func func;
  /** Extra data, for functions which implement multiple commands... */
  void *cmd_data;
  /** What overrides need to be allowed to enable this command. */
  int req_override;
  /** What the command expects as arguments
   *  @defvar cmd_how args_how*/
  enum cmd_how args_how;
  
  /** 'usage' message, in case of syntax errors */
  const char *errmsg;
};

/**
 * @defgroup ConfigDirectives Allowed locations for configuration directives.
 *
 * The allowed locations for a configuration directive are the union of
 * those indicated by each set bit in the req_override mask.
 *
 * @{
 */
#define OR_NONE 0             /**< *.conf is not available anywhere in this override */
#define OR_LIMIT 1           /**< *.conf inside <Directory> or <Location>
                                and .htaccess when AllowOverride Limit */
#define OR_OPTIONS 2         /**< *.conf anywhere
                                and .htaccess when AllowOverride Options */
#define OR_FILEINFO 4        /**< *.conf anywhere
                                and .htaccess when AllowOverride FileInfo */
#define OR_AUTHCFG 8         /**< *.conf inside <Directory> or <Location>
                                and .htaccess when AllowOverride AuthConfig */
#define OR_INDEXES 16        /**< *.conf anywhere
                                and .htaccess when AllowOverride Indexes */
#define OR_UNSET 32          /**< unset a directive (in Allow) */
#define ACCESS_CONF 64       /**< *.conf inside <Directory> or <Location> */
#define RSRC_CONF 128        /**< *.conf outside <Directory> or <Location> */
#define EXEC_ON_READ 256     /**< force directive to execute a command
                which would modify the configuration (like including another
                file, or IFModule */
/** this directive can be placed anywhere */
#define OR_ALL (OR_LIMIT|OR_OPTIONS|OR_FILEINFO|OR_AUTHCFG|OR_INDEXES)

/**
 * @brief Structure used to build the config tree.
 *
 * The config tree only stores
 * the directives that will be active in the running server.  Directives
 * that contain other directions, such as <Directory ...> cause a sub-level
 * to be created, where the included directives are stored.  The closing
 * directive (</Directory>) is not stored in the tree.
 */
struct rc_directive_t 
{
  /** The current directive */
  const char *directive;
  /** The arguments for the current directive, stored as a space
   *  separated list */
  const char *args;
  /** The next directive node in the tree
   *  @defvar rc_directive_t *next */
  struct rc_directive_t *next;
  /** The first child node of this directive
   *  @defvar rc_directive_t *first_child */
  struct rc_directive_t *first_child;
  /** The parent node of this directive
   *  @defvar rc_directive_t *parent */
  struct rc_directive_t *parent;
  
  /** directive's module can store add'l data here */
  void *data;
  
  /* ### these may go away in the future, but are needed for now */
  /** The name of the file this directive was found in */
  const char *filename;
  /** The line number the directive was on */
  int line_num;
};

extern int rc_read_config (struct rovm *, rc_pool_t *, const char *, rc_directive_t **);
extern rc_status_t rv_pcfg_openfile (rc_configfile_t **ret_cfg, rc_pool_t *p, const char *name);
extern int rc_cfg_closefile (rc_configfile_t *cfp);
extern int rc_cfg_getline (char *buf, size_t bufsize, rc_configfile_t *cfp);

#endif
