code: 9ferno

ref: 6d69f6fba35087686f79adb2ea0d67944a62ca7b
dir: /appl/cmd/gunzip.b/

View raw version
implement Gunzip;

include "sys.m";
	sys:	Sys;
	fprint, sprint: import sys;

include "draw.m";

include "string.m";
	str: String;

include "bufio.m";
	bufio:	Bufio;
	Iobuf:	import bufio;

include "filter.m";
	inflate: Filter;

Gunzip: module
{
	init:	fn(ctxt: ref Draw->Context, argv: list of string);
};

argv0:	con "gunzip";
stderr:	ref Sys->FD;

INFLATEPATH: con "/dis/lib/inflate.dis";

init(nil: ref Draw->Context, argv: list of string)
{
	sys = load Sys Sys->PATH;
	stderr = sys->fildes(2);
	bufio = load Bufio Bufio->PATH;
	if (bufio == nil)
		fatal(sys->sprint("cannot load %s: %r", Bufio->PATH));
	str = load String String->PATH;
	if (bufio == nil)
		fatal(sys->sprint("cannot load %s: %r", String->PATH));
	inflate = load Filter INFLATEPATH;
	if (inflate == nil)
		fatal(sys->sprint("cannot load %s: %r", INFLATEPATH));

	inflate->init();

	if(argv != nil)
		argv = tl argv;

	ok := 1;
	if(len argv == 0){
		bin := bufio->fopen(sys->fildes(0), Bufio->OREAD);
		bout := bufio->fopen(sys->fildes(1), Bufio->OWRITE);
		ok = gunzip(bin, bout, "stdin", "stdout");
		bout.close();
	} else {
		for(; argv != nil; argv = tl argv)
			ok &= gunzipf(hd argv);
	}
	if(ok == 0)
		raise "fail:errors";
}

gunzipf(file: string): int
{
	bin := bufio->open(file, Bufio->OREAD);
	if(bin == nil){
		fprint(stderr, "%s: can't open %s: %r\n", argv0, file);
		return 0;
	}

	(nil, ofile) := str->splitr(file, "/");
	n := len ofile;
	if(n < 4 || ofile[n-3:] != ".gz"){
		fprint(stderr, "%s: .gz extension required: %s\n", argv0, file);
		bin.close();
		return 0;
	} else
		ofile = ofile[:n-3];
	bout := bufio->create(ofile, Bufio->OWRITE, 8r666);
	if(bout == nil){
		fprint(stderr, "%s: can't open %s: %r\n", argv0, ofile);
		bin.close();
		return 0;
	}

	ok := gunzip(bin, bout, file, ofile);
	bin.close();
	bout.close();
	if(ok) {
		# did possibly rename file and update modification time here.
		if (sys->remove(file) == -1)
			sys->fprint(stderr, "%s: cannot remove %s: %r\n", argv0, file);
	}

	return ok;
}

gunzip(bin, bout: ref Iobuf, fin, fout: string): int
{
	rq := inflate->start("h");
	for(;;) {
		pick m := <-rq {
		Fill =>
			n := bin.read(m.buf, len m.buf);
			m.reply <-= n;
			if (n == -1) {
				sys->fprint(stderr, "%s: %s: read error: %r\n", argv0, fin);
				return 0;
			}
		Result =>
			if (len m.buf > 0) {
				n := bout.write(m.buf, len m.buf);
				if (n != len m.buf) {
					m.reply <-= -1;
					sys->fprint(stderr, "%s: %s: write error: %r\n", argv0, fout);
					return 0;
				}
				m.reply <-= 0;
			}
		#Info =>
		#	if m.msg begins with "file", it's the original filename of the compressed file.
		#	if m.msg begins with "mtime", it's the original modification time.
		Finished =>
			if (bout.flush() != 0) {
				sys->fprint(stderr, "%s: %s: flush error: %r\n", argv0, fout);
				return 0;
			}
			return 1;
		Error =>
			sys->fprint(stderr, "%s: %s: inflate error: %s\n", argv0, fin, m.e);
			return 0;
		}
	}
}

fatal(msg: string)
{
	fprint(stderr, "%s: %s\n", argv0, msg);
	raise "fail:error";
}