/*
 * COPYRIGHT (c) International Business Machines Corp. 2013-2017
 *
 * This program is provided under the terms of the Common Public License,
 * version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
 * software constitutes recipient's acceptance of CPL-1.0 terms which can be
 * found in the file LICENSE file or at
 * https://opensource.org/licenses/cpl1.0.php
 */

%{
/*
 * Parse openCryptoki's config file.
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#include "log.h"
#include "slotmgr.h"
#include "pkcsslotd.h"

Slot_Info_t_64 sinfo_struct;
unsigned long int Index;
int slot_count = 0;

#define ERRSTRLEN	256
#define DEF_MANUFID	"IBM"
#define DEF_SLOTDESC	"Linux"

static char errbuf[ERRSTRLEN + 1];

extern char *strsep(char **stringp, const char *delim);
extern FILE *yyin;
extern int yyparse();
extern void yyerror(const char *s);
extern int line_num;
extern int yylex();

typedef enum {
	KW_STDLL,
	KW_SLOTDESC,
	KW_MANUFID,
	KW_HWVERSION,
	KW_FWVERSION,
	KW_CONFNAME,
	KW_TOKNAME,
	KW_TOKVERSION,
	KW_MAX
} keyword_token;

struct ock_key {
	char *name;
	keyword_token token;
};

static const struct ock_key ock_keywords[] = {
	{"stdll",       	KW_STDLL},
	{"description", 	KW_SLOTDESC},
	{"manufacturer",	KW_MANUFID},
	{"hwversion",		KW_HWVERSION},
	{"firmwareversion",	KW_FWVERSION},
	{"confname",		KW_CONFNAME},
	{"tokname",		KW_TOKNAME},
	{"tokversion",		KW_TOKVERSION}
};

void set_init(void);
void set_defaults(void);
int lookup_keyword(const char *key);
int do_str(char *slotinfo, size_t size, char* kw, char *val);
int do_vers(CK_VERSION *slotinfo, char *kw, char *val);

%}

%union {
	char *str;
	unsigned int num;
}

%token EQUAL DOT SLOT EOL OCKVERSION BEGIN_DEF END_DEF
%token <str> STRING
%token <str> KEYWORD
%token <num> INTEGER
%token <num> TOKVERSION

%%

config_file:
	config_file sections
	|
	;

sections:
	OCKVERSION STRING EOL
	{
		free($2);
	}
	| SLOT INTEGER BEGIN_DEF EOL
	{
		/* inititalize sinfo_struct */
		set_init();
		sinfo_struct.slot_number = $2;
		Index = $2;

	} keyword_defs END_DEF
	{
		/* set some defaults if needed before copying */
		set_defaults();
		memcpy(&sinfo[Index], &sinfo_struct, sizeof(sinfo_struct));
		slot_count++;
	}
	| EOL
	;

keyword_defs:
	STRING EQUAL TOKVERSION EOL keyword_defs
	{
		int kw;

		kw = lookup_keyword($1);

		switch (kw) {
		case KW_TOKVERSION:
			sinfo_struct.version = $3;
			break;
		default:
			yyerror("unknown config keyword");
			break;
		}
	}
	| STRING EQUAL STRING EOL keyword_defs
	{
		int kw;

		kw = lookup_keyword($1);

		switch (kw) {
		case KW_STDLL:
			sinfo_struct.present = TRUE;
			sinfo_struct.pk_slot.flags |= (CKF_TOKEN_PRESENT);
			memset(sinfo_struct.dll_location, 0, sizeof(sinfo_struct.dll_location));
			memcpy(sinfo_struct.dll_location, $3, strlen($3));
			break;
		case KW_SLOTDESC:
			do_str((char *)sinfo_struct.pk_slot.slotDescription,
			  sizeof(sinfo_struct.pk_slot.slotDescription), $1, $3);
			break;
		case KW_MANUFID:
			do_str((char *)sinfo_struct.pk_slot.manufacturerID,
			   sizeof(sinfo_struct.pk_slot.manufacturerID), $1, $3);
			break;
		case KW_HWVERSION:
			do_vers(&sinfo_struct.pk_slot.hardwareVersion, $1, $3);
			break;
		case KW_FWVERSION:
			do_vers(&sinfo_struct.pk_slot.firmwareVersion, $1, $3);
			break;
		case KW_CONFNAME:
			memset(sinfo_struct.confname, 0, sizeof(sinfo_struct.confname));
			memcpy(sinfo_struct.confname, $3, strlen($3));
			break;
		case KW_TOKNAME:
			memset(sinfo_struct.tokname, 0, sizeof(sinfo_struct.tokname));
			memcpy(sinfo_struct.tokname, $3, strlen($3));
			break;
		default:
			yyerror("unknown config keyword");
			break;
		}
		free ($3);
	}
	|
	;

%%

void
yyerror(const char *s)
{
	fprintf(stderr, "parse error on line %d: %s\n", line_num, s);
}

void
set_init(void)
{
	memset(&sinfo_struct, 0, sizeof(sinfo_struct));
}

void
set_defaults(void)
{
	/* set some defaults if user hasn't set these. */
	if (!sinfo_struct.pk_slot.slotDescription[0]) {
		memset(&sinfo_struct.pk_slot.slotDescription[0], ' ',
		       sizeof(sinfo_struct.pk_slot.slotDescription));
		memcpy(&sinfo_struct.pk_slot.slotDescription[0],
			DEF_SLOTDESC, strlen(DEF_SLOTDESC));
	}
	if (!sinfo_struct.pk_slot.manufacturerID[0]) {
		memset(&sinfo_struct.pk_slot.manufacturerID[0], ' ',
		       sizeof(sinfo_struct.pk_slot.manufacturerID));
		memcpy(&sinfo_struct.pk_slot.manufacturerID[0],
			DEF_MANUFID, strlen(DEF_MANUFID));
	}
}

int
do_str(char *slotinfo, size_t size, char* kw, char *val)
{
	if (strlen(val) > size) {
		snprintf(errbuf, ERRSTRLEN, "%s has too many characters\n", kw);
		yyerror(errbuf);
		return -1 ;
	}
	memcpy(slotinfo, val, strlen(val));
	return 0;
}

int
do_vers(CK_VERSION *slotinfo, char *kw, char *val)
{
	char **ap, *argp[2];
	char *valp;

	if (!val) {
		snprintf(errbuf, ERRSTRLEN, "%s has no value\n", kw);
		yyerror(errbuf);
		return -1 ;
	}

	valp = val;
	for (ap = argp; (*ap = strsep(&valp, ".")) != NULL;)
		if (**ap != '\0')
			if (++ap >= &argp[2])
				break;

	slotinfo->major = (char) atoi(argp[0]);
	if(!argp[1])
		slotinfo->minor = 0;
	else
		slotinfo->minor = (char) atoi(argp[1]);

	return 0;
}

int
lookup_keyword(const char *key)
{
	int i;

	for (i = 0; i < KW_MAX ; i++ ) {
		if (strncmp(key, ock_keywords[i].name, strlen(key)) == 0)
			return ock_keywords[i].token;
	}
	/* if we get here that means did not find a match... */
	return -1;
}

int
load_and_parse(const char *configfile)
{

	FILE *conf;

	extern FILE *yyin;

	conf = fopen(configfile, "r");

	if (!conf) {
		fprintf(stderr, "Failed to open %s: %s\n", configfile, strerror(errno));
		return -1;
	}

	yyin = conf;

	do {
		yyparse();

	} while (!feof(yyin));

	fclose(conf);

	NumberSlotsInDB = slot_count;

	return 0;
}
