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", "fields", "padl", "padr", }; for (i := 0; i < len names; i++) ctxt.addsbuiltin(names[i], 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, 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); "fields" => return sbuiltin_fields(ctxt, argv); "padl" => return sbuiltin_pad(ctxt, argv, -1); "padr" => return sbuiltin_pad(ctxt, argv, 1); } return nil; } sbuiltin_pad(ctxt: ref Context, argv: list of ref Listnode, dir: int): list of ref Listnode { if (tl argv == nil || !isnum((hd tl argv).word)) ctxt.fail("usage", "usage: " + (hd argv).word + " n [arg...]"); argv = tl argv; n := int (hd argv).word * dir; s := ""; for (argv = tl argv; argv != nil; argv = tl argv) { s += word(hd argv); if (tl argv != nil) s[len s] = ' '; } if (n != 0) s = sys->sprint("%*s", n, s); return ref Listnode(nil, s) :: nil; } sbuiltin_fields(ctxt: ref Context, argv: list of ref Listnode): list of ref Listnode { argv = tl argv; if (len argv != 2) ctxt.fail("usage", "usage: fields cl s"); cl := word(hd argv); s := word(hd tl argv); r: list of string; n := 0; for (i := 0; i < len s; i++) { if (str->in(s[i], cl)) { r = s[n:i] :: r; n = i + 1; } } r = s[n:i] :: r; rl: list of ref Listnode; for (; r != nil; r = tl r) rl = ref Listnode(nil, hd r) :: rl; return rl; } 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; }