[libsendmail-milter-perl] 16/20: Imported Upstream version 1.0

Hilko Bengen bengen at moszumanska.debian.org
Sun Sep 27 18:32:54 UTC 2015


This is an automated email from the git hooks/post-receive script.

bengen pushed a commit to annotated tag debian/1.0-1
in repository libsendmail-milter-perl.

commit 58b3d466a4ca4dbd30d9de4920f4c55b5ec92068
Author: Hilko Bengen <bengen at debian.org>
Date:   Tue Jun 12 22:46:25 2012 +0200

    Imported Upstream version 1.0
---
 Changes                                |  20 +++
 MANIFEST                               |   1 +
 META.yml                               |  40 +++--
 Makefile.PL                            |   1 +
 README                                 |   2 +-
 bin/gtkregcompare.pl                   | 243 +++++----------------------
 bin/gtkregscope.pl                     |  49 +++---
 bin/gtkregview.pl                      | 297 +--------------------------------
 bin/regdump.pl                         |   2 +-
 bin/regml.pl                           |  55 ++++++
 bin/regscan.pl                         |  13 +-
 bin/regsecurity.pl                     |   7 +-
 bin/wxregcompare.pl                    |  76 +++------
 bin/wxregscope.pl                      |  82 ++++-----
 bin/wxregview.pl                       |  63 +++----
 lib/Parse/Win32Registry.pm             |  29 ++--
 lib/Parse/Win32Registry/Base.pm        |  52 ++++--
 lib/Parse/Win32Registry/Entry.pm       |   6 +
 lib/Parse/Win32Registry/Value.pm       |   2 +-
 lib/Parse/Win32Registry/Win95/File.pm  |   1 +
 lib/Parse/Win32Registry/Win95/Key.pm   |  14 --
 lib/Parse/Win32Registry/Win95/Value.pm |  30 ++--
 lib/Parse/Win32Registry/WinNT/File.pm  |   1 +
 lib/Parse/Win32Registry/WinNT/Key.pm   |  40 +----
 lib/Parse/Win32Registry/WinNT/Value.pm | 149 +++++++++++++----
 t/errors.t                             |   2 +-
 t/misc.t                               |   4 +-
 t/security.t                           |  13 ++
 t/use.t                                |   2 +-
 t/value.t                              | 182 ++++++++++++++++++++
 t/win95_value_tests.rf                 | Bin 995 -> 1255 bytes
 t/winnt_value_tests.rf                 | Bin 6544 -> 7200 bytes
 32 files changed, 673 insertions(+), 805 deletions(-)

diff --git a/Changes b/Changes
index 1736f52..f7d9ad2 100644
--- a/Changes
+++ b/Changes
@@ -1,5 +1,25 @@
 Revision history for Perl extension Parse::Win32Registry.
 
+** 1.0 2012-04-29
+
+Added support for decoding System Mandatory Label ACEs (a feature
+introduced with Windows Vista) and added the command line script
+regml.pl for listing keys with explicit System Mandatory Label ACEs
+set. Improved handling of security descriptors.
+
+Tidied up various aspects of the wxWidgets and GTK applications, and
+harmonised functionality between the various pairs of equivalent
+programs, with a minor difference being the wxWidgets applications
+following Windows keyboard shortcut conventions while the GTK
+applications following Linux keyboard shortcut conventions.
+
+The get_data method of Value objects now returns the unpacked integer
+value for REG_DWORD_BIG_ENDIAN value types instead of the original
+packed binary data.
+
+Added support for values with 'big data'. Thanks to Harlan Carvey for
+all his help with this.
+
 ** 0.60 2010-08-15
 
 Parse::Win32Registry now requires Perl 5.8.1.
diff --git a/MANIFEST b/MANIFEST
index cbb7588..c15f2e4 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -10,6 +10,7 @@ bin/regdiff.pl
 bin/regdump.pl
 bin/regexport.pl
 bin/regfind.pl
+bin/regml.pl
 bin/regmultidiff.pl
 bin/regscan.pl
 bin/regsecurity.pl
diff --git a/META.yml b/META.yml
index f7df916..e99c6f8 100644
--- a/META.yml
+++ b/META.yml
@@ -1,19 +1,27 @@
 --- #YAML:1.0
-name:                Parse-Win32Registry
-version:             0.60
-abstract:            Parse Windows Registry Files
-license:             perl
-author:              
+name:               Parse-Win32Registry
+version:            1.0
+abstract:           Parse Windows Registry Files
+author:
     - James Macfarlane
-generated_by:        ExtUtils::MakeMaker version 6.42
-distribution_type:   module
-requires:     
-    Carp:                          0
-    Data::Dumper:                  0
-    Encode:                        0
-    File::Basename:                0
-    Test::More:                    0
-    Time::Local:                   0
+license:            perl
+distribution_type:  module
+configure_requires:
+    ExtUtils::MakeMaker:  0
+build_requires:
+    ExtUtils::MakeMaker:  0
+requires:
+    Carp:            0
+    Data::Dumper:    0
+    Encode:          0
+    File::Basename:  0
+    Test::More:      0
+    Time::Local:     0
+no_index:
+    directory:
+        - t
+        - inc
+generated_by:       ExtUtils::MakeMaker version 6.55_02
 meta-spec:
-    url:     http://module-build.sourceforge.net/META-spec-v1.3.html
-    version: 1.3
+    url:      http://module-build.sourceforge.net/META-spec-v1.4.html
+    version:  1.4
diff --git a/Makefile.PL b/Makefile.PL
index 99af4a0..3098e05 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -23,6 +23,7 @@ WriteMakefile(
         'bin/regdump.pl',
         'bin/regexport.pl',
         'bin/regfind.pl',
+        'bin/regml.pl',
         'bin/regmultidiff.pl',
         'bin/regscan.pl',
         'bin/regsecurity.pl',
diff --git a/README b/README
index 6a0d55c..02e0198 100644
--- a/README
+++ b/README
@@ -25,7 +25,7 @@ The tests require the Test::More and Data::Dumper modules.
 
 COPYRIGHT AND LICENCE
 
-Copyright (C) 2006,2007,2008,2009,2010 by James Macfarlane
+Copyright (C) 2006-2012 by James Macfarlane
 
 This library is free software; you can redistribute it and/or modify
 it under the same terms as Perl itself.
diff --git a/bin/gtkregcompare.pl b/bin/gtkregcompare.pl
index cef878d..36710e8 100755
--- a/bin/gtkregcompare.pl
+++ b/bin/gtkregcompare.pl
@@ -170,22 +170,21 @@ my $uimanager = Gtk2::UIManager->new;
 my @actions = (
     # name, stock id, label
     ['FileMenu', undef, '_File'],
+    ['EditMenu', undef, '_Edit'],
     ['SearchMenu', undef, '_Search'],
-    ['BookmarksMenu', undef, '_Bookmarks'],
     ['ViewMenu', undef, '_View'],
     ['HelpMenu', undef, '_Help'],
     # name, stock-id, label, accelerator, tooltip, callback
     ['Open', 'gtk-open', '_Select Files...', '<control>O', undef, \&open_files],
     ['Close', 'gtk-close', '_Close Files', '<control>W', undef, \&close_files],
     ['Quit', 'gtk-quit', '_Quit', '<control>Q', undef, \&quit],
+    ['Copy', 'gtk-copy', '_Copy Path', '<control>C', undef, \&copy_path],
     ['Find', 'gtk-find', '_Find...', '<control>F', undef, \&find],
     ['FindNext', undef, 'Find _Next', '<control>G', undef, \&find_next],
     ['FindNext2', undef, 'Find Next', 'F3', undef, \&find_next],
     ['FindChange', 'gtk-find-and-replace', 'Find _Change...', '<control>N', undef, \&find_change],
     ['FindNextChange', undef, 'Find N_ext Change', '<control>M', undef, \&find_next_change],
     ['FindNextChange2', undef, 'Find Next Change', 'F4', undef, \&find_next_change],
-    ['AddBookmark', 'gtk-add', '_Add Bookmark', '<control>D', undef, \&add_bookmark],
-    ['EditBookmarks', undef, '_Edit Bookmarks...', '<control>B', undef, \&edit_bookmarks],
     ['About', 'gtk-about', '_About...', undef, undef, \&about],
 );
 
@@ -194,16 +193,11 @@ $default_actions->add_actions(\@actions, undef);
 
 my @toggle_actions = (
     # name, stock id, label, accelerator, tooltip, callback, active
-    ['ShowDetail', 'gtk-edit', 'Show _Detail', '<control>X', undef, \&toggle_item_detail, TRUE],
+    ['ShowDetail', 'gtk-edit', 'Show _Detail', '<control>D', undef, \&toggle_item_detail, TRUE],
 );
 $default_actions->add_toggle_actions(\@toggle_actions, undef);
 
-my $bookmark_actions = Gtk2::ActionGroup->new('actions2'); # bookmarks
-my $bookmarks_merge_id = $uimanager->new_merge_id;
-my $action_name = 1; # unique action name
-
 $uimanager->insert_action_group($default_actions, 0);
-$uimanager->insert_action_group($bookmark_actions, 1);
 
 my $ui_info = <<END_OF_UI;
 <ui>
@@ -214,6 +208,9 @@ my $ui_info = <<END_OF_UI;
             <separator/>
             <menuitem action='Quit'/>
         </menu>
+        <menu action='EditMenu'>
+            <menuitem action='Copy'/>
+        </menu>
         <menu action='SearchMenu'>
             <menuitem action='Find'/>
             <menuitem action='FindNext'/>
@@ -221,11 +218,6 @@ my $ui_info = <<END_OF_UI;
             <menuitem action='FindChange'/>
             <menuitem action='FindNextChange'/>
         </menu>
-        <menu action='BookmarksMenu'>
-            <menuitem action='AddBookmark'/>
-            <menuitem action='EditBookmarks'/>
-            <separator/>
-        </menu>
         <menu action='ViewMenu'>
             <menuitem action='ShowDetail'/>
             <separator/>
@@ -242,7 +234,6 @@ END_OF_UI
 $uimanager->add_ui_from_string($ui_info);
 
 my $menubar = $uimanager->get_widget('/MenuBar');
-my $bookmarks_menu = $uimanager->get_widget('/MenuBar/BookmarksMenu')->get_submenu;
 
 ### STATUSBAR
 
@@ -364,119 +355,6 @@ sub build_open_files_dialog {
 
 my $open_files_dialog = build_open_files_dialog;
 
-### BOOKMARKS STORE
-
-use constant {
-    BMCOL_NAME => 0,
-    BMCOL_LOCATION => 1,
-    BMCOL_ICON => 2,
-};
-
-my $bookmark_store = Gtk2::ListStore->new(
-    'Glib::String', 'Glib::Scalar', 'Glib::String',
-);
-
-sub build_bookmarks_dialog {
-    my $bookmark_view = Gtk2::TreeView->new($bookmark_store);
-    $bookmark_view->set_reorderable(TRUE);
-
-    my $bookmark_icon_cell = Gtk2::CellRendererPixbuf->new;
-    my $bookmark_name_cell = Gtk2::CellRendererText->new;
-    my $bookmark_column0 = Gtk2::TreeViewColumn->new;
-    $bookmark_column0->set_title('Bookmark');
-    $bookmark_column0->pack_start($bookmark_icon_cell, FALSE);
-    $bookmark_column0->pack_start($bookmark_name_cell, TRUE);
-    $bookmark_column0->set_attributes($bookmark_icon_cell,
-        'stock-id', BMCOL_ICON);
-    $bookmark_column0->set_attributes($bookmark_name_cell,
-        'text', BMCOL_NAME);
-    $bookmark_column0->set_resizable(TRUE);
-    $bookmark_view->append_column($bookmark_column0);
-
-    my $bookmark_location_cell = Gtk2::CellRendererText->new;
-    my $bookmark_column1 = $bookmark_view->insert_column_with_data_func(
-        1, 'Path From Root', $bookmark_location_cell,
-        sub {
-            my ($column, $cell, $model, $iter, $num) = @_;
-            my $location = $model->get($iter, BMCOL_LOCATION);
-            if (defined $location) {
-                my ($subkey_path, $value_name) = @$location;
-                my $string = $subkey_path;
-                if (defined $value_name) {
-                    $value_name = '(Default)' if $value_name eq '';
-                    $string .= ", $value_name";
-                }
-                $cell->set('text', $string);
-            }
-            else {
-                $cell->set('text', '?');
-            }
-        },
-    );
-    $bookmark_location_cell->set('ellipsize', 'end');
-
-    my $scrolled_bookmark_view = Gtk2::ScrolledWindow->new;
-    $scrolled_bookmark_view->set_policy('automatic', 'automatic');
-    $scrolled_bookmark_view->set_shadow_type('in');
-    $scrolled_bookmark_view->add($bookmark_view);
-
-    my $label = Gtk2::Label->new;
-    $label->set_markup('<i>Drag bookmarks to reorder them</i>');
-
-    my $dialog = Gtk2::Dialog->new('Edit Bookmarks', $window, 'modal',
-        'gtk-remove' => 50,
-        'gtk-ok' => 'ok',
-    );
-    $dialog->resize($window_width * 0.8, $window_height * 0.8);
-    $dialog->vbox->pack_start($scrolled_bookmark_view, TRUE, TRUE, 0);
-    $dialog->vbox->pack_start($label, FALSE, FALSE, 5);
-    $dialog->set_default_response('ok');
-
-    $dialog->signal_connect(delete_event => sub {
-        $dialog->hide;
-        return TRUE;
-    });
-    $dialog->signal_connect(response => sub {
-        my ($dialog, $response) = @_;
-        if ($response eq '50') {
-            # Remove selected bookmark
-            my $selection = $bookmark_view->get_selection;
-            my $iter = $selection->get_selected;
-            if (defined $iter) {
-                $bookmark_store->remove($iter);
-            }
-        }
-        else {
-            # Before exiting, move menuitems into current bookmark order
-            $uimanager->remove_ui($bookmarks_merge_id);
-            $uimanager->ensure_update;
-            foreach my $action ($bookmark_actions->list_actions) {
-                $bookmark_actions->remove_action($action);
-            }
-            $action_name = 1;
-            my $iter = $bookmark_store->get_iter_first;
-            while (defined $iter) {
-                my $bookmark_name = $bookmark_store->get($iter, BMCOL_NAME);
-                my $location = $bookmark_store->get($iter, BMCOL_LOCATION);
-                my $icon = $bookmark_store->get($iter, BMCOL_ICON);
-                my $display_name = $bookmark_name;
-                $display_name =~ s/_/__/g;
-                $bookmark_actions->add_actions([
-                    [$action_name, $icon, $display_name, undef, undef, \&go_to_bookmark],
-                ], $location);
-                $uimanager->add_ui($bookmarks_merge_id, '/MenuBar/BookmarksMenu', $action_name, $action_name, 'menuitem', FALSE);
-                $action_name++;
-                $iter = $bookmark_store->iter_next($iter);
-            }
-            $dialog->hide;
-        }
-    });
-
-    return $dialog;
-}
-
-my $bookmarks_dialog = build_bookmarks_dialog;
-
 ######################## GLOBAL SETUP
 
 my @registries = ();
@@ -917,7 +795,7 @@ sub about {
     Gtk2->show_about_dialog(undef,
         'program-name' => $script_name,
         'version' => $Parse::Win32Registry::VERSION,
-        'copyright' => 'Copyright (c) 2008,2009,2010 James Macfarlane',
+        'copyright' => 'Copyright (c) 2008-2012 James Macfarlane',
         'comments' => 'GTK2 Registry Compare for the Parse::Win32Registry module',
     );
 }
@@ -938,6 +816,36 @@ sub show_message {
     $dialog->destroy;
 }
 
+sub get_location {
+    my ($model, $iter) = $tree_selection->get_selected;
+    if (defined $model && defined $iter) {
+        my $keys = $model->get($iter, TREECOL_KEYS);
+        my $values = $model->get($iter, TREECOL_VALUES);
+        return ($keys, $values);
+    }
+    else {
+        return ();
+    }
+}
+
+sub copy_path {
+    my ($keys, $values) = get_location;
+    my $clip = '';
+    if (defined $keys) {
+        my $any_key = (grep { defined } @$keys)[0];
+
+        if (defined $values) { # only values
+            my $any_value = (grep { defined } @$values)[0];
+            $clip = $any_key->get_path . ", " . $any_value->get_name;
+        }
+        else {
+            $clip = $any_key->get_path;
+        }
+    }
+    my $clipboard = Gtk2::Clipboard->get(Gtk2::Gdk->SELECTION_CLIPBOARD);
+    $clipboard->set_text($clip);
+}
+
 sub find_matching_child_iter {
     my ($iter, $name, $icon) = @_;
 
@@ -1037,18 +945,6 @@ sub get_search_message {
     return $message;
 }
 
-sub get_location {
-    my ($model, $iter) = $tree_selection->get_selected;
-    if (defined $model && defined $iter) {
-        my $keys = $model->get($iter, TREECOL_KEYS);
-        my $values = $model->get($iter, TREECOL_VALUES);
-        return ($keys, $values);
-    }
-    else {
-        return ();
-    }
-}
-
 sub find_next {
     if (!defined $find_param || !defined $find_iter) {
         return;
@@ -1171,13 +1067,12 @@ sub find {
     $dialog->show_all;
 
     my $response = $dialog->run;
-    $dialog->destroy;
-
     if ($response eq 'ok' && @root_keys > 0) {
         $search_keys = $check1->get_active;
         $search_values = $check2->get_active;
         $search_selected = $radio2->get_active;
         $find_param = $entry->get_text;
+        $dialog->destroy;
         $find_iter = undef;
         if ($find_param ne '') {
             $find_iter = $search_selected
@@ -1186,6 +1081,9 @@ sub find {
             find_next;
         }
     }
+    else {
+        $dialog->destroy;
+    }
 }
 
 sub find_next_change {
@@ -1309,71 +1207,18 @@ sub find_change {
     $dialog->show_all;
 
     my $response = $dialog->run;
-    $dialog->destroy;
-
     if ($response eq 'ok') {
         $search_keys = $check1->get_active;
         $search_values = $check2->get_active;
         $search_selected = $radio2->get_active;
+        $dialog->destroy;
         $change_iter = $search_selected
                      ? make_multiple_subtree_iterator(@$selected_keys)
                      : make_multiple_subtree_iterator(@root_keys);
         $change_iter->get_next; # skip the starting key
         find_next_change;
     }
-}
-
-sub add_bookmark {
-    my ($keys, $values) = get_location;
-    if (defined $keys) {
-        my $any_key = (grep { defined } @$keys)[0];
-        my $key_path = $any_key->get_path;
-        my $key_name = $any_key->get_name;
-
-        # Remove root key name to get subkey path
-        my $subkey_path = (split(/\\/, $key_path, 2))[1];
-        return if !defined $subkey_path;
-
-        my $bookmark_name;
-        my $location;
-        my $icon;
-        if (defined $values) {
-            my $any_value = (grep { defined } @$values)[0];
-            my $value_name = $any_value->get_name;
-            $location = [$subkey_path, $value_name];
-            $value_name = '(Default)' if $value_name eq '';
-            $bookmark_name = "$value_name";
-            $icon = 'gtk-file';
-        }
-        else {
-            $bookmark_name = $key_name;
-            $location = [$subkey_path];
-            $icon = 'gtk-directory';
-        }
-        $bookmark_name =~ s/\0/[NUL]/g;
-        my $display_name = $bookmark_name;
-        $display_name =~ s/_/__/g;
-        $bookmark_actions->add_actions([
-            [$action_name, $icon, $display_name, undef, undef, \&go_to_bookmark],
-        ], $location);
-        $uimanager->add_ui($bookmarks_merge_id, '/MenuBar/BookmarksMenu', $action_name, $action_name, 'menuitem', FALSE);
-        $action_name++;
-        if (my $iter = $bookmark_store->append) {
-            $bookmark_store->set($iter,
-                BMCOL_NAME, $bookmark_name,
-                BMCOL_LOCATION, $location,
-                BMCOL_ICON, $icon,
-            );
-        }
+    else {
+        $dialog->destroy;
     }
 }
-
-sub edit_bookmarks {
-    $bookmarks_dialog->show_all;
-}
-
-sub go_to_bookmark {
-    my ($menuitem, $location) = @_;
-    my ($subkey_path, $value_name) = @$location;
-    go_to_subkey_and_value($subkey_path, $value_name);
-}
diff --git a/bin/gtkregscope.pl b/bin/gtkregscope.pl
index 67e449a..12caf6d 100755
--- a/bin/gtkregscope.pl
+++ b/bin/gtkregscope.pl
@@ -427,7 +427,7 @@ sub about {
     Gtk2->show_about_dialog(undef,
         'program-name' => $script_name,
         'version' => $Parse::Win32Registry::VERSION,
-        'copyright' => 'Copyright (c) 2009,2010 James Macfarlane',
+        'copyright' => 'Copyright (c) 2009-2012 James Macfarlane',
         'comments' => 'GTK2 Registry Scope for the Parse::Win32Registry module',
     );
 }
@@ -490,34 +490,31 @@ sub find_next {
     $dialog->show_all;
 
     my $id = Glib::Idle->add(sub {
-            my $entry = $find_iter->get_next;
-            if (defined $entry) {
-                my $found = 0;
-
-                if (index($entry->get_raw_bytes, $find_param) > -1) {
+        my $entry = $find_iter->get_next;
+        if (defined $entry) {
+            my $found = 0;
+            if (index(lc $entry->get_raw_bytes, lc $find_param) > -1) {
+                $found = 1;
+            }
+            else {
+                my $uni_find_param = encode("UCS-2LE", $find_param);
+                if (index(lc $entry->get_raw_bytes, lc $uni_find_param) > -1) {
                     $found = 1;
                 }
-                else {
-                    my $uni_find_param = encode("UCS-2LE", $find_param);
-                    if (index($entry->get_raw_bytes, $uni_find_param) > -1) {
-                        $found = 1;
-                    }
-                }
-
-                if ($found) {
-                    go_to_block($entry->get_offset);
-                    go_to_entry($entry->get_offset);
-
-                    $dialog->response(50);
-                    return FALSE;
-                }
+            }
+            if ($found) {
+                go_to_block($entry->get_offset);
+                go_to_entry($entry->get_offset);
 
-                return TRUE; # continue searching...
+                $dialog->response(50);
+                return FALSE;
             }
 
+            return TRUE; # continue searching...
+        }
+
         $dialog->response('ok');
         return FALSE;
-
     });
 
     my $response = $dialog->run;
@@ -552,16 +549,18 @@ sub find {
     $dialog->show_all;
 
     my $response = $dialog->run;
-    $dialog->destroy;
-
     if ($response eq 'ok') {
         $find_param = $entry->get_text;
+        $dialog->destroy;
         $find_iter = undef;
         if ($find_param ne '') {
             $find_iter = $registry->get_entry_iterator;
             find_next;
         }
     }
+    else {
+        $dialog->destroy;
+    }
 }
 
 sub go_to_offset {
@@ -583,6 +582,7 @@ sub go_to_offset {
     $entry->set_position(-1);
 
     my $response = $dialog->run;
+    my $answer = $entry->get_text;
     $dialog->destroy;
 
     if ($response ne 'ok') {
@@ -591,7 +591,6 @@ sub go_to_offset {
 
     my $offset;
     eval {
-        my $answer = $entry->get_text;
         if ($answer =~ m/^\s*0x[\da-fA-F]+\s*$/ || $answer =~ m/^\s*\d+\s*$/) {
             $offset = int(eval $answer);
         }
diff --git a/bin/gtkregview.pl b/bin/gtkregview.pl
index d3ca2f2..8b6cfd3 100755
--- a/bin/gtkregview.pl
+++ b/bin/gtkregview.pl
@@ -197,34 +197,6 @@ my $search_menu = Gtk2::Menu->new;
 $search_menu->append($find_menuitem);
 $search_menu->append($find_next_menuitem);
 
-# Bookmarks Menu
-my $add_bookmark_menuitem = Gtk2::MenuItem->new('_Add Bookmark');
-$add_bookmark_menuitem->signal_connect('activate' => \&add_bookmark);
-$add_bookmark_menuitem->add_accelerator('activate', $accel_group,
-    $Gtk2::Gdk::Keysyms{D}, ['control-mask'], ['visible', 'locked']);
-my $edit_bookmarks_menuitem = Gtk2::MenuItem->new('_Edit Bookmarks...');
-$edit_bookmarks_menuitem->signal_connect('activate' => \&edit_bookmarks);
-$edit_bookmarks_menuitem->add_accelerator('activate', $accel_group,
-    $Gtk2::Gdk::Keysyms{B}, ['control-mask'], ['visible', 'locked']);
-
-my $bookmarks_menu = Gtk2::Menu->new;
-$bookmarks_menu->append($add_bookmark_menuitem);
-$bookmarks_menu->append($edit_bookmarks_menuitem);
-
-my $bookmarks_separator; # placeholder, becomes separator for bookmarks
-
-# Reports Menu
-my $show_report_menuitem = Gtk2::MenuItem->new('Show _Bookmark Report...');
-$show_report_menuitem->signal_connect('activate' => \&view_report);
-$show_report_menuitem->add_accelerator('activate', $accel_group,
-    $Gtk2::Gdk::Keysyms{R}, ['control-mask'], ['visible', 'locked']);
-#my $dump_loaded_keys_menuitem = Gtk2::MenuItem->new('Dump loaded keys');
-#$dump_loaded_keys_menuitem->signal_connect('activate' => \&dump_loaded_keys);
-
-my $view_menu = Gtk2::Menu->new;
-$view_menu->append($show_report_menuitem);
-#$view_menu->append($dump_loaded_keys_menuitem);
-
 # Help Menu
 my $about_menuitem = Gtk2::MenuItem->new('_About...');
 $about_menuitem->signal_connect('activate' => \&about);
@@ -245,14 +217,6 @@ my $search_menuitem = Gtk2::MenuItem->new('_Search');
 $search_menuitem->set_submenu($search_menu);
 $menubar->append($search_menuitem);
 
-my $bookmarks_menuitem = Gtk2::MenuItem->new('_Bookmarks');
-$bookmarks_menuitem->set_submenu($bookmarks_menu);
-$menubar->append($bookmarks_menuitem);
-
-my $view_menuitem = Gtk2::MenuItem->new('_View');
-$view_menuitem->set_submenu($view_menu);
-$menubar->append($view_menuitem);
-
 my $help_menuitem = Gtk2::MenuItem->new('_Help');
 $help_menuitem->set_submenu($help_menu);
 $menubar->append($help_menuitem);
@@ -279,123 +243,6 @@ $window->add_accel_group($accel_group);
 $window->set_title($script_name);
 $window->show_all;
 
-### BOOKMARK STORE
-
-my $bookmark_store = Gtk2::ListStore->new(
-    'Glib::String', 'Glib::String', 'Glib::Scalar',
-);
-# 0 = bookmark name
-# 1 = bookmark location (subkey path)
-# 2 = bookmark menuitem
-
-sub build_bookmarks_dialog {
-    my $bookmark_view = Gtk2::TreeView->new($bookmark_store);
-    $bookmark_view->set_reorderable(TRUE);
-
-    my $bookmark_column0 = Gtk2::TreeViewColumn->new_with_attributes(
-        'Bookmark', Gtk2::CellRendererText->new, 'text', 0);
-    $bookmark_column0->set_resizable(TRUE);
-    $bookmark_view->append_column($bookmark_column0);
-
-    my $bookmark_location_cell = Gtk2::CellRendererText->new;
-    my $bookmark_column1 = Gtk2::TreeViewColumn->new_with_attributes(
-        'Path From Root', $bookmark_location_cell, 'text', 1);
-    $bookmark_location_cell->set('ellipsize', 'end');
-    $bookmark_column1->set_resizable(FALSE);
-    $bookmark_view->append_column($bookmark_column1);
-
-    my $scrolled_bookmark_view = Gtk2::ScrolledWindow->new;
-    $scrolled_bookmark_view->set_policy('automatic', 'automatic');
-    $scrolled_bookmark_view->set_shadow_type('in');
-    $scrolled_bookmark_view->add($bookmark_view);
-
-    my $label = Gtk2::Label->new;
-    $label->set_markup('<i>Drag bookmarks to reorder them</i>');
-
-    my $dialog = Gtk2::Dialog->new('Edit Bookmarks', $window, 'modal',
-        'gtk-remove' => 50,
-        'gtk-ok' => 'ok',
-    );
-    $dialog->resize($window_width * 0.8, $window_height * 0.8);
-    $dialog->vbox->pack_start($scrolled_bookmark_view, TRUE, TRUE, 0);
-    $dialog->vbox->pack_start($label, FALSE, FALSE, 5);
-    $dialog->set_default_response('ok');
-
-    $dialog->signal_connect(delete_event => sub {
-        $dialog->hide;
-        return TRUE;
-    });
-    $dialog->signal_connect(response => sub {
-        my ($dialog, $response) = @_;
-        if ($response eq '50') {
-            # Remove selected bookmark
-            my $selection = $bookmark_view->get_selection;
-            my $iter = $selection->get_selected;
-            if (defined $iter) {
-                my $menuitem = $bookmark_store->get($iter, 2);
-                $menuitem->destroy;
-                $bookmark_store->remove($iter);
-            }
-        }
-        else {
-            # Before exiting, move menuitems into current bookmark order
-            my $iter = $bookmark_store->get_iter_first;
-            while (defined $iter) {
-                my $menuitem = $bookmark_store->get($iter, 2);
-                $bookmarks_menu->remove($menuitem);
-                $bookmarks_menu->append($menuitem);
-                $iter = $bookmark_store->iter_next($iter);
-            }
-            $dialog->hide;
-        }
-    });
-
-    return $dialog;
-}
-
-my $bookmarks_dialog = build_bookmarks_dialog;
-
-my $report_view;
-
-sub build_report_dialog {
-    $report_view = Gtk2::TextView->new;
-    $report_view->set_editable(FALSE);
-    $report_view->modify_font(Gtk2::Pango::FontDescription->from_string('monospace'));
-
-    my $text_buffer = $report_view->get_buffer;
-
-    my $scrolled_report_view = Gtk2::ScrolledWindow->new;
-    $scrolled_report_view->set_policy('automatic', 'automatic');
-    $scrolled_report_view->set_shadow_type('in');
-    $scrolled_report_view->add($report_view);
-
-    my $dialog = Gtk2::Dialog->new('Report', $window, 'modal',
-        'gtk-save' => 50,
-        'gtk-cancel' => 'cancel',
-    );
-    $dialog->resize($window_width * 0.8, $window_height * 0.8);
-    $dialog->vbox->add($scrolled_report_view);
-    $dialog->set_default_response('ok');
-
-    $dialog->signal_connect(delete_event => sub {
-        $dialog->hide;
-        return TRUE;
-    });
-    $dialog->signal_connect(response => sub {
-        my ($dialog, $response) = @_;
-        if ($response eq '50') {
-            save_report();
-        }
-        else {
-            $dialog->hide;
-        }
-    });
-
-    return $dialog;
-}
-
-my $report_dialog = build_report_dialog;
-
 ### GLOBALS
 
 my $search_keys = TRUE;
@@ -644,22 +491,6 @@ sub open_file {
     }
 }
 
-sub save_report {
-    if (my $filename = choose_file('Save Log File As', 'save', "report.txt")) {
-        my $basename = basename $filename;
-        if (open my $fh, ">", $filename) {
-            my $text_buffer = $report_view->get_buffer;
-            my $start_iter = $text_buffer->get_start_iter;
-            my $end_iter = $text_buffer->get_end_iter;
-            print {$fh} $text_buffer->get_text($start_iter, $end_iter, 0);
-#            show_message("info", "Report saved to '$basename'");
-        }
-        else {
-            show_message("error", "Error saving log to '$basename'");
-        }
-    }
-}
-
 sub close_file {
     $tree_store->clear;
     $list_store->clear;
@@ -677,7 +508,7 @@ sub about {
     Gtk2->show_about_dialog(undef,
         'program-name' => $script_name,
         'version' => $Parse::Win32Registry::VERSION,
-        'copyright' => 'Copyright (c) 2008,2009,2010 James Macfarlane',
+        'copyright' => 'Copyright (c) 2008-2012 James Macfarlane',
         'comments' => 'GTK2 Registry Viewer for the Parse::Win32Registry module',
     );
 }
@@ -698,76 +529,15 @@ sub show_message {
     $dialog->destroy;
 }
 
-sub create_bookmark_menuitem {
-    my ($name, $subkey_path) = @_;
-
-    my $display_name = $name;
-    $display_name =~ s/_/__/g;
-    if (my $menuitem = Gtk2::MenuItem->new($display_name)) {
-        $bookmarks_menu->append($menuitem);
-        $bookmarks_menu->show_all;
-        if (my $iter = $bookmark_store->append) {
-            $bookmark_store->set($iter,
-                0, $name,
-                1, $subkey_path,
-                2, $menuitem,
-            );
-        }
-        $menuitem->signal_connect('activate' => \&go_to_bookmark,
-                                                 $subkey_path);
-    }
-}
-
-sub add_bookmark {
-    my $iter = $tree_selection->get_selected;
-    return if !defined $iter;
-
-    # Add separator for bookmarks if it is not already there
-    if (!defined $bookmarks_separator) {
-        $bookmarks_separator = Gtk2::SeparatorMenuItem->new;
-        $bookmarks_menu->append($bookmarks_separator);
-    }
-
-    my $key = $tree_store->get($iter, 3);
-
-    # Remove root key name to get subkey path
-    my $subkey_path = (split(/\\/, $key->get_path, 2))[1];
-
-    if (defined $subkey_path) {
-        my $name = $key->get_name;
-        create_bookmark_menuitem($name, $subkey_path);
-    }
-}
-
-sub edit_bookmarks {
-    $bookmarks_dialog->show_all;
-}
-
-sub remove_all_bookmarks {
-    my $iter = $bookmark_store->get_iter_first;
-    # destroy all the bookmark menu items
-    while (defined $iter) {
-        my $menuitem = $bookmark_store->get($iter, 2);
-        $bookmarks_menu->remove($menuitem);
-        $menuitem->destroy;
-        $iter = $bookmark_store->iter_next($iter);
-    }
-    # then empty the bookmark store
-    $bookmark_store->clear;
-}
-
-sub go_to_bookmark {
-    my ($menuitem, $path) = @_;
-    go_to_subkey($path);
-}
-
 sub copy_key_path {
     my $tree_iter = $tree_selection->get_selected;
+    my $clip = '';
     if (defined $tree_iter) {
-       my $key = $tree_store->get($tree_iter, 3);
-       my $clipboard = Gtk2::Clipboard->get(Gtk2::Gdk->SELECTION_CLIPBOARD);
-       $clipboard->set_text($key->get_path);
+        my $key = $tree_store->get($tree_iter, 3);
+        $clip = $key->get_path;
     }
+    my $clipboard = Gtk2::Clipboard->get(Gtk2::Gdk->SELECTION_CLIPBOARD);
+    $clipboard->set_text($clip);
 }
 
 sub go_to_value {
@@ -1002,13 +772,12 @@ sub find {
     $dialog->show_all;
 
     my $response = $dialog->run;
-    $dialog->destroy;
-
     if ($response eq 'ok') {
         $search_keys = $check1->get_active;
         $search_values = $check2->get_active;
         $search_selected = $radio2->get_active;
         $find_param = $entry->get_text;
+        $dialog->destroy;
         $find_iter = undef;
         if ($find_param ne '') {
             $find_iter = $search_selected
@@ -1017,55 +786,7 @@ sub find {
             find_next;
         }
     }
-}
-
-sub dump_loaded_keys {
-    print "Dumping loaded keys:\n";
-    $tree_store->foreach(sub {
-        my ($model, $path, $iter) = @_;
-
-        my $key = $model->get($iter, 3);
-        if (defined $key) {
-            print $key->get_path, "\n";
-        }
-        return FALSE;
-    });
-}
-
-sub view_report {
-    my $root_iter = $tree_store->get_iter_first;
-    if (!defined $root_iter) {
-        print "(no registry file loaded)\n";
-        return;
-    }
-
-    my $text_buffer = $report_view->get_buffer;
-    $text_buffer->set_text('');
-
-    my $root_key = $tree_store->get($root_iter, 3);
-    my $iter = $bookmark_store->get_iter_first;
-    while (defined $iter) {
-        my $name = $bookmark_store->get($iter, 0);
-        my $path = $bookmark_store->get($iter, 1);
-
-        if (my $key = $root_key->get_subkey($path)) {
-            my $str = $key->as_string . "\n";
-            $str =~ s/\0/[NUL]/g;
-            $text_buffer->insert_at_cursor($str);
-            foreach my $value ($key->get_list_of_values) {
-                my $value_name = $value->get_name;
-                $value_name = "(Default)" if $value_name eq "";
-                $value_name =~ s/\0/[NUL]/g;
-                my $value_type = $value->get_type_as_string;
-                my $str = "$value_name ($value_type):\n";
-                $str .= hexdump($value->get_raw_data);
-                $text_buffer->insert_at_cursor($str);
-            }
-            $text_buffer->insert_at_cursor("\n");
-        }
-        $iter = $bookmark_store->iter_next($iter);
+    else {
+        $dialog->destroy;
     }
-
-    $report_dialog->show_all;
 }
-
diff --git a/bin/regdump.pl b/bin/regdump.pl
index d686dcd..4f364e9 100755
--- a/bin/regdump.pl
+++ b/bin/regdump.pl
@@ -116,6 +116,6 @@ $script_name <filename> [subkey] [-r] [-v] [-x] [-c] [-s] [-o]
     -s or --security    display the security information for the key,
                         including the owner and group SIDs,
                         and the system and discretionary ACLs (if present)
-    -o or --owner       display only the owner SID for the key (if present)
+    -o or --owner       display the owner SID for the key (if present)
 USAGE
 }
diff --git a/bin/regml.pl b/bin/regml.pl
new file mode 100755
index 0000000..2171658
--- /dev/null
+++ b/bin/regml.pl
@@ -0,0 +1,55 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+use File::Basename;
+use Parse::Win32Registry 0.60;
+
+binmode(STDOUT, ':utf8');
+
+my $filename = shift or die usage();
+
+my $registry = Parse::Win32Registry->new($filename)
+    or die "'$filename' is not a registry file\n";
+my $root_key = $registry->get_root_key
+    or die "Could not get root key of '$filename'\n";
+
+my $security = $root_key->get_security
+    or die "Root key of '$filename' does not have any security information\n";
+
+traverse($root_key);
+
+sub traverse {
+    my $key = shift;
+
+    my $security = $key->get_security;
+    if (defined $security) {
+        my $sd = $security->get_security_descriptor;
+        my $sacl = $sd->get_sacl;
+        if (defined $sacl) {
+            foreach my $ace ($sacl->get_list_of_aces) {
+                if ($ace->get_type == 0x11) {
+                    print $key->as_string, "\n";
+                    print "ACE: ", $ace->as_string, "\n\n";
+                }
+            }
+        }
+    }
+
+    foreach my $subkey ($key->get_list_of_subkeys) {
+        traverse($subkey);
+    }
+}
+
+sub usage {
+    my $script_name = basename $0;
+    return <<USAGE;
+$script_name for Parse::Win32Registry $Parse::Win32Registry::VERSION
+
+Displays those keys in a registry file that have a System ACL
+that includes a System Mandatory Label ACE.
+Only Windows NT registry files contain security information.
+
+$script_name <filename>
+USAGE
+}
diff --git a/bin/regscan.pl b/bin/regscan.pl
index 69b00bc..7ddb4d7 100755
--- a/bin/regscan.pl
+++ b/bin/regscan.pl
@@ -15,15 +15,7 @@ GetOptions('parse-info|p' => \my $show_parse_info,
            'allocated|a'  => \my $list_allocated,
            'keys|k'       => \my $list_keys,
            'values|v'     => \my $list_values,
-           'security|s'   => \my $list_security,
-           'warnings|w'   => \my $show_warnings);
-
-if ($show_warnings) {
-    Parse::Win32Registry->enable_warnings;
-}
-else {
-    Parse::Win32Registry->disable_warnings;
-}
+           'security|s'   => \my $list_security);
 
 my $filename = shift or die usage();
 
@@ -60,7 +52,7 @@ associated with but are not actually keys or values. Additionally,
 some of the keys, values, and associated elements displayed
 will no longer be active and may be invalid or deleted.
 
-$script_name <filename> [-k] [-v] [-s] [-a] [-p] [-u] [-w]
+$script_name <filename> [-k] [-v] [-s] [-a] [-p] [-u]
     -k or --keys        list only 'key' entries
     -v or --values      list only 'value' entries
     -s or --security    list only 'security' entries
@@ -68,6 +60,5 @@ $script_name <filename> [-k] [-v] [-s] [-a] [-p] [-u] [-w]
     -p or --parse-info  show the technical information for an entry
                         instead of the string representation
     -u or --unparsed    show the unparsed on-disk entries as a hex dump
-    -w or --warnings    display warnings of invalid keys and values
 USAGE
 }
diff --git a/bin/regsecurity.pl b/bin/regsecurity.pl
index b879feb..23e3dfa 100755
--- a/bin/regsecurity.pl
+++ b/bin/regsecurity.pl
@@ -3,15 +3,10 @@ use strict;
 use warnings;
 
 use File::Basename;
-use Getopt::Long;
 use Parse::Win32Registry 0.50;
 
 binmode(STDOUT, ':utf8');
 
-Getopt::Long::Configure('bundling');
-
-GetOptions('unparsed|u' => \my $show_unparsed);
-
 my $filename = shift or die usage();
 
 my $registry = Parse::Win32Registry->new($filename)
@@ -26,7 +21,7 @@ my $security = $root_key->get_security
 my %offsets_seen = ();
 my $offset = $security->get_offset;
 while (!exists $offsets_seen{$offset}) {
-    $offsets_seen{$offset} = $security;
+    $offsets_seen{$offset} = undef; # value not required
 
     printf "Security at offset 0x%x, %d references\n",
         $offset, $security->get_reference_count;
diff --git a/bin/wxregcompare.pl b/bin/wxregcompare.pl
index c4b464f..a796124 100755
--- a/bin/wxregcompare.pl
+++ b/bin/wxregcompare.pl
@@ -131,43 +131,6 @@ sub OnTreeItemExpanding {
     }
 }
 
-sub DumpLoadedEntries {
-    my ($self) = @_;
-
-    my $root_item = $self->GetRootItem;
-    my @items;
-    if ($root_item->IsOk) {
-        push @items, $root_item;
-    }
-    while (@items) {
-        my $item = shift @items;
-
-        my ($changes, $keys, $values) = @{$self->GetPlData($item)};
-
-        my $num_changes = grep { $_ } @$changes;
-        printf "%2d", $num_changes;
-
-        my $any_key = (grep { defined } @$keys)[0];
-        print " ", $any_key->get_path;
-
-        if (defined $values) {
-            my $any_value = (grep { defined } @$values)[0];
-            my $name = $any_value->get_name;
-            $name = "(Default)" if $name eq '';
-            print " ", $name;
-        }
-        print "\n";
-
-        if ($self->ItemHasChildren($item)) {
-            my ($child_item, $cookie) = $self->GetFirstChild($item);
-            while ($child_item->IsOk) {
-                push @items, $child_item;
-                ($child_item, $cookie) = $self->GetNextChild($item, $cookie);
-            }
-        }
-    }
-}
-
 sub FindMatchingKey {
     my ($self, $item, $key_name) = @_;
 
@@ -456,8 +419,7 @@ sub new {
     $menu1->Append(wxID_EXIT, "E&xit\tAlt+F4");
 
     my $menu2 = Wx::Menu->new;
-    $menu2->Append(wxID_COPY, "&Copy Key Path\tCtrl+C");
-#    $menu2->Append(ID_DUMP_ENTRIES, "Dump Loaded Entries");
+    $menu2->Append(wxID_COPY, "&Copy Path\tCtrl+C");
 
     my $menu3 = Wx::Menu->new;
     $menu3->Append(wxID_FIND, "&Find...\tCtrl+F");
@@ -488,7 +450,6 @@ sub new {
     EVT_MENU($self, wxID_CLOSE, \&OnCloseFiles);
     EVT_MENU($self, wxID_EXIT, \&OnQuit);
     EVT_MENU($self, wxID_COPY, \&OnCopy);
-    EVT_MENU($self, ID_DUMP_ENTRIES, sub { $_[0]->{_tree}->DumpLoadedEntries; });
     EVT_MENU($self, wxID_FIND, \&OnFind);
     EVT_MENU($self, ID_FIND_NEXT, \&FindNext);
     EVT_MENU($self, wxID_REPLACE, \&OnFindChange);
@@ -507,7 +468,7 @@ sub new {
 
     my $list = EntryListCtrl->new($vsplitter);
 
-    my $text = Wx::TextCtrl->new($vsplitter, -1, '', wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_DONTWRAP);
+    my $text = Wx::TextCtrl->new($vsplitter, -1, '', wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_DONTWRAP|wxTE_READONLY);
     $text->SetFont(Wx::Font->new(10, wxMODERN, wxNORMAL, wxNORMAL));
 
     $vsplitter->SplitHorizontally($list, $text);
@@ -524,13 +485,6 @@ sub new {
     EVT_TREE_SEL_CHANGED($self, $tree, \&OnEntryTreeSelChanged);
     EVT_LIST_ITEM_SELECTED($self, $list, \&OnEntryListItemSelected);
 
-    if (@ARGV) {
-        $self->LoadFiles(@ARGV);
-    }
-    else {
-        $self->{_registries} = [];
-    }
-
     $self->SetIcon(Wx::GetWxPerlIcon());
 
     my $accelerators = Wx::AcceleratorTable->new(
@@ -538,6 +492,13 @@ sub new {
     );
     $self->SetAcceleratorTable($accelerators);
 
+    if (@ARGV) {
+        $self->LoadFiles(@ARGV);
+    }
+    else {
+        $self->{_registries} = [];
+    }
+
     return $self;
 }
 
@@ -560,7 +521,7 @@ sub OnCopy {
     if (defined $keys) {
         my $any_key = (grep { defined } @$keys)[0];
 
-        if (defined $values) { # only values
+        if (defined $values) {
             my $any_value = (grep { defined } @$values)[0];
             $clip = $any_key->get_path . ", " . $any_value->get_name;
         }
@@ -655,7 +616,7 @@ sub OnAbout {
     my $info = Wx::AboutDialogInfo->new;
     $info->SetName($FindBin::Script);
     $info->SetVersion($Parse::Win32Registry::VERSION);
-    $info->SetCopyright("Copyright (c) 2010 James Macfarlane");
+    $info->SetCopyright("Copyright (c) 2010-2012 James Macfarlane");
     $info->SetDescription("wxWidgets Registry Compare for the Parse::Win32Registry module");
     Wx::AboutBox($info);
 }
@@ -1085,25 +1046,26 @@ sub GetSearchSelected {
 sub SetSearchKeys {
     my ($self, $state) = @_;
     $state = 1 if !defined $state;
-    return $self->{_check1}->SetValue($state);
+    $self->{_check1}->SetValue($state);
 }
 
 sub SetSearchValues {
     my ($self, $state) = @_;
     $state = 1 if !defined $state;
-    return $self->{_check2}->SetValue($state);
+    $self->{_check2}->SetValue($state);
 }
 
 sub SetText {
     my ($self, $value) = @_;
     $value = '' if !defined $value;
-    return $self->{_text}->ChangeValue($value);
+    $self->{_text}->ChangeValue($value);
+    $self->{_text}->SetSelection(-1, -1);
 }
 
 sub SetSearchSelected {
     my ($self, $n) = @_;
     $n = 0 if !defined $n;
-    return $self->{_radio}->SetSelection($n);
+    $self->{_radio}->SetSelection($n);
 }
 
 
@@ -1180,19 +1142,19 @@ sub GetSearchSelected {
 sub SetSearchKeys {
     my ($self, $state) = @_;
     $state = 1 if !defined $state;
-    return $self->{_check1}->SetValue($state);
+    $self->{_check1}->SetValue($state);
 }
 
 sub SetSearchValues {
     my ($self, $state) = @_;
     $state = 1 if !defined $state;
-    return $self->{_check2}->SetValue($state);
+    $self->{_check2}->SetValue($state);
 }
 
 sub SetSearchSelected {
     my ($self, $n) = @_;
     $n = 0 if !defined $n;
-    return $self->{_radio}->SetSelection($n);
+    $self->{_radio}->SetSelection($n);
 }
 
 
diff --git a/bin/wxregscope.pl b/bin/wxregscope.pl
index 8a9d8be..655e48b 100755
--- a/bin/wxregscope.pl
+++ b/bin/wxregscope.pl
@@ -163,7 +163,7 @@ sub OnGetItemText {
     elsif ($column == 4) {
         my $name = '';
         if ($entry->can('get_name')) {
-             $name = $entry->get_name; # FIXME nulls?
+             $name = $entry->get_name;
              $name =~ s/\0/[NUL]/g;
              $name =~ s/\n/[LF]/g;
              $name =~ s/\r/[CR]/g;
@@ -280,12 +280,14 @@ sub SetText {
     my ($self, $value) = @_;
 
     $value = '' if !defined $value;
-    return $self->{_text}->ChangeValue($value);
+    $self->{_text}->ChangeValue($value);
+    $self->{_text}->SetSelection(-1, -1);
 }
 
 
 package ScopeFrame;
 
+use Encode;
 use File::Basename;
 use FindBin;
 use Parse::Win32Registry;
@@ -316,7 +318,7 @@ sub new {
     $menu2->Append(wxID_FIND, "&Find...\tCtrl+F");
     $menu2->Append(ID_FIND_NEXT, "Find &Next\tF3");
     $menu2->AppendSeparator;
-    $menu2->Append(ID_GO_TO, "&Go To Offset...\tCtrl+I");
+    $menu2->Append(ID_GO_TO, "&Go To Offset...\tCtrl+G");
 
     my $menu3 = Wx::Menu->new;
     $menu3->Append(ID_SELECT_FONT, "Select &Font...");
@@ -346,7 +348,7 @@ sub new {
 
     my $vsplitter = Wx::SplitterWindow->new($self, -1, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER);
 
-    my $text = Wx::TextCtrl->new($vsplitter, -1, '', wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_DONTWRAP);
+    my $text = Wx::TextCtrl->new($vsplitter, -1, '', wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_DONTWRAP|wxTE_READONLY);
     $text->SetFont(Wx::Font->new(10, wxMODERN, wxNORMAL, wxNORMAL));
 
     my $hsplitter = Wx::SplitterWindow->new($vsplitter, -1, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER);
@@ -373,11 +375,6 @@ sub new {
     EVT_LIST_ITEM_SELECTED($self, $list1, \&OnBlockSelected);
     EVT_LIST_ITEM_SELECTED($self, $list2, \&OnEntrySelected);
 
-    my $filename = shift @ARGV;
-    if (defined $filename) {
-        $self->LoadFile($filename);
-    }
-
     $self->SetIcon(Wx::GetWxPerlIcon());
 
     my $accelerators = Wx::AcceleratorTable->new(
@@ -385,6 +382,11 @@ sub new {
     );
     $self->SetAcceleratorTable($accelerators);
 
+    my $filename = shift @ARGV;
+    if (defined $filename) {
+        $self->LoadFile($filename);
+    }
+
     return $self;
 }
 
@@ -439,20 +441,19 @@ sub OnBlockSelected {
 
     my $index = $event->GetIndex;
     my $block = $self->{_list1}->GetBlock($index);
-#    if (defined $block) {
-        $self->{_list2}->SetBlock($block);
 
-        my $parse_info = $block->parse_info; # FIXME nulls - not relevant
-        $parse_info =~ s/\0/[NUL]/g;
-        $parse_info =~ s/\n/[LF]/g;
-        $parse_info =~ s/\r/[CR]/g;
-        my $details = $parse_info . "\n" . $block->unparsed;
+    $self->{_list2}->SetBlock($block);
+
+    my $parse_info = $block->parse_info;
+    $parse_info =~ s/\0/[NUL]/g;
+    $parse_info =~ s/\n/[LF]/g;
+    $parse_info =~ s/\r/[CR]/g;
+    my $details = $parse_info . "\n" . $block->unparsed;
 
-        $self->{_text}->ChangeValue($details);
+    $self->{_text}->ChangeValue($details);
 
-        my $status = sprintf "Block Offset: 0x%x", $block->get_offset;
-        $self->{_statusbar}->SetStatusText($status);
-#    }
+    my $status = sprintf "Block Offset: 0x%x", $block->get_offset;
+    $self->{_statusbar}->SetStatusText($status);
 }
 
 sub OnEntrySelected {
@@ -461,18 +462,16 @@ sub OnEntrySelected {
     my $index = $event->GetIndex;
     my $entry = $self->{_list2}->GetEntry($index);
 
-#    if (defined $entry) {
-        my $parse_info = $entry->parse_info; # FIXME nulls
-        $parse_info =~ s/\0/[NUL]/g;
-        $parse_info =~ s/\n/[LF]/g;
-        $parse_info =~ s/\r/[CR]/g;
-        my $details = $parse_info . "\n" . $entry->unparsed;
+    my $parse_info = $entry->parse_info;
+    $parse_info =~ s/\0/[NUL]/g;
+    $parse_info =~ s/\n/[LF]/g;
+    $parse_info =~ s/\r/[CR]/g;
+    my $details = $parse_info . "\n" . $entry->unparsed;
 
-        $self->{_text}->ChangeValue($details);
+    $self->{_text}->ChangeValue($details);
 
-        my $status = sprintf "Entry Offset: 0x%x", $entry->get_offset;
-        $self->{_statusbar}->SetStatusText($status);
-#    }
+    my $status = sprintf "Entry Offset: 0x%x", $entry->get_offset;
+    $self->{_statusbar}->SetStatusText($status);
 }
 
 sub OnAbout {
@@ -481,7 +480,7 @@ sub OnAbout {
     my $info = Wx::AboutDialogInfo->new;
     $info->SetName($FindBin::Script);
     $info->SetVersion($Parse::Win32Registry::VERSION);
-    $info->SetCopyright('Copyright (c) 2010 James Macfarlane');
+    $info->SetCopyright('Copyright (c) 2010-2012 James Macfarlane');
     $info->SetDescription('wxWidgets Registry Scope for the Parse::Win32Registry module');
     Wx::AboutBox($info);
 }
@@ -520,15 +519,22 @@ sub FindNext {
     my $iter_finished = 1;
 
     while (my $entry = $find_iter->get_next) {
-        if ($entry->can('get_name')) {
-            my $name = $entry->get_name;
-            if (index(lc $name, lc $find_param) >= 0) {
-                $self->{_list1}->GoToBlock($entry->get_offset);
-                $self->{_list2}->GoToEntry($entry->get_offset);
-                $iter_finished = 0;
-                last;
+        my $found = 0;
+        if (index(lc $entry->get_raw_bytes, lc $find_param) > -1) {
+            $found = 1;
+        }
+        else {
+            my $uni_find_param = encode("UCS-2LE", $find_param);
+            if (index(lc $entry->get_raw_bytes, lc $uni_find_param) > -1) {
+                $found = 1;
             }
         }
+        if ($found) {
+            $self->{_list1}->GoToBlock($entry->get_offset);
+            $self->{_list2}->GoToEntry($entry->get_offset);
+            $iter_finished = 0;
+            last;
+        }
 
         if (defined $progress_dialog) {
             if (!$progress_dialog->Update) {
diff --git a/bin/wxregview.pl b/bin/wxregview.pl
index 72144ef..2460cad 100755
--- a/bin/wxregview.pl
+++ b/bin/wxregview.pl
@@ -84,30 +84,6 @@ sub OnTreeItemExpanding {
     }
 }
 
-sub DumpLoadedKeys {
-    my ($self) = @_;
-
-    my $root_item = $self->GetRootItem;
-    my @items;
-    if ($root_item->IsOk) {
-        push @items, $root_item;
-    }
-    while (@items) {
-        my $item = shift @items;
-
-        my $key = $self->GetPlData($item);
-        print $key->get_path, "\n";
-
-        if ($self->ItemHasChildren($item)) {
-            my ($child_item, $cookie) = $self->GetFirstChild($item);
-            while ($child_item->IsOk) {
-                push @items, $child_item;
-                ($child_item, $cookie) = $self->GetNextChild($item, $cookie);
-            }
-        }
-    }
-}
-
 sub FindMatchingItem {
     my ($self, $key_name, $item) = @_;
 
@@ -305,7 +281,6 @@ sub new {
 
     my $menu2 = Wx::Menu->new;
     $menu2->Append(wxID_COPY, "&Copy Key Path\tCtrl+C");
-#    $menu2->Append(ID_DUMP_KEYS, "Dump Loaded Keys");
 
     my $menu3 = Wx::Menu->new;
     $menu3->Append(wxID_FIND, "&Find...\tCtrl+F");
@@ -335,7 +310,6 @@ sub new {
     EVT_MENU($self, wxID_CLOSE, \&OnCloseFile);
     EVT_MENU($self, wxID_EXIT, \&OnQuit);
     EVT_MENU($self, wxID_COPY, \&OnCopy);
-    EVT_MENU($self, ID_DUMP_KEYS, sub { $_[0]->{_tree}->DumpLoadedKeys; });
     EVT_MENU($self, wxID_FIND, \&OnFind);
     EVT_MENU($self, ID_FIND_NEXT, \&FindNext);
     EVT_MENU($self, ID_TIMELINE, \&ShowTimeline);
@@ -353,7 +327,7 @@ sub new {
 
     my $list = ValueListCtrl->new($vsplitter);
 
-    my $text = Wx::TextCtrl->new($vsplitter, -1, '', wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_DONTWRAP);
+    my $text = Wx::TextCtrl->new($vsplitter, -1, '', wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_DONTWRAP|wxTE_READONLY);
     # Set a monospaced font
     $text->SetFont(Wx::Font->new(10, wxMODERN, wxNORMAL, wxNORMAL));
 
@@ -371,11 +345,6 @@ sub new {
     EVT_TREE_SEL_CHANGED($self, $tree, \&OnKeyTreeSelChanged);
     EVT_LIST_ITEM_SELECTED($self, $list, \&OnValueListItemSelected);
 
-    my $filename = shift @ARGV;
-    if (defined $filename) {
-        $self->LoadFile($filename);
-    }
-
     $self->SetIcon(Wx::GetWxPerlIcon());
 
     my $accelerators = Wx::AcceleratorTable->new(
@@ -383,6 +352,11 @@ sub new {
     );
     $self->SetAcceleratorTable($accelerators);
 
+    my $filename = shift @ARGV;
+    if (defined $filename) {
+        $self->LoadFile($filename);
+    }
+
     return $self;
 }
 
@@ -437,6 +411,14 @@ sub ShowTimeline {
         $dialog->SetTimeline($self->{_keys_by_time});
     }
 
+    if (scalar keys %{$self->{_keys_by_time}} == 0) {
+        my $dialog = Wx::MessageDialog->new($self,
+            'No keys have timestamps!', 'Timeline', wxICON_ERROR|wxOK);
+        $dialog->ShowModal;
+        $dialog->Destroy;
+        return;
+    }
+
     $dialog->Show;
     $dialog->Raise;
     $dialog->{_list1}->SetFocus;
@@ -456,7 +438,9 @@ sub BuildTimeline {
     my %keys_by_time = ();
 
     my $max = 0;
-    my $progress_dialog = Wx::ProgressDialog->new('Building Timeline', 'Ordering registry keys...', $max, $self, wxPD_CAN_ABORT|wxPD_AUTO_HIDE);
+    my $progress_dialog = Wx::ProgressDialog->new('Building Timeline',
+        'Ordering registry keys...', $max, $self, 
+        wxPD_CAN_ABORT|wxPD_AUTO_HIDE);
     $progress_dialog->Update;
 
     while (my $key = $subtree_iter->get_next) {
@@ -541,7 +525,7 @@ sub OnAbout {
     my $info = Wx::AboutDialogInfo->new;
     $info->SetName($FindBin::Script);
     $info->SetVersion($Parse::Win32Registry::VERSION);
-    $info->SetCopyright('Copyright (c) 2010 James Macfarlane');
+    $info->SetCopyright('Copyright (c) 2010-2012 James Macfarlane');
     $info->SetDescription('wxWidgets Registry Viewer for the Parse::Win32Registry module');
     Wx::AboutBox($info);
 }
@@ -762,8 +746,6 @@ sub new {
     $sizer->Add($check2, 0, wxALL, 5);
     $sizer->Add($radio, 0, wxALL, 5);
 
-    my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
-
     my $button_sizer = $self->CreateSeparatedButtonSizer(wxOK|wxCANCEL);
 
     $sizer->Add($button_sizer, 0, wxEXPAND|wxALL, 5);
@@ -817,25 +799,26 @@ sub GetSearchSelected {
 sub SetSearchKeys {
     my ($self, $state) = @_;
     $state = 1 if !defined $state;
-    return $self->{_check1}->SetValue($state);
+    $self->{_check1}->SetValue($state);
 }
 
 sub SetSearchValues {
     my ($self, $state) = @_;
     $state = 1 if !defined $state;
-    return $self->{_check2}->SetValue($state);
+    $self->{_check2}->SetValue($state);
 }
 
 sub SetText {
     my ($self, $value) = @_;
     $value = '' if !defined $value;
-    return $self->{_text}->ChangeValue($value);
+    $self->{_text}->ChangeValue($value);
+    $self->{_text}->SetSelection(-1, -1);
 }
 
 sub SetSearchSelected {
     my ($self, $n) = @_;
     $n = 0 if !defined $n;
-    return $self->{_radio}->SetSelection($n);
+    $self->{_radio}->SetSelection($n);
 }
 
 
diff --git a/lib/Parse/Win32Registry.pm b/lib/Parse/Win32Registry.pm
index 2279955..1aa4556 100644
--- a/lib/Parse/Win32Registry.pm
+++ b/lib/Parse/Win32Registry.pm
@@ -4,7 +4,7 @@ use 5.008_001;
 use strict;
 use warnings;
 
-our $VERSION = '0.60';
+our $VERSION = '1.0';
 
 use base qw(Exporter);
 
@@ -39,14 +39,6 @@ sub disable_warnings {
     $Parse::Win32Registry::Base::WARNINGS = 0;
 }
 
-sub enable_trace {
-    $Parse::Win32Registry::Base::TRACE = 1;
-}
-
-sub disable_trace {
-    $Parse::Win32Registry::Base::TRACE = 0;
-}
-
 sub set_codepage {
     my $codepage = shift;
     if (defined $codepage) {
@@ -1585,7 +1577,7 @@ Type regdump.pl on its own to see the help:
         -s or --security    display the security information for the key,
                             including the owner and group SIDs,
                             and the system and discretionary ACLs (if present)
-        -o or --owner       display only the owner SID for the key (if present)
+        -o or --owner       display the owner SID for the key (if present)
 
 The contents of the root key will be displayed unless a subkey is
 specified. Paths to subkeys are always specified relative to the root
@@ -1658,6 +1650,20 @@ To list all REG_MULTI_SZ values:
 
 Search strings are not case-sensitive.
 
+=head2 regml.pl
+
+regml.pl will display those keys with explicit System Mandatory Label ACEs
+set in the System ACL.
+This feature was introduced with Windows Vista, and is used by applications
+such as Internet Explorer running in Protected Mode.
+Note that if a key does not have an explicit System Mandatory Label ACE,
+it has Medium Integrity Level.
+Only Windows NT registry files can contain System Mandatory Label ACEs.
+
+Type regml.pl on its own to see the help:
+
+    regml.pl <filename>
+
 =head2 regmultidiff.pl
 
 regmultidiff.pl can be used to compare multiple registry files
@@ -1689,7 +1695,6 @@ Type regscan.pl on its own to see the help:
         -p or --parse-info  show the technical information for an entry
                             instead of the string representation
         -u or --unparsed    show the unparsed on-disk entries as a hex dump
-        -w or --warnings    display warnings of invalid keys and values
 
 =head2 regsecurity.pl
 
@@ -1816,7 +1821,7 @@ James Macfarlane, E<lt>jmacfarla at cpan.orgE<gt>
 
 =head1 COPYRIGHT AND LICENSE
 
-Copyright (C) 2006,2007,2008,2009,2010 by James Macfarlane
+Copyright (C) 2006-2012 by James Macfarlane
 
 This library is free software; you can redistribute it and/or modify
 it under the same terms as Perl itself.
diff --git a/lib/Parse/Win32Registry/Base.pm b/lib/Parse/Win32Registry/Base.pm
index e0db3b2..6598f37 100644
--- a/lib/Parse/Win32Registry/Base.pm
+++ b/lib/Parse/Win32Registry/Base.pm
@@ -94,10 +94,6 @@ sub hexdump {
         $output .= '  ';
         $output .= ' ' x $indent;
         $row =~ tr/\x20-\x7e/./c;
-#        $row = decode($CODEPAGE, $row);
-#        $row =~ s/\x{00ad}/ /g;
-#        $row =~ s/[\x{0000}-\x{001f}]/\x{00b7}/g;
-#        $row =~ s/[\x{fffd}\x{007f}]/\x{25ab}/g;
         $output .= $row;
         $output .= "\n";
         $pos += $len;
@@ -113,10 +109,6 @@ sub hexdump {
         }
         $output .= '  ';
         $row =~ tr/\x20-\x7e/./c;
-#        $row = decode($CODEPAGE, $row);
-#        $row =~ s/\x{00ad}/ /g;
-#        $row =~ s/[\x{0000}-\x{001f}]/\x{00b7}/g;
-#        $row =~ s/[\x{fffd}\x{007f}]/\x{25ab}/g;
         $output .= $row;
         $output .= "\n";
         $pos += 16;
@@ -178,7 +170,7 @@ sub unpack_windows_time {
     my $epoch_offset = timegm(0, 0, 0, 1, 0, 70);
     $epoch_time += $epoch_offset;
 
-    if ($epoch_time < 0) {
+    if ($epoch_time < 0 || $epoch_time > 0x7fffffff) {
         $epoch_time = undef;
     }
 
@@ -187,11 +179,16 @@ sub unpack_windows_time {
 
 sub iso8601 {
     my $time = shift;
+    my $tz = shift;
 
     if (!defined $time) {
         return '(undefined)';
     }
 
+    if (!defined $tz || $tz ne 'Z') {
+        $tz = 'Z'
+    }
+
     # On Windows, gmtime will return undef if $time < 0 or > 0x7fffffff
     if ($time < 0 || $time > 0x7fffffff) {
         return '(undefined)';
@@ -199,8 +196,8 @@ sub iso8601 {
     my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime $time;
 
     # The final 'Z' indicates UTC ("zero meridian")
-    return sprintf '%04d-%02d-%02dT%02d:%02d:%02dZ',
-        1900+$year, 1+$mon, $mday, $hour, $min, $sec;
+    return sprintf '%04d-%02d-%02dT%02d:%02d:%02d%s',
+        1900+$year, 1+$mon, $mday, $hour, $min, $sec, $tz;
 }
 
 sub unpack_string {
@@ -681,6 +678,10 @@ our %WellKnownSids = (
     'S-1-5-32-550' => 'Print Operators',
     'S-1-5-32-551' => 'Backup Operators',
     'S-1-5-32-552' => 'Replicators',
+    'S-1-16-4096' => 'Low Integrity Level',
+    'S-1-16-8192' => 'Medium Integrity Level',
+    'S-1-16-12288' => 'High Integrity Level',
+    'S-1-16-16384' => 'System Integrity Level',
 );
 
 sub get_name {
@@ -727,7 +728,7 @@ sub new {
     # ACCESS_ALLOWED_ACE_TYPE = 0
     # ACCESS_DENIED_ACE_TYPE  = 1
     # SYSTEM_AUDIT_ACE_TYPE   = 2
-    # SYSTEM_ALARM_ACE_TYPE   = 3
+    # SYSTEM_MANDATORY_LABEL_ACE_TYPE = x011
 
     # Flags:
     # OBJECT_INHERIT_ACE         = 0x01
@@ -756,7 +757,7 @@ sub new {
 
     # Only the following types are currently unpacked:
     # 0 (ACCESS_ALLOWED_ACE), 1 (ACCESS_DENIED_ACE), 2 (SYSTEM_AUDIT_ACE)
-    if ($type >= 0 && $type <= 2) {
+    if ($type >= 0 && $type <= 2 || $type == 0x11) {
         my $access_mask = unpack('x4V', $data);
         my $sid = Parse::Win32Registry::SID->new(substr($data, 8,
                                                         $ace_len - 8));
@@ -797,6 +798,15 @@ our @Types = qw(
     ACCESS_DENIED_OBJECT
     SYSTEM_AUDIT_OBJECT
     SYSTEM_ALARM_OBJECT
+    ACCESS_ALLOWED_CALLBACK
+    ACCESS_DENIED_CALLBACK
+    ACCESS_ALLOWED_CALLBACK_OBJECT
+    ACCESS_DENIED_CALLBACK_OBJECT
+    SYSTEM_AUDIT_CALLBACK
+    SYSTEM_ALARM_CALLBACK
+    SYSTEM_AUDIT_CALLBACK_OBJECT
+    SYSTEM_ALARM_CALLBACK_OBJECT
+    SYSTEM_MANDATORY_LABEL
 );
 
 sub _look_up_ace_type {
@@ -994,28 +1004,36 @@ sub new {
                                                           $offset_to_owner));
         return if !defined $owner;
         $self->{_owner} = $owner;
-        $sd_len += $owner->get_length;
+        if ($offset_to_owner + $owner->get_length > $sd_len) {
+            $sd_len = $offset_to_owner + $owner->get_length;
+        }
     }
     if ($offset_to_group > 0 && $offset_to_group < length($data)) {
         my $group = Parse::Win32Registry::SID->new(substr($data,
                                                           $offset_to_group));
         return if !defined $group;
         $self->{_group} = $group;
-        $sd_len += $group->get_length;
+        if ($offset_to_group + $group->get_length > $sd_len) {
+            $sd_len = $offset_to_group + $group->get_length;
+        }
     }
     if ($offset_to_sacl > 0 && $offset_to_sacl < length($data)) {
         my $sacl = Parse::Win32Registry::ACL->new(substr($data,
                                                          $offset_to_sacl));
         return if !defined $sacl;
         $self->{_sacl} = $sacl;
-        $sd_len += $sacl->get_length;
+        if ($offset_to_sacl + $sacl->get_length > $sd_len) {
+            $sd_len = $offset_to_sacl + $sacl->get_length;
+        }
     }
     if ($offset_to_dacl > 0 && $offset_to_dacl < length($data)) {
         my $dacl = Parse::Win32Registry::ACL->new(substr($data,
                                                          $offset_to_dacl));
         return if !defined $dacl;
         $self->{_dacl} = $dacl;
-        $sd_len += $dacl->get_length;
+        if ($offset_to_dacl + $dacl->get_length > $sd_len) {
+            $sd_len = $offset_to_dacl + $dacl->get_length;
+        }
     }
     $self->{_length} = $sd_len;
     bless $self, $class;
diff --git a/lib/Parse/Win32Registry/Entry.pm b/lib/Parse/Win32Registry/Entry.pm
index ee81b83..1e14a80 100644
--- a/lib/Parse/Win32Registry/Entry.pm
+++ b/lib/Parse/Win32Registry/Entry.pm
@@ -6,6 +6,12 @@ use warnings;
 use Carp;
 use Parse::Win32Registry::Base qw(:all);
 
+sub get_regfile {
+    my $self = shift;
+
+    return $self->{_regfile};
+}
+
 sub get_offset {
     my $self = shift;
 
diff --git a/lib/Parse/Win32Registry/Value.pm b/lib/Parse/Win32Registry/Value.pm
index fb5f08c..de8587a 100644
--- a/lib/Parse/Win32Registry/Value.pm
+++ b/lib/Parse/Win32Registry/Value.pm
@@ -68,7 +68,7 @@ sub get_data_as_string {
         my $i = 0;
         return join(' ', map { "[" . $i++ . "] $_" } @data);
     }
-    elsif ($type == REG_DWORD) {
+    elsif ($type == REG_DWORD || $type == REG_DWORD_BIG_ENDIAN) {
         return sprintf '0x%08x (%u)', $data, $data;
     }
     else {
diff --git a/lib/Parse/Win32Registry/Win95/File.pm b/lib/Parse/Win32Registry/Win95/File.pm
index 0dbafb2..6b713f2 100644
--- a/lib/Parse/Win32Registry/Win95/File.pm
+++ b/lib/Parse/Win32Registry/Win95/File.pm
@@ -82,6 +82,7 @@ sub get_virtual_root_key {
     my $fake_root = shift;
 
     my $root_key = $self->get_root_key;
+    return if !defined $root_key;
 
     if (!defined $fake_root) {
         # guess virtual root from filename
diff --git a/lib/Parse/Win32Registry/Win95/Key.pm b/lib/Parse/Win32Registry/Win95/Key.pm
index 4100073..6bff102 100644
--- a/lib/Parse/Win32Registry/Win95/Key.pm
+++ b/lib/Parse/Win32Registry/Win95/Key.pm
@@ -204,18 +204,4 @@ sub get_value_iterator {
     }
 }
 
-sub get_associated_offsets {
-    my $self = shift;
-
-    my @owners = ();
-
-    push @owners, $self->{_offset};
-
-    if (defined $self->{_offset_to_rgdb_entry}) {
-        push @owners, $self->{_offset_to_rgdb_entry};
-    }
-
-    return @owners;
-}
-
 1;
diff --git a/lib/Parse/Win32Registry/Win95/Value.pm b/lib/Parse/Win32Registry/Win95/Value.pm
index f04bdb0..baffd13 100644
--- a/lib/Parse/Win32Registry/Win95/Value.pm
+++ b/lib/Parse/Win32Registry/Win95/Value.pm
@@ -90,6 +90,15 @@ sub get_data {
             $data = undef;
         }
     }
+    elsif ($type == REG_DWORD_BIG_ENDIAN) {
+        if (length($data) == 4) {
+            $data = unpack('N', $data);
+        }
+        else {
+            # incorrect length for dword data
+            $data = undef;
+        }
+    }
     elsif ($type == REG_SZ || $type == REG_EXPAND_SZ) {
         # Snip off any terminating null.
         # Typically, REG_SZ values will not have a terminating null,
@@ -118,13 +127,19 @@ sub as_regedit_export {
 
     my $type = $self->get_type;
 
+    # XXX
+#    if (!defined $self->{_data}) {
+#        $name = $name eq '' ? '@' : qq{"$name"};
+#        return qq{; $name=(invalid data)\n};
+#    }
+
     if ($type == REG_SZ) {
         $export .= '"' . $self->get_data . '"';
         $export .= "\n";
     }
     elsif ($type == REG_BINARY) {
         $export .= 'hex:';
-        $export .= format_octets($self->get_data, length($export));
+        $export .= format_octets($self->{_data}, length($export));
     }
     elsif ($type == REG_DWORD) {
         my $data = $self->get_data;
@@ -141,9 +156,8 @@ sub as_regedit_export {
         $export .= format_octets($data, length($export));
     }
     else {
-        my $data = $self->get_data;
         $export .= sprintf("hex(%x):", $type);
-        $export .= format_octets($data, length($export));
+        $export .= format_octets($self->{_data}, length($export));
     }
     return $export;
 }
@@ -160,14 +174,4 @@ sub parse_info {
     return $info;
 }
 
-sub get_associated_offsets {
-    my $self = shift;
-
-    my @owners = ();
-
-    push @owners, $self->{_offset};
-
-    return @owners;
-}
-
 1;
diff --git a/lib/Parse/Win32Registry/WinNT/File.pm b/lib/Parse/Win32Registry/WinNT/File.pm
index 5780fe2..97a859b 100644
--- a/lib/Parse/Win32Registry/WinNT/File.pm
+++ b/lib/Parse/Win32Registry/WinNT/File.pm
@@ -107,6 +107,7 @@ sub get_virtual_root_key {
     my $fake_root = shift;
 
     my $root_key = $self->get_root_key;
+    return if !defined $root_key;
 
     if (!defined $fake_root) {
         # guess virtual root from filename
diff --git a/lib/Parse/Win32Registry/WinNT/Key.pm b/lib/Parse/Win32Registry/WinNT/Key.pm
index 7ab62ad..d362086 100644
--- a/lib/Parse/Win32Registry/WinNT/Key.pm
+++ b/lib/Parse/Win32Registry/WinNT/Key.pm
@@ -90,6 +90,11 @@ sub new {
     }
     # allocated should be true
 
+    if ($length < NK_HEADER_LENGTH) {
+        warnf('Invalid value entry length at 0x%x', $offset);
+        return;
+    }
+
     if ($sig ne 'nk') {
         warnf('Invalid signature for key at 0x%x', $offset);
         return;
@@ -100,6 +105,7 @@ sub new {
         warnf('Could not read name for key at 0x%x', $offset);
         return;
     }
+
     if ($flags & 0x20) {
         $name = decode($Parse::Win32Registry::Base::CODEPAGE, $name);
     }
@@ -330,7 +336,6 @@ sub _get_offsets_to_subkeys {
     }
     elsif ($sig eq 'ri') {
         foreach my $offset (unpack("V$num_entries", $subkey_list)) {
-            $self->{_indirect_offsets}{OFFSET_TO_FIRST_HBIN + $offset} = undef;
             my $offsets_ref =
                 $self->_get_offsets_to_subkeys(OFFSET_TO_FIRST_HBIN + $offset);
             if (defined $offsets_ref && ref $offsets_ref eq 'ARRAY') {
@@ -436,37 +441,4 @@ sub get_value_iterator {
     });
 }
 
-sub get_associated_offsets {
-    my $self = shift;
-
-    my @owners = ();
-
-    push @owners, $self->{_offset};
-
-    if ($self->{_offset_to_security}) {
-        push @owners, $self->{_offset_to_security};
-    }
-
-    if ($self->{_offset_to_class_name}) {
-        push @owners, $self->{_offset_to_class_name};
-    }
-
-    if ($self->{_num_subkeys}) {
-        push @owners, $self->{_offset_to_subkey_list};
-    }
-
-    # Indirect offsets must be added after _get_offsets_to_subkeys
-    # has been called (as this populates the _indirect_offsets field)
-    $self->_get_offsets_to_subkeys;
-    if ($self->{_indirect_offsets}) {
-        push @owners, keys %{ $self->{_indirect_offsets} };
-    }
-
-    if ($self->{_num_values}) {
-        push @owners, $self->{_offset_to_value_list};
-    }
-
-    return @owners;
-}
-
 1;
diff --git a/lib/Parse/Win32Registry/WinNT/Value.pm b/lib/Parse/Win32Registry/WinNT/Value.pm
index f1269b3..b9e882b 100644
--- a/lib/Parse/Win32Registry/WinNT/Value.pm
+++ b/lib/Parse/Win32Registry/WinNT/Value.pm
@@ -57,6 +57,11 @@ sub new {
     }
     # allocated should be true
 
+    if ($length < VK_HEADER_LENGTH) {
+        warnf('Invalid value entry length at 0x%x', $offset);
+        return;
+    }
+
     if ($sig ne 'vk') {
         warnf('Invalid signature for value at 0x%x', $offset);
         return;
@@ -67,6 +72,7 @@ sub new {
         warnf('Could not read name for value at 0x%x', $offset);
         return;
     }
+
     if ($flags & 1) {
         $name = decode($Parse::Win32Registry::Base::CODEPAGE, $name);
     }
@@ -74,10 +80,9 @@ sub new {
         $name = decode('UCS-2LE', $name);
     };
 
-    my $data;
-
     # If the top bit of the data_length is set, then
     # the value is inline and stored in the offset to data field (at 0xc).
+    my $data;
     my $data_inline = $data_length >> 31;
     if ($data_inline) {
         # REG_DWORDs are always inline, but I've also seen
@@ -94,15 +99,15 @@ sub new {
         }
     }
     else {
-        $offset_to_data += OFFSET_TO_FIRST_HBIN
-            if $offset_to_data != 0xffffffff;
-
-        sysseek($fh, $offset_to_data + 4, 0);
-        $bytes_read = sysread($fh, $data, $data_length);
-        if ($bytes_read != $data_length) {
-            warnf("Could not read data at 0x%x for value '%s' at 0x%x",
-                $offset_to_data, $name, $offset);
-            $data = undef;
+        if ($offset_to_data != 0 && $offset_to_data != 0xffffffff) {
+            $offset_to_data += OFFSET_TO_FIRST_HBIN;
+            if ($offset_to_data < ($regfile->get_length - $data_length)) {
+                $data = _extract_data($fh, $offset_to_data, $data_length);
+            }
+            else {
+                warnf("Invalid offset to data for value '%s' at 0x%x",
+                    $name, $offset);
+            }
         }
     }
 
@@ -125,6 +130,94 @@ sub new {
     return $self;
 }
 
+sub _extract_data {
+    my $fh = shift;
+    my $offset_to_data = shift;
+    my $data_length = shift;
+
+    if ($offset_to_data == 0 || $offset_to_data == 0xffffffff) {
+        return undef;
+    }
+
+    sysseek($fh, $offset_to_data, 0);
+    my $bytes_read = sysread($fh, my $data_header, 4);
+    if ($bytes_read != 4) {
+        warnf('Could not read data at 0x%x', $offset_to_data);
+        return undef;
+    }
+
+    my ($max_data_length) = unpack('V', $data_header);
+
+    my $data_allocated = 0;
+    if ($max_data_length > 0x7fffffff) {
+        $data_allocated = 1;
+        $max_data_length = (0xffffffff - $max_data_length) + 1;
+    }
+    # data_allocated should be true
+
+    my $data;
+
+    if ($data_length > $max_data_length) {
+        $bytes_read = sysread($fh, my $db_entry, 8);
+        if ($bytes_read != 8) {
+            warnf('Could not read data at 0x%x', $offset_to_data);
+            return undef;
+        }
+
+        my ($sig, $num_data_blocks, $offset_to_data_block_list)
+            = unpack('a2vV', $db_entry);
+        if ($sig ne 'db') {
+            warnf('Invalid signature for big data at 0x%x', $offset_to_data);
+            return undef;
+        }
+        $offset_to_data_block_list += OFFSET_TO_FIRST_HBIN;
+
+        sysseek($fh, $offset_to_data_block_list + 4, 0);
+        $bytes_read = sysread($fh, my $data_block_list, $num_data_blocks * 4);
+        if ($bytes_read != $num_data_blocks * 4) {
+            warnf('Could not read data block list at 0x%x',
+                $offset_to_data_block_list);
+            return undef;
+        }
+
+        $data = "";
+        my @offsets = map { OFFSET_TO_FIRST_HBIN + $_ }
+            unpack("V$num_data_blocks", $data_block_list);
+        foreach my $offset (@offsets) {
+            sysseek($fh, $offset, 0);
+            $bytes_read = sysread($fh, my $block_header, 4);
+            if ($bytes_read != 4) {
+                warnf('Could not read data block at 0x%x', $offset);
+                return undef;
+            }
+            my ($block_length) = unpack('V', $block_header);
+            if ($block_length > 0x7fffffff) {
+                $block_length = (0xffffffff - $block_length) + 1;
+            }
+            $bytes_read = sysread($fh, my $block_data, $block_length - 8);
+            if ($bytes_read != $block_length - 8) {
+                warnf('Could not read data block at 0x%x', $offset);
+                return undef;
+            }
+            $data .= $block_data;
+        }
+        if (length($data) < $data_length) {
+            warnf("Insufficient data blocks for data at 0x%x", $offset_to_data);
+            return undef;
+        }
+        $data = substr($data, 0, $data_length);
+        return $data;
+    }
+    else {
+        $bytes_read = sysread($fh, $data, $data_length);
+        if ($bytes_read != $data_length) {
+            warnf("Could not read data at 0x%x", $offset_to_data);
+            return undef;
+        }
+    }
+    return $data;
+}
+
 sub get_data {
     my $self = shift;
 
@@ -143,6 +236,15 @@ sub get_data {
             $data = undef;
         }
     }
+    elsif ($type == REG_DWORD_BIG_ENDIAN) {
+        if (length($data) == 4) {
+            $data = unpack('N', $data);
+        }
+        else {
+            # incorrect length for dword data
+            $data = undef;
+        }
+    }
     elsif ($type == REG_SZ || $type == REG_EXPAND_SZ) {
         $data = decode('UCS-2LE', $data);
         # snip off any terminating null
@@ -171,13 +273,19 @@ sub as_regedit_export {
 
     my $type = $self->get_type;
 
+    # XXX
+#    if (!defined $self->{_data}) {
+#        $name = $name eq '' ? '@' : qq{"$name"};
+#        return qq{; $name=(invalid data)\n};
+#    }
+
     if ($type == REG_SZ) {
         $export .= '"' . $self->get_data . '"';
         $export .= "\n";
     }
     elsif ($type == REG_BINARY) {
         $export .= "hex:";
-        $export .= format_octets($self->get_data, length($export));
+        $export .= format_octets($self->{_data}, length($export));
     }
     elsif ($type == REG_DWORD) {
         my $data = $self->get_data;
@@ -194,9 +302,8 @@ sub as_regedit_export {
         $export .= format_octets($data, length($export));
     }
     else {
-        my $data = $self->get_data;
         $export .= sprintf("hex(%x):", $type);
-        $export .= format_octets($data, length($export));
+        $export .= format_octets($self->{_data}, length($export));
     }
     return $export;
 }
@@ -222,18 +329,4 @@ sub parse_info {
     return $info;
 }
 
-sub get_associated_offsets {
-    my $self = shift;
-
-    my @owners = ();
-
-    push @owners, $self->{_offset};
-
-    if (!$self->{_data_inline}) {
-        push @owners, $self->{_offset_to_data};
-    }
-
-    return @owners;
-}
-
 1;
diff --git a/t/errors.t b/t/errors.t
index 7cc1599..f67d05c 100644
--- a/t/errors.t
+++ b/t/errors.t
@@ -272,7 +272,7 @@ my @tests = (
         filename => 'winnt_error_tests.rf',
         class => 'Parse::Win32Registry::WinNT::Value',
         offset => 0x19c0,
-        warning => 'Could not read data at 0x',
+        warning => 'Invalid offset to data for value \'.*\' at 0x',
         further_tests => [
             ['defined($object)'],
             ['$object->get_name', 'value5'],
diff --git a/t/misc.t b/t/misc.t
index 329e122..c563047 100644
--- a/t/misc.t
+++ b/t/misc.t
@@ -478,13 +478,13 @@ my @time_tests = (
     [
         "TIME22",
         "\x00\x80\x3e\xd5\x1e\xfd\xe9\x01",
-        2147483648, # 2147483648
+        undef, # 2147483648
         '(undefined)', # '2038-01-19T03:14:08Z'
     ],
     [
         "TIME23",
         "\x00\x00\x00\x00\x00\x00\x00\x02",
-        2767045207, # 2767045207
+        undef, # 2767045207
         '(undefined)', # '2057-09-06T23:40:07Z'
     ],
     [
diff --git a/t/security.t b/t/security.t
index dc02958..a0616c3 100644
--- a/t/security.t
+++ b/t/security.t
@@ -174,6 +174,19 @@ my @ace_tests = (
         "\x01\xff\x00\x00\x00\x00\x00\x05\x0c\x00\x00\x00",
         undef,
     ],
+    [
+        "ACE11",
+        "\x11\x00\x14\x00\x01\x00\x00\x00".
+        "\x01\x01\x00\x00\x00\x00\x00\x10\x00\x10\x00\x00",
+        {
+            type => 17,
+            type_as_string => 'SYSTEM_MANDATORY_LABEL',
+            flags => 0x00,
+            mask => 0x00000001,
+            trustee => "S-1-16-4096",
+        },
+        20,
+    ],
 );
 
 sub check_ace {
diff --git a/t/use.t b/t/use.t
index dce8153..689eaec 100644
--- a/t/use.t
+++ b/t/use.t
@@ -5,7 +5,7 @@ use Test::More 'no_plan';
 
 BEGIN { use_ok('Parse::Win32Registry') };
 
-is($Parse::Win32Registry::VERSION, '0.60', 'correct version');
+is($Parse::Win32Registry::VERSION, '1.0', 'correct version');
 can_ok('Parse::Win32Registry', 'new');
 can_ok('Parse::Win32Registry', 'convert_filetime_to_epoch_time');
 can_ok('Parse::Win32Registry', 'iso8601');
diff --git a/t/value.t b/t/value.t
index 169dd20..97ed1e4 100644
--- a/t/value.t
+++ b/t/value.t
@@ -5,6 +5,8 @@ use Test::More 'no_plan';
 use Data::Dumper;
 use Parse::Win32Registry 0.60 qw(:REG_);
 
+Parse::Win32Registry::disable_warnings;
+
 $Data::Dumper::Useqq = 1;
 $Data::Dumper::Terse = 1;
 $Data::Dumper::Indent = 0;
@@ -231,6 +233,78 @@ sub run_value_tests
             raw_data => "\xff\xff\xff\xff",
         },
         {
+            name => 'dword_big_endian1',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => 16909060,
+            data_as_string => '0x01020304 (16909060)',
+            as_regedit_export => qq{"dword_big_endian1"=hex(5):01,02,03,04\n},
+            raw_data => "\x01\x02\x03\x04",
+        },
+        {
+            name => 'dword_big_endian2',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => undef,
+            data_as_string => '(invalid data)',
+            as_regedit_export => qq{"dword_big_endian2"=hex(5):01,02,03,04,05,06\n},
+            raw_data => "\x01\x02\x03\x04\x05\x06",
+        },
+        {
+            name => 'dword_big_endian3',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => undef,
+            data_as_string => '(invalid data)',
+            as_regedit_export => qq{"dword_big_endian3"=hex(5):01,02\n},
+            raw_data => "\x01\x02",
+        },
+        {
+            name => 'dword_big_endian4',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => undef,
+            data_as_string => '(invalid data)',
+            as_regedit_export => qq{"dword_big_endian4"=hex(5):\n},
+            raw_data => "",
+        },
+        {
+            name => 'dword_big_endian5',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => 0,
+            data_as_string => '0x00000000 (0)',
+            as_regedit_export => qq{"dword_big_endian5"=hex(5):00,00,00,00\n},
+            raw_data => "\x00\x00\x00\x00",
+        },
+        {
+            name => 'dword_big_endian6',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => 0x7fffffff,
+            data_as_string => '0x7fffffff (2147483647)',
+            as_regedit_export => qq{"dword_big_endian6"=hex(5):7f,ff,ff,ff\n},
+            raw_data => "\x7f\xff\xff\xff",
+        },
+        {
+            name => 'dword_big_endian7',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => 0x80000000,
+            data_as_string => '0x80000000 (2147483648)',
+            as_regedit_export => qq{"dword_big_endian7"=hex(5):80,00,00,00\n},
+            raw_data => "\x80\x00\x00\x00",
+        },
+        {
+            name => 'dword_big_endian8',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => 0xffffffff,
+            data_as_string => '0xffffffff (4294967295)',
+            as_regedit_export => qq{"dword_big_endian8"=hex(5):ff,ff,ff,ff\n},
+            raw_data => "\xff\xff\xff\xff",
+        },
+        {
             name => 'multi_sz1',
             type => REG_MULTI_SZ,
             type_as_string => 'REG_MULTI_SZ',
@@ -632,6 +706,114 @@ sub run_value_tests
             raw_data => "\xff\xff\xff\xff",
         },
         {
+            name => 'dword_big_endian1',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => 16909060,
+            data_as_string => '0x01020304 (16909060)',
+            as_regedit_export => qq{"dword_big_endian1"=hex(5):01,02,03,04\n},
+            raw_data => "\x01\x02\x03\x04",
+        },
+        {
+            name => 'dword_big_endian2',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => undef,
+            data_as_string => '(invalid data)',
+            as_regedit_export => qq{"dword_big_endian2"=hex(5):01,02,03,04,05,06\n},
+            raw_data => "\x01\x02\x03\x04\x05\x06",
+        },
+        {
+            name => 'dword_big_endian3',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => undef,
+            data_as_string => '(invalid data)',
+            as_regedit_export => qq{"dword_big_endian3"=hex(5):01,02\n},
+            raw_data => "\x01\x02",
+        },
+        {
+            name => 'dword_big_endian4',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => undef,
+            data_as_string => '(invalid data)',
+            as_regedit_export => qq{"dword_big_endian4"=hex(5):\n},
+            raw_data => "",
+        },
+        {
+            name => 'dword_big_endian5',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => 16909060,
+            data_as_string => '0x01020304 (16909060)',
+            as_regedit_export => qq{"dword_big_endian5"=hex(5):01,02,03,04\n},
+            raw_data => "\x01\x02\x03\x04",
+        },
+        {
+            name => 'dword_big_endian6',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => undef,
+            data_as_string => '(invalid data)',
+            as_regedit_export => qq{"dword_big_endian6"=hex(5):\n},
+            raw_data => undef,
+        },
+        {
+            name => 'dword_big_endian7',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => undef,
+            data_as_string => '(invalid data)',
+            as_regedit_export => qq{"dword_big_endian7"=hex(5):01,02\n},
+            raw_data => "\x01\x02",
+        },
+        {
+            name => 'dword_big_endian8',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => undef,
+            data_as_string => '(invalid data)',
+            as_regedit_export => qq{"dword_big_endian8"=hex(5):\n},
+            raw_data => "",
+        },
+        {
+            name => 'dword_big_endian9',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => 0,
+            data_as_string => '0x00000000 (0)',
+            as_regedit_export => qq{"dword_big_endian9"=hex(5):00,00,00,00\n},
+            raw_data => "\x00\x00\x00\x00",
+        },
+        {
+            name => 'dword_big_endian10',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => 0x7fffffff,
+            data_as_string => '0x7fffffff (2147483647)',
+            as_regedit_export => qq{"dword_big_endian10"=hex(5):7f,ff,ff,ff\n},
+            raw_data => "\x7f\xff\xff\xff",
+        },
+        {
+            name => 'dword_big_endian11',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => 0x80000000,
+            data_as_string => '0x80000000 (2147483648)',
+            as_regedit_export => qq{"dword_big_endian11"=hex(5):80,00,00,00\n},
+            raw_data => "\x80\x00\x00\x00",
+        },
+        {
+            name => 'dword_big_endian12',
+            type => REG_DWORD_BIG_ENDIAN,
+            type_as_string => 'REG_DWORD_BIG_ENDIAN',
+            data => 0xffffffff,
+            data_as_string => '0xffffffff (4294967295)',
+            as_regedit_export => qq{"dword_big_endian12"=hex(5):ff,ff,ff,ff\n},
+            raw_data => "\xff\xff\xff\xff",
+        },
+        {
             name => 'multi_sz1',
             type => REG_MULTI_SZ,
             type_as_string => 'REG_MULTI_SZ',
diff --git a/t/win95_value_tests.rf b/t/win95_value_tests.rf
index 824e599..9be685e 100644
Binary files a/t/win95_value_tests.rf and b/t/win95_value_tests.rf differ
diff --git a/t/winnt_value_tests.rf b/t/winnt_value_tests.rf
index 3039dfa..f9e72db 100644
Binary files a/t/winnt_value_tests.rf and b/t/winnt_value_tests.rf differ

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-perl/packages/libsendmail-milter-perl.git



More information about the Pkg-perl-cvs-commits mailing list