%{
/* ----------------------------------------------------------------------- *
 *   
 *  nss_parser.y - nsswitch parser.
 *
 *   Copyright 2006 Ian Kent <raven@themaw.net>
 *
 *   This program 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, Inc., 675 Mass Ave, Cambridge MA 02139,
 *   USA; either version 2 of the License, or (at your option) any later
 *   version.
 *   
 *   This program 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.
 *
 * ----------------------------------------------------------------------- */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <limits.h>

#include "automount.h"
#include "nsswitch.h"
#include "nss_parse.tab.h"

static pthread_mutex_t parse_mutex = PTHREAD_MUTEX_INITIALIZER;

static struct list_head *nss_list;
static struct nss_source *src;
struct nss_action act[NSS_STATUS_MAX];

#define YYDEBUG 0

#ifndef YYENABLE_NLS
#define YYENABLE_NLS 0
#endif
#ifndef YYLTYPE_IS_TRIVIAL
#define YYLTYPE_IS_TRIVIAL 0
#endif

unsigned int nss_automount_found;

extern int nss_lineno;
extern int nss_lex(void);
extern FILE *nss_in;

static int nss_ignore(const char *s);
static int nss_error(const char *s);

%}

%union {
char strval[128];
}

%token LBRACKET RBRACKET EQUAL BANG NL
%token <strval> SOURCE
%token <strval> STATUS
%token <strval> ACTION

%start file

%%

file: {
#if YYDEBUG != 0
		nss_debug = YYDEBUG;
#endif
	} sources NL
	;

sources: nss_source
	| nss_source sources
	;

nss_source: SOURCE
{
	if (!strcmp($1, "files") || !strcmp($1, "yp") ||
	    !strcmp($1, "nis") || !strcmp($1, "ldap") ||
	    !strcmp($1, "nisplus") || !strcmp($1, "hesiod") ||
	    !strcmp($1, "sss"))
		src = add_source(nss_list, $1);
	else
		nss_ignore($1);
} | SOURCE LBRACKET status_exp_list RBRACKET
{
	enum nsswitch_status a;

	if (!strcmp($1, "files") || !strcmp($1, "yp") ||
	    !strcmp($1, "nis") || !strcmp($1, "ldap") ||
	    !strcmp($1, "nisplus") || !strcmp($1, "hesiod") ||
	    !strcmp($1, "sss")) {
		src = add_source(nss_list, $1);
		for (a = 0; a < NSS_STATUS_MAX; a++) {
			if (act[a].action != NSS_ACTION_UNKNOWN) {
				src->action[a].action = act[a].action;
				src->action[a].negated = act[a].negated;
			}
		}
	} else
		nss_ignore($1);
} | SOURCE LBRACKET status_exp_list SOURCE { nss_error("missing close bracket"); YYABORT; }
  | SOURCE LBRACKET status_exp_list NL { nss_error("missing close bracket"); YYABORT; }
  | SOURCE LBRACKET SOURCE { nss_error($3); YYABORT; }
  | error SOURCE { nss_error($2); YYABORT; };

status_exp_list: status_exp
		| status_exp status_exp_list

status_exp: STATUS EQUAL ACTION
{
	set_action(act, $1, $3, 0);
} | BANG STATUS EQUAL ACTION
{
	set_action(act, $2, $4, 1);
} | STATUS EQUAL SOURCE {nss_error($3); YYABORT; }
  | STATUS SOURCE {nss_error($2); YYABORT; }
  | BANG STATUS EQUAL SOURCE {nss_error($4); YYABORT; }
  | BANG STATUS SOURCE {nss_error($3); YYABORT; }
  | BANG SOURCE {nss_error($2); YYABORT; };

%%

static int nss_ignore(const char *s)
{
	logmsg("ignored unsupported autofs nsswitch source \"%s\"", s);
	return(0);
}

static int nss_error(const char *s)
{
	logmsg("syntax error in nsswitch config near [ %s ]\n", s);
	return(0);
}

static void parse_mutex_lock(void)
{
	int status = pthread_mutex_lock(&parse_mutex);
	if (status)
		fatal(status);
	return;
}

static void parse_mutex_unlock(void *arg)
{
	int status = pthread_mutex_unlock(&parse_mutex);
	if (status)
		fatal(status);
	return;
}

static void parse_close_nsswitch(void *arg)
{
	FILE *nsswitch = (FILE *) arg;
	fclose(nsswitch);
	return;
}

int nsswitch_parse(struct list_head *list)
{
	FILE *nsswitch;
	int status;

	nsswitch = open_fopen_r(NSSWITCH_FILE);
	if (!nsswitch) {
		logerr("couldn't open %s\n", NSSWITCH_FILE);
		return 1;
	}

	pthread_cleanup_push(parse_close_nsswitch, nsswitch);

	parse_mutex_lock();
	pthread_cleanup_push(parse_mutex_unlock, NULL);

	nss_in = nsswitch;

	nss_automount_found = 0;
	nss_list = list;
	status = nss_parse();
	nss_list = NULL;

	/* No "automount" nsswitch entry, use "files" */
	if (!nss_automount_found)
		if (add_source(list, "files"))
			status = 0;

	pthread_cleanup_pop(1);
	pthread_cleanup_pop(1);

	if (status)
		return 1;

	return 0;
}
