#!mkcmd
# $Id: binpack.m,v 1.16 2012/07/24 23:51:07 ksb Exp $
from '<stdio.h>'
from '<stdlib.h>'
from '<sysexits.h>'
from '<limits.h>'
from '"machine.h"'
from '"config.h"'
from '"binpack.h"'

require "std_help.m" "std_version.m"
getenv "BINPACK"
basename "binpack" ""

augment action 'V' {
	user "ShowTable(stdout);"
}

%i
static char rcsid[] =
	"$Id: binpack.m,v 1.16 2012/07/24 23:51:07 ksb Exp $";
%%

type "bytes" 'b' {
	named "bsize" "pcBinSize"
	track "fGaveB"
	param "bytes"
	init '"1440k"'
	help "specify the bin size (default %qi, use ? for help)"
}

boolean 'u' {
	named "fUserSize"
	exclude 'roNE'
	help "user provides each item size as column 1 of the input list"
}
boolean 'N' {
	named "fLiteral"
	help "use the name of the file as the size, not number of bytes"
}
char* 'E' {
	named "pcSizeEnv"
	param "name"
	help "prefixed an assignment of the size of each bin to the output line"
}
type "bytes" 'r' {
	named "iRound" "pcRound"
	param "round"
	init '"1"'
	help "specify the round units to pack with (default %qi)"
}

type "bytes" 'o' {
	named "iOverhead" "pcOverhead"
	param "overhead"
	init '"0"'
	help "how much overhead per file, after unit rounding (default %qi)"
}

type "bytes" 'p' {
	named "iPacking" "pcPacking"
	param "packing"
	init '"0"'
	help "packing header size, added before rounding (default %qi)"
}

long 'w' {
	named "iMaxNums"
	param "window"
	init "8192"
	help "the window of files to consider for packing (default %i)"
}

long 'n' {
	named "iMaxFiles"
	init "0"
	param "files"
	help "the max number of files per bin"
}

boolean 'z' {
	local named "fPrint0"
	init "0"
	help "read stdin as `find -print0' output"
}

boolean 'v' {
	named "fVerbose"
	init "0"
	help "be verbose on stderr"
}

after {
	named "Sanity"
	update "%n();"
}

list {
	named "SetCmd"
	param "cmd"
	help "shell command to prefix to list"
}

exit {
	named "DoPack"
	update "/*below*/"
	aborts "exit(%n(%rzn));"
}

# This almost never does better than the unFib packer, OK it never does.
# The ln packer is even better then this one, and I don't include it in
# the binpack.c explode module.
action 'L' {
	hidden update "VE_scale = VE_log2;"
	help "use the log2 packer rather than the unFib"
}

%c
/* This is complex enough, we need to check the params			(ksb)
 */
static void
Sanity()
{
	if (!fUserSize && bsize < iRound + iOverhead) {
		fprintf(stderr, "%s: -b %ld < -r %ld + -p %ld: only zero length files fit\n", progname, bsize, iRound, iOverhead);
		exit(EX_DATAERR);
	}
	if (iMaxNums < (*VE_scale)((VEWEIGHT)bsize)) {
		fprintf(stderr, "%s: -w %ld < %lu [scale(-b %ld)]: increase -w to allow packing\n", progname, iMaxNums, (unsigned long)VE_scale(bsize), bsize);
		exit(EX_PROTOCOL);
	}
}

/* Try to show the compression table the binpacker is using		(ksb)
 */
static void
ShowTable(FILE *fp)
{
	register VEWEIGHT wScan, wLast;
	register size_t iSlot, iCur;

	if (!fGaveB) {
		return;
	}
	fprintf(fp, "%s: packing compression table:\n", progname);
	wLast = 0;
	iSlot = -1;
	for (wScan = 0; wScan < bsize; ++wScan) {
		iCur = (*VE_scale)(wScan);
		if (iCur == iSlot)
			continue;
		if (wLast == wScan)
			fprintf(fp, " %3lu: %lu\n", (unsigned long)iCur, (unsigned long)wScan);
		else
			fprintf(fp, " %3lu: %lu-%lu\n", (unsigned long)iCur, (unsigned long)wLast, (unsigned long)wScan);
		iSlot = iCur;
		wLast = wScan+1;
	}
	if (wScan > wLast) {
		fprintf(fp, " %3lu: %lu-%lu\n", (unsigned long)iSlot+1, (unsigned long)wLast, (unsigned long)wScan);
	}
}
%%
