http://www.perlmonks.org?node_id=143570

hotshot has asked for the wisdom of the Perl Monks concerning the following question:

Hi guys !

A little problem. I have a parser (not mine) for a configuration file (smb.conf if someone knows). this file as from the format:
[name1] key1 = value1 key2 = value2 ... [name2] key1 = value1 key2 = value2 ... # and so on
The problem is when I have something like this:
[name1] key1 = value1 key2 = value2 ... [name2] [name3] key1 = value1 key2 = value2 ...
as you can see name2 has no values, this thing is ok (default values are taken), but the parser I have doesn't return it, here is it's code:
sub smbconf_parse { my $smbconf = shift; my %smbconf; my $share = ''; if (! open(SMB, $smbconf)) { warn "Couldn't read smbconf file $smbconf\n"; warn "$!\n"; return 0; } while (<SMB>) { s/^\s+//g; s/\s+$//g; next if (/^$/); next if (/^\#/); next if (/^\;/); if (/^\[(.*)\]/) { $share = $1; ####### } else { my ($key, $value) = (/^(.*) ?\= (\S.*)/); $key =~ s/\s+$//; if ($value =~ /^\"(.*)\"$/) { $smbconf{$share}{$key} = $1; } elsif ($value =~ /\,/) { my @value = split(/\,/, $value); $smbconf{$share}{$key} = [ @value ]; } else { $smbconf{$share}{$key} = $value; } } } close SMB; return \%smbconf; }
I can see the problem, no hash entry is opened if it didn't find any key=value pairs, so I tried adding something like
$smbconf{$share} = ''; # just to open the entry
right after the line with $share = $1;, but that seemed to mess the whole parsing.

anyone has an idea ?

Hotshot

Replies are listed 'Best First'.
Re: hash reference
by Zaxo (Archbishop) on Feb 06, 2002 at 08:14 UTC

    The smb.conf file is in a standard format popular with MS Windows and applications.for it. It will be no surprise that there is Config::IniFiles at CPAN to help parse them.

    One More Really Round Wheel.

    Update: That was too short. Here's a usage that gets you what you want:

    use Config::IniFiles; my $smb = new Config::IniFiles( -file => '/etc/samba/smb.conf'); print join $/, $smb->Sections; # prints the section names
    There are provisions for rewriting the file, but you'll have to take care of locking yourself. There is also a tied interface, but I'd be leary of that without built-in file locks.

    After Compline,
    Zaxo

Re (tilly) 1: hash reference
by tilly (Archbishop) on Feb 06, 2002 at 08:35 UTC
    You need that to be initialized as a hash reference, else you are going to be doing unwanted symbolic referencing which you don't want. (Using strict.pm would have given you a more informative error message.)

    A better way to do that initialization is:

    $smbconf{$share} ||= {};
    (I use ||= in case you want a given section to be able to be declared, redeclared, and added to.)
Re: hash reference
by shotgunefx (Parson) on Feb 06, 2002 at 08:27 UTC
    I agree with Zaxo, but to answer your question, you could change this
    $share = $1; ####### to $share = $1; ####### $smbconf{$share} = ();


    -Lee

    "To be civilized is to deny one's nature."
Re: hash reference
by CharlesClarkson (Curate) on Feb 06, 2002 at 08:29 UTC

    Place this line before the $share = $1; line.

    $smbconf{$share} = undef unless exists $smbconf{$share} || $share eq '';


    HTH,
    Charles K. Clarkson
    Clarkson Energy Homes, Inc.
Re: hash reference
by snowcrash (Friar) on Feb 06, 2002 at 08:35 UTC
    in addition to what Zaxo replied:
    $smbconf{$share} seems to expect a reference to a hash, like your node's title states. you set it to a scalar, i.e. "", which seems to mess up somewhere else later. i guess it would work if you assign an empty hashref, $smbconf{$share} = {} (or some hashref containing default values?).

    hth,
    snowcrash
Re: hash reference
by strat (Canon) on Feb 06, 2002 at 14:32 UTC
    I'd parse it in the following way... (haven't tested)
    sub ReadIniFile { my ($iniFile) = @_; my %config = (); local ($/) = undef; unless (open (INI, $iniFile)){ print (LOG "Error: couldn't read from INI $iniFile: $!\n"); die ("Error: couldn't read from INI $iniFile: $!\n"); } my $ini = <INI>; close (INI); my (@mainpoints) = ($ini =~ /(\[\w+?\])\n/g); # extract [.....] my ($x, @contents) = split(/\[\w+?\]/, $ini); @config{@mainpoints} = @contents; foreach (keys %config){ my %subHash = (); my @values = split(/\n+/, $config{$_}); foreach (@values){ my ($key, $val) = split(/\s*\:\s*/, $_, 2); next unless defined $key; next if $key eq ''; next if $key =~ /^\s*\#/; $subHash{lc($key)} = $val; } $config{$_} = \%subHash; } return (\%config); } # ReadIniFile

    Best regards,
    perl -e "$_=*F=>y~\*martinF~stronat~=>s~[^\w]~~g=>chop,print"