#! /usr/bin/perl -W
use strict;
use FileHandle;
use IO::Dir;
use File::stat;
use Fcntl ':mode';

sub get_config ()
{
    my ($file, $chip_ref) = @_;
    my ($fh, $line, $cur, $chip, $cmd, %var, $var, $rest);

    $fh = new FileHandle;
    $fh->open($file, "r");

    $cur = 0;

    while ($line = $fh->getline()) {
	chomp($line);
	$line =~ s/#.*$//;
	$line =~ s/^\s*//;
	if ($line eq "") {
	    next;
	} elsif ($line =~ /^chip/i) {
	    $cur = 0;
L1:	    while ($line =~ /"([^"]*)"/g) {
		$chip = $1;
		$chip =~ s/\*/.*/g;
		if ($$chip_ref{'name'} =~ /$chip/) {
		    $cur = 1;
		    last L1;
		}
	    }
	} elsif ($cur) {
	    $line =~ /^([^\s]+)\s+([^\s]+)(\s+(.*))?$/i;
	    $cmd = $1;
	    %var = break_varname2($2);
	    $$chip_ref{$var{'id'}}{'id'}{'name'} = $var{'name'};
	    $$chip_ref{$var{'id'}}{'id'}{'sub'} = $var{'sub'};
	    $$chip_ref{$var{'id'}}{'id'}{'num'} = $var{'num'};
	    $$chip_ref{$var{'id'}}{'id'}{'id'} = $var{'id'};
	    $rest = $4;
	    $var = $var{'id'};
	    if ($cmd eq "ignore") {
		$$chip_ref{$var}{'ignore'} = 1;
	    } elsif ($cmd eq "label") {
		$rest =~ /"([^"]+)"/i;
		$$chip_ref{$var}{'label'} = $1;
	    } elsif ($cmd eq "compute") {
		$rest =~ /^\s*([^,]+)\s*,\s*(.*)\s*$/i;
		$$chip_ref{$var}{'compute_from'} = $1;
		$$chip_ref{$var}{'compute_to'} = $2;
	    } elsif ($cmd eq "set") {
		if (defined($var{'sub'})) {
		    $$chip_ref{$var}{'subs'}{$var{'sub'}}{'force'} = $rest;
		} else {
		    $$chip_ref{$var}{'force'} = $rest;
		}
	    } elsif ($cmd eq "alarm_bit") {
		if (defined($var{'sub'})) {
		    $$chip_ref{$var}{'subs'}{$var{'sub'}}{'alarm_bit'} = $rest;
		} else {
		    $$chip_ref{$var}{'alarm_bit'} = $rest;
		}
	    } else {
		print("Line: -$line-\n");
	    }
	}
    }
}

sub print_mess ()
{
    my ($pre, $mess) = @_;
    my ($t1, $t2);

    $t1 = ref($mess);
    if (!$t1) {
	print($mess . "\n");
    } elsif ($t1 eq "ARRAY") {
	print("[\n");
	foreach $t2 (@{$mess}) {
	    print($pre . "= ");
	    &print_mess($pre . "\t", $t2);
	}
	print($pre . "]\n");
    } elsif ($t1 eq "HASH") {
	print("{\n");
	foreach $t2 (sort(keys(%{$mess}))) {
	    print("$pre$t2 = ");
	    &print_mess($pre . "\t", $$mess{$t2});
	}
	print($pre . "}\n");
    }
}

sub break_varname ()
{
    my ($varname) = @_;
    my (%out);

    if ($varname =~ /div/) {
	$varname =~ /^([^0-9]*)([0-9]*)$/;
	$out{'name'} = $1;
	$out{'num'} = $2;
    } else {
	$varname =~ /^([^_0-9]*)(_([^0-9]*))?([0-9]*)?$/;
	$out{'name'} = $1;
	$out{'sub'} = $3 if (defined($3) && $3 ne "input");
	$out{'num'} = $4;
    }
    if ($out{'name'} eq 'temp' && defined($out{'sub'})) {
	if ($out{'sub'} eq 'over') {
	    $out{'sub'} = 'max';
	} elsif ($out{'sub'} eq 'hyst') {
	    $out{'sub'} = 'min';
	}
    }
    $out{'id'} = $out{'name'};
    $out{'id'} .= $out{'num'} if (defined($out{'num'}));
    return %out;
}

sub break_varname2 ()
{
    my ($varname) = @_;
    my (%out);

    if ($varname =~ /div/) {
	$varname =~ /^([^0-9]*)([0-9]*)_div$/;
	$out{'name'} = $1 . '_div';
	$out{'num'} = $2;
    } else {
	$varname =~ /^([^_0-9]*)([0-9]*)?(_([^0-9]*))?$/;
	$out{'name'} = $1;
	$out{'sub'} = $4 if (defined($4) && $4 ne "input");
	$out{'num'} = $2;
    }
    if ($out{'name'} eq 'temp' && defined($out{'sub'})) {
	if ($out{'sub'} eq 'over') {
	    $out{'sub'} = 'max';
	} elsif ($out{'sub'} eq 'hyst') {
	    $out{'sub'} = 'min';
	}
    }
    $out{'id'} = $out{'name'};
    $out{'id'} .= $out{'num'} if (defined($out{'num'}));
    return %out;
}

sub do_expression ()
{
    my ($expr, $in) = @_;
    $expr =~ s/@/$in/g;
    return eval($expr);
}

sub sensor_dir ()
{
    my ($dir_name, $bus, $id) = @_;
    my (%chip, $dir, $fh, $file, $line, $value, %var);

    $fh = new FileHandle;
    $fh->open($dir_name . '/name', "r");
    $line = $fh->getline(); chomp($line);
    $fh->close;
    $chip{'name'} = "$line-$bus-$id";

    $dir = new IO::Dir $dir_name;
    while (defined($file = $dir->read())) {
	next if ($file eq ".");
	next if ($file eq "..");
	next if ($file eq "name");
	next if ($file eq "power");
	%var = &break_varname($file);
	$fh = new FileHandle;
	$fh->open($dir_name . '/' . $file);
	$value = $fh->getline(); chomp($value);
	$fh->close();

	if ($var{'name'} eq "in") {
	    $value /= 1000;
	}
	if ($var{'name'} eq "temp") {
	    $value /= 1000;
	}
	
	$chip{$var{'id'}}{'id'}{'name'} = $var{'name'};
	$chip{$var{'id'}}{'id'}{'sub'} = $var{'sub'};
	$chip{$var{'id'}}{'id'}{'num'} = $var{'num'};
	$chip{$var{'id'}}{'id'}{'id'} = $var{'id'};
	if (defined($var{'sub'})) {
	    $chip{$var{'id'}}{'subs'}{$var{'sub'}}{'value'} = $value;
	} else {
	    $chip{$var{'id'}}{'value'} = $value;
	}
    }

    &get_config("/etc/sensors.conf", \%chip);

    &print_sensor(\%chip);

    if (defined($ARGV[0]) && $ARGV[0] eq '-s') {
	&set_sensor($dir_name, \%chip);
    }
}

sub set_sensor ()
{
    my ($dir_name, $sensors) = @_;
    my ($key, $key2, $ref, $t1, $sen, $sub, $val, $file, $fh);

    foreach $key (sort(keys(%{$sensors}))) {
	if (($ref = ref($$sensors{$key}))) {
	    $sen = $$sensors{$key};
	    next if ($$sen{'ignore'});
	    if (defined($$sen{'force'})) {
		$file = $dir_name . '/' . $$sen{'id'}{'name'};
		$file .= $$sen{'id'}{'num'} if (defined($$sen{'id'}{'num'}));
		$fh = new FileHandle;
		die "$file" if (!$fh->open($file, "w"));

		if (defined($$sen{'compute_to'})) {
		    $val =&do_expression($$sen{'compute_to'}, $$sen{'force'});
		} else {
		    $val = &do_expression($$sen{'force'}, 0);
		}
		$val *= 1000 if ($$sen{'id'}{'name'} eq 'in');
		$val *= 1000 if ($$sen{'id'}{'name'} eq 'temp');
		$fh->print("$val\n");
		$fh->close();
	    }
	    if (defined($$sen{'subs'})) {
		for $key2 (keys(%{$$sen{'subs'}})) {
		    $sub = $$sen{'subs'}{$key2};
		    next if (!defined($$sub{'force'}));
		    $file = $dir_name . '/' . $$sen{'id'}{'name'};
		    $file .= '_' . $key2;
		    $file .=$$sen{'id'}{'num'} if (defined($$sen{'id'}{'num'}));
		    $fh = new FileHandle;
		    if (!$fh->open($file, "w")) {
			&print_mess("", $sen);
			print("$file not found!\n");
			next;
		    }
		    if (defined($$sen{'compute_to'})) {
			$val=&do_expression($$sen{'compute_to'},$$sub{'force'});
		    } else {
			$val = &do_expression($$sub{'force'}, 0);
		    }
		    $val *= 1000 if ($$sen{'id'}{'name'} eq 'in');
		    $val *= 1000 if ($$sen{'id'}{'name'} eq 'temp');
		    $fh->print("$val\n");
		    $fh->close();
		}
	    }
	}
    }
}

sub print_sensor ()
{
    my ($sensors) = @_;
    my ($key, $key2, $ref, $t1, $sen, $sub, $val, $name);

    print("NAME : " . $$sensors{'name'} . "\n");
    foreach $key (sort(keys(%{$sensors}))) {
	if (($ref = ref($$sensors{$key}))) {
	    $sen = $$sensors{$key};
	    next if ($$sen{'ignore'});
	    if (defined($$sen{'label'})) {
		$name = $$sen{'label'};
	    } else {
		$name = $key;
	    }
	    if (defined($$sen{'value'})) {
		if (defined($$sen{'compute_from'})) {
		    $val =&do_expression($$sen{'compute_from'}, $$sen{'value'});
		} else {
		    $val = $$sen{'value'};
		}
		printf("%12s : %-+5g", $name, $val);
		if (defined($$sen{'alarm_bit'}) && $$sensors{'alarm'}{'value'} & (1 << $$sen{'alarm_bit'})) {
		    printf("!");
		}
	    }
	    if (defined($$sen{'subs'})) {
		for $key2 (keys(%{$$sen{'subs'}})) {
		    $sub = $$sen{'subs'}{$key2};
		    next if (!defined($$sub{'value'}));
		    if (defined($$sen{'compute_from'})) {
			$val =&do_expression($$sen{'compute_from'}, $$sub{'value'});
		    } else {
			$val = $$sub{'value'};
		    }
		    printf("\t$key2 : %-+5g", $val);
		    if (defined($$sub{'alarm_bit'}) && $$sensors{'alarm'}{'value'} & (1 << $$sub{'alarm_bit'})) {
			printf("!");
		    }
		}
	    }
	    print("\n");
	}
    }
}

sub find_sensors ()
{
    my ($d0, $d1, $f0, $f1, $f2, $fh, $bus, $found, $name, $b0, $b1, $b2);

    $b0 = '/sys/devices/legacy';
    if (!-d $b0) {
	die "No sensors found.\n";
    }

    $d0 = new IO::Dir $b0;
    $found = 0;
    while(defined($f0 = $d0->read())) {
	next if (($f0 eq ".") || ($f0 eq ".."));
	$b1 = $b0 . '/' . $f0;
	if (-d $b1) {
	    $fh = new FileHandle ($b1 . '/name');
	    $name = $fh->getline;
	    $fh->close;
	    chomp($name);
	    if ($name eq "ISA main adapter") {
		$bus = "isa";
	    } else {
		$bus = "i2c";
	    }

	    $d1 = new IO::Dir ($b1);
	    while(defined($f1 = $d1->read())) {
		next if (($f1 eq ".") || ($f1 eq ".."));
		$b2 = $b1 . '/' . $f1;
		if (-d $b2) {
		    &sensor_dir($b2, $bus, $f1);
		    $found++;
		}
	    }
	}
    }
}

#&sensor_dir($ARGV[0], $ARGV[1], $ARGV[2]);
&find_sensors();

