ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/sh/arg.b/
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;
}