Added history with the capability to undo/redo before the disk changes are actually written to disk.
Matt Davis
mattdavis9 at gmail.com
Thu Jun 28 04:04:44 UTC 2007
Three commands were added
1) Save (commits changes to disk)
2) Undo Undoes the previous change
3) Redo Redoes the previous change
This is still in testing but I feel it is workable now to put in a patch. Please give me feedback as to positives/negatives of this change.
Also, the idea of collecting all changes and applying them at the end was from Vanni Brutto, who posted his concept 7 November 2006. The idea I use is basically deep copying disk objects and command objects from the 'do_***' commands in parted.c. When the 'Save' command is issued the collected disk changes are applied via ped_disk_commit().
-Matt
---
include/parted/disk.h | 5 +-
libparted/disk.c | 11 +++
parted/Makefile.am | 2 +
parted/command.c | 2 +
parted/history.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++++
parted/history.h | 67 +++++++++++++++++
parted/parted.c | 101 +++++++++++++++++++++-----
7 files changed, 361 insertions(+), 20 deletions(-)
create mode 100644 parted/history.c
create mode 100644 parted/history.h
diff --git a/include/parted/disk.h b/include/parted/disk.h
index 4dd81d9..bdf8ba8 100644
--- a/include/parted/disk.h
+++ b/include/parted/disk.h
@@ -261,10 +261,13 @@ extern PedDisk* ped_disk_new_fresh (PedDevice* dev,
extern PedDisk* ped_disk_duplicate (const PedDisk* old_disk);
extern void ped_disk_destroy (PedDisk* disk);
extern int ped_disk_commit (PedDisk* disk);
-extern int ped_disk_commit_to_dev (PedDisk* disk);
extern int ped_disk_commit_to_os (PedDisk* disk);
extern int ped_disk_check (const PedDisk* disk);
extern void ped_disk_print (const PedDisk* disk);
+/* Located in parted/history.h
+ * extern void ped_disk_commit_to_history (const PedDisk* disk);
+ */
+extern int ped_disk_commit_to_dev (PedDisk* disk);
extern int ped_disk_get_primary_partition_count (const PedDisk* disk);
extern int ped_disk_get_last_partition_num (const PedDisk* disk);
diff --git a/libparted/disk.c b/libparted/disk.c
index 547d037..1593484 100644
--- a/libparted/disk.c
+++ b/libparted/disk.c
@@ -510,6 +510,17 @@ ped_disk_commit (PedDisk* disk)
}
/**
+ * Adds the changes to disk to the command history.
+ * This allows the commands to have an undo/redo effect
+ * without actually performing any (possibly unwanted)
+ * disk actions.
+ *
+ * This is actually defined in parted/history.c
+void
+ped_disk_commit_to_history(const PedDisk *disk)
+*/
+
+/**
* \addtogroup PedPartition
*
* @{
diff --git a/parted/Makefile.am b/parted/Makefile.am
index c699102..80023de 100644
--- a/parted/Makefile.am
+++ b/parted/Makefile.am
@@ -9,6 +9,8 @@ parted_SOURCES = command.c \
strlist.h \
ui.c \
ui.h \
+ history.c \
+ history.h \
table.c \
table.h
diff --git a/parted/command.c b/parted/command.c
index a5c4998..f6c155e 100644
--- a/parted/command.c
+++ b/parted/command.c
@@ -20,6 +20,7 @@
#include <config.h>
#include "command.h"
#include "ui.h"
+#include "history.h"
#include <parted/debug.h>
@@ -137,6 +138,7 @@ command_print_help (Command* cmd)
int
command_run (Command* cmd, PedDevice** dev)
{
+ history_add(cmd);
return cmd->method (dev);
}
diff --git a/parted/history.c b/parted/history.c
new file mode 100644
index 0000000..ff955d3
--- /dev/null
+++ b/parted/history.c
@@ -0,0 +1,193 @@
+#include <stdio.h>
+#include "history.h"
+#include "ui.h"
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+
+struct PedHistoryManager_t PedHistMgr = {0};
+
+
+void history_add(const Command *cmd)
+{
+ PedHistObj *addme;
+
+ /* Deep copy and add to end of list */
+ addme = history_alloc_obj();
+ addme->cmd = *cmd;
+ addme->cmd.help = str_list_duplicate(cmd->help);
+ addme->cmd.names = str_list_duplicate(cmd->names);
+ addme->cmd.summary = str_list_duplicate(cmd->summary);
+ addme->id = PedHistMgr.id++;
+
+ /* This becomes the new end, so remember who is before us */
+ addme->prev = PedHistMgr.end;
+
+ /* Add this to the end of the list */
+ if (PedHistMgr.end)
+ PedHistMgr.end->next = addme;
+ PedHistMgr.end = addme;
+
+ if (!PedHistMgr.begin)
+ PedHistMgr.begin = addme;
+
+ PedHistMgr.n_objs++;
+}
+
+
+void history_clear(void)
+{
+ PedHistObj *ii, *freeme;
+
+ ii = PedHistMgr.begin;
+ while (ii)
+ {
+ freeme = ii;
+ ii = ii->next;
+ history_dealloc_obj(freeme);
+ }
+
+ memset(&PedHistMgr, 0, sizeof(PedHistMgr));
+}
+
+
+void history_undo(void)
+{
+ PedHistObj *ii;
+
+ /* Mark the most recent change as ignored and that is an
+ * actual 'disk' modification
+ * Start with the last command issued before this 'undo' call
+ */
+ for (ii=PedHistMgr.end->prev; ii; ii=ii->prev)
+ if (!ii->ignore && ii->disk)
+ break;
+
+ if (!ii)
+ {
+ printf(PED_HISTORY_TAG "Cannot undo\n");
+ return;
+ }
+
+ ii->ignore = 1;
+ printf(PED_HISTORY_TAG "Undo: ");
+ str_list_print(ii->cmd.names);
+ fputs("\n", stdout);
+}
+
+
+void history_redo(void)
+{
+ PedHistObj *ii;
+
+ /* Find the most recent undone entry that is a 'disk' modification */
+ for (ii=PedHistMgr.begin; ii; ii=ii->next)
+ if (ii->ignore && ii->disk)
+ break;
+
+ if (!ii)
+ {
+ printf(PED_HISTORY_TAG "Cannot redo\n");
+ return;
+ }
+
+ ii->ignore = 0;
+ printf(PED_HISTORY_TAG "Redo: ");
+ str_list_print(ii->cmd.names);
+ fputs("\n", stdout);
+}
+
+
+void ped_disk_commit_to_history(const PedDisk *disk)
+{
+ PedHistMgr.end->disk = ped_disk_duplicate(disk);
+}
+
+
+PedHistObj *history_alloc_obj(void)
+{
+ int size = sizeof(PedHistObj) + sizeof(Command);
+ PedHistObj *obj = (PedHistObj *)malloc(size);
+ memset(obj, 0, size);
+ return obj;
+}
+
+
+void history_dealloc_obj(PedHistObj *obj)
+{
+ if (!obj)
+ return;
+
+ if (obj->disk)
+ ped_disk_destroy(obj->disk);
+ str_list_destroy(obj->cmd.help);
+ str_list_destroy(obj->cmd.names);
+ str_list_destroy(obj->cmd.summary);
+ free(obj);
+}
+
+
+void history_commit_to_disk(void)
+{
+ PedHistObj *ii;
+
+ for (ii=PedHistMgr.begin; ii; ii=ii->next)
+ if (ii->disk && !ii->ignore)
+ {
+ printf(PED_HISTORY_TAG "Apply ");
+ str_list_print_wrap(ii->cmd.names, screen_width(), 8, 8);
+ if (ped_disk_commit(ii->disk))
+ printf(" (Success)\n");
+ else
+ printf(" (Failure)\n");
+ }
+
+ /* Start fresh */
+ history_clear();
+}
+
+
+static void history_pr(PedHistObj *obj)
+{
+ /* Only print disk changes */
+ if (!obj->disk)
+ return;
+
+ printf("[History %d]\t", obj->id);
+ str_list_print_wrap(obj->cmd.names, screen_width(), 8, 8);
+ if (obj->ignore)
+ printf(" (UNDONE)");
+ fputs("\n", stdout);
+}
+
+
+/* Print range (or specific if start == end) */
+void history_print(int start, int end)
+{
+ int i;
+ PedHistObj *ii;
+
+ for (i=0, ii=PedHistMgr.begin;
+ ii && i>=start && i<end;
+ ii=ii->next, i++)
+ {
+ history_pr(ii);
+ }
+}
+
+
+void history_print_specific(int idx)
+{
+ history_print(idx, idx);
+}
+
+
+void history_print_all(void)
+{
+ history_print(0, PedHistMgr.n_objs);
+}
diff --git a/parted/history.h b/parted/history.h
new file mode 100644
index 0000000..ae81cc0
--- /dev/null
+++ b/parted/history.h
@@ -0,0 +1,67 @@
+#ifndef HISTORY_H_INCLUDED
+#define HISTORY_H_INCLUDED
+
+#include "command.h"
+
+
+#define PED_HISTORY_TAG "[History] "
+
+
+/*
+ * Structs
+ */
+
+typedef struct PedHistObj {
+ int id;
+ int ignore; /* Undo/Redo functionality */
+ Command cmd;
+ PedDisk *disk;
+
+ struct PedHistObj *prev;
+ struct PedHistObj *next;
+} PedHistObj;
+
+
+struct PedHistoryManager_t{
+ PedHistObj *begin;
+ PedHistObj *end;
+ int n_objs;
+ int id;
+};
+
+extern struct PedHistoryManager_t PedHistMgr;
+
+
+/*
+ * Funcs
+ */
+
+/* Add/Clear history */
+extern void history_add(const Command *cmd);
+extern void history_clear(void);
+
+/* Before changes are committed */
+extern void history_undo(void);
+extern void history_redo(void);
+
+/* Write changes to disk */
+extern void history_commit_to_disk(void);
+
+/* Print */
+extern void history_print(int start, int end);
+extern void history_print_specific(int i);
+extern void history_print_all(void);
+
+/* Alloc/dealloc */
+extern PedHistObj *history_alloc_obj(void);
+extern void history_dealloc_obj(PedHistObj *obj);
+
+/* Remember changes
+ * Note: This is not defined in disk.h
+ * This preserves encapsulation between
+ * PedCommands and libparted
+ */
+extern void ped_disk_commit_to_history(const PedDisk *disk);
+
+
+#endif /* HISTORY_H_INCLUDED */
diff --git a/parted/parted.c b/parted/parted.c
index 73be7a4..e01f401 100644
--- a/parted/parted.c
+++ b/parted/parted.c
@@ -26,6 +26,7 @@
#include "command.h"
#include "ui.h"
#include "table.h"
+#include "history.h"
#define AUTHORS \
"<http://parted.alioth.debian.org/cgi-bin/trac.cgi/browser/AUTHORS>"
@@ -535,8 +536,7 @@ do_cp (PedDevice** dev)
/* update the partition table, close disks */
if (!ped_partition_set_system (dst, dst_fs_type))
goto error_destroy_disk;
- if (!ped_disk_commit (dst_disk))
- goto error_destroy_disk;
+ ped_disk_commit_to_history (dst_disk);
if (src_disk != dst_disk)
ped_disk_destroy (src_disk);
ped_disk_destroy (dst_disk);
@@ -620,8 +620,7 @@ do_mklabel (PedDevice** dev)
if (!disk)
goto error;
- if (!ped_disk_commit (disk))
- goto error_destroy_disk;
+ ped_disk_commit_to_history (disk);
ped_disk_destroy (disk);
if ((*dev)->type != PED_DEVICE_FILE)
@@ -666,8 +665,7 @@ do_mkfs (PedDevice** dev)
goto error_destroy_disk;
if (ped_partition_is_flag_available (part, PED_PARTITION_LBA))
ped_partition_set_flag (part, PED_PARTITION_LBA, 1);
- if (!ped_disk_commit (disk))
- goto error_destroy_disk;
+ ped_disk_commit_to_history (disk);
ped_disk_destroy (disk);
if ((*dev)->type != PED_DEVICE_FILE)
@@ -798,8 +796,7 @@ do_mkpart (PedDevice** dev)
if (ped_partition_is_flag_available (part, PED_PARTITION_LBA))
ped_partition_set_flag (part, PED_PARTITION_LBA, 1);
- if (!ped_disk_commit (disk))
- goto error_destroy_disk;
+ ped_disk_commit_to_history (disk);
/* clean up */
ped_constraint_destroy (final_constraint);
@@ -973,8 +970,7 @@ do_mkpartfs (PedDevice** dev)
if (!ped_partition_set_system (part, fs_type))
goto error_destroy_disk;
- if (!ped_disk_commit (disk))
- goto error_destroy_disk;
+ ped_disk_commit_to_history (disk);
/* clean up */
ped_constraint_destroy (final_constraint);
@@ -1091,8 +1087,7 @@ do_move (PedDevice** dev)
goto error_close_fs;
ped_file_system_close (fs_copy);
ped_file_system_close (fs);
- if (!ped_disk_commit (disk))
- goto error_destroy_disk;
+ ped_disk_commit_to_history (disk);
ped_disk_destroy (disk);
if (range_start != NULL)
ped_geometry_destroy (range_start);
@@ -1140,7 +1135,7 @@ do_name (PedDevice** dev)
goto error_free_name;
free (name);
- if (!ped_disk_commit (disk))
+ ped_disk_commit_to_history (disk);
goto error_destroy_disk;
ped_disk_destroy (disk);
return 1;
@@ -1641,7 +1636,7 @@ _rescue_add_partition (PedPartition* part)
}
ped_partition_set_system (part, fs_type);
- ped_disk_commit (part->disk);
+ ped_disk_commit_to_history (part->disk);
return 1;
}
@@ -1817,7 +1812,7 @@ do_resize (PedDevice** dev)
ped_file_system_close (fs);
}
- ped_disk_commit (disk);
+ ped_disk_commit_to_history (disk);
ped_constraint_destroy (constraint);
ped_disk_destroy (disk);
if (range_start != NULL)
@@ -1860,7 +1855,7 @@ do_rm (PedDevice** dev)
goto error_destroy_disk;
ped_disk_delete_partition (disk, part);
- ped_disk_commit (disk);
+ ped_disk_commit_to_history (disk);
ped_disk_destroy (disk);
if ((*dev)->type != PED_DEVICE_FILE)
@@ -1915,8 +1910,7 @@ do_set (PedDevice** dev)
if (!ped_partition_set_flag (part, flag, state))
goto error_destroy_disk;
- if (!ped_disk_commit (disk))
- goto error_destroy_disk;
+ ped_disk_commit_to_history (disk);
ped_disk_destroy (disk);
if ((*dev)->type != PED_DEVICE_FILE)
@@ -1960,7 +1954,35 @@ do_version ()
_(copyright_msg));
return 1;
}
-
+
+static int
+do_undo ()
+{
+ history_undo();
+ return 1;
+}
+
+static int
+do_redo ()
+{
+ history_redo();
+ return 1;
+}
+
+static int
+do_history ()
+{
+ history_print_all();
+ return 1;
+}
+
+static int
+do_save ()
+{
+ history_commit_to_disk();
+ return 1;
+}
+
static void
_init_messages ()
{
@@ -2279,6 +2301,47 @@ _("'version' displays copyright and version information corresponding to this "
"copy of GNU Parted\n"),
NULL), 1));
+command_register (commands, command_create (
+ str_list_create_unique ("undo", _("undo"), NULL),
+ do_undo,
+ str_list_create (
+_("undo Undo change to partition table"),
+ NULL),
+ str_list_create (
+_("'undo' undoes the previous partition table modifcation "), NULL), 1));
+
+command_register (commands, command_create (
+ str_list_create_unique ("redo", _("redo"), NULL),
+ do_redo,
+ str_list_create (
+_("redo Redo change to partition table"),
+ NULL),
+ str_list_create (
+_("'redo' redoes the previous change to the parititon table"),
+NULL), 1));
+
+
+
+command_register (commands, command_create (
+ str_list_create_unique ("history", _("history"), NULL),
+ do_history,
+ str_list_create (
+_("history display command history"), NULL),
+ str_list_create (
+_("'history' displays list of commands that have are to be executed\n"),
+NULL), 1));
+
+command_register (commands, command_create (
+ str_list_create_unique ("save", _("save"), NULL),
+ do_save,
+ str_list_create (
+_("save Write changes to partition table"),
+ NULL),
+ str_list_create (
+_("'save' commits the changes that were created during the current parted "
+ "session. This writes the changes to disk."),
+NULL), 1));
+
}
static void
--
1.5.2
--------------060807040702010406000404--
More information about the parted-devel
mailing list