summaryrefslogtreecommitdiffstats
path: root/ext2ed/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext2ed/main.c')
-rw-r--r--ext2ed/main.c548
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