implement Shellbuiltin; include "sys.m"; sys: Sys; include "draw.m"; include "sh.m"; sh: Sh; Listnode, Context: import sh; myself: Shellbuiltin; include "string.m"; str: String; 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("string: cannot load self: %r")); str = load String String->PATH; if (str == nil) ctxt.fail("bad module", sys->sprint("string: cannot load %s: %r", String->PATH)); ctxt.addbuiltin("prefix", myself); ctxt.addbuiltin("in", myself); names := array[] of { "splitl", "splitr", "drop", "take", "splitstrl", "splitstrr", "tolower", "toupper", "len", "alen", "slice", }; for (i := 0; i < len names; i++) ctxt.addsbuiltin(names[i], myself); return nil; } getself(): Shellbuiltin { return myself; } runbuiltin(ctxt: ref Context, nil: Sh, argv: list of ref Listnode, nil: int): string { case (hd argv).word { "prefix" => (a, b) := earg2("prefix", ctxt, argv); if (!str->prefix(a, b)) return "false"; "in" => (a, b) := earg2("in", ctxt, argv); if (a == nil || !str->in(a[0], b)) return "false"; } return nil; } runsbuiltin(ctxt: ref Context, nil: Sh, argv: list of ref Listnode): list of ref Listnode { name := (hd argv).word; case name { "splitl" => (a, b) := earg2("splitl", ctxt, argv); return mk2(str->splitl(a, b)); "splitr" => (a, b) := earg2("splitr", ctxt, argv); return mk2(str->splitr(a, b)); "drop" => (a, b) := earg2("drop", ctxt, argv); return mk1(str->drop(a, b)); "take" => (a, b) := earg2("take", ctxt, argv); return mk1(str->take(a, b)); "splitstrl" => (a, b) := earg2("splitstrl", ctxt, argv); return mk2(str->splitstrl(a, b)); "splitstrr" => (a, b) := earg2("splitstrr", ctxt, argv); return mk2(str->splitstrr(a, b)); "tolower" => return mk1(str->tolower(earg1("tolower", ctxt, argv))); "toupper" => return mk1(str->toupper(earg1("tolower", ctxt, argv))); "len" => return mk1(string len earg1("len", ctxt, argv)); "alen" => return mk1(string len array of byte earg1("alen", ctxt, argv)); "slice" => return sbuiltin_slice(ctxt, argv); } return nil; } sbuiltin_slice(ctxt: ref Context, argv: list of ref Listnode): list of ref Listnode { argv = tl argv; if (len argv != 3 || !isnum((hd argv).word) || (hd tl argv).word != "end" && !isnum((hd tl argv).word)) ctxt.fail("usage", "usage: slice start end arg"); n1 := int (hd argv).word; n2: int; s := word(hd tl tl argv); r := ""; if ((hd tl argv).word == "end") n2 = len s; else n2 = int (hd tl argv).word; if (n2 > len s) n2 = len s; if (n1 > len s) n1 = len s; if (n2 > n1) r = s[n1:n2]; return mk1(r); } earg2(cmd: string, ctxt: ref Context, argv: list of ref Listnode): (string, string) { argv = tl argv; if (len argv != 2) ctxt.fail("usage", "usage: " + cmd + " arg1 arg2"); return (word(hd argv), word(hd tl argv)); } earg1(cmd: string, ctxt: ref Context, argv: list of ref Listnode): string { if (len argv != 2) ctxt.fail("usage", "usage: " + cmd + " arg"); return word(hd tl argv); } mk2(x: (string, string)): list of ref Listnode { (a, b) := x; return ref Listnode(nil, a) :: ref Listnode(nil, b) :: nil; } mk1(x: string): list of ref Listnode { return ref Listnode(nil, x) :: nil; } isnum(s: string): int { for (i := 0; i < len s; i++) if (s[i] > '9' || s[i] < '0') return 0; return 1; } word(n: ref Listnode): string { if (n.word != nil) return n.word; if (n.cmd != nil) n.word = sh->cmd2string(n.cmd); return n.word; }