diff options
Diffstat (limited to '')
-rw-r--r-- | ext2ed/main.c | 548 |
1 files changed, 548 insertions, 0 deletions
diff --git a/ext2ed/main.c b/ext2ed/main.c new file mode 100644 index 0000000..9d33a8e --- /dev/null +++ b/ext2ed/main.c @@ -0,0 +1,548 @@ +/* + +/usr/src/ext2ed/main.c + +A part of the extended file system 2 disk editor. + +------------ +Main program +------------ + +This file mostly contains: + +1. A list of global variables used through the entire program. +2. The parser, which asks the command line from the user. +3. The dispatcher, which analyzes the command line and calls the appropriate handler function. +4. A command pattern matcher which is used along with the readline completion feature. +5. A function which tells the user that an internal error has occurred. + +First written on: March 30 1995 + +Copyright (C) 1995 Gadi Oxman + +*/ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> + +#ifdef HAVE_READLINE +#include <readline.h> +#include <history.h> +#endif + +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#else +extern int optind; +extern char *optarg; +#endif + +#include "ext2ed.h" + +/* Global variables */ + +/* + +Configuration file options + +The following variables will be set by init.c to the values selected in the user configuration file. +They are initialized below to some logical defaults. + +*/ + + +char Ext2Descriptors [200]="ext2.descriptors"; /* The location of the ext2 filesystem object definition */ +char AlternateDescriptors [200]=""; /* We allow the user to define additional structures */ +char LogFile [200]="ext2ed.log"; /* The location of the log file - Each write will be logged there */ +int LogChanges=1; /* 1 enables logging, 0 disables logging */ +int AllowChanges=0; /* When set, the enablewrite command will fail */ +int AllowMountedRead=0; /* Behavior when trying to open a mounted filesystem read-only */ +int ForceExt2=0; /* When set, ext2 autodetection is overridden */ +int DefaultBlockSize=1024; +unsigned long DefaultTotalBlocks=2097151; +unsigned long DefaultBlocksInGroup=8192; /* The default values are used when an ext2 filesystem is not */ +int ForceDefault=0; /* detected, or ForceDefault is set */ + +char last_command_line [80]; /* A simple one command cache, in addition to the readline history */ + +char device_name [80]; /* The location of the filesystem */ +FILE *device_handle=NULL; /* This is passed to the fopen / fread ... commands */ +long device_offset; /* The current position in the filesystem */ + /* Note that we have a 2 GB limitation */ + +int mounted=0; /* This is set when we find that the filesystem is mounted */ + +struct struct_commands general_commands,ext2_commands; /* Used to define the general and ext2 commands */ +struct struct_descriptor *first_type,*last_type,*current_type; /* Used to access the double linked list */ +struct struct_type_data type_data; /* The current data is sometimes stored here */ +struct struct_file_system_info file_system_info; /* Essential information on the filesystem */ +struct struct_file_info file_info,first_file_info; /* Used by file_com.c to access files */ +struct struct_group_info group_info; /* Used by group_com.c */ +struct struct_super_info super_info; /* Used by super_com.c */ +struct struct_remember_lifo remember_lifo; /* A circular memory of objects */ +struct struct_block_bitmap_info block_bitmap_info; /* Used by blockbitmap_com.c */ +struct struct_inode_bitmap_info inode_bitmap_info; /* Used by inodebitmap_com.c */ + +int redraw_request=0; /* Is set by a signal handler to handle terminal */ + /* screen size change. */ + + +/* + * We just call the parser to get commands from the user. We quit when + * parser returns. + */ +int main (int argc, char **argv) +{ + int write_priv = 0; + int c; + char *buf; + + if (!init ()) + return (1); + while ((c = getopt (argc, argv, "w")) != EOF) { + switch (c) { + case 'w': + write_priv++; + break; + } + } + if (optind < argc) { + buf = malloc(strlen(argv[optind]) + 32); + if (!buf) { + fprintf(stderr, "Couldn't allocate filename buffer\n"); + exit(1); + } + strcpy(buf, "set_device "); + strcat(buf, argv[optind]); + set_device(buf); + free(buf); + if (write_priv) { + wprintw (command_win,"\n"); + enable_write("enable_write"); + } + } + parser (); /* Get and parse user commands */ + prepare_to_close(); /* Do some cleanup */ + printf("Quitting ...\n"); + return(0); +} + + +/* + * Read a character from the command window + */ +int command_read_key() +{ + int key = 0; + + while (!key) { + if (redraw_request) { + redraw_all(); + redraw_request=0; + } + key = wgetch(command_win); + switch (key) { + case 0x1A: + key = 0; + kill(getpid(), SIGTSTP); + break; + + case KEY_NPAGE: + pgdn(""); + refresh_command_win (); + break; + + case KEY_PPAGE: + pgup(""); + refresh_command_win (); + break; + case ERR: + key = 0; + break; + + case KEY_BACKSPACE: + key = '\b'; + } + if ((key < 32 && key != '\b' && key != '\n') || + (key > 127)) + key = 0; + } + return key; +} + +#ifdef HAVE_READLINE +int rl_getc_replacement(FILE *f) +{ + int key = command_read_key(); + + if (key == '\b') { + if (rl_point > 0) + wprintw(command_win, "\b \b"); + } else + wprintw(command_win, "%c", key); + return key; +} + +/* + * This function asks the user for a command and calls the dispatcher + * function, dispatch, to analyze it. We use the readline library + * function readline to read the command, hence all the usual readline + * keys are available. The new command is saved both in the + * readline's history and in our tiny one-command cache, so that only + * the enter key is needed to retype it. + */ +void parser (void) +{ + char *ptr,command_line [80]; + int quit=0; + +#if 0 + noecho(); + cbreak(); + keypad(command_win, 1); + wtimeout(command_win, 100); + + rl_getc_function = rl_getc_replacement; +#endif + + while (!quit) { + /* Terminal screen size has changed */ + if (redraw_request) { + redraw_all(); + redraw_request=0; + } + + wmove (command_win,0,0); + wclrtoeol (command_win); + wprintw (command_win,"ext2ed > "); + refresh_command_win (); + + /* + * The ncurses library optimizes cursor movement by + * keeping track of the cursor position. However, by + * using the readline library I'm breaking its + * assumptions. The double -1 arguments tell ncurses + * to disable cursor movement optimization this + * time. + */ + mvcur (-1,-1,LINES-COMMAND_WIN_LINES,0); + + /* echo (); */ + ptr=readline ("ext2ed > "); + /* noecho (); */ + + /* + * Readline allocated the buffer - Copy the string + * and free the allocated buffer + * XXX WHY??? + */ + strcpy (command_line,ptr); + free (ptr); + + if (*command_line != 0) + add_history (command_line); + + /* If only enter was pressed, recall the last command */ + if (*command_line==0) + strcpy (command_line,last_command_line); + + /* Emulate readline's actions for ncurses */ + mvcur (-1,-1,LINES-COMMAND_WIN_LINES,0); + werase (command_win); + wprintw (command_win,"ext2ed > "); + wprintw (command_win,command_line); + wprintw (command_win,"\n"); + refresh_command_win (); + + /* Save this command in our tiny cache */ + strcpy (last_command_line,command_line); + + /* And call dispatch to do the actual job */ + quit=dispatch (command_line); + } +} +#else +void read_line(char * foo) { + char * chptr = foo; + int ch; + int done = 0; + + while (!done && (ch = command_read_key())) { + switch (ch) { + case '\n': + done = 1; + break; + + case '\b': + if (chptr > foo) { + wprintw(command_win, "\b \b"); + chptr--; + } + break; + + default: + if (ch > 256) + break; + if (ch == '\n') break; + *chptr++ = ch; + wprintw(command_win, "%c", ch); + break; + } + } + *chptr = '\0'; +} + +void parser (void) +{ + char command_line [80]; + int quit=0; + + noecho(); + cbreak(); + wtimeout(command_win, 100); + keypad(command_win, 1); + + while (!quit) { + /* Terminal screen size has changed */ + if (redraw_request) { + redraw_all(); + redraw_request=0; + } + + wmove (command_win,0,0);wclrtoeol (command_win); + + wmove(command_win, 0, 0); + wprintw(command_win, "ext2ed > "); + read_line(command_line); + + /* If only enter was pressed, recall the last command */ + if (*command_line==0) + strcpy (command_line,last_command_line); + + mvcur (-1,-1,LINES-COMMAND_WIN_LINES + 1,0); + + strcpy (last_command_line,command_line); /* Save this command in our tiny cache */ + + /* And call dispatch to do the actual job */ + quit=dispatch (command_line); + } +} +#endif + + +/* + * This is a very important function. Its task is to receive a command + * name and link it to a C function. There are three types of commands: + * + * 1. General commands - Always available and accessed through + * general_commands. + * 2. Ext2 specific commands - Available when editing an ext2 + * filesystem, accessed through ext2_commands. + * 3. Type specific commands - Those are changing according to the + * current type. The global variable current_type points to the + * current object definition (of type struct_descriptor). In it, the + * struct_commands entry contains the type specific commands links. + * + * Overriding is an important feature - Much like in C++ : The same + * command name can dispatch to different functions. The overriding + * priority is 3,2,1; That is - A type specific command will always + * override a general command. This is used through the program to + * allow fine tuned operation. + * + * When an handling function is found, it is called along with the + * command line that was passed to us. The handling function is then + * free to interpret the arguments in its own style. + */ +int dispatch (char *command_line) +{ + int i,found=0; + + char command [80]; + + parse_word (command_line,command); + + if (strcasecmp (command,"quit")==0) return (1); + + /* 1. Search for type specific commands FIRST - Allows + overriding of a general command */ + + if (current_type != NULL) + for (i=0; + i<=current_type->type_commands.last_command && !found; + i++) { + if (strcasecmp (command,current_type->type_commands.names [i])==0) { + (*current_type->type_commands.callback [i]) (command_line); + found=1; + } + } + + /* 2. Now search for ext2 filesystem general commands */ + + if (!found) + for (i=0;i<=ext2_commands.last_command && !found;i++) { + if (strcasecmp (command,ext2_commands.names [i])==0) { + (*ext2_commands.callback [i]) (command_line); + found=1; + } + } + + + /* 3. If not found, search the general commands */ + + if (!found) + for (i=0;i<=general_commands.last_command && !found;i++) { + if (strcasecmp (command,general_commands.names [i])==0) { + (*general_commands.callback [i]) (command_line); + found=1; + } + } + + /* 4. If not found, issue an error message and return */ + + if (!found) { + wprintw (command_win,"Error: Unknown command\n"); + refresh_command_win (); + } + + return (0); +} + + +/* + * + * This function copies the next word in source to the variable dest, + * ignoring whitespaces. It returns a pointer to the next word in + * source. It is used to split the command line into command and arguments. + */ +char *parse_word (char *source,char *dest) +{ + char ch,*source_ptr,*target_ptr; + + if (*source==0) { + *dest=0; + return (source); + }; + + source_ptr=source;target_ptr=dest; + do { + ch=*source_ptr++; + } while (! (ch>' ' && ch<='z') && ch!=0); + + while (ch>' ' && ch<='z') { + *target_ptr++=ch; + ch=*source_ptr++; + } + + *target_ptr=0; + + source_ptr--; + do { + ch=*source_ptr++; + } while (! (ch>' ' && ch<='z') && ch!=0); + + return (--source_ptr); +} + +/* + * text is the partial command entered by the user; We assume that it + * is a part of a command - I didn't write code for smarter completion. + * + * The state variable is an index which tells us how many possible + * completions we already returned to readline. + * + * We return only one possible completion or (char *) NULL if there + * are no more completions. This function will be called by readline + * over and over until we tell it to stop. + * + * While scanning for possible completions, we use the same priority + * definition which was used in dispatch. + */ +#if HAVE_READLINE +char *complete_command (char *text,int state) +{ + int state_index=-1; + int i,len; + + len=strlen (text); + + /* Is the command type specific ? */ + + if (current_type != NULL) + for (i=0;i<=current_type->type_commands.last_command;i++) { + if (strncmp (current_type->type_commands.names [i],text,len)==0) { + state_index++; + if (state==state_index) { + return (dupstr (current_type->type_commands.names [i])); + } + } + } + + /* No, perhaps ext2 specific command then ? */ + + for (i=0;i<=ext2_commands.last_command;i++) { + if (strncmp (ext2_commands.names [i],text,len)==0) { + state_index++; + if (state==state_index) + return (dupstr (ext2_commands.names [i])); + } + } + + + /* Check for a general command */ + + for (i=0;i<=general_commands.last_command;i++) { + if (strncmp (general_commands.names [i],text,len)==0) { + state_index++; + if (state==state_index) + return (dupstr (general_commands.names [i])); + } + } + + /* quit is handled differently */ + + if (strncmp ("quit",text,len)==0) { + state_index++; + if (state==state_index) + return (dupstr ("quit")); + } + + /* No more completions */ + + return ((char *) NULL); +} +#endif + + +/* + * Nothing special - Just allocates enough space and copy the string. + */ +char *dupstr (char *src) +{ + char *ptr; + + ptr=(char *) malloc (strlen (src)+1); + if (!ptr) + return NULL; + strcpy (ptr,src); + return (ptr); +} + +#ifdef DEBUG +/* + * This function reports an internal error. It is almost not used. One + * place in which I do check for internal errors is disk.c. + * + * We just report the error, and try to continue ... + */ +void internal_error (char *description,char *source_name,char *function_name) +{ + wprintw (command_win,"Internal error - Found by source: %s.c , function: %s\n",source_name,function_name); + wprintw (command_win,"\t%s\n",description); + wprintw (command_win,"Press enter to (hopefully) continue\n"); + refresh_command_win ();getch ();werase (command_win); +} + +#endif |