summaryrefslogtreecommitdiff
path: root/appl/demo/odbc/odbcmnt.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/demo/odbc/odbcmnt.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/demo/odbc/odbcmnt.b')
-rw-r--r--appl/demo/odbc/odbcmnt.b428
1 files changed, 428 insertions, 0 deletions
diff --git a/appl/demo/odbc/odbcmnt.b b/appl/demo/odbc/odbcmnt.b
new file mode 100644
index 00000000..3cadc55f
--- /dev/null
+++ b/appl/demo/odbc/odbcmnt.b
@@ -0,0 +1,428 @@
+implement Odbcmnt;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+ stderr: ref Sys->FD;
+include "draw.m";
+include "arg.m";
+include "string.m";
+ str: String;
+include "daytime.m";
+ daytime: Daytime;
+include "convcs.m";
+ convcs : Convcs;
+include "styx.m";
+ styx: Styx;
+ Tmsg, Rmsg: import styx;
+include "styxservers.m";
+ styxservers: Styxservers;
+ Styxserver, Navigator: import styxservers;
+ nametree: Nametree;
+ Tree: import nametree;
+
+Column: adt {
+ name: string;
+ ctype: string;
+ size: int;
+};
+
+Qroot: con iota;
+WINCHARSET := "windows-1252"; # BUG: odbc.c should do the conversion!
+
+Odbcmnt: module
+{
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+init(nil: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ stderr = sys->fildes(2);
+
+ arg := load Arg Arg->PATH;
+ if(arg == nil)
+ notloaded(Arg->PATH);
+ daytime = load Daytime Daytime->PATH;
+ if (daytime == nil)
+ notloaded(Daytime->PATH);
+ str = load String String->PATH;
+ if(str == nil)
+ notloaded(String->PATH);
+ convcs = load Convcs Convcs->PATH;
+ if(convcs == nil)
+ notloaded(Convcs->PATH);
+ cserr := convcs->init(nil);
+ if (cserr != nil)
+ err("convcs init failed " + cserr);
+ styx = load Styx Styx->PATH;
+ if(styx == nil)
+ notloaded(Styx->PATH);
+ styx->init();
+ styxservers = load Styxservers Styxservers->PATH;
+ if(styxservers == nil)
+ notloaded(Styxservers->PATH);
+ styxservers->init(styx);
+ nametree = load Nametree Nametree->PATH;
+ if(nametree == nil)
+ notloaded(Nametree->PATH);
+ nametree->init();
+ addr := "127.0.0.1";
+ arg->init(argv);
+ stype := "ODBC";
+ while((o := arg->opt()) != 0)
+ case o {
+ 'a' =>
+ addr = arg->earg();
+ * =>
+ usage();
+ }
+
+ argv = arg->argv();
+ arg = nil;
+ sys->pctl(Sys->FORKNS | sys->NEWPGRP, nil);
+ dbdir := do_mount(netmkaddr(addr, "tcp", "6700"));
+ (cfd, cdir) := do_clone(dbdir);
+ sources := find_sources(cdir);
+ sys->print("Found %d sources\n", len sources);
+ spawn serveloop(dbdir, sources, sys->fildes(0));
+}
+
+netmkaddr(addr, net, svc: string): string
+{
+ if(net == nil)
+ net = "net";
+ (n, l) := sys->tokenize(addr, "!");
+ if(n <= 1){
+ if(svc== nil)
+ return sys->sprint("%s!%s", net, addr);
+ return sys->sprint("%s!%s!%s", net, addr, svc);
+ }
+ if(svc == nil || n > 2)
+ return addr;
+ return sys->sprint("%s!%s", addr, svc);
+}
+
+split1(s, delim: string): (string, string)
+{
+ (l, r) := str->splitl(s, delim);
+ return (l, str->drop(r, delim));
+}
+
+notloaded(s: string)
+{
+ err(sys->sprint("failed to load %s: %r", s));
+}
+
+usage()
+{
+ sys->fprint(stderr, "Usage: odbcmnt [ -a address ]\n");
+ raise "fail:usage";
+}
+
+do_mount(addr: string): string
+{
+ (ok, c) := sys->dial(addr, nil);
+ remdir := "/n/remote";
+ if (ok < 0)
+ err(sys->sprint("failed to dial odbc server on %s: %r", addr));
+ if (sys->mount(c.dfd, nil, remdir, 0, nil) < 0)
+ err(sys->sprint("failed to mount odbc server on %s: %r", addr));
+ dbdir := remdir + "/db";
+ return dbdir;
+}
+
+
+do_clone(dbdir: string): (ref Sys->FD, string)
+{
+ newfile := dbdir + "/new";
+ cfd := sys->open(newfile, Sys->OREAD);
+ if (cfd == nil)
+ err(sys->sprint("failed to open %s: %r", newfile));
+ cname := read_fd(cfd);
+ if (cname == nil)
+ err("failed to find clone directory name");
+ return(cfd, dbdir + "/" + cname);
+}
+
+dir(name: string, perm: int, length: int, qid: int): Sys->Dir
+{
+ uid := read_file("/dev/user");
+ d := sys->zerodir;
+ d.name = name;
+ d.uid = uid;
+ d.gid = uid;
+ d.qid.path = big qid;
+ if (perm & Sys->DMDIR)
+ d.qid.qtype = Sys->QTDIR;
+ else {
+ d.qid.qtype = Sys->QTFILE;
+ d.length = big length;
+ }
+ d.mode = perm;
+ d.atime = d.mtime = daytime->now();
+ return d;
+}
+
+newconv(dbdir, source: string): (ref Sys->FD, ref Sys->FD, ref Sys->FD, ref Sys->FD, string)
+{
+ err := "";
+ (clonefd, cdir) := do_clone(dbdir);
+ ctlf := cdir + "/ctl";
+ ctlfd := sys->open(ctlf, Sys->ORDWR);
+ if (ctlfd == nil)
+ err = sys->sprint("Failed to open %s: %r", ctlf);
+ cmdf := cdir + "/cmd";
+ cmdfd := sys->open(cmdf, Sys->ORDWR);
+ if (cmdfd == nil)
+ err = sys->sprint("Failed to open %s: %r", cmdf);
+ dataf := cdir + "/data";
+ datafd := sys->open(dataf, Sys->ORDWR);
+ if (datafd == nil)
+ err = sys->sprint("Failed to open %s: %r", dataf);
+ if (write_fd(ctlfd, "connect " + source) < 0)
+ err = sys->sprint("failed to connect to %s: %r", source);
+ return (clonefd, ctlfd, cmdfd, datafd, err);
+}
+
+SRCDIR: con 1;
+SQL: con 2;
+TABLE: con 3;
+TABLEDIR: con 4;
+COLUMN: con 5;
+
+gettype(fid: big): int
+{
+ return int fid & 7;
+}
+
+SrcFD: adt {
+ clonefd, ctlfd, cmdfd, datafd: ref sys->FD;
+};
+
+serveloop(dbdir: string, sources: list of string, confd: ref sys->FD)
+{
+ srcqid := 0;
+ sqlqid := 0;
+ tableqid := 0;
+ colqid := 0;
+ tabledirqid := 0;
+ (bs, cserr) := convcs->getbtos(WINCHARSET);
+ if (bs == nil)
+ err("getbtos error: " + cserr);
+ (tree, treeop) := nametree->start();
+ tree.create(big Qroot, dir(".",8r555 | sys->DMDIR,0,Qroot));
+ contents: list of string;
+ srcfds := array[len sources] of SrcFD;
+ i := 0;
+ for (sl := sources; sl!=nil; sl=tl sl) {
+ (srcname, srcdriver) := split1(hd sl, ":");
+ # Don't do anything with 'srvdriver' - could make a driver
+ # file to read - but does anyone care about it?
+ (clonefd, ctlfd, cmdfd, datafd, e) := newconv(dbdir, srcname);
+ if (e != nil)
+ sys->fprint(sys->fildes(2), "Odbcmnt: %s\n",e);
+ else {
+ srcfds[i] = (clonefd, ctlfd, cmdfd, datafd);
+ sys->print("%s\n",srcname);
+ Qsrc := SRCDIR + (srcqid++<<3);
+ tree.create(big Qroot, dir(srcname,8r555 | sys->DMDIR,0,Qsrc));
+ Qtabledir := TABLEDIR + (tabledirqid++<<3);
+ tree.create(big Qsrc, dir("tables",8r555 | sys->DMDIR,0,Qtabledir));
+ Qsql := SQL + (sqlqid++<<3);
+ tree.create(big Qsrc, dir("sql",8r666,0, Qsql));
+
+ tables := find_tables(srcfds[i].cmdfd, srcfds[i].datafd);
+ if (tables == nil)
+ err(sys->sprint("failed to find tables: %r"));
+ if (write_fd(srcfds[i].ctlfd, "headings") < 0)
+ err(sys->sprint("failed to write to ctl file: %r"));
+ sys->print("\tBuilding tree...");
+ for (tlist:=tables; tlist!=nil; tlist=tl tlist) {
+ table := hd tlist;
+ Qtable := TABLE + (tableqid++<<3);
+ tree.create(big Qtabledir, dir(table,8r555 | sys->DMDIR,0,Qtable));
+ columns := find_columns(srcfds[i].cmdfd, srcfds[i].datafd, table);
+ for (clist:=columns; clist!=nil; clist=tl clist) {
+ column := hd clist;
+ Qcol := COLUMN + (colqid<<3);
+ tree.create(big Qtable, dir(column.name,8r555,0,Qcol));
+ data := sys->sprint("%s %d\n", column.ctype, column.size);
+ contents = data :: contents;
+ colqid++;
+ }
+ }
+ sys->print("done\n");
+ }
+ i++;
+ }
+ colcontent := array[colqid] of string;
+ for (i = colqid - 1; i >= 0; i--) {
+ colcontent[i] = hd contents;
+ contents = tl contents;
+ }
+ (tchan, srv) := Styxserver.new(confd, Navigator.new(treeop), big Qroot);
+ sys->pctl(Sys->FORKNS|Sys->FORKFD, nil);
+ gm: ref Tmsg;
+ buf := array[Sys->ATOMICIO] of byte;
+ serverloop: for (;;) {
+ gm = <-tchan;
+ if (gm == nil)
+ break serverloop;
+ pick m := gm {
+ Readerror =>
+ sys->fprint(sys->fildes(2), "odbcmnt: fatal read error: %s\n", m.error);
+ break serverloop;
+ Read =>
+ c := srv.getfid(m.fid);
+ if(c.qtype & Sys->QTDIR){
+ srv.read(m); # does readdir
+ break;
+ }
+ case gettype(c.path) {
+ SQL =>
+ srcno := int c.path >> 3;
+ sys->seek(srcfds[srcno].datafd, m.offset, Sys->SEEKSTART);
+ n := sys->read(srcfds[srcno].datafd, buf, len buf);
+ if (n >= 0) {
+ (state, s, err) := bs->btos(nil, buf[:n], -1);
+ r := ref Rmsg.Read(gm.tag, array of byte s);
+ srv.reply(r);
+ } else
+ srv.reply(ref Rmsg.Error(gm.tag, sys->sprint("%r")));
+ break;
+ COLUMN =>
+ srv.reply(styxservers->readstr(m, colcontent[int c.path>>3]));
+ * =>
+ srv.default(gm);
+ }
+ Write =>
+ c := srv.getfid(m.fid);
+ case gettype(c.path) {
+ SQL =>
+ srcno := int c.path >> 3;
+ n := sys->write(srcfds[srcno].cmdfd, m.data, len m.data);
+ if (n == len m.data)
+ srv.reply(ref Rmsg.Write(m.tag, n));
+ else
+ srv.reply(ref Rmsg.Error(gm.tag, sys->sprint("%r")));
+ break;
+ * =>
+ srv.default(gm);
+ }
+
+ * =>
+ srv.default(gm);
+ }
+ }
+ tree.quit();
+}
+
+find_tables(cmdfd, datafd: ref Sys->FD): list of string
+{
+ tlist: list of string;
+ if (write_fd(cmdfd, "tables") < 0)
+ err(sys->sprint("failed to write to cmd file: %r"));
+ while((rec := read_fd(datafd)) != nil) {
+ fields := atokenize(rec, "|");
+ if (len fields < 4)
+ err("bad table name");
+ tname := fields[2];
+ tlist = tname :: tlist;
+ }
+ return tlist;
+}
+
+
+find_columns(cmdfd, datafd: ref Sys->FD, table: string): list of Column
+{
+ clist: list of Column;
+ if (write_fd(cmdfd, "columns " + table) < 0)
+ err(sys->sprint("failed to write to cmd file: %r"));
+ while((rec := read_fd(datafd)) != nil) {
+ fields := atokenize(rec, "|");
+ if (len fields < 3)
+ err("bad column name");
+ cname :=fields[3];
+ ctype := "";
+ if (len fields > 5)
+ ctype = fields[5];
+ csize := 0;
+ if (len fields > 6)
+ csize = int fields[6];
+ clist = (fields[3], ctype, csize) :: clist;
+ }
+ return clist;
+}
+
+atokenize(s: string, delim: string): array of string
+{
+ if (s == nil)
+ return nil;
+ dl := len delim;
+ r: list of string;
+ l: string;
+ for (;;) {
+ (l, s) = str->splitstrl(s, delim);
+ r = l :: r;
+ if (s == nil || s == delim)
+ break;
+ s = s[dl:];
+ }
+ a := array[len r] of string;
+ for (i:=len r-1; i>=0; i--) {
+ a[i] = hd r;
+ r = tl r;
+ }
+ return a;
+}
+
+find_sources(cdir: string): list of string
+{
+ sfile := cdir+"/sources";
+ fd := sys->open(sfile, Sys->OREAD);
+ if (fd == nil)
+ err(sys->sprint("failed to open %s: %r", sfile));
+ s := read_fd(fd);
+ (n, lines) := sys->tokenize(s, "\n");
+ return lines;
+}
+
+err(s: string)
+{
+ sys->fprint(stderr, "odbcgw: %s\n", s);
+ raise "fail:error";
+}
+
+read_fd(fd: ref Sys->FD): string
+{
+ MAX : con Sys->ATOMICIO;
+ buf := array[MAX] of byte;
+# sys->seek(fd, big 0, Sys->SEEKSTART);
+ size := sys->read(fd, buf, MAX);
+ if (size <= 0) {
+# if (size < 0)
+# sys->fprint(stderr, "read_fd error: %r\n");
+ return nil;
+ }
+ return string buf[0:size];
+}
+
+read_file(f: string): string
+{
+ fd := sys->open(f, Sys->OREAD);
+ if (fd == nil)
+ return nil;
+ return read_fd(fd);
+}
+
+write_fd(fd: ref Sys->FD, s: string): int
+{
+ a := array of byte s;
+ if (sys->write(fd, a, len a) != len a)
+ return -1;
+ return 0;
+} \ No newline at end of file