diff options
Diffstat (limited to 'appl/cmd/sh/string.b')
| -rw-r--r-- | appl/cmd/sh/string.b | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/appl/cmd/sh/string.b b/appl/cmd/sh/string.b new file mode 100644 index 00000000..b6d079e4 --- /dev/null +++ b/appl/cmd/sh/string.b @@ -0,0 +1,212 @@ +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; +} |
