shithub: purgatorio

ref: 5edeca01b0622463a65c126ebcc29314013fd928
dir: /appl/cmd/sh/arg.b/

View raw version
implement Shellbuiltin;

include "sys.m";
	sys: Sys;
include "draw.m";
include "sh.m";
	sh: Sh;
	Listnode, Context: import sh;
	myself: Shellbuiltin;

initbuiltin(ctxt: ref Context, shmod: Sh): string
{
	sys = load Sys Sys->PATH;
	sh = shmod;
	myself = load Shellbuiltin "$self";
	if (myself == nil)
		ctxt.fail("bad module", sys->sprint("arg: cannot load self: %r"));
	ctxt.addbuiltin("arg", myself);
	return nil;
}

whatis(nil: ref Sh->Context, nil: Sh, nil: string, nil: int): string
{
	return nil;
}

getself(): Shellbuiltin
{
	return myself;
}

runbuiltin(ctxt: ref Context, nil: Sh,
			argv: list of ref Listnode, last: int): string
{
	case (hd argv).word {
	"arg" =>
		return builtin_arg(ctxt, argv, last);
	}
	return nil;
}

runsbuiltin(nil: ref Sh->Context, nil: Sh,
			nil: list of ref Listnode): list of ref Listnode
{
	return nil;
}

argusage(ctxt: ref Context)
{
	ctxt.fail("usage", "usage: arg [opts {command}]... - args");
}

builtin_arg(ctxt: ref Context, argv: list of ref Listnode, nil: int): string
{
	for (args := tl argv; args != nil; args = tl tl args) {
		if ((hd args).word == "-")
			break;
		if ((hd args).cmd != nil && (hd args).word == nil)
			argusage(ctxt);
		if (tl args == nil)
			argusage(ctxt);
		if ((hd tl args).cmd == nil)
			argusage(ctxt);
	}
	if (args == nil)
		args = ctxt.get("*");
	else
		args = tl args;
	laststatus := "";
	ctxt.push();
	{
		arg := Arg.init(args);
		while ((opt := arg.opt()) != 0) {
			for (argt := tl argv; argt != nil && (hd argt).word != "-"; argt = tl tl argt) {
				w := (hd argt).word;
				argcount := 0;
				for (e := len w - 1; e >= 0; e--) {
					if (w[e] != '+')
						break;
					argcount++;
				}
				w = w[0:e+1];
				if (w == nil)
					continue;
				for (i := 0; i < len w; i++)
					if (w[i] == opt || w[i] == '*')
						break;
				if (i < len w) {
					optstr := ""; optstr[0] = opt;
					ctxt.setlocal("opt", ref Listnode(nil, optstr) :: nil);
					args = arg.arg(argcount);
					if (argcount > 0 && args == nil)
						ctxt.fail("usage", sys->sprint("option -%c requires %d arguments", opt, argcount));
					ctxt.setlocal("arg", args);
					laststatus = ctxt.run(hd tl argt :: nil, 0);
					break;
				}
			}
			if (argt == nil || (hd argt).word == "-")
				ctxt.fail("usage", sys->sprint("unknown option -%c", opt));
		}
		ctxt.pop();
		ctxt.set("args", arg.args);		# XXX backward compatibility - should go
		ctxt.set("*", arg.args);
		return laststatus;
	}
	exception e{
	"fail:*" =>
		ctxt.pop();
		if (e[5:] == "break")
			return laststatus;
		raise e;
	}
}

Arg: adt {
	args: list of ref Listnode;
	curropt: string;
	init: fn(argv: list of ref Listnode): ref Arg;
	arg: fn(ctxt: self ref Arg, n: int): list of ref Listnode;
	opt: fn(ctxt: self ref Arg): int;
};
	

Arg.init(argv: list of ref Listnode): ref Arg
{
	return ref Arg(argv, nil);
}

# get next n option arguments (nil list if not enough arguments found)
Arg.arg(ctxt: self ref Arg, n: int): list of ref Listnode
{
	if (n == 0)
		return nil;

	args: list of ref Listnode;
	while (--n >= 0) {
		if (ctxt.curropt != nil) {
			args = ref Listnode(nil, ctxt.curropt) :: args;
			ctxt.curropt = nil;
		} else if (ctxt.args == nil)
			return nil;
		else {
			args = hd ctxt.args :: args;
			ctxt.args = tl ctxt.args;
		}
	}
	r: list of ref Listnode;
	for (; args != nil; args = tl args)
		r = hd args :: r;
	return r;
}

# get next option letter
# return 0 at end of options
Arg.opt(ctxt: self ref Arg): int
{
	if (ctxt.curropt != "") {
		opt := ctxt.curropt[0];
		ctxt.curropt = ctxt.curropt[1:];
		return opt;
	}

	if (ctxt.args == nil)
		return 0;

	nextarg := (hd ctxt.args).word;
	if (len nextarg < 2 || nextarg[0] != '-')
		return 0;

	if (nextarg == "--") {
		ctxt.args = tl ctxt.args;
		return 0;
	}

	opt := nextarg[1];
	if (len nextarg > 2)
		ctxt.curropt = nextarg[2:];
	ctxt.args = tl ctxt.args;
	return opt;
}