/*
 * kgram.y -- YACC grammar for key-map interpreter
 *
 * Copyright 1989 by Eric S. Raymond <esr@snark.thyrsus.com>, all rights 
 * reserved.  You may use, modify or redistribute this code freely, but do
 * not delete this notice. Check with me before you make money with it.
 */
%{
/*LINTLIBRARY*/
#ifndef lint
static char *sccsid = "@(#)kgram.y	1.3";
#endif /* lint */

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/at_ansi.h>
#include <sys/kd.h>
#include "keybind.h"

extern kbd_t	kbd;
extern char yytext[];

extern void enable_alt(), list_shift(), restore_defaults();
extern void dump_keymap(), dump_funkeys();
extern void set_key(), set_string(), clear_strings();
extern void do_changes();

static int	prefval;
static int	codes1[NUM_KEYS];
static int	ccount;
static int	codes2[NUM_KEYS];

static void do_help();
%}   

%start map

%token SCANCODE CHARKEY FUNKEY SPECIAL
%token PREFIX STRING
%token SHIFTER ENABLED DISABLED
%token META RESTORE DUMP SHIFTS FUNKEYS CLEARSTRINGS EXECUTE HELP
%token ASSIGN DASH
%token NEWLINE	10
%token LEXERR

%%	/* beginning of rules section */

/* a map description consists of a sequence of commands */
map	:    /* EMPTY */
	|    map command NEWLINE
	|    map error NEWLINE
		{yyerrok;}
	|    map NEWLINE
	;

command	:    SCANCODE funcs
		{
		    int i;

		    if (ccount > NUM_STATES)
			yyerror("too many functions in scancode spec");
		    else
			for (i = 0; i < ccount; i++)
			    if (codes1[i] >= 0)
				setkey($1, i, codes1[i]);
		}

	|    SCANCODE ASSIGN func
		{setkey($1, 0, $3);}
	|    PREFIX SCANCODE ASSIGN func
		{setkey($2, $1, $4);}
	|    PREFIX PREFIX SCANCODE ASSIGN func
		{setkey($3, ($1 | $2), $5);}
	|    PREFIX PREFIX PREFIX SCANCODE ASSIGN func
		{setkey($4, ($1 | $2 | $3), $6);}

	|    FUNKEY ASSIGN STRING
    		{setstring($1 - K_FUNF, yytext);}
	|    SHIFTER ENABLED ranges
		{
		    int i, j;

		    if (ccount == 0)
		    {
			codes1[0] = 0;
			codes2[0] = NUM_KEYS - 1;
			ccount++;
		    }
		    for (i = 0; i < ccount; i++)
			for (j = codes1[i]; j <= codes2[i]; j++)
			    kbd.keymap.key[j].flgs |= $1;
		}

	|    SHIFTER DISABLED ranges
		{
		    int i, j;

		    if (ccount == 0)
		    {
			codes1[0] = 0;
			codes2[0] = NUM_KEYS - 1;
			ccount++;
		    }
		    for (i = 0; i < ccount; i++)
			for (j = codes1[i]; j <= codes2[i]; j++)
			    kbd.keymap.key[j].flgs &=~ $1;
		}
	|    DUMP ranges
		{
		    int i, j;

		    if (ccount == 0)
			dump_keymap(0, kbd.keymap.n_keys - 1);
		    else
			for (i = 0; i < ccount; i++)
			    dump_keymap(codes1[i], codes2[i]);
		}
	|    FUNKEYS franges
		{
		    int i, j;

		    if (ccount == 0)
			dump_funkeys(1, NSTRKEYS);
		    else
			for (i = 0; i < ccount; i++)
			    dump_funkeys(codes1[i]-K_FUNF+1,codes2[i]-K_FUNF+1);
		}
	|    SHIFTER
		{
		    if ($1 == KMF_CLOCK)
			list_shift(KMF_CLOCK, "capslock");
		    else
			list_shift(KMF_NLOCK, "numlock ");
		}
	|    META
		{enable_alt();}
	|    RESTORE
		{restore_defaults();}
	|    CLEARSTRINGS
		{clear_strings();}
	|    EXECUTE
		{do_changes();};
	|    HELP
		{do_help();};
	;

ranges	:    /* EMPTY */
		{ccount = 0;}
	|    ranges SCANCODE DASH SCANCODE
		{codes1[ccount] = $2; codes2[ccount] = $4; ccount++;}
	|    ranges SCANCODE
		{codes1[ccount] = codes2[ccount] = $2; ccount++;}
	;

franges	:    /* EMPTY */
		{ccount = 0;}
	|    franges FUNKEY DASH FUNKEY
		{codes1[ccount] = $2; codes2[ccount] = $4; ccount++;}
	|    franges FUNKEY
		{codes1[ccount] = codes2[ccount] = $2; ccount++;}
	;

func	:    CHARKEY		{$$ = ($1 & 0xff);}
	|    FUNKEY		{$$ = ($1 & 0xff);}
	|    SPECIAL		{$$ = $1 + NUM_KEYS;}
	;

funcs	:    func
		{ccount = 0; codes1[ccount++] = $1;}
	|    funcs func
		{codes1[ccount++] = $2;}
	|    funcs DASH
		{codes1[ccount++] = -1;}
	|    /* EMPTY */
		{(void)yyerror("expected a list of function codes"); YYERROR;}
	;
%%

int yyerror(message)
char	*message;
{
    (void) fprintf(stderr, "keybind: %s\n", message);
}

void do_help()
{
#define P(s)	(void) printf(s)
P("Keybind commands are:\n\n");

P("@scancode [func...]              -- assign functions to scancode\n");
P("[prefix] @scancode = [func]      -- assign to key/state pair\n\n");

P("fkey<nn> = \"string\"              -- load string key\n\n");

P("cl[earstrings]                   -- clear the string table\n");
P("capslock di[sabled] [range...]   -- disable capslock on chars\n");
P("numlock di[sabled] [range...]    -- disable numlock on chars\n");
P("du[mp] [range...]                -- show functions of given keys\n");
P("[ca]pslock en[abled] [range...]  -- enable capslock on chars\n");
P("[nu]mlock en[abled] [range...]   -- enable numlock on chars\n");
P("ex[ecute]                        -- write changes to the driver\n");
P("fu[nkeys] [range...]             -- show bindings of string keys\n");
P("he[lp]                           -- display this message\n");
P("me[ta]                           -- enable the Alt key as meta\n");
P("re[store]                        -- restore defaults\n");
P("ca[pslock]                       -- show keys capslock affects\n");
P("nu[mlock]                        -- show keys numlock affects\n\n");

P("Ranges may be a character literal or two dash-separated char literals.\n");
P("Functions may be char literals or one of the named special functions\n");
P("documented in keyboard(7)\n");
}

/* kgram.y ends here */
