Datafile-Hash

 view release on metacpan or  search on metacpan

lib/Datafile/Hash.pm  view on Meta::CPAN

            $has_real_nested = 1;
        }

        my %global_data;
        for my $k ( keys %data ) {
            next if ref $data{$k} eq 'HASH';
            $global_data{$k} = delete $data{$k};
        }
        my $write_section;
        $write_section = sub {
            my ( $cur, $path ) = @_;
            my $name = @$path ? join( '.', @$path ) : '';

            print $fh "\n" unless ($first_section);
            $first_section = 0;
            if ( $name ne '' ) {
                print $fh "[$name]\n";
                push @messages, "> [$name]\n" if $verbose;
            }

            my $maxsize = 0;
            for my $k ( sort grep { !ref $cur->{$_} } keys %$cur ) {
                $maxsize = length($k) if length($k) > $maxsize;
            }
            for my $k ( sort grep { !ref $cur->{$_} } keys %$cur ) {
                my $v = $cur->{$k};
                my $needs_quoting =
                    ( $v =~ /[#"'\r\n]/ || $v =~ /^\s+|\s+$/ || $v eq '' );
                if ( $delim eq '=' && $needs_quoting ) {
                    $v =~ s/"/\\"/g;
                    $v = qq("$v");
                }
                my $line = sprintf "%-*s %s %s", $maxsize, $k, $delim, $v;
                print $fh "$line\n";
                $entry_count++;
            }

            for my $sub ( sort grep { ref $cur->{$_} eq 'HASH' } keys %$cur ) {
                $write_section->( $cur->{$sub}, [ @$path, $sub ] );
            }
        };

        if ( keys %global_data ) {
            $write_section->( \%global_data, [] );
        }
        for my $top ( sort keys %data ) {
            $write_section->( $data{$top}, [$top] );
        }
    }
    print $fh "#EOF\n" if $comment_char eq '#';
    close $fh
        or return ( 0, ["ERROR: failed to close '$tmp': $!"] );

    if ( $backup && -f $filename ) {
        rename( $filename, $filename . '.bak' )
            or push @messages,
            "WARNING: backup to ${filename}.bak failed: $!";
    }
    rename( $tmp, $filename )
        or return ( 0, ["ERROR: failed to rename '$tmp' to '$filename': $!"] );
    chmod $prot, $filename;

    push @messages, "- $entry_count entries written to $filename\n"
        if $verbose;
    return ( $entry_count, \@messages );
}

1;

__END__

=encoding UTF-8

=head1 NAME

Datafile::Hash - Pure-Perl utilities for datafiles and INI-style config files with multi-level sections

=head1 LICENSE

This module is free software.

You can redistribute it and/or modify it under the same terms as Perl itself.

=head1 SYNOPSIS

  use Datafile::Hash qw(readhash writehash);

  my %config;

  readhash('config.ini', \%config, {
      delimiter => '=',      # INI mode
      group     => 2,        # nested hashes (default)
      verbose   => 1,
  });

  # $config{section}{subsection}{key} = 'value'

  # Flat key-value file example
  readhash('settings.txt', \%config, {
      delimiter => '=',      # still INI mode
      group     => 0,        # flat hash, ignore sections
  });

  writehash('config.ini', \%config, {
      backup  => 1,
      comment => ['Auto-generated - do not edit manually', scalar localtime],
  });

=head1 DESCRIPTION

Lightweight pure-Perl module for reading and writing key-value data files,
including full INI-style files with multi-level sections.
Supports flat files, dotted-key notation, or true nested hashes.
Safe atomic writes and consistent error handling.

=head1 FUNCTIONS

=head2 readhash($filename, $hash_ref, \%options)

Loads key-value data into a hash reference.



( run in 2.083 seconds using v1.01-cache-2.11-cpan-cdf2f3d4e48 )