Control: tag -1 patch

On Sun, Dec 25, 2016 at 12:24:45AM +0100, Johannes Schauer wrote:
> Package: libconfig-inifiles-perl
> Version: 2.94-1
> Severity: normal

> use Config::IniFiles;
> tie (my %ini, 'Config::IniFiles', -file=>"test.ini");
> foreach my $s (keys %ini) {
>     while (my ($k,$v) = each %{$ini{$s}}) {
>         print("$s $k $v\n");
>     }
> }

> The result will be an infinite loop printing "foo bar baz".

Thanks for the report.

What is happening is that every time $ini{$s} is evaluated,
Config::IniFiles::FETCH() creates a new Config::IniFiles::_section tied
hash that has its own each() iterator.

A workaround is to do something like

  foreach my $s (@k) {
      my $i = $ini{$s};
      while (my ($k,$v) = each %$i) {
          print("$s $k $v\n");
      }
  }
 
but I'm attaching a patch that seems to fix this without breaking any
existing tests.
-- 
Niko Tyni   nt...@debian.org
>From b9acffc56b85c71cde4b13751256bb37a64d9c2a Mon Sep 17 00:00:00 2001
From: Niko Tyni <nt...@debian.org>
Date: Sun, 25 Dec 2016 21:38:51 +0200
Subject: [PATCH] Reuse Config::IniFiles::_section hashes in
 Config::IniFiles::FETCH

As reported by Johannes Schauer, code like

 foreach my $s (keys %ini) {
     while (my ($k,$v) = each %{$ini{$s}}) {
         print("$s $k $v\n");
     }
 }

goes to an infinite loop. This is because every time $ini{$s}
is evaluated, Config::IniFiles::FETCH() creates a new
Config::IniFiles::_section tied hash that has its own each() iterator.

Store and reuse the section hashes instead.

Bug-Debian: https://bugs.debian.org/849298
---
 lib/Config/IniFiles.pm  |  6 +++++-
 t/35section-iterators.t | 24 ++++++++++++++++++++++++
 2 files changed, 29 insertions(+), 1 deletion(-)
 create mode 100644 t/35section-iterators.t

diff --git a/lib/Config/IniFiles.pm b/lib/Config/IniFiles.pm
index 85296e8..817dece 100644
--- a/lib/Config/IniFiles.pm
+++ b/lib/Config/IniFiles.pm
@@ -1841,12 +1841,16 @@ sub FETCH {
   my $self = shift;
   my( $key ) = @_;
 
+  $self->{_section_cache} ||= {};
+
   $self->_caseify(\$key);
   return if (! $self->{v}{$key});
 
+  return $self->{_section_cache}->{$key} if exists $self->{_section_cache}->{$key};
+
   my %retval;
   tie %retval, 'Config::IniFiles::_section', $self, $key;
-  return \%retval;
+  return $self->{_section_cache}->{$key} = \%retval;
 
 } # end FETCH
 
diff --git a/t/35section-iterators.t b/t/35section-iterators.t
new file mode 100644
index 0000000..54adf7f
--- /dev/null
+++ b/t/35section-iterators.t
@@ -0,0 +1,24 @@
+#!/usr/bin/perl
+
+# See: https://bugs.debian.org/849298
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+
+use Config::IniFiles;
+
+my $ini_contents = <<'EOF';
+[foo]
+bar=baz
+rab=zab
+EOF
+
+tie (my %ini, 'Config::IniFiles', -file=>\$ini_contents);
+
+my ($k1,$v1) = each %{$ini{foo}};
+my ($k2,$v2) = each %{$ini{foo}};
+
+isnt($k1, $k2, "got different keys with successive each() calls");
+isnt($v1, $v2, "got different values with successive each() calls");
-- 
2.11.0

Reply via email to