# parser-lib.pl
# Functions for reading and writing the squid config file

# get_config()
# Parses squid.conf into an array of data structures
sub get_config
{
local($lnum, $_);
if (!@get_config_cache) {
	open(CONF, $config{'squid_conf'});
	$lnum = 0;
	while(<CONF>) {
		s/\r|\n//g;	# strip newlines and comments
		s/#.*$//g;
		if (/^\s*(\S+)\s*(.*)$/) {
			local(%dir);
			$dir{'name'} = $1;
			$dir{'value'} = $2;
			$dir{'values'} = [ split(/\s+/, $2) ];
			$dir{'line'} = $lnum;
			$dir{'index'} = scalar(@get_config_cache);
			push(@get_config_cache, \%dir);
			}
		$lnum++;
		}
	close(CONF);
	}
return \@get_config_cache;
}

# find_config(name, &config)
# Returns the structure(s) with some name
sub find_config
{
local($c, @rv);
foreach $c (@{$_[1]}) {
	if ($c->{'name'} eq $_[0]) {
		push(@rv, $c);
		}
	}
return @rv ? wantarray ? @rv : $rv[0]
           : wantarray ? () : undef;
}

# find_value(name, &config)
# Returns the value of some directive
sub find_value
{
local $rv = &find_config(@_);
return $rv ? $rv->{'value'} : undef;
}

# find_values(name, &config)
# Returns the value of some directive
sub find_values
{
local $rv = &find_config(@_);
return $rv ? $rv->{'values'} : undef;
}

# save_value(&config, name, value*)
sub save_value
{
local @v = map { { 'name' => $_[1],
		   'values' => [ $_ ] } } @_[2..@_-1];
&save_directive($_[0], $_[1], \@v);
}

# save_directive(&config, name, &values)
# Given a structure containing a directive name, type, values and members
# add, update or remove that directive in config structure and data files.
sub save_directive
{
local(@oldv, @newv, $i, $o, $n, $lref, $nl, $change);
@oldv = &find_config($_[1], $_[0]);
@newv = map { local %n = %$_; \%n } @{$_[2]};
$lref = &read_file_lines($config{'squid_conf'});
for($i=0; $i<@oldv || $i<@newv; $i++) {
	if ($i >= @oldv) {
		# a new directive is being added.. put it at the end of the file
		$nl = &directive_line($newv[$i]);
		if ($change) {
			# put it after any directives of the same type
			$newv[$i]->{'line'} = $change->{'line'}+1;
			splice(@$lref, $newv[$i]->{'line'}, 0, $nl);
			&renumber($_[0], $change->{'line'}, 1);
			splice(@{$_[0]}, &indexof($change, @{$_[0]}),
			       0, $newv[$i]);
			$change = $newv[$i];
			}
		else {
			# put it at the end of the file
			$newv[$i]->{'line'} = scalar(@$lref);
			push(@$lref, $nl);
			push(@{$_[0]}, $newv[$i]);
			}
		}
	elsif ($i >= @newv) {
		# a directive was deleted
		splice(@$lref, $oldv[$i]->{'line'}, 1);
		&renumber($_[0], $oldv[$i]->{'line'}, -1);
		splice(@{$_[0]}, &indexof($oldv[$i], @{$_[0]}), 1);
		}
	else {
		# updating some directive
		$nl = &directive_line($newv[$i]);
		splice(@$lref, $oldv[$i]->{'line'}, 1, $nl);
		$newv[$i]->{'line'} = $oldv[$i]->{'line'};
		$_[0]->[&indexof($oldv[$i], @{$_[0]})] = $newv[$i];
		$change = $newv[$i];
		}
	}
}

# directive_line(&details)
sub directive_line
{
local(@v) = @{$_[0]->{'values'}};
return $_[0]->{'name'}.(@v ? " ".join(' ',@v) : "");
}

# renumber(&directives, line, count)
# Runs through the given array of directives and increases the line numbers
# of all those greater than some line by the given count
sub renumber
{
local($d);
foreach $d (@{$_[0]}) {
	if ($d->{'line'} > $_[1]) {
		$d->{'line'} += $_[2];
		}
	}
}




1;

