implement Tcs; include "sys.m"; sys: Sys; include "draw.m"; include "arg.m"; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "convcs.m"; convcs: Convcs; Tcs: module { init: fn (nil: ref Draw->Context, args: list of string); }; stderr: ref Sys->FD; init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; stderr = sys->fildes(2); if ((arg := load Arg Arg->PATH) == nil) badmodule(Arg->PATH); if ((bufio = load Bufio Bufio->PATH) == nil) badmodule(Bufio->PATH); if ((convcs = load Convcs Convcs->PATH) == nil) badmodule(Convcs->PATH); arg->init(args); arg->setusage("tcs [-C configfile] [-l] [-f ics] [-t ocs] file ..."); lflag := 0; vflag := 0; ics := "utf8"; ocs := "utf8"; csfile := ""; while ((c := arg->opt()) != 0) { case c { 'C' => csfile = arg->arg(); 'f' => ics = arg->arg(); 'l' => lflag = 1; 't' => ocs = arg->arg(); 'v' => vflag = 1; * => arg->usage(); } } file := arg->arg(); out := bufio->fopen(sys->fildes(1), Sys->OWRITE); err := convcs->init(csfile); if (err != nil) { sys->fprint(stderr, "convcs: %s\n", err); raise "fail:init"; } if (lflag) { if (file != nil) dumpaliases(out, file, vflag); else dumpconvs(out, vflag); return; } stob: Stob; btos: Btos; (stob, err) = convcs->getstob(ocs); if (err != nil) { sys->fprint(stderr, "tcs: %s: %s\n", ocs, err); raise "fail:badarg"; } (btos, err) = convcs->getbtos(ics); if (err != nil) { sys->fprint(stderr, "tcs: %s: %s\n", ics, err); raise "fail:badarg"; } fd: ref Sys->FD; if (file == nil) { fd = sys->fildes(0); file = "standard input"; } else fd = open(file); inbuf := array [Sys->ATOMICIO] of byte; for(;;){ btoss: Convcs->State = nil; stobs: Convcs->State = nil; unc := 0; nc: int; s: string; while ((n := sys->read(fd, inbuf[unc:], len inbuf - unc)) > 0) { n += unc; # include unconsumed prefix (btoss, s, nc) = btos->btos(btoss, inbuf[0:n], -1); if (s != nil) stobs = output(out, stob, stobs, s); # copy down unconverted part of buffer unc = n - nc; if (unc > 0 && nc > 0) inbuf[0:] = inbuf[nc: n]; } if (n < 0) { sys->fprint(stderr, "tcs: error reading %s: %r\n", file); raise "fail:read error"; } # flush conversion state (nil, s, nil) = btos->btos(btoss, inbuf[0: unc], 0); if(s != nil) stobs = output(out, stob, stobs, s); output(out, stob, stobs, ""); if(out.flush() != 0) { sys->fprint(stderr, "tcs: write error: %r\n"); raise "fail:write error"; } file = arg->arg(); if (file == nil) break; fd = open(file); } } output(out: ref Iobuf, stob: Stob, stobs: Convcs->State, s: string): Convcs->State { outbuf: array of byte; (stobs, outbuf) = stob->stob(stobs, s); if(outbuf != nil) out.write(outbuf, len outbuf); return stobs; } badmodule(s: string) { sys->fprint(stderr, "tcs: cannot load module %s: %r\n", s); raise "fail:init"; } dumpconvs(out: ref Iobuf, verbose: int) { first := 1; for (csl := convcs->enumcs(); csl != nil; csl = tl csl) { (name, desc, mode) := hd csl; if (!verbose) { if (!first) out.putc(' '); out.puts(name); } else { ms := ""; case mode { Convcs->BTOS => ms = "(from)"; Convcs->STOB => ms = "(to)"; } out.puts(sys->sprint("%s%s\t%s\n", name, ms, desc)); } first = 0; } if (!verbose) out.putc('\n'); out.flush(); } dumpaliases(out: ref Iobuf, cs: string, verbose: int) { (desc, asl) := convcs->aliases(cs); if (asl == nil) { sys->fprint(stderr, "%s\n", desc); return; } if (verbose) { out.puts(desc); out.putc('\n'); } first := 1; for (; asl != nil; asl = tl asl) { a := hd asl; if (!first) out.putc(' '); out.puts(a); first = 0; } out.putc('\n'); out.flush(); } open(path: string): ref Sys->FD { fd := sys->open(path, Bufio->OREAD); if (fd == nil) { sys->fprint(stderr, "tcs: cannot open %s: %r\n", path); raise "fail:open"; } return fd; }