Home | History | Annotate | Download | only in ext2ed
      1 /*
      2 
      3 /usr/src/ext2ed/main.c
      4 
      5 A part of the extended file system 2 disk editor.
      6 
      7 ------------
      8 Main program
      9 ------------
     10 
     11 This file mostly contains:
     12 
     13 1.	A list of global variables used through the entire program.
     14 2.	The parser, which asks the command line from the user.
     15 3.	The dispatcher, which analyzes the command line and calls the appropriate handler function.
     16 4.	A command pattern matcher which is used along with the readline completion feature.
     17 5.	A function which tells the user that an internal error has occured.
     18 
     19 First written on: March 30 1995
     20 
     21 Copyright (C) 1995 Gadi Oxman
     22 
     23 */
     24 
     25 #include <stdio.h>
     26 #include <stdlib.h>
     27 #include <string.h>
     28 #include <signal.h>
     29 
     30 #ifdef HAVE_READLINE
     31 #include <readline.h>
     32 #include <history.h>
     33 #endif
     34 
     35 #ifdef HAVE_GETOPT_H
     36 #include <getopt.h>
     37 #else
     38 extern int optind;
     39 extern char *optarg;
     40 #endif
     41 
     42 #include "ext2ed.h"
     43 
     44 /* Global variables */
     45 
     46 /*
     47 
     48 Configuration file options
     49 
     50 The following variables will be set by init.c to the values selected in the user configuration file.
     51 They are initialized below to some logical defaults.
     52 
     53 */
     54 
     55 
     56 char Ext2Descriptors [200]="ext2.descriptors";	/* The location of the ext2 filesystem object definition */
     57 char AlternateDescriptors [200]="";		/* We allow the user to define additional structures */
     58 char LogFile [200]="ext2ed.log";		/* The location of the log file - Each write will be logged there */
     59 int LogChanges=1;				/* 1 enables logging, 0 diables logging */
     60 int AllowChanges=0;				/* When set, the enablewrite command will fail */
     61 int AllowMountedRead=0;				/* Behavior when trying to open a mounted filesystem read-only */
     62 int ForceExt2=0;				/* When set, ext2 autodetection is overridden */
     63 int DefaultBlockSize=1024;
     64 unsigned long DefaultTotalBlocks=2097151;
     65 unsigned long DefaultBlocksInGroup=8192;	/* The default values are used when an ext2 filesystem is not */
     66 int ForceDefault=0;				/* detected, or ForceDefault is set */
     67 
     68 char last_command_line [80];			/* A simple one command cache, in addition to the readline history */
     69 
     70 char device_name [80];				/* The location of the filesystem */
     71 FILE *device_handle=NULL;			/* This is passed to the fopen / fread ... commands */
     72 long device_offset;				/* The current position in the filesystem */
     73 						/* Note that we have a 2 GB limitation */
     74 
     75 int mounted=0;					/* This is set when we find that the filesystem is mounted */
     76 
     77 struct struct_commands general_commands,ext2_commands;		/* Used to define the general and ext2 commands */
     78 struct struct_descriptor *first_type,*last_type,*current_type;	/* Used to access the double linked list */
     79 struct struct_type_data type_data;				/* The current data is sometimes stored here */
     80 struct struct_file_system_info file_system_info;		/* Essential information on the filesystem */
     81 struct struct_file_info file_info,first_file_info;		/* Used by file_com.c to access files */
     82 struct struct_group_info group_info;				/* Used by group_com.c */
     83 struct struct_super_info super_info;				/* Used by super_com.c */
     84 struct struct_remember_lifo remember_lifo;			/* A circular memory of objects */
     85 struct struct_block_bitmap_info block_bitmap_info;		/* Used by blockbitmap_com.c */
     86 struct struct_inode_bitmap_info inode_bitmap_info;		/* Used by inodebitmap_com.c */
     87 
     88 int redraw_request=0;						/* Is set by a signal handler to handle terminal */
     89 								/* screen size change. */
     90 
     91 
     92 /*
     93  * We just call the parser to get commands from the user. We quit when
     94  * parser returns.
     95  */
     96 int main (int argc, char **argv)
     97 {
     98 	int	write_priv = 0;
     99 	int	c;
    100 	char	*buf;
    101 
    102 	if (!init ())
    103 		return (1);
    104 	while ((c = getopt (argc, argv, "w")) != EOF) {
    105 		switch (c) {
    106 		case 'w':
    107 			write_priv++;
    108 			break;
    109 		}
    110 	}
    111 	if (optind < argc) {
    112 		buf = malloc(strlen(argv[optind]) + 32);
    113 		if (!buf) {
    114 			fprintf(stderr, "Couldn't allocate filename buffer\n");
    115 			exit(1);
    116 		}
    117 		strcpy(buf, "set_device ");
    118 		strcat(buf, argv[optind]);
    119 		set_device(buf);
    120 		free(buf);
    121 		if (write_priv) {
    122 			wprintw (command_win,"\n");
    123 			enable_write("enable_write");
    124 		}
    125 	}
    126 	parser ();			/* Get and parse user commands */
    127 	prepare_to_close();		/* Do some cleanup */
    128 	printf("Quitting ...\n");
    129 	return(0);
    130 }
    131 
    132 
    133 /*
    134  * Read a character from the command window
    135  */
    136 int command_read_key()
    137 {
    138 	int	key = 0;
    139 
    140 	while (!key) {
    141 		if (redraw_request) {
    142 			redraw_all();
    143 			redraw_request=0;
    144 		}
    145 		key = wgetch(command_win);
    146 		switch (key) {
    147 		case 0x1A:
    148 			key = 0;
    149 			kill(getpid(), SIGTSTP);
    150 			break;
    151 
    152 		case KEY_NPAGE:
    153 			pgdn("");
    154 			refresh_command_win ();
    155 			break;
    156 
    157 		case KEY_PPAGE:
    158 			pgup("");
    159 			refresh_command_win ();
    160 			break;
    161 		case ERR:
    162 			key = 0;
    163 			break;
    164 
    165 		case KEY_BACKSPACE:
    166 			key = '\b';
    167 		}
    168 		if ((key < 32 && key != '\b' && key != '\n') ||
    169 		    (key > 127))
    170 			key = 0;
    171 	}
    172 	return key;
    173 }
    174 
    175 #ifdef HAVE_READLINE
    176 int rl_getc_replacement(FILE *f)
    177 {
    178 	int	key = command_read_key();
    179 
    180 	if (key == '\b') {
    181 		if (rl_point > 0)
    182 			wprintw(command_win, "\b \b");
    183 	} else
    184 		wprintw(command_win, "%c", key);
    185 	return key;
    186 }
    187 
    188 /*
    189  * This function asks the user for a command and calls the dispatcher
    190  * function, dispatch, to analyze it.  We use the readline library
    191  * function readline to read the command, hence all the usual readline
    192  * keys are available.  The new command is saved both in the
    193  * readline's history and in our tiny one-command cache, so that only
    194  * the enter key is needed to retype it.
    195  */
    196 void parser (void)
    197 {
    198 	char *ptr,command_line [80];
    199 	int quit=0;
    200 
    201 #if 0
    202 	noecho();
    203 	cbreak();
    204 	keypad(command_win, 1);
    205 	wtimeout(command_win, 100);
    206 
    207 	rl_getc_function = rl_getc_replacement;
    208 #endif
    209 
    210 	while (!quit) {
    211 		/* Terminal screen size has changed */
    212 		if (redraw_request) {
    213 			redraw_all();
    214 			redraw_request=0;
    215 		}
    216 
    217 		wmove (command_win,0,0);
    218 		wclrtoeol (command_win);
    219 		wprintw (command_win,"ext2ed > ");
    220 		refresh_command_win ();
    221 
    222 		/*
    223 		 * The ncurses library optimizes cursor movement by
    224 		 * keeping track of the cursor position. However, by
    225 		 * using the readline library I'm breaking its
    226 		 * assumptions. The double -1 arguments tell ncurses
    227 		 * to disable cursor movement optimization this
    228 		 * time.
    229 		 */
    230 		mvcur (-1,-1,LINES-COMMAND_WIN_LINES,0);
    231 
    232 		/* echo (); */
    233 		ptr=readline ("ext2ed > ");
    234 		/* noecho (); */
    235 
    236 		/*
    237 		 * Readline allocated the buffer - Copy the string
    238 		 * and free the allocated buffer
    239 		 * XXX WHY???
    240 		 */
    241 		strcpy (command_line,ptr);
    242 		free (ptr);
    243 
    244 		if (*command_line != 0)
    245 			add_history (command_line);
    246 
    247 		/* If only enter was pressed, recall the last command */
    248 		if (*command_line==0)
    249 			strcpy (command_line,last_command_line);
    250 
    251 		/* Emulate readline's actions for ncurses */
    252 		mvcur (-1,-1,LINES-COMMAND_WIN_LINES,0);
    253 		werase (command_win);
    254 		wprintw (command_win,"ext2ed > ");
    255 		wprintw (command_win,command_line);
    256 		wprintw (command_win,"\n");
    257 		refresh_command_win ();
    258 
    259 		/* Save this command in our tiny cache */
    260 		strcpy (last_command_line,command_line);
    261 
    262 		/* And call dispatch to do the actual job */
    263 		quit=dispatch (command_line);
    264 	}
    265 }
    266 #else
    267 void read_line(char * foo) {
    268 	char * chptr = foo;
    269 	int ch;
    270 	int done = 0;
    271 
    272 	while (!done && (ch = command_read_key())) {
    273 		switch (ch) {
    274 		case '\n':
    275 			done = 1;
    276 			break;
    277 
    278 		case '\b':
    279 			if (chptr > foo) {
    280 				wprintw(command_win, "\b \b");
    281 				chptr--;
    282 			}
    283 			break;
    284 
    285 		default:
    286 			if (ch > 256)
    287 				break;
    288 			if (ch == '\n') break;
    289 			*chptr++ = ch;
    290 			wprintw(command_win, "%c", ch);
    291 			break;
    292 		}
    293 	}
    294 	*chptr = '\0';
    295 }
    296 
    297 void parser (void)
    298 {
    299 	char command_line [80];
    300 	int quit=0;
    301 
    302 	noecho();
    303 	cbreak();
    304 	wtimeout(command_win, 100);
    305 	keypad(command_win, 1);
    306 
    307 	while (!quit) {
    308 		/* Terminal screen size has changed */
    309 		if (redraw_request) {
    310 			redraw_all();
    311 			redraw_request=0;
    312 		}
    313 
    314 		wmove (command_win,0,0);wclrtoeol (command_win);
    315 
    316 		wmove(command_win, 0, 0);
    317 		wprintw(command_win, "ext2ed > ");
    318 		read_line(command_line);
    319 
    320 		/* If only enter was pressed, recall the last command */
    321  		if (*command_line==0)
    322  			strcpy (command_line,last_command_line);
    323 
    324 		mvcur (-1,-1,LINES-COMMAND_WIN_LINES + 1,0);
    325 
    326  		strcpy (last_command_line,command_line);	/* Save this command in our tiny cache */
    327 
    328 		/* And call dispatch to do the actual job */
    329 		quit=dispatch (command_line);
    330 	}
    331 }
    332 #endif
    333 
    334 
    335 /*
    336  * This is a very important function. Its task is to recieve a command
    337  * name and link it to a C function.  There are three types of commands:
    338  *
    339  * 1.	General commands - Always available and accessed through
    340  * general_commands.
    341  * 2.	Ext2 specific commands - Available when editing an ext2
    342  * filesystem, accessed through ext2_commands.
    343  * 3.	Type specific commands - Those are changing according to the
    344  * current type. The global variable current_type points to the
    345  * current object definition (of type struct_descriptor). In it, the
    346  * struct_commands entry contains the type specific commands links.
    347  *
    348  * Overriding is an important feature - Much like in C++ : The same
    349  * command name can dispatch to different functions. The overriding
    350  * priority is 3,2,1; That is - A type specific command will always
    351  * override a general command. This is used through the program to
    352  * allow fine tuned operation.
    353  *
    354  * When an handling function is found, it is called along with the
    355  * command line that was passed to us. The handling function is then
    356  * free to interpert the arguments in its own style.
    357  */
    358 int dispatch (char *command_line)
    359 {
    360 	int i,found=0;
    361 
    362 	char command [80];
    363 
    364 	parse_word (command_line,command);
    365 
    366 	if (strcasecmp (command,"quit")==0) return (1);
    367 
    368 	/* 1. Search for type specific commands FIRST - Allows
    369 	overriding of a general command */
    370 
    371 	if (current_type != NULL)
    372 		for (i=0;
    373 		     i<=current_type->type_commands.last_command && !found;
    374 		     i++) {
    375 			if (strcasecmp (command,current_type->type_commands.names [i])==0) {
    376 				(*current_type->type_commands.callback [i]) (command_line);
    377 				found=1;
    378 			}
    379 		}
    380 
    381 	/* 2. Now search for ext2 filesystem general commands */
    382 
    383 	if (!found)
    384 		for (i=0;i<=ext2_commands.last_command && !found;i++) {
    385 			if (strcasecmp (command,ext2_commands.names [i])==0) {
    386 				(*ext2_commands.callback [i]) (command_line);
    387 				found=1;
    388 			}
    389 		}
    390 
    391 
    392 	/* 3. If not found, search the general commands */
    393 
    394 	if (!found)
    395 		for (i=0;i<=general_commands.last_command && !found;i++) {
    396 			if (strcasecmp (command,general_commands.names [i])==0) {
    397 				(*general_commands.callback [i]) (command_line);
    398 				found=1;
    399 			}
    400 		}
    401 
    402 	/* 4. If not found, issue an error message and return */
    403 
    404 	if (!found) {
    405 		wprintw (command_win,"Error: Unknown command\n");
    406 		refresh_command_win ();
    407 	}
    408 
    409 	return (0);
    410 }
    411 
    412 
    413 /*
    414  *
    415  * This function copies the next word in source to the variable dest,
    416  * ignoring whitespaces.  It returns a pointer to the next word in
    417  * source.  It is used to split the command line into command and arguments.
    418  */
    419 char *parse_word (char *source,char *dest)
    420 {
    421 	char ch,*source_ptr,*target_ptr;
    422 
    423 	if (*source==0) {
    424 		*dest=0;
    425 		return (source);
    426 	};
    427 
    428 	source_ptr=source;target_ptr=dest;
    429 	do {
    430 		ch=*source_ptr++;
    431 	} while (! (ch>' ' && ch<='z') && ch!=0);
    432 
    433 	while (ch>' ' && ch<='z') {
    434 		*target_ptr++=ch;
    435 		ch=*source_ptr++;
    436 	}
    437 
    438 	*target_ptr=0;
    439 
    440 	source_ptr--;
    441 	do {
    442 		ch=*source_ptr++;
    443 	} while (! (ch>' ' && ch<='z') && ch!=0);
    444 
    445 	return (--source_ptr);
    446 }
    447 
    448 /*
    449  * text is the partial command entered by the user; We assume that it
    450  * is a part of a command - I didn't write code for smarter completion.
    451  *
    452  * The state variable is an index which tells us how many possible
    453  * completions we already returned to readline.
    454  *
    455  * We return only one possible completion or (char *) NULL if there
    456  * are no more completions. This function will be called by readline
    457  * over and over until we tell it to stop.
    458  *
    459  * While scanning for possible completions, we use the same priority
    460  * definition which was used in dispatch.
    461  */
    462 #if HAVE_READLINE
    463 char *complete_command (char *text,int state)
    464 {
    465 	int state_index=-1;
    466 	int i,len;
    467 
    468 	len=strlen (text);
    469 
    470 	/* Is the command type specific ? */
    471 
    472 	if (current_type != NULL)
    473 		for (i=0;i<=current_type->type_commands.last_command;i++) {
    474 			if (strncmp (current_type->type_commands.names [i],text,len)==0) {
    475 				state_index++;
    476 				if (state==state_index) {
    477 					return (dupstr (current_type->type_commands.names [i]));
    478 				}
    479 			}
    480 		}
    481 
    482 	/* No, pehaps ext2 specific command then ? */
    483 
    484 	for (i=0;i<=ext2_commands.last_command;i++) {
    485 		if (strncmp (ext2_commands.names [i],text,len)==0) {
    486 			state_index++;
    487 			if (state==state_index)
    488 			return (dupstr (ext2_commands.names [i]));
    489 		}
    490 	}
    491 
    492 
    493 	/* Check for a general command */
    494 
    495 	for (i=0;i<=general_commands.last_command;i++) {
    496 		if (strncmp (general_commands.names [i],text,len)==0) {
    497 				state_index++;
    498 				if (state==state_index)
    499 					return (dupstr (general_commands.names [i]));
    500 		}
    501 	}
    502 
    503 	/* quit is handled differently */
    504 
    505 	if (strncmp ("quit",text,len)==0) {
    506 		state_index++;
    507 		if (state==state_index)
    508 			return (dupstr ("quit"));
    509 	}
    510 
    511 	/* No more completions */
    512 
    513 	return ((char *) NULL);
    514 }
    515 #endif
    516 
    517 
    518 /*
    519  * Nothing special - Just allocates enough space and copy the string.
    520  */
    521 char *dupstr (char *src)
    522 {
    523 	char *ptr;
    524 
    525 	ptr=(char *) malloc (strlen (src)+1);
    526 	strcpy (ptr,src);
    527 	return (ptr);
    528 }
    529 
    530 #ifdef DEBUG
    531 /*
    532  * This function reports an internal error. It is almost not used. One
    533  * place in which I do check for internal errors is disk.c.
    534  *
    535  * We just report the error, and try to continue ...
    536  */
    537 void internal_error (char *description,char *source_name,char *function_name)
    538 {
    539 	wprintw (command_win,"Internal error - Found by source: %s.c , function: %s\n",source_name,function_name);
    540 	wprintw (command_win,"\t%s\n",description);
    541 	wprintw (command_win,"Press enter to (hopefully) continue\n");
    542 	refresh_command_win ();getch ();werase (command_win);
    543 }
    544 
    545 #endif
    546