#!/usr/bin/env perl
#
# Copyright 1998-2024 Luke Mewburn <luke@mewburn.net>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
#    derived from this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# 
# quotarep --
#	parse the output of a NetApp's "quota report" and highlight
#	which entries may or do exceed their maximum.
#	(default is to highlight entries within 10%)
#
#	http://www.mewburn.net/luke/src/quotarep
#

# margins can be:
#	0		any exceeded
#	0 < x < 1	less than percentage of maximum
#	>=1		absolute free value

$kmargin = 0.10;
$fmargin = 0.10;

require "getopts.pl";

$opt_a = $opt_f = $opt_k = $opt_q = undef;
&Getopts("af:k:q:") || &usage();
$fmargin = defined($opt_f) ? $opt_f : 0.10;
$kmargin = defined($opt_k) ? $opt_k : 0.10;

if ($opt_q) {
	open(NQ, $opt_q) || die("can't open $opt_q - $!\n");
	while (<NQ>) {
		chop;
		next if (/^(#|$)/);
		($id, $type, $klimit, $flimit) = split;
		$key= "${id}:${type}";
		$nklimit{$key} = &expandsuffix($klimit);
		$nflimit{$key} = &expandsuffix($flimit);
	}
	close(<NQ>);
}


print <<HdR;
                                  K-Bytes           Files
       ID     Vol     Tree     Used     Limit     Used    Limit Quota Specifier
---------  ------ -------- --------  --------  -------  ------- ----------------
HdR

while (<>) {
	if (1 .. /^-----/) {
#		if (/Volume/) {
#			$newfmt = 1;
#		}
		next;
	}
	chop;
	($type, $id, $volume, $tree, $kused, $klimit, $fused, $flimit, $spec) =
	    split;
	$key = "${id}:${type}";
	if ($opt_q) {
		$klimit = defined($nklimit{$key})
		    ? $nklimit{$key}
		    : $nklimit{"*:${type}"};
		$flimit = defined($nflimit{$key})
		    ? $nflimit{$key}
		    : $nflimit{"*:${type}"};
	}
	$koq = &overquota($kused, $klimit, $kmargin);
	$foq = &overquota($fused, $flimit, $fmargin);
	next if (! $opt_a && ! $koq && ! $foq);

	if ($type eq "tree") {
		$type = "+";
	} elsif ($type eq "user") {
		$type = "";
	} elsif ($type eq "group") {
		$type = "@";
	} else {
		die("unknown type: $type\n");
	}
	if ($id =~ /^\d+$/) {
		$id = "($id)";
	}
	$results{$key} = sprintf("%8s%1s  %6s %8s %8d %s%8d  %7d %s%7d %s",
	    $id,
	    $type,
	    $volume,
	    $tree,
	    $kused,
	    $koq ? "*" : " ",
	    $klimit,
	    $fused,
	    $foq ? "*" : " ",
	    $flimit,
	    $spec);
}

foreach $key (sort bytypeid keys %results) {
	print $results{$key}, "\n";
}

sub
bytypeid
{
	local($ai, $at) = split(/:/, $a);
	local($bi, $bt) = split(/:/, $b);

	return ($at cmp $bt || $ai <=> $bi || $ai cmp $bi);
}

sub
overquota
{
	local($cur, $max, $thres) = @_;
	if ($thres <= 0) {
		return ($max <= $cur);
	} elsif ($thres >= 1) {
		return (($max - $cur) < $thres);
	} else {
		return (($max - $cur) < ($max * $thres));
	}
}

sub
expandsuffix
{
	local($ent) = @_;
	local($amt, $suffix);

	if (($amt, $suffix) = ($ent =~ m/(\d+)(K|M|G)/)) {
		if ($suffix == "K") {
			$amt *= 1024;
		} elsif ($suffix == "M") {
			$amt *= 1048576;
		} elsif ($suffix == "G") {
			$amt *= 1073741824;
		}
	} else {
		$amt = $ent;
	}
	return ($amt);
}

sub
usage
{
	print <<UsagE;
usage: $0 [-a] [-k margin] [-f margin] [-q quotafile]  report [...]
	-a		show all users
	-k margin	threshold margin for kilobytes (default 10%)
	-f margin	threshold margin for files (default 10%)
	-q quotafile	quotafile is a quotas(5) formatted file that will
			override entries from the report to determine the
			effect of the new quotas file.
    highlight which users have exceeded their quota.
    margin `x' can be one of:
	x <= 0		usage exceeds maximum
	0 < x < 1	usage is within (x * 100)% of maximum
	x >= 1		usage is within x of maximum
UsagE
	exit 1;
}
