#! /usr/local/bin/perl -- 

# This program is designed to discover under and overallocated disk
# space.  To discover what disks are available, it uses the program
# format, and the files [v]fstab and m[n]tab.  It gathers mount point
# information from [v]fstab and m[n]tab, and determines disk geometries
# using the program dkinfo/prtvtoc.  It also finds out if it's the cache
# for an optical filesystem, or part of a metadevice.

# Some assumptions made are:
#     -	an unmentioned "b" partition on the same disk as the root
#	partition is used for swap space.
#     -	the "c" partition should be the entire disk

    #MB = #SECTORS / 2048
    #SECTORS = #cyls * #SEC_PER_CYL
    #SEC_PER_CYL = #sec_per_track * #heads_per_cyl

# Warning: This currently does not print warning if partition is more
# than one of:
#   mounted locally
#   part of a metadevice
#   an optical disk cache
# it should. (daw, 5/11/95)

sub parseLine {
# parameters: line, cyl, sec, hd
local($xxx) = @_;
local($i);
@NAME = split(' ',$xxx);
for ($i=0;$i<=$#NAME;$i++) {
#	print "   ",$i," ",$NAME[$i],"\n";
	pname: {
		if ($NAME[$i] eq "cyl") {
			$_[1] = $NAME[$i+1];
#			print "cyl ",$_[1],"\n";;
			last pname;
			}
		if ($NAME[$i] eq "sec") {
			$_[2] = $NAME[$i+1];
#			print "sec ",$_[2],"\n";;
			last pname;
			}
		if ($NAME[$i] eq "hd") {
			$_[3] = $NAME[$i+1];
#			print "hd ",$_[3],"\n";;
			last pname;
			}
		}
	}
}

sub tryFormat {
open(FORMAT, "$FORMAT < /dev/null 2>&1 |") || die "can't run '$FORMAT': $!\n";
while (<FORMAT>) {
	@LINE = split;
#	$len = length($LINE[1]);
#	if (substr($LINE[1],$len-1) eq ":") {
    if (index($_,"<") > 0) {
	if ($OSTYPE eq "SunOS") {
	    $len = length($LINE[1]);
	    $devices[$ndev++] = substr($LINE[1],0,$len-1);
	}
	elsif ($OSTYPE eq "Solaris") {
#	    print;
#	    if ($LINE[0] eq "") { # perl4
#		$devices[$ndev++] = $LINE[2];
#print "debug1a: .$LINE[0]. $devices[$ndev-1]\n";
#	    }
#	    else {		# perl5 (difference in split?)
#		$devices[$ndev++] = $LINE[1];
#print "debug1b: .$LINE[0]. $devices[$ndev-1]\n";
#	    }
	    if ($LINE[0] eq "") {
		shift(LINE);	# perl5 does not give empty first arg
	    }
	    $devices[$ndev++] = $LINE[1];
#print "debug1b: .$LINE[0]. $devices[$ndev-1]\n";
	}
		$il = index($_,"<");
		$ir = index($_,">");
#		print $il," ",$ir,"\n";
#		print substr($_,$il+1,$ir-$il-1),"\n";
		$names[$ndev-1] = substr($_,index($_,"<"));
		$names[$ndev-1] = substr($_,$il+1,$ir-$il-1);
#	print "$names[$ndev-1]\n";
#		print $LINE[$#LINE];
#		print $names[$ndev-1];
		$cyl[$ndev-1] = "";
		$sec[$ndev-1] = "";
		$hd[$ndev-1] = "";
		do parseLine($names[$ndev-1],$cyl[$ndev-1],$sec[$ndev-1],$hd[$ndev-1]);
#	print "cyl = $cyl[$ndev-1];sec = $sec[$ndev-1];hd = $hd[$ndev-1]\n";
		}
	}
close(FORMAT);
}

sub tryRfutil {
open(RFUTIL, "/usr/bin/echo s q | /sbin/rfutil $_[0] 2>&1 |") || die "can't run '/sbin/rfutil': $!\n";
while (<RFUTIL>) {
	if (substr($_,0,1) eq "<") {
#		print($_);
		$line = substr($_,1,length($_)-3);
		}
	}
close(RFUTIL);
print $line,"\n";
$cyl[$cur] = "";
$sec[$cur] = "";
$hd[$cur] = "";
do parseLine($line,$cyl[$cur],$sec[$cur],$hd[$cur]);
#print $cur," ",$cyl[$cur]," ",$sec[$cur]," ",$hd[$cur],"\n";
}

sub checkFstab {
open(FSTAB,$FSTAB);
while (<FSTAB>) {
	@LINE = split;
	if ((index($LINE[0],"/dev/") == 0) && (($LINE[2] eq "4.2") || $LINE[2] eq "swap")) { # SunOS
		$len = length($LINE[0]);
		$dev = substr($LINE[0],5,$len-6);
		$idx = $dev.substr($LINE[0],$len-1);
		$used{$idx} = 1;
		if ($LINE[2] eq "4.2") {
			$fsmpt{$idx} = $LINE[1];
			}
		else	{
			$fsmpt{$idx} = "swap";
			}
		if ($LINE[1] eq "/") {
			$dswap = $dev.$B;
			}
		for ($i=0;$i<$ndev;) {
			if ($devices[$i++] eq $dev) { last }
			if ($i == $ndev) {
				$devices[$ndev++] = $dev;
				last;
				}
			}
	}
	if ((index($LINE[0],"/dev/") == 0) && (index($LINE[0],"/dsk/") == 4) && (($LINE[3] eq "ufs") || $LINE[3] eq "swap")) { # Solaris
		$len = length($LINE[0]);
		$dev = substr($LINE[0],9,$len-11);
#		$idx = $dev.substr($LINE[0],$len-1);
	    $idx = substr($LINE[0],9);
		$used{$idx} = 1;
		if ($LINE[3] eq "ufs") {
			$fsmpt{$idx} = $LINE[2];
			}
		else	{
			$fsmpt{$idx} = "swap";
			}
		if ($LINE[2] eq "/") {
			$dswap = $dev."s1";
			}
		for ($i=0;$i<$ndev;) {
			if ($devices[$i++] eq $dev) { last }
			if ($i == $ndev) {
				$devices[$ndev++] = $dev;
print "debug2: $devices[$ndev-1]\n";
				last;
				}
			}
	    }

	if ((index($LINE[0],"/dev/") == 0) && (index($LINE[0],"/md/") == 4) && (($LINE[3] eq "ufs") || $LINE[3] eq "swap")) { # Solaris
#	    print;
	    $idx = substr($LINE[0],12);
#	    print "idx = $idx\n";
	    $used{$idx} = 1;
	    $fsmpt{$idx} = $LINE[2];
	}

    }
close(FSTAB);
}

sub lookInMtab {
open(MTAB,$MTAB);
while (<MTAB>) {
	@LINE = split;
	if ((index($LINE[0],":") == -1) && $LINE[2] eq "4.2") {	# SunOS
		$len = length($LINE[0]);
		$dev = substr($LINE[0],5,$len-6);
		$idx = $dev.substr($LINE[0],$len-1);
		$used{$idx} = 1;
		$mtmpt{$idx} = $LINE[1];
		for ($i=0;$i<$ndev;) {
			if ($devices[$i++] eq $dev) { last }
			if ($i == $ndev) {
				$devices[$ndev++] = $dev;
				last;
				}
			}
		}
	if ($LINE[2] eq "mcfs") { # SunOS Optical
	    $mtmpt{$LINE[0]} = $LINE[1];
#	    print "mtmpt{$LINE[0]} = $mtmpt{$LINE[0]}\n";
	}
	if ((index($LINE[0],":") == -1) && (index($LINE[0],"/dsk/") == 4) && $LINE[2] eq "ufs") {	# Solaris

		$len = length($LINE[0]);
		$dev = substr($LINE[0],9,$len-11);
#		$idx = $dev.substr($LINE[0],$len-1);
		$idx = substr($LINE[0],9);
		$used{$idx} = 1;
		$mtmpt{$idx} = $LINE[1];
		for ($i=0;$i<$ndev;) {
			if ($devices[$i++] eq $dev) { last }
			if ($i == $ndev) {
				$devices[$ndev++] = $dev;
print "debug3: $devices[$ndev-1]\n";
				last;
				}
			}
	    }
	if ((index($LINE[0],"/dev/") == 0) && (index($LINE[0],"/md/") == 4) && (($LINE[2] eq "ufs") || $LINE[3] eq "swap")) { # Solaris
#	    print;
	    $idx = substr($LINE[0],12);
#	    print "idx = $idx\n";
	    $used{$idx} = 1;
	    $mtmpt{$idx} = $LINE[1];
#	    print "mtmpt{$idx} = $mtmpt{$idx}\n"
	}
    }
close(MTAB);
}

sub markSwap {
if ($cyls{$dswap}    && ! $fsmpt{$dswap}
    && ! $mtmpt{$dswap} ) {
	$used{$dswap} = 1;
	}
}

sub getPartitioning {
    if ($OSTYPE eq "SunOS") {
  for ($cur=0;$cur<$ndev;$cur++) {
#    print "$devices[$cur]\n";
	open(DKINFO, "/etc/dkinfo $devices[$cur] 2>&1 |") || die "can't run '/etc/dkinfo': $!\n";
	while (<DKINFO>) {
		@LINE = split;
		if ((substr($LINE[0],1) eq ":") && ($LINE[1] ne "No")) {
			$curp = substr($LINE[0],0,1);
			$idx = $devices[$cur].$curp;
			$cyls{$idx} = substr($LINE[3],1);
			$unacc{$idx} = $cyls{$idx};
			if ($LINE[4] ne "cyls)") {
# can't handle disks with partial cylinder allocations
				die "dkinfo output for $devices[$cur]: $_\n";
				}
			}
		if ($LINE[1] eq "cylinders") {
			$size{$devices[$cur]} = $LINE[0];
			}
		if (($LINE[1] eq "starting") && ($curp ne "")) {
			$start{$idx} = $LINE[3];
			$end{$idx} = $start{$idx} + $cyls{$idx};
			}
		}
	close(DKINFO);
	}
  }
elsif ($OSTYPE eq "Solaris") {
  for ($cur=0;$cur<$ndev;$cur++) {
#    print "$devices[$cur]\n";
	open(PRTVTOC, "/etc/prtvtoc /dev/rdsk/$devices[$cur]s2 2>&1 |") || die "can't run '/etc/prtvtoc': $!\n";
	while (<PRTVTOC>) {
#	    print;
		@LINE = split;
	    if ($LINE[0] eq "") {
		shift(LINE);	# perl5 does not give empty first arg
	    }
	    if ($LINE[2] eq "accessible") {
#		print;
			$size{$devices[$cur]} = $LINE[1];
#		print "size = $size{$devices[$cur]}\n";
	    }

	    if ($LINE[2] eq "sectors/cylinder") {
#		print;
		$spc = $LINE[1];
#		print "spc = $spc\n";
	    }

	    if ($LINE[1] eq "Partition") {
#		print
	    }
	    if ($LINE[0] eq "prtvtoc:") {
		print STDERR
#		print
	    }
	    if (($LINE[0] ne "*") && ($LINE[0] ne "prtvtoc:")) {
#		print;
		$curp = "s".$LINE[0];
#		print "curp = $curp\n";
		$idx = $devices[$cur].$curp;
#		print "idx = $idx\n";
		$cyls{$idx} = $LINE[4] / $spc;
#		print "cyls = $cyls{$idx}\n";
		$unacc{$idx} = $cyls{$idx};
		if ($LINE[4] != ($cyls{$idx} * $spc)) {
#print "$LINE[4] != ($cyls{$idx} * $spc)\n";
		    die "$idx not whole number of cylinders\n";
		}
		$start{$idx} = $LINE[3] / $spc;
		$end{$idx} = $start{$idx} + $cyls{$idx};
#		print "start = $start{$idx}; end = $end{$idx}\n";
	    }
		}
	close(PRTVTOC);
	}
  }
}

sub checkOs {
    open(UNAME, "/bin/uname -s -r 2>&1 |") || die "can't run '/bin/uname': $!\n";
    $OS = <UNAME>;
    chop($OS);
    if (index($OS,"SunOS 4") == 0) { $OSTYPE = "SunOS" }
    elsif(index($OS,"SunOS 5") == 0) { $OSTYPE = "Solaris"}
    else { $OSTYPE = "unknown" }
    close(UNAME);
}

sub checkOptical {
    open(VLLSSET, "/usr/local/etc/vllsset 2>&1 |") || die "can't run '/usr/local/etc/vllsset': $!\n";
    while (<VLLSSET>) {
	@LINE = split;
	if ($#LINE > 0) {
#	    print "$LINE[1]\n";
	    $oset[$nset++]=$LINE[1];
	}
    }
    close(VLLSSET);
    for ($set=0;$set<$nset;$set++) {
#	print "$oset[$set]\n";




	open(VLSETEDIT, "/usr/local/etc/vlsetedit -l $oset[$set] 2>&1 |") || die "can't run '/usr/local/etc/vlsetedit': $!\n";
	while (<VLSETEDIT>) {
	    @LINE = split;
#	    print;
	    if ($LINE[0] eq "cfsmount") {
#		print "$LINE[3]\n";
		$idx = substr($LINE[3],5);
#		print "$idx\n";
		$used{$idx} = 1;
		$cmpt{$idx} = $oset[$set];
#		print "cmpt{$idx} = $cmpt{$idx}\n";
	    }
	}
	close(VLSETEDIT);
    }
}

sub checkMeta {
    open(METASTAT, "/usr/opt/SUNWmd/sbin/metastat -p 2>&1 |") || die "can't run '/usr/opt/SUNWmd/sbin/metastat': $!\n";
    while (<METASTAT>) {
#	print;
	@LINE = split;
	if ($remainingstripes > 0) {
#	    print " line is continuation of a two stripe metadevice\n";
	    for ($i=0;$i<$LINE[0];$i++) {
#		print " $LINE[$i+1]\n";
		$idx = $LINE[$i+1];
#		print " idx = $idx\n";
		$md{$idx} = $omd;
#		print " metadevice for $idx is $md{$idx}\n";
	    }
	    if ($LINE[$#LINE-1] eq "-h") {
		if ($hs{$LINE[$#LINE]}) {
		    $hs{$LINE[$#LINE]} .= ", " . $omd;
		}
		else {
		    $hs{$LINE[$#LINE]} = $omd;
		}
#		print " $LINE[$#LINE] is hot spare for $hs{$LINE[$#LINE]}\n";
	    }
	    $remainingstripes = 0;
	}
	elsif ($LINE[1] eq "1") {
#	    print " line is a one stripe metadevice\n";
	    for ($i=0;$i<$LINE[2];$i++) {
#		print " $LINE[$i+3]\n";
#		$idx = substr($LINE[$i+3],9);
		$idx = $LINE[$i+3];
#		print " idx = $idx\n";
		$md{$idx} = $LINE[0];
#		print " metadevice for $idx is $md{$idx}\n";
		}
	    if ($LINE[$#LINE-1] eq "-h") {
		if ($hs{$LINE[$#LINE]}) {
		    $hs{$LINE[$#LINE]} .= ", " . $LINE[0];
		}
		else {
		    $hs{$LINE[$#LINE]} = $LINE[0];
		}
#		print " $LINE[$#LINE] is hot spare for $hs{$LINE[$#LINE]}\n";
	    }
	}
	elsif ($LINE[1] eq "2") {
#	    print " line is a two stripe metadevice\n";
	    for ($i=0;$i<$LINE[2];$i++) {
#		print " $LINE[$i+3]\n";
#		$idx = substr($LINE[$i+3],9);
# I just uncommented the two uncommented lines below
		$idx = $LINE[$i+3];
#		print " idx = $idx\n";
		$md{$idx} = $LINE[0];
#		print " metadevice for $idx is $md{$idx}\n";
	    }
	    $remainingstripes = 1;
	    $omd = $LINE[0];
	}
	elsif ($LINE[1] eq "-r") {
#	    print " line is a RAID metadevice\n";
	    $seensw = 0;
	    for ($i=2;$i<=$#LINE;$i++) {
#		print " $LINE[$i]\n";
		if (substr($LINE[$i],0,1) eq "-") { $seensw = 1 }
		if ($seensw == 0) {
		    $idx = $LINE[$i];
#		    print " idx = $idx\n";
		    $md{$idx} = $LINE[0];
#		    print " metadevice for $idx is $md{$idx}\n";
		}
	    }
	}
	elsif (substr($LINE[0],0,3) eq "hsp") {
#	    print " line is a hot spare metadevice\n";
	    for ($i=1;$i<=$#LINE;$i++) {
#		print " $LINE[$i]\n";
		$idx = substr($LINE[$i],9);
		$idx = $LINE[$i];
#		print " idx = $idx\n";
		$md{$idx} = $LINE[0];
#		print " metadevice for $idx is $md{$idx}\n";
	    }
	}
	elsif ($LINE[1] eq "-m") {
#	    print " line is a metamirror\n";
#	    if ($#LINE != 3) {
#		die " $0: do not understand metamirror '$_' from metastat\n";
#	    }
#	    $mm{$LINE[2]} = $LINE[0];
	    for ($i=2;$i<=$#LINE;$i++) {
		    $mm{$LINE[$i]} = $LINE[0];
	    }
#	    print " metamirror for $LINE[2] is $mm{$LINE[2]}\n";
	}
	elsif ($LINE[1] eq "-t") {
#	    print " line is a metatrans\n";
#	    if ($#LINE != 2) {
	    if ($#LINE > 3) {	# number 3 could be logging device
		die " $0: do not understand metatrans '$_' from metastat\n";
	    }
	    $mt{$LINE[2]} = $LINE[0];
#	    print " metatrans for $LINE[2] is $mt{$LINE[2]}\n";
	    if ($mtl{$LINE[3]}) {
		    $mtl{$LINE[3]} = $mtl{$LINE[3]}.", ".$LINE[0];
	    }
	    else {
		    $mtl{$LINE[3]} = $LINE[0];
	    }
#	    print " metatrans log for $LINE[3] is $mtl{$LINE[3]}\n";
	}
	else {
	    chop;
	    die " $0: do not understand '$_' from metastat\n";
	}
    }
    close(METASTAT);
}

# Here we go:

&checkOs;
if ($OSTYPE eq "unknown") {die "$0: can't run on $OS\n";}
if ($OSTYPE eq "SunOS") {
    $FORMAT = "/usr/etc/format";
    $FSTAB = "/etc/fstab";
    $MTAB = "/etc/mtab";
    $A = "a"; $B = "b"; $C = "c"; $D = "d";
    $E = "e"; $F = "f"; $G = "g"; $H = "h";
}
if ($OSTYPE eq "Solaris") {
#    warn "$0: under construction for $OS\n";
    $FORMAT = "/etc/format";
    $FSTAB = "/etc/vfstab";
    $MTAB = "/etc/mnttab";
    $A = "s0"; $B = "s1"; $C = "s2"; $D = "s3";
    $E = "s4"; $F = "s5"; $G = "s6"; $H = "s7";
}

# See what structures are around...

# First, try format:

&tryFormat;

# Now check fstab.  (format doesn't know about rf's and may not see
# everthing else if format.dat is not up-to-date)

&checkFstab;

# look in mtab to find differences from fstab's impression and unknown
# disks not in fstab yet.

&lookInMtab;

# unused b partition on disk with root is default swap (and therefore
# used)

&markSwap;

# if optical disk software exists, check for cache partitions

if (-e "/usr/local/etc/vllsset") {
    &checkOptical;
}

# if metadisk software exists, check for metadisk partitions

if (-e "/usr/opt/SUNWmd/sbin/metastat") {
    &checkMeta;
}

# use dkinfo (or prtvtoc) to get disk partitioning and size

&getPartitioning;

# Here's the main analysis/output loop:

$cur=0;

while ($cur<$ndev) {
	$dev = $devices[$cur];
	if ($devprinted++) {
		print "\n";
		}
	print "$dev:";
	if ($names[$cur]) {
		print "	$names[$cur]\n";
		}
	else	{
# if we don't have label and rf, ask rfutil
		if (substr($dev,0,2) eq "rf") {
			print "	";
			$tmp = "/dev/r".$dev.$C;
			$names[$cur] = "";
			$cyl[$cur] = "";
			$sec[$cur] = "";
			$hd[$cur] = "";
			do tryRfutil($tmp);
			}
		else	{
			print "   [Format doesn't see this disk]\n";
			}
		}

# verify c partition is whole disk

	if (($start{$dev.$C} != 0) || ($cyls{$dev.$C} != $size{$dev})) {
		print "	*** c partition is not whole device ***\n";
		print " dev = $dev; start = $start{$dev.$C}; cyls = $cyls{$dev.$C}; size = $size{$dev}\n";
		}

	print "\n";

# disk type printed, now display all allocated space

	$bot = 0;
	$top = $size{$dev};
# note whether c partition is actually in use
	if ($fsmpt{$dev.$C} || $mtmpt{$dev.$C} || $md{$dev.$C}) {
#	    print " debug: $dev$C is used\n";
		$cpartused = 1
		}
	else	{
		$cpartused = 0;
#		print " debug: $dev$C is not used\n";
		}
# add up all allocated, unaccounted for cylinders
	$sum = 0;
	foreach $p ($A,$B,$C,$D,$E,$F,$G,$H) {
		$sum += $unacc{$dev.$p}
		}
# and account for all space on disk, and all allocated cylinders
	while(($bot < $top) || ($sum != 0)) {
		$idx = "";
# find a non-null partition beginning at bottom of remaining space
		foreach $p ($A,$B,$C,$D,$E,$F,$G,$H) {
			if ($unacc{$dev.$p} == 0) { next }
			if ($start{$dev.$p} <= $bot) {
				$idx = $dev.$p;
				$curp = $p;
				last;
				}
			}
		if ($idx eq "") {
# didn't find one.  display an unallocated "partition"
			$nbot = $top;
			foreach $p ($A,$B,$C,$D,$E,$F,$G,$H) {
				if ($unacc{$dev.$p} == 0) { next }
				if ($start{$dev.$p} < $nbot) {
					$nbot = $start{$dev.$p};
					}
				}
			print "  $dev?";
			printf "	%4d	%4d	[unallocated]\n",$bot,$nbot-$bot;
			$bot = $nbot;
# I suspect this is not necessary:
#			$sum = 0;
#			foreach $p ("a","b","c","d","e","f","g","h") {
#				$sum += $unacc{$dev.$p}
#				}
			next;
			}
# display next partition, starting cylinder and size
		print "  $idx";
		printf "  %4d  %4d",$start{$idx},$cyls{$idx};
# how big it is
		if ($cyl[$cur]) {
			$sec_per_cyl = $sec[$cur] * $hd[$cur];
			$sec = $cyls{$idx} * $sec_per_cyl;
			$nmb = $sec / 2048;
#print " ",$sec[$cur]," * ",$hd[$cur]," * ",$cyls{$idx}," / 2048 ";
			printf "  %4dMB",$nmb;
			}
# where it's mounted
			if ($fsmpt{$idx} eq $mtmpt{$idx}) {
				print "  $fsmpt{$idx}";
				if ($fsmpt{$idx} eq "") {
				    if ($md{$idx} ne "") {
					if ($mt{$md{$idx}} ne "") {
					    print "(part of mtrans $mt{$md{$idx}}) ";
#					    $fsmpt{$mt{$md{$idx}}} = "X";
					    if ($fsmpt{$mt{$md{$idx}}} eq $mtmpt{$mt{$md{$idx}}}) {
						print "$fsmpt{$mt{$md{$idx}}}"
					    }
					    else {
						print "	(";
						if ($fsmpt{$mt{$md{$idx}}}) {
						    print "should be mounted on $fsmpt{$mt{$md{$idx}}}";
						}
						if ($fsmpt{$mt{$md{$idx}}} && $mtmpt{$mt{$md{$idx}}}) {
						    print ", ";
						}
						if ($mtmpt{$mt{$md{$idx}}}) {
						    print "mounted on $mtmpt{$mt{$md{$idx}}}";
						}
						print ")";
					    }
					}
					elsif ($mm{$md{$idx}} ne "") {
					    print "(sub of mmirror $mm{$md{$idx}}) ";
					    if ($mtl{$mm{$md{$idx}}}) {
						print "(logging $mtl{$mm{$md{$idx}}})";
					    }
					    else {
						print "(part of mtrans $mt{$mm{$md{$idx}}}) ";
					    if ($fsmpt{$mt{$mm{$md{$idx}}}} eq $mtmpt{$mt{$mm{$md{$idx}}}}) {
						print "$fsmpt{$mt{$mm{$md{$idx}}}}"
					    }
					    else {
						print "	(";
						if ($fsmpt{$mt{$mm{$md{$idx}}}}) {
						    print "should be mounted on $fsmpt{$mt{$mm{$md{$idx}}}}";
						}
						if ($fsmpt{$mt{$mm{$md{$idx}}}} && $mtmpt{$mt{$mm{$md{$idx}}}}) {
						    print ", ";
						}
						if ($mtmpt{$mt{$mm{$md{$idx}}}}) {
						    print "mounted on $mtmpt{$mt{$mm{$md{$idx}}}}";
						}
						print ")";
					    }
					    }
					}
					else {
					    if ($hs{$md{$idx}}) {
						print "(hspare for $hs{$md{$idx}})";
					    }
					    else {
						print "(part of mdevice $md{$idx}) [unused]";
					    }
					}
				    }
				    elsif ($cmpt{$idx}) {
					print "(cache for $cmpt{$idx}";
					if ($mtmpt{$cmpt{$idx}}) {
					    print " which is mounted on $mtmpt{$cmpt{$idx}}";
					}
					print ")";
				    }
				    elsif ($idx eq $dswap) {
						print "swap";
						}
				    elsif ($curp ne $C) {
						print "[unused]";
					    }
				}
			    }
			elsif ($fsmpt{$idx} eq "swap") {
				print "   swap";
				}
			else	{
# show discrepancy between fstab and mtab
				print "	(";
				if ($fsmpt{$idx}) {
					print "should be mounted on $fsmpt{$idx}";
					}
				if ($fsmpt{$idx} && $mtmpt{$idx}) {
					print ", ";
					}
				if ($mtmpt{$idx}) {
					print "mounted on $mtmpt{$idx}";
					}
				print ")";
				}
# now determine any overlaps between partitions
		$overlap = "";
		$danger = "";
		foreach $p ($A,$B,$C,$D,$E,$F,$G,$H) {
# null partitions don't overlap
			if ($cyls{$dev.$p} == 0) { next }
# don't check against self
			if ($idx eq $dev.$p) { next }
#  or unused c partition
			if ($idx eq $dev.$C && ! $cpartused) { next }
			$tidx = $dev.$p;
			if (
				($start{$idx} == $start{$tidx})
				|| (($start{$idx} < $start{$tidx}) && ($end{$idx} > $start{$tidx}))
				|| (($start{$idx} > $start{$tidx}) && ($start{$idx} < $end{$tidx}))
					) {
					if ($p ne $C || $cpartused) {
						if ($overlap) {
							$overlap .= ",";
							}
						$overlap .= " ".$p;
# if both overlapping partitions are in use, draw attention to it!!!
						if ($used{$idx} && $used{$tidx}) {
							$danger = " [DANGER!]";
							}
						}
					}
			}
		if ($overlap) {
			print "	overlaps$overlap $danger";
			}
		print "\n";
# adjust bottom of accounted for disk space if not unused c partition
		if ($idx ne $dev.$C || $cpartused) {
			$bot = $start{$idx} + $unacc{$idx};
			}
# we've now accounted for this structure
		$unacc{$idx} = 0;
# sum up unaccounted for partitions
		$sum = 0;
		foreach $p ($A,$B,$C,$D,$E,$F,$G,$H) {
			$sum += $unacc{$dev.$p}
			}
# loop back until done
		}
# now on to the next disk...
	$cur++;
	}
