Blob Blame History Raw
 boom/command.py | 84 ++++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 63 insertions(+), 21 deletions(-)

diff --git a/boom/command.py b/boom/command.py
index dc9abae..8900457 100644
--- a/boom/command.py
+++ b/boom/command.py
@@ -482,24 +482,72 @@ def _do_print_type(report_fields, selected, output_fields=None,
     return br.report_output()
 
 
-def _merge_add_del_opts(orig_opts, opts):
+def _merge_add_del_opts(bp, add_opts, del_opts):
     """Merge a set of existing bootparams option alterations with
         a set of command-line provided values to produce a single
         set of options to add or remove from a cloned or edited
         ``BootEntry``.
-        :param orig_opts: A list of original option modifications
-        :param opts: A space-separated string containing a list of
-                     command line option modifications
-        :returns: A single list containing the merged options
-    """
-    # Merge new and cloned kernel options
-    all_opts = set()
-    if opts:
-        all_opts.update(opts.split())
-    if orig_opts:
-        all_opts.update(orig_opts)
 
-    return list(all_opts)
+        The sets are merged giving precedence to alterations on the
+        current command line: i.e. if an option is present in both
+        ``bp.del_opts`` and ``add_opts`` (or vice versa) then the
+        option taken from the current command line will be effective.
+
+        :param bp: A ``BootParams`` object with the original ``add_opts``
+                   and ``del_opts`` values.
+        :param add_opts: A space-separated string containing a list of
+                         additional options taken from the current
+                         command line.
+        :param del_opts: A space-separated string containing a list of
+                         options to delete taken from the current
+                         command line.
+        :returns: A tuple ``(effective_add_opts, effective_del_opts)``
+                  giving the final effective values as a list of
+                  strings, one per option word.
+    """
+    def _merge_opts(orig_opts, opts, r_opts):
+        # Merge new and cloned kernel options
+        all_opts = set()
+        if opts:
+            all_opts.update(opts)
+        if orig_opts:
+            all_opts.update(orig_opts)
+        all_opts = list(all_opts)
+        return [o for o in all_opts if o not in r_opts]
+
+    _log_debug_cmd("Add opts: %s" % add_opts)
+    _log_debug_cmd("Del opts: %s" % del_opts)
+    _log_debug_cmd("Original add_opts: %s" % bp.add_opts)
+    _log_debug_cmd("Original del_opts: %s" % bp.del_opts)
+
+    r_del_opts = []
+    r_add_opts = []
+
+    add_opts = add_opts.split() if add_opts else []
+    del_opts = del_opts.split() if del_opts else []
+
+    for add_opt in list(add_opts):
+        # Do not allow conflicting command line add/del opts
+        if add_opt in del_opts:
+            raise ValueError("Conflicting --add-opts %s and --del-opts %s" %
+                             (add_opt, add_opt))
+
+        if add_opt in bp.del_opts:
+            r_del_opts.append(add_opt)
+            add_opts.remove(add_opt)
+
+    for del_opt in list(del_opts):
+        if del_opt in bp.add_opts:
+            r_add_opts.append(del_opt)
+            del_opts.remove(del_opt)
+
+    add_opts = _merge_opts(bp.add_opts, add_opts, r_add_opts)
+    del_opts = _merge_opts(bp.del_opts, del_opts, r_del_opts)
+
+    _log_debug_cmd("Effective add options: %s" % add_opts)
+    _log_debug_cmd("Effective del options: %s" % del_opts)
+
+    return (add_opts, del_opts)
 
 
 #
@@ -759,10 +807,7 @@ def clone_entry(selection=None, title=None, version=None, machine_id=None,
                        else be.bp.btrfs_subvol_id)
     profile = profile if profile else be._osp
 
-    add_opts = _merge_add_del_opts(be.bp.add_opts, add_opts)
-    del_opts = _merge_add_del_opts(be.bp.del_opts, del_opts)
-    _log_debug_cmd("Effective add options: %s" % add_opts)
-    _log_debug_cmd("Effective del options: %s" % del_opts)
+    (add_opts, del_opts) = _merge_add_del_opts(be.bp, add_opts, del_opts)
 
     bp = BootParams(version, root_device, lvm_root_lv=lvm_root_lv,
                     btrfs_subvol_path=btrfs_subvol_path,
@@ -864,10 +909,7 @@ def edit_entry(selection=None, title=None, version=None, machine_id=None,
     machine_id = machine_id or be.machine_id
     version = version or be.version
 
-    add_opts = _merge_add_del_opts(be.bp.add_opts, add_opts)
-    del_opts = _merge_add_del_opts(be.bp.del_opts, del_opts)
-    _log_debug_cmd("Effective add options: %s" % add_opts)
-    _log_debug_cmd("Effective del options: %s" % del_opts)
+    (add_opts, del_opts) = _merge_add_del_opts(be.bp, add_opts, del_opts)
 
     be._osp = profile or be._osp
     be.title = title or be.title