%{
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 * Copyright (c) 1999-2000 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <stdio.h>
#include <locale.h>
#include <sys/param.h>
#include <sys/fs/udf_volume.h>

char	shell_name[128] = "/bin/sh";
extern char	prompt[];
extern uint16_t ricb_prn;
extern uint32_t ricb_loc;
extern int32_t bsize, bmask, l2d, l2b;

int	base = 16;
int	old_value = 0;
int	value = 0;
int	count = 0;


int	last_op_type = 0;
#define	TYPE_NONE	0
#define	TYPE_INODE	1
#define	TYPE_DIRENT	2
#define	TYPE_BLOCK	3
#define	TYPE_CD		4

uint32_t i_number = 0;
uint32_t d_entry = 0;
int	error_override = 0;

int	register_array[256];
char	cwd[MAXPATHLEN] = "/";

int32_t ls_flags;
#define	LONG_LIST	0x1
#define	RECU_LIST	0x2
#define	LIST_LS		0x4

int32_t find_flags;
#define	FIND_DIR	0x1
#define	FIND_NAME	0x2
#define	FIND_INODE	0x4
#define	FIND_DONE	0x8
char find_dir[1024];
char find_name[1024];
uint32_t find_in;

%}

%union
{
	uint8_t		*strval;
	uint64_t	intval;
};

%token BASE BLOCK CD DIRECTORY TFILE FIND FILL
%token INODE LS OVERRIDE PROMPT PWD QUIT TAG BANG

%token AVD MVDS RVDS INTS FSDS ROOT
%token ATTZ ATYE ATMO ATDA ATHO ATMI ATSE ATCE ATHU ATMIC
%token CTTZ CTYE CTMO CTDA CTHO CTMI CTSE CTCE CTHU CTMIC
%token MTTZ MTYE MTMO MTDA MTHO MTMI MTSE MTCE MTHU MTMIC
%token GID LN MD MAJ MIO NM SZ UID UNIQ
%token DOT
%token NL

%token WORD

%left '-' '+'
%left '*' '%'

%type <strval>	WORD
%type <intval> expr

%%

session		: statement_list

statement_list	: /* empty */			{ print_prompt(); }
		| statement_list statement
			{
				ls_flags = 0;
			}
		;

statement	: empty_statement
		| current_value
		| register
		| base | block | cd | directory | file | find | fill
		| inode | ls | override | nprompt | pwd | quit | tag | shell
		| avd | mvds | rvds | ints | fsds | root
		| at | ct | gid | ln | mt | md
		| maj | min | nm | sz | uid | uniq
		| dump | texpr
		| error	{ yyclearin; yyerrok; }
		;

empty_statement	: NL
			{
				print_prompt();
			}
		;


current_value	: DOT
			{
				if (last_op_type == TYPE_INODE) {
					print_inode(i_number << l2b);
				} else if (last_op_type == TYPE_DIRENT) {
					print_dent(i_number << l2b, d_entry);
				} else {
					fprintf(stdout,
						gettext("%x\n"), value);
				}
			}
			;

register	: '<' WORD
			{
				if ((strlen((caddr_t)$2) == 1) &&
					((($2[0] >= 'a') &&
						($2[0] <= 'z')) ||
					(($2[0] >= 'A') &&
						($2[0] <= 'Z')))) {
					value = register_array[$2[0]];
				} else {
					fprintf(stdout,
						gettext("Registers can"
						" be only a-z or A-Z\n"));
				}
			}
		| '>' WORD
			{
				if ((strlen((caddr_t)$2) == 1) &&
					((($2[0] >= 'a') &&
						($2[0] <= 'z')) ||
					(($2[0] >= 'A') &&
						($2[0] <= 'Z')))) {
					register_array[$2[0]] = value;
				} else {
					fprintf(stdout,
						gettext("Registers can"
						" be only a-z or A-Z\n"));
				}
			}
		;

base		: BASE '=' expr
			{
				if (($3 == 8) || ($3 == 10) || ($3 == 16)) {
					base = $3;
				} else {
					fprintf(stdout,
						gettext("Requested %x Only"
						" Oct, Dec and"
						" Hex are Supported\n"), $3);
				}
			}
		| BASE
			{
				fprintf(stdout,
					gettext("Current Base in Decimal"
					" : %d\n"), base);
			}
		;

block		: BLOCK
			{
				last_op_type = TYPE_NONE;
				value = value * DEV_BSIZE;
			}
		;

cd		: CD ' ' WORD
			{
				uint8_t fl;
				uint32_t temp;
				char temp_cwd[MAXPATHLEN];

				strcpy(temp_cwd, cwd);
				if (strcmp((caddr_t)$3, "..") == 0) {
					if (strlen(temp_cwd) == 1) {
						if (temp_cwd[0] != '/') {
							fprintf(stdout,
							gettext("cwd is invalid"
							"setting to /\n"));
							strcpy(temp_cwd, "/");
						}
					} else {
						dirname(temp_cwd);
					}
				} else {
					int32_t len;

					len = strlen(temp_cwd);
					if (temp_cwd[len - 1] != '/') {
						temp_cwd[len] = '/';
						temp_cwd[len + 1] = '\0';
					}
					strcat(temp_cwd, (caddr_t)$3);
				}
				if (inode_from_path(temp_cwd, &temp,
							&fl) != 0) {
					fprintf(stdout,
						gettext("Could not locate inode"
						" for path %s\n"), temp_cwd);
					strcpy(temp_cwd, "/");
					if ((temp = ud_xlate_to_daddr(ricb_prn,
						ricb_loc)) == 0) {
						fprintf(stdout,
						gettext("Failed to translate"
						" prn %x loc %x\n"),
						ricb_prn, ricb_loc);
					}
				} else {
					if ((fl & FID_DIR) == 0) {
						fprintf(stdout,
						gettext("%s is not a"
						" directory\n"), temp_cwd);
					} else {
						strcpy(cwd, temp_cwd);
						value = temp << l2b;
						last_op_type = TYPE_CD;
						i_number = temp;
					}
				}
			}
		| CD
			{
				uint32_t block;

				(void) strcpy(cwd, "/");
				/*
				 * set current value to root icb
				 */
				if ((block = ud_xlate_to_daddr(ricb_prn,
						ricb_loc)) == 0) {
					fprintf(stdout,
						gettext("Failed to translate "
						"prn %x loc %x\n"),
						ricb_prn, ricb_loc);
				} else {
					value = block << l2b;
					last_op_type = TYPE_CD;
					i_number = block;
				}
			}
		;

directory	: DIRECTORY
			{
				if (verify_dent(i_number << l2b, value) == 0) {
					last_op_type = TYPE_DIRENT;
					d_entry = value;
				}
			}
		;

file		: TFILE
			{
			}
		;

find		: xfind
			{
				if ((find_flags & (FIND_NAME | FIND_INODE)) &&
					(find_flags & FIND_DONE)) {
					if (find_dir[0] != '/') {
						char buf[1024];

						strcpy(buf, find_dir);
						if ((strlen(cwd) == 1) &&
							(cwd[0] == '/')) {
							strcpy(find_dir, "/");
						} else {
							strcpy(find_dir, cwd);
							strcat(find_dir, "/");
						}
						strcat(find_dir, buf);
					}
					find_it(find_dir, find_name, find_in,
				(find_flags & (FIND_NAME | FIND_INODE)));
				}
				find_flags = 0;
				find_dir[0] = '\0';
				find_name[0] = '\0';
				find_in = 0;
			}
		;

xfind		: FIND WORD
			{
				strcpy(find_dir, (char *)$2);
				find_flags = FIND_DIR;
			}
		| xfind ' ' WORD
			{
				if (find_flags == FIND_DIR) {
					if (strcmp((char *)$3, "-name") == 0) {
						find_flags = FIND_NAME;
					} else if (strcmp((char *)$3, "-inum")
							== 0) {
						find_flags = FIND_INODE;
					} else {
						fprintf(stdout,
				gettext("find dir-name {-name n | -inum n}\n"));
					}
				} else if (find_flags == FIND_NAME) {
					strcpy(find_name, (char *)$3);
					find_flags |= FIND_DONE;
				} else if (find_flags == FIND_INODE) {
					uint64_t temp;

					if (check_and_get_int($3, &temp) ==
						0) {
						find_in = temp;
						find_flags |= FIND_DONE;
					} else {
						fprintf(stdout,
				gettext("find dir-name {-name n | -inum n}\n"));
					}
				} else {
					fprintf(stdout,
				gettext("find dir-name {-name n | -inum n}\n"));
				}
			}
		| xfind ' ' expr
			{
				if (find_flags == FIND_INODE) {
					find_in = $3;
					find_flags |= FIND_DONE;
				} else {
					fprintf(stdout,
				gettext("find dir-name {-name n | -inum n}\n"));
				}
			}
		;


fill		: FILL '=' WORD
			{
				fill_pattern(value, count, $3);
			}
		;

inode		: INODE
			{
				uint32_t temp;

				if (last_op_type == TYPE_CD) {
					temp = value;
				} else {
					temp = value << l2b;
				}
				last_op_type = TYPE_INODE;
				if (verify_inode(temp, 0) != 0) {
					i_number = temp >> l2b;
					d_entry = 0;
				}
			}
		;

ls		: xls
			{
				if (ls_flags & LIST_LS) {
					list(".", i_number, ls_flags);
				}
			}
		;

xls		: LS
			{
				/* Do nothing */
				ls_flags = LIST_LS;
			}
		| xls ' ' WORD
			{
				if (strcmp((caddr_t)$3, "-l") == 0) {
					ls_flags |= LONG_LIST;
				} else if (strcmp((caddr_t)$3, "-R") == 0) {
					ls_flags |= RECU_LIST;
				} else if ((strcmp((caddr_t)$3, "-lR") == 0) ||
					(strcmp((caddr_t)$3, "-Rl") == 0)) {
					ls_flags |= LONG_LIST | RECU_LIST;
				} else {
					list(".", i_number, ls_flags);
					ls_flags &= ~LIST_LS;
				}
			}
		;

override	: OVERRIDE
			{
				if (error_override == 0) {
					error_override = 1;
					(void) fprintf(stdout,
					gettext("error checking on\n"));
				} else {
					error_override = 0;
					(void) fprintf(stdout,
					gettext("error checking off\n"));
				}
			}
		;

nprompt		: PROMPT '=' WORD
			{
				(void) strcpy(prompt, (caddr_t)$3);
			}
		;

pwd		: PWD
			{
				fprintf(stdout, gettext("%s\n"), cwd);
			}
		;

quit		: QUIT
			{
				exit (0);
			}
		;

tag		: TAG
			{
				print_desc(value, 0);
			}
		;

shell		: BANG
			{
				system(shell_name);
			}
		;

avd		: AVD	{ print_desc(NULL, AVD); }
		;
mvds		: MVDS	{ print_desc(NULL, MVDS); }
		;
rvds		: RVDS	{ print_desc(NULL, RVDS); }
		;
ints		: INTS	{ print_desc(NULL, INTS); }
		;
fsds		: FSDS	{ print_desc(NULL, FSDS); }
		;
root		: ROOT	{ print_desc(NULL, ROOT); }
		;

at		: ATTZ '=' expr	{ set_file(ATTZ, i_number << l2b, $3); }
		| ATYE '=' expr	{ set_file(ATYE, i_number << l2b, $3); }
		| ATMO '=' expr	{ set_file(ATMO, i_number << l2b, $3); }
		| ATDA '=' expr	{ set_file(ATDA, i_number << l2b, $3); }
		| ATHO '=' expr	{ set_file(ATHO, i_number << l2b, $3); }
		| ATMI '=' expr	{ set_file(ATMI, i_number << l2b, $3); }
		| ATSE '=' expr	{ set_file(ATSE, i_number << l2b, $3); }
		| ATCE '=' expr	{ set_file(ATCE, i_number << l2b, $3); }
		| ATHU '=' expr	{ set_file(ATHU, i_number << l2b, $3); }
		| ATMIC '=' expr
			{
				set_file(ATMIC, i_number << l2b, $3);
			}
		;

ct		: CTTZ '=' expr	{ set_file(CTTZ, i_number << l2b, $3); }
		| CTYE '=' expr	{ set_file(CTYE, i_number << l2b, $3); }
		| CTMO '=' expr	{ set_file(CTMO, i_number << l2b, $3); }
		| CTDA '=' expr	{ set_file(CTDA, i_number << l2b, $3); }
		| CTHO '=' expr	{ set_file(CTHO, i_number << l2b, $3); }
		| CTMI '=' expr	{ set_file(CTMI, i_number << l2b, $3); }
		| CTSE '=' expr	{ set_file(CTSE, i_number << l2b, $3); }
		| CTCE '=' expr	{ set_file(CTCE, i_number << l2b, $3); }
		| CTHU '=' expr	{ set_file(CTHU, i_number << l2b, $3); }
		| CTMIC '=' expr
			{
				set_file(CTMIC, i_number << l2b, $3);
			}
		;

mt		: MTTZ '=' expr	{ set_file(MTTZ, i_number << l2b, $3); }
		| MTYE '=' expr	{ set_file(MTYE, i_number << l2b, $3); }
		| MTMO '=' expr	{ set_file(MTMO, i_number << l2b, $3); }
		| MTDA '=' expr	{ set_file(MTDA, i_number << l2b, $3); }
		| MTHO '=' expr	{ set_file(MTHO, i_number << l2b, $3); }
		| MTMI '=' expr	{ set_file(MTMI, i_number << l2b, $3); }
		| MTSE '=' expr	{ set_file(MTSE, i_number << l2b, $3); }
		| MTCE '=' expr	{ set_file(MTCE, i_number << l2b, $3); }
		| MTHU '=' expr	{ set_file(MTHU, i_number << l2b, $3); }
		| MTMIC '=' expr
			{
				set_file(MTMIC, i_number << l2b, $3);
			}
		;


gid		: GID '=' expr	{ set_file(GID, i_number << l2b, $3); }
		;

ln		: LN '=' expr	{ set_file(LN, i_number << l2b, $3); }
		;

md		: MD '=' expr	{ set_file(MD, i_number << l2b, $3); }
		;

maj		: MAJ '=' expr	{ set_file(MAJ, i_number << l2b, $3); }
		;

min		: MIO '=' expr	{ set_file(MIO, i_number << l2b, $3); }
		;

nm		: NM '=' expr	{ set_file(NM, i_number << l2b, $3); }
		;

sz		: SZ '=' expr	{ set_file(SZ, i_number << l2b, $3); }
		;

uid		: UID '=' expr	{ set_file(UID, i_number << l2b, $3); }
		;

uniq		: UNIQ '=' expr	{ set_file(UNIQ, i_number << l2b, $3); }
		;

dump		: '/' WORD
			{
				if (strlen((char *)$2) != 1) {
					fprintf(stdout,
						gettext("Invalid command\n"));
				} else {
					dump_disk(value, count, $2);
				}
			}
		| '?' WORD
			{
				if (strcmp((char *)$2, "i") == 0) {
					if (verify_inode(value << l2b,
							0) != 0) {
						print_inode(value << l2b);
						i_number = value;
						last_op_type == TYPE_INODE;
					}
				} else if (strcmp((char *)$2, "d") == 0) {
					if (verify_dent(i_number << l2b,
							value) == 0) {
						print_dent(i_number << l2b,
							value);
						d_entry = value;
						last_op_type == TYPE_DIRENT;
					}
				} else {
					fprintf(stdout,
						gettext("Invalid command\n"));
				}
			}
		;

texpr		: expr
			{
				value = $1;
				count = 0;
			}
		| expr ',' expr
			{
				value = $1;
				count = $3;
			}
		;

expr		: '+'
			{
				if (last_op_type == TYPE_INODE) {
					if (verify_inode((i_number + 1) << l2b,
							0) != 0) {
						i_number ++;
						print_inode(i_number << l2b);
						$$ = i_number << l2b;
					}
				} else if (last_op_type == TYPE_DIRENT) {
					if (verify_dent(i_number << l2b,
							d_entry + 1) == 0) {
						d_entry ++;
						print_dent(i_number << l2b,
							d_entry);
					}
				} else {
					count = 0; $$ = value++;
				}
			}
		| '-'
			{
				if (last_op_type == TYPE_INODE) {
					if (verify_inode((i_number - 1) << l2b,
							0) != 0) {
						i_number --;
						print_inode(i_number << l2b);
						$$ = i_number << l2b;
					}
				} else if (last_op_type == TYPE_DIRENT) {
					if (verify_dent(i_number << l2b,
							d_entry - 1) == 0) {
						d_entry --;
						print_dent(i_number << l2b,
							d_entry);
					}
				} else {
					count = 0; $$ = value--;
				}
			}
		| '-' WORD
			{
				uint64_t number;

				if (check_and_get_int($2, &number) == 0) {
					count = 0;
					$$ = value - number;
				}
			}
		| '+' WORD
			{
				uint64_t number;

				if (check_and_get_int($2, &number) == 0) {
					count = 0;
					$$ = value + number;
				}
			}
		| '*' WORD
			{
				uint64_t number;

				if (check_and_get_int($2, &number) == 0) {
					count = 0;
					$$ = value * number;
				}
			}
		| '%' WORD
			{
				uint64_t number;

				if (check_and_get_int($2, &number) == 0) {
					if (number == 0) {
						fprintf(stdout,
						gettext("Divide by zero ?\n"));
					} else {
						count = 0;
						$$ = value / number;
					}
				}
			}
		| expr '-' expr		{ count = 0; $$ = $1 - $3; }
		| expr '+' expr		{ count = 0; $$ = $1 + $3; }
		| expr '*' expr		{ count = 0; $$ = $1 * $3; }
		| expr '%' expr
			{
				if ($3 == 0) {
					fprintf(stdout,
						gettext("Divide by zero ?\n"));
				} else {
					$$ = $1 / $3;
				}
				count = 0;
			}
		| WORD
			{
				uint64_t number;

				count = 0;
				if (check_and_get_int($1, &number) == 0) {
					$$ = number;
				}
			}
		;

%%

int32_t
check_and_get_int(uint8_t *str, uint64_t *value)
{
	int32_t length, cbase, index, cvalue;

	*value = 0;
	length = strlen((caddr_t)str);
	/*
	 * Decide on what base to be used
	 * and strip off the base specifier
	 */
	if ((str[0] == '0') && (str[1] == 'x')) {
		cbase = 0x10;
		index = 2;
	} else if ((str[0] == '0') && (str[1] == 't')) {
		cbase = 0xa;
		index = 2;
	} else if (str[0] == '0') {
		cbase = 0x8;
		index = 1;
	} else {
		cbase = base;
		index = 0;
	}

	/*
	 * Verify if the string is integer
	 * and convert to a binary value
	 */
	for ( ; index < length; index++) {
		if (cbase == 0x8) {
			if ((str[index] < '0') ||
				(str[index] > '7')) {
				fprintf(stdout,
					gettext("Invalid Octal Number %s\n"),
					str);
				return (1);
			}
			cvalue = str[index] - '0';
		} else if (cbase == 0xa) {
			if ((str[index] < '0') ||
				(str[index] > '9' )) {
				fprintf(stdout,
					gettext("Invalid Decimal Number %s\n"),
					str);
				return (1);
			}
			cvalue = str[index] - '0';
		} else {
			if ((str[index] >= '0') &&
					(str[index] <= '9')) {
				cvalue = str[index] - '0';
			} else if ((str[index] >= 'a') &&
					(str[index] <= 'f')) {	
				cvalue = str[index] - 'a' + 10;
			} else if ((str[index] >= 'A') &&
					(str[index] <= 'F')) {
				cvalue = str[index] - 'A' + 10;
			} else {
				fprintf(stdout,
					gettext("Invalid Hex Number %s\n"),
					str);
				return (1);
			}
		}
		*value = *value * cbase + cvalue;
	}
	return (0);
}

void print_prompt();
extern FILE *yyin;

void
print_prompt()
{
	fprintf(stdout, gettext("%s"), prompt);
}

int32_t
run_fsdb()
{
	yyin = stdin;
	if (yyparse() != 0)
		return (-1);
	return 0;
}