/*

/usr/src/ext2ed/general_com.c

A part of the extended file system 2 disk editor.

---------------------
General user commands
---------------------

First written on: April 9 1995

Copyright (C) 1995 Gadi Oxman

*/

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

#include "ext2ed.h"

void help (char *command_line)

{
	int i,max_line=0;
	char argument [80],*ptr;

	werase (show_pad);wmove (show_pad,0,0);

	ptr=parse_word (command_line,argument);

	if (*ptr!=0) {
		 ptr=parse_word (ptr,argument);
		 if (*argument!=0) {		 
			 detailed_help (argument);
			 return;
		}
	}

	if (current_type!=NULL) {
		
		wprintw (show_pad,"Type %s specific commands:\n",current_type->name);max_line++;
		
		if (current_type->type_commands.last_command==-1) {
			wprintw (show_pad,"\nnone\n");max_line+=2;
		}
		else
			for (i=0;i<=current_type->type_commands.last_command;i++) {
				if (i%5==0) {
					wprintw (show_pad,"\n");max_line++;
				}
				wprintw (show_pad,"%-13s",current_type->type_commands.names [i]);
				if (i%5!=4)
					wprintw (show_pad,";  ");				
			}
		
		wprintw (show_pad,"\n\n");max_line+=2;
	}		

	if (ext2_commands.last_command != -1) {
		wprintw (show_pad,"ext2 filesystem general commands: \n");max_line++;
		for (i=0;i<=ext2_commands.last_command;i++) {
			if (i%5==0) {
				wprintw (show_pad,"\n");max_line++;
			}
			wprintw (show_pad,"%-13s",ext2_commands.names [i]);
			if (i%5!=4)
				wprintw (show_pad,";  ");				

		}
		wprintw (show_pad,"\n\n");max_line+=2;
	}

	wprintw (show_pad,"General commands: \n");
	
	for (i=0;i<=general_commands.last_command;i++) {
		if (i%5==0) {
			wprintw (show_pad,"\n");max_line++;
		}
		wprintw (show_pad,"%-13s",general_commands.names [i]);
		if (i%5!=4)
			wprintw (show_pad,";  ");				
	}
	
	wprintw (show_pad,"\n\n");max_line+=2;
	
	wprintw (show_pad,"EXT2ED ver %d.%d (%s)\n",version_major,version_minor,revision_date);
	wprintw (show_pad,"Copyright (C) 1995 Gadi Oxman\n");
	wprintw (show_pad,"EXT2ED is hereby placed under the terms of the GNU General Public License.\n\n");
	wprintw (show_pad,"EXT2ED was programmed as a student project in the software laboratory\n");
	wprintw (show_pad,"of the faculty of electrical engineering in the\n");
	wprintw (show_pad,"Technion - Israel Institute of Technology\n");
	wprintw (show_pad,"with the guide of Avner Lottem and Dr. Ilana David.\n");

	max_line+=6;
	
	wprintw (show_pad,"\n\n");max_line+=2;
	
	wprintw (show_pad,"Please feel free to mail me at (currently) %s\n",email_address);
	wprintw (show_pad,"with any commet, suggestion, and of-course, bug report concerning EXT2ED.\n");
	
	max_line+=2;
	
	show_pad_info.line=0;show_pad_info.max_line=max_line;

	werase (show_win);wmove (show_win,0,0);
	wprintw (show_win,"EXT2ED help");
	
	refresh_show_win ();
	refresh_show_pad ();
}

void detailed_help (char *text)

{
	int i;
	
	if (current_type != NULL)
		for (i=0;i<=current_type->type_commands.last_command;i++) {
			if (strcmp (current_type->type_commands.names [i],text)==0) {
				wprintw (show_pad,"%s - %s\n",text,current_type->type_commands.descriptions [i]);
				refresh_show_pad ();return;
			}
		}

	for (i=0;i<=ext2_commands.last_command;i++) {
		if (strcmp (ext2_commands.names [i],text)==0) {
				wprintw (show_pad,"%s - %s\n",text,ext2_commands.descriptions [i]);
				refresh_show_pad ();return;
		}
	}

	for (i=0;i<=general_commands.last_command;i++) {
		if (strcmp (general_commands.names [i],text)==0) {
				wprintw (show_pad,"%s - %s\n",text,general_commands.descriptions [i]);
				refresh_show_pad ();return;
		}
	}

	if (strcmp ("quit",text)==0) {
		wprintw (show_pad,"quit - Exists EXT2ED");
		refresh_show_pad ();return;
	}

	wprintw (show_pad,"Error - Command %s not aviable now\n",text);
	refresh_show_pad ();return;
}



void set_device (char *command_line)

{
	char *ptr,new_device [80];
	
	ptr=parse_word (command_line,new_device);
	if (*ptr==0) {
		wprintw (command_win,"Error - Device name not specified\n");
		refresh_command_win ();return;
	}
	parse_word (ptr,new_device);	
	check_mounted (new_device);
	if (mounted && !AllowMountedRead) {
		wprintw (command_win,"Error - Filesystem is mounted, aborting\n");
		wprintw (command_win,"You may wish to use the AllowMountedRead on configuration option\n");
		refresh_command_win ();return;
	}
	
	if (mounted && AllowMountedRead) {
		wprintw (command_win,"Warning - Filesystem is mounted. Displayed data may be unreliable.\n");
		refresh_command_win ();
	}

	if (device_handle!=NULL)
		fclose (device_handle);
		
	if ( (device_handle=fopen (new_device,"rb"))==NULL) {
		wprintw (command_win,"Error - Can not open device %s\n",new_device);refresh_command_win ();
		return;
	}
	else {
		strcpy (device_name,new_device);
		write_access=0;				/* Write access disabled */
		current_type=NULL;			/* There is no type now */
		remember_lifo.entries_count=0;		/* Empty Object memory */
		free_user_commands (&ext2_commands);	/* Free filesystem specific objects */
		free_struct_descriptors ();
		if (!set_file_system_info ()) {		/* Error while getting info --> abort */
			free_user_commands (&ext2_commands);
			free_struct_descriptors ();
			fclose (device_handle);
			device_handle=NULL;		/* Notice that our device is still not set up */
			device_offset=-1;
			return;
		}
		if (*AlternateDescriptors)		/* Check if user defined objects exist */
			set_struct_descriptors (AlternateDescriptors);
		dispatch ("setoffset 0");
		dispatch ("help");			/* Show help screen */
		wprintw (command_win,"Device changed to %s",device_name);refresh_command_win ();
	}
}

void set_offset (char *command_line)

{
	long mult=1;
	long new_offset;
	char *ptr,new_offset_buffer [80];
	
	if (device_handle==NULL) {
		wprintw (command_win,"Error - No device opened\n");refresh_command_win ();
		return;
	}
	
	ptr=parse_word (command_line,new_offset_buffer);
	
	if (*ptr==0) {
		wprintw (command_win,"Error - No argument specified\n");refresh_command_win ();
		return;
	}

	ptr=parse_word (ptr,new_offset_buffer);

	if (strcmp (new_offset_buffer,"block")==0) {
		mult=file_system_info.block_size;
		ptr=parse_word (ptr,new_offset_buffer);
	}

	if (strcmp (new_offset_buffer,"type")==0) {
		if (current_type==NULL) {
			wprintw (command_win,"Error - No type set\n");refresh_command_win ();
			return;
		}

		mult=current_type->length;
		ptr=parse_word (ptr,new_offset_buffer);
	}

	if (*new_offset_buffer==0) {
		wprintw (command_win,"Error - No offset specified\n");refresh_command_win ();
		return;
	}

	if (new_offset_buffer [0]=='+') {
		if (device_offset==-1) {
			wprintw (command_win,"Error - Select a fixed offset first\n");refresh_command_win ();
			return;
		}
		new_offset=device_offset+atol (new_offset_buffer+1)*mult;
	}
	
	else if (new_offset_buffer [0]=='-') {
		if (device_offset==-1) {
			wprintw (command_win,"Error - Select a fixed offset first\n");refresh_command_win ();
			return;
		}
		new_offset=device_offset-atol (new_offset_buffer+1)*mult;
		if (new_offset<0) new_offset=0;
	}
	
	else 
		new_offset=atol (new_offset_buffer)*mult;
	
	if ( (fseek (device_handle,new_offset,SEEK_SET))==-1) {
		wprintw (command_win,"Error - Failed to seek to offset %ld in device %s\n",new_offset,device_name);
		refresh_command_win ();
		return;
	};
	device_offset=new_offset;
	wprintw (command_win,"Device offset changed to %ld\n",device_offset);refresh_command_win ();
	load_type_data ();
	type_data.offset_in_block=0;
}

void set (char *command_line)

{
	unsigned short *int_ptr;
	unsigned char *char_ptr;
	unsigned long *long_ptr,offset=0;
	int i,found=0;
	char *ptr,buffer [80],variable [80],value [80];
	
	if (device_handle==NULL) {
		wprintw (command_win,"Error - No device opened\n");refresh_command_win ();
		return;
	}

	if (current_type==NULL) {
		hex_set (command_line);
		return;
	}

	ptr=parse_word (command_line,buffer);
	if (ptr==NULL || *ptr==0) {
		wprintw (command_win,"Error - Missing arguments\n");refresh_command_win ();
		return;
	}
	parse_word (ptr,buffer);
	ptr=strchr (buffer,'=');
	if (ptr==NULL) {
		wprintw (command_win,"Error - Bad syntax\n");refresh_command_win ();return;
	}
	strncpy (variable,buffer,ptr-buffer);variable [ptr-buffer]=0;
	strcpy (value,++ptr);

	if (current_type==NULL) {
		wprintw (command_win,"Sorry, not yet supported\n");refresh_command_win ();return;
	}
	
	for (i=0;i<current_type->fields_num && !found;i++) {
		if (strcmp (current_type->field_names [i],variable)==0) {
			found=1;
			ptr=type_data.u.buffer+offset;
			switch (current_type->field_lengths [i]) {
				case 1:
					char_ptr=(unsigned char *) ptr;
					*char_ptr=(char) atoi (value);
					wprintw (command_win,"Variable %s set to %u\n",variable,*char_ptr);refresh_command_win ();
					break;
				case 2:
					int_ptr=(unsigned short *) ptr;
					*int_ptr=atoi (value);
					wprintw (command_win,"Variable %s set to %u\n",variable,*int_ptr);refresh_command_win ();
					break;

				case 4:
					long_ptr=(unsigned long *) ptr;
					*long_ptr=atol (value);
					wprintw (command_win,"Variable %s set to %lu\n",variable,*long_ptr);refresh_command_win ();
					break;
			}
		}
		offset+=current_type->field_lengths [i];
	}
	if (found)
		dispatch ("show");
	else {
		wprintw (command_win,"Error - Variable %s not found\n",variable);
		refresh_command_win ();
	}
}

void hex_set (char *command_line)

{
	unsigned char tmp;
	char *ptr,buffer [80],*ch_ptr;
	int mode=HEX;
	
	ptr=parse_word (command_line,buffer);
	if (*ptr==0) {
		wprintw (command_win,"Error - Argument not specified\n");refresh_command_win ();return;
	}

	ptr=parse_word (ptr,buffer);

	if (strcasecmp (buffer,"text")==0) {
		mode=TEXT;
		strcpy (buffer,ptr);
	}

	else if (strcasecmp (buffer,"hex")==0) {
		mode=HEX;
		ptr=parse_word (ptr,buffer);
	}

	if (*buffer==0) {
		wprintw (command_win,"Error - Data not specified\n");refresh_command_win ();return;
	}

	if (mode==HEX) {
		do {
			tmp=(unsigned char) strtol (buffer,NULL,16);
			type_data.u.buffer [type_data.offset_in_block]=tmp;
			type_data.offset_in_block++;
			ptr=parse_word (ptr,buffer);
			if (type_data.offset_in_block==file_system_info.block_size) {
				if (*ptr) {
					wprintw (command_win,"Error - Ending offset outside block, only partial string changed\n");
					refresh_command_win ();
				}
				type_data.offset_in_block--;
			}
		} while (*buffer) ;
	}

	else {
		ch_ptr=buffer;
		while (*ch_ptr) {
			tmp=(unsigned char) *ch_ptr++;
			type_data.u.buffer [type_data.offset_in_block]=tmp;
			type_data.offset_in_block++;
			if (type_data.offset_in_block==file_system_info.block_size) {
				if (*ch_ptr) {
					wprintw (command_win,"Error - Ending offset outside block, only partial string changed\n");
					refresh_command_win ();
				}
				type_data.offset_in_block--;
			}
		}
	}
	
	strcpy (buffer,"show");dispatch (buffer);
}



void set_type (char *command_line)

{
	struct struct_descriptor *descriptor_ptr;
	char *ptr,buffer [80],tmp_buffer [80];
	short found=0;

	if (!load_type_data ())
		return;

	ptr=parse_word (command_line,buffer);
	parse_word (ptr,buffer);
	
	if (strcmp (buffer,"none")==0 || strcmp (buffer,"hex")==0) {
		wprintw (command_win,"Data will be shown as hex dump\n");refresh_command_win ();
		current_type=NULL;
		sprintf (tmp_buffer,"show");dispatch (tmp_buffer);
		return;
	}
	
	descriptor_ptr=first_type;
	while (descriptor_ptr!=NULL && !found) {
		if (strcmp (descriptor_ptr->name,buffer)==0)
			found=1;
		else
			descriptor_ptr=descriptor_ptr->next;
	}
	if (found) {
		wprintw (command_win,"Structure type set to %s\n",buffer);refresh_command_win ();
		current_type=descriptor_ptr;
		sprintf (tmp_buffer,"show");dispatch (tmp_buffer);
	}
	else {
		wprintw (command_win,"Error - %s is not a valid type\n",buffer);refresh_command_win ();
	}
}    


void show (char *command_line)

{
	unsigned int i,l,temp_int;
	unsigned long offset=0,temp_long;	
	unsigned char temp_char,*ch_ptr;
	void *ptr;

	if (device_handle==NULL)
		return;

	show_pad_info.line=0;
	
	if (current_type==NULL) {
		wmove (show_pad,0,0);
		ch_ptr=type_data.u.buffer;
		for (l=0;l<file_system_info.block_size/16;l++) {
			wprintw (show_pad,"%08ld :  ",offset);
			for (i=0;i<16;i++) {
				if (type_data.offset_in_block==offset+i)
					wattrset (show_pad,A_REVERSE);
			
				if (ch_ptr [i]>=' ' && ch_ptr [i]<='z')
					wprintw (show_pad,"%c",ch_ptr [i]);
				else
					wprintw (show_pad,".");
				if (type_data.offset_in_block==offset+i)
					wattrset (show_pad,A_NORMAL);
			}
			wprintw (show_pad,"   ");
			for (i=0;i<16;i++) {
				if (type_data.offset_in_block==offset+i)
					wattrset (show_pad,A_REVERSE);
			
				wprintw (show_pad,"%02x",ch_ptr [i]);

				if (type_data.offset_in_block==offset+i) {
					wattrset (show_pad,A_NORMAL);
					show_pad_info.line=l-l % show_pad_info.display_lines;
				}

				wprintw (show_pad," ");
			}
			wprintw (show_pad,"\n");
			offset+=16;
			ch_ptr+=16;
		}
		show_pad_info.max_line=l-1;show_pad_info.max_col=COLS-1;
		refresh_show_pad ();show_info ();
	}
	else {
		wmove (show_pad,0,0);l=0;
		for (i=0;i<current_type->fields_num;i++) {
			wprintw (show_pad,"%-20s = ",current_type->field_names [i]);
			ptr=type_data.u.buffer+offset;
			switch (current_type->field_lengths [i]) {
				case 1:
					temp_char=*((unsigned char *) ptr);
					wprintw (show_pad,"%3u (0x%02x",temp_char,temp_char);
					if (temp_char>=' ' && temp_char<='z')
						wprintw (show_pad," , %c)\n",temp_char);
					else
						wprintw (show_pad,")\n");

					offset ++;l++;
					break;
				case 2:
					temp_int=*((unsigned short *) ptr);
					wprintw (show_pad,"%u (0x%x)\n",temp_int,temp_int);
					offset +=2;l++;
					break;
				case 4:
					temp_long=*((unsigned long *) ptr);
					wprintw (show_pad,"%lu\n",temp_long);
					offset +=4;l++;
					break;
			}
/*			offset+=current_type->field_lengths [i]; */
		}
		current_type->length=offset;
		show_pad_info.max_line=l-1;
		refresh_show_pad ();show_info ();
	}
}

void next (char *command_line)

{
	long offset=1;
	char *ptr,buffer [80];

	ptr=parse_word (command_line,buffer);
	
	if (*ptr!=0) {
		ptr=parse_word (ptr,buffer);
		offset*=atol (buffer);
	}
	
	if (current_type!=NULL) {
		sprintf (buffer,"setoffset type +%ld",offset);
		dispatch (buffer);
		return;
	}

	if (type_data.offset_in_block+offset < file_system_info.block_size) {
		type_data.offset_in_block+=offset;
		sprintf (buffer,"show");dispatch (buffer);
	}
		
	else {
		wprintw (command_win,"Error - Offset out of block\n");refresh_command_win ();
	}
}

void prev (char *command_line)

{
	long offset=1;
	char *ptr,buffer [80];

	ptr=parse_word (command_line,buffer);
	
	if (*ptr!=0) {
		ptr=parse_word (ptr,buffer);
		offset*=atol (buffer);
	}
	
	if (current_type!=NULL) {
		sprintf (buffer,"setoffset type -%ld",offset);
		dispatch (buffer);
		return;
	}

	if (type_data.offset_in_block-offset >= 0) {
		type_data.offset_in_block-=offset;
		sprintf (buffer,"show");dispatch (buffer);
	}
	
	else {
		wprintw (command_win,"Error - Offset out of block\n");refresh_command_win ();
	}
}

void pgdn (char *commnad_line) 

{
	show_pad_info.line+=show_pad_info.display_lines;
	refresh_show_pad ();refresh_show_win ();
}

void pgup (char *command_line)

{
	show_pad_info.line-=show_pad_info.display_lines;
	refresh_show_pad ();refresh_show_win ();
}

void redraw (char *command_line)

{
	redraw_all ();
	dispatch ("show");
}

void remember (char *command_line)

{
	long entry_num;
	char *ptr,buffer [80];
	
	if (device_handle==NULL) {
		wprintw (command_win,"Error - No device opened\n");refresh_command_win ();
		return;
	}

	ptr=parse_word (command_line,buffer);
	
	if (*ptr==0) {
		wprintw (command_win,"Error - Argument not specified\n");refresh_command_win ();
		return;		
	}
	
	ptr=parse_word (ptr,buffer);

	entry_num=remember_lifo.entries_count++;
	if (entry_num>REMEMBER_COUNT-1) {
		entry_num=0;
		remember_lifo.entries_count--;
	}
	
	remember_lifo.offset [entry_num]=device_offset;
	remember_lifo.type [entry_num]=current_type;
	strcpy (remember_lifo.name [entry_num],buffer);
	
	if (current_type!=NULL)
		wprintw (command_win,"Object %s in Offset %ld remembered as %s\n",current_type->name,device_offset,buffer);
	else
		wprintw (command_win,"Offset %ld remembered as %s\n",device_offset,buffer);
			
	refresh_command_win ();
}

void recall (char *command_line)

{
	char *ptr,buffer [80];
	long entry_num;

	if (device_handle==NULL) {
		wprintw (command_win,"Error - No device opened\n");refresh_command_win ();
		return;
	}

	ptr=parse_word (command_line,buffer);

	if (*ptr==0) {
		wprintw (command_win,"Error - Argument not specified\n");refresh_command_win ();
		return;		
	}

	ptr=parse_word (ptr,buffer);

	
	for (entry_num=remember_lifo.entries_count-1;entry_num>=0;entry_num--) {
		if (strcmp (remember_lifo.name [entry_num],buffer)==0)
			break;	
	}
	
	if (entry_num==-1) {
		wprintw (command_win,"Error - Can not recall %s\n",buffer);refresh_command_win ();
		return;
	}

	sprintf (buffer,"setoffset %ld",remember_lifo.offset [entry_num]);dispatch (buffer);
	if (remember_lifo.type [entry_num] != NULL) {
		sprintf (buffer,"settype %s",remember_lifo.type [entry_num]->name);dispatch (buffer);	
	}

	else {
		sprintf (buffer,"settype none");dispatch (buffer);	
	}
			
	wprintw (command_win,"Object %s in Offset %ld recalled\n",current_type->name,device_offset);
	refresh_command_win ();
}

void enable_write (char *command_line)

{
	FILE *fp;

	if (device_handle==NULL) {
		wprintw (command_win,"Error - No device opened\n");refresh_command_win ();
		return;
	}

	if (!AllowChanges) {
		wprintw (command_win,"Sorry, write access is not allowed\n");
    		return;
    	}
    	
    	if (mounted) {
    		wprintw (command_win,"Error - Filesystem is mounted\n");
		return;    		
    	}
    	
	if ( (fp=fopen (device_name,"r+b"))==NULL) {
		wprintw (command_win,"Error - Can not open device %s for reading and writing\n",device_name);refresh_command_win ();
		return;
	}
	fclose (device_handle);
	device_handle=fp;write_access=1;
	wprintw (command_win,"Write access enabled - Be careful\n");refresh_command_win ();
}

void disable_write (char *command_line)

{
	FILE *fp;

	if (device_handle==NULL) {
		wprintw (command_win,"Error - No device opened\n");refresh_command_win ();
		return;
	}

	if ( (fp=fopen (device_name,"rb"))==NULL) {
		wprintw (command_win,"Error - Can not open device %s\n",device_name);refresh_command_win ();
		return;
	}
	
	fclose (device_handle);
	device_handle=fp;write_access=0;
	wprintw (command_win,"Write access disabled\n");refresh_command_win ();
}

void write_data (char *command_line)

{
	write_type_data ();
}
