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