/*	$NetBSD: sun_map_parse.y,v 1.4 2015/01/17 17:46:31 christos Exp $	*/

%{
/*
 * Copyright (c) 1997-2014 Erez Zadok
 * Copyright (c) 2005 Daniel P. Ottavio
 * Copyright (c) 1990 Jan-Simon Pendry
 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Jan-Simon Pendry at Imperial College, London.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *
 * File: am-utils/amd/sun_map_parse.y
 *
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <am_defs.h>
#include <amd.h>
#include <sun_map.h>


#define SUN_FSTYPE_STR  "fstype="


extern int sun_map_lex(void);
extern int sun_map_error(const char *);
extern void sun_map_tok_setbuff(const char *);
extern int sun_map_parse(void);

struct sun_entry *sun_map_parse_read(const char *);

static struct sun_list *sun_entry_list = NULL;
static struct sun_list *sun_opt_list = NULL;
static struct sun_list *sun_host_list = NULL;
static struct sun_list *sun_location_list = NULL;
static struct sun_list *mountpt_list = NULL;
static char *tmpFsType = NULL;


/*
 * Each get* function returns a pointer to the corresponding global
 * list structure.  If the structure is NULL than a new instance is
 * returned.
 */
static struct sun_list *get_sun_opt_list(void);
static struct sun_list *get_sun_host_list(void);
static struct sun_list *get_sun_location_list(void);
static struct sun_list *get_mountpt_list(void);
static struct sun_list *get_sun_entry_list(void);

%}

%union {
  char strval[2048];
}

%token NEWLINE COMMENT WSPACE
%token <strval> WORD

%%

amap : file
     ;

file : new_lines entries
     | entries
     ;

entries : entry
        | entry new_lines
        | entry new_lines entries
        ;

new_lines : NEWLINE
          | NEWLINE new_lines
          ;

entry : locations {

  struct sun_list *list;
  struct sun_entry *entry;

  /* allocate an entry */
  entry = CALLOC(struct sun_entry);

  /*
   * Assign the global location list to this entry and reset the
   * global pointer.  Reseting the global pointer will create a new
   * list instance next time get_sun_location_list() is called.
   */
  list = get_sun_location_list();
  entry->location_list = (struct sun_location *)list->first;
  sun_location_list = NULL;

   /* Add this entry to the entry list. */
  sun_list_add(get_sun_entry_list(), (qelem *)entry);
}

| '-' options WSPACE locations {

  struct sun_list *list;
  struct sun_entry *entry;

  entry = CALLOC(struct sun_entry);

  /* An fstype may have been defined in the 'options'. */
  if (tmpFsType != NULL) {
    entry->fstype = tmpFsType;
    tmpFsType = NULL;
  }

  /*
   * Assign the global location list to this entry and reset the
   * global pointer.  Reseting the global pointer will create a new
   * list instance next time get_sun_location_list() is called.
   */
  list = get_sun_location_list();
  entry->location_list = (struct sun_location *)list->first;
  sun_location_list = NULL;

  /*
   * Assign the global opt list to this entry and reset the global
   * pointer.  Reseting the global pointer will create a new list
   * instance next time get_sun_opt_list() is called.
   */
  list = get_sun_opt_list();
  entry->opt_list = (struct sun_opt *)list->first;
  sun_opt_list = NULL;

  /* Add this entry to the entry list. */
  sun_list_add(get_sun_entry_list(), (qelem *)entry);
}

| mountpoints {

  struct sun_list *list;
  struct sun_entry *entry;

  /* allocate an entry */
  entry = CALLOC(struct sun_entry);

  /*
   * Assign the global mountpt list to this entry and reset the global
   * pointer.  Reseting the global pointer will create a new list
   * instance next time get_mountpt_list() is called.
   */
  list = get_mountpt_list();
  entry->mountpt_list = (struct sun_mountpt *)list->first;
  mountpt_list = NULL;

  /* Add this entry to the entry list. */
  sun_list_add(get_sun_entry_list(), (qelem *)entry);
}

| '-' options WSPACE mountpoints {

  struct sun_list *list;
  struct sun_entry *entry;

  /* allocate an entry */
  entry = CALLOC(struct sun_entry);

  /* An fstype may have been defined in the 'options'. */
  if (tmpFsType != NULL) {
    entry->fstype = tmpFsType;
    tmpFsType = NULL;
  }

  /*
   * Assign the global mountpt list to this entry and reset the global
   * pointer.  Reseting the global pointer will create a new list
   * instance next time get_mountpt_list() is called.
   */
  list = get_mountpt_list();
  entry->mountpt_list = (struct sun_mountpt *)list->first;
  mountpt_list = NULL;

  /*
   * Assign the global opt list to this entry and reset the global
   * pointer.  Reseting the global pointer will create a new list
   * instance next time get_sun_opt_list() is called.
   */
  list = get_sun_opt_list();
  entry->opt_list = (struct sun_opt *)list->first;
  sun_opt_list = NULL;

  /* Add this entry to the entry list. */
  sun_list_add(get_sun_entry_list(), (qelem *)entry);
}
;

mountpoints : mountpoint
            | mountpoint WSPACE mountpoints
            ;

mountpoint : WORD WSPACE location {

  struct sun_list *list;
  struct sun_mountpt *mountpt;

  /* allocate a mountpt */
  mountpt = CALLOC(struct sun_mountpt);

  /*
   * Assign the global loaction list to this entry and reset the
   * global pointer.  Reseting the global pointer will create a new
   * list instance next time get_sun_location_list() is called.
   */
  list = get_sun_location_list();
  mountpt->location_list = (struct sun_location *)list->first;
  sun_location_list = NULL;

  mountpt->path = xstrdup($1);

  /* Add this mountpt to the mountpt list. */
  sun_list_add(get_mountpt_list(), (qelem *)mountpt);
}

| WORD WSPACE '-' options WSPACE location {

  struct sun_list *list;
  struct sun_mountpt *mountpt;

  /* allocate a mountpt */
  mountpt = CALLOC(struct sun_mountpt);

  /* An fstype may have been defined in the 'options'. */
  if (tmpFsType != NULL) {
    mountpt->fstype = tmpFsType;
    tmpFsType = NULL;
  }

  /*
   * Assign the global location list to this entry and reset the
   * global pointer.  Reseting the global pointer will create a new
   * list instance next time get_sun_location_list() is called.
   */
  list = get_sun_location_list();
  mountpt->location_list = (struct sun_location *)list->first;
  sun_location_list = NULL;

  /*
   * Assign the global opt list to this entry and reset the global
   * pointer.  Reseting the global pointer will create a new list
   * instance next time get_sun_opt_list() is called.
   */
  list = get_sun_opt_list();
  mountpt->opt_list = (struct sun_opt *)list->first;
  sun_opt_list = NULL;

  mountpt->path = xstrdup($1);

  /* Add this mountpt to the mountpt list. */
  sun_list_add(get_mountpt_list(), (qelem *)mountpt);
}
;

locations : location
          | location WSPACE locations
          ;

location : hosts ':' WORD {

  struct sun_list *list;
  struct sun_location *location;

  /* allocate a new location */
  location = CALLOC(struct sun_location);

  /*
   * Assign the global opt list to this entry and reset the global
   * pointer.  Reseting the global pointer will create a new list
   * instance next time get_sun_opt_list() is called.
   */
  list = get_sun_host_list();
  location->host_list = (struct sun_host *)list->first;
  sun_host_list = NULL;

  location->path = xstrdup($3);

  /* Add this location to the location list. */
  sun_list_add(get_sun_location_list(), (qelem *)location);
}

| ':' WORD {

  struct sun_location *location;

  /* allocate a new location */
  location = CALLOC(struct sun_location);

  location->path = xstrdup($2);

  /* Add this location to the location list. */
  sun_list_add(get_sun_location_list(), (qelem *)location);
}
;

hosts : host
      | host ',' hosts
      ;

host : WORD {

  /* allocate a new host */
  struct sun_host *host = CALLOC(struct sun_host);

  host->name = xstrdup($1);

  /* Add this host to the host list. */
  sun_list_add(get_sun_host_list(),(qelem *)host);
}

| WORD weight {

  /*
   * It is assumed that the host for this rule was allocated by the
   * 'weight' rule and assigned to be the last host item on the host
   * list.
   */
  struct sun_host *host = (struct sun_host *)sun_host_list->last;

  host->name = xstrdup($1);
}
;

weight : '(' WORD ')' {

  int val;
  /* allocate a new host */
  struct sun_host *host = CALLOC(struct sun_host);

  val = atoi($2);

  host->weight = val;

  /* Add this host to the host list. */
  sun_list_add(get_sun_host_list(), (qelem *)host);
}
;

options : option
        | option ',' options
        ;

option : WORD {

  char *type;

  /* check if this is an fstype option */
  if ((type = strstr($1,SUN_FSTYPE_STR)) != NULL) {
    /* parse out the fs type from the Sun fstype keyword  */
    if ((type = type + strlen(SUN_FSTYPE_STR)) != NULL) {
      /*
       * This global fstype str will be assigned to the current being
       * parsed later in the parsing.
       */
      tmpFsType = xstrdup(type);
    }
  }
  else {
    /*
     * If it is not an fstype option allocate an opt struct and assign
     * the value.
     */
    struct sun_opt *opt = CALLOC(struct sun_opt);
    opt->str = xstrdup($1);
    /* Add this opt to the opt list. */
    sun_list_add(get_sun_opt_list(), (qelem *)opt);
  }
}

;

%%

/*
 * Parse 'map_data' which is assumed to be a Sun-syle map.  If
 * successful a sun_entry is returned.
 *
 * The parser is designed to parse map entries with out the keys.  For
 * example the entry:
 *
 * usr -ro pluto:/usr/local
 *
 * should be passed to the parser as:
 *
 * -ro pluto:/usr/local
 *
 * The reason for this is that the Amd info services already strip off
 * the key when they read map info.
 */
struct sun_entry *
sun_map_parse_read(const char *map_data)
{
  struct sun_entry *retval = NULL;

  /* pass map_data to lex */
  sun_map_tok_setbuff(map_data);

  /* call yacc */
  sun_map_parse();

  if (sun_entry_list != NULL) {
    /* return the first Sun entry in the list */
    retval = (struct sun_entry*)sun_entry_list->first;
    sun_entry_list = NULL;
  }
  else {
    plog(XLOG_ERROR, "Sun map parser did not produce data structs.");
  }

  return retval;
}


static struct sun_list *
get_sun_entry_list(void)
{
  if (sun_entry_list == NULL) {
    sun_entry_list = CALLOC(struct sun_list);
  }
  return sun_entry_list;
}


static struct sun_list *
get_mountpt_list(void)
{
  if (mountpt_list == NULL) {
    mountpt_list = CALLOC(struct sun_list);
  }
  return mountpt_list;
}


static struct sun_list *
get_sun_location_list(void)
{
  if (sun_location_list == NULL) {
    sun_location_list = CALLOC(struct sun_list);
  }
  return sun_location_list;
}


static struct sun_list *
get_sun_host_list(void)
{
  if (sun_host_list == NULL) {
    sun_host_list = CALLOC(struct sun_list);
  }
  return sun_host_list;
}


static struct sun_list *
get_sun_opt_list(void)
{
  if (sun_opt_list == NULL) {
    sun_opt_list = CALLOC(struct sun_list);
  }
  return sun_opt_list;
}