implement Sed; ## partial sed implementation borrowed from plan9 sed.# include "sys.m"; sys: Sys;include "draw.m";include "arg.m"; arg: Arg;include "bufio.m"; bufio: Bufio; Iobuf: import bufio;include "string.m"; str: String;include "regex.m"; regex: Regex; Re: import regex; Sed : module { init: fn(ctxt: ref Draw->Context, argv: list of string);}; false, true: con iota;bool: type int; Addr: adt { pick { None => Dollar => Line => line: int; Regex => re: Re; }}; Sedcom: adt { command: fn(c: self ref Sedcom); executable: fn(c: self ref Sedcom) : int; ad1, ad2: ref Addr; negfl: bool; active: int; pick { S => gfl, pfl: int; re: Re; b: ref Iobuf; rhs: string; D or CD or P or Q or EQ or G or CG or H or CH or N or CN or X or CP or L=> A or C or I => text: string; R => filename: string; W => b: ref Iobuf; Y => map: list of (int, int); B or T or Lab => lab: string; }}; dflag := false;nflag := false;gflag := false;sflag := 0; delflag := 0;dolflag := 0;fhead := 0;files: list of string;fout: ref Iobuf;infile: ref Iobuf;jflag := 0;lastregex: Re;linebuf: string;filename := "";lnum := 0;peekc := 0; holdsp := "";patsp := ""; cmds: list of ref Sedcom;appendlist: list of ref Sedcom;bufioflush: list of ref Iobuf; init(nil: ref Draw->Context, args: list of string){ sys = load Sys Sys->PATH; if ((arg = load Arg Arg->PATH) == nil) fatal(sys->sprint("could not load %s: %r", Arg->PATH)); if ((bufio = load Bufio Bufio->PATH) == nil) fatal(sys->sprint("could not load %s: %r", Bufio->PATH)); if ((str = load String String->PATH) == nil) fatal(sys->sprint("could not load %s: %r", String->PATH)); if ((regex = load Regex Regex->PATH) == nil) fatal(sys->sprint("could not load %s: %r", Regex->PATH)); arg->init(args); compfl := 0; while ((c := arg->opt()) != 0) case c { 'n' => nflag = true; 'g' => gflag = true; 'e' => if ((s := arg->arg()) == nil) usage(); filename = ""; cmds = compile(bufio->sopen(s + "\n"), cmds); compfl = 1; 'f' => if ((filename = arg->arg()) == nil) usage(); b := bufio->open(filename, bufio->OREAD); if (b == nil) fatal(sys->sprint("couldn't open '%s': %r", filename)); cmds = compile(b, cmds); compfl = 1; 'd' => dflag = true; * => usage(); } args = arg->argv(); if (compfl == 0) { if (len args == 0) fatal("missing pattern"); filename = ""; cmds = compile(bufio->sopen(hd args + "\n"), cmds); args = tl args; } # reverse command list, we could compile addresses here if required l: list of ref Sedcom; for (p := cmds; p != nil; p = tl p) { l = hd p :: l; } cmds = l; # add files to file list (and reverse to get in right order) f: list of string; if (len args == 0) f = "" :: f; else for (; len args != 0; args = tl args) f = hd args :: f; for (;f != nil; f = tl f) files = hd f :: files; if ((fout = bufio->fopen(sys->fildes(1), bufio->OWRITE)) == nil) fatal(sys->sprint("couldn't buffer stdout: %r")); bufioflush = fout :: bufioflush; lnum = 0; execute(cmds); exits(nil);} depth := 0;maxdepth: con 20;cmdend := array [maxdepth] of string;cmdcnt := array [maxdepth] of int; compile(b: ref Iobuf, l: list of ref Sedcom) : list of ref Sedcom{ lnum = 1; nextline: for (;;) { err: int; (err, linebuf) = getline(b); if (err < 0) break; s := linebuf; do { rep: ref Sedcom; ad1, ad2: ref Addr; negfl := 0; if (s != "") s = str->drop(s, " \t;"); if (s == "" || s[0] == '#') continue nextline; # read addresses (s, ad1) = address(s); pick a := ad1 { None => ad2 = ref Addr.None(); * => if (s != "" && (s[0] == ',' || s[0] == ';')) { (s, ad2) = address(s[1:]); } else { ad2 = ref Addr.None(); } } s = str->drop(s, " \t"); if (s != "" && str->in(s[0], "!")) { negfl = true; s = str->drop(s, "!"); } s = str->drop(s, " \t"); if (s == "") break; c := s[0]; s = s[1:]; # mop up commands that got two addresses but only want one. case c { 'a' or 'c' or 'q' or '=' or 'i' => if (tagof ad2 != tagof Addr.None) fatal(sys->sprint("only one address allowed: '%s'", linebuf)); } case c { * => fatal(sys->sprint("unrecognised command: '%s' (%c)", linebuf, c)); 'a' => if (s != "" && s[0] == '\\') s = s[1:]; if (s == "" || s[0] != '\n') fatal("unexpected characters in a command: " + s); rep = ref Sedcom.A (ad1, ad2, negfl, 0, s[1:]); s = ""; 'c' => if (s != "" && s[0] == '\\') s = s[1:]; if (s == "" || s[0] != '\n') fatal("unexpected characters in c command: " + s); rep = ref Sedcom.C (ad1, ad2, negfl, 0, s[1:]); s = ""; 'i' => if (s != "" && s[0] == '\\') s = s[1:]; if (s == "" || s[0] != '\n') fatal("unexpected characters in i command: " + s); rep = ref Sedcom.I (ad1, ad2, negfl, 0, s[1:]); s = ""; 'r' => s = str->drop(s, " \t"); rep = ref Sedcom.R (ad1, ad2, negfl, 0, s); s = ""; 'w' => if (s != "") s = str->drop(s, " \t"); if (s == "") fatal("no filename in w command: " + linebuf); bo := bufio->open(s, bufio->OWRITE); if (bo == nil) bo = bufio->create(s, bufio->OWRITE, 8r666); if (bo == nil) fatal(sys->sprint("can't create output file: '%s'", s)); bufioflush = bo :: bufioflush; rep = ref Sedcom.W (ad1, ad2, negfl, 0, bo); s = ""; 'd' => rep = ref Sedcom.D (ad1, ad2, negfl, 0); 'D' => rep = ref Sedcom.CD (ad1, ad2, negfl, 0); 'p' => rep = ref Sedcom.P (ad1, ad2, negfl, 0); 'P' => rep = ref Sedcom.CP (ad1, ad2, negfl, 0); 'q' => rep = ref Sedcom.Q (ad1, ad2, negfl, 0); '=' => rep = ref Sedcom.EQ (ad1, ad2, negfl, 0); 'g' => rep = ref Sedcom.G (ad1, ad2, negfl, 0); 'G' => rep = ref Sedcom.CG (ad1, ad2, negfl, 0); 'h' => rep = ref Sedcom.H (ad1, ad2, negfl, 0); 'H' => rep = ref Sedcom.CH (ad1, ad2, negfl, 0); 'n' => rep = ref Sedcom.N (ad1, ad2, negfl, 0); 'N' => rep = ref Sedcom.CN (ad1, ad2, negfl, 0); 'x' => rep = ref Sedcom.X (ad1, ad2, negfl, 0); 'l' => rep = ref Sedcom.L (ad1, ad2, negfl, 0); 'y' => if (s == "") fatal("expected args: " + linebuf); seof := s[0:1]; s = s[1:]; if (s == "") fatal("no lhs: " + linebuf); (lhs, s2) := str->splitl(s, seof); if (s2 == "") fatal("no lhs terminator: " + linebuf); s2 = s2[1:]; (rhs, s4) := str->splitl(s2, seof); if (s4 == "") fatal("no rhs: " + linebuf); s = s4[1:]; if (len lhs != len rhs) fatal("y command needs same length sets: " + linebuf); map: list of (int, int); for (i := 0; i < len lhs; i++) map = (lhs[i], rhs[i]) :: map; rep = ref Sedcom.Y (ad1, ad2, negfl, 0, map); 's' => seof := s[0:1]; re: Re; (re, s) = recomp(s); rhs: string; (s, rhs) = compsub(seof + s); gfl := gflag; pfl := 0; if (s != "" && s[0] == 'g') { gfl = 1; s = s[1:]; } if (s != "" && s[0] == 'p') { pfl = 1; s = s[1:]; } if (s != "" && s[0] == 'P') { pfl = 2; s = s[1:]; } b: ref Iobuf = nil; if (s != "" && s[0] == 'w') { s = s[1:]; if (s != "") s = str->drop(s, " \t"); if (s == "") fatal("no filename in s with w: " + linebuf); b = bufio->open(s, bufio->OWRITE); if (b == nil) b = bufio->create(s, bufio->OWRITE, 8r666); if (b == nil) fatal(sys->sprint("can't create output file: '%s'", s)); bufioflush = b :: bufioflush; s = ""; } rep = ref Sedcom.S (ad1, ad2, negfl, 0, gfl, pfl, re, b, rhs); ':' => if (s != "") s = str->drop(s, " \t"); (lab, s1) := str->splitl(s, " \t;#"); s = s1; if (lab == "") fatal(sys->sprint("null label: '%s'", linebuf)); if (findlabel(lab)) fatal(sys->sprint("duplicate label: '%s'", lab)); rep = ref Sedcom.Lab (ad1, ad2, negfl, 0, lab); 'b' or 't' => if (s != "") s = str->drop(s, " \t"); (lab, s1) := str->splitl(s, " \t;#"); s = s1; if (c == 'b') rep = ref Sedcom.B (ad1, ad2, negfl, 0, lab); else rep = ref Sedcom.T (ad1, ad2, negfl, 0, lab); '{' => # replace { with branch to }. lab := mklab(depth); depth++; rep = ref Sedcom.B (ad1, ad2, !negfl, 0, lab); s = ";" + s; '}' => if (tagof ad1 != tagof Addr.None) fatal("did not expect address:" + linebuf); if (--depth < 0) fatal("too many }'s: " + linebuf); lab := mklab(depth); cmdcnt[depth]++; rep = ref Sedcom.Lab ( ad1, ad2, negfl, 0, lab); s = ";" + s; } l = rep :: l; } while (s != nil && str->in(s[0], ";{}")); if (s != nil) fatal("leftover junk: " + s); } return l;} findlabel(lab: string) : bool{ for (l := cmds; l != nil; l = tl l) pick x := hd l { Lab => if (x.lab == lab) return true; } return false;} mklab(depth: int): string{ return "_" + string cmdcnt[depth] + "_" + string depth;} Sedcom.command(c: self ref Sedcom){ pick x := c { S => m: bool; (m, patsp) = substitute(x, patsp); if (m) { case x.pfl { 0 => ; 1 => fout.puts(patsp + "\n"); * => l: string; (l, patsp) = str->splitl(patsp, "\n"); fout.puts(l + "\n"); break; } if (x.b != nil) x.b.puts(patsp + "\n"); } P => fout.puts(patsp + "\n"); CP => (s, nil) := str->splitl(patsp, "\n"); fout.puts(s + "\n"); A => appendlist = c :: appendlist; R => appendlist = c :: appendlist; C => delflag++; if (c.active == 1) fout.puts(x.text + "\n"); I => fout.puts(x.text + "\n"); W => x.b.puts(patsp + "\n"); G => patsp = holdsp; CG => patsp += holdsp; H => holdsp = patsp; CH => holdsp += patsp; X => (holdsp, patsp) = (patsp, holdsp); Y => # yes this is O(N²). for (i := 0; i < len patsp; i++) for (h := x.map; h != nil; h = tl h) { (s, d) := hd h; if (patsp[i] == s) patsp[i] = d; } D => delflag++; CD => # loose upto \n. (s1, s2) := str->splitl(patsp, "\n"); if (s2 == nil) patsp = s1; else if (len s2 > 1) patsp = s2[1:]; else patsp = ""; jflag++; Q => if (!nflag) fout.puts(patsp + "\n"); arout(); exits(nil); N => if (!nflag) fout.puts(patsp + "\n"); arout(); n: int;Lines 1–500 of 911. Load more
implement Sed; # # partial sed implementation borrowed from plan9 sed. # include "sys.m"; sys: Sys; include "draw.m"; include "arg.m"; arg: Arg; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "string.m"; str: String; include "regex.m"; regex: Regex; Re: import regex; Sed : module { init: fn(ctxt: ref Draw->Context, argv: list of string); }; false, true: con iota; bool: type int; Addr: adt { pick { None => Dollar => Line => line: int; Regex => re: Re; } }; Sedcom: adt { command: fn(c: self ref Sedcom); executable: fn(c: self ref Sedcom) : int; ad1, ad2: ref Addr; negfl: bool; active: int; pick { S => gfl, pfl: int; re: Re; b: ref Iobuf; rhs: string; D or CD or P or Q or EQ or G or CG or H or CH or N or CN or X or CP or L=> A or C or I => text: string; R => filename: string; W => b: ref Iobuf; Y => map: list of (int, int); B or T or Lab => lab: string; } }; dflag := false; nflag := false; gflag := false; sflag := 0; delflag := 0; dolflag := 0; fhead := 0; files: list of string; fout: ref Iobuf; infile: ref Iobuf; jflag := 0; lastregex: Re; linebuf: string; filename := ""; lnum := 0; peekc := 0; holdsp := ""; patsp := ""; cmds: list of ref Sedcom; appendlist: list of ref Sedcom; bufioflush: list of ref Iobuf; init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; if ((arg = load Arg Arg->PATH) == nil) fatal(sys->sprint("could not load %s: %r", Arg->PATH)); if ((bufio = load Bufio Bufio->PATH) == nil) fatal(sys->sprint("could not load %s: %r", Bufio->PATH)); if ((str = load String String->PATH) == nil) fatal(sys->sprint("could not load %s: %r", String->PATH)); if ((regex = load Regex Regex->PATH) == nil) fatal(sys->sprint("could not load %s: %r", Regex->PATH)); arg->init(args); compfl := 0; while ((c := arg->opt()) != 0) case c { 'n' => nflag = true; 'g' => gflag = true; 'e' => if ((s := arg->arg()) == nil) usage(); filename = ""; cmds = compile(bufio->sopen(s + "\n"), cmds); compfl = 1; 'f' => if ((filename = arg->arg()) == nil) usage(); b := bufio->open(filename, bufio->OREAD); if (b == nil) fatal(sys->sprint("couldn't open '%s': %r", filename)); cmds = compile(b, cmds); compfl = 1; 'd' => dflag = true; * => usage(); } args = arg->argv(); if (compfl == 0) { if (len args == 0) fatal("missing pattern"); filename = ""; cmds = compile(bufio->sopen(hd args + "\n"), cmds); args = tl args; } # reverse command list, we could compile addresses here if required l: list of ref Sedcom; for (p := cmds; p != nil; p = tl p) { l = hd p :: l; } cmds = l; # add files to file list (and reverse to get in right order) f: list of string; if (len args == 0) f = "" :: f; else for (; len args != 0; args = tl args) f = hd args :: f; for (;f != nil; f = tl f) files = hd f :: files; if ((fout = bufio->fopen(sys->fildes(1), bufio->OWRITE)) == nil) fatal(sys->sprint("couldn't buffer stdout: %r")); bufioflush = fout :: bufioflush; lnum = 0; execute(cmds); exits(nil); } depth := 0; maxdepth: con 20; cmdend := array [maxdepth] of string; cmdcnt := array [maxdepth] of int; compile(b: ref Iobuf, l: list of ref Sedcom) : list of ref Sedcom { lnum = 1; nextline: for (;;) { err: int; (err, linebuf) = getline(b); if (err < 0) break; s := linebuf; do { rep: ref Sedcom; ad1, ad2: ref Addr; negfl := 0; if (s != "") s = str->drop(s, " \t;"); if (s == "" || s[0] == '#') continue nextline; # read addresses (s, ad1) = address(s); pick a := ad1 { None => ad2 = ref Addr.None(); * => if (s != "" && (s[0] == ',' || s[0] == ';')) { (s, ad2) = address(s[1:]); } else { ad2 = ref Addr.None(); } } s = str->drop(s, " \t"); if (s != "" && str->in(s[0], "!")) { negfl = true; s = str->drop(s, "!"); } s = str->drop(s, " \t"); if (s == "") break; c := s[0]; s = s[1:]; # mop up commands that got two addresses but only want one. case c { 'a' or 'c' or 'q' or '=' or 'i' => if (tagof ad2 != tagof Addr.None) fatal(sys->sprint("only one address allowed: '%s'", linebuf)); } case c { * => fatal(sys->sprint("unrecognised command: '%s' (%c)", linebuf, c)); 'a' => if (s != "" && s[0] == '\\') s = s[1:]; if (s == "" || s[0] != '\n') fatal("unexpected characters in a command: " + s); rep = ref Sedcom.A (ad1, ad2, negfl, 0, s[1:]); s = ""; 'c' => if (s != "" && s[0] == '\\') s = s[1:]; if (s == "" || s[0] != '\n') fatal("unexpected characters in c command: " + s); rep = ref Sedcom.C (ad1, ad2, negfl, 0, s[1:]); s = ""; 'i' => if (s != "" && s[0] == '\\') s = s[1:]; if (s == "" || s[0] != '\n') fatal("unexpected characters in i command: " + s); rep = ref Sedcom.I (ad1, ad2, negfl, 0, s[1:]); s = ""; 'r' => s = str->drop(s, " \t"); rep = ref Sedcom.R (ad1, ad2, negfl, 0, s); s = ""; 'w' => if (s != "") s = str->drop(s, " \t"); if (s == "") fatal("no filename in w command: " + linebuf); bo := bufio->open(s, bufio->OWRITE); if (bo == nil) bo = bufio->create(s, bufio->OWRITE, 8r666); if (bo == nil) fatal(sys->sprint("can't create output file: '%s'", s)); bufioflush = bo :: bufioflush; rep = ref Sedcom.W (ad1, ad2, negfl, 0, bo); s = ""; 'd' => rep = ref Sedcom.D (ad1, ad2, negfl, 0); 'D' => rep = ref Sedcom.CD (ad1, ad2, negfl, 0); 'p' => rep = ref Sedcom.P (ad1, ad2, negfl, 0); 'P' => rep = ref Sedcom.CP (ad1, ad2, negfl, 0); 'q' => rep = ref Sedcom.Q (ad1, ad2, negfl, 0); '=' => rep = ref Sedcom.EQ (ad1, ad2, negfl, 0); 'g' => rep = ref Sedcom.G (ad1, ad2, negfl, 0); 'G' => rep = ref Sedcom.CG (ad1, ad2, negfl, 0); 'h' => rep = ref Sedcom.H (ad1, ad2, negfl, 0); 'H' => rep = ref Sedcom.CH (ad1, ad2, negfl, 0); 'n' => rep = ref Sedcom.N (ad1, ad2, negfl, 0); 'N' => rep = ref Sedcom.CN (ad1, ad2, negfl, 0); 'x' => rep = ref Sedcom.X (ad1, ad2, negfl, 0); 'l' => rep = ref Sedcom.L (ad1, ad2, negfl, 0); 'y' => if (s == "") fatal("expected args: " + linebuf); seof := s[0:1]; s = s[1:]; if (s == "") fatal("no lhs: " + linebuf); (lhs, s2) := str->splitl(s, seof); if (s2 == "") fatal("no lhs terminator: " + linebuf); s2 = s2[1:]; (rhs, s4) := str->splitl(s2, seof); if (s4 == "") fatal("no rhs: " + linebuf); s = s4[1:]; if (len lhs != len rhs) fatal("y command needs same length sets: " + linebuf); map: list of (int, int); for (i := 0; i < len lhs; i++) map = (lhs[i], rhs[i]) :: map; rep = ref Sedcom.Y (ad1, ad2, negfl, 0, map); 's' => seof := s[0:1]; re: Re; (re, s) = recomp(s); rhs: string; (s, rhs) = compsub(seof + s); gfl := gflag; pfl := 0; if (s != "" && s[0] == 'g') { gfl = 1; s = s[1:]; } if (s != "" && s[0] == 'p') { pfl = 1; s = s[1:]; } if (s != "" && s[0] == 'P') { pfl = 2; s = s[1:]; } b: ref Iobuf = nil; if (s != "" && s[0] == 'w') { s = s[1:]; if (s != "") s = str->drop(s, " \t"); if (s == "") fatal("no filename in s with w: " + linebuf); b = bufio->open(s, bufio->OWRITE); if (b == nil) b = bufio->create(s, bufio->OWRITE, 8r666); if (b == nil) fatal(sys->sprint("can't create output file: '%s'", s)); bufioflush = b :: bufioflush; s = ""; } rep = ref Sedcom.S (ad1, ad2, negfl, 0, gfl, pfl, re, b, rhs); ':' => if (s != "") s = str->drop(s, " \t"); (lab, s1) := str->splitl(s, " \t;#"); s = s1; if (lab == "") fatal(sys->sprint("null label: '%s'", linebuf)); if (findlabel(lab)) fatal(sys->sprint("duplicate label: '%s'", lab)); rep = ref Sedcom.Lab (ad1, ad2, negfl, 0, lab); 'b' or 't' => if (s != "") s = str->drop(s, " \t"); (lab, s1) := str->splitl(s, " \t;#"); s = s1; if (c == 'b') rep = ref Sedcom.B (ad1, ad2, negfl, 0, lab); else rep = ref Sedcom.T (ad1, ad2, negfl, 0, lab); '{' => # replace { with branch to }. lab := mklab(depth); depth++; rep = ref Sedcom.B (ad1, ad2, !negfl, 0, lab); s = ";" + s; '}' => if (tagof ad1 != tagof Addr.None) fatal("did not expect address:" + linebuf); if (--depth < 0) fatal("too many }'s: " + linebuf); lab := mklab(depth); cmdcnt[depth]++; rep = ref Sedcom.Lab ( ad1, ad2, negfl, 0, lab); s = ";" + s; } l = rep :: l; } while (s != nil && str->in(s[0], ";{}")); if (s != nil) fatal("leftover junk: " + s); } return l; } findlabel(lab: string) : bool { for (l := cmds; l != nil; l = tl l) pick x := hd l { Lab => if (x.lab == lab) return true; } return false; } mklab(depth: int): string { return "_" + string cmdcnt[depth] + "_" + string depth; } Sedcom.command(c: self ref Sedcom) { pick x := c { S => m: bool; (m, patsp) = substitute(x, patsp); if (m) { case x.pfl { 0 => ; 1 => fout.puts(patsp + "\n"); * => l: string; (l, patsp) = str->splitl(patsp, "\n"); fout.puts(l + "\n"); break; } if (x.b != nil) x.b.puts(patsp + "\n"); } P => fout.puts(patsp + "\n"); CP => (s, nil) := str->splitl(patsp, "\n"); fout.puts(s + "\n"); A => appendlist = c :: appendlist; R => appendlist = c :: appendlist; C => delflag++; if (c.active == 1) fout.puts(x.text + "\n"); I => fout.puts(x.text + "\n"); W => x.b.puts(patsp + "\n"); G => patsp = holdsp; CG => patsp += holdsp; H => holdsp = patsp; CH => holdsp += patsp; X => (holdsp, patsp) = (patsp, holdsp); Y => # yes this is O(N²). for (i := 0; i < len patsp; i++) for (h := x.map; h != nil; h = tl h) { (s, d) := hd h; if (patsp[i] == s) patsp[i] = d; } D => delflag++; CD => # loose upto \n. (s1, s2) := str->splitl(patsp, "\n"); if (s2 == nil) patsp = s1; else if (len s2 > 1) patsp = s2[1:]; else patsp = ""; jflag++; Q => if (!nflag) fout.puts(patsp + "\n"); arout(); exits(nil); N => if (!nflag) fout.puts(patsp + "\n"); arout(); n: int;