From 37da2899f40661e3e9631e497da8dc59b971cbd0 Mon Sep 17 00:00:00 2001 From: "Charles.Forsyth" Date: Fri, 22 Dec 2006 17:07:39 +0000 Subject: 20060303a --- CHANGES | 232 + FreeBSD/386/bin/data2c | Bin 0 -> 40729 bytes FreeBSD/386/bin/mk | Bin 0 -> 160607 bytes FreeBSD/386/bin/yacc | Bin 0 -> 94814 bytes FreeBSD/386/include/fpuctl.h | 76 + FreeBSD/386/include/lib9.h | 491 + Inferno/386/include/lib9.h | 8 + Inferno/386/include/u.h | 64 + Inferno/386/include/ureg.h | 25 + Inferno/arm/include/lib9.h | 8 + Inferno/arm/include/u.h | 65 + Inferno/arm/include/ureg.h | 24 + Inferno/mips/include/lib9.h | 8 + Inferno/mips/include/u.h | 65 + Inferno/power/include/lib9.h | 4 + Inferno/power/include/u.h | 84 + Inferno/power/include/ureg.h | 43 + Inferno/sparc/include/lib9.h | 9 + Inferno/sparc/include/u.h | 64 + Inferno/sparc/include/ureg.h | 45 + Inferno/thumb/include/lib9.h | 8 + Inferno/thumb/include/u.h | 65 + Inferno/thumb/include/ureg.h | 24 + Irix/mips/bin/awk | Bin 0 -> 213400 bytes Irix/mips/bin/mk | Bin 0 -> 272072 bytes Irix/mips/bin/mkext | Bin 0 -> 150136 bytes Irix/mips/bin/yacc | Bin 0 -> 198184 bytes Irix/mips/include/lib9.h | 463 + LICENCE | 37 + Linux/386/bin/data2c | Bin 0 -> 50083 bytes Linux/386/bin/mk | Bin 0 -> 190738 bytes Linux/386/bin/yacc | Bin 0 -> 111379 bytes Linux/386/include/fpuctl.h | 76 + Linux/386/include/lib9.h | 488 + MacOSX/README | 66 + MacOSX/power/bin/data2c | Bin 0 -> 25616 bytes MacOSX/power/bin/mk | Bin 0 -> 76736 bytes MacOSX/power/bin/yacc | Bin 0 -> 65948 bytes MacOSX/power/include/fpuctl.h | 81 + MacOSX/power/include/lib9.h | 495 + MacOSX/tcshrc | 12 + NOTICE | 52 + Nt/386/bin/awk.exe | Bin 0 -> 167936 bytes Nt/386/bin/c2l.exe | Bin 0 -> 225280 bytes Nt/386/bin/cp.exe | Bin 0 -> 61440 bytes Nt/386/bin/data2c.exe | Bin 0 -> 53248 bytes Nt/386/bin/echo.exe | Bin 0 -> 49152 bytes Nt/386/bin/format.exe | Bin 0 -> 65536 bytes Nt/386/bin/gzip.exe | Bin 0 -> 90112 bytes Nt/386/bin/infdb.exe | Bin 0 -> 88064 bytes Nt/386/bin/mk.exe | Bin 0 -> 114688 bytes Nt/386/bin/mkdir.exe | Bin 0 -> 49152 bytes Nt/386/bin/mkext.exe | Bin 0 -> 69632 bytes Nt/386/bin/mv.exe | Bin 0 -> 61440 bytes Nt/386/bin/rcsh.exe | Bin 0 -> 118784 bytes Nt/386/bin/rm.exe | Bin 0 -> 57344 bytes Nt/386/bin/sed.exe | Bin 0 -> 77824 bytes Nt/386/bin/test.exe | Bin 0 -> 61440 bytes Nt/386/bin/tr.exe | Bin 0 -> 53248 bytes Nt/386/bin/yacc.exe | Bin 0 -> 86016 bytes Nt/386/include/lib9.h | 487 + Plan9/386/bin/data2c | Bin 0 -> 60240 bytes Plan9/386/include/lib9.h | 680 + Plan9/386/include/u.h | 64 + Plan9/mips/include/lib9.h | 680 + Plan9/mips/include/u.h | 65 + Plan9/power/include/lib9.h | 687 + Plan9/power/include/u.h | 84 + Plan9/sparc/include/lib9.h | 679 + Plan9/sparc/include/os.h | 17 + Plan9/sparc/include/u.h | 64 + README.gcode | 11 + Solaris/sparc/bin/data2c | Bin 0 -> 261272 bytes Solaris/sparc/bin/mk | Bin 0 -> 826928 bytes Solaris/sparc/bin/mkext | Bin 0 -> 424500 bytes Solaris/sparc/bin/yacc | Bin 0 -> 421936 bytes Solaris/sparc/include/fpuctl.h | 75 + Solaris/sparc/include/lib9.h | 476 + acme/acid/Acid.dis | Bin 0 -> 360 bytes acme/acid/Acid0.dis | Bin 0 -> 117 bytes acme/acid/guide | 2 + acme/acid/readme | 12 + acme/bin/guide | 5 + acme/bin/readme | 25 + acme/dis/adiff.dis | Bin 0 -> 2638 bytes acme/dis/agrep.dis | Bin 0 -> 514 bytes acme/dis/awd.dis | Bin 0 -> 455 bytes acme/dis/cd.dis | Bin 0 -> 891 bytes acme/dis/new.dis | Bin 0 -> 1157 bytes acme/dis/spout.dis | Bin 0 -> 1481 bytes acme/dis/win.dis | Bin 0 -> 9295 bytes acme/dis/winm.dis | Bin 0 -> 9454 bytes acme/edit/a.dis | Bin 0 -> 4287 bytes acme/edit/c.dis | Bin 0 -> 4481 bytes acme/edit/d.dis | Bin 0 -> 314 bytes acme/edit/e.dis | Bin 0 -> 3986 bytes acme/edit/g.dis | Bin 0 -> 4625 bytes acme/edit/guide | 4 + acme/edit/i.dis | Bin 0 -> 4286 bytes acme/edit/p.dis | Bin 0 -> 4731 bytes acme/edit/pipe.dis | Bin 0 -> 6305 bytes acme/edit/readme | 31 + acme/edit/x.dis | Bin 0 -> 5052 bytes acme/mail/Mail.dis | Bin 0 -> 20058 bytes acme/mail/Mailpop3.dis | Bin 0 -> 18989 bytes acme/mail/guide | 5 + acme/mail/mkbox.dis | Bin 0 -> 263 bytes acme/mail/readme | 29 + appl/NOTICE | 25 + appl/acme/acme.b | 1125 + appl/acme/acme.m | 32 + appl/acme/acme/acid/guide | 2 + appl/acme/acme/acid/mkfile | 24 + appl/acme/acme/acid/readme | 12 + appl/acme/acme/acid/src/Acid.b | 31 + appl/acme/acme/acid/src/Acid0.b | 86 + appl/acme/acme/acid/src/mkfile | 20 + appl/acme/acme/bin/guide | 5 + appl/acme/acme/bin/mkfile | 24 + appl/acme/acme/bin/readme | 25 + appl/acme/acme/bin/src/adiff.b | 155 + appl/acme/acme/bin/src/agrep.b | 46 + appl/acme/acme/bin/src/awd.b | 45 + appl/acme/acme/bin/src/cd.b | 70 + appl/acme/acme/bin/src/mkfile | 22 + appl/acme/acme/bin/src/new.b | 70 + appl/acme/acme/bin/src/spout.b | 143 + appl/acme/acme/bin/src/win.b | 764 + appl/acme/acme/bin/src/winm.b | 791 + appl/acme/acme/edit/guide | 4 + appl/acme/acme/edit/mkfile | 24 + appl/acme/acme/edit/readme | 31 + appl/acme/acme/edit/src/a.b | 89 + appl/acme/acme/edit/src/c.b | 104 + appl/acme/acme/edit/src/d.b | 30 + appl/acme/acme/edit/src/e.b | 154 + appl/acme/acme/edit/src/findfile.b | 210 + appl/acme/acme/edit/src/g.b | 95 + appl/acme/acme/edit/src/i.b | 88 + appl/acme/acme/edit/src/input.b | 84 + appl/acme/acme/edit/src/mkfile | 23 + appl/acme/acme/edit/src/p.b | 109 + appl/acme/acme/edit/src/pipe.b | 212 + appl/acme/acme/edit/src/x.b | 123 + appl/acme/acme/edit/src/xxx.b | 327 + appl/acme/acme/mail/guide | 5 + appl/acme/acme/mail/mkbox.b | 25 + appl/acme/acme/mail/mkfile | 34 + appl/acme/acme/mail/readme | 29 + appl/acme/acme/mail/src/Mail.b | 1715 + appl/acme/acme/mail/src/Mailp.b | 1684 + appl/acme/acme/mail/src/Mailpop3.b | 1716 + appl/acme/acme/mail/src/mashfile | 18 + appl/acme/acme/mail/src/mkfile | 16 + appl/acme/acme/mkfile | 9 + appl/acme/buff.b | 380 + appl/acme/buff.m | 34 + appl/acme/col.b | 610 + appl/acme/col.m | 26 + appl/acme/common.m | 30 + appl/acme/dat.b | 107 + appl/acme/dat.m | 280 + appl/acme/disk.b | 136 + appl/acme/disk.m | 19 + appl/acme/ecmd.b | 1349 + appl/acme/ecmd.m | 18 + appl/acme/edit.b | 676 + appl/acme/edit.m | 85 + appl/acme/elog.b | 353 + appl/acme/elog.m | 24 + appl/acme/exec.b | 1350 + appl/acme/exec.m | 19 + appl/acme/file.b | 331 + appl/acme/file.m | 40 + appl/acme/frame.b | 1189 + appl/acme/frame.m | 54 + appl/acme/fsys.b | 866 + appl/acme/fsys.m | 18 + appl/acme/graph.b | 82 + appl/acme/graph.m | 18 + appl/acme/gui.b | 126 + appl/acme/gui.m | 15 + appl/acme/look.b | 743 + appl/acme/look.m | 17 + appl/acme/mkfile | 90 + appl/acme/regx.b | 1050 + appl/acme/regx.m | 13 + appl/acme/row.b | 767 + appl/acme/row.m | 29 + appl/acme/scrl.b | 187 + appl/acme/scrl.m | 9 + appl/acme/styxaux.b | 174 + appl/acme/styxaux.m | 24 + appl/acme/text.b | 1404 + appl/acme/text.m | 65 + appl/acme/time.b | 129 + appl/acme/time.m | 10 + appl/acme/util.b | 574 + appl/acme/util.m | 52 + appl/acme/wind.b | 554 + appl/acme/wind.m | 67 + appl/acme/xfid.b | 1087 + appl/acme/xfid.m | 34 + appl/alphabet/abc/abc.b | 53 + appl/alphabet/abc/autoconvert.b | 80 + appl/alphabet/abc/autodeclare.b | 42 + appl/alphabet/abc/declare.b | 70 + appl/alphabet/abc/declares.b | 124 + appl/alphabet/abc/define.b | 52 + appl/alphabet/abc/eval.b | 66 + appl/alphabet/abc/import.b | 53 + appl/alphabet/abc/mkfile | 29 + appl/alphabet/abc/newtypeset.b | 147 + appl/alphabet/abc/rewrite.b | 71 + appl/alphabet/abc/type.b | 53 + appl/alphabet/abc/typeset.b | 51 + appl/alphabet/abc/undeclare.b | 48 + appl/alphabet/alphabet.b | 1677 + appl/alphabet/alphabet.proto | 29 + appl/alphabet/alphabet.shmod.b | 413 + appl/alphabet/auxi/endpoints.b | 105 + appl/alphabet/auxi/endpointsrv.b | 58 + appl/alphabet/auxi/fsfilter.b | 62 + appl/alphabet/auxi/mkfile | 21 + appl/alphabet/auxi/rexecsrv.b | 301 + appl/alphabet/declare.sh | 25 + appl/alphabet/eval.b | 757 + appl/alphabet/extvalues.b | 49 + appl/alphabet/fs/and.b | 70 + appl/alphabet/fs/bundle.b | 210 + appl/alphabet/fs/bundle.m | 9 + appl/alphabet/fs/chstat.b | 189 + appl/alphabet/fs/compose.b | 105 + appl/alphabet/fs/depth.b | 54 + appl/alphabet/fs/entries.b | 91 + appl/alphabet/fs/exec.b | 172 + appl/alphabet/fs/filter.b | 66 + appl/alphabet/fs/ls.b | 107 + appl/alphabet/fs/match.b | 84 + appl/alphabet/fs/merge.b | 192 + appl/alphabet/fs/mergewrite.b | 244 + appl/alphabet/fs/mkext.b | 266 + appl/alphabet/fs/mkfile | 55 + appl/alphabet/fs/mode.b | 125 + appl/alphabet/fs/newer.b | 64 + appl/alphabet/fs/not.b | 53 + appl/alphabet/fs/or.b | 70 + appl/alphabet/fs/path.b | 82 + appl/alphabet/fs/pipe.b | 230 + appl/alphabet/fs/print.b | 61 + appl/alphabet/fs/proto.b | 416 + appl/alphabet/fs/query.b | 135 + appl/alphabet/fs/run.b | 65 + appl/alphabet/fs/select.b | 60 + appl/alphabet/fs/setroot.b | 109 + appl/alphabet/fs/size.b | 64 + appl/alphabet/fs/unbundle.b | 259 + appl/alphabet/fs/unbundle.m | 9 + appl/alphabet/fs/walk.b | 242 + appl/alphabet/fs/write.b | 137 + appl/alphabet/fsdecl.sh | 13 + appl/alphabet/getendpoint.sh | 13 + appl/alphabet/grid/farm.b | 144 + appl/alphabet/grid/line2rec.b | 91 + appl/alphabet/grid/local.b | 86 + appl/alphabet/grid/mkfile | 22 + appl/alphabet/grid/remote.b | 88 + appl/alphabet/grid/rexec.b | 112 + appl/alphabet/main/auth.b | 157 + appl/alphabet/main/cat.b | 78 + appl/alphabet/main/create.b | 55 + appl/alphabet/main/dial.b | 85 + appl/alphabet/main/echo.b | 51 + appl/alphabet/main/export.b | 52 + appl/alphabet/main/fd.b | 83 + appl/alphabet/main/filter.b | 114 + appl/alphabet/main/genfilter.b | 79 + appl/alphabet/main/mkfile | 36 + appl/alphabet/main/mount.b | 80 + appl/alphabet/main/par.b | 50 + appl/alphabet/main/parse.b | 43 + appl/alphabet/main/pretty.b | 116 + appl/alphabet/main/print.b | 55 + appl/alphabet/main/read.b | 56 + appl/alphabet/main/readall.b | 46 + appl/alphabet/main/rewrite.b | 97 + appl/alphabet/main/rw.b | 50 + appl/alphabet/main/seq.b | 66 + appl/alphabet/main/unparse.b | 38 + appl/alphabet/main/w2fd.b | 61 + appl/alphabet/main/wait.b | 35 + appl/alphabet/mkendpoint.sh | 14 + appl/alphabet/mkfile | 49 + appl/alphabet/newtypesets | 229 + appl/alphabet/proxy.b | 304 + appl/alphabet/reports.b | 189 + appl/alphabet/rexecsrv.sh | 9 + appl/alphabet/setup | 63 + appl/alphabet/typesets/abc.b | 180 + appl/alphabet/typesets/abctypes.b | 229 + appl/alphabet/typesets/fs.b | 226 + appl/alphabet/typesets/fstypes.b | 230 + appl/alphabet/typesets/grid.b | 160 + appl/alphabet/typesets/gridtypes.b | 230 + appl/alphabet/typesets/mkfile | 34 + appl/charon/build.b | 2862 ++ appl/charon/build.m | 478 + appl/charon/charon.b | 2171 + appl/charon/charon.m | 20 + appl/charon/chutils.b | 2050 + appl/charon/chutils.m | 371 + appl/charon/common.m | 23 + appl/charon/cookiesrv.b | 595 + appl/charon/cookiesrv.m | 12 + appl/charon/ctype.b | 70 + appl/charon/ctype.m | 24 + appl/charon/date.b | 62 + appl/charon/date.m | 12 + appl/charon/event.b | 273 + appl/charon/event.m | 100 + appl/charon/file.b | 134 + appl/charon/ftp.b | 314 + appl/charon/gui.b | 560 + appl/charon/gui.m | 48 + appl/charon/http.b | 1040 + appl/charon/img.b | 3607 ++ appl/charon/img.m | 115 + appl/charon/jscript.b | 3025 ++ appl/charon/layout.b | 4828 +++ appl/charon/layout.m | 235 + appl/charon/lex.b | 1340 + appl/charon/lex.m | 105 + appl/charon/mkfile | 92 + appl/charon/paginate.b | 511 + appl/charon/paginate.m | 16 + appl/charon/rgb.inc | 620 + appl/charon/script.m | 14 + appl/charon/transport.m | 13 + appl/charon/url.b | 225 + appl/charon/url.m | 30 + appl/charon/xxx.inc | 75 + appl/charon/ycbcr.inc | 621 + appl/cmd/9660srv.b | 1504 + appl/cmd/9export.b | 180 + appl/cmd/9srvfs.b | 99 + appl/cmd/9win.b | 453 + appl/cmd/B.b | 107 + appl/cmd/archfs.b | 630 + appl/cmd/auplay.b | 114 + appl/cmd/auth/aescbc.b | 254 + appl/cmd/auth/changelogin.b | 305 + appl/cmd/auth/convpasswd.b | 120 + appl/cmd/auth/countersigner.b | 59 + appl/cmd/auth/createsignerkey.b | 144 + appl/cmd/auth/factotum/authio.m | 80 + appl/cmd/auth/factotum/factotum.b | 978 + appl/cmd/auth/factotum/feedkey.b | 321 + appl/cmd/auth/factotum/mkfile | 27 + appl/cmd/auth/factotum/proto/infauth.b | 362 + appl/cmd/auth/factotum/proto/keyreps.b | 173 + appl/cmd/auth/factotum/proto/keyreps.m | 23 + appl/cmd/auth/factotum/proto/mkfile | 22 + appl/cmd/auth/factotum/proto/p9any.b | 232 + appl/cmd/auth/factotum/proto/pass.b | 29 + appl/cmd/auth/factotum/rpc.b | 68 + appl/cmd/auth/getpk.b | 83 + appl/cmd/auth/keyfs.b | 806 + appl/cmd/auth/keysrv.b | 199 + appl/cmd/auth/logind.b | 244 + appl/cmd/auth/mkauthinfo.b | 125 + appl/cmd/auth/mkfile | 38 + appl/cmd/auth/passwd.b | 290 + appl/cmd/auth/secstore.b | 317 + appl/cmd/auth/signer.b | 132 + appl/cmd/auth/verify.b | 85 + appl/cmd/auxi/cpuslave.b | 79 + appl/cmd/auxi/digest.b | 91 + appl/cmd/auxi/fpgaload.b | 67 + appl/cmd/auxi/mangaload.b | 362 + appl/cmd/auxi/mkfile | 24 + appl/cmd/auxi/pcmcia.b | 491 + appl/cmd/auxi/rdbgsrv.b | 222 + appl/cmd/auxi/rstyxd.b | 114 + appl/cmd/avr/burn.b | 859 + appl/cmd/avr/mkfile | 10 + appl/cmd/basename.b | 50 + appl/cmd/bind.b | 66 + appl/cmd/bit2gif.b | 86 + appl/cmd/broke.b | 84 + appl/cmd/bytes.b | 212 + appl/cmd/cal.b | 295 + appl/cmd/cat.b | 57 + appl/cmd/cd.b | 48 + appl/cmd/chgrp.b | 58 + appl/cmd/chmod.b | 125 + appl/cmd/cleanname.b | 45 + appl/cmd/cmp.b | 151 + appl/cmd/comm.b | 124 + appl/cmd/cook.b | 1924 + appl/cmd/cp.b | 237 + appl/cmd/cprof.b | 190 + appl/cmd/cpu.b | 168 + appl/cmd/crypt.b | 234 + appl/cmd/date.b | 71 + appl/cmd/dbfs.b | 518 + appl/cmd/dbm/delete.b | 34 + appl/cmd/dbm/fetch.b | 37 + appl/cmd/dbm/keys.b | 32 + appl/cmd/dbm/list.b | 34 + appl/cmd/dbm/mkfile | 19 + appl/cmd/dbm/store.b | 69 + appl/cmd/dd.b | 625 + appl/cmd/dial.b | 148 + appl/cmd/diff.b | 858 + appl/cmd/disdep.b | 250 + appl/cmd/disdump.b | 52 + appl/cmd/disk/format.b | 755 + appl/cmd/disk/ftl.b | 911 + appl/cmd/disk/kfs.b | 3842 ++ appl/cmd/disk/kfscmd.b | 53 + appl/cmd/disk/mbr.b | 134 + appl/cmd/disk/mkext.b | 377 + appl/cmd/disk/mkfile | 25 + appl/cmd/disk/mkfs.b | 778 + appl/cmd/disk/prep/calc.tab.b | 454 + appl/cmd/disk/prep/calc.tab.m | 7 + appl/cmd/disk/prep/calc.y | 174 + appl/cmd/disk/prep/fdisk.b | 925 + appl/cmd/disk/prep/mkfile | 26 + appl/cmd/disk/prep/pedit.b | 504 + appl/cmd/disk/prep/pedit.m | 53 + appl/cmd/disk/prep/prep.b | 509 + appl/cmd/dossrv.b | 3432 ++ appl/cmd/du.b | 163 + appl/cmd/echo.b | 36 + appl/cmd/ed.b | 1588 + appl/cmd/emuinit.b | 110 + appl/cmd/env.b | 53 + appl/cmd/export.b | 57 + appl/cmd/fc.b | 612 + appl/cmd/fcp.b | 312 + appl/cmd/fmt.b | 204 + appl/cmd/fone.b | 560 + appl/cmd/fortune.b | 100 + appl/cmd/freq.b | 112 + appl/cmd/fs.b | 109 + appl/cmd/fs/and.b | 65 + appl/cmd/fs/bundle.b | 195 + appl/cmd/fs/chstat.b | 185 + appl/cmd/fs/compose.b | 100 + appl/cmd/fs/depth.b | 49 + appl/cmd/fs/entries.b | 86 + appl/cmd/fs/eval.b | 648 + appl/cmd/fs/exec.b | 162 + appl/cmd/fs/filter.b | 64 + appl/cmd/fs/ls.b | 97 + appl/cmd/fs/match.b | 79 + appl/cmd/fs/merge.b | 187 + appl/cmd/fs/mergewrite.b | 186 + appl/cmd/fs/mkfile | 60 + appl/cmd/fs/mode.b | 120 + appl/cmd/fs/not.b | 48 + appl/cmd/fs/or.b | 65 + appl/cmd/fs/path.b | 77 + appl/cmd/fs/pipe.b | 223 + appl/cmd/fs/print.b | 51 + appl/cmd/fs/proto.b | 388 + appl/cmd/fs/query.b | 130 + appl/cmd/fs/readfile.b | 144 + appl/cmd/fs/run.b | 60 + appl/cmd/fs/select.b | 56 + appl/cmd/fs/setroot.b | 104 + appl/cmd/fs/size.b | 54 + appl/cmd/fs/template.b | 35 + appl/cmd/fs/unbundle.b | 259 + appl/cmd/fs/void.b | 33 + appl/cmd/fs/walk.b | 233 + appl/cmd/fs/write.b | 111 + appl/cmd/ftest.b | 153 + appl/cmd/ftpfs.b | 1959 + appl/cmd/getauthinfo.b | 185 + appl/cmd/getfile.b | 74 + appl/cmd/gettar.b | 248 + appl/cmd/gif2bit.b | 101 + appl/cmd/grep.b | 155 + appl/cmd/gunzip.b | 139 + appl/cmd/gzip.b | 228 + appl/cmd/idea.b | 116 + appl/cmd/import.b | 192 + appl/cmd/install/NOTICE | 6 + appl/cmd/install/applylog.b | 699 + appl/cmd/install/arch.b | 288 + appl/cmd/install/arch.m | 36 + appl/cmd/install/archfs.b | 579 + appl/cmd/install/archfs.m | 7 + appl/cmd/install/ckproto.b | 267 + appl/cmd/install/create.b | 445 + appl/cmd/install/eproto.b | 357 + appl/cmd/install/info.b | 73 + appl/cmd/install/inst.b | 500 + appl/cmd/install/install.b | 430 + appl/cmd/install/log.b | 76 + appl/cmd/install/logs.b | 287 + appl/cmd/install/logs.m | 44 + appl/cmd/install/mergelog.b | 239 + appl/cmd/install/mkfile | 43 + appl/cmd/install/mkproto.b | 99 + appl/cmd/install/proto.b | 320 + appl/cmd/install/proto.m | 6 + appl/cmd/install/proto2list.b | 209 + appl/cmd/install/protocaller.m | 8 + appl/cmd/install/updatelog.b | 386 + appl/cmd/install/wdiff.b | 148 + appl/cmd/install/wfind.b | 204 + appl/cmd/install/wrap.b | 684 + appl/cmd/install/wrap.m | 41 + appl/cmd/install/wrap2list.b | 305 + appl/cmd/iostats.b | 635 + appl/cmd/ip/bootpd.b | 662 + appl/cmd/ip/dhcp.b | 162 + appl/cmd/ip/mkfile | 30 + appl/cmd/ip/nppp/mkfile | 24 + appl/cmd/ip/nppp/modem.b | 469 + appl/cmd/ip/nppp/modem.m | 47 + appl/cmd/ip/nppp/pppchat.b | 322 + appl/cmd/ip/nppp/ppplink.b | 782 + appl/cmd/ip/nppp/ppptest.b | 90 + appl/cmd/ip/nppp/script.b | 171 + appl/cmd/ip/nppp/script.m | 15 + appl/cmd/ip/obootpd.b | 777 + appl/cmd/ip/ping.b | 369 + appl/cmd/ip/ppp/mkfile | 27 + appl/cmd/ip/ppp/modem.b | 468 + appl/cmd/ip/ppp/modem.m | 41 + appl/cmd/ip/ppp/pppclient.b | 216 + appl/cmd/ip/ppp/pppclient.m | 31 + appl/cmd/ip/ppp/pppdial.b | 283 + appl/cmd/ip/ppp/pppgui.b | 373 + appl/cmd/ip/ppp/pppgui.m | 21 + appl/cmd/ip/ppp/ppptest.b | 86 + appl/cmd/ip/ppp/script.b | 168 + appl/cmd/ip/ppp/script.m | 14 + appl/cmd/ip/rip.b | 620 + appl/cmd/ip/sntp.b | 313 + appl/cmd/ip/tftpd.b | 514 + appl/cmd/ip/virgild.b | 127 + appl/cmd/irtest.b | 70 + appl/cmd/itest.b | 478 + appl/cmd/itreplay.b | 230 + appl/cmd/kill.b | 146 + appl/cmd/lc.b | 156 + appl/cmd/lego/clock.b | 214 + appl/cmd/lego/clockface.b | 384 + appl/cmd/lego/firmdl.b | 294 + appl/cmd/lego/link.b | 603 + appl/cmd/lego/mkfile | 23 + appl/cmd/lego/rcxsend.b | 240 + appl/cmd/lego/rcxsend.m | 6 + appl/cmd/lego/send.b | 86 + appl/cmd/lego/timers.b | 263 + appl/cmd/lego/timers.m | 17 + appl/cmd/limbo/arg.m | 50 + appl/cmd/limbo/asm.b | 263 + appl/cmd/limbo/com.b | 1387 + appl/cmd/limbo/decls.b | 1177 + appl/cmd/limbo/dis.b | 560 + appl/cmd/limbo/disoptab.m | 355 + appl/cmd/limbo/ecom.b | 2345 ++ appl/cmd/limbo/gen.b | 1012 + appl/cmd/limbo/isa.m | 247 + appl/cmd/limbo/lex.b | 1146 + appl/cmd/limbo/limbo.b | 3099 ++ appl/cmd/limbo/limbo.m | 527 + appl/cmd/limbo/limbo.y | 1973 + appl/cmd/limbo/mkfile | 35 + appl/cmd/limbo/nodes.b | 1402 + appl/cmd/limbo/opname.m | 109 + appl/cmd/limbo/optim.b | 3 + appl/cmd/limbo/sbl.b | 397 + appl/cmd/limbo/stubs.b | 575 + appl/cmd/limbo/typecheck.b | 3223 ++ appl/cmd/limbo/types.b | 4234 ++ appl/cmd/listen.b | 261 + appl/cmd/lockfs.b | 773 + appl/cmd/logfile.b | 259 + appl/cmd/look.b | 393 + appl/cmd/lookman.b | 250 + appl/cmd/ls.b | 318 + appl/cmd/lstar.b | 120 + appl/cmd/man.b | 199 + appl/cmd/man2txt.b | 79 + appl/cmd/manufacture.b | 42 + appl/cmd/mash/builtins.b | 347 + appl/cmd/mash/depends.b | 228 + appl/cmd/mash/dump.b | 199 + appl/cmd/mash/exec.b | 401 + appl/cmd/mash/expr.b | 158 + appl/cmd/mash/eyacc.b | 2785 ++ appl/cmd/mash/eyaccpar | 223 + appl/cmd/mash/history.b | 206 + appl/cmd/mash/lex.b | 547 + appl/cmd/mash/make.b | 723 + appl/cmd/mash/mash.b | 154 + appl/cmd/mash/mash.m | 372 + appl/cmd/mash/mash.y | 269 + appl/cmd/mash/mashfile | 36 + appl/cmd/mash/mashlib.b | 60 + appl/cmd/mash/mashparse.b | 662 + appl/cmd/mash/mashparse.m | 56 + appl/cmd/mash/misc.b | 313 + appl/cmd/mash/mkfile | 78 + appl/cmd/mash/serve.b | 154 + appl/cmd/mash/symb.b | 265 + appl/cmd/mash/tk.b | 603 + appl/cmd/mash/xeq.b | 543 + appl/cmd/mathcalc.b | 79 + appl/cmd/mc.b | 2547 ++ appl/cmd/md5sum.b | 65 + appl/cmd/mdb.b | 335 + appl/cmd/memfs.b | 648 + appl/cmd/metamorph.b | 94 + appl/cmd/mk/ar.m | 26 + appl/cmd/mk/mk.b | 4211 ++ appl/cmd/mk/mkbinds | 2 + appl/cmd/mk/mkconfig | 28 + appl/cmd/mk/mkfile | 19 + appl/cmd/mk/mksubdirs | 16 + appl/cmd/mkdir.b | 75 + appl/cmd/mkfile | 219 + appl/cmd/mntgen.b | 188 + appl/cmd/mount.b | 348 + appl/cmd/mouse.b | 394 + appl/cmd/mpc/mkfile | 14 + appl/cmd/mpc/qconfig.b | 193 + appl/cmd/mpc/qflash.b | 188 + appl/cmd/mprof.b | 260 + appl/cmd/mv.b | 184 + appl/cmd/ndb/cs.b | 676 + appl/cmd/ndb/csquery.b | 97 + appl/cmd/ndb/dns.b | 1860 + appl/cmd/ndb/dnsquery.b | 177 + appl/cmd/ndb/mkfile | 28 + appl/cmd/ndb/mkhash.b | 119 + appl/cmd/ndb/query.b | 135 + appl/cmd/ndb/registry.b | 671 + appl/cmd/ndb/regquery.b | 104 + appl/cmd/netkey.b | 166 + appl/cmd/netstat.b | 91 + appl/cmd/newer.b | 36 + appl/cmd/ns.b | 157 + appl/cmd/nsbuild.b | 41 + appl/cmd/os.b | 155 + appl/cmd/p.b | 141 + appl/cmd/palm/connex.b | 124 + appl/cmd/palm/desklink.b | 843 + appl/cmd/palm/desklink.m | 90 + appl/cmd/palm/mkfile | 16 + appl/cmd/palm/palmsrv.b | 901 + appl/cmd/pause.b | 17 + appl/cmd/plumb.b | 115 + appl/cmd/plumber.b | 766 + appl/cmd/prof.b | 243 + appl/cmd/promptstring.b | 66 + appl/cmd/ps.b | 61 + appl/cmd/puttar.b | 183 + appl/cmd/pwd.b | 28 + appl/cmd/ramfile.b | 97 + appl/cmd/randpass.b | 45 + appl/cmd/raw2iaf.b | 122 + appl/cmd/rawdbfs.b | 813 + appl/cmd/rcmd.b | 170 + appl/cmd/rdp.b | 1230 + appl/cmd/read.b | 62 + appl/cmd/rioimport.b | 620 + appl/cmd/rm.b | 99 + appl/cmd/runas.b | 60 + appl/cmd/sed.b | 908 + appl/cmd/sendmail.b | 252 + appl/cmd/sh/arg.b | 181 + appl/cmd/sh/csv.b | 244 + appl/cmd/sh/doc/History | 14 + appl/cmd/sh/echo.b | 96 + appl/cmd/sh/expr.b | 281 + appl/cmd/sh/file2chan.b | 459 + appl/cmd/sh/mkfile | 60 + appl/cmd/sh/regex.b | 220 + appl/cmd/sh/sexprs.b | 271 + appl/cmd/sh/sh.b | 2843 ++ appl/cmd/sh/sh.y | 2592 ++ appl/cmd/sh/std.b | 812 + appl/cmd/sh/string.b | 212 + appl/cmd/sh/test.b | 96 + appl/cmd/sh/tk.b | 426 + appl/cmd/sha1sum.b | 65 + appl/cmd/shutdown.b | 72 + appl/cmd/sleep.b | 46 + appl/cmd/sort.b | 129 + appl/cmd/spki/mkfile | 22 + appl/cmd/spki/verify.b | 107 + appl/cmd/src.b | 28 + appl/cmd/stack.b | 184 + appl/cmd/stackv.b | 445 + appl/cmd/stream.b | 98 + appl/cmd/strings.b | 87 + appl/cmd/styxchat.b | 557 + appl/cmd/styxlisten.b | 262 + appl/cmd/styxmon.b | 110 + appl/cmd/sum.b | 59 + appl/cmd/tail.b | 379 + appl/cmd/tarfs.b | 411 + appl/cmd/tclsh.b | 48 + appl/cmd/tcs.b | 184 + appl/cmd/tee.b | 79 + appl/cmd/telnet.b | 482 + appl/cmd/test.b | 278 + appl/cmd/time.b | 97 + appl/cmd/timestamp.b | 42 + appl/cmd/tkcmd.b | 190 + appl/cmd/tokenize.b | 33 + appl/cmd/touch.b | 77 + appl/cmd/touchcal.b | 278 + appl/cmd/tr.b | 319 + appl/cmd/tsort.b | 133 + appl/cmd/unicode.b | 162 + appl/cmd/uniq.b | 79 + appl/cmd/units.b | 1061 + appl/cmd/units.y | 771 + appl/cmd/unmount.b | 44 + appl/cmd/usb/mkfile | 11 + appl/cmd/usb/usbd.b | 835 + appl/cmd/uudecode.b | 132 + appl/cmd/uuencode.b | 101 + appl/cmd/wav2iaf.b | 171 + appl/cmd/wc.b | 303 + appl/cmd/webgrab.b | 532 + appl/cmd/wish.b | 191 + appl/cmd/wmexport.b | 557 + appl/cmd/wmimport.b | 64 + appl/cmd/xargs.b | 86 + appl/cmd/xd.b | 316 + appl/cmd/xmount.b | 231 + appl/cmd/yacc.b | 2810 ++ appl/cmd/zeros.b | 68 + appl/collab/clients/chat.b | 224 + appl/collab/clients/poll.b | 282 + appl/collab/clients/poller.b | 330 + appl/collab/clients/whiteboard.b | 586 + appl/collab/collabsrv.b | 176 + appl/collab/connect.b | 156 + appl/collab/lib/messages.b | 86 + appl/collab/lib/messages.m | 42 + appl/collab/mkfile | 62 + appl/collab/proxy.b | 177 + appl/collab/proxy.m | 5 + appl/collab/runcollab | 8 + appl/collab/servers/chatsrv.b | 263 + appl/collab/servers/memfssrv.b | 20 + appl/collab/servers/mpx.b | 301 + appl/collab/servers/wbsrv.b | 226 + appl/collab/service.m | 4 + appl/collab/srvmgr.b | 190 + appl/collab/srvmgr.m | 22 + appl/demo/camera/camera.b | 2557 ++ appl/demo/camera/camera.dis | Bin 0 -> 37619 bytes appl/demo/camera/camera.sbl | 6687 +++ appl/demo/camera/camload.bit | Bin 0 -> 53700 bytes appl/demo/camera/camproc.bit | Bin 0 -> 7650 bytes appl/demo/camera/mkfile | 42 + appl/demo/camera/runcam.sh | 3 + appl/demo/camera/runcamlocal.sh | 3 + appl/demo/camera/tkinterface.b | 2508 ++ appl/demo/camera/tkinterface.dis | Bin 0 -> 56826 bytes appl/demo/camera/tkinterface.sbl | 7950 ++++ appl/demo/chat/chat.b | 203 + appl/demo/chat/chat.dis | Bin 0 -> 3995 bytes appl/demo/chat/chat.sbl | 695 + appl/demo/chat/chatclient.sh | 20 + appl/demo/chat/chatsrv.b | 268 + appl/demo/chat/chatsrv.dis | Bin 0 -> 4058 bytes appl/demo/chat/chatsrv.sbl | 938 + appl/demo/chat/mkfile | 30 + appl/demo/cpupool/mkfile | 26 + appl/demo/cpupool/regpoll.b | 63 + appl/demo/cpupool/regpoll.dis | Bin 0 -> 904 bytes appl/demo/cpupool/regpoll.sbl | 262 + appl/demo/cpupool/runrstyx.sh | 50 + appl/demo/lego/clockface.b | 367 + appl/demo/lego/clockface.dis | Bin 0 -> 4676 bytes appl/demo/lego/clockface.sbl | 998 + appl/demo/lego/clockreg.sh | 17 + appl/demo/lego/firmdl.b | 294 + appl/demo/lego/firmdl.dis | Bin 0 -> 4423 bytes appl/demo/lego/firmdl.sbl | 819 + appl/demo/lego/legolink.b | 601 + appl/demo/lego/legolink.dis | Bin 0 -> 6591 bytes appl/demo/lego/legolink.sbl | 1298 + appl/demo/lego/mkfile | 36 + appl/demo/lego/rcxsend.b | 240 + appl/demo/lego/rcxsend.dis | Bin 0 -> 3000 bytes appl/demo/lego/rcxsend.m | 4 + appl/demo/lego/rcxsend.sbl | 552 + appl/demo/lego/styx.srec | 329 + appl/demo/lego/timers.b | 263 + appl/demo/lego/timers.dis | Bin 0 -> 1645 bytes appl/demo/lego/timers.m | 15 + appl/demo/lego/timers.sbl | 441 + appl/demo/mkfile | 14 + appl/demo/ns/mkfile | 27 + appl/demo/ns/ns.b | 124 + appl/demo/ns/ns.dis | Bin 0 -> 1973 bytes appl/demo/ns/ns.sbl | 420 + appl/demo/ns/runns.sh | 7 + appl/demo/odbc/mkfile | 31 + appl/demo/odbc/odbcmnt.b | 428 + appl/demo/odbc/odbcmnt.dis | Bin 0 -> 8085 bytes appl/demo/odbc/odbcmnt.sbl | 1602 + appl/demo/odbc/runodbc.sh | 17 + appl/demo/spree/mkfile | 23 + appl/demo/spree/spreeclient.sh | 44 + appl/demo/whiteboard/mkfile | 28 + appl/demo/whiteboard/runwb.sh | 7 + appl/demo/whiteboard/wbsrv.b | 268 + appl/demo/whiteboard/wbsrv.dis | Bin 0 -> 3717 bytes appl/demo/whiteboard/wbsrv.sbl | 818 + appl/demo/whiteboard/whiteboard.b | 605 + appl/demo/whiteboard/whiteboard.dis | Bin 0 -> 11229 bytes appl/demo/whiteboard/whiteboard.sbl | 1880 + appl/ebook/checkxml.b | 132 + appl/ebook/cssfont.b | 179 + appl/ebook/cssfont.m | 9 + appl/ebook/cssparser.b | 143 + appl/ebook/cssparser.m | 11 + appl/ebook/dtd/oebdoc101.dtd | 524 + appl/ebook/dtd/oebpkg101.dtd | 280 + appl/ebook/ebook.b | 1893 + appl/ebook/mimeimage.b | 96 + appl/ebook/mimeimage.m | 7 + appl/ebook/mkfile | 36 + appl/ebook/oebpackage.b | 276 + appl/ebook/oebpackage.m | 34 + appl/ebook/reader.b | 1797 + appl/ebook/reader.m | 120 + appl/ebook/strcache.m | 6 + appl/ebook/strmap.b | 40 + appl/ebook/strmap.m | 12 + appl/ebook/stylesheet.b | 156 + appl/ebook/stylesheet.m | 42 + appl/ebook/table.b | 176 + appl/ebook/table.m | 11 + appl/ebook/tst.txt | 379 + appl/ebook/understandingoeb.opf | 65 + appl/ebook/units.b | 146 + appl/ebook/units.m | 6 + appl/grid/blurdemo.b | 977 + appl/grid/cpupool.b | 917 + appl/grid/demo/block.b | 212 + appl/grid/demo/blur.b | 654 + appl/grid/demo/mkfile | 32 + appl/grid/find.b | 262 + appl/grid/jpg2bit.b | 47 + appl/grid/lib/announce.b | 42 + appl/grid/lib/browser.b | 1178 + appl/grid/lib/browser.m | 97 + appl/grid/lib/fbrowse.b | 390 + appl/grid/lib/mkfile | 27 + appl/grid/lib/pathreader.m | 3 + appl/grid/lib/srvbrowse.b | 719 + appl/grid/mkfile | 56 + appl/grid/query.b | 399 + appl/grid/readjpg.b | 1146 + appl/grid/register.b | 239 + appl/grid/reglisten.b | 305 + appl/grid/regstyxlisten.b | 279 + appl/grid/remotelogon.b | 427 + appl/grid/usercreatesrv.b | 93 + appl/lib/NOTICE | 25 + appl/lib/arg.b | 118 + appl/lib/asn1.b | 1030 + appl/lib/attrdb.b | 486 + appl/lib/attrhash.b | 109 + appl/lib/auth.b | 326 + appl/lib/auth9.b | 351 + appl/lib/bloomfilter.b | 72 + appl/lib/bufio.b | 533 + appl/lib/cfg.b | 234 + appl/lib/cfgfile.b | 152 + appl/lib/chanfill.b | 56 + appl/lib/convcs/8bit_stob.b | 24 + appl/lib/convcs/big5_btos.b | 87 + appl/lib/convcs/big5_stob.b | 93 + appl/lib/convcs/convcs.b | 165 + appl/lib/convcs/cp932_btos.b | 179 + appl/lib/convcs/cp_btos.b | 45 + appl/lib/convcs/cp_stob.b | 49 + appl/lib/convcs/euc-jp_btos.b | 162 + appl/lib/convcs/gb2312_btos.b | 70 + appl/lib/convcs/genbig5.b | 1790 + appl/lib/convcs/gencp.b | 28 + appl/lib/convcs/gencp932.b | 7814 ++++ appl/lib/convcs/gengb2312.b | 1143 + appl/lib/convcs/genjisx0201kana.b | 117 + appl/lib/convcs/genjisx0208-1997.b | 6958 +++ appl/lib/convcs/genjisx0212.b | 6146 +++ appl/lib/convcs/ibm437.b | 34 + appl/lib/convcs/ibm850.b | 34 + appl/lib/convcs/ibm866.b | 34 + appl/lib/convcs/iso8859-1.b | 42 + appl/lib/convcs/iso8859-10.b | 43 + appl/lib/convcs/iso8859-2.b | 42 + appl/lib/convcs/iso8859-3.b | 42 + appl/lib/convcs/iso8859-4.b | 42 + appl/lib/convcs/iso8859-5.b | 42 + appl/lib/convcs/iso8859-6.b | 42 + appl/lib/convcs/iso8859-7.b | 42 + appl/lib/convcs/iso8859-8.b | 42 + appl/lib/convcs/iso8859-9.b | 42 + appl/lib/convcs/koi8-r.b | 43 + appl/lib/convcs/mkdata | 39 + appl/lib/convcs/mkfile | 27 + appl/lib/convcs/utf8_btos.b | 35 + appl/lib/convcs/utf8_stob.b | 17 + appl/lib/convcs/windows-1250.b | 26 + appl/lib/convcs/windows-1251.b | 34 + appl/lib/convcs/windows-1252.b | 26 + appl/lib/crc.b | 45 + appl/lib/crypt/mkfile | 24 + appl/lib/crypt/pkcs.b | 572 + appl/lib/crypt/ssl3.b | 5557 +++ appl/lib/crypt/sslsession.b | 176 + appl/lib/crypt/x509.b | 4125 ++ appl/lib/daytime.b | 478 + appl/lib/db.b | 265 + appl/lib/dbm.b | 463 + appl/lib/dbsrv.b | 124 + appl/lib/debug.b | 1493 + appl/lib/deflate.b | 1367 + appl/lib/devpointer.b | 123 + appl/lib/dhcpclient.b | 1039 + appl/lib/dialog.b | 190 + appl/lib/dict.b | 57 + appl/lib/dis.b | 609 + appl/lib/diskblocks.b | 122 + appl/lib/disks.b | 377 + appl/lib/dividers.b | 242 + appl/lib/ecmascript/builtin.b | 1480 + appl/lib/ecmascript/date.b | 495 + appl/lib/ecmascript/ecmascript.b | 2626 ++ appl/lib/ecmascript/exec.b | 863 + appl/lib/ecmascript/mkfile | 23 + appl/lib/ecmascript/obj.b | 836 + appl/lib/ecmascript/pprint.b | 378 + appl/lib/ecmascript/regexp.b | 1286 + appl/lib/ecmascript/uri.b | 140 + appl/lib/encoding/base16.b | 43 + appl/lib/encoding/base32.b | 60 + appl/lib/encoding/base32a.b | 57 + appl/lib/encoding/base64.b | 92 + appl/lib/encoding/mkfile | 16 + appl/lib/env.b | 91 + appl/lib/ether.b | 83 + appl/lib/exception.b | 59 + appl/lib/factotum.b | 308 + appl/lib/filepat.b | 169 + appl/lib/format.b | 147 + appl/lib/fsfilter.b | 67 + appl/lib/fslib.b | 400 + appl/lib/fsproto.b | 385 + appl/lib/gamer.b | 147 + appl/lib/hash.b | 80 + appl/lib/html.b | 664 + appl/lib/ida/NOTICE | 33 + appl/lib/ida/ida.b | 228 + appl/lib/ida/idatab.b | 65549 +++++++++++++++++++++++++++++ appl/lib/ida/idatest.b | 84 + appl/lib/ida/mkfile | 18 + appl/lib/ida/mktab.b | 32 + appl/lib/imageremap.b | 738 + appl/lib/inflate.b | 745 + appl/lib/ip.b | 656 + appl/lib/ipattr.b | 217 + appl/lib/ir.b | 95 + appl/lib/irmpath.b | 118 + appl/lib/irsage.b | 99 + appl/lib/irsim.b | 83 + appl/lib/itslib.b | 45 + appl/lib/keyset.b | 89 + appl/lib/libc.b | 141 + appl/lib/libc0.b | 241 + appl/lib/lock.b | 26 + appl/lib/login.b | 175 + appl/lib/memfs.b | 46 + appl/lib/mkfile | 223 + appl/lib/mpeg.b | 148 + appl/lib/names.b | 152 + appl/lib/nametree.b | 278 + appl/lib/newns.b | 434 + appl/lib/palm.b | 504 + appl/lib/palmdb.b | 576 + appl/lib/palmfile.b | 703 + appl/lib/parseman.b | 805 + appl/lib/plumbing.b | 254 + appl/lib/plumbing.m | 23 + appl/lib/plumbmsg.b | 190 + appl/lib/pop3.b | 298 + appl/lib/popup.b | 124 + appl/lib/powerman.b | 59 + appl/lib/print/hp_driver.b | 1536 + appl/lib/print/mkfile | 26 + appl/lib/print/print.b | 625 + appl/lib/print/scaler.b | 186 + appl/lib/print/scaler.m | 30 + appl/lib/profile.b | 1230 + appl/lib/pslib.b | 714 + appl/lib/quicktime.b | 205 + appl/lib/rand.b | 29 + appl/lib/random.b | 50 + appl/lib/readdir.b | 123 + appl/lib/readgif.b | 442 + appl/lib/readjpg.b | 973 + appl/lib/readpicfile.b | 164 + appl/lib/readpng.b | 823 + appl/lib/readxbitmap.b | 131 + appl/lib/regex.b | 389 + appl/lib/regexutils.b | 65 + appl/lib/registries.b | 288 + appl/lib/riff.b | 225 + appl/lib/scoretable.b | 150 + appl/lib/scsiio.b | 317 + appl/lib/secstore.b | 474 + appl/lib/selectfile.b | 624 + appl/lib/sets.b | 329 + appl/lib/sets32.b | 226 + appl/lib/sexprs.b | 638 + appl/lib/slip.b | 113 + appl/lib/smtp.b | 241 + appl/lib/sort.b | 36 + appl/lib/spki/mkfile | 21 + appl/lib/spki/spki.b | 2109 + appl/lib/spki/verifier.b | 188 + appl/lib/ssl.b | 90 + appl/lib/string.b | 358 + appl/lib/strinttab.b | 28 + appl/lib/strokes/buildstrokes.b | 260 + appl/lib/strokes/mkfile | 18 + appl/lib/strokes/readstrokes.b | 205 + appl/lib/strokes/strokes.b | 793 + appl/lib/strokes/writestrokes.b | 68 + appl/lib/styx.b | 934 + appl/lib/styxconv/mkfile | 20 + appl/lib/styxconv/ostyx.b | 773 + appl/lib/styxconv/ostyx.m | 148 + appl/lib/styxconv/osys.m | 34 + appl/lib/styxconv/styxconv.b | 447 + appl/lib/styxlib.b | 453 + appl/lib/styxpersist.b | 898 + appl/lib/styxservers.b | 605 + appl/lib/tables.b | 105 + appl/lib/tabs.b | 191 + appl/lib/tcl.m | 19 + appl/lib/tcl_calc.b | 909 + appl/lib/tcl_core.b | 1397 + appl/lib/tcl_inthash.b | 95 + appl/lib/tcl_io.b | 350 + appl/lib/tcl_list.b | 335 + appl/lib/tcl_modhash.b | 114 + appl/lib/tcl_stack.b | 142 + appl/lib/tcl_strhash.b | 116 + appl/lib/tcl_string.b | 246 + appl/lib/tcl_symhash.b | 99 + appl/lib/tcl_tk.b | 223 + appl/lib/tcl_utils.b | 61 + appl/lib/tftp.b | 170 + appl/lib/timers.b | 99 + appl/lib/titlebar.b | 111 + appl/lib/tkclient.b | 249 + appl/lib/translate.b | 248 + appl/lib/ubfa.b | 623 + appl/lib/url.b | 224 + appl/lib/usb/mkfile | 16 + appl/lib/usb/usb.b | 453 + appl/lib/usb/usbmass.b | 465 + appl/lib/usb/usbmct.b | 204 + appl/lib/usb/usbmouse.b | 60 + appl/lib/utils.m | 112 + appl/lib/venti.b | 660 + appl/lib/virgil.b | 177 + appl/lib/volume.b | 172 + appl/lib/w3c/css.b | 1019 + appl/lib/w3c/mkfile | 17 + appl/lib/w3c/xpointers.b | 858 + appl/lib/wait.b | 55 + appl/lib/watchvars.b | 44 + appl/lib/winplace.b | 359 + appl/lib/wmclient.b | 346 + appl/lib/wmlib.b | 590 + appl/lib/wmsrv.b | 610 + appl/lib/workdir.b | 14 + appl/lib/writegif.b | 362 + appl/lib/xml.b | 712 + appl/math/ack.b | 43 + appl/math/crackerbarrel.b | 133 + appl/math/doc.txt | 48 + appl/math/factor.b | 180 + appl/math/ffts.b | 639 + appl/math/fibonacci.b | 59 + appl/math/fit.b | 264 + appl/math/genprimes.b | 64 + appl/math/geodesy.b | 849 + appl/math/gr.b | 557 + appl/math/graph0.b | 84 + appl/math/hist0.b | 99 + appl/math/linalg.b | 197 + appl/math/linbench.b | 197 + appl/math/mersenne.b | 99 + appl/math/mkfile | 43 + appl/math/parts.b | 125 + appl/math/perms.b | 131 + appl/math/pi.b | 405 + appl/math/polyfill.b | 440 + appl/math/polyhedra.b | 195 + appl/math/powers.b | 672 + appl/math/primes.b | 228 + appl/math/sieve.b | 220 + appl/mkfile | 20 + appl/spree/archives.b | 515 + appl/spree/clients/bounce.b | 958 + appl/spree/clients/cards.b | 2220 + appl/spree/clients/chat.b | 194 + appl/spree/clients/gather.b | 178 + appl/spree/clients/lobby.b | 562 + appl/spree/clients/othello.b | 270 + appl/spree/engines/afghan.b | 302 + appl/spree/engines/bounce.b | 258 + appl/spree/engines/canfield.b | 340 + appl/spree/engines/chat.b | 60 + appl/spree/engines/debug.b | 163 + appl/spree/engines/freecell.b | 428 + appl/spree/engines/gather.b | 267 + appl/spree/engines/hearts.b | 300 + appl/spree/engines/liars.b | 490 + appl/spree/engines/liars.y | 132 + appl/spree/engines/lobby.b | 389 + appl/spree/engines/othello.b | 242 + appl/spree/engines/racingdemon.b | 464 + appl/spree/engines/snap.b | 241 + appl/spree/engines/spider.b | 259 + appl/spree/engines/spit.b | 483 + appl/spree/engines/whist.b | 305 + appl/spree/gather.m | 10 + appl/spree/join.b | 115 + appl/spree/join.m | 5 + appl/spree/joinsession.b | 115 + appl/spree/joinsession.m | 7 + appl/spree/lib/allow.b | 194 + appl/spree/lib/allow.m | 9 + appl/spree/lib/base64.b | 72 + appl/spree/lib/base64.m | 5 + appl/spree/lib/cardlib.b | 917 + appl/spree/lib/cardlib.m | 114 + appl/spree/lib/commandline.b | 191 + appl/spree/lib/commandline.m | 16 + appl/spree/lib/objstore.b | 65 + appl/spree/lib/objstore.m | 8 + appl/spree/lib/testsets.b | 152 + appl/spree/lib/tricks.b | 140 + appl/spree/lib/tricks.m | 21 + appl/spree/man/gamesrv.man2 | 471 + appl/spree/man/gamesrv.man4 | 296 + appl/spree/man/styxservers-nametree.man2 | 180 + appl/spree/man/styxservers.man2 | 902 + appl/spree/mkfile | 66 + appl/spree/other/tst.b | 151 + appl/spree/other/tstboing.b | 158 + appl/spree/other/tstlines.sh | 53 + appl/spree/other/tstwin.b | 351 + appl/spree/spree.b | 1554 + appl/spree/spree.m | 140 + appl/svc/auth.sh | 13 + appl/svc/httpd/alarms.b | 45 + appl/svc/httpd/alarms.m | 11 + appl/svc/httpd/cache.b | 188 + appl/svc/httpd/cache.m | 9 + appl/svc/httpd/cgiparse.b | 258 + appl/svc/httpd/cgiparse.m | 22 + appl/svc/httpd/contents.b | 192 + appl/svc/httpd/contents.m | 16 + appl/svc/httpd/date.b | 264 + appl/svc/httpd/date.m | 11 + appl/svc/httpd/echo.b | 89 + appl/svc/httpd/httpd.b | 721 + appl/svc/httpd/httpd.debug | 27 + appl/svc/httpd/httpd.log | 0 appl/svc/httpd/httpd.m | 49 + appl/svc/httpd/httpd.rewrite | 10 + appl/svc/httpd/httpd.suff | 110 + appl/svc/httpd/imagemap.b | 251 + appl/svc/httpd/mkfile | 49 + appl/svc/httpd/parser.b | 861 + appl/svc/httpd/parser.m | 16 + appl/svc/httpd/redirect.b | 130 + appl/svc/httpd/redirect.m | 7 + appl/svc/httpd/stats.b | 85 + appl/svc/mkfile | 24 + appl/svc/net.sh | 6 + appl/svc/registry.sh | 8 + appl/svc/rstyx.sh | 4 + appl/svc/styx.sh | 4 + appl/svc/webget/date.b | 266 + appl/svc/webget/date.m | 12 + appl/svc/webget/file.b | 67 + appl/svc/webget/ftp.b | 227 + appl/svc/webget/http.b | 602 + appl/svc/webget/image2enc.b | 1070 + appl/svc/webget/image2enc.m | 7 + appl/svc/webget/message.b | 249 + appl/svc/webget/message.m | 28 + appl/svc/webget/mkfile | 40 + appl/svc/webget/transport.m | 5 + appl/svc/webget/webget.b | 464 + appl/svc/webget/webget.log | 0 appl/svc/webget/wgutils.b | 298 + appl/svc/webget/wgutils.m | 53 + appl/tiny/mkfile | 18 + appl/tiny/rm.b | 23 + appl/tiny/sh.b | 628 + appl/wm/about.b | 72 + appl/wm/avi.b | 384 + appl/wm/bounce.b | 356 + appl/wm/brutus.b | 2013 + appl/wm/brutus/excerpt.b | 264 + appl/wm/brutus/image.b | 259 + appl/wm/brutus/mkfile | 24 + appl/wm/brutus/mod.b | 335 + appl/wm/brutus/table.b | 1478 + appl/wm/c4.b | 718 + appl/wm/calendar.b | 1064 + appl/wm/clock.b | 123 + appl/wm/coffee.b | 227 + appl/wm/collide.b | 2180 + appl/wm/colors.b | 153 + appl/wm/cprof.b | 360 + appl/wm/date.b | 78 + appl/wm/deb.b | 1444 + appl/wm/debdata.b | 418 + appl/wm/debsrc.b | 633 + appl/wm/dir.b | 511 + appl/wm/drawmux/dmview.b | 163 + appl/wm/drawmux/dmwm.b | 207 + appl/wm/drawmux/drawmux.b | 1827 + appl/wm/drawmux/drawmux.m | 6 + appl/wm/drawmux/drawoffs.m | 185 + appl/wm/drawmux/mkfile | 37 + appl/wm/edit.b | 730 + appl/wm/filename.b | 74 + appl/wm/ftree/cptree.b | 136 + appl/wm/ftree/cptree.m | 8 + appl/wm/ftree/ftree.b | 873 + appl/wm/ftree/items.b | 326 + appl/wm/ftree/items.m | 30 + appl/wm/ftree/mkfile | 36 + appl/wm/ftree/wmsetup | 48 + appl/wm/getauthinfo.b | 291 + appl/wm/hebrew.m | 30 + appl/wm/keyboard.b | 511 + appl/wm/logon.b | 339 + appl/wm/logwindow.b | 187 + appl/wm/man.b | 769 + appl/wm/mand.b | 839 + appl/wm/mash.b | 577 + appl/wm/memory.b | 246 + appl/wm/minitel/README | 209 + appl/wm/minitel/event.b | 19 + appl/wm/minitel/event.m | 19 + appl/wm/minitel/keyb.b | 367 + appl/wm/minitel/mdisplay.b | 799 + appl/wm/minitel/mdisplay.dis | Bin 0 -> 10304 bytes appl/wm/minitel/mdisplay.m | 115 + appl/wm/minitel/mdisplay.sbl | 1969 + appl/wm/minitel/miniterm.b | 1187 + appl/wm/minitel/miniterm.dis | Bin 0 -> 48192 bytes appl/wm/minitel/miniterm.m | 120 + appl/wm/minitel/miniterm.sbl | 6810 +++ appl/wm/minitel/mkfile | 24 + appl/wm/minitel/modem.b | 620 + appl/wm/minitel/screen.b | 1610 + appl/wm/minitel/socket.b | 49 + appl/wm/minitel/swkeyb.b | 370 + appl/wm/minitel/swkeyb.dis | Bin 0 -> 6496 bytes appl/wm/minitel/swkeyb.m | 21 + appl/wm/minitel/swkeyb.sbl | 724 + appl/wm/mkfile | 103 + appl/wm/mpeg.b | 185 + appl/wm/mpeg/c0.tab | 261 + appl/wm/mpeg/c0.vlc | 50 + appl/wm/mpeg/c1.tab | 37 + appl/wm/mpeg/c1.vlc | 18 + appl/wm/mpeg/c2.tab | 21 + appl/wm/mpeg/c2.vlc | 10 + appl/wm/mpeg/c3.tab | 21 + appl/wm/mpeg/c3.vlc | 10 + appl/wm/mpeg/c4.tab | 9 + appl/wm/mpeg/c4.vlc | 4 + appl/wm/mpeg/c5.tab | 9 + appl/wm/mpeg/c5.vlc | 4 + appl/wm/mpeg/c6.tab | 9 + appl/wm/mpeg/c6.vlc | 4 + appl/wm/mpeg/c7.tab | 9 + appl/wm/mpeg/c7.vlc | 4 + appl/wm/mpeg/cbp.tab | 517 + appl/wm/mpeg/cbp.vlc | 65 + appl/wm/mpeg/cdc.tab | 261 + appl/wm/mpeg/cdc.vlc | 11 + appl/wm/mpeg/closest.m | 514 + appl/wm/mpeg/decode.b | 831 + appl/wm/mpeg/decode4.b | 709 + appl/wm/mpeg/fixidct.b | 188 + appl/wm/mpeg/fltidct.b | 177 + appl/wm/mpeg/mai.tab | 2053 + appl/wm/mpeg/mai.vlc | 35 + appl/wm/mpeg/makergbvmap.b | 31 + appl/wm/mpeg/maketables | 36 + appl/wm/mpeg/mbb.tab | 69 + appl/wm/mpeg/mbb.vlc | 13 + appl/wm/mpeg/mbi.tab | 9 + appl/wm/mpeg/mbi.vlc | 4 + appl/wm/mpeg/mbp.tab | 69 + appl/wm/mpeg/mbp.vlc | 9 + appl/wm/mpeg/mkfile | 47 + appl/wm/mpeg/motion.tab | 2053 + appl/wm/mpeg/motion.vlc | 19 + appl/wm/mpeg/mpeg.b | 285 + appl/wm/mpeg/mpegio.b | 870 + appl/wm/mpeg/mpegio.m | 218 + appl/wm/mpeg/refidct.b | 58 + appl/wm/mpeg/remap.b | 128 + appl/wm/mpeg/remap1.b | 116 + appl/wm/mpeg/remap2.b | 80 + appl/wm/mpeg/remap24.b | 82 + appl/wm/mpeg/remap4.b | 62 + appl/wm/mpeg/remap8.b | 84 + appl/wm/mpeg/rgbvmap.m | 258 + appl/wm/mpeg/rl0f.tab | 517 + appl/wm/mpeg/rl0f.vlc | 34 + appl/wm/mpeg/rl0n.tab | 517 + appl/wm/mpeg/rl0n.vlc | 35 + appl/wm/mpeg/scidct.b | 160 + appl/wm/mpeg/vlc.b | 213 + appl/wm/mpeg/ydc.tab | 133 + appl/wm/mpeg/ydc.vlc | 11 + appl/wm/mprof.b | 314 + appl/wm/pen.b | 447 + appl/wm/polyhedra.b | 800 + appl/wm/prof.b | 323 + appl/wm/qt.b | 161 + appl/wm/readmail.b | 885 + appl/wm/remotelogon.b | 314 + appl/wm/reversi.b | 903 + appl/wm/rmtdir.b | 215 + appl/wm/rt.b | 701 + appl/wm/sam.b | 230 + appl/wm/samstub.b | 1338 + appl/wm/samstub.m | 132 + appl/wm/samterm.m | 75 + appl/wm/samtk.b | 688 + appl/wm/samtk.m | 54 + appl/wm/sendmail.b | 652 + appl/wm/sh.b | 851 + appl/wm/smenu.b | 204 + appl/wm/smenu.m | 18 + appl/wm/snake.b | 373 + appl/wm/stopwatch.b | 184 + appl/wm/sweeper.b | 330 + appl/wm/task.b | 240 + appl/wm/telnet.b | 820 + appl/wm/tetris.b | 806 + appl/wm/toolbar.b | 566 + appl/wm/unibrowse.b | 966 + appl/wm/view.b | 484 + appl/wm/vt.b | 1007 + appl/wm/wish.b | 165 + appl/wm/wm.b | 678 + appl/wm/wmdeb.m | 82 + appl/wm/wmplay.b | 176 + asm/NOTICE | 25 + asm/asm.h | 149 + asm/asm.y | 385 + asm/assem.c | 672 + asm/lex.c | 685 + asm/mkfile | 21 + emu/FreeBSD/asm-386.S | 110 + emu/FreeBSD/audio.c | 547 + emu/FreeBSD/cmd.c | 204 + emu/FreeBSD/deveia.c | 39 + emu/FreeBSD/devfs.c | 1 + emu/FreeBSD/emu | 106 + emu/FreeBSD/ipif.c | 369 + emu/FreeBSD/mkfile | 47 + emu/FreeBSD/mkfile-FreeBSD | 17 + emu/FreeBSD/os.c | 513 + emu/Hp/NOTE | 1 + emu/Hp/asm-s800.s | 113 + emu/Hp/cmd.c | 205 + emu/Hp/devaudio.c | 506 + emu/Hp/devfs.c | 1 + emu/Hp/emu | 107 + emu/Hp/mkfile | 50 + emu/Hp/mkfile-Hp | 13 + emu/Hp/os.c | 591 + emu/Irix/NOTE | 1 + emu/Irix/asm-mips.s | 31 + emu/Irix/cmd.c | 205 + emu/Irix/devfs.c | 1 + emu/Irix/emu | 106 + emu/Irix/mkfile | 50 + emu/Irix/mkfile-Irix | 14 + emu/Irix/os.c | 477 + emu/Linux/asm-386.S | 109 + emu/Linux/cmd.c | 205 + emu/Linux/deveia.c | 44 + emu/Linux/devfs.c | 1 + emu/Linux/emu | 106 + emu/Linux/mkfile | 50 + emu/Linux/os.c | 523 + emu/MacOSX/NOTE | 2 + emu/MacOSX/NOTICE | 2 + emu/MacOSX/asm-power.s | 36 + emu/MacOSX/cmd.c | 205 + emu/MacOSX/deveia.c | 154 + emu/MacOSX/devfs.c | 1 + emu/MacOSX/emu | 108 + emu/MacOSX/emu-g | 96 + emu/MacOSX/ipif.c | 1 + emu/MacOSX/mkfile | 63 + emu/MacOSX/mkfile-g | 62 + emu/MacOSX/os.c | 647 + emu/NOTICE | 31 + emu/Nt/audio.c | 810 + emu/Nt/cmd.c | 245 + emu/Nt/devarch.c | 414 + emu/Nt/deveia.c | 672 + emu/Nt/devfs.c | 2507 ++ emu/Nt/emu | 108 + emu/Nt/fp.c | 107 + emu/Nt/ie | 104 + emu/Nt/ie-os.c | 847 + emu/Nt/ie-win.c | 223 + emu/Nt/ieplugin.h | 75 + emu/Nt/ipif.c | 407 + emu/Nt/mkfile | 51 + emu/Nt/nt.rc | 1 + emu/Nt/os.c | 875 + emu/Nt/vlrt.c | 764 + emu/Nt/win.c | 841 + emu/Plan9/asm-386.s | 33 + emu/Plan9/asm-mips.s | 24 + emu/Plan9/asm-power.s | 28 + emu/Plan9/asm-sparc.s | 22 + emu/Plan9/cmd.c | 178 + emu/Plan9/devfs.c | 365 + emu/Plan9/devsrv9.c | 403 + emu/Plan9/emu | 109 + emu/Plan9/emusig | 95 + emu/Plan9/mkfile | 49 + emu/Plan9/os.c | 422 + emu/Plan9/win.c | 587 + emu/Solaris/asm-386.s | 72 + emu/Solaris/asm-sparc.s | 76 + emu/Solaris/audio.c | 593 + emu/Solaris/cmd.c | 205 + emu/Solaris/deveia.c | 38 + emu/Solaris/devfs.c | 1 + emu/Solaris/emu | 106 + emu/Solaris/mkfile | 47 + emu/Solaris/os.c | 437 + emu/Unixware/asm-386.s | 71 + emu/Unixware/cmd.c | 205 + emu/Unixware/deveia.c | 11 + emu/Unixware/devfs.c | 1 + emu/Unixware/emu | 106 + emu/Unixware/ipif.c | 1 + emu/Unixware/mkfile | 45 + emu/Unixware/mkfile-Unixware | 15 + emu/Unixware/os.c | 438 + emu/mkfile | 19 + emu/port/acme-offset | 1 + emu/port/alloc.c | 1021 + emu/port/audio-tbls.c | 53 + emu/port/audio.h | 81 + emu/port/cache.c | 45 + emu/port/chan.c | 1404 + emu/port/dat.h | 512 + emu/port/dev.c | 448 + emu/port/devaudio.c | 397 + emu/port/devcap.c | 243 + emu/port/devcmd.c | 661 + emu/port/devcons.c | 611 + emu/port/devconsx.c | 833 + emu/port/devdraw.c | 2024 + emu/port/devdup.c | 150 + emu/port/devdynld.c | 357 + emu/port/devdynldx.c | 357 + emu/port/deveia-bsd.c | 95 + emu/port/deveia-posix.c | 463 + emu/port/devenv.c | 245 + emu/port/devfs-posix.c | 1058 + emu/port/devindir.c | 35 + emu/port/devip.c | 1133 + emu/port/devlogfs.c | 1522 + emu/port/devmem.c | 485 + emu/port/devmnt.c | 1202 + emu/port/devpipe.c | 460 + emu/port/devpointer.c | 304 + emu/port/devprof.c | 831 + emu/port/devprog.c | 1507 + emu/port/devroot.c | 150 + emu/port/devsign.c | 440 + emu/port/devsnarf.c | 161 + emu/port/devsrv.c | 725 + emu/port/devssl.c | 1443 + emu/port/devtinyfs.c | 895 + emu/port/devtk.c | 177 + emu/port/dial.c | 414 + emu/port/dis.c | 1125 + emu/port/discall.c | 179 + emu/port/dynld.c | 52 + emu/port/dynldc.c | 65 + emu/port/env.c | 77 + emu/port/error.c | 66 + emu/port/error.h | 68 + emu/port/errstr.c | 38 + emu/port/exception.c | 214 + emu/port/exportfs.c | 1221 + emu/port/exptab.c | 6 + emu/port/file.c | 16 + emu/port/fns.h | 244 + emu/port/inferno.c | 1029 + emu/port/ip.h | 64 + emu/port/ipaux.c | 525 + emu/port/ipif-posix.c | 415 + emu/port/keysym2ucs.h | 9 + emu/port/latin1.c | 83 + emu/port/latin1.h | 99 + emu/port/lock.c | 141 + emu/port/main.c | 433 + emu/port/mkdevc | 99 + emu/port/mkdevlist | 75 + emu/port/mkroot | 121 + emu/port/parse.c | 111 + emu/port/pgrp.c | 265 + emu/port/portmkfile | 169 + emu/port/print.c | 23 + emu/port/proc.c | 239 + emu/port/qio.c | 1531 + emu/port/random.c | 167 + emu/port/srv.c | 153 + emu/port/styx.c | 701 + emu/port/sysfile.c | 1089 + emu/port/uqid.c | 114 + emu/port/win-x11-old.c | 845 + emu/port/win-x11a.c | 1424 + emu/port/x11-keysym2ucs.c | 857 + include/NOTICE | 25 + include/a.out.h | 1 + include/bio.h | 73 + include/cursor.h | 19 + include/draw.h | 549 + include/drawif.h | 12 + include/dynld.h | 44 + include/fcall.h | 140 + include/flate.h | 36 + include/freetype.h | 55 + include/freetype/cache/ftccache.h | 304 + include/freetype/cache/ftccmap.h | 216 + include/freetype/cache/ftcglyph.h | 193 + include/freetype/cache/ftcimage.h | 312 + include/freetype/cache/ftcmanag.h | 244 + include/freetype/cache/ftcsbits.h | 275 + include/freetype/cache/ftlru.h | 202 + include/freetype/config/ftconfig.h | 334 + include/freetype/config/ftconfig.h.orig | 334 + include/freetype/config/ftheader.h | 524 + include/freetype/config/ftmodule.h | 19 + include/freetype/config/ftmodule.h.orig | 19 + include/freetype/config/ftoption.h | 478 + include/freetype/config/ftstdlib.h | 149 + include/freetype/config/ftstdlib.h.orig | 140 + include/freetype/freetype.h | 2860 ++ include/freetype/ft2build.h | 40 + include/freetype/ftbbox.h | 83 + include/freetype/ftbdf.h | 84 + include/freetype/ftcache.h | 415 + include/freetype/ftchapters.h | 69 + include/freetype/fterrdef.h | 229 + include/freetype/fterrors.h | 207 + include/freetype/ftglyph.h | 517 + include/freetype/ftgzip.h | 86 + include/freetype/ftimage.h | 1241 + include/freetype/ftincrem.h | 286 + include/freetype/ftlist.h | 268 + include/freetype/ftmac.h | 128 + include/freetype/ftmm.h | 203 + include/freetype/ftmoderr.h | 149 + include/freetype/ftmodule.h | 307 + include/freetype/ftoutln.h | 400 + include/freetype/ftpfr.h | 156 + include/freetype/ftrender.h | 229 + include/freetype/ftsizes.h | 151 + include/freetype/ftsnames.h | 161 + include/freetype/ftstroker.h | 139 + include/freetype/ftsynth.h | 65 + include/freetype/ftsysio.h | 195 + include/freetype/ftsysmem.h | 202 + include/freetype/ftsystem.h | 309 + include/freetype/fttrigon.h | 325 + include/freetype/fttypes.h | 558 + include/freetype/ftxf86.h | 53 + include/freetype/internal/autohint.h | 205 + include/freetype/internal/bdftypes.h | 53 + include/freetype/internal/cfftypes.h | 256 + include/freetype/internal/fnttypes.h | 155 + include/freetype/internal/ftcalc.h | 77 + include/freetype/internal/ftcore.h | 185 + include/freetype/internal/ftdebug.h | 196 + include/freetype/internal/ftdriver.h | 202 + include/freetype/internal/ftexcept.h | 82 + include/freetype/internal/ftgloadr.h | 153 + include/freetype/internal/fthash.h | 502 + include/freetype/internal/ftmemory.h | 296 + include/freetype/internal/ftobject.h | 533 + include/freetype/internal/ftobjs.h | 794 + include/freetype/internal/ftstream.h | 498 + include/freetype/internal/fttrace.h | 106 + include/freetype/internal/internal.h | 57 + include/freetype/internal/pcftypes.h | 56 + include/freetype/internal/pfr.h | 36 + include/freetype/internal/psaux.h | 717 + include/freetype/internal/pshints.h | 626 + include/freetype/internal/psnames.h | 241 + include/freetype/internal/sfnt.h | 534 + include/freetype/internal/t1types.h | 199 + include/freetype/internal/t42types.h | 55 + include/freetype/internal/tttypes.h | 1669 + include/freetype/t1tables.h | 391 + include/freetype/ttnameid.h | 1022 + include/freetype/tttables.h | 605 + include/freetype/tttags.h | 74 + include/interp.h | 526 + include/isa.h | 249 + include/kern.h | 598 + include/kernel.h | 50 + include/keyboard.h | 60 + include/libcrypt_o.h | 646 + include/libsec.h | 370 + include/logfs.h | 156 + include/mathi.h | 81 + include/memdraw.h | 194 + include/memlayer.h | 50 + include/mp.h | 141 + include/nandecc.h | 12 + include/nandfs.h | 72 + include/pool.h | 73 + include/pooldefs.h | 5 + include/prefab.h | 103 + include/raise.h | 26 + include/rdbg.h | 35 + include/styx.h | 140 + include/tk.h | 838 + include/trace.h | 24 + include/version.h | 1 + include/vm.h | 8 + lib9/NOTICE | 29 + lib9/argv0.c | 1 + lib9/charstod.c | 68 + lib9/cistrcmp.c | 25 + lib9/cistrncmp.c | 27 + lib9/cistrstr.c | 22 + lib9/cleanname.c | 51 + lib9/convD2M.c | 94 + lib9/convM2D.c | 93 + lib9/convM2S.c | 314 + lib9/convS2M.c | 385 + lib9/create.c | 32 + lib9/dirstat-Nt.c | 64 + lib9/dirstat-posix.c | 79 + lib9/dirwstat.c | 15 + lib9/dofmt.c | 532 + lib9/dorfmt.c | 58 + lib9/errfmt.c | 24 + lib9/errstr-Nt.c | 65 + lib9/errstr-Plan9.c | 8 + lib9/errstr-posix.c | 37 + lib9/exits.c | 17 + lib9/fcallfmt.c | 246 + lib9/fltfmt.c | 324 + lib9/fmt.c | 188 + lib9/fmtdef.h | 98 + lib9/fmtfd.c | 43 + lib9/fmtlock.c | 24 + lib9/fmtprint.c | 44 + lib9/fmtquote.c | 259 + lib9/fmtrune.c | 37 + lib9/fmtstr.c | 23 + lib9/fmtvprint.c | 43 + lib9/fprint.c | 26 + lib9/getcallerpc-FreeBSD-386.S | 7 + lib9/getcallerpc-Hp-s800.s | 12 + lib9/getcallerpc-Irix-mips.s | 9 + lib9/getcallerpc-Linux-386.S | 7 + lib9/getcallerpc-MacOSX-power.s | 19 + lib9/getcallerpc-Solaris-386.s | 8 + lib9/getcallerpc-Solaris-sparc.s | 11 + lib9/getcallerpc-Unixware-386.s | 8 + lib9/getfields.c | 35 + lib9/getuser-Nt.c | 9 + lib9/getuser-posix.c | 22 + lib9/getwd-Nt.c | 24 + lib9/getwd-posix.c | 9 + lib9/lock-Hp-s800.s | 38 + lib9/lock-Irix-mips.s | 19 + lib9/lock-MacOSX-power.s | 16 + lib9/lock-Nt-386.c | 15 + lib9/lock-Solaris-386.s | 56 + lib9/lock-Solaris-sparc.s | 20 + lib9/lock-Unixware-386.s | 56 + lib9/lock.c | 141 + lib9/mkfile | 85 + lib9/mkfile-Nt | 8 + lib9/mkfile-Plan9 | 2 + lib9/mkfile-Posix | 8 + lib9/nulldir.c | 8 + lib9/pow10.c | 44 + lib9/print.c | 26 + lib9/qsort.c | 122 + lib9/readn.c | 21 + lib9/rerrstr.c | 12 + lib9/rune.c | 162 + lib9/runestrlen.c | 13 + lib9/sbrk-posix.c | 48 + lib9/seek.c | 9 + lib9/seprint.c | 26 + lib9/setbinmode-Nt.c | 9 + lib9/smprint.c | 26 + lib9/snprint.c | 27 + lib9/sprint.c | 35 + lib9/strdup.c | 12 + lib9/strecpy.c | 16 + lib9/strtoll.c | 81 + lib9/sysfatal.c | 28 + lib9/tokenize.c | 58 + lib9/u16.c | 52 + lib9/u32.c | 109 + lib9/u64.c | 126 + lib9/utfecpy.c | 20 + lib9/utflen.c | 22 + lib9/utfnlen.c | 25 + lib9/utfrrune.c | 30 + lib9/utfrune.c | 29 + lib9/vfprint.c | 46 + lib9/vseprint.c | 35 + lib9/vsmprint.c | 74 + lib9/vsnprint.c | 34 + libbio/NOTICE | 29 + libbio/bbuffered.c | 20 + libbio/bfildes.c | 9 + libbio/bflush.c | 33 + libbio/bgetc.c | 52 + libbio/bgetd.c | 36 + libbio/bgetrune.c | 46 + libbio/binit.c | 142 + libbio/boffset.c | 25 + libbio/bprint.c | 28 + libbio/bputc.c | 29 + libbio/bputrune.c | 22 + libbio/brdline.c | 94 + libbio/bread.c | 47 + libbio/bseek.c | 56 + libbio/bwrite.c | 38 + libbio/mkfile | 23 + libdraw/NOTICE | 31 + libdraw/alloc.c | 237 + libdraw/allocimagemix.c | 42 + libdraw/arith.c | 203 + libdraw/bezier.c | 243 + libdraw/border.c | 26 + libdraw/buildfont.c | 142 + libdraw/bytesperline.c | 30 + libdraw/chan.c | 76 + libdraw/cloadimage.c | 49 + libdraw/computil.c | 37 + libdraw/creadimage.c | 116 + libdraw/defont.c | 401 + libdraw/draw.c | 68 + libdraw/drawrepl.c | 22 + libdraw/ellipse.c | 81 + libdraw/font.c | 398 + libdraw/freesubfont.c | 16 + libdraw/getdefont.c | 59 + libdraw/getsubfont.c | 36 + libdraw/init.c | 341 + libdraw/line.c | 34 + libdraw/loadimage.c | 70 + libdraw/mkfile | 54 + libdraw/mkfont.c | 54 + libdraw/openfont.c | 37 + libdraw/poly.c | 86 + libdraw/readcolmap.c | 46 + libdraw/readimage.c | 123 + libdraw/readsubfont.c | 58 + libdraw/rectclip.c | 24 + libdraw/replclipr.c | 24 + libdraw/rgb.c | 98 + libdraw/string.c | 136 + libdraw/stringbg.c | 50 + libdraw/stringsubfont.c | 64 + libdraw/stringwidth.c | 96 + libdraw/subfont.c | 27 + libdraw/subfontcache.c | 38 + libdraw/subfontname.c | 45 + libdraw/test.c | 9 + libdraw/unloadimage.c | 58 + libdraw/window.c | 203 + libdraw/writecolmap.c | 37 + libdraw/writeimage.c | 185 + libdraw/writesubfont.c | 45 + libdynld/NOTICE | 28 + libdynld/dynld-386.c | 42 + libdynld/dynld-68000.c | 22 + libdynld/dynld-arm.c | 44 + libdynld/dynld-mips.c | 22 + libdynld/dynld-power.c | 65 + libdynld/dynld-sparc.c | 20 + libdynld/dynld.c | 258 + libdynld/dynloadfd.c | 35 + libdynld/mkfile | 12 + libfreetype/NOTICE/FTL.txt | 164 + libfreetype/NOTICE/GPL.txt | 339 + libfreetype/NOTICE/PATENTS | 27 + libfreetype/NOTICE/license.txt | 10 + libfreetype/adler32.c | 48 + libfreetype/ahangles.c | 147 + libfreetype/ahangles.h | 64 + libfreetype/aherrors.h | 40 + libfreetype/ahglobal.c | 395 + libfreetype/ahglobal.h | 49 + libfreetype/ahglyph.c | 1579 + libfreetype/ahglyph.h | 93 + libfreetype/ahhint.c | 1493 + libfreetype/ahhint.h | 75 + libfreetype/ahloader.h | 61 + libfreetype/ahmodule.c | 137 + libfreetype/ahmodule.h | 42 + libfreetype/ahoptim.c | 882 + libfreetype/ahoptim.h | 137 + libfreetype/ahtypes.h | 518 + libfreetype/autohint.c | 32 + libfreetype/bdf.c | 34 + libfreetype/bdf.h | 295 + libfreetype/bdfdrivr.c | 674 + libfreetype/bdfdrivr.h | 74 + libfreetype/bdferror.h | 44 + libfreetype/bdflib.c | 2436 ++ libfreetype/cff.c | 29 + libfreetype/cffcmap.c | 326 + libfreetype/cffcmap.h | 88 + libfreetype/cffdrivr.c | 420 + libfreetype/cffdrivr.h | 39 + libfreetype/cfferrs.h | 41 + libfreetype/cffgload.c | 2481 ++ libfreetype/cffgload.h | 214 + libfreetype/cffload.c | 2247 + libfreetype/cffload.h | 74 + libfreetype/cffobjs.c | 575 + libfreetype/cffobjs.h | 160 + libfreetype/cffparse.c | 681 + libfreetype/cffparse.h | 69 + libfreetype/cfftoken.h | 97 + libfreetype/ciderrs.h | 40 + libfreetype/cidgload.c | 437 + libfreetype/cidgload.h | 51 + libfreetype/cidload.c | 546 + libfreetype/cidload.h | 57 + libfreetype/cidobjs.c | 448 + libfreetype/cidobjs.h | 158 + libfreetype/cidparse.c | 155 + libfreetype/cidparse.h | 116 + libfreetype/cidriver.c | 113 + libfreetype/cidriver.h | 39 + libfreetype/cidtoken.h | 96 + libfreetype/fnterrs.h | 41 + libfreetype/freetype.c | 134 + libfreetype/ft2system.c | 0 libfreetype/ftapi.c | 121 + libfreetype/ftbase.c | 34 + libfreetype/ftbbox.c | 653 + libfreetype/ftbdf.c | 63 + libfreetype/ftcache.c | 31 + libfreetype/ftcalc.c | 561 + libfreetype/ftccache.c | 714 + libfreetype/ftccmap.c | 411 + libfreetype/ftcerror.h | 40 + libfreetype/ftcglyph.c | 115 + libfreetype/ftcimage.c | 399 + libfreetype/ftcmanag.c | 765 + libfreetype/ftcsbits.c | 557 + libfreetype/ftdbgmem.c | 672 + libfreetype/ftdebug.c | 197 + libfreetype/ftexcept.c | 197 + libfreetype/ftgloadr.c | 361 + libfreetype/ftglyph.c | 684 + libfreetype/ftgrays.c | 2159 + libfreetype/ftgrays.h | 57 + libfreetype/ftgzip.c | 561 + libfreetype/fthash.c | 246 + libfreetype/ftinit.c | 161 + libfreetype/ftlist.c | 217 + libfreetype/ftlru.c | 338 + libfreetype/ftmac.c | 919 + libfreetype/ftmm.c | 126 + libfreetype/ftnames.c | 94 + libfreetype/ftobject.c | 396 + libfreetype/ftobjs.c | 2505 ++ libfreetype/ftoutln.c | 658 + libfreetype/ftpfr.c | 105 + libfreetype/ftraster.c | 3288 ++ libfreetype/ftraster.h | 46 + libfreetype/ftrend1.c | 273 + libfreetype/ftrend1.h | 44 + libfreetype/ftsmerrs.h | 41 + libfreetype/ftsmooth.c | 371 + libfreetype/ftsmooth.h | 49 + libfreetype/ftstream.c | 803 + libfreetype/ftstroker.c | 1364 + libfreetype/ftsynth.c | 286 + libfreetype/ftsysio.c | 131 + libfreetype/ftsysmem.c | 30 + libfreetype/ftsystem.c | 303 + libfreetype/ftsystem_inf.c | 308 + libfreetype/fttrigon.c | 485 + libfreetype/fttype1.c | 87 + libfreetype/ftutil.c | 330 + libfreetype/ftxf86.c | 80 + libfreetype/infblock.c | 383 + libfreetype/infblock.h | 36 + libfreetype/infcodes.c | 250 + libfreetype/infcodes.h | 31 + libfreetype/inffixed.h | 151 + libfreetype/inflate.c | 273 + libfreetype/inftrees.c | 453 + libfreetype/inftrees.h | 63 + libfreetype/infutil.c | 86 + libfreetype/infutil.h | 96 + libfreetype/mkfile | 49 + libfreetype/otlayout.h | 205 + libfreetype/otlbase.c | 181 + libfreetype/otlbase.h | 14 + libfreetype/otlcommn.c | 940 + libfreetype/otlcommn.h | 277 + libfreetype/otlconf.h | 78 + libfreetype/otlgdef.c | 175 + libfreetype/otlgdef.h | 14 + libfreetype/otlgpos.c | 980 + libfreetype/otlgpos.h | 14 + libfreetype/otlgsub.c | 867 + libfreetype/otlgsub.h | 26 + libfreetype/otljstf.c | 189 + libfreetype/otljstf.h | 14 + libfreetype/otlparse.c | 142 + libfreetype/otlparse.h | 99 + libfreetype/otltable.h | 60 + libfreetype/otltags.h | 86 + libfreetype/otlutils.h | 33 + libfreetype/pcf.c | 36 + libfreetype/pcf.h | 238 + libfreetype/pcfdriver.c | 501 + libfreetype/pcfdriver.h | 44 + libfreetype/pcferror.h | 40 + libfreetype/pcfread.c | 1057 + libfreetype/pcfutil.c | 215 + libfreetype/pcfutil.h | 58 + libfreetype/pfr.c | 29 + libfreetype/pfrcmap.c | 158 + libfreetype/pfrcmap.h | 46 + libfreetype/pfrdrivr.c | 168 + libfreetype/pfrdrivr.h | 39 + libfreetype/pfrerror.h | 40 + libfreetype/pfrgload.c | 801 + libfreetype/pfrgload.h | 49 + libfreetype/pfrload.c | 901 + libfreetype/pfrload.h | 118 + libfreetype/pfrobjs.c | 437 + libfreetype/pfrobjs.h | 96 + libfreetype/pfrsbit.c | 670 + libfreetype/pfrsbit.h | 36 + libfreetype/pfrtypes.h | 354 + libfreetype/psaux.c | 28 + libfreetype/psauxerr.h | 41 + libfreetype/psauxmod.c | 118 + libfreetype/psauxmod.h | 38 + libfreetype/pshalgo.h | 53 + libfreetype/pshalgo1.c | 785 + libfreetype/pshalgo1.h | 110 + libfreetype/pshalgo2.c | 1557 + libfreetype/pshalgo2.h | 203 + libfreetype/pshalgo3.c | 1757 + libfreetype/pshalgo3.h | 254 + libfreetype/pshglob.c | 743 + libfreetype/pshglob.h | 187 + libfreetype/pshinter.c | 30 + libfreetype/pshmod.c | 120 + libfreetype/pshmod.h | 39 + libfreetype/pshrec.c | 1211 + libfreetype/pshrec.h | 180 + libfreetype/psmodule.c | 357 + libfreetype/psmodule.h | 38 + libfreetype/psnamerr.h | 41 + libfreetype/psnames.c | 25 + libfreetype/psobjs.c | 1403 + libfreetype/psobjs.h | 204 + libfreetype/pstables.h | 2967 ++ libfreetype/raster.c | 26 + libfreetype/rasterrs.h | 41 + libfreetype/sfdriver.c | 332 + libfreetype/sfdriver.h | 38 + libfreetype/sferrors.h | 39 + libfreetype/sfnt.c | 37 + libfreetype/sfobjs.c | 829 + libfreetype/sfobjs.h | 54 + libfreetype/smooth.c | 26 + libfreetype/stddef.h | 36 + libfreetype/t1afm.c | 282 + libfreetype/t1afm.h | 66 + libfreetype/t1cmap.c | 454 + libfreetype/t1cmap.h | 124 + libfreetype/t1decode.c | 1170 + libfreetype/t1decode.h | 65 + libfreetype/t1driver.c | 279 + libfreetype/t1driver.h | 38 + libfreetype/t1errors.h | 40 + libfreetype/t1gload.c | 416 + libfreetype/t1gload.h | 46 + libfreetype/t1load.c | 1791 + libfreetype/t1load.h | 84 + libfreetype/t1objs.c | 541 + libfreetype/t1objs.h | 170 + libfreetype/t1parse.c | 460 + libfreetype/t1parse.h | 135 + libfreetype/t1tokens.h | 80 + libfreetype/t42drivr.c | 169 + libfreetype/t42drivr.h | 38 + libfreetype/t42error.h | 40 + libfreetype/t42objs.c | 637 + libfreetype/t42objs.h | 126 + libfreetype/t42parse.c | 994 + libfreetype/t42parse.h | 89 + libfreetype/test_bbox.c | 160 + libfreetype/test_trig.c | 236 + libfreetype/truetype.c | 32 + libfreetype/ttcmap.c | 1110 + libfreetype/ttcmap.h | 45 + libfreetype/ttcmap0.c | 1784 + libfreetype/ttcmap0.h | 74 + libfreetype/ttdriver.c | 420 + libfreetype/ttdriver.h | 38 + libfreetype/tterrors.h | 40 + libfreetype/ttgload.c | 1783 + libfreetype/ttgload.h | 55 + libfreetype/ttinterp.c | 7496 ++++ libfreetype/ttinterp.h | 317 + libfreetype/ttload.c | 1849 + libfreetype/ttload.h | 137 + libfreetype/ttobjs.c | 861 + libfreetype/ttobjs.h | 422 + libfreetype/ttpload.c | 264 + libfreetype/ttpload.h | 48 + libfreetype/ttpost.c | 521 + libfreetype/ttpost.h | 46 + libfreetype/ttsbit.c | 1474 + libfreetype/ttsbit.h | 59 + libfreetype/type1.c | 33 + libfreetype/type1cid.c | 29 + libfreetype/type42.c | 25 + libfreetype/winfnt.c | 691 + libfreetype/winfnt.h | 39 + libfreetype/zconf.h | 275 + libfreetype/zlib.h | 830 + libfreetype/zutil.c | 181 + libfreetype/zutil.h | 216 + libinterp/NOTICE | 17 + libinterp/README | 1 + libinterp/alt.c | 297 + libinterp/comp-386.c | 1975 + libinterp/comp-68020.c | 2074 + libinterp/comp-arm.c | 2260 + libinterp/comp-mips.c | 1955 + libinterp/comp-power.c | 2257 + libinterp/comp-s800.c | 2024 + libinterp/comp-sparc.c | 1882 + libinterp/comp-spim.c | 1 + libinterp/comp-thumb.c | 2466 ++ libinterp/conv.c | 110 + libinterp/das-386.c | 1630 + libinterp/das-68000.c | 1 + libinterp/das-68020.c | 1940 + libinterp/das-arm.c | 535 + libinterp/das-mips.c | 531 + libinterp/das-power.c | 961 + libinterp/das-s800.c | 457 + libinterp/das-sparc.c | 833 + libinterp/das-spim.c | 1 + libinterp/das-stub.c | 9 + libinterp/das-thumb.c | 548 + libinterp/dec.c | 1811 + libinterp/decgen.c | 122 + libinterp/dlm-Inferno.c | 101 + libinterp/dlm-Nt.c | 48 + libinterp/dlm-Plan9.c | 101 + libinterp/dlm-Posix.c | 48 + libinterp/draw.c | 2345 ++ libinterp/drawmod.h | 94 + libinterp/freetype.c | 231 + libinterp/freetypemod.h | 11 + libinterp/gc.c | 383 + libinterp/geom.c | 309 + libinterp/heap.c | 499 + libinterp/heapaudit.c | 198 + libinterp/ipint.c | 547 + libinterp/keyring.c | 2511 ++ libinterp/keyring.h | 75 + libinterp/link.c | 132 + libinterp/load.c | 638 + libinterp/loader.c | 444 + libinterp/loadermod.h | 13 + libinterp/math.c | 955 + libinterp/mathmod.h | 73 + libinterp/mkfile | 120 + libinterp/mkoptab | 19 + libinterp/optab.h | 179 + libinterp/prefab.c | 824 + libinterp/raise.c | 25 + libinterp/readmod.c | 84 + libinterp/runt.c | 496 + libinterp/runt.h | 3919 ++ libinterp/sign.c | 29 + libinterp/stack.c | 118 + libinterp/string.c | 612 + libinterp/sysmod.h | 47 + libinterp/tab.h | 184 + libinterp/tk.c | 1295 + libinterp/tkmod.h | 15 + libinterp/validstk.c | 53 + libinterp/xec.c | 1692 + libkern/NOTICE | 29 + libkern/abort.c | 7 + libkern/abs.c | 17 + libkern/atol.c | 47 + libkern/charstod.c | 68 + libkern/cistrcmp.c | 25 + libkern/cistrncmp.c | 27 + libkern/cistrstr.c | 22 + libkern/cleanname.c | 51 + libkern/convD2M.c | 94 + libkern/convM2D.c | 93 + libkern/convM2S.c | 314 + libkern/convS2M.c | 385 + libkern/div-arm.s | 119 + libkern/dofmt.c | 532 + libkern/exp.c | 39 + libkern/fcallfmt.c | 240 + libkern/floor.c | 26 + libkern/fmt.c | 188 + libkern/fmtdef.h | 85 + libkern/fmtprint.c | 44 + libkern/fmtquote.c | 259 + libkern/fmtstr.c | 23 + libkern/fmtvprint.c | 43 + libkern/frexp-386.c | 79 + libkern/frexp-68000.c | 80 + libkern/frexp-arm.c | 79 + libkern/frexp-mips.c | 80 + libkern/frexp-power.c | 79 + libkern/frexp-sparc.c | 79 + libkern/frexp-thumb.c | 79 + libkern/getfcr-386.s | 27 + libkern/getfcr-68000.s | 19 + libkern/getfcr-arm.s | 12 + libkern/getfcr-mips.s | 15 + libkern/getfcr-power.s | 28 + libkern/getfcr-sparc.s | 27 + libkern/getfcr-thumb.s | 12 + libkern/getfields.c | 35 + libkern/log.c | 57 + libkern/memccpy-power.s | 23 + libkern/memccpy.c | 17 + libkern/memchr.c | 16 + libkern/memcmp-power.s | 110 + libkern/memcmp.c | 22 + libkern/memcpy-386.s | 58 + libkern/memcpy-arm.c | 35 + libkern/memcpy-thumb.c | 35 + libkern/memmove-386.s | 58 + libkern/memmove-68000.s | 124 + libkern/memmove-arm.s | 223 + libkern/memmove-mips.s | 237 + libkern/memmove-power.s | 170 + libkern/memmove-sparc.s | 162 + libkern/memmove-thumb.s | 223 + libkern/memmove.c | 43 + libkern/memset-386.s | 35 + libkern/memset-68000.s | 57 + libkern/memset-arm.s | 66 + libkern/memset-mips.s | 88 + libkern/memset-power.s | 73 + libkern/memset-sparc.s | 88 + libkern/memset-thumb.s | 66 + libkern/memset.c | 15 + libkern/mkfile | 81 + libkern/mkfile-386 | 12 + libkern/mkfile-68000 | 12 + libkern/mkfile-arm | 13 + libkern/mkfile-mips | 13 + libkern/mkfile-power | 13 + libkern/mkfile-sparc | 13 + libkern/mkfile-spim | 8 + libkern/mkfile-thumb | 12 + libkern/muldiv-68000.s | 172 + libkern/nan-386.c | 69 + libkern/nan-68000.c | 70 + libkern/nan-arm.c | 69 + libkern/nan-mips.c | 70 + libkern/nan-power.c | 69 + libkern/nan-sparc.c | 69 + libkern/nan-thumb.c | 69 + libkern/netmkaddr.c | 50 + libkern/pow.c | 68 + libkern/pow10.c | 40 + libkern/qsort.c | 122 + libkern/rune.c | 162 + libkern/runestrlen.c | 13 + libkern/seprint.c | 26 + libkern/sin.c | 67 + libkern/smprint.c | 26 + libkern/snprint.c | 27 + libkern/sqrt.c | 53 + libkern/strcat.c | 9 + libkern/strchr-386.s | 29 + libkern/strchr-68000.s | 27 + libkern/strchr-arm.s | 52 + libkern/strchr-mips.c | 0 libkern/strchr-mips.s | 63 + libkern/strchr-power.s | 16 + libkern/strchr-sparc.s | 73 + libkern/strchr-thumb.s | 52 + libkern/strchr.c | 18 + libkern/strcmp-power.s | 21 + libkern/strcmp.c | 19 + libkern/strcpy.c | 15 + libkern/strdup.c | 12 + libkern/strecpy.c | 16 + libkern/strlen.c | 8 + libkern/strncmp-power.s | 29 + libkern/strncmp.c | 21 + libkern/strncpy.c | 17 + libkern/strrchr.c | 14 + libkern/strstr.c | 21 + libkern/strtod.c | 31 + libkern/strtol.c | 94 + libkern/strtoll.c | 81 + libkern/strtoul.c | 96 + libkern/tokenize.c | 58 + libkern/toupper.c | 16 + libkern/u16.c | 52 + libkern/u32.c | 109 + libkern/u64.c | 126 + libkern/utfecpy.c | 20 + libkern/utflen.c | 22 + libkern/utfnlen.c | 25 + libkern/utfrrune.c | 30 + libkern/utfrune.c | 29 + libkern/vlop-386.s | 44 + libkern/vlop-arm.s | 34 + libkern/vlop-mips.s | 17 + libkern/vlop-power.s | 14 + libkern/vlop-sparc.s | 112 + libkern/vlop-thumb.s | 34 + libkern/vlrt-386.c | 695 + libkern/vlrt-68000.c | 771 + libkern/vlrt-arm.c | 718 + libkern/vlrt-mips.c | 719 + libkern/vlrt-power.c | 718 + libkern/vlrt-sparc.c | 718 + libkern/vlrt-thumb.c | 718 + libkern/vseprint.c | 34 + libkern/vsmprint.c | 74 + libkern/vsnprint.c | 34 + libkeyring/NOTICE | 25 + libkeyring/dsaalg.c | 203 + libkeyring/egalg.c | 212 + libkeyring/keys.h | 118 + libkeyring/mkfile | 17 + libkeyring/rsaalg.c | 211 + liblogfs/NOTICE | 25 + liblogfs/boot.c | 496 + liblogfs/clunk.c | 19 + liblogfs/conv.c | 285 + liblogfs/create.c | 82 + liblogfs/dump.c | 71 + liblogfs/error.c | 17 + liblogfs/extentlist.c | 276 + liblogfs/fidmap.c | 66 + liblogfs/findfreeblock.c | 31 + liblogfs/flush.c | 17 + liblogfs/format.c | 109 + liblogfs/gn.c | 26 + liblogfs/group.c | 93 + liblogfs/groupset.c | 91 + liblogfs/is.c | 305 + liblogfs/local.h | 333 + liblogfs/log.c | 270 + liblogfs/map.c | 136 + liblogfs/mkfile | 45 + liblogfs/open.c | 72 + liblogfs/path.c | 48 + liblogfs/perm.c | 25 + liblogfs/read.c | 253 + liblogfs/remove.c | 147 + liblogfs/replace.c | 175 + liblogfs/replay.c | 386 + liblogfs/scan.c | 293 + liblogfs/srv.c | 308 + liblogfs/sweep.c | 272 + liblogfs/tagname.c | 20 + liblogfs/test.c | 13 + liblogfs/ust.c | 65 + liblogfs/walk.c | 108 + liblogfs/write.c | 593 + liblogfs/wstat.c | 245 + libmath/FPcontrol-FreeBSD.c | 78 + libmath/FPcontrol-Hp.c | 103 + libmath/FPcontrol-Inferno.c | 77 + libmath/FPcontrol-Irix.c | 102 + libmath/FPcontrol-Linux.c | 78 + libmath/FPcontrol-MacOSX.c | 94 + libmath/FPcontrol-Nt.c | 82 + libmath/FPcontrol-Plan9.c | 1 + libmath/FPcontrol-Solaris.c | 99 + libmath/FPcontrol-Unixware.c | 77 + libmath/NOTICE | 29 + libmath/bin/fdlibm-stubs | 71 + libmath/bin/unif_dtoa | 37 + libmath/bin/unif_fdlibm | 20 + libmath/blas.c | 61 + libmath/dtoa.c | 1807 + libmath/fdim.c | 30 + libmath/fdlibm/e_acos.c | 97 + libmath/fdlibm/e_acosh.c | 57 + libmath/fdlibm/e_asin.c | 106 + libmath/fdlibm/e_atan2.c | 115 + libmath/fdlibm/e_atanh.c | 60 + libmath/fdlibm/e_cosh.c | 81 + libmath/fdlibm/e_exp.c | 149 + libmath/fdlibm/e_fmod.c | 132 + libmath/fdlibm/e_hypot.c | 111 + libmath/fdlibm/e_j0.c | 375 + libmath/fdlibm/e_j1.c | 370 + libmath/fdlibm/e_jn.c | 259 + libmath/fdlibm/e_lgamma_r.c | 291 + libmath/fdlibm/e_log.c | 131 + libmath/fdlibm/e_log10.c | 83 + libmath/fdlibm/e_pow.c | 296 + libmath/fdlibm/e_rem_pio2.c | 159 + libmath/fdlibm/e_remainder.c | 69 + libmath/fdlibm/e_sinh.c | 74 + libmath/fdlibm/e_sqrt.c | 442 + libmath/fdlibm/fdlibm.h | 153 + libmath/fdlibm/k_cos.c | 84 + libmath/fdlibm/k_rem_pio2.c | 300 + libmath/fdlibm/k_sin.c | 66 + libmath/fdlibm/k_tan.c | 117 + libmath/fdlibm/readme | 261 + libmath/fdlibm/s_asinh.c | 53 + libmath/fdlibm/s_atan.c | 114 + libmath/fdlibm/s_cbrt.c | 75 + libmath/fdlibm/s_ceil.c | 70 + libmath/fdlibm/s_copysign.c | 27 + libmath/fdlibm/s_cos.c | 74 + libmath/fdlibm/s_erf.c | 297 + libmath/fdlibm/s_expm1.c | 208 + libmath/fdlibm/s_fabs.c | 25 + libmath/fdlibm/s_finite.c | 27 + libmath/fdlibm/s_floor.c | 71 + libmath/fdlibm/s_ilogb.c | 42 + libmath/fdlibm/s_isnan.c | 30 + libmath/fdlibm/s_log1p.c | 157 + libmath/fdlibm/s_modf.c | 72 + libmath/fdlibm/s_nextafter.c | 74 + libmath/fdlibm/s_rint.c | 88 + libmath/fdlibm/s_scalbn.c | 55 + libmath/fdlibm/s_sin.c | 74 + libmath/fdlibm/s_tan.c | 68 + libmath/fdlibm/s_tanh.c | 74 + libmath/g_fmt.c | 106 + libmath/gemm.c | 167 + libmath/gfltconv.c | 123 + libmath/mkfile | 67 + libmath/pow10.c | 8 + libmemdraw/NOTICE | 29 + libmemdraw/alloc.c | 194 + libmemdraw/arc.c | 116 + libmemdraw/cload.c | 67 + libmemdraw/cmap.c | 316 + libmemdraw/cread.c | 95 + libmemdraw/defont.c | 67 + libmemdraw/draw.c | 2514 ++ libmemdraw/drawtest.c | 1003 + libmemdraw/ellipse.c | 247 + libmemdraw/fillpoly.c | 522 + libmemdraw/hwdraw.c | 11 + libmemdraw/icossin.c | 139 + libmemdraw/icossin2.c | 260 + libmemdraw/iprint.c | 11 + libmemdraw/line.c | 481 + libmemdraw/load.c | 71 + libmemdraw/mkfile | 33 + libmemdraw/mkfile-FreeBSD | 4 + libmemdraw/mkfile-Inferno | 9 + libmemdraw/mkfile-Irix | 4 + libmemdraw/mkfile-Linux | 4 + libmemdraw/mkfile-MacOSX | 4 + libmemdraw/mkfile-Nt | 4 + libmemdraw/mkfile-Plan9 | 9 + libmemdraw/mkfile-Solaris | 4 + libmemdraw/mkfile-os | 9 + libmemdraw/openmemsubfont.c | 51 + libmemdraw/poly.c | 23 + libmemdraw/read.c | 112 + libmemdraw/string.c | 66 + libmemdraw/subfont.c | 33 + libmemdraw/unload.c | 24 + libmemdraw/write.c | 182 + libmemlayer/NOTICE | 29 + libmemlayer/draw.c | 192 + libmemlayer/lalloc-x11.c | 185 + libmemlayer/lalloc.c | 78 + libmemlayer/layerop.c | 111 + libmemlayer/ldelete.c | 66 + libmemlayer/lhide.c | 67 + libmemlayer/line.c | 121 + libmemlayer/load.c | 54 + libmemlayer/lorigin.c | 106 + libmemlayer/lreshape.c | 0 libmemlayer/lsetrefresh.c | 34 + libmemlayer/ltofront.c | 80 + libmemlayer/ltorear.c | 68 + libmemlayer/mkfile | 26 + libmemlayer/mkfile-FreeBSD | 4 + libmemlayer/mkfile-Hp | 4 + libmemlayer/mkfile-Inferno | 4 + libmemlayer/mkfile-Irix | 4 + libmemlayer/mkfile-Linux | 4 + libmemlayer/mkfile-MacOSX | 4 + libmemlayer/mkfile-NetBSD | 4 + libmemlayer/mkfile-Nt | 4 + libmemlayer/mkfile-Plan9 | 4 + libmemlayer/mkfile-Posix | 4 + libmemlayer/mkfile-Solaris | 4 + libmemlayer/mkfile-Unixware | 4 + libmemlayer/mkfile-os | 4 + libmemlayer/unload.c | 51 + libmp/Inferno-386/mkfile | 22 + libmp/Inferno-386/mpdigdiv.s | 21 + libmp/Inferno-386/mpvecadd.s | 53 + libmp/Inferno-386/mpvecdigmuladd.s | 52 + libmp/Inferno-386/mpvecdigmulsub.s | 53 + libmp/Inferno-386/mpvecsub.s | 44 + libmp/Inferno-amd64/mkfile | 21 + libmp/Inferno-amd64/mpdigdiv.s | 21 + libmp/Inferno-amd64/mpvecadd.s | 54 + libmp/Inferno-amd64/mpvecdigmuladd.s | 53 + libmp/Inferno-amd64/mpvecdigmulsub.s | 54 + libmp/Inferno-amd64/mpvecsub.s | 45 + libmp/Inferno-mips/mkfile | 21 + libmp/Inferno-mips/mpdigdiv.s | 41 + libmp/Inferno-mips/mpvecadd.s | 67 + libmp/Inferno-mips/mpvecdigmuladd.s | 58 + libmp/Inferno-mips/mpvecdigmulsub.s | 61 + libmp/Inferno-mips/mpvecsub.s | 66 + libmp/Inferno-power/mkfile | 20 + libmp/Inferno-power/mpvecadd.s | 61 + libmp/Inferno-power/mpvecdigmuladd.s | 56 + libmp/Inferno-power/mpvecdigmulsub.s | 66 + libmp/Inferno-power/mpvecsub.s | 57 + libmp/NOTICE | 4 + libmp/Plan9-386/mkfile | 21 + libmp/Plan9-386/mpdigdiv.s | 21 + libmp/Plan9-386/mpvecadd.s | 53 + libmp/Plan9-386/mpvecdigmuladd.s | 52 + libmp/Plan9-386/mpvecdigmulsub.s | 53 + libmp/Plan9-386/mpvecsub.s | 44 + libmp/Plan9-amd64/mkfile | 21 + libmp/Plan9-amd64/mpdigdiv.s | 21 + libmp/Plan9-amd64/mpvecadd.s | 54 + libmp/Plan9-amd64/mpvecdigmuladd.s | 53 + libmp/Plan9-amd64/mpvecdigmulsub.s | 54 + libmp/Plan9-amd64/mpvecsub.s | 45 + libmp/Plan9-mips/mkfile | 21 + libmp/Plan9-mips/mpdigdiv.s | 41 + libmp/Plan9-mips/mpvecadd.s | 67 + libmp/Plan9-mips/mpvecdigmuladd.s | 58 + libmp/Plan9-mips/mpvecdigmulsub.s | 61 + libmp/Plan9-mips/mpvecsub.s | 66 + libmp/Plan9-power/mkfile | 20 + libmp/Plan9-power/mpvecadd.s | 61 + libmp/Plan9-power/mpvecdigmuladd.s | 56 + libmp/Plan9-power/mpvecdigmulsub.s | 66 + libmp/Plan9-power/mpvecsub.s | 57 + libmp/bigtest.c | 102 + libmp/mkfile | 27 + libmp/mtest.c | 70 + libmp/port/betomp.c | 40 + libmp/port/crt.c | 122 + libmp/port/crttest.c | 54 + libmp/port/dat.h | 12 + libmp/port/letomp.c | 28 + libmp/port/mkfile | 52 + libmp/port/mpadd.c | 54 + libmp/port/mpaux.c | 189 + libmp/port/mpcmp.c | 28 + libmp/port/mpdigdiv.c | 43 + libmp/port/mpdiv.c | 112 + libmp/port/mpeuclid.c | 85 + libmp/port/mpexp.c | 94 + libmp/port/mpextendedgcd.c | 106 + libmp/port/mpfactorial.c | 75 + libmp/port/mpfmt.c | 193 + libmp/port/mpinvert.c | 21 + libmp/port/mpleft.c | 52 + libmp/port/mpmod.c | 15 + libmp/port/mpmul.c | 156 + libmp/port/mprand.c | 42 + libmp/port/mpright.c | 54 + libmp/port/mpsub.c | 52 + libmp/port/mptobe.c | 57 + libmp/port/mptoi.c | 46 + libmp/port/mptole.c | 54 + libmp/port/mptoui.c | 35 + libmp/port/mptouv.c | 49 + libmp/port/mptov.c | 69 + libmp/port/mpvecadd.c | 35 + libmp/port/mpveccmp.c | 27 + libmp/port/mpvecdigmuladd.c | 103 + libmp/port/mpvecsub.c | 34 + libmp/port/os.h | 3 + libmp/port/reduce-nt | 4 + libmp/port/reduce-rc | 16 + libmp/port/reduce-sh | 13 + libmp/port/strtomp.c | 205 + libmp/test.c | 453 + libnandfs/NOTICE | 5 + libnandfs/calcformat.c | 24 + libnandfs/correctauxilliary.c | 81 + libnandfs/ecc.c | 98 + libnandfs/eraseblock.c | 50 + libnandfs/extracttags.c | 26 + libnandfs/findfreeblock.c | 28 + libnandfs/formatblock.c | 57 + libnandfs/getblockstatus.c | 27 + libnandfs/hamming31_26.c | 62 + libnandfs/init.c | 102 + libnandfs/local.h | 51 + libnandfs/markblockbad.c | 42 + libnandfs/mkfile | 33 + libnandfs/open.c | 253 + libnandfs/readblock.c | 36 + libnandfs/readpage.c | 52 + libnandfs/readpageauxilliary.c | 35 + libnandfs/reformatblock.c | 34 + libnandfs/setget.c | 101 + libnandfs/updatepage.c | 38 + libnandfs/writeblock.c | 47 + libnandfs/writepageauxilliary.c | 33 + libprefab/NOTICE | 25 + libprefab/box.c | 80 + libprefab/compound.c | 193 + libprefab/element.c | 723 + libprefab/elistelement.c | 113 + libprefab/iconbox.c | 83 + libprefab/iconelement.c | 50 + libprefab/mkfile | 21 + libprefab/textbox.c | 138 + libprefab/textelement.c | 488 + libsec/Inferno-386/md5block.s | 220 + libsec/Inferno-386/mkfile | 17 + libsec/Inferno-386/sha1block.s | 195 + libsec/Inferno-mips/md5block.s | 305 + libsec/Inferno-mips/mkfile | 16 + libsec/Inferno-mips/sha1block.s | 220 + libsec/NOTICE | 4 + libsec/Plan9-386/md5block.s | 220 + libsec/Plan9-386/mkfile | 16 + libsec/Plan9-386/sha1block.s | 195 + libsec/Plan9-mips/md5block.s | 305 + libsec/Plan9-mips/mkfile | 16 + libsec/Plan9-mips/sha1block.s | 220 + libsec/mkfile | 6 + libsec/port/aes.c | 1543 + libsec/port/blowfish.c | 579 + libsec/port/decodepem.c | 89 + libsec/port/des.c | 480 + libsec/port/des3CBC.c | 59 + libsec/port/des3ECB.c | 48 + libsec/port/desCBC.c | 59 + libsec/port/desECB.c | 48 + libsec/port/desmodes.c | 31 + libsec/port/dsaalloc.c | 72 + libsec/port/dsagen.c | 58 + libsec/port/dsaprimes.c | 97 + libsec/port/dsaprivtopub.c | 16 + libsec/port/dsasign.c | 52 + libsec/port/dsaverify.c | 46 + libsec/port/egalloc.c | 70 + libsec/port/egdecrypt.c | 28 + libsec/port/egencrypt.c | 38 + libsec/port/eggen.c | 21 + libsec/port/egprivtopub.c | 17 + libsec/port/egsign.c | 43 + libsec/port/egtest.c | 34 + libsec/port/egverify.c | 29 + libsec/port/fastrand.c | 15 + libsec/port/genprime.c | 27 + libsec/port/genrandom.c | 62 + libsec/port/gensafeprime.c | 36 + libsec/port/genstrongprime.c | 57 + libsec/port/hmac.c | 56 + libsec/port/hmactest.c | 19 + libsec/port/idea.c | 168 + libsec/port/md4.c | 271 + libsec/port/md4test.c | 31 + libsec/port/md5.c | 147 + libsec/port/md5block.c | 267 + libsec/port/md5pickle.c | 39 + libsec/port/mkfile | 32 + libsec/port/nfastrand.c | 22 + libsec/port/primetest.c | 41 + libsec/port/prng.c | 15 + libsec/port/probably_prime.c | 84 + libsec/port/rc4.c | 104 + libsec/port/reduce-nt | 4 + libsec/port/reduce-rc | 16 + libsec/port/reduce-sh | 13 + libsec/port/rsaalloc.c | 52 + libsec/port/rsadecrypt.c | 37 + libsec/port/rsaencrypt.c | 12 + libsec/port/rsafill.c | 61 + libsec/port/rsagen.c | 70 + libsec/port/rsaprivtopub.c | 16 + libsec/port/rsatest.c | 57 + libsec/port/sha1.c | 127 + libsec/port/sha1block.c | 187 + libsec/port/sha1pickle.c | 38 + libsec/port/smallprimes.c | 1004 + libsec/port/smallprimetest.c | 1039 + libtk/NOTICE | 25 + libtk/buton.c | 609 + libtk/canvs.c | 2220 + libtk/canvs.h | 238 + libtk/canvu.c | 506 + libtk/carcs.c | 280 + libtk/cbits.c | 211 + libtk/cimag.c | 211 + libtk/cline.c | 268 + libtk/colrs.c | 90 + libtk/coval.c | 248 + libtk/cpoly.c | 270 + libtk/crect.c | 252 + libtk/ctext.c | 666 + libtk/cwind.c | 410 + libtk/ebind.c | 1033 + libtk/entry.c | 1379 + libtk/extns.c | 37 + libtk/frame.c | 277 + libtk/frame.h | 2 + libtk/grids.c | 1552 + libtk/image.c | 380 + libtk/label.c | 539 + libtk/label.h | 73 + libtk/listb.c | 1065 + libtk/listb.h | 1 + libtk/mail.tk | 224 + libtk/menu.tk | 49 + libtk/menus.c | 1840 + libtk/mkfile | 26 + libtk/mkfile-std | 44 + libtk/packr.c | 689 + libtk/panel.c | 408 + libtk/parse.c | 1165 + libtk/radio.tk | 4 + libtk/scale.c | 958 + libtk/scrol.c | 781 + libtk/textu.c | 1076 + libtk/textw.c | 3693 ++ libtk/textw.h | 229 + libtk/tindx.c | 609 + libtk/tmark.c | 392 + libtk/ttags.c | 1029 + libtk/twind.c | 396 + libtk/utils.c | 1951 + libtk/windw.c | 716 + libtk/xdata.c | 217 + limbo/NOTICE | 25 + limbo/asm.c | 289 + limbo/com.c | 1489 + limbo/decls.c | 1367 + limbo/dis.c | 638 + limbo/dtocanon.c | 35 + limbo/ecom.c | 2560 ++ limbo/fns.h | 391 + limbo/gen.c | 1097 + limbo/lex.c | 1429 + limbo/limbo.h | 701 + limbo/limbo.y | 2032 + limbo/mkfile | 41 + limbo/nodes.c | 1538 + limbo/optab.c | 658 + limbo/optim.c | 1803 + limbo/runt.h | 2012 + limbo/sbl.c | 351 + limbo/stubs.c | 590 + limbo/typecheck.c | 3635 ++ limbo/types.c | 4745 +++ 2729 files changed, 887554 insertions(+) create mode 100644 CHANGES create mode 100755 FreeBSD/386/bin/data2c create mode 100755 FreeBSD/386/bin/mk create mode 100755 FreeBSD/386/bin/yacc create mode 100644 FreeBSD/386/include/fpuctl.h create mode 100644 FreeBSD/386/include/lib9.h create mode 100644 Inferno/386/include/lib9.h create mode 100644 Inferno/386/include/u.h create mode 100644 Inferno/386/include/ureg.h create mode 100644 Inferno/arm/include/lib9.h create mode 100644 Inferno/arm/include/u.h create mode 100644 Inferno/arm/include/ureg.h create mode 100644 Inferno/mips/include/lib9.h create mode 100644 Inferno/mips/include/u.h create mode 100644 Inferno/power/include/lib9.h create mode 100644 Inferno/power/include/u.h create mode 100644 Inferno/power/include/ureg.h create mode 100644 Inferno/sparc/include/lib9.h create mode 100644 Inferno/sparc/include/u.h create mode 100644 Inferno/sparc/include/ureg.h create mode 100644 Inferno/thumb/include/lib9.h create mode 100644 Inferno/thumb/include/u.h create mode 100644 Inferno/thumb/include/ureg.h create mode 100755 Irix/mips/bin/awk create mode 100755 Irix/mips/bin/mk create mode 100755 Irix/mips/bin/mkext create mode 100755 Irix/mips/bin/yacc create mode 100644 Irix/mips/include/lib9.h create mode 100644 LICENCE create mode 100755 Linux/386/bin/data2c create mode 100755 Linux/386/bin/mk create mode 100755 Linux/386/bin/yacc create mode 100644 Linux/386/include/fpuctl.h create mode 100644 Linux/386/include/lib9.h create mode 100644 MacOSX/README create mode 100755 MacOSX/power/bin/data2c create mode 100755 MacOSX/power/bin/mk create mode 100755 MacOSX/power/bin/yacc create mode 100644 MacOSX/power/include/fpuctl.h create mode 100644 MacOSX/power/include/lib9.h create mode 100644 MacOSX/tcshrc create mode 100644 NOTICE create mode 100755 Nt/386/bin/awk.exe create mode 100755 Nt/386/bin/c2l.exe create mode 100755 Nt/386/bin/cp.exe create mode 100755 Nt/386/bin/data2c.exe create mode 100755 Nt/386/bin/echo.exe create mode 100755 Nt/386/bin/format.exe create mode 100755 Nt/386/bin/gzip.exe create mode 100755 Nt/386/bin/infdb.exe create mode 100755 Nt/386/bin/mk.exe create mode 100755 Nt/386/bin/mkdir.exe create mode 100755 Nt/386/bin/mkext.exe create mode 100755 Nt/386/bin/mv.exe create mode 100755 Nt/386/bin/rcsh.exe create mode 100755 Nt/386/bin/rm.exe create mode 100755 Nt/386/bin/sed.exe create mode 100755 Nt/386/bin/test.exe create mode 100755 Nt/386/bin/tr.exe create mode 100755 Nt/386/bin/yacc.exe create mode 100755 Nt/386/include/lib9.h create mode 100755 Plan9/386/bin/data2c create mode 100644 Plan9/386/include/lib9.h create mode 100644 Plan9/386/include/u.h create mode 100644 Plan9/mips/include/lib9.h create mode 100644 Plan9/mips/include/u.h create mode 100644 Plan9/power/include/lib9.h create mode 100644 Plan9/power/include/u.h create mode 100644 Plan9/sparc/include/lib9.h create mode 100644 Plan9/sparc/include/os.h create mode 100644 Plan9/sparc/include/u.h create mode 100644 README.gcode create mode 100755 Solaris/sparc/bin/data2c create mode 100755 Solaris/sparc/bin/mk create mode 100755 Solaris/sparc/bin/mkext create mode 100755 Solaris/sparc/bin/yacc create mode 100644 Solaris/sparc/include/fpuctl.h create mode 100644 Solaris/sparc/include/lib9.h create mode 100644 acme/acid/Acid.dis create mode 100644 acme/acid/Acid0.dis create mode 100644 acme/acid/guide create mode 100644 acme/acid/readme create mode 100644 acme/bin/guide create mode 100644 acme/bin/readme create mode 100644 acme/dis/adiff.dis create mode 100644 acme/dis/agrep.dis create mode 100644 acme/dis/awd.dis create mode 100644 acme/dis/cd.dis create mode 100644 acme/dis/new.dis create mode 100644 acme/dis/spout.dis create mode 100644 acme/dis/win.dis create mode 100644 acme/dis/winm.dis create mode 100644 acme/edit/a.dis create mode 100644 acme/edit/c.dis create mode 100644 acme/edit/d.dis create mode 100644 acme/edit/e.dis create mode 100644 acme/edit/g.dis create mode 100644 acme/edit/guide create mode 100644 acme/edit/i.dis create mode 100644 acme/edit/p.dis create mode 100644 acme/edit/pipe.dis create mode 100644 acme/edit/readme create mode 100644 acme/edit/x.dis create mode 100644 acme/mail/Mail.dis create mode 100644 acme/mail/Mailpop3.dis create mode 100644 acme/mail/guide create mode 100644 acme/mail/mkbox.dis create mode 100644 acme/mail/readme create mode 100644 appl/NOTICE create mode 100644 appl/acme/acme.b create mode 100644 appl/acme/acme.m create mode 100644 appl/acme/acme/acid/guide create mode 100644 appl/acme/acme/acid/mkfile create mode 100644 appl/acme/acme/acid/readme create mode 100644 appl/acme/acme/acid/src/Acid.b create mode 100644 appl/acme/acme/acid/src/Acid0.b create mode 100644 appl/acme/acme/acid/src/mkfile create mode 100644 appl/acme/acme/bin/guide create mode 100644 appl/acme/acme/bin/mkfile create mode 100644 appl/acme/acme/bin/readme create mode 100644 appl/acme/acme/bin/src/adiff.b create mode 100644 appl/acme/acme/bin/src/agrep.b create mode 100644 appl/acme/acme/bin/src/awd.b create mode 100644 appl/acme/acme/bin/src/cd.b create mode 100644 appl/acme/acme/bin/src/mkfile create mode 100644 appl/acme/acme/bin/src/new.b create mode 100644 appl/acme/acme/bin/src/spout.b create mode 100644 appl/acme/acme/bin/src/win.b create mode 100644 appl/acme/acme/bin/src/winm.b create mode 100644 appl/acme/acme/edit/guide create mode 100644 appl/acme/acme/edit/mkfile create mode 100644 appl/acme/acme/edit/readme create mode 100644 appl/acme/acme/edit/src/a.b create mode 100644 appl/acme/acme/edit/src/c.b create mode 100644 appl/acme/acme/edit/src/d.b create mode 100644 appl/acme/acme/edit/src/e.b create mode 100644 appl/acme/acme/edit/src/findfile.b create mode 100644 appl/acme/acme/edit/src/g.b create mode 100644 appl/acme/acme/edit/src/i.b create mode 100644 appl/acme/acme/edit/src/input.b create mode 100644 appl/acme/acme/edit/src/mkfile create mode 100644 appl/acme/acme/edit/src/p.b create mode 100644 appl/acme/acme/edit/src/pipe.b create mode 100644 appl/acme/acme/edit/src/x.b create mode 100644 appl/acme/acme/edit/src/xxx.b create mode 100644 appl/acme/acme/mail/guide create mode 100644 appl/acme/acme/mail/mkbox.b create mode 100644 appl/acme/acme/mail/mkfile create mode 100644 appl/acme/acme/mail/readme create mode 100644 appl/acme/acme/mail/src/Mail.b create mode 100644 appl/acme/acme/mail/src/Mailp.b create mode 100644 appl/acme/acme/mail/src/Mailpop3.b create mode 100644 appl/acme/acme/mail/src/mashfile create mode 100644 appl/acme/acme/mail/src/mkfile create mode 100644 appl/acme/acme/mkfile create mode 100644 appl/acme/buff.b create mode 100644 appl/acme/buff.m create mode 100644 appl/acme/col.b create mode 100644 appl/acme/col.m create mode 100644 appl/acme/common.m create mode 100644 appl/acme/dat.b create mode 100644 appl/acme/dat.m create mode 100644 appl/acme/disk.b create mode 100644 appl/acme/disk.m create mode 100644 appl/acme/ecmd.b create mode 100644 appl/acme/ecmd.m create mode 100644 appl/acme/edit.b create mode 100644 appl/acme/edit.m create mode 100644 appl/acme/elog.b create mode 100644 appl/acme/elog.m create mode 100644 appl/acme/exec.b create mode 100644 appl/acme/exec.m create mode 100644 appl/acme/file.b create mode 100644 appl/acme/file.m create mode 100644 appl/acme/frame.b create mode 100644 appl/acme/frame.m create mode 100644 appl/acme/fsys.b create mode 100644 appl/acme/fsys.m create mode 100644 appl/acme/graph.b create mode 100644 appl/acme/graph.m create mode 100644 appl/acme/gui.b create mode 100644 appl/acme/gui.m create mode 100644 appl/acme/look.b create mode 100644 appl/acme/look.m create mode 100644 appl/acme/mkfile create mode 100644 appl/acme/regx.b create mode 100644 appl/acme/regx.m create mode 100644 appl/acme/row.b create mode 100644 appl/acme/row.m create mode 100644 appl/acme/scrl.b create mode 100644 appl/acme/scrl.m create mode 100644 appl/acme/styxaux.b create mode 100644 appl/acme/styxaux.m create mode 100644 appl/acme/text.b create mode 100644 appl/acme/text.m create mode 100644 appl/acme/time.b create mode 100644 appl/acme/time.m create mode 100644 appl/acme/util.b create mode 100644 appl/acme/util.m create mode 100644 appl/acme/wind.b create mode 100644 appl/acme/wind.m create mode 100644 appl/acme/xfid.b create mode 100644 appl/acme/xfid.m create mode 100644 appl/alphabet/abc/abc.b create mode 100644 appl/alphabet/abc/autoconvert.b create mode 100644 appl/alphabet/abc/autodeclare.b create mode 100644 appl/alphabet/abc/declare.b create mode 100644 appl/alphabet/abc/declares.b create mode 100644 appl/alphabet/abc/define.b create mode 100644 appl/alphabet/abc/eval.b create mode 100644 appl/alphabet/abc/import.b create mode 100644 appl/alphabet/abc/mkfile create mode 100644 appl/alphabet/abc/newtypeset.b create mode 100644 appl/alphabet/abc/rewrite.b create mode 100644 appl/alphabet/abc/type.b create mode 100644 appl/alphabet/abc/typeset.b create mode 100644 appl/alphabet/abc/undeclare.b create mode 100644 appl/alphabet/alphabet.b create mode 100644 appl/alphabet/alphabet.proto create mode 100644 appl/alphabet/alphabet.shmod.b create mode 100644 appl/alphabet/auxi/endpoints.b create mode 100644 appl/alphabet/auxi/endpointsrv.b create mode 100644 appl/alphabet/auxi/fsfilter.b create mode 100644 appl/alphabet/auxi/mkfile create mode 100644 appl/alphabet/auxi/rexecsrv.b create mode 100644 appl/alphabet/declare.sh create mode 100644 appl/alphabet/eval.b create mode 100644 appl/alphabet/extvalues.b create mode 100644 appl/alphabet/fs/and.b create mode 100644 appl/alphabet/fs/bundle.b create mode 100644 appl/alphabet/fs/bundle.m create mode 100644 appl/alphabet/fs/chstat.b create mode 100644 appl/alphabet/fs/compose.b create mode 100644 appl/alphabet/fs/depth.b create mode 100644 appl/alphabet/fs/entries.b create mode 100644 appl/alphabet/fs/exec.b create mode 100644 appl/alphabet/fs/filter.b create mode 100644 appl/alphabet/fs/ls.b create mode 100644 appl/alphabet/fs/match.b create mode 100644 appl/alphabet/fs/merge.b create mode 100644 appl/alphabet/fs/mergewrite.b create mode 100644 appl/alphabet/fs/mkext.b create mode 100644 appl/alphabet/fs/mkfile create mode 100644 appl/alphabet/fs/mode.b create mode 100644 appl/alphabet/fs/newer.b create mode 100644 appl/alphabet/fs/not.b create mode 100644 appl/alphabet/fs/or.b create mode 100644 appl/alphabet/fs/path.b create mode 100644 appl/alphabet/fs/pipe.b create mode 100644 appl/alphabet/fs/print.b create mode 100644 appl/alphabet/fs/proto.b create mode 100644 appl/alphabet/fs/query.b create mode 100644 appl/alphabet/fs/run.b create mode 100644 appl/alphabet/fs/select.b create mode 100644 appl/alphabet/fs/setroot.b create mode 100644 appl/alphabet/fs/size.b create mode 100644 appl/alphabet/fs/unbundle.b create mode 100644 appl/alphabet/fs/unbundle.m create mode 100644 appl/alphabet/fs/walk.b create mode 100644 appl/alphabet/fs/write.b create mode 100644 appl/alphabet/fsdecl.sh create mode 100755 appl/alphabet/getendpoint.sh create mode 100644 appl/alphabet/grid/farm.b create mode 100644 appl/alphabet/grid/line2rec.b create mode 100644 appl/alphabet/grid/local.b create mode 100644 appl/alphabet/grid/mkfile create mode 100644 appl/alphabet/grid/remote.b create mode 100644 appl/alphabet/grid/rexec.b create mode 100644 appl/alphabet/main/auth.b create mode 100644 appl/alphabet/main/cat.b create mode 100644 appl/alphabet/main/create.b create mode 100644 appl/alphabet/main/dial.b create mode 100644 appl/alphabet/main/echo.b create mode 100644 appl/alphabet/main/export.b create mode 100644 appl/alphabet/main/fd.b create mode 100644 appl/alphabet/main/filter.b create mode 100644 appl/alphabet/main/genfilter.b create mode 100644 appl/alphabet/main/mkfile create mode 100644 appl/alphabet/main/mount.b create mode 100644 appl/alphabet/main/par.b create mode 100644 appl/alphabet/main/parse.b create mode 100644 appl/alphabet/main/pretty.b create mode 100644 appl/alphabet/main/print.b create mode 100644 appl/alphabet/main/read.b create mode 100644 appl/alphabet/main/readall.b create mode 100644 appl/alphabet/main/rewrite.b create mode 100644 appl/alphabet/main/rw.b create mode 100644 appl/alphabet/main/seq.b create mode 100644 appl/alphabet/main/unparse.b create mode 100644 appl/alphabet/main/w2fd.b create mode 100644 appl/alphabet/main/wait.b create mode 100755 appl/alphabet/mkendpoint.sh create mode 100644 appl/alphabet/mkfile create mode 100644 appl/alphabet/newtypesets create mode 100644 appl/alphabet/proxy.b create mode 100644 appl/alphabet/reports.b create mode 100755 appl/alphabet/rexecsrv.sh create mode 100644 appl/alphabet/setup create mode 100644 appl/alphabet/typesets/abc.b create mode 100644 appl/alphabet/typesets/abctypes.b create mode 100644 appl/alphabet/typesets/fs.b create mode 100644 appl/alphabet/typesets/fstypes.b create mode 100644 appl/alphabet/typesets/grid.b create mode 100644 appl/alphabet/typesets/gridtypes.b create mode 100644 appl/alphabet/typesets/mkfile create mode 100644 appl/charon/build.b create mode 100644 appl/charon/build.m create mode 100644 appl/charon/charon.b create mode 100644 appl/charon/charon.m create mode 100644 appl/charon/chutils.b create mode 100644 appl/charon/chutils.m create mode 100644 appl/charon/common.m create mode 100644 appl/charon/cookiesrv.b create mode 100644 appl/charon/cookiesrv.m create mode 100644 appl/charon/ctype.b create mode 100644 appl/charon/ctype.m create mode 100644 appl/charon/date.b create mode 100644 appl/charon/date.m create mode 100644 appl/charon/event.b create mode 100644 appl/charon/event.m create mode 100644 appl/charon/file.b create mode 100644 appl/charon/ftp.b create mode 100644 appl/charon/gui.b create mode 100644 appl/charon/gui.m create mode 100644 appl/charon/http.b create mode 100644 appl/charon/img.b create mode 100644 appl/charon/img.m create mode 100644 appl/charon/jscript.b create mode 100644 appl/charon/layout.b create mode 100644 appl/charon/layout.m create mode 100644 appl/charon/lex.b create mode 100644 appl/charon/lex.m create mode 100644 appl/charon/mkfile create mode 100644 appl/charon/paginate.b create mode 100644 appl/charon/paginate.m create mode 100644 appl/charon/rgb.inc create mode 100644 appl/charon/script.m create mode 100644 appl/charon/transport.m create mode 100644 appl/charon/url.b create mode 100644 appl/charon/url.m create mode 100644 appl/charon/xxx.inc create mode 100644 appl/charon/ycbcr.inc create mode 100644 appl/cmd/9660srv.b create mode 100644 appl/cmd/9export.b create mode 100644 appl/cmd/9srvfs.b create mode 100644 appl/cmd/9win.b create mode 100644 appl/cmd/B.b create mode 100644 appl/cmd/archfs.b create mode 100644 appl/cmd/auplay.b create mode 100644 appl/cmd/auth/aescbc.b create mode 100644 appl/cmd/auth/changelogin.b create mode 100644 appl/cmd/auth/convpasswd.b create mode 100644 appl/cmd/auth/countersigner.b create mode 100644 appl/cmd/auth/createsignerkey.b create mode 100644 appl/cmd/auth/factotum/authio.m create mode 100644 appl/cmd/auth/factotum/factotum.b create mode 100644 appl/cmd/auth/factotum/feedkey.b create mode 100644 appl/cmd/auth/factotum/mkfile create mode 100644 appl/cmd/auth/factotum/proto/infauth.b create mode 100644 appl/cmd/auth/factotum/proto/keyreps.b create mode 100644 appl/cmd/auth/factotum/proto/keyreps.m create mode 100644 appl/cmd/auth/factotum/proto/mkfile create mode 100644 appl/cmd/auth/factotum/proto/p9any.b create mode 100644 appl/cmd/auth/factotum/proto/pass.b create mode 100644 appl/cmd/auth/factotum/rpc.b create mode 100644 appl/cmd/auth/getpk.b create mode 100644 appl/cmd/auth/keyfs.b create mode 100644 appl/cmd/auth/keysrv.b create mode 100644 appl/cmd/auth/logind.b create mode 100644 appl/cmd/auth/mkauthinfo.b create mode 100644 appl/cmd/auth/mkfile create mode 100644 appl/cmd/auth/passwd.b create mode 100644 appl/cmd/auth/secstore.b create mode 100644 appl/cmd/auth/signer.b create mode 100644 appl/cmd/auth/verify.b create mode 100644 appl/cmd/auxi/cpuslave.b create mode 100644 appl/cmd/auxi/digest.b create mode 100644 appl/cmd/auxi/fpgaload.b create mode 100644 appl/cmd/auxi/mangaload.b create mode 100644 appl/cmd/auxi/mkfile create mode 100644 appl/cmd/auxi/pcmcia.b create mode 100644 appl/cmd/auxi/rdbgsrv.b create mode 100644 appl/cmd/auxi/rstyxd.b create mode 100644 appl/cmd/avr/burn.b create mode 100644 appl/cmd/avr/mkfile create mode 100644 appl/cmd/basename.b create mode 100644 appl/cmd/bind.b create mode 100644 appl/cmd/bit2gif.b create mode 100644 appl/cmd/broke.b create mode 100644 appl/cmd/bytes.b create mode 100644 appl/cmd/cal.b create mode 100644 appl/cmd/cat.b create mode 100644 appl/cmd/cd.b create mode 100644 appl/cmd/chgrp.b create mode 100644 appl/cmd/chmod.b create mode 100644 appl/cmd/cleanname.b create mode 100644 appl/cmd/cmp.b create mode 100755 appl/cmd/comm.b create mode 100644 appl/cmd/cook.b create mode 100644 appl/cmd/cp.b create mode 100644 appl/cmd/cprof.b create mode 100644 appl/cmd/cpu.b create mode 100644 appl/cmd/crypt.b create mode 100644 appl/cmd/date.b create mode 100644 appl/cmd/dbfs.b create mode 100755 appl/cmd/dbm/delete.b create mode 100755 appl/cmd/dbm/fetch.b create mode 100755 appl/cmd/dbm/keys.b create mode 100755 appl/cmd/dbm/list.b create mode 100644 appl/cmd/dbm/mkfile create mode 100755 appl/cmd/dbm/store.b create mode 100644 appl/cmd/dd.b create mode 100644 appl/cmd/dial.b create mode 100644 appl/cmd/diff.b create mode 100644 appl/cmd/disdep.b create mode 100644 appl/cmd/disdump.b create mode 100644 appl/cmd/disk/format.b create mode 100644 appl/cmd/disk/ftl.b create mode 100644 appl/cmd/disk/kfs.b create mode 100644 appl/cmd/disk/kfscmd.b create mode 100644 appl/cmd/disk/mbr.b create mode 100644 appl/cmd/disk/mkext.b create mode 100644 appl/cmd/disk/mkfile create mode 100644 appl/cmd/disk/mkfs.b create mode 100644 appl/cmd/disk/prep/calc.tab.b create mode 100644 appl/cmd/disk/prep/calc.tab.m create mode 100644 appl/cmd/disk/prep/calc.y create mode 100644 appl/cmd/disk/prep/fdisk.b create mode 100644 appl/cmd/disk/prep/mkfile create mode 100644 appl/cmd/disk/prep/pedit.b create mode 100644 appl/cmd/disk/prep/pedit.m create mode 100644 appl/cmd/disk/prep/prep.b create mode 100644 appl/cmd/dossrv.b create mode 100644 appl/cmd/du.b create mode 100644 appl/cmd/echo.b create mode 100644 appl/cmd/ed.b create mode 100644 appl/cmd/emuinit.b create mode 100644 appl/cmd/env.b create mode 100644 appl/cmd/export.b create mode 100644 appl/cmd/fc.b create mode 100644 appl/cmd/fcp.b create mode 100755 appl/cmd/fmt.b create mode 100644 appl/cmd/fone.b create mode 100755 appl/cmd/fortune.b create mode 100755 appl/cmd/freq.b create mode 100644 appl/cmd/fs.b create mode 100644 appl/cmd/fs/and.b create mode 100644 appl/cmd/fs/bundle.b create mode 100644 appl/cmd/fs/chstat.b create mode 100644 appl/cmd/fs/compose.b create mode 100644 appl/cmd/fs/depth.b create mode 100644 appl/cmd/fs/entries.b create mode 100644 appl/cmd/fs/eval.b create mode 100644 appl/cmd/fs/exec.b create mode 100644 appl/cmd/fs/filter.b create mode 100644 appl/cmd/fs/ls.b create mode 100644 appl/cmd/fs/match.b create mode 100644 appl/cmd/fs/merge.b create mode 100644 appl/cmd/fs/mergewrite.b create mode 100644 appl/cmd/fs/mkfile create mode 100644 appl/cmd/fs/mode.b create mode 100644 appl/cmd/fs/not.b create mode 100644 appl/cmd/fs/or.b create mode 100644 appl/cmd/fs/path.b create mode 100644 appl/cmd/fs/pipe.b create mode 100644 appl/cmd/fs/print.b create mode 100644 appl/cmd/fs/proto.b create mode 100644 appl/cmd/fs/query.b create mode 100644 appl/cmd/fs/readfile.b create mode 100644 appl/cmd/fs/run.b create mode 100644 appl/cmd/fs/select.b create mode 100644 appl/cmd/fs/setroot.b create mode 100644 appl/cmd/fs/size.b create mode 100644 appl/cmd/fs/template.b create mode 100644 appl/cmd/fs/unbundle.b create mode 100644 appl/cmd/fs/void.b create mode 100644 appl/cmd/fs/walk.b create mode 100644 appl/cmd/fs/write.b create mode 100644 appl/cmd/ftest.b create mode 100644 appl/cmd/ftpfs.b create mode 100644 appl/cmd/getauthinfo.b create mode 100644 appl/cmd/getfile.b create mode 100644 appl/cmd/gettar.b create mode 100644 appl/cmd/gif2bit.b create mode 100644 appl/cmd/grep.b create mode 100644 appl/cmd/gunzip.b create mode 100644 appl/cmd/gzip.b create mode 100644 appl/cmd/idea.b create mode 100644 appl/cmd/import.b create mode 100644 appl/cmd/install/NOTICE create mode 100644 appl/cmd/install/applylog.b create mode 100644 appl/cmd/install/arch.b create mode 100644 appl/cmd/install/arch.m create mode 100644 appl/cmd/install/archfs.b create mode 100644 appl/cmd/install/archfs.m create mode 100644 appl/cmd/install/ckproto.b create mode 100644 appl/cmd/install/create.b create mode 100644 appl/cmd/install/eproto.b create mode 100644 appl/cmd/install/info.b create mode 100644 appl/cmd/install/inst.b create mode 100644 appl/cmd/install/install.b create mode 100644 appl/cmd/install/log.b create mode 100644 appl/cmd/install/logs.b create mode 100644 appl/cmd/install/logs.m create mode 100644 appl/cmd/install/mergelog.b create mode 100644 appl/cmd/install/mkfile create mode 100644 appl/cmd/install/mkproto.b create mode 100644 appl/cmd/install/proto.b create mode 100644 appl/cmd/install/proto.m create mode 100644 appl/cmd/install/proto2list.b create mode 100644 appl/cmd/install/protocaller.m create mode 100644 appl/cmd/install/updatelog.b create mode 100644 appl/cmd/install/wdiff.b create mode 100644 appl/cmd/install/wfind.b create mode 100644 appl/cmd/install/wrap.b create mode 100644 appl/cmd/install/wrap.m create mode 100644 appl/cmd/install/wrap2list.b create mode 100644 appl/cmd/iostats.b create mode 100644 appl/cmd/ip/bootpd.b create mode 100644 appl/cmd/ip/dhcp.b create mode 100644 appl/cmd/ip/mkfile create mode 100644 appl/cmd/ip/nppp/mkfile create mode 100644 appl/cmd/ip/nppp/modem.b create mode 100644 appl/cmd/ip/nppp/modem.m create mode 100644 appl/cmd/ip/nppp/pppchat.b create mode 100644 appl/cmd/ip/nppp/ppplink.b create mode 100644 appl/cmd/ip/nppp/ppptest.b create mode 100644 appl/cmd/ip/nppp/script.b create mode 100644 appl/cmd/ip/nppp/script.m create mode 100644 appl/cmd/ip/obootpd.b create mode 100644 appl/cmd/ip/ping.b create mode 100644 appl/cmd/ip/ppp/mkfile create mode 100644 appl/cmd/ip/ppp/modem.b create mode 100644 appl/cmd/ip/ppp/modem.m create mode 100644 appl/cmd/ip/ppp/pppclient.b create mode 100644 appl/cmd/ip/ppp/pppclient.m create mode 100644 appl/cmd/ip/ppp/pppdial.b create mode 100644 appl/cmd/ip/ppp/pppgui.b create mode 100644 appl/cmd/ip/ppp/pppgui.m create mode 100644 appl/cmd/ip/ppp/ppptest.b create mode 100644 appl/cmd/ip/ppp/script.b create mode 100644 appl/cmd/ip/ppp/script.m create mode 100644 appl/cmd/ip/rip.b create mode 100644 appl/cmd/ip/sntp.b create mode 100644 appl/cmd/ip/tftpd.b create mode 100644 appl/cmd/ip/virgild.b create mode 100644 appl/cmd/irtest.b create mode 100644 appl/cmd/itest.b create mode 100644 appl/cmd/itreplay.b create mode 100644 appl/cmd/kill.b create mode 100644 appl/cmd/lc.b create mode 100644 appl/cmd/lego/clock.b create mode 100644 appl/cmd/lego/clockface.b create mode 100644 appl/cmd/lego/firmdl.b create mode 100644 appl/cmd/lego/link.b create mode 100644 appl/cmd/lego/mkfile create mode 100644 appl/cmd/lego/rcxsend.b create mode 100644 appl/cmd/lego/rcxsend.m create mode 100644 appl/cmd/lego/send.b create mode 100644 appl/cmd/lego/timers.b create mode 100644 appl/cmd/lego/timers.m create mode 100644 appl/cmd/limbo/arg.m create mode 100644 appl/cmd/limbo/asm.b create mode 100644 appl/cmd/limbo/com.b create mode 100644 appl/cmd/limbo/decls.b create mode 100644 appl/cmd/limbo/dis.b create mode 100644 appl/cmd/limbo/disoptab.m create mode 100644 appl/cmd/limbo/ecom.b create mode 100644 appl/cmd/limbo/gen.b create mode 100644 appl/cmd/limbo/isa.m create mode 100644 appl/cmd/limbo/lex.b create mode 100644 appl/cmd/limbo/limbo.b create mode 100644 appl/cmd/limbo/limbo.m create mode 100644 appl/cmd/limbo/limbo.y create mode 100644 appl/cmd/limbo/mkfile create mode 100644 appl/cmd/limbo/nodes.b create mode 100644 appl/cmd/limbo/opname.m create mode 100644 appl/cmd/limbo/optim.b create mode 100644 appl/cmd/limbo/sbl.b create mode 100644 appl/cmd/limbo/stubs.b create mode 100644 appl/cmd/limbo/typecheck.b create mode 100644 appl/cmd/limbo/types.b create mode 100644 appl/cmd/listen.b create mode 100644 appl/cmd/lockfs.b create mode 100644 appl/cmd/logfile.b create mode 100755 appl/cmd/look.b create mode 100644 appl/cmd/lookman.b create mode 100644 appl/cmd/ls.b create mode 100644 appl/cmd/lstar.b create mode 100644 appl/cmd/man.b create mode 100644 appl/cmd/man2txt.b create mode 100644 appl/cmd/manufacture.b create mode 100644 appl/cmd/mash/builtins.b create mode 100644 appl/cmd/mash/depends.b create mode 100644 appl/cmd/mash/dump.b create mode 100644 appl/cmd/mash/exec.b create mode 100644 appl/cmd/mash/expr.b create mode 100644 appl/cmd/mash/eyacc.b create mode 100644 appl/cmd/mash/eyaccpar create mode 100644 appl/cmd/mash/history.b create mode 100644 appl/cmd/mash/lex.b create mode 100644 appl/cmd/mash/make.b create mode 100644 appl/cmd/mash/mash.b create mode 100644 appl/cmd/mash/mash.m create mode 100644 appl/cmd/mash/mash.y create mode 100644 appl/cmd/mash/mashfile create mode 100644 appl/cmd/mash/mashlib.b create mode 100644 appl/cmd/mash/mashparse.b create mode 100644 appl/cmd/mash/mashparse.m create mode 100644 appl/cmd/mash/misc.b create mode 100644 appl/cmd/mash/mkfile create mode 100644 appl/cmd/mash/serve.b create mode 100644 appl/cmd/mash/symb.b create mode 100644 appl/cmd/mash/tk.b create mode 100644 appl/cmd/mash/xeq.b create mode 100644 appl/cmd/mathcalc.b create mode 100644 appl/cmd/mc.b create mode 100644 appl/cmd/md5sum.b create mode 100644 appl/cmd/mdb.b create mode 100644 appl/cmd/memfs.b create mode 100644 appl/cmd/metamorph.b create mode 100644 appl/cmd/mk/ar.m create mode 100644 appl/cmd/mk/mk.b create mode 100644 appl/cmd/mk/mkbinds create mode 100644 appl/cmd/mk/mkconfig create mode 100644 appl/cmd/mk/mkfile create mode 100644 appl/cmd/mk/mksubdirs create mode 100644 appl/cmd/mkdir.b create mode 100644 appl/cmd/mkfile create mode 100644 appl/cmd/mntgen.b create mode 100644 appl/cmd/mount.b create mode 100644 appl/cmd/mouse.b create mode 100644 appl/cmd/mpc/mkfile create mode 100644 appl/cmd/mpc/qconfig.b create mode 100644 appl/cmd/mpc/qflash.b create mode 100644 appl/cmd/mprof.b create mode 100644 appl/cmd/mv.b create mode 100644 appl/cmd/ndb/cs.b create mode 100644 appl/cmd/ndb/csquery.b create mode 100644 appl/cmd/ndb/dns.b create mode 100644 appl/cmd/ndb/dnsquery.b create mode 100644 appl/cmd/ndb/mkfile create mode 100644 appl/cmd/ndb/mkhash.b create mode 100644 appl/cmd/ndb/query.b create mode 100644 appl/cmd/ndb/registry.b create mode 100644 appl/cmd/ndb/regquery.b create mode 100644 appl/cmd/netkey.b create mode 100644 appl/cmd/netstat.b create mode 100644 appl/cmd/newer.b create mode 100644 appl/cmd/ns.b create mode 100644 appl/cmd/nsbuild.b create mode 100644 appl/cmd/os.b create mode 100644 appl/cmd/p.b create mode 100644 appl/cmd/palm/connex.b create mode 100644 appl/cmd/palm/desklink.b create mode 100644 appl/cmd/palm/desklink.m create mode 100644 appl/cmd/palm/mkfile create mode 100644 appl/cmd/palm/palmsrv.b create mode 100644 appl/cmd/pause.b create mode 100644 appl/cmd/plumb.b create mode 100644 appl/cmd/plumber.b create mode 100644 appl/cmd/prof.b create mode 100644 appl/cmd/promptstring.b create mode 100644 appl/cmd/ps.b create mode 100644 appl/cmd/puttar.b create mode 100644 appl/cmd/pwd.b create mode 100644 appl/cmd/ramfile.b create mode 100644 appl/cmd/randpass.b create mode 100644 appl/cmd/raw2iaf.b create mode 100644 appl/cmd/rawdbfs.b create mode 100644 appl/cmd/rcmd.b create mode 100644 appl/cmd/rdp.b create mode 100644 appl/cmd/read.b create mode 100644 appl/cmd/rioimport.b create mode 100644 appl/cmd/rm.b create mode 100644 appl/cmd/runas.b create mode 100644 appl/cmd/sed.b create mode 100644 appl/cmd/sendmail.b create mode 100644 appl/cmd/sh/arg.b create mode 100644 appl/cmd/sh/csv.b create mode 100644 appl/cmd/sh/doc/History create mode 100644 appl/cmd/sh/echo.b create mode 100644 appl/cmd/sh/expr.b create mode 100644 appl/cmd/sh/file2chan.b create mode 100644 appl/cmd/sh/mkfile create mode 100644 appl/cmd/sh/regex.b create mode 100644 appl/cmd/sh/sexprs.b create mode 100644 appl/cmd/sh/sh.b create mode 100644 appl/cmd/sh/sh.y create mode 100644 appl/cmd/sh/std.b create mode 100644 appl/cmd/sh/string.b create mode 100644 appl/cmd/sh/test.b create mode 100644 appl/cmd/sh/tk.b create mode 100644 appl/cmd/sha1sum.b create mode 100644 appl/cmd/shutdown.b create mode 100644 appl/cmd/sleep.b create mode 100644 appl/cmd/sort.b create mode 100644 appl/cmd/spki/mkfile create mode 100644 appl/cmd/spki/verify.b create mode 100644 appl/cmd/src.b create mode 100644 appl/cmd/stack.b create mode 100644 appl/cmd/stackv.b create mode 100644 appl/cmd/stream.b create mode 100644 appl/cmd/strings.b create mode 100644 appl/cmd/styxchat.b create mode 100644 appl/cmd/styxlisten.b create mode 100644 appl/cmd/styxmon.b create mode 100644 appl/cmd/sum.b create mode 100644 appl/cmd/tail.b create mode 100644 appl/cmd/tarfs.b create mode 100644 appl/cmd/tclsh.b create mode 100644 appl/cmd/tcs.b create mode 100644 appl/cmd/tee.b create mode 100644 appl/cmd/telnet.b create mode 100644 appl/cmd/test.b create mode 100644 appl/cmd/time.b create mode 100644 appl/cmd/timestamp.b create mode 100644 appl/cmd/tkcmd.b create mode 100644 appl/cmd/tokenize.b create mode 100644 appl/cmd/touch.b create mode 100644 appl/cmd/touchcal.b create mode 100644 appl/cmd/tr.b create mode 100644 appl/cmd/tsort.b create mode 100644 appl/cmd/unicode.b create mode 100644 appl/cmd/uniq.b create mode 100644 appl/cmd/units.b create mode 100644 appl/cmd/units.y create mode 100644 appl/cmd/unmount.b create mode 100644 appl/cmd/usb/mkfile create mode 100644 appl/cmd/usb/usbd.b create mode 100644 appl/cmd/uudecode.b create mode 100644 appl/cmd/uuencode.b create mode 100644 appl/cmd/wav2iaf.b create mode 100644 appl/cmd/wc.b create mode 100644 appl/cmd/webgrab.b create mode 100644 appl/cmd/wish.b create mode 100644 appl/cmd/wmexport.b create mode 100644 appl/cmd/wmimport.b create mode 100644 appl/cmd/xargs.b create mode 100644 appl/cmd/xd.b create mode 100644 appl/cmd/xmount.b create mode 100644 appl/cmd/yacc.b create mode 100644 appl/cmd/zeros.b create mode 100644 appl/collab/clients/chat.b create mode 100644 appl/collab/clients/poll.b create mode 100644 appl/collab/clients/poller.b create mode 100644 appl/collab/clients/whiteboard.b create mode 100644 appl/collab/collabsrv.b create mode 100644 appl/collab/connect.b create mode 100644 appl/collab/lib/messages.b create mode 100644 appl/collab/lib/messages.m create mode 100644 appl/collab/mkfile create mode 100644 appl/collab/proxy.b create mode 100644 appl/collab/proxy.m create mode 100755 appl/collab/runcollab create mode 100644 appl/collab/servers/chatsrv.b create mode 100644 appl/collab/servers/memfssrv.b create mode 100644 appl/collab/servers/mpx.b create mode 100644 appl/collab/servers/wbsrv.b create mode 100644 appl/collab/service.m create mode 100644 appl/collab/srvmgr.b create mode 100644 appl/collab/srvmgr.m create mode 100644 appl/demo/camera/camera.b create mode 100644 appl/demo/camera/camera.dis create mode 100644 appl/demo/camera/camera.sbl create mode 100644 appl/demo/camera/camload.bit create mode 100644 appl/demo/camera/camproc.bit create mode 100644 appl/demo/camera/mkfile create mode 100755 appl/demo/camera/runcam.sh create mode 100755 appl/demo/camera/runcamlocal.sh create mode 100644 appl/demo/camera/tkinterface.b create mode 100644 appl/demo/camera/tkinterface.dis create mode 100644 appl/demo/camera/tkinterface.sbl create mode 100644 appl/demo/chat/chat.b create mode 100644 appl/demo/chat/chat.dis create mode 100644 appl/demo/chat/chat.sbl create mode 100755 appl/demo/chat/chatclient.sh create mode 100644 appl/demo/chat/chatsrv.b create mode 100644 appl/demo/chat/chatsrv.dis create mode 100644 appl/demo/chat/chatsrv.sbl create mode 100644 appl/demo/chat/mkfile create mode 100644 appl/demo/cpupool/mkfile create mode 100644 appl/demo/cpupool/regpoll.b create mode 100644 appl/demo/cpupool/regpoll.dis create mode 100644 appl/demo/cpupool/regpoll.sbl create mode 100755 appl/demo/cpupool/runrstyx.sh create mode 100644 appl/demo/lego/clockface.b create mode 100644 appl/demo/lego/clockface.dis create mode 100644 appl/demo/lego/clockface.sbl create mode 100644 appl/demo/lego/clockreg.sh create mode 100644 appl/demo/lego/firmdl.b create mode 100644 appl/demo/lego/firmdl.dis create mode 100644 appl/demo/lego/firmdl.sbl create mode 100644 appl/demo/lego/legolink.b create mode 100644 appl/demo/lego/legolink.dis create mode 100644 appl/demo/lego/legolink.sbl create mode 100644 appl/demo/lego/mkfile create mode 100644 appl/demo/lego/rcxsend.b create mode 100644 appl/demo/lego/rcxsend.dis create mode 100644 appl/demo/lego/rcxsend.m create mode 100644 appl/demo/lego/rcxsend.sbl create mode 100755 appl/demo/lego/styx.srec create mode 100644 appl/demo/lego/timers.b create mode 100644 appl/demo/lego/timers.dis create mode 100644 appl/demo/lego/timers.m create mode 100644 appl/demo/lego/timers.sbl create mode 100644 appl/demo/mkfile create mode 100644 appl/demo/ns/mkfile create mode 100644 appl/demo/ns/ns.b create mode 100644 appl/demo/ns/ns.dis create mode 100644 appl/demo/ns/ns.sbl create mode 100755 appl/demo/ns/runns.sh create mode 100644 appl/demo/odbc/mkfile create mode 100644 appl/demo/odbc/odbcmnt.b create mode 100644 appl/demo/odbc/odbcmnt.dis create mode 100644 appl/demo/odbc/odbcmnt.sbl create mode 100755 appl/demo/odbc/runodbc.sh create mode 100644 appl/demo/spree/mkfile create mode 100755 appl/demo/spree/spreeclient.sh create mode 100644 appl/demo/whiteboard/mkfile create mode 100755 appl/demo/whiteboard/runwb.sh create mode 100644 appl/demo/whiteboard/wbsrv.b create mode 100644 appl/demo/whiteboard/wbsrv.dis create mode 100644 appl/demo/whiteboard/wbsrv.sbl create mode 100644 appl/demo/whiteboard/whiteboard.b create mode 100644 appl/demo/whiteboard/whiteboard.dis create mode 100644 appl/demo/whiteboard/whiteboard.sbl create mode 100644 appl/ebook/checkxml.b create mode 100644 appl/ebook/cssfont.b create mode 100644 appl/ebook/cssfont.m create mode 100644 appl/ebook/cssparser.b create mode 100644 appl/ebook/cssparser.m create mode 100644 appl/ebook/dtd/oebdoc101.dtd create mode 100644 appl/ebook/dtd/oebpkg101.dtd create mode 100644 appl/ebook/ebook.b create mode 100644 appl/ebook/mimeimage.b create mode 100644 appl/ebook/mimeimage.m create mode 100644 appl/ebook/mkfile create mode 100644 appl/ebook/oebpackage.b create mode 100644 appl/ebook/oebpackage.m create mode 100644 appl/ebook/reader.b create mode 100644 appl/ebook/reader.m create mode 100644 appl/ebook/strcache.m create mode 100644 appl/ebook/strmap.b create mode 100644 appl/ebook/strmap.m create mode 100644 appl/ebook/stylesheet.b create mode 100644 appl/ebook/stylesheet.m create mode 100644 appl/ebook/table.b create mode 100644 appl/ebook/table.m create mode 100644 appl/ebook/tst.txt create mode 100644 appl/ebook/understandingoeb.opf create mode 100644 appl/ebook/units.b create mode 100644 appl/ebook/units.m create mode 100644 appl/grid/blurdemo.b create mode 100644 appl/grid/cpupool.b create mode 100644 appl/grid/demo/block.b create mode 100644 appl/grid/demo/blur.b create mode 100644 appl/grid/demo/mkfile create mode 100644 appl/grid/find.b create mode 100644 appl/grid/jpg2bit.b create mode 100644 appl/grid/lib/announce.b create mode 100644 appl/grid/lib/browser.b create mode 100644 appl/grid/lib/browser.m create mode 100644 appl/grid/lib/fbrowse.b create mode 100644 appl/grid/lib/mkfile create mode 100644 appl/grid/lib/pathreader.m create mode 100644 appl/grid/lib/srvbrowse.b create mode 100644 appl/grid/mkfile create mode 100644 appl/grid/query.b create mode 100644 appl/grid/readjpg.b create mode 100644 appl/grid/register.b create mode 100644 appl/grid/reglisten.b create mode 100644 appl/grid/regstyxlisten.b create mode 100644 appl/grid/remotelogon.b create mode 100644 appl/grid/usercreatesrv.b create mode 100644 appl/lib/NOTICE create mode 100644 appl/lib/arg.b create mode 100644 appl/lib/asn1.b create mode 100644 appl/lib/attrdb.b create mode 100644 appl/lib/attrhash.b create mode 100644 appl/lib/auth.b create mode 100644 appl/lib/auth9.b create mode 100644 appl/lib/bloomfilter.b create mode 100644 appl/lib/bufio.b create mode 100644 appl/lib/cfg.b create mode 100644 appl/lib/cfgfile.b create mode 100644 appl/lib/chanfill.b create mode 100644 appl/lib/convcs/8bit_stob.b create mode 100644 appl/lib/convcs/big5_btos.b create mode 100644 appl/lib/convcs/big5_stob.b create mode 100644 appl/lib/convcs/convcs.b create mode 100644 appl/lib/convcs/cp932_btos.b create mode 100644 appl/lib/convcs/cp_btos.b create mode 100644 appl/lib/convcs/cp_stob.b create mode 100644 appl/lib/convcs/euc-jp_btos.b create mode 100644 appl/lib/convcs/gb2312_btos.b create mode 100644 appl/lib/convcs/genbig5.b create mode 100644 appl/lib/convcs/gencp.b create mode 100644 appl/lib/convcs/gencp932.b create mode 100644 appl/lib/convcs/gengb2312.b create mode 100644 appl/lib/convcs/genjisx0201kana.b create mode 100644 appl/lib/convcs/genjisx0208-1997.b create mode 100644 appl/lib/convcs/genjisx0212.b create mode 100644 appl/lib/convcs/ibm437.b create mode 100644 appl/lib/convcs/ibm850.b create mode 100644 appl/lib/convcs/ibm866.b create mode 100644 appl/lib/convcs/iso8859-1.b create mode 100644 appl/lib/convcs/iso8859-10.b create mode 100644 appl/lib/convcs/iso8859-2.b create mode 100644 appl/lib/convcs/iso8859-3.b create mode 100644 appl/lib/convcs/iso8859-4.b create mode 100644 appl/lib/convcs/iso8859-5.b create mode 100644 appl/lib/convcs/iso8859-6.b create mode 100644 appl/lib/convcs/iso8859-7.b create mode 100644 appl/lib/convcs/iso8859-8.b create mode 100644 appl/lib/convcs/iso8859-9.b create mode 100644 appl/lib/convcs/koi8-r.b create mode 100755 appl/lib/convcs/mkdata create mode 100644 appl/lib/convcs/mkfile create mode 100644 appl/lib/convcs/utf8_btos.b create mode 100644 appl/lib/convcs/utf8_stob.b create mode 100644 appl/lib/convcs/windows-1250.b create mode 100644 appl/lib/convcs/windows-1251.b create mode 100644 appl/lib/convcs/windows-1252.b create mode 100644 appl/lib/crc.b create mode 100644 appl/lib/crypt/mkfile create mode 100644 appl/lib/crypt/pkcs.b create mode 100644 appl/lib/crypt/ssl3.b create mode 100644 appl/lib/crypt/sslsession.b create mode 100644 appl/lib/crypt/x509.b create mode 100644 appl/lib/daytime.b create mode 100644 appl/lib/db.b create mode 100644 appl/lib/dbm.b create mode 100644 appl/lib/dbsrv.b create mode 100644 appl/lib/debug.b create mode 100644 appl/lib/deflate.b create mode 100644 appl/lib/devpointer.b create mode 100644 appl/lib/dhcpclient.b create mode 100644 appl/lib/dialog.b create mode 100644 appl/lib/dict.b create mode 100644 appl/lib/dis.b create mode 100644 appl/lib/diskblocks.b create mode 100644 appl/lib/disks.b create mode 100644 appl/lib/dividers.b create mode 100644 appl/lib/ecmascript/builtin.b create mode 100644 appl/lib/ecmascript/date.b create mode 100644 appl/lib/ecmascript/ecmascript.b create mode 100644 appl/lib/ecmascript/exec.b create mode 100644 appl/lib/ecmascript/mkfile create mode 100644 appl/lib/ecmascript/obj.b create mode 100644 appl/lib/ecmascript/pprint.b create mode 100644 appl/lib/ecmascript/regexp.b create mode 100644 appl/lib/ecmascript/uri.b create mode 100644 appl/lib/encoding/base16.b create mode 100644 appl/lib/encoding/base32.b create mode 100644 appl/lib/encoding/base32a.b create mode 100644 appl/lib/encoding/base64.b create mode 100644 appl/lib/encoding/mkfile create mode 100644 appl/lib/env.b create mode 100644 appl/lib/ether.b create mode 100644 appl/lib/exception.b create mode 100644 appl/lib/factotum.b create mode 100644 appl/lib/filepat.b create mode 100644 appl/lib/format.b create mode 100644 appl/lib/fsfilter.b create mode 100644 appl/lib/fslib.b create mode 100644 appl/lib/fsproto.b create mode 100644 appl/lib/gamer.b create mode 100644 appl/lib/hash.b create mode 100644 appl/lib/html.b create mode 100644 appl/lib/ida/NOTICE create mode 100644 appl/lib/ida/ida.b create mode 100644 appl/lib/ida/idatab.b create mode 100644 appl/lib/ida/idatest.b create mode 100644 appl/lib/ida/mkfile create mode 100644 appl/lib/ida/mktab.b create mode 100644 appl/lib/imageremap.b create mode 100644 appl/lib/inflate.b create mode 100644 appl/lib/ip.b create mode 100644 appl/lib/ipattr.b create mode 100644 appl/lib/ir.b create mode 100644 appl/lib/irmpath.b create mode 100644 appl/lib/irsage.b create mode 100644 appl/lib/irsim.b create mode 100644 appl/lib/itslib.b create mode 100644 appl/lib/keyset.b create mode 100644 appl/lib/libc.b create mode 100644 appl/lib/libc0.b create mode 100644 appl/lib/lock.b create mode 100644 appl/lib/login.b create mode 100644 appl/lib/memfs.b create mode 100644 appl/lib/mkfile create mode 100644 appl/lib/mpeg.b create mode 100644 appl/lib/names.b create mode 100644 appl/lib/nametree.b create mode 100644 appl/lib/newns.b create mode 100644 appl/lib/palm.b create mode 100644 appl/lib/palmdb.b create mode 100644 appl/lib/palmfile.b create mode 100644 appl/lib/parseman.b create mode 100644 appl/lib/plumbing.b create mode 100644 appl/lib/plumbing.m create mode 100644 appl/lib/plumbmsg.b create mode 100644 appl/lib/pop3.b create mode 100644 appl/lib/popup.b create mode 100644 appl/lib/powerman.b create mode 100644 appl/lib/print/hp_driver.b create mode 100644 appl/lib/print/mkfile create mode 100644 appl/lib/print/print.b create mode 100644 appl/lib/print/scaler.b create mode 100644 appl/lib/print/scaler.m create mode 100644 appl/lib/profile.b create mode 100644 appl/lib/pslib.b create mode 100644 appl/lib/quicktime.b create mode 100644 appl/lib/rand.b create mode 100644 appl/lib/random.b create mode 100644 appl/lib/readdir.b create mode 100644 appl/lib/readgif.b create mode 100644 appl/lib/readjpg.b create mode 100644 appl/lib/readpicfile.b create mode 100644 appl/lib/readpng.b create mode 100644 appl/lib/readxbitmap.b create mode 100644 appl/lib/regex.b create mode 100644 appl/lib/regexutils.b create mode 100644 appl/lib/registries.b create mode 100644 appl/lib/riff.b create mode 100644 appl/lib/scoretable.b create mode 100644 appl/lib/scsiio.b create mode 100644 appl/lib/secstore.b create mode 100644 appl/lib/selectfile.b create mode 100644 appl/lib/sets.b create mode 100644 appl/lib/sets32.b create mode 100644 appl/lib/sexprs.b create mode 100644 appl/lib/slip.b create mode 100644 appl/lib/smtp.b create mode 100644 appl/lib/sort.b create mode 100644 appl/lib/spki/mkfile create mode 100644 appl/lib/spki/spki.b create mode 100644 appl/lib/spki/verifier.b create mode 100644 appl/lib/ssl.b create mode 100644 appl/lib/string.b create mode 100644 appl/lib/strinttab.b create mode 100644 appl/lib/strokes/buildstrokes.b create mode 100644 appl/lib/strokes/mkfile create mode 100644 appl/lib/strokes/readstrokes.b create mode 100644 appl/lib/strokes/strokes.b create mode 100644 appl/lib/strokes/writestrokes.b create mode 100644 appl/lib/styx.b create mode 100644 appl/lib/styxconv/mkfile create mode 100644 appl/lib/styxconv/ostyx.b create mode 100644 appl/lib/styxconv/ostyx.m create mode 100644 appl/lib/styxconv/osys.m create mode 100644 appl/lib/styxconv/styxconv.b create mode 100644 appl/lib/styxlib.b create mode 100644 appl/lib/styxpersist.b create mode 100644 appl/lib/styxservers.b create mode 100644 appl/lib/tables.b create mode 100644 appl/lib/tabs.b create mode 100644 appl/lib/tcl.m create mode 100644 appl/lib/tcl_calc.b create mode 100644 appl/lib/tcl_core.b create mode 100644 appl/lib/tcl_inthash.b create mode 100644 appl/lib/tcl_io.b create mode 100644 appl/lib/tcl_list.b create mode 100644 appl/lib/tcl_modhash.b create mode 100644 appl/lib/tcl_stack.b create mode 100644 appl/lib/tcl_strhash.b create mode 100644 appl/lib/tcl_string.b create mode 100644 appl/lib/tcl_symhash.b create mode 100644 appl/lib/tcl_tk.b create mode 100644 appl/lib/tcl_utils.b create mode 100644 appl/lib/tftp.b create mode 100644 appl/lib/timers.b create mode 100644 appl/lib/titlebar.b create mode 100644 appl/lib/tkclient.b create mode 100644 appl/lib/translate.b create mode 100644 appl/lib/ubfa.b create mode 100644 appl/lib/url.b create mode 100644 appl/lib/usb/mkfile create mode 100644 appl/lib/usb/usb.b create mode 100644 appl/lib/usb/usbmass.b create mode 100644 appl/lib/usb/usbmct.b create mode 100644 appl/lib/usb/usbmouse.b create mode 100644 appl/lib/utils.m create mode 100644 appl/lib/venti.b create mode 100644 appl/lib/virgil.b create mode 100644 appl/lib/volume.b create mode 100644 appl/lib/w3c/css.b create mode 100644 appl/lib/w3c/mkfile create mode 100644 appl/lib/w3c/xpointers.b create mode 100644 appl/lib/wait.b create mode 100644 appl/lib/watchvars.b create mode 100644 appl/lib/winplace.b create mode 100644 appl/lib/wmclient.b create mode 100644 appl/lib/wmlib.b create mode 100644 appl/lib/wmsrv.b create mode 100644 appl/lib/workdir.b create mode 100644 appl/lib/writegif.b create mode 100644 appl/lib/xml.b create mode 100644 appl/math/ack.b create mode 100644 appl/math/crackerbarrel.b create mode 100644 appl/math/doc.txt create mode 100644 appl/math/factor.b create mode 100644 appl/math/ffts.b create mode 100644 appl/math/fibonacci.b create mode 100644 appl/math/fit.b create mode 100644 appl/math/genprimes.b create mode 100644 appl/math/geodesy.b create mode 100644 appl/math/gr.b create mode 100644 appl/math/graph0.b create mode 100644 appl/math/hist0.b create mode 100644 appl/math/linalg.b create mode 100644 appl/math/linbench.b create mode 100644 appl/math/mersenne.b create mode 100644 appl/math/mkfile create mode 100644 appl/math/parts.b create mode 100644 appl/math/perms.b create mode 100644 appl/math/pi.b create mode 100644 appl/math/polyfill.b create mode 100644 appl/math/polyhedra.b create mode 100644 appl/math/powers.b create mode 100644 appl/math/primes.b create mode 100644 appl/math/sieve.b create mode 100644 appl/mkfile create mode 100644 appl/spree/archives.b create mode 100644 appl/spree/clients/bounce.b create mode 100644 appl/spree/clients/cards.b create mode 100644 appl/spree/clients/chat.b create mode 100644 appl/spree/clients/gather.b create mode 100644 appl/spree/clients/lobby.b create mode 100644 appl/spree/clients/othello.b create mode 100644 appl/spree/engines/afghan.b create mode 100644 appl/spree/engines/bounce.b create mode 100644 appl/spree/engines/canfield.b create mode 100644 appl/spree/engines/chat.b create mode 100644 appl/spree/engines/debug.b create mode 100644 appl/spree/engines/freecell.b create mode 100644 appl/spree/engines/gather.b create mode 100644 appl/spree/engines/hearts.b create mode 100644 appl/spree/engines/liars.b create mode 100644 appl/spree/engines/liars.y create mode 100644 appl/spree/engines/lobby.b create mode 100644 appl/spree/engines/othello.b create mode 100644 appl/spree/engines/racingdemon.b create mode 100644 appl/spree/engines/snap.b create mode 100644 appl/spree/engines/spider.b create mode 100644 appl/spree/engines/spit.b create mode 100644 appl/spree/engines/whist.b create mode 100644 appl/spree/gather.m create mode 100644 appl/spree/join.b create mode 100644 appl/spree/join.m create mode 100644 appl/spree/joinsession.b create mode 100644 appl/spree/joinsession.m create mode 100644 appl/spree/lib/allow.b create mode 100644 appl/spree/lib/allow.m create mode 100644 appl/spree/lib/base64.b create mode 100644 appl/spree/lib/base64.m create mode 100644 appl/spree/lib/cardlib.b create mode 100644 appl/spree/lib/cardlib.m create mode 100644 appl/spree/lib/commandline.b create mode 100644 appl/spree/lib/commandline.m create mode 100644 appl/spree/lib/objstore.b create mode 100644 appl/spree/lib/objstore.m create mode 100644 appl/spree/lib/testsets.b create mode 100644 appl/spree/lib/tricks.b create mode 100644 appl/spree/lib/tricks.m create mode 100644 appl/spree/man/gamesrv.man2 create mode 100644 appl/spree/man/gamesrv.man4 create mode 100644 appl/spree/man/styxservers-nametree.man2 create mode 100644 appl/spree/man/styxservers.man2 create mode 100644 appl/spree/mkfile create mode 100644 appl/spree/other/tst.b create mode 100644 appl/spree/other/tstboing.b create mode 100755 appl/spree/other/tstlines.sh create mode 100644 appl/spree/other/tstwin.b create mode 100644 appl/spree/spree.b create mode 100644 appl/spree/spree.m create mode 100644 appl/svc/auth.sh create mode 100644 appl/svc/httpd/alarms.b create mode 100644 appl/svc/httpd/alarms.m create mode 100644 appl/svc/httpd/cache.b create mode 100644 appl/svc/httpd/cache.m create mode 100644 appl/svc/httpd/cgiparse.b create mode 100644 appl/svc/httpd/cgiparse.m create mode 100644 appl/svc/httpd/contents.b create mode 100644 appl/svc/httpd/contents.m create mode 100644 appl/svc/httpd/date.b create mode 100644 appl/svc/httpd/date.m create mode 100644 appl/svc/httpd/echo.b create mode 100644 appl/svc/httpd/httpd.b create mode 100644 appl/svc/httpd/httpd.debug create mode 100644 appl/svc/httpd/httpd.log create mode 100644 appl/svc/httpd/httpd.m create mode 100644 appl/svc/httpd/httpd.rewrite create mode 100644 appl/svc/httpd/httpd.suff create mode 100644 appl/svc/httpd/imagemap.b create mode 100644 appl/svc/httpd/mkfile create mode 100644 appl/svc/httpd/parser.b create mode 100644 appl/svc/httpd/parser.m create mode 100644 appl/svc/httpd/redirect.b create mode 100644 appl/svc/httpd/redirect.m create mode 100644 appl/svc/httpd/stats.b create mode 100644 appl/svc/mkfile create mode 100644 appl/svc/net.sh create mode 100644 appl/svc/registry.sh create mode 100644 appl/svc/rstyx.sh create mode 100644 appl/svc/styx.sh create mode 100644 appl/svc/webget/date.b create mode 100644 appl/svc/webget/date.m create mode 100644 appl/svc/webget/file.b create mode 100644 appl/svc/webget/ftp.b create mode 100644 appl/svc/webget/http.b create mode 100644 appl/svc/webget/image2enc.b create mode 100644 appl/svc/webget/image2enc.m create mode 100644 appl/svc/webget/message.b create mode 100644 appl/svc/webget/message.m create mode 100644 appl/svc/webget/mkfile create mode 100644 appl/svc/webget/transport.m create mode 100644 appl/svc/webget/webget.b create mode 100644 appl/svc/webget/webget.log create mode 100644 appl/svc/webget/wgutils.b create mode 100644 appl/svc/webget/wgutils.m create mode 100644 appl/tiny/mkfile create mode 100644 appl/tiny/rm.b create mode 100644 appl/tiny/sh.b create mode 100644 appl/wm/about.b create mode 100644 appl/wm/avi.b create mode 100644 appl/wm/bounce.b create mode 100644 appl/wm/brutus.b create mode 100644 appl/wm/brutus/excerpt.b create mode 100644 appl/wm/brutus/image.b create mode 100644 appl/wm/brutus/mkfile create mode 100644 appl/wm/brutus/mod.b create mode 100644 appl/wm/brutus/table.b create mode 100644 appl/wm/c4.b create mode 100644 appl/wm/calendar.b create mode 100644 appl/wm/clock.b create mode 100644 appl/wm/coffee.b create mode 100644 appl/wm/collide.b create mode 100644 appl/wm/colors.b create mode 100644 appl/wm/cprof.b create mode 100644 appl/wm/date.b create mode 100644 appl/wm/deb.b create mode 100644 appl/wm/debdata.b create mode 100644 appl/wm/debsrc.b create mode 100644 appl/wm/dir.b create mode 100644 appl/wm/drawmux/dmview.b create mode 100644 appl/wm/drawmux/dmwm.b create mode 100644 appl/wm/drawmux/drawmux.b create mode 100644 appl/wm/drawmux/drawmux.m create mode 100644 appl/wm/drawmux/drawoffs.m create mode 100644 appl/wm/drawmux/mkfile create mode 100644 appl/wm/edit.b create mode 100644 appl/wm/filename.b create mode 100644 appl/wm/ftree/cptree.b create mode 100644 appl/wm/ftree/cptree.m create mode 100644 appl/wm/ftree/ftree.b create mode 100644 appl/wm/ftree/items.b create mode 100644 appl/wm/ftree/items.m create mode 100644 appl/wm/ftree/mkfile create mode 100644 appl/wm/ftree/wmsetup create mode 100644 appl/wm/getauthinfo.b create mode 100644 appl/wm/hebrew.m create mode 100644 appl/wm/keyboard.b create mode 100644 appl/wm/logon.b create mode 100644 appl/wm/logwindow.b create mode 100644 appl/wm/man.b create mode 100644 appl/wm/mand.b create mode 100644 appl/wm/mash.b create mode 100644 appl/wm/memory.b create mode 100644 appl/wm/minitel/README create mode 100644 appl/wm/minitel/event.b create mode 100644 appl/wm/minitel/event.m create mode 100644 appl/wm/minitel/keyb.b create mode 100644 appl/wm/minitel/mdisplay.b create mode 100644 appl/wm/minitel/mdisplay.dis create mode 100644 appl/wm/minitel/mdisplay.m create mode 100644 appl/wm/minitel/mdisplay.sbl create mode 100644 appl/wm/minitel/miniterm.b create mode 100644 appl/wm/minitel/miniterm.dis create mode 100644 appl/wm/minitel/miniterm.m create mode 100644 appl/wm/minitel/miniterm.sbl create mode 100644 appl/wm/minitel/mkfile create mode 100644 appl/wm/minitel/modem.b create mode 100644 appl/wm/minitel/screen.b create mode 100644 appl/wm/minitel/socket.b create mode 100644 appl/wm/minitel/swkeyb.b create mode 100644 appl/wm/minitel/swkeyb.dis create mode 100644 appl/wm/minitel/swkeyb.m create mode 100644 appl/wm/minitel/swkeyb.sbl create mode 100644 appl/wm/mkfile create mode 100644 appl/wm/mpeg.b create mode 100644 appl/wm/mpeg/c0.tab create mode 100644 appl/wm/mpeg/c0.vlc create mode 100644 appl/wm/mpeg/c1.tab create mode 100644 appl/wm/mpeg/c1.vlc create mode 100644 appl/wm/mpeg/c2.tab create mode 100644 appl/wm/mpeg/c2.vlc create mode 100644 appl/wm/mpeg/c3.tab create mode 100644 appl/wm/mpeg/c3.vlc create mode 100644 appl/wm/mpeg/c4.tab create mode 100644 appl/wm/mpeg/c4.vlc create mode 100644 appl/wm/mpeg/c5.tab create mode 100644 appl/wm/mpeg/c5.vlc create mode 100644 appl/wm/mpeg/c6.tab create mode 100644 appl/wm/mpeg/c6.vlc create mode 100644 appl/wm/mpeg/c7.tab create mode 100644 appl/wm/mpeg/c7.vlc create mode 100644 appl/wm/mpeg/cbp.tab create mode 100644 appl/wm/mpeg/cbp.vlc create mode 100644 appl/wm/mpeg/cdc.tab create mode 100644 appl/wm/mpeg/cdc.vlc create mode 100644 appl/wm/mpeg/closest.m create mode 100644 appl/wm/mpeg/decode.b create mode 100644 appl/wm/mpeg/decode4.b create mode 100644 appl/wm/mpeg/fixidct.b create mode 100644 appl/wm/mpeg/fltidct.b create mode 100644 appl/wm/mpeg/mai.tab create mode 100644 appl/wm/mpeg/mai.vlc create mode 100644 appl/wm/mpeg/makergbvmap.b create mode 100644 appl/wm/mpeg/maketables create mode 100644 appl/wm/mpeg/mbb.tab create mode 100644 appl/wm/mpeg/mbb.vlc create mode 100644 appl/wm/mpeg/mbi.tab create mode 100644 appl/wm/mpeg/mbi.vlc create mode 100644 appl/wm/mpeg/mbp.tab create mode 100644 appl/wm/mpeg/mbp.vlc create mode 100644 appl/wm/mpeg/mkfile create mode 100644 appl/wm/mpeg/motion.tab create mode 100644 appl/wm/mpeg/motion.vlc create mode 100644 appl/wm/mpeg/mpeg.b create mode 100644 appl/wm/mpeg/mpegio.b create mode 100644 appl/wm/mpeg/mpegio.m create mode 100644 appl/wm/mpeg/refidct.b create mode 100644 appl/wm/mpeg/remap.b create mode 100644 appl/wm/mpeg/remap1.b create mode 100644 appl/wm/mpeg/remap2.b create mode 100644 appl/wm/mpeg/remap24.b create mode 100644 appl/wm/mpeg/remap4.b create mode 100644 appl/wm/mpeg/remap8.b create mode 100644 appl/wm/mpeg/rgbvmap.m create mode 100644 appl/wm/mpeg/rl0f.tab create mode 100644 appl/wm/mpeg/rl0f.vlc create mode 100644 appl/wm/mpeg/rl0n.tab create mode 100644 appl/wm/mpeg/rl0n.vlc create mode 100644 appl/wm/mpeg/scidct.b create mode 100644 appl/wm/mpeg/vlc.b create mode 100644 appl/wm/mpeg/ydc.tab create mode 100644 appl/wm/mpeg/ydc.vlc create mode 100644 appl/wm/mprof.b create mode 100644 appl/wm/pen.b create mode 100644 appl/wm/polyhedra.b create mode 100644 appl/wm/prof.b create mode 100644 appl/wm/qt.b create mode 100644 appl/wm/readmail.b create mode 100644 appl/wm/remotelogon.b create mode 100644 appl/wm/reversi.b create mode 100644 appl/wm/rmtdir.b create mode 100644 appl/wm/rt.b create mode 100644 appl/wm/sam.b create mode 100644 appl/wm/samstub.b create mode 100644 appl/wm/samstub.m create mode 100644 appl/wm/samterm.m create mode 100644 appl/wm/samtk.b create mode 100644 appl/wm/samtk.m create mode 100644 appl/wm/sendmail.b create mode 100644 appl/wm/sh.b create mode 100644 appl/wm/smenu.b create mode 100644 appl/wm/smenu.m create mode 100644 appl/wm/snake.b create mode 100644 appl/wm/stopwatch.b create mode 100644 appl/wm/sweeper.b create mode 100644 appl/wm/task.b create mode 100644 appl/wm/telnet.b create mode 100644 appl/wm/tetris.b create mode 100644 appl/wm/toolbar.b create mode 100644 appl/wm/unibrowse.b create mode 100644 appl/wm/view.b create mode 100644 appl/wm/vt.b create mode 100644 appl/wm/wish.b create mode 100644 appl/wm/wm.b create mode 100644 appl/wm/wmdeb.m create mode 100644 appl/wm/wmplay.b create mode 100644 asm/NOTICE create mode 100644 asm/asm.h create mode 100644 asm/asm.y create mode 100644 asm/assem.c create mode 100644 asm/lex.c create mode 100644 asm/mkfile create mode 100644 emu/FreeBSD/asm-386.S create mode 100644 emu/FreeBSD/audio.c create mode 100644 emu/FreeBSD/cmd.c create mode 100644 emu/FreeBSD/deveia.c create mode 100644 emu/FreeBSD/devfs.c create mode 100644 emu/FreeBSD/emu create mode 100644 emu/FreeBSD/ipif.c create mode 100644 emu/FreeBSD/mkfile create mode 100644 emu/FreeBSD/mkfile-FreeBSD create mode 100644 emu/FreeBSD/os.c create mode 100644 emu/Hp/NOTE create mode 100644 emu/Hp/asm-s800.s create mode 100644 emu/Hp/cmd.c create mode 100644 emu/Hp/devaudio.c create mode 100644 emu/Hp/devfs.c create mode 100644 emu/Hp/emu create mode 100644 emu/Hp/mkfile create mode 100644 emu/Hp/mkfile-Hp create mode 100644 emu/Hp/os.c create mode 100644 emu/Irix/NOTE create mode 100644 emu/Irix/asm-mips.s create mode 100644 emu/Irix/cmd.c create mode 100644 emu/Irix/devfs.c create mode 100644 emu/Irix/emu create mode 100644 emu/Irix/mkfile create mode 100644 emu/Irix/mkfile-Irix create mode 100644 emu/Irix/os.c create mode 100644 emu/Linux/asm-386.S create mode 100644 emu/Linux/cmd.c create mode 100644 emu/Linux/deveia.c create mode 100644 emu/Linux/devfs.c create mode 100644 emu/Linux/emu create mode 100644 emu/Linux/mkfile create mode 100644 emu/Linux/os.c create mode 100644 emu/MacOSX/NOTE create mode 100644 emu/MacOSX/NOTICE create mode 100644 emu/MacOSX/asm-power.s create mode 100644 emu/MacOSX/cmd.c create mode 100644 emu/MacOSX/deveia.c create mode 100644 emu/MacOSX/devfs.c create mode 100644 emu/MacOSX/emu create mode 100644 emu/MacOSX/emu-g create mode 100644 emu/MacOSX/ipif.c create mode 100644 emu/MacOSX/mkfile create mode 100644 emu/MacOSX/mkfile-g create mode 100644 emu/MacOSX/os.c create mode 100644 emu/NOTICE create mode 100644 emu/Nt/audio.c create mode 100644 emu/Nt/cmd.c create mode 100644 emu/Nt/devarch.c create mode 100644 emu/Nt/deveia.c create mode 100644 emu/Nt/devfs.c create mode 100644 emu/Nt/emu create mode 100644 emu/Nt/fp.c create mode 100644 emu/Nt/ie create mode 100644 emu/Nt/ie-os.c create mode 100644 emu/Nt/ie-win.c create mode 100644 emu/Nt/ieplugin.h create mode 100644 emu/Nt/ipif.c create mode 100644 emu/Nt/mkfile create mode 100644 emu/Nt/nt.rc create mode 100644 emu/Nt/os.c create mode 100644 emu/Nt/vlrt.c create mode 100644 emu/Nt/win.c create mode 100644 emu/Plan9/asm-386.s create mode 100644 emu/Plan9/asm-mips.s create mode 100644 emu/Plan9/asm-power.s create mode 100644 emu/Plan9/asm-sparc.s create mode 100644 emu/Plan9/cmd.c create mode 100644 emu/Plan9/devfs.c create mode 100644 emu/Plan9/devsrv9.c create mode 100644 emu/Plan9/emu create mode 100644 emu/Plan9/emusig create mode 100644 emu/Plan9/mkfile create mode 100644 emu/Plan9/os.c create mode 100644 emu/Plan9/win.c create mode 100644 emu/Solaris/asm-386.s create mode 100644 emu/Solaris/asm-sparc.s create mode 100644 emu/Solaris/audio.c create mode 100644 emu/Solaris/cmd.c create mode 100644 emu/Solaris/deveia.c create mode 100644 emu/Solaris/devfs.c create mode 100644 emu/Solaris/emu create mode 100644 emu/Solaris/mkfile create mode 100644 emu/Solaris/os.c create mode 100644 emu/Unixware/asm-386.s create mode 100644 emu/Unixware/cmd.c create mode 100644 emu/Unixware/deveia.c create mode 100644 emu/Unixware/devfs.c create mode 100644 emu/Unixware/emu create mode 100644 emu/Unixware/ipif.c create mode 100644 emu/Unixware/mkfile create mode 100644 emu/Unixware/mkfile-Unixware create mode 100644 emu/Unixware/os.c create mode 100644 emu/mkfile create mode 100644 emu/port/acme-offset create mode 100644 emu/port/alloc.c create mode 100644 emu/port/audio-tbls.c create mode 100644 emu/port/audio.h create mode 100644 emu/port/cache.c create mode 100644 emu/port/chan.c create mode 100644 emu/port/dat.h create mode 100644 emu/port/dev.c create mode 100644 emu/port/devaudio.c create mode 100644 emu/port/devcap.c create mode 100644 emu/port/devcmd.c create mode 100644 emu/port/devcons.c create mode 100644 emu/port/devconsx.c create mode 100644 emu/port/devdraw.c create mode 100644 emu/port/devdup.c create mode 100644 emu/port/devdynld.c create mode 100644 emu/port/devdynldx.c create mode 100644 emu/port/deveia-bsd.c create mode 100644 emu/port/deveia-posix.c create mode 100644 emu/port/devenv.c create mode 100644 emu/port/devfs-posix.c create mode 100644 emu/port/devindir.c create mode 100644 emu/port/devip.c create mode 100755 emu/port/devlogfs.c create mode 100644 emu/port/devmem.c create mode 100644 emu/port/devmnt.c create mode 100644 emu/port/devpipe.c create mode 100644 emu/port/devpointer.c create mode 100644 emu/port/devprof.c create mode 100644 emu/port/devprog.c create mode 100644 emu/port/devroot.c create mode 100644 emu/port/devsign.c create mode 100644 emu/port/devsnarf.c create mode 100644 emu/port/devsrv.c create mode 100644 emu/port/devssl.c create mode 100644 emu/port/devtinyfs.c create mode 100644 emu/port/devtk.c create mode 100644 emu/port/dial.c create mode 100644 emu/port/dis.c create mode 100644 emu/port/discall.c create mode 100644 emu/port/dynld.c create mode 100644 emu/port/dynldc.c create mode 100644 emu/port/env.c create mode 100644 emu/port/error.c create mode 100644 emu/port/error.h create mode 100644 emu/port/errstr.c create mode 100644 emu/port/exception.c create mode 100644 emu/port/exportfs.c create mode 100644 emu/port/exptab.c create mode 100644 emu/port/file.c create mode 100644 emu/port/fns.h create mode 100644 emu/port/inferno.c create mode 100644 emu/port/ip.h create mode 100644 emu/port/ipaux.c create mode 100644 emu/port/ipif-posix.c create mode 100644 emu/port/keysym2ucs.h create mode 100644 emu/port/latin1.c create mode 100644 emu/port/latin1.h create mode 100644 emu/port/lock.c create mode 100644 emu/port/main.c create mode 100644 emu/port/mkdevc create mode 100644 emu/port/mkdevlist create mode 100644 emu/port/mkroot create mode 100644 emu/port/parse.c create mode 100644 emu/port/pgrp.c create mode 100644 emu/port/portmkfile create mode 100644 emu/port/print.c create mode 100644 emu/port/proc.c create mode 100644 emu/port/qio.c create mode 100644 emu/port/random.c create mode 100644 emu/port/srv.c create mode 100644 emu/port/styx.c create mode 100644 emu/port/sysfile.c create mode 100644 emu/port/uqid.c create mode 100644 emu/port/win-x11-old.c create mode 100644 emu/port/win-x11a.c create mode 100644 emu/port/x11-keysym2ucs.c create mode 100644 include/NOTICE create mode 100644 include/a.out.h create mode 100644 include/bio.h create mode 100644 include/cursor.h create mode 100644 include/draw.h create mode 100644 include/drawif.h create mode 100644 include/dynld.h create mode 100644 include/fcall.h create mode 100644 include/flate.h create mode 100644 include/freetype.h create mode 100644 include/freetype/cache/ftccache.h create mode 100644 include/freetype/cache/ftccmap.h create mode 100644 include/freetype/cache/ftcglyph.h create mode 100644 include/freetype/cache/ftcimage.h create mode 100644 include/freetype/cache/ftcmanag.h create mode 100644 include/freetype/cache/ftcsbits.h create mode 100644 include/freetype/cache/ftlru.h create mode 100644 include/freetype/config/ftconfig.h create mode 100644 include/freetype/config/ftconfig.h.orig create mode 100644 include/freetype/config/ftheader.h create mode 100644 include/freetype/config/ftmodule.h create mode 100644 include/freetype/config/ftmodule.h.orig create mode 100644 include/freetype/config/ftoption.h create mode 100644 include/freetype/config/ftstdlib.h create mode 100644 include/freetype/config/ftstdlib.h.orig create mode 100644 include/freetype/freetype.h create mode 100644 include/freetype/ft2build.h create mode 100644 include/freetype/ftbbox.h create mode 100644 include/freetype/ftbdf.h create mode 100644 include/freetype/ftcache.h create mode 100644 include/freetype/ftchapters.h create mode 100644 include/freetype/fterrdef.h create mode 100644 include/freetype/fterrors.h create mode 100644 include/freetype/ftglyph.h create mode 100644 include/freetype/ftgzip.h create mode 100644 include/freetype/ftimage.h create mode 100644 include/freetype/ftincrem.h create mode 100644 include/freetype/ftlist.h create mode 100644 include/freetype/ftmac.h create mode 100644 include/freetype/ftmm.h create mode 100644 include/freetype/ftmoderr.h create mode 100644 include/freetype/ftmodule.h create mode 100644 include/freetype/ftoutln.h create mode 100644 include/freetype/ftpfr.h create mode 100644 include/freetype/ftrender.h create mode 100644 include/freetype/ftsizes.h create mode 100644 include/freetype/ftsnames.h create mode 100644 include/freetype/ftstroker.h create mode 100644 include/freetype/ftsynth.h create mode 100644 include/freetype/ftsysio.h create mode 100644 include/freetype/ftsysmem.h create mode 100644 include/freetype/ftsystem.h create mode 100644 include/freetype/fttrigon.h create mode 100644 include/freetype/fttypes.h create mode 100644 include/freetype/ftxf86.h create mode 100644 include/freetype/internal/autohint.h create mode 100644 include/freetype/internal/bdftypes.h create mode 100644 include/freetype/internal/cfftypes.h create mode 100644 include/freetype/internal/fnttypes.h create mode 100644 include/freetype/internal/ftcalc.h create mode 100644 include/freetype/internal/ftcore.h create mode 100644 include/freetype/internal/ftdebug.h create mode 100644 include/freetype/internal/ftdriver.h create mode 100644 include/freetype/internal/ftexcept.h create mode 100644 include/freetype/internal/ftgloadr.h create mode 100644 include/freetype/internal/fthash.h create mode 100644 include/freetype/internal/ftmemory.h create mode 100644 include/freetype/internal/ftobject.h create mode 100644 include/freetype/internal/ftobjs.h create mode 100644 include/freetype/internal/ftstream.h create mode 100644 include/freetype/internal/fttrace.h create mode 100644 include/freetype/internal/internal.h create mode 100644 include/freetype/internal/pcftypes.h create mode 100644 include/freetype/internal/pfr.h create mode 100644 include/freetype/internal/psaux.h create mode 100644 include/freetype/internal/pshints.h create mode 100644 include/freetype/internal/psnames.h create mode 100644 include/freetype/internal/sfnt.h create mode 100644 include/freetype/internal/t1types.h create mode 100644 include/freetype/internal/t42types.h create mode 100644 include/freetype/internal/tttypes.h create mode 100644 include/freetype/t1tables.h create mode 100644 include/freetype/ttnameid.h create mode 100644 include/freetype/tttables.h create mode 100644 include/freetype/tttags.h create mode 100644 include/interp.h create mode 100644 include/isa.h create mode 100644 include/kern.h create mode 100644 include/kernel.h create mode 100644 include/keyboard.h create mode 100644 include/libcrypt_o.h create mode 100644 include/libsec.h create mode 100755 include/logfs.h create mode 100644 include/mathi.h create mode 100644 include/memdraw.h create mode 100644 include/memlayer.h create mode 100644 include/mp.h create mode 100644 include/nandecc.h create mode 100644 include/nandfs.h create mode 100644 include/pool.h create mode 100644 include/pooldefs.h create mode 100644 include/prefab.h create mode 100644 include/raise.h create mode 100644 include/rdbg.h create mode 100644 include/styx.h create mode 100644 include/tk.h create mode 100644 include/trace.h create mode 100644 include/version.h create mode 100644 include/vm.h create mode 100644 lib9/NOTICE create mode 100644 lib9/argv0.c create mode 100644 lib9/charstod.c create mode 100644 lib9/cistrcmp.c create mode 100644 lib9/cistrncmp.c create mode 100644 lib9/cistrstr.c create mode 100644 lib9/cleanname.c create mode 100644 lib9/convD2M.c create mode 100644 lib9/convM2D.c create mode 100644 lib9/convM2S.c create mode 100644 lib9/convS2M.c create mode 100644 lib9/create.c create mode 100644 lib9/dirstat-Nt.c create mode 100644 lib9/dirstat-posix.c create mode 100644 lib9/dirwstat.c create mode 100644 lib9/dofmt.c create mode 100644 lib9/dorfmt.c create mode 100644 lib9/errfmt.c create mode 100644 lib9/errstr-Nt.c create mode 100644 lib9/errstr-Plan9.c create mode 100644 lib9/errstr-posix.c create mode 100644 lib9/exits.c create mode 100644 lib9/fcallfmt.c create mode 100644 lib9/fltfmt.c create mode 100644 lib9/fmt.c create mode 100644 lib9/fmtdef.h create mode 100644 lib9/fmtfd.c create mode 100644 lib9/fmtlock.c create mode 100644 lib9/fmtprint.c create mode 100644 lib9/fmtquote.c create mode 100644 lib9/fmtrune.c create mode 100644 lib9/fmtstr.c create mode 100644 lib9/fmtvprint.c create mode 100644 lib9/fprint.c create mode 100644 lib9/getcallerpc-FreeBSD-386.S create mode 100644 lib9/getcallerpc-Hp-s800.s create mode 100644 lib9/getcallerpc-Irix-mips.s create mode 100644 lib9/getcallerpc-Linux-386.S create mode 100644 lib9/getcallerpc-MacOSX-power.s create mode 100644 lib9/getcallerpc-Solaris-386.s create mode 100644 lib9/getcallerpc-Solaris-sparc.s create mode 100644 lib9/getcallerpc-Unixware-386.s create mode 100644 lib9/getfields.c create mode 100644 lib9/getuser-Nt.c create mode 100644 lib9/getuser-posix.c create mode 100644 lib9/getwd-Nt.c create mode 100644 lib9/getwd-posix.c create mode 100644 lib9/lock-Hp-s800.s create mode 100644 lib9/lock-Irix-mips.s create mode 100644 lib9/lock-MacOSX-power.s create mode 100644 lib9/lock-Nt-386.c create mode 100644 lib9/lock-Solaris-386.s create mode 100644 lib9/lock-Solaris-sparc.s create mode 100644 lib9/lock-Unixware-386.s create mode 100644 lib9/lock.c create mode 100644 lib9/mkfile create mode 100644 lib9/mkfile-Nt create mode 100644 lib9/mkfile-Plan9 create mode 100644 lib9/mkfile-Posix create mode 100644 lib9/nulldir.c create mode 100644 lib9/pow10.c create mode 100644 lib9/print.c create mode 100644 lib9/qsort.c create mode 100644 lib9/readn.c create mode 100644 lib9/rerrstr.c create mode 100644 lib9/rune.c create mode 100644 lib9/runestrlen.c create mode 100644 lib9/sbrk-posix.c create mode 100644 lib9/seek.c create mode 100644 lib9/seprint.c create mode 100644 lib9/setbinmode-Nt.c create mode 100644 lib9/smprint.c create mode 100644 lib9/snprint.c create mode 100644 lib9/sprint.c create mode 100644 lib9/strdup.c create mode 100644 lib9/strecpy.c create mode 100644 lib9/strtoll.c create mode 100644 lib9/sysfatal.c create mode 100644 lib9/tokenize.c create mode 100644 lib9/u16.c create mode 100644 lib9/u32.c create mode 100644 lib9/u64.c create mode 100644 lib9/utfecpy.c create mode 100644 lib9/utflen.c create mode 100644 lib9/utfnlen.c create mode 100644 lib9/utfrrune.c create mode 100644 lib9/utfrune.c create mode 100644 lib9/vfprint.c create mode 100644 lib9/vseprint.c create mode 100644 lib9/vsmprint.c create mode 100644 lib9/vsnprint.c create mode 100644 libbio/NOTICE create mode 100644 libbio/bbuffered.c create mode 100644 libbio/bfildes.c create mode 100644 libbio/bflush.c create mode 100644 libbio/bgetc.c create mode 100644 libbio/bgetd.c create mode 100644 libbio/bgetrune.c create mode 100644 libbio/binit.c create mode 100644 libbio/boffset.c create mode 100644 libbio/bprint.c create mode 100644 libbio/bputc.c create mode 100644 libbio/bputrune.c create mode 100644 libbio/brdline.c create mode 100644 libbio/bread.c create mode 100644 libbio/bseek.c create mode 100644 libbio/bwrite.c create mode 100644 libbio/mkfile create mode 100644 libdraw/NOTICE create mode 100644 libdraw/alloc.c create mode 100644 libdraw/allocimagemix.c create mode 100644 libdraw/arith.c create mode 100644 libdraw/bezier.c create mode 100644 libdraw/border.c create mode 100644 libdraw/buildfont.c create mode 100644 libdraw/bytesperline.c create mode 100644 libdraw/chan.c create mode 100644 libdraw/cloadimage.c create mode 100644 libdraw/computil.c create mode 100644 libdraw/creadimage.c create mode 100644 libdraw/defont.c create mode 100644 libdraw/draw.c create mode 100644 libdraw/drawrepl.c create mode 100644 libdraw/ellipse.c create mode 100644 libdraw/font.c create mode 100644 libdraw/freesubfont.c create mode 100644 libdraw/getdefont.c create mode 100644 libdraw/getsubfont.c create mode 100644 libdraw/init.c create mode 100644 libdraw/line.c create mode 100644 libdraw/loadimage.c create mode 100644 libdraw/mkfile create mode 100644 libdraw/mkfont.c create mode 100644 libdraw/openfont.c create mode 100644 libdraw/poly.c create mode 100644 libdraw/readcolmap.c create mode 100644 libdraw/readimage.c create mode 100644 libdraw/readsubfont.c create mode 100644 libdraw/rectclip.c create mode 100644 libdraw/replclipr.c create mode 100644 libdraw/rgb.c create mode 100644 libdraw/string.c create mode 100644 libdraw/stringbg.c create mode 100644 libdraw/stringsubfont.c create mode 100644 libdraw/stringwidth.c create mode 100644 libdraw/subfont.c create mode 100644 libdraw/subfontcache.c create mode 100644 libdraw/subfontname.c create mode 100644 libdraw/test.c create mode 100644 libdraw/unloadimage.c create mode 100644 libdraw/window.c create mode 100644 libdraw/writecolmap.c create mode 100644 libdraw/writeimage.c create mode 100644 libdraw/writesubfont.c create mode 100644 libdynld/NOTICE create mode 100644 libdynld/dynld-386.c create mode 100644 libdynld/dynld-68000.c create mode 100644 libdynld/dynld-arm.c create mode 100644 libdynld/dynld-mips.c create mode 100644 libdynld/dynld-power.c create mode 100644 libdynld/dynld-sparc.c create mode 100644 libdynld/dynld.c create mode 100644 libdynld/dynloadfd.c create mode 100644 libdynld/mkfile create mode 100755 libfreetype/NOTICE/FTL.txt create mode 100755 libfreetype/NOTICE/GPL.txt create mode 100755 libfreetype/NOTICE/PATENTS create mode 100755 libfreetype/NOTICE/license.txt create mode 100644 libfreetype/adler32.c create mode 100644 libfreetype/ahangles.c create mode 100644 libfreetype/ahangles.h create mode 100644 libfreetype/aherrors.h create mode 100644 libfreetype/ahglobal.c create mode 100644 libfreetype/ahglobal.h create mode 100644 libfreetype/ahglyph.c create mode 100644 libfreetype/ahglyph.h create mode 100644 libfreetype/ahhint.c create mode 100644 libfreetype/ahhint.h create mode 100644 libfreetype/ahloader.h create mode 100644 libfreetype/ahmodule.c create mode 100644 libfreetype/ahmodule.h create mode 100644 libfreetype/ahoptim.c create mode 100644 libfreetype/ahoptim.h create mode 100644 libfreetype/ahtypes.h create mode 100644 libfreetype/autohint.c create mode 100644 libfreetype/bdf.c create mode 100644 libfreetype/bdf.h create mode 100644 libfreetype/bdfdrivr.c create mode 100644 libfreetype/bdfdrivr.h create mode 100644 libfreetype/bdferror.h create mode 100644 libfreetype/bdflib.c create mode 100644 libfreetype/cff.c create mode 100644 libfreetype/cffcmap.c create mode 100644 libfreetype/cffcmap.h create mode 100644 libfreetype/cffdrivr.c create mode 100644 libfreetype/cffdrivr.h create mode 100644 libfreetype/cfferrs.h create mode 100644 libfreetype/cffgload.c create mode 100644 libfreetype/cffgload.h create mode 100644 libfreetype/cffload.c create mode 100644 libfreetype/cffload.h create mode 100644 libfreetype/cffobjs.c create mode 100644 libfreetype/cffobjs.h create mode 100644 libfreetype/cffparse.c create mode 100644 libfreetype/cffparse.h create mode 100644 libfreetype/cfftoken.h create mode 100644 libfreetype/ciderrs.h create mode 100644 libfreetype/cidgload.c create mode 100644 libfreetype/cidgload.h create mode 100644 libfreetype/cidload.c create mode 100644 libfreetype/cidload.h create mode 100644 libfreetype/cidobjs.c create mode 100644 libfreetype/cidobjs.h create mode 100644 libfreetype/cidparse.c create mode 100644 libfreetype/cidparse.h create mode 100644 libfreetype/cidriver.c create mode 100644 libfreetype/cidriver.h create mode 100644 libfreetype/cidtoken.h create mode 100644 libfreetype/fnterrs.h create mode 100644 libfreetype/freetype.c create mode 100644 libfreetype/ft2system.c create mode 100644 libfreetype/ftapi.c create mode 100644 libfreetype/ftbase.c create mode 100644 libfreetype/ftbbox.c create mode 100644 libfreetype/ftbdf.c create mode 100644 libfreetype/ftcache.c create mode 100644 libfreetype/ftcalc.c create mode 100644 libfreetype/ftccache.c create mode 100644 libfreetype/ftccmap.c create mode 100644 libfreetype/ftcerror.h create mode 100644 libfreetype/ftcglyph.c create mode 100644 libfreetype/ftcimage.c create mode 100644 libfreetype/ftcmanag.c create mode 100644 libfreetype/ftcsbits.c create mode 100644 libfreetype/ftdbgmem.c create mode 100644 libfreetype/ftdebug.c create mode 100644 libfreetype/ftexcept.c create mode 100644 libfreetype/ftgloadr.c create mode 100644 libfreetype/ftglyph.c create mode 100644 libfreetype/ftgrays.c create mode 100644 libfreetype/ftgrays.h create mode 100644 libfreetype/ftgzip.c create mode 100644 libfreetype/fthash.c create mode 100644 libfreetype/ftinit.c create mode 100644 libfreetype/ftlist.c create mode 100644 libfreetype/ftlru.c create mode 100644 libfreetype/ftmac.c create mode 100644 libfreetype/ftmm.c create mode 100644 libfreetype/ftnames.c create mode 100644 libfreetype/ftobject.c create mode 100644 libfreetype/ftobjs.c create mode 100644 libfreetype/ftoutln.c create mode 100644 libfreetype/ftpfr.c create mode 100644 libfreetype/ftraster.c create mode 100644 libfreetype/ftraster.h create mode 100644 libfreetype/ftrend1.c create mode 100644 libfreetype/ftrend1.h create mode 100644 libfreetype/ftsmerrs.h create mode 100644 libfreetype/ftsmooth.c create mode 100644 libfreetype/ftsmooth.h create mode 100644 libfreetype/ftstream.c create mode 100644 libfreetype/ftstroker.c create mode 100644 libfreetype/ftsynth.c create mode 100644 libfreetype/ftsysio.c create mode 100644 libfreetype/ftsysmem.c create mode 100644 libfreetype/ftsystem.c create mode 100644 libfreetype/ftsystem_inf.c create mode 100644 libfreetype/fttrigon.c create mode 100644 libfreetype/fttype1.c create mode 100644 libfreetype/ftutil.c create mode 100644 libfreetype/ftxf86.c create mode 100644 libfreetype/infblock.c create mode 100644 libfreetype/infblock.h create mode 100644 libfreetype/infcodes.c create mode 100644 libfreetype/infcodes.h create mode 100644 libfreetype/inffixed.h create mode 100644 libfreetype/inflate.c create mode 100644 libfreetype/inftrees.c create mode 100644 libfreetype/inftrees.h create mode 100644 libfreetype/infutil.c create mode 100644 libfreetype/infutil.h create mode 100644 libfreetype/mkfile create mode 100644 libfreetype/otlayout.h create mode 100644 libfreetype/otlbase.c create mode 100644 libfreetype/otlbase.h create mode 100644 libfreetype/otlcommn.c create mode 100644 libfreetype/otlcommn.h create mode 100644 libfreetype/otlconf.h create mode 100644 libfreetype/otlgdef.c create mode 100644 libfreetype/otlgdef.h create mode 100644 libfreetype/otlgpos.c create mode 100644 libfreetype/otlgpos.h create mode 100644 libfreetype/otlgsub.c create mode 100644 libfreetype/otlgsub.h create mode 100644 libfreetype/otljstf.c create mode 100644 libfreetype/otljstf.h create mode 100644 libfreetype/otlparse.c create mode 100644 libfreetype/otlparse.h create mode 100644 libfreetype/otltable.h create mode 100644 libfreetype/otltags.h create mode 100644 libfreetype/otlutils.h create mode 100644 libfreetype/pcf.c create mode 100644 libfreetype/pcf.h create mode 100644 libfreetype/pcfdriver.c create mode 100644 libfreetype/pcfdriver.h create mode 100644 libfreetype/pcferror.h create mode 100644 libfreetype/pcfread.c create mode 100644 libfreetype/pcfutil.c create mode 100644 libfreetype/pcfutil.h create mode 100644 libfreetype/pfr.c create mode 100644 libfreetype/pfrcmap.c create mode 100644 libfreetype/pfrcmap.h create mode 100644 libfreetype/pfrdrivr.c create mode 100644 libfreetype/pfrdrivr.h create mode 100644 libfreetype/pfrerror.h create mode 100644 libfreetype/pfrgload.c create mode 100644 libfreetype/pfrgload.h create mode 100644 libfreetype/pfrload.c create mode 100644 libfreetype/pfrload.h create mode 100644 libfreetype/pfrobjs.c create mode 100644 libfreetype/pfrobjs.h create mode 100644 libfreetype/pfrsbit.c create mode 100644 libfreetype/pfrsbit.h create mode 100644 libfreetype/pfrtypes.h create mode 100644 libfreetype/psaux.c create mode 100644 libfreetype/psauxerr.h create mode 100644 libfreetype/psauxmod.c create mode 100644 libfreetype/psauxmod.h create mode 100644 libfreetype/pshalgo.h create mode 100644 libfreetype/pshalgo1.c create mode 100644 libfreetype/pshalgo1.h create mode 100644 libfreetype/pshalgo2.c create mode 100644 libfreetype/pshalgo2.h create mode 100644 libfreetype/pshalgo3.c create mode 100644 libfreetype/pshalgo3.h create mode 100644 libfreetype/pshglob.c create mode 100644 libfreetype/pshglob.h create mode 100644 libfreetype/pshinter.c create mode 100644 libfreetype/pshmod.c create mode 100644 libfreetype/pshmod.h create mode 100644 libfreetype/pshrec.c create mode 100644 libfreetype/pshrec.h create mode 100644 libfreetype/psmodule.c create mode 100644 libfreetype/psmodule.h create mode 100644 libfreetype/psnamerr.h create mode 100644 libfreetype/psnames.c create mode 100644 libfreetype/psobjs.c create mode 100644 libfreetype/psobjs.h create mode 100644 libfreetype/pstables.h create mode 100644 libfreetype/raster.c create mode 100644 libfreetype/rasterrs.h create mode 100644 libfreetype/sfdriver.c create mode 100644 libfreetype/sfdriver.h create mode 100644 libfreetype/sferrors.h create mode 100644 libfreetype/sfnt.c create mode 100644 libfreetype/sfobjs.c create mode 100644 libfreetype/sfobjs.h create mode 100644 libfreetype/smooth.c create mode 100644 libfreetype/stddef.h create mode 100644 libfreetype/t1afm.c create mode 100644 libfreetype/t1afm.h create mode 100644 libfreetype/t1cmap.c create mode 100644 libfreetype/t1cmap.h create mode 100644 libfreetype/t1decode.c create mode 100644 libfreetype/t1decode.h create mode 100644 libfreetype/t1driver.c create mode 100644 libfreetype/t1driver.h create mode 100644 libfreetype/t1errors.h create mode 100644 libfreetype/t1gload.c create mode 100644 libfreetype/t1gload.h create mode 100644 libfreetype/t1load.c create mode 100644 libfreetype/t1load.h create mode 100644 libfreetype/t1objs.c create mode 100644 libfreetype/t1objs.h create mode 100644 libfreetype/t1parse.c create mode 100644 libfreetype/t1parse.h create mode 100644 libfreetype/t1tokens.h create mode 100644 libfreetype/t42drivr.c create mode 100644 libfreetype/t42drivr.h create mode 100644 libfreetype/t42error.h create mode 100644 libfreetype/t42objs.c create mode 100644 libfreetype/t42objs.h create mode 100644 libfreetype/t42parse.c create mode 100644 libfreetype/t42parse.h create mode 100644 libfreetype/test_bbox.c create mode 100644 libfreetype/test_trig.c create mode 100644 libfreetype/truetype.c create mode 100644 libfreetype/ttcmap.c create mode 100644 libfreetype/ttcmap.h create mode 100644 libfreetype/ttcmap0.c create mode 100644 libfreetype/ttcmap0.h create mode 100644 libfreetype/ttdriver.c create mode 100644 libfreetype/ttdriver.h create mode 100644 libfreetype/tterrors.h create mode 100644 libfreetype/ttgload.c create mode 100644 libfreetype/ttgload.h create mode 100644 libfreetype/ttinterp.c create mode 100644 libfreetype/ttinterp.h create mode 100644 libfreetype/ttload.c create mode 100644 libfreetype/ttload.h create mode 100644 libfreetype/ttobjs.c create mode 100644 libfreetype/ttobjs.h create mode 100644 libfreetype/ttpload.c create mode 100644 libfreetype/ttpload.h create mode 100644 libfreetype/ttpost.c create mode 100644 libfreetype/ttpost.h create mode 100644 libfreetype/ttsbit.c create mode 100644 libfreetype/ttsbit.h create mode 100644 libfreetype/type1.c create mode 100644 libfreetype/type1cid.c create mode 100644 libfreetype/type42.c create mode 100644 libfreetype/winfnt.c create mode 100644 libfreetype/winfnt.h create mode 100644 libfreetype/zconf.h create mode 100644 libfreetype/zlib.h create mode 100644 libfreetype/zutil.c create mode 100644 libfreetype/zutil.h create mode 100644 libinterp/NOTICE create mode 100644 libinterp/README create mode 100644 libinterp/alt.c create mode 100644 libinterp/comp-386.c create mode 100644 libinterp/comp-68020.c create mode 100644 libinterp/comp-arm.c create mode 100644 libinterp/comp-mips.c create mode 100644 libinterp/comp-power.c create mode 100644 libinterp/comp-s800.c create mode 100644 libinterp/comp-sparc.c create mode 100644 libinterp/comp-spim.c create mode 100644 libinterp/comp-thumb.c create mode 100644 libinterp/conv.c create mode 100644 libinterp/das-386.c create mode 100644 libinterp/das-68000.c create mode 100644 libinterp/das-68020.c create mode 100644 libinterp/das-arm.c create mode 100644 libinterp/das-mips.c create mode 100644 libinterp/das-power.c create mode 100644 libinterp/das-s800.c create mode 100644 libinterp/das-sparc.c create mode 100644 libinterp/das-spim.c create mode 100644 libinterp/das-stub.c create mode 100644 libinterp/das-thumb.c create mode 100644 libinterp/dec.c create mode 100644 libinterp/decgen.c create mode 100644 libinterp/dlm-Inferno.c create mode 100644 libinterp/dlm-Nt.c create mode 100644 libinterp/dlm-Plan9.c create mode 100644 libinterp/dlm-Posix.c create mode 100644 libinterp/draw.c create mode 100644 libinterp/drawmod.h create mode 100644 libinterp/freetype.c create mode 100644 libinterp/freetypemod.h create mode 100644 libinterp/gc.c create mode 100644 libinterp/geom.c create mode 100644 libinterp/heap.c create mode 100644 libinterp/heapaudit.c create mode 100644 libinterp/ipint.c create mode 100644 libinterp/keyring.c create mode 100644 libinterp/keyring.h create mode 100644 libinterp/link.c create mode 100644 libinterp/load.c create mode 100644 libinterp/loader.c create mode 100644 libinterp/loadermod.h create mode 100644 libinterp/math.c create mode 100644 libinterp/mathmod.h create mode 100644 libinterp/mkfile create mode 100644 libinterp/mkoptab create mode 100644 libinterp/optab.h create mode 100644 libinterp/prefab.c create mode 100644 libinterp/raise.c create mode 100644 libinterp/readmod.c create mode 100644 libinterp/runt.c create mode 100644 libinterp/runt.h create mode 100644 libinterp/sign.c create mode 100644 libinterp/stack.c create mode 100644 libinterp/string.c create mode 100644 libinterp/sysmod.h create mode 100644 libinterp/tab.h create mode 100644 libinterp/tk.c create mode 100644 libinterp/tkmod.h create mode 100644 libinterp/validstk.c create mode 100644 libinterp/xec.c create mode 100644 libkern/NOTICE create mode 100644 libkern/abort.c create mode 100644 libkern/abs.c create mode 100644 libkern/atol.c create mode 100644 libkern/charstod.c create mode 100644 libkern/cistrcmp.c create mode 100644 libkern/cistrncmp.c create mode 100644 libkern/cistrstr.c create mode 100644 libkern/cleanname.c create mode 100644 libkern/convD2M.c create mode 100644 libkern/convM2D.c create mode 100644 libkern/convM2S.c create mode 100644 libkern/convS2M.c create mode 100644 libkern/div-arm.s create mode 100644 libkern/dofmt.c create mode 100644 libkern/exp.c create mode 100644 libkern/fcallfmt.c create mode 100644 libkern/floor.c create mode 100644 libkern/fmt.c create mode 100644 libkern/fmtdef.h create mode 100644 libkern/fmtprint.c create mode 100644 libkern/fmtquote.c create mode 100644 libkern/fmtstr.c create mode 100644 libkern/fmtvprint.c create mode 100644 libkern/frexp-386.c create mode 100644 libkern/frexp-68000.c create mode 100644 libkern/frexp-arm.c create mode 100644 libkern/frexp-mips.c create mode 100644 libkern/frexp-power.c create mode 100644 libkern/frexp-sparc.c create mode 100644 libkern/frexp-thumb.c create mode 100644 libkern/getfcr-386.s create mode 100644 libkern/getfcr-68000.s create mode 100644 libkern/getfcr-arm.s create mode 100644 libkern/getfcr-mips.s create mode 100644 libkern/getfcr-power.s create mode 100644 libkern/getfcr-sparc.s create mode 100644 libkern/getfcr-thumb.s create mode 100644 libkern/getfields.c create mode 100644 libkern/log.c create mode 100644 libkern/memccpy-power.s create mode 100644 libkern/memccpy.c create mode 100644 libkern/memchr.c create mode 100644 libkern/memcmp-power.s create mode 100644 libkern/memcmp.c create mode 100644 libkern/memcpy-386.s create mode 100644 libkern/memcpy-arm.c create mode 100644 libkern/memcpy-thumb.c create mode 100644 libkern/memmove-386.s create mode 100644 libkern/memmove-68000.s create mode 100644 libkern/memmove-arm.s create mode 100644 libkern/memmove-mips.s create mode 100644 libkern/memmove-power.s create mode 100644 libkern/memmove-sparc.s create mode 100644 libkern/memmove-thumb.s create mode 100644 libkern/memmove.c create mode 100644 libkern/memset-386.s create mode 100644 libkern/memset-68000.s create mode 100644 libkern/memset-arm.s create mode 100644 libkern/memset-mips.s create mode 100644 libkern/memset-power.s create mode 100644 libkern/memset-sparc.s create mode 100644 libkern/memset-thumb.s create mode 100644 libkern/memset.c create mode 100644 libkern/mkfile create mode 100644 libkern/mkfile-386 create mode 100644 libkern/mkfile-68000 create mode 100644 libkern/mkfile-arm create mode 100644 libkern/mkfile-mips create mode 100644 libkern/mkfile-power create mode 100644 libkern/mkfile-sparc create mode 100644 libkern/mkfile-spim create mode 100644 libkern/mkfile-thumb create mode 100644 libkern/muldiv-68000.s create mode 100644 libkern/nan-386.c create mode 100644 libkern/nan-68000.c create mode 100644 libkern/nan-arm.c create mode 100644 libkern/nan-mips.c create mode 100644 libkern/nan-power.c create mode 100644 libkern/nan-sparc.c create mode 100644 libkern/nan-thumb.c create mode 100644 libkern/netmkaddr.c create mode 100644 libkern/pow.c create mode 100644 libkern/pow10.c create mode 100644 libkern/qsort.c create mode 100644 libkern/rune.c create mode 100644 libkern/runestrlen.c create mode 100644 libkern/seprint.c create mode 100644 libkern/sin.c create mode 100644 libkern/smprint.c create mode 100644 libkern/snprint.c create mode 100644 libkern/sqrt.c create mode 100644 libkern/strcat.c create mode 100644 libkern/strchr-386.s create mode 100644 libkern/strchr-68000.s create mode 100644 libkern/strchr-arm.s create mode 100644 libkern/strchr-mips.c create mode 100644 libkern/strchr-mips.s create mode 100644 libkern/strchr-power.s create mode 100644 libkern/strchr-sparc.s create mode 100644 libkern/strchr-thumb.s create mode 100644 libkern/strchr.c create mode 100644 libkern/strcmp-power.s create mode 100644 libkern/strcmp.c create mode 100644 libkern/strcpy.c create mode 100644 libkern/strdup.c create mode 100644 libkern/strecpy.c create mode 100644 libkern/strlen.c create mode 100644 libkern/strncmp-power.s create mode 100644 libkern/strncmp.c create mode 100644 libkern/strncpy.c create mode 100644 libkern/strrchr.c create mode 100644 libkern/strstr.c create mode 100644 libkern/strtod.c create mode 100644 libkern/strtol.c create mode 100644 libkern/strtoll.c create mode 100644 libkern/strtoul.c create mode 100644 libkern/tokenize.c create mode 100644 libkern/toupper.c create mode 100644 libkern/u16.c create mode 100644 libkern/u32.c create mode 100644 libkern/u64.c create mode 100644 libkern/utfecpy.c create mode 100644 libkern/utflen.c create mode 100644 libkern/utfnlen.c create mode 100644 libkern/utfrrune.c create mode 100644 libkern/utfrune.c create mode 100644 libkern/vlop-386.s create mode 100644 libkern/vlop-arm.s create mode 100644 libkern/vlop-mips.s create mode 100644 libkern/vlop-power.s create mode 100644 libkern/vlop-sparc.s create mode 100644 libkern/vlop-thumb.s create mode 100644 libkern/vlrt-386.c create mode 100644 libkern/vlrt-68000.c create mode 100644 libkern/vlrt-arm.c create mode 100644 libkern/vlrt-mips.c create mode 100644 libkern/vlrt-power.c create mode 100644 libkern/vlrt-sparc.c create mode 100644 libkern/vlrt-thumb.c create mode 100644 libkern/vseprint.c create mode 100644 libkern/vsmprint.c create mode 100644 libkern/vsnprint.c create mode 100644 libkeyring/NOTICE create mode 100644 libkeyring/dsaalg.c create mode 100644 libkeyring/egalg.c create mode 100644 libkeyring/keys.h create mode 100644 libkeyring/mkfile create mode 100644 libkeyring/rsaalg.c create mode 100644 liblogfs/NOTICE create mode 100644 liblogfs/boot.c create mode 100644 liblogfs/clunk.c create mode 100644 liblogfs/conv.c create mode 100644 liblogfs/create.c create mode 100644 liblogfs/dump.c create mode 100644 liblogfs/error.c create mode 100644 liblogfs/extentlist.c create mode 100644 liblogfs/fidmap.c create mode 100644 liblogfs/findfreeblock.c create mode 100644 liblogfs/flush.c create mode 100644 liblogfs/format.c create mode 100644 liblogfs/gn.c create mode 100644 liblogfs/group.c create mode 100644 liblogfs/groupset.c create mode 100644 liblogfs/is.c create mode 100644 liblogfs/local.h create mode 100644 liblogfs/log.c create mode 100644 liblogfs/map.c create mode 100644 liblogfs/mkfile create mode 100644 liblogfs/open.c create mode 100644 liblogfs/path.c create mode 100644 liblogfs/perm.c create mode 100644 liblogfs/read.c create mode 100644 liblogfs/remove.c create mode 100644 liblogfs/replace.c create mode 100644 liblogfs/replay.c create mode 100644 liblogfs/scan.c create mode 100644 liblogfs/srv.c create mode 100644 liblogfs/sweep.c create mode 100644 liblogfs/tagname.c create mode 100644 liblogfs/test.c create mode 100644 liblogfs/ust.c create mode 100644 liblogfs/walk.c create mode 100644 liblogfs/write.c create mode 100644 liblogfs/wstat.c create mode 100644 libmath/FPcontrol-FreeBSD.c create mode 100644 libmath/FPcontrol-Hp.c create mode 100644 libmath/FPcontrol-Inferno.c create mode 100644 libmath/FPcontrol-Irix.c create mode 100644 libmath/FPcontrol-Linux.c create mode 100644 libmath/FPcontrol-MacOSX.c create mode 100644 libmath/FPcontrol-Nt.c create mode 100644 libmath/FPcontrol-Plan9.c create mode 100644 libmath/FPcontrol-Solaris.c create mode 100644 libmath/FPcontrol-Unixware.c create mode 100644 libmath/NOTICE create mode 100644 libmath/bin/fdlibm-stubs create mode 100644 libmath/bin/unif_dtoa create mode 100644 libmath/bin/unif_fdlibm create mode 100644 libmath/blas.c create mode 100644 libmath/dtoa.c create mode 100644 libmath/fdim.c create mode 100644 libmath/fdlibm/e_acos.c create mode 100644 libmath/fdlibm/e_acosh.c create mode 100644 libmath/fdlibm/e_asin.c create mode 100644 libmath/fdlibm/e_atan2.c create mode 100644 libmath/fdlibm/e_atanh.c create mode 100644 libmath/fdlibm/e_cosh.c create mode 100644 libmath/fdlibm/e_exp.c create mode 100644 libmath/fdlibm/e_fmod.c create mode 100644 libmath/fdlibm/e_hypot.c create mode 100644 libmath/fdlibm/e_j0.c create mode 100644 libmath/fdlibm/e_j1.c create mode 100644 libmath/fdlibm/e_jn.c create mode 100644 libmath/fdlibm/e_lgamma_r.c create mode 100644 libmath/fdlibm/e_log.c create mode 100644 libmath/fdlibm/e_log10.c create mode 100644 libmath/fdlibm/e_pow.c create mode 100644 libmath/fdlibm/e_rem_pio2.c create mode 100644 libmath/fdlibm/e_remainder.c create mode 100644 libmath/fdlibm/e_sinh.c create mode 100644 libmath/fdlibm/e_sqrt.c create mode 100644 libmath/fdlibm/fdlibm.h create mode 100644 libmath/fdlibm/k_cos.c create mode 100644 libmath/fdlibm/k_rem_pio2.c create mode 100644 libmath/fdlibm/k_sin.c create mode 100644 libmath/fdlibm/k_tan.c create mode 100644 libmath/fdlibm/readme create mode 100644 libmath/fdlibm/s_asinh.c create mode 100644 libmath/fdlibm/s_atan.c create mode 100644 libmath/fdlibm/s_cbrt.c create mode 100644 libmath/fdlibm/s_ceil.c create mode 100644 libmath/fdlibm/s_copysign.c create mode 100644 libmath/fdlibm/s_cos.c create mode 100644 libmath/fdlibm/s_erf.c create mode 100644 libmath/fdlibm/s_expm1.c create mode 100644 libmath/fdlibm/s_fabs.c create mode 100644 libmath/fdlibm/s_finite.c create mode 100644 libmath/fdlibm/s_floor.c create mode 100644 libmath/fdlibm/s_ilogb.c create mode 100644 libmath/fdlibm/s_isnan.c create mode 100644 libmath/fdlibm/s_log1p.c create mode 100644 libmath/fdlibm/s_modf.c create mode 100644 libmath/fdlibm/s_nextafter.c create mode 100644 libmath/fdlibm/s_rint.c create mode 100644 libmath/fdlibm/s_scalbn.c create mode 100644 libmath/fdlibm/s_sin.c create mode 100644 libmath/fdlibm/s_tan.c create mode 100644 libmath/fdlibm/s_tanh.c create mode 100644 libmath/g_fmt.c create mode 100644 libmath/gemm.c create mode 100644 libmath/gfltconv.c create mode 100644 libmath/mkfile create mode 100644 libmath/pow10.c create mode 100644 libmemdraw/NOTICE create mode 100644 libmemdraw/alloc.c create mode 100644 libmemdraw/arc.c create mode 100644 libmemdraw/cload.c create mode 100644 libmemdraw/cmap.c create mode 100644 libmemdraw/cread.c create mode 100644 libmemdraw/defont.c create mode 100644 libmemdraw/draw.c create mode 100644 libmemdraw/drawtest.c create mode 100644 libmemdraw/ellipse.c create mode 100644 libmemdraw/fillpoly.c create mode 100644 libmemdraw/hwdraw.c create mode 100644 libmemdraw/icossin.c create mode 100644 libmemdraw/icossin2.c create mode 100644 libmemdraw/iprint.c create mode 100644 libmemdraw/line.c create mode 100644 libmemdraw/load.c create mode 100644 libmemdraw/mkfile create mode 100644 libmemdraw/mkfile-FreeBSD create mode 100644 libmemdraw/mkfile-Inferno create mode 100644 libmemdraw/mkfile-Irix create mode 100644 libmemdraw/mkfile-Linux create mode 100644 libmemdraw/mkfile-MacOSX create mode 100644 libmemdraw/mkfile-Nt create mode 100644 libmemdraw/mkfile-Plan9 create mode 100644 libmemdraw/mkfile-Solaris create mode 100644 libmemdraw/mkfile-os create mode 100644 libmemdraw/openmemsubfont.c create mode 100644 libmemdraw/poly.c create mode 100644 libmemdraw/read.c create mode 100644 libmemdraw/string.c create mode 100644 libmemdraw/subfont.c create mode 100644 libmemdraw/unload.c create mode 100644 libmemdraw/write.c create mode 100644 libmemlayer/NOTICE create mode 100644 libmemlayer/draw.c create mode 100644 libmemlayer/lalloc-x11.c create mode 100644 libmemlayer/lalloc.c create mode 100644 libmemlayer/layerop.c create mode 100644 libmemlayer/ldelete.c create mode 100644 libmemlayer/lhide.c create mode 100644 libmemlayer/line.c create mode 100644 libmemlayer/load.c create mode 100644 libmemlayer/lorigin.c create mode 100644 libmemlayer/lreshape.c create mode 100644 libmemlayer/lsetrefresh.c create mode 100644 libmemlayer/ltofront.c create mode 100644 libmemlayer/ltorear.c create mode 100644 libmemlayer/mkfile create mode 100644 libmemlayer/mkfile-FreeBSD create mode 100644 libmemlayer/mkfile-Hp create mode 100644 libmemlayer/mkfile-Inferno create mode 100644 libmemlayer/mkfile-Irix create mode 100644 libmemlayer/mkfile-Linux create mode 100644 libmemlayer/mkfile-MacOSX create mode 100644 libmemlayer/mkfile-NetBSD create mode 100644 libmemlayer/mkfile-Nt create mode 100644 libmemlayer/mkfile-Plan9 create mode 100644 libmemlayer/mkfile-Posix create mode 100644 libmemlayer/mkfile-Solaris create mode 100644 libmemlayer/mkfile-Unixware create mode 100644 libmemlayer/mkfile-os create mode 100644 libmemlayer/unload.c create mode 100644 libmp/Inferno-386/mkfile create mode 100644 libmp/Inferno-386/mpdigdiv.s create mode 100644 libmp/Inferno-386/mpvecadd.s create mode 100644 libmp/Inferno-386/mpvecdigmuladd.s create mode 100644 libmp/Inferno-386/mpvecdigmulsub.s create mode 100644 libmp/Inferno-386/mpvecsub.s create mode 100644 libmp/Inferno-amd64/mkfile create mode 100644 libmp/Inferno-amd64/mpdigdiv.s create mode 100644 libmp/Inferno-amd64/mpvecadd.s create mode 100644 libmp/Inferno-amd64/mpvecdigmuladd.s create mode 100644 libmp/Inferno-amd64/mpvecdigmulsub.s create mode 100644 libmp/Inferno-amd64/mpvecsub.s create mode 100644 libmp/Inferno-mips/mkfile create mode 100644 libmp/Inferno-mips/mpdigdiv.s create mode 100644 libmp/Inferno-mips/mpvecadd.s create mode 100644 libmp/Inferno-mips/mpvecdigmuladd.s create mode 100644 libmp/Inferno-mips/mpvecdigmulsub.s create mode 100644 libmp/Inferno-mips/mpvecsub.s create mode 100644 libmp/Inferno-power/mkfile create mode 100644 libmp/Inferno-power/mpvecadd.s create mode 100644 libmp/Inferno-power/mpvecdigmuladd.s create mode 100644 libmp/Inferno-power/mpvecdigmulsub.s create mode 100644 libmp/Inferno-power/mpvecsub.s create mode 100644 libmp/NOTICE create mode 100644 libmp/Plan9-386/mkfile create mode 100644 libmp/Plan9-386/mpdigdiv.s create mode 100644 libmp/Plan9-386/mpvecadd.s create mode 100644 libmp/Plan9-386/mpvecdigmuladd.s create mode 100644 libmp/Plan9-386/mpvecdigmulsub.s create mode 100644 libmp/Plan9-386/mpvecsub.s create mode 100644 libmp/Plan9-amd64/mkfile create mode 100644 libmp/Plan9-amd64/mpdigdiv.s create mode 100644 libmp/Plan9-amd64/mpvecadd.s create mode 100644 libmp/Plan9-amd64/mpvecdigmuladd.s create mode 100644 libmp/Plan9-amd64/mpvecdigmulsub.s create mode 100644 libmp/Plan9-amd64/mpvecsub.s create mode 100644 libmp/Plan9-mips/mkfile create mode 100644 libmp/Plan9-mips/mpdigdiv.s create mode 100644 libmp/Plan9-mips/mpvecadd.s create mode 100644 libmp/Plan9-mips/mpvecdigmuladd.s create mode 100644 libmp/Plan9-mips/mpvecdigmulsub.s create mode 100644 libmp/Plan9-mips/mpvecsub.s create mode 100644 libmp/Plan9-power/mkfile create mode 100644 libmp/Plan9-power/mpvecadd.s create mode 100644 libmp/Plan9-power/mpvecdigmuladd.s create mode 100644 libmp/Plan9-power/mpvecdigmulsub.s create mode 100644 libmp/Plan9-power/mpvecsub.s create mode 100644 libmp/bigtest.c create mode 100644 libmp/mkfile create mode 100644 libmp/mtest.c create mode 100644 libmp/port/betomp.c create mode 100644 libmp/port/crt.c create mode 100644 libmp/port/crttest.c create mode 100644 libmp/port/dat.h create mode 100644 libmp/port/letomp.c create mode 100644 libmp/port/mkfile create mode 100644 libmp/port/mpadd.c create mode 100644 libmp/port/mpaux.c create mode 100644 libmp/port/mpcmp.c create mode 100644 libmp/port/mpdigdiv.c create mode 100644 libmp/port/mpdiv.c create mode 100644 libmp/port/mpeuclid.c create mode 100644 libmp/port/mpexp.c create mode 100644 libmp/port/mpextendedgcd.c create mode 100644 libmp/port/mpfactorial.c create mode 100644 libmp/port/mpfmt.c create mode 100644 libmp/port/mpinvert.c create mode 100644 libmp/port/mpleft.c create mode 100644 libmp/port/mpmod.c create mode 100644 libmp/port/mpmul.c create mode 100644 libmp/port/mprand.c create mode 100644 libmp/port/mpright.c create mode 100644 libmp/port/mpsub.c create mode 100644 libmp/port/mptobe.c create mode 100644 libmp/port/mptoi.c create mode 100644 libmp/port/mptole.c create mode 100644 libmp/port/mptoui.c create mode 100644 libmp/port/mptouv.c create mode 100644 libmp/port/mptov.c create mode 100644 libmp/port/mpvecadd.c create mode 100644 libmp/port/mpveccmp.c create mode 100644 libmp/port/mpvecdigmuladd.c create mode 100644 libmp/port/mpvecsub.c create mode 100644 libmp/port/os.h create mode 100644 libmp/port/reduce-nt create mode 100644 libmp/port/reduce-rc create mode 100644 libmp/port/reduce-sh create mode 100644 libmp/port/strtomp.c create mode 100644 libmp/test.c create mode 100644 libnandfs/NOTICE create mode 100644 libnandfs/calcformat.c create mode 100644 libnandfs/correctauxilliary.c create mode 100644 libnandfs/ecc.c create mode 100644 libnandfs/eraseblock.c create mode 100644 libnandfs/extracttags.c create mode 100644 libnandfs/findfreeblock.c create mode 100644 libnandfs/formatblock.c create mode 100644 libnandfs/getblockstatus.c create mode 100644 libnandfs/hamming31_26.c create mode 100644 libnandfs/init.c create mode 100644 libnandfs/local.h create mode 100644 libnandfs/markblockbad.c create mode 100644 libnandfs/mkfile create mode 100644 libnandfs/open.c create mode 100644 libnandfs/readblock.c create mode 100644 libnandfs/readpage.c create mode 100644 libnandfs/readpageauxilliary.c create mode 100644 libnandfs/reformatblock.c create mode 100644 libnandfs/setget.c create mode 100644 libnandfs/updatepage.c create mode 100644 libnandfs/writeblock.c create mode 100644 libnandfs/writepageauxilliary.c create mode 100644 libprefab/NOTICE create mode 100644 libprefab/box.c create mode 100644 libprefab/compound.c create mode 100644 libprefab/element.c create mode 100644 libprefab/elistelement.c create mode 100644 libprefab/iconbox.c create mode 100644 libprefab/iconelement.c create mode 100644 libprefab/mkfile create mode 100644 libprefab/textbox.c create mode 100644 libprefab/textelement.c create mode 100644 libsec/Inferno-386/md5block.s create mode 100644 libsec/Inferno-386/mkfile create mode 100644 libsec/Inferno-386/sha1block.s create mode 100644 libsec/Inferno-mips/md5block.s create mode 100644 libsec/Inferno-mips/mkfile create mode 100644 libsec/Inferno-mips/sha1block.s create mode 100644 libsec/NOTICE create mode 100644 libsec/Plan9-386/md5block.s create mode 100644 libsec/Plan9-386/mkfile create mode 100644 libsec/Plan9-386/sha1block.s create mode 100644 libsec/Plan9-mips/md5block.s create mode 100644 libsec/Plan9-mips/mkfile create mode 100644 libsec/Plan9-mips/sha1block.s create mode 100644 libsec/mkfile create mode 100644 libsec/port/aes.c create mode 100644 libsec/port/blowfish.c create mode 100644 libsec/port/decodepem.c create mode 100644 libsec/port/des.c create mode 100644 libsec/port/des3CBC.c create mode 100644 libsec/port/des3ECB.c create mode 100644 libsec/port/desCBC.c create mode 100644 libsec/port/desECB.c create mode 100644 libsec/port/desmodes.c create mode 100644 libsec/port/dsaalloc.c create mode 100644 libsec/port/dsagen.c create mode 100644 libsec/port/dsaprimes.c create mode 100644 libsec/port/dsaprivtopub.c create mode 100644 libsec/port/dsasign.c create mode 100644 libsec/port/dsaverify.c create mode 100644 libsec/port/egalloc.c create mode 100644 libsec/port/egdecrypt.c create mode 100644 libsec/port/egencrypt.c create mode 100644 libsec/port/eggen.c create mode 100644 libsec/port/egprivtopub.c create mode 100644 libsec/port/egsign.c create mode 100644 libsec/port/egtest.c create mode 100644 libsec/port/egverify.c create mode 100644 libsec/port/fastrand.c create mode 100644 libsec/port/genprime.c create mode 100644 libsec/port/genrandom.c create mode 100644 libsec/port/gensafeprime.c create mode 100644 libsec/port/genstrongprime.c create mode 100644 libsec/port/hmac.c create mode 100644 libsec/port/hmactest.c create mode 100644 libsec/port/idea.c create mode 100644 libsec/port/md4.c create mode 100644 libsec/port/md4test.c create mode 100644 libsec/port/md5.c create mode 100644 libsec/port/md5block.c create mode 100644 libsec/port/md5pickle.c create mode 100644 libsec/port/mkfile create mode 100644 libsec/port/nfastrand.c create mode 100644 libsec/port/primetest.c create mode 100644 libsec/port/prng.c create mode 100644 libsec/port/probably_prime.c create mode 100644 libsec/port/rc4.c create mode 100644 libsec/port/reduce-nt create mode 100644 libsec/port/reduce-rc create mode 100644 libsec/port/reduce-sh create mode 100644 libsec/port/rsaalloc.c create mode 100644 libsec/port/rsadecrypt.c create mode 100644 libsec/port/rsaencrypt.c create mode 100644 libsec/port/rsafill.c create mode 100644 libsec/port/rsagen.c create mode 100644 libsec/port/rsaprivtopub.c create mode 100644 libsec/port/rsatest.c create mode 100644 libsec/port/sha1.c create mode 100644 libsec/port/sha1block.c create mode 100644 libsec/port/sha1pickle.c create mode 100644 libsec/port/smallprimes.c create mode 100644 libsec/port/smallprimetest.c create mode 100644 libtk/NOTICE create mode 100644 libtk/buton.c create mode 100644 libtk/canvs.c create mode 100644 libtk/canvs.h create mode 100644 libtk/canvu.c create mode 100644 libtk/carcs.c create mode 100644 libtk/cbits.c create mode 100644 libtk/cimag.c create mode 100644 libtk/cline.c create mode 100644 libtk/colrs.c create mode 100644 libtk/coval.c create mode 100644 libtk/cpoly.c create mode 100644 libtk/crect.c create mode 100644 libtk/ctext.c create mode 100644 libtk/cwind.c create mode 100644 libtk/ebind.c create mode 100644 libtk/entry.c create mode 100644 libtk/extns.c create mode 100644 libtk/frame.c create mode 100644 libtk/frame.h create mode 100644 libtk/grids.c create mode 100644 libtk/image.c create mode 100644 libtk/label.c create mode 100644 libtk/label.h create mode 100644 libtk/listb.c create mode 100644 libtk/listb.h create mode 100644 libtk/mail.tk create mode 100644 libtk/menu.tk create mode 100644 libtk/menus.c create mode 100644 libtk/mkfile create mode 100644 libtk/mkfile-std create mode 100644 libtk/packr.c create mode 100644 libtk/panel.c create mode 100644 libtk/parse.c create mode 100644 libtk/radio.tk create mode 100644 libtk/scale.c create mode 100644 libtk/scrol.c create mode 100644 libtk/textu.c create mode 100644 libtk/textw.c create mode 100644 libtk/textw.h create mode 100644 libtk/tindx.c create mode 100644 libtk/tmark.c create mode 100644 libtk/ttags.c create mode 100644 libtk/twind.c create mode 100644 libtk/utils.c create mode 100644 libtk/windw.c create mode 100644 libtk/xdata.c create mode 100644 limbo/NOTICE create mode 100644 limbo/asm.c create mode 100644 limbo/com.c create mode 100644 limbo/decls.c create mode 100644 limbo/dis.c create mode 100644 limbo/dtocanon.c create mode 100644 limbo/ecom.c create mode 100644 limbo/fns.h create mode 100644 limbo/gen.c create mode 100644 limbo/lex.c create mode 100644 limbo/limbo.h create mode 100644 limbo/limbo.y create mode 100644 limbo/mkfile create mode 100644 limbo/nodes.c create mode 100644 limbo/optab.c create mode 100644 limbo/optim.c create mode 100644 limbo/runt.h create mode 100644 limbo/sbl.c create mode 100644 limbo/stubs.c create mode 100644 limbo/typecheck.c create mode 100644 limbo/types.c diff --git a/CHANGES b/CHANGES new file mode 100644 index 00000000..b8a60acd --- /dev/null +++ b/CHANGES @@ -0,0 +1,232 @@ +20060303 + /emu/Plan9/win.c replace ldepthof +20060302 + add KPX11 flag to hosted kproc to boost the stack for silly x11 & co. + put keyboard and cursor processing into a separate kproc with big stack in /emu/port/win-x11a.c + change various os.c to match +20060301 + /appl/cmd/rioimport.b fix initialisation race +20060227 + fix /utils/ql/l.h: oprange should be [ALAST] (with extra 405xx opcodes), also AEND->ALIST elsewhere + put faster gethunk in ql and kl (mimic other compilers) +20060226 + enable /emu/port/devpointer.c, with changes to /emu/*/win*.c to call mousetrack, /emu/port/main.c to bind #m, + and code for pointer and cursor removed from devcons.c + update Nt/win.c and port/win-x11a.c from drawterm to get/put host snarf buffer + change devmem.c from #m to #% not to clash with pointer +20060225 + add /emu/port/devsnarf.c (#^) and put clipread from drawterm in /emu/Nt/win.c +20060224 + modify /appl/lib/secstore.b, /module/secstore.m, secstore(2) [add dial, auth, mkseckey, mkfilekey, remove] + add /appl/cmd/auth/secstore.b, secstore(1) + remove strange exception handling in /appl/cmd/dd.b + add dhcpclient(2) +20060223 + add /appl/cmd/auth/aescbc.c + add /appl/cmd/crypt.b [rog] + add crypt(1) + add /appl/lib/secstore.b /module/secstore.m secstore(2) +20060221 + /libsec/port/hmac.c: treat existing but not seeded digest correctly + /libinterp/ipint.c, /module/keyring.m, add new operator `invert' +20060220 + add ida(2), /appl/lib/ida, /module/ida.m +20060216 + add ubfa(2), ubfa(6), /module/ubfa.m, /appl/lib/ubfa.b +20060214 + [rog] add cursor changing support to tk, wm, wmlib (eg, for acme) +20060213 + remove libcrypt_o from distribution +20060211 + add /man/1/9win [rog] + change /appl/cmd/auth/keyfs.b to confirm key only when creating the file [rog] + punt floating-point conversions in powerpc jit on macos for time being + (have particular values in certain FP registers when native) +20060210 + update /libmemdraw/draw.c to include 9's changes for concurrent use + remove canlock as assembly language interface, replaced by _tas + replace native use of tas by _tas; make declarations all agree + add holdon/holdoff ctl request to wm/sh + wmproxy in appl/lib/wmlib.b does not create new pgrp +20060206 + update /appl/cmd/cp.b with digbyt's changes (mode/uid/gid correct on copied directories) +20060203 + update /appl/cmd/ip/dhcp.b /appl/cmd/lib/dhcpclient.b + update /os/init/i4e.b to use it + add /man/8/dhcp +20060118 + add ksize(10.1), kstrip(10.1), /utils/kstrip + new _MAGIC definitions in utils/libmach/a.out.h +20060114 + change /os/boot/arm1110 mkfile to work on unix and windows; add to /lib/proto/os +20060111 + -s (exportonly) -x/-y (geometry) options to 9win (TODO: rog, manual page!) + wm/sh.b: correct hold mode; remove little-used and undocumented history file +20060109 + delete #pragma from flate.h + ensure lib9 compiles replacement sbrk for MacOSX +20060106 + update compilers + update compilers' manual page + fix os/port/portmkfile for Plan 9: don't use $OBJDIR but Inferno/$OBJTYPE + have os/port/portmkfile check for i$CONF.p9 and use that not i$CONF for acid + add srclist(10.1) +20060105 + account for new definition of rendezvous (void* not ulong) + add dummy setmalloctag to utils where needed (for Plan 9) + set profileflg only for ATEXT in utils/?c/txt.c + update /os/boot/pc +20060103 + /os/ip updated from Plan 9 + /os/ip/^(bootp.c dhcp.c ihbootp.c) changed to use announce not connect for udp +20051215 + /man/6/keytext added +20051207 + /tools/odbc.c portability changes, and fix modes on several files +20051202 + /emu/Plan9/devfs.c strip Inferno root from file system diagnostics +20051130 + change /appl/svc/httpd to use lock(2) not lockprocs +20051123 + /appl/cmd/bind.b changed not to use arg.dis, implement -q, and diagnostic change + /appl/cmd/mount.b acquired a -q option as well +20051114 + avoid limit==0 in wm/memory.b +20051108 + have poolmaxsize return 0 for pool of size 0 + remove /os/*/u.h (in favour of /$SYSTARG/$OBJTYPE/include/u.h) + update mkfiles accordingly + adjust mkfiles for libsec and libmp compilation for native kernels +20051107 + added /os/manga +20051101 + fixed /os/port/devloopback.c + updated netif.c netif.h +20051028 + updated gettar(1) and /appl/cmd/gettar.b +20051025 + fix British Summer Time in locales +20051021 + updated /appl/lib/newns.b and namespace(6) for environment variable substitution +20051018 + moved in changes from home to os/port: cis.c devbridge.c ethermii.c portclock.c devuart.c devbench.c random.c portfns.h tod.c uart.h + - mainly for changes to implement fasttick and timers +20051017 + /limbo/ecom.c and /appl/cmd/limbo/ecom.c: ensure src set in temporary Node to avoid `no file specified' in sbl.[bc] +20050925 + added format(2) +20050922 + iostats(4) added +20050919 + improve behaviour in wm/sh when in raw mode +20050916 + /appl/lib/styx.b: return value for Rmsg.unpack for Rstat didn't include len[2] +20050912 + update /appl/lib/disks.b, disks(2) + update /appl/cmd/disk/format.b + add /appl/cmd/disk/prep, /appl/cmd/disk/mbr.b + replace format(8) by prep(8) + delete undocumented /appl/cmd/disk/part.b +20050908 + added disks(2) and scsiio(2) +20050906 + os/port/devsrv.c and emu/port/devsrv.c to allow setting length by wstat (also DMAPPEND) + appl/cmd/dossrv.b fix to interpret aname:offset + usb updated to support current native uhci drivers + /appl/lib/usb/usbmass.b changed to work with new driver and more devices +20050901 + new /appl/cmd/ip/sntp.b, sntp(8) +20050824 + cp(1): added -gux options +20050810 + mangaload(8): new +20050812 + /appl/cmd/limbo/nodes.b didn't always initialise n.c (eg, a != a) +20050712 + updated utils/[12][acl] to match Plan 9's +20050627 + added streamcp (renamed fcp since that's what plan 9 calls a similar thing) +20050626 + added write lock to emu/port/devip.c to stop Linux (and perhaps others) splitting socket writes by different processes +20050620 + added w3c-xpointers(2) [/module/xpointers.m; /appl/lib/w3c/xpointers.b] +20050617 + fixed qid array reference in /appl/cmd/lockfs.b [cjones83] +20050610 + updated lib/ndb/dns + fixed count < 0 when reading beyond end of file in disk/kfs +20050526 + fixed factotum's p9any to use user= attribute in key not /dev/user + fix /appl/lib/daytime.b's handling of dlpairs; also read /env/timezone if that's there + update /appl/lib/w3c/css.b to read CSS2.1 + add w3c-css(2); possibly should move module file to w3c/css.m? +20050518 + added GPL/LGPL notice files + made single mk.b from many included source files + added MIT-template or LGPL NOTICE files to lib* directories + updated doc/port.ms + included libmp and libsec in lib/proto/src (Lucent Public License), not yet used + fix bug in cmd(3) introduced by killonclose +20050425 + add sexprs(6) + add truerand/ntruerand and nsec[defined as osnsec] to lib9.h + redefine fmax and log2 in lib9.h + some/all FreeBSD-5.x-y don't initialise rfork_thread's procmask from parent (contrary to docs); compensate in FreeBSD/os.c + finally copy last year's changes to 1c from home + call logs->init in applylog + changed /appl/cmd/cmp.b to work correctly for differing buffer sizes, errors, etc. + included /os/pc/sd53c8xx.[in] and added /utils/na [not compiled by default] + mask off OEXCL in (emu/port os/port)^/sysfile.c:/^kcreate's openmode + wm/sh.b: keep menuitem 0 if noscroll selected + added units(1) +20050413 + licence following MIT-template replaces `free for all' instances + "-N nice" option in os(1) + geodesy(2) is new + ebook(1) is newly released (Open Ebook browser) + wm-sh(1) has a rearranged menu, with scroll/noscroll option added + /doc/asm.ms has been updated + section 10 has been updated + limbo compiler will make simple functions inline if possible + limbo compiler supports `ref fn' type + /doc/limbo/addendum.ms [also .ps .pdf] updated to reflect `ref fn' and other changes + keyring-ipint(2) has a few new operations (shl, shr, copy) + DigestState has new copy operation replacing cloneDigestState + Keyring has new functions certtoattr, pktoattr and sktoattr each returning + a string containing attr=value ... representations of certificates and keys + u.h has been updated for all platforms, hosted and native + new ptrint, u8int, u16int, u64int, FPdbleword, and more accurate varargs for < 4 byte values + lib9.h has extra encode/decode functions moved from libcrypt + string.m has new quotec function [TODO: man page] + crypt/ssl3.b handles a particular certificate type better + charon has several bug fixes in javascript, notably parsing of certain expressions + /doc/descent/* `Descent into Limbo' updated + internally libinterp uses a different representation for parts of the linkage table + /doc/lprof.ms [also .ps and .pdf] gives overview of Limbo profiling + /doc/ebookimp.ms [also .ps and .pdf] discusses implementation of its XML browsing + /doc/compiler.ms has been updated + /doc/dis.ms eclr has been removed + emu(1) -b enables bounds checking in JIT + cs(8) handles general query (!attr=val ...) + mkfs/mkext(8) handles big archives + emuinit tries sh -c on command if not immediately dis + /appl/lib/string.b: rewritten unquoted for correct handling of embedded quotes + limbo warns about unused local variables + limbo: -F enables new implementation of function ref + limbo: -O runs optimiser + mount -9 uses 9fs not styx as service address + cmd(3) adds "killonclose" and parameter to "nice" + TODO: group check in styxserver + styxlisten accumulates algs correctly + os/port/mkdevc builds vgacursor table + touch uses OEXCL + /appl/lib/debug knows about ref fn + ecmascript: for(... in ...) parsed correctly + getuserpasswd added to factotum(2) + /appl/lib/print reincorporated + wm/clock + ftpfs calls factotum (getuserpasswd) + hoststdin/hoststdout/hoststderr [preliminary] + /os/boot/pc updated, as is its shipping list +20041217 + base point diff --git a/FreeBSD/386/bin/data2c b/FreeBSD/386/bin/data2c new file mode 100755 index 00000000..f755f8fa Binary files /dev/null and b/FreeBSD/386/bin/data2c differ diff --git a/FreeBSD/386/bin/mk b/FreeBSD/386/bin/mk new file mode 100755 index 00000000..766d6349 Binary files /dev/null and b/FreeBSD/386/bin/mk differ diff --git a/FreeBSD/386/bin/yacc b/FreeBSD/386/bin/yacc new file mode 100755 index 00000000..7390e221 Binary files /dev/null and b/FreeBSD/386/bin/yacc differ diff --git a/FreeBSD/386/include/fpuctl.h b/FreeBSD/386/include/fpuctl.h new file mode 100644 index 00000000..8389f6ee --- /dev/null +++ b/FreeBSD/386/include/fpuctl.h @@ -0,0 +1,76 @@ +/* + * Linux 386 fpu support + * Mimic Plan9 floating point support + */ + +static void +setfcr(ulong fcr) +{ + __asm__( "xorb $0x3f, %%al\n\t" + "pushw %%ax\n\t" + "fwait\n\t" + "fldcw (%%esp)\n\t" + "popw %%ax\n\t" + : /* no output */ + : "al" (fcr) + ); +} + +static ulong +getfcr(void) +{ + ulong fcr = 0; + + __asm__( "pushl %%eax\n\t" + "fwait\n\t" + "fstcw (%%esp)\n\t" + "popl %%eax\n\t" + "xorb $0x3f, %%al\n\t" + : "=a" (fcr) + : "eax" (fcr) + ); + return fcr; +} + +static ulong +getfsr(void) +{ + ulong fsr = -1; + + __asm__( "fwait\n\t" + "fstsw (%%eax)\n\t" + "movl (%%eax), %%eax\n\t" + "andl $0xffff, %%eax\n\t" + : "=a" (fsr) + : "eax" (&fsr) + ); + return fsr; +} + +static void +setfsr(ulong fsr) +{ + __asm__("fclex\n\t"); +} + +/* FCR */ +#define FPINEX (1<<5) +#define FPUNFL ((1<<4)|(1<<1)) +#define FPOVFL (1<<3) +#define FPZDIV (1<<2) +#define FPINVAL (1<<0) +#define FPRNR (0<<10) +#define FPRZ (3<<10) +#define FPRPINF (2<<10) +#define FPRNINF (1<<10) +#define FPRMASK (3<<10) +#define FPPEXT (3<<8) +#define FPPSGL (0<<8) +#define FPPDBL (2<<8) +#define FPPMASK (3<<8) +/* FSR */ +#define FPAINEX FPINEX +#define FPAOVFL FPOVFL +#define FPAUNFL FPUNFL +#define FPAZDIV FPZDIV +#define FPAINVAL FPINVAL diff --git a/FreeBSD/386/include/lib9.h b/FreeBSD/386/include/lib9.h new file mode 100644 index 00000000..a969d0d2 --- /dev/null +++ b/FreeBSD/386/include/lib9.h @@ -0,0 +1,491 @@ +/* define _BSD_SOURCE to use ISO C, POSIX, and 4.3BSD things. */ +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +/* these aren't really needed because FreeBSD does the right thing and makes off_t 64 bits, full stop */ +#define _LARGEFILE64_SOURCE 1 +#define _FILE_OFFSET_BITS 64 +#include +#include +#include +#include +#include +#include +#include +#include "math.h" +#include +#include +#include +#include + +#define getwd infgetwd + +#undef isnan +#define round infround +#define fmax inffmax +#define log2 inflog2 + +#ifndef EMU +typedef struct Proc Proc; +#endif + +/* + * math module dtoa + */ +#define __LITTLE_ENDIAN + +#define nil ((void*)0) + +typedef unsigned char uchar; +typedef signed char schar; +typedef unsigned long ulong; +typedef unsigned short Rune; +typedef long long int vlong; +typedef unsigned long long int uvlong; +typedef unsigned int u32int; +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned short u16int; +typedef unsigned char u8int; +typedef unsigned long uintptr; + +#define USED(x) if(x){}else{} +#define SET(x) + +#undef nelem +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#undef offsetof +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) +#undef assert +#define assert(x) if(x){}else _assert("x") + +/* + * most mem and string routines are declared by ANSI/POSIX files above + */ + +extern char* strecpy(char*, char*, char*); +extern char* strdup(const char*); +extern int cistrncmp(char*, char*, int); +extern int cistrcmp(char*, char*); +extern char* cistrstr(char*, char*); +extern int tokenize(char*, char**, int); + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80 /* decoding error in UTF */ +}; + +/* + * rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern int runelen(long); +extern int runenlen(Rune*, int); +extern int fullrune(char*, int); +extern int utflen(char*); +extern int utfnlen(char*, long); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); +extern char* utfutf(char*, char*); +extern char* utfecpy(char*, char*, char*); + +extern Rune* runestrcat(Rune*, Rune*); +extern Rune* runestrchr(Rune*, Rune); +extern int runestrcmp(Rune*, Rune*); +extern Rune* runestrcpy(Rune*, Rune*); +extern Rune* runestrncpy(Rune*, Rune*, long); +extern Rune* runestrecpy(Rune*, Rune*, Rune*); +extern Rune* runestrdup(Rune*); +extern Rune* runestrncat(Rune*, Rune*, long); +extern int runestrncmp(Rune*, Rune*, long); +extern Rune* runestrrchr(Rune*, Rune); +extern long runestrlen(Rune*); +extern Rune* runestrstr(Rune*, Rune*); + +extern Rune tolowerrune(Rune); +extern Rune totitlerune(Rune); +extern Rune toupperrune(Rune); +extern int isalpharune(Rune); +extern int islowerrune(Rune); +extern int isspacerune(Rune); +extern int istitlerune(Rune); +extern int isupperrune(Rune); + +/* + * malloc + */ +extern void* malloc(size_t); +extern void* mallocz(ulong, int); +extern void free(void*); +extern ulong msize(void*); +extern void* calloc(size_t, size_t); +extern void* realloc(void*, size_t); +extern void setmalloctag(void*, ulong); +extern void setrealloctag(void*, ulong); +extern ulong getmalloctag(void*); +extern ulong getrealloctag(void*); +extern void* malloctopoolblock(void*); + +/* + * print routines + */ +typedef struct Fmt Fmt; +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + ulong flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + + FmtFlag = FmtByte << 1 +}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); +extern int runefmtstrinit(Fmt*); +extern Rune* runefmtstrflush(Fmt*); + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int dorfmt(Fmt*, Rune*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); +extern int fmtrunestrcpy(Fmt*, Rune*); +/* + * error string for %r + * supplied on per os basis, not part of fmt library + */ +extern int errfmt(Fmt *f); + +/* + * quoted strings + */ +extern char *unquotestrdup(char*); +extern Rune *unquoterunestrdup(Rune*); +extern char *quotestrdup(char*); +extern Rune *quoterunestrdup(Rune*); +extern int quotestrfmt(Fmt*); +extern int quoterunestrfmt(Fmt*); +extern void quotefmtinstall(void); +extern int (*doquote)(int); + +/* + * random number + */ +extern int nrand(int); +extern ulong truerand(void); +extern ulong ntruerand(ulong); + +/* + * math + */ +extern int isNaN(double); +extern int isInf(double, int); +extern double pow(double, double); + +/* + * Time-of-day + */ + +typedef struct Tm Tm; +struct Tm { + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; + int yday; + char zone[4]; + int tzoff; +}; +extern vlong osnsec(void); +#define nsec osnsec + +/* + * one-of-a-kind + */ +extern void _assert(char*); +extern double charstod(int(*)(void*), void*); +extern char* cleanname(char*); +extern double frexp(double, int*); +extern ulong getcallerpc(void*); +extern int getfields(char*, char**, int, int, char*); +extern char* getuser(void); +extern char* getwd(char*, int); +extern double ipow10(int); +extern double ldexp(double, int); +extern double modf(double, double*); +#define pow10 infpow10 +extern double pow10(int); +extern vlong strtoll(const char*, char**, int); +extern uvlong strtoull(const char*, char**, int); +extern void sysfatal(char*, ...); +extern int dec64(uchar*, int, char*, int); +extern int enc64(char*, int, uchar*, int); +extern int dec32(uchar*, int, char*, int); +extern int enc32(char*, int, uchar*, int); +extern int dec16(uchar*, int, char*, int); +extern int enc16(char*, int, uchar*, int); +extern int encodefmt(Fmt*); + +/* + * synchronization + */ +typedef +struct Lock { + ulong val; + int pid; +} Lock; + +extern ulong _tas(ulong*); + +extern void lock(Lock*); +extern void unlock(Lock*); +extern int canlock(Lock*); + +typedef struct QLock QLock; +struct QLock +{ + Lock use; /* to access Qlock structure */ + Proc *head; /* next process waiting for object */ + Proc *tail; /* last process waiting for object */ + int locked; /* flag */ +}; + +extern void qlock(QLock*); +extern void qunlock(QLock*); +extern int canqlock(QLock*); +extern void _qlockinit(ulong (*)(ulong, ulong)); /* called only by the thread library */ + +typedef +struct RWLock +{ + Lock l; /* Lock modify lock */ + QLock x; /* Mutual exclusion lock */ + QLock k; /* Lock for waiting writers */ + int readers; /* Count of readers in lock */ +} RWLock; + +extern int canrlock(RWLock*); +extern int canwlock(RWLock*); +extern void rlock(RWLock*); +extern void runlock(RWLock*); +extern void wlock(RWLock*); +extern void wunlock(RWLock*); + +/* + * network dialing + */ +#define NETPATHLEN 40 + +/* + * system calls + * + */ +#define STATMAX 65535U /* max length of machine-independent stat structure */ +#define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */ +#define ERRMAX 128 /* max length of error string */ + +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MCACHE 0x0010 /* cache some data */ +#define MMASK 0x0017 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ +#define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ + +#define AEXIST 0 /* accessible: exists */ +#define AEXEC 1 /* execute access */ +#define AWRITE 2 /* write access */ +#define AREAD 4 /* read access */ + +/* bits in Qid.type */ +#define QTDIR 0x80 /* type bit for directories */ +#define QTAPPEND 0x40 /* type bit for append only files */ +#define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ +#define QTAUTH 0x08 /* type bit for authentication file */ +#define QTFILE 0x00 /* plain file */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMAUTH 0x08000000 /* mode bit for authentication file */ +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +typedef +struct Qid +{ + uvlong path; + ulong vers; + uchar type; +} Qid; + +typedef +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +} Dir; + +extern Dir* dirstat(char*); +extern Dir* dirfstat(int); +extern int dirwstat(char*, Dir*); +extern int dirfwstat(int, Dir*); +extern long dirread(int, Dir**); +extern void nulldir(Dir*); +extern long dirreadall(int, Dir**); + +typedef +struct Waitmsg +{ + int pid; /* of loved one */ + ulong time[3]; /* of loved one & descendants */ + char *msg; +} Waitmsg; + +extern void _exits(char*); + +extern void exits(char*); +extern int create(char*, int, int); +extern int errstr(char*, uint); + +extern void perror(const char*); +extern long readn(int, void*, long); +extern int remove(const char*); +extern void rerrstr(char*, uint); +extern vlong seek(int, vlong, int); +extern int segflush(void*, ulong); +extern void werrstr(char*, ...); + +extern char *argv0; +#define ARGBEGIN for((argv0||(argv0=*argv)),argv++,argc--;\ + argv[0] && argv[0][0]=='-' && argv[0][1];\ + argc--, argv++) {\ + char *_args, *_argt;\ + Rune _argc;\ + _args = &argv[0][1];\ + if(_args[0]=='-' && _args[1]==0){\ + argc--; argv++; break;\ + }\ + _argc = 0;\ + while(*_args && (_args += chartorune(&_argc, _args)))\ + switch(_argc) +#define ARGEND SET(_argt);USED(_argt);USED(_argc); USED(_args);}USED(argv); USED(argc); +#define ARGF() (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): 0)) +#define EARGF(x) (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0))) + +#define ARGC() _argc + +/* + * Extensions for Inferno to basic libc.h + */ + +#define setbinmode() + +/* + * Extensions for emu kernel emulation + */ +#ifdef EMU + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +typedef struct FPU FPU; +struct FPU +{ + uchar env[28]; +}; + +#define KSTACK (32 * 1024) + +static __inline Proc *getup(void) { + Proc *p; + __asm__( "movl %%esp, %%eax\n\t" + : "=a" (p) + ); + return *(Proc **)((unsigned long)p & ~(KSTACK - 1)); +}; + +#define up (getup()) + +typedef sigjmp_buf osjmpbuf; +#define ossetjmp(buf) sigsetjmp(buf, 1) + +#endif diff --git a/Inferno/386/include/lib9.h b/Inferno/386/include/lib9.h new file mode 100644 index 00000000..1767d7b3 --- /dev/null +++ b/Inferno/386/include/lib9.h @@ -0,0 +1,8 @@ +#include +#include + +/* + * Extensions for Inferno to basic libc.h + */ + +#define __LITTLE_ENDIAN /* math/dtoa.c only */ diff --git a/Inferno/386/include/u.h b/Inferno/386/include/u.h new file mode 100644 index 00000000..5c07da93 --- /dev/null +++ b/Inferno/386/include/u.h @@ -0,0 +1,64 @@ +#define nil ((void*)0) +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned long ulong; +typedef unsigned int uint; +typedef signed char schar; +typedef long long vlong; +typedef unsigned long long uvlong; +typedef unsigned long uintptr; +typedef ushort Rune; +typedef union FPdbleword FPdbleword; +typedef long jmp_buf[2]; +#define JMPBUFSP 0 +#define JMPBUFPC 1 +#define JMPBUFDPC 0 +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned char u8int; +typedef unsigned short u16int; +typedef unsigned int u32int; +typedef unsigned long long u64int; + +/* FCR */ +#define FPINEX (1<<5) +#define FPUNFL ((1<<4)|(1<<1)) +#define FPOVFL (1<<3) +#define FPZDIV (1<<2) +#define FPINVAL (1<<0) +#define FPRNR (0<<10) +#define FPRZ (3<<10) +#define FPRPINF (2<<10) +#define FPRNINF (1<<10) +#define FPRMASK (3<<10) +#define FPPEXT (3<<8) +#define FPPSGL (0<<8) +#define FPPDBL (2<<8) +#define FPPMASK (3<<8) +/* FSR */ +#define FPAINEX FPINEX +#define FPAOVFL FPOVFL +#define FPAUNFL FPUNFL +#define FPAZDIV FPZDIV +#define FPAINVAL FPINVAL +union FPdbleword +{ + double x; + struct { /* little endian */ + ulong lo; + ulong hi; + }; +}; + +typedef char* va_list; +#define va_start(list, start) list =\ + (sizeof(start) < 4?\ + (char*)((int*)&(start)+1):\ + (char*)(&(start)+1)) +#define va_end(list)\ + USED(list) +#define va_arg(list, mode)\ + ((sizeof(mode) == 1)?\ + ((mode*)(list += 4))[-4]:\ + (sizeof(mode) == 2)?\ + ((mode*)(list += 4))[-2]:\ + ((mode*)(list += sizeof(mode)))[-1]) diff --git a/Inferno/386/include/ureg.h b/Inferno/386/include/ureg.h new file mode 100644 index 00000000..31c3f961 --- /dev/null +++ b/Inferno/386/include/ureg.h @@ -0,0 +1,25 @@ +struct Ureg +{ + ulong di; /* general registers */ + ulong si; /* ... */ + ulong bp; /* ... */ + ulong nsp; + ulong bx; /* ... */ + ulong dx; /* ... */ + ulong cx; /* ... */ + ulong ax; /* ... */ + ulong gs; /* data segments */ + ulong fs; /* ... */ + ulong es; /* ... */ + ulong ds; /* ... */ + ulong trap; /* trap type */ + ulong ecode; /* error code (or zero) */ + ulong pc; /* pc */ + ulong cs; /* old context */ + ulong flags; /* old flags */ + union { + ulong usp; + ulong sp; + }; + ulong ss; /* old stack segment */ +}; diff --git a/Inferno/arm/include/lib9.h b/Inferno/arm/include/lib9.h new file mode 100644 index 00000000..aa72ded4 --- /dev/null +++ b/Inferno/arm/include/lib9.h @@ -0,0 +1,8 @@ +#include +#include + +/* + * Extensions for Inferno to basic libc.h + */ + +#undef __LITTLE_ENDIAN /* math/dtoa.c; longs in ARM doubles are big-endian */ diff --git a/Inferno/arm/include/u.h b/Inferno/arm/include/u.h new file mode 100644 index 00000000..2ab381d2 --- /dev/null +++ b/Inferno/arm/include/u.h @@ -0,0 +1,65 @@ +#define nil ((void*)0) + +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned long ulong; +typedef unsigned int uint; +typedef signed char schar; +typedef long long vlong; +typedef unsigned long long uvlong; +typedef ushort Rune; +typedef union FPdbleword FPdbleword; +typedef long jmp_buf[2]; +#define JMPBUFSP 0 +#define JMPBUFPC 1 +#define JMPBUFDPC 0 +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned char u8int; +typedef unsigned short u16int; +typedef unsigned int u32int; +typedef unsigned long long u64int; +typedef unsigned long uintptr; + +/* FCR */ +#define FPINEX (1<<20) +#define FPUNFL (1<<19) +#define FPOVFL (1<<18) +#define FPZDIV (1<<17) +#define FPINVAL (1<<16) +#define FPRNR (0<<0) +#define FPRZ (1<<0) +#define FPRPINF (2<<0) +#define FPRNINF (3<<0) +#define FPRMASK (3<<0) +#define FPPEXT 0 +#define FPPSGL 0 +#define FPPDBL 0 +#define FPPMASK 0 +/* FSR */ +#define FPAINEX (1<<4) +#define FPAUNFL (1<<3) +#define FPAOVFL (1<<2) +#define FPAZDIV (1<<1) +#define FPAINVAL (1<<0) +union FPdbleword +{ + double x; + struct { /* big endian (on 7500) */ + ulong hi; + ulong lo; + }; +}; + +typedef char* va_list; +#define va_start(list, start) list =\ + (sizeof(start) < 4?\ + (char*)((int*)&(start)+1):\ + (char*)(&(start)+1)) +#define va_end(list)\ + USED(list) +#define va_arg(list, mode)\ + ((sizeof(mode) == 1)?\ + ((mode*)(list += 4))[-4]:\ + (sizeof(mode) == 2)?\ + ((mode*)(list += 4))[-2]:\ + ((mode*)(list += sizeof(mode)))[-1]) diff --git a/Inferno/arm/include/ureg.h b/Inferno/arm/include/ureg.h new file mode 100644 index 00000000..7784f4d5 --- /dev/null +++ b/Inferno/arm/include/ureg.h @@ -0,0 +1,24 @@ +typedef struct Ureg { + uint r0; + uint r1; + uint r2; + uint r3; + uint r4; + uint r5; + uint r6; + uint r7; + uint r8; + uint r9; + uint r10; + uint r11; + uint r12; + union { + uint r13; + uint sp; + }; + uint r14; + uint link; + uint type; + uint psr; + uint pc; +} Ureg; diff --git a/Inferno/mips/include/lib9.h b/Inferno/mips/include/lib9.h new file mode 100644 index 00000000..9315eea6 --- /dev/null +++ b/Inferno/mips/include/lib9.h @@ -0,0 +1,8 @@ +#include +#include + +/* + * Extensions for Inferno to basic libc.h + */ + +#undef __LITTLE_ENDIAN /* math/dtoa.c; longs in MIPS doubles are big-endian */ diff --git a/Inferno/mips/include/u.h b/Inferno/mips/include/u.h new file mode 100644 index 00000000..9df12bff --- /dev/null +++ b/Inferno/mips/include/u.h @@ -0,0 +1,65 @@ +#define nil ((void*)0) +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned long ulong; +typedef unsigned int uint; +typedef signed char schar; +typedef long long vlong; +typedef unsigned long long uvlong; +typedef unsigned long uintptr; +typedef ushort Rune; +typedef union FPdbleword FPdbleword; +typedef long jmp_buf[2]; +#define JMPBUFSP 0 +#define JMPBUFPC 1 +#define JMPBUFDPC 0 +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned char u8int; +typedef unsigned short u16int; +typedef unsigned int u32int; +typedef unsigned long long u64int; + +/* FCR */ +#define FPINEX (1<<7) +#define FPUNFL (1<<8) +#define FPOVFL (1<<9) +#define FPZDIV (1<<10) +#define FPINVAL (1<<11) +#define FPRNR (0<<0) +#define FPRZ (1<<0) +#define FPRPINF (2<<0) +#define FPRNINF (3<<0) +#define FPRMASK (3<<0) +#define FPPEXT 0 +#define FPPSGL 0 +#define FPPDBL 0 +#define FPPMASK 0 +/* FSR */ +#define FPAINEX (1<<2) +#define FPAOVFL (1<<4) +#define FPAUNFL (1<<3) +#define FPAZDIV (1<<5) +#define FPAINVAL (1<<6) +union FPdbleword +{ + double x; + struct { /* big endian */ + ulong hi; + ulong lo; + }; +}; + +/* stdarg */ +typedef char* va_list; +#define va_start(list, start) list =\ + (sizeof(start) < 4?\ + (char*)((int*)&(start)+1):\ + (char*)(&(start)+1)) +#define va_end(list)\ + USED(list) +#define va_arg(list, mode)\ + ((sizeof(mode) == 1)?\ + ((mode*)(list += 4))[-1]:\ + (sizeof(mode) == 2)?\ + ((mode*)(list += 4))[-1]:\ + ((mode*)(list += sizeof(mode)))[-1]) diff --git a/Inferno/power/include/lib9.h b/Inferno/power/include/lib9.h new file mode 100644 index 00000000..6529a6c7 --- /dev/null +++ b/Inferno/power/include/lib9.h @@ -0,0 +1,4 @@ +#include +#include + +#undef __LITTLE_ENDIAN /* math/dtoa.c; longs in PowerPC doubles are big-endian */ diff --git a/Inferno/power/include/u.h b/Inferno/power/include/u.h new file mode 100644 index 00000000..61e7d236 --- /dev/null +++ b/Inferno/power/include/u.h @@ -0,0 +1,84 @@ +#define nil ((void*)0) +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned long ulong; +typedef unsigned int uint; +typedef signed char schar; +typedef long long vlong; +typedef unsigned long long uvlong; +typedef unsigned long uintptr; +typedef ushort Rune; +typedef union FPdbleword FPdbleword; +typedef long jmp_buf[2]; +#define JMPBUFSP 0 +#define JMPBUFPC 1 +#define JMPBUFDPC 0 +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned char u8int; +typedef unsigned short u16int; +typedef unsigned int u32int; +typedef unsigned long long u64int; + +/* FPSCR */ +#define FPSFX (1<<31) /* exception summary (sticky) */ +#define FPSEX (1<<30) /* enabled exception summary */ +#define FPSVX (1<<29) /* invalid operation exception summary */ +#define FPSOX (1<<28) /* overflow exception OX (sticky) */ +#define FPSUX (1<<27) /* underflow exception UX (sticky) */ +#define FPSZX (1<<26) /* zero divide exception ZX (sticky) */ +#define FPSXX (1<<25) /* inexact exception XX (sticky) */ +#define FPSVXSNAN (1<<24) /* invalid operation exception for SNaN (sticky) */ +#define FPSVXISI (1<<23) /* invalid operation exception for ∞-∞ (sticky) */ +#define FPSVXIDI (1<<22) /* invalid operation exception for ∞/∞ (sticky) */ +#define FPSVXZDZ (1<<21) /* invalid operation exception for 0/0 (sticky) */ +#define FPSVXIMZ (1<<20) /* invalid operation exception for ∞*0 (sticky) */ +#define FPSVXVC (1<<19) /* invalid operation exception for invalid compare (sticky) */ +#define FPSFR (1<<18) /* fraction rounded */ +#define FPSFI (1<<17) /* fraction inexact */ +#define FPSFPRF (1<<16) /* floating point result class */ +#define FPSFPCC (0xF<<12) /* <, >, =, unordered */ +#define FPVXCVI (1<<8) /* enable exception for invalid integer convert (sticky) */ +#define FPVE (1<<7) /* invalid operation exception enable */ +#define FPOVFL (1<<6) /* enable overflow exceptions */ +#define FPUNFL (1<<5) /* enable underflow */ +#define FPZDIV (1<<4) /* enable zero divide */ +#define FPINEX (1<<3) /* enable inexact exceptions */ +#define FPRMASK (3<<0) /* rounding mode */ +#define FPRNR (0<<0) +#define FPRZ (1<<0) +#define FPRPINF (2<<0) +#define FPRNINF (3<<0) +#define FPPEXT 0 +#define FPPSGL 0 +#define FPPDBL 0 +#define FPPMASK 0 +#define FPINVAL FPVE + +#define FPAOVFL FPSOX +#define FPAINEX FPSXX +#define FPAUNFL FPSUX +#define FPAZDIV FPSZX +#define FPAINVAL FPSVX + +union FPdbleword +{ + double x; + struct { /* big endian */ + ulong hi; + ulong lo; + }; +}; + +typedef char* va_list; +#define va_start(list, start) list =\ + (sizeof(start) < 4?\ + (char*)((int*)&(start)+1):\ + (char*)(&(start)+1)) +#define va_end(list)\ + USED(list) +#define va_arg(list, mode)\ + ((sizeof(mode) == 1)?\ + ((mode*)(list += 4))[-1]:\ + (sizeof(mode) == 2)?\ + ((mode*)(list += 4))[-1]:\ + ((mode*)(list += sizeof(mode)))[-1]) diff --git a/Inferno/power/include/ureg.h b/Inferno/power/include/ureg.h new file mode 100644 index 00000000..7ccdb492 --- /dev/null +++ b/Inferno/power/include/ureg.h @@ -0,0 +1,43 @@ +struct Ureg +{ + ulong cause; + union { ulong srr1; ulong status;}; + ulong pc; /* SRR0 */ + ulong pad; + ulong lr; + ulong cr; + ulong xer; + ulong ctr; + ulong r0; + union{ ulong r1; ulong sp; ulong usp; }; + ulong r2; + ulong r3; + ulong r4; + ulong r5; + ulong r6; + ulong r7; + ulong r8; + ulong r9; + ulong r10; + ulong r11; + ulong r12; + ulong r13; + ulong r14; + ulong r15; + ulong r16; + ulong r17; + ulong r18; + ulong r19; + ulong r20; + ulong r21; + ulong r22; + ulong r23; + ulong r24; + ulong r25; + ulong r26; + ulong r27; + ulong r28; + ulong r29; + ulong r30; + ulong r31; +}; diff --git a/Inferno/sparc/include/lib9.h b/Inferno/sparc/include/lib9.h new file mode 100644 index 00000000..1a5b3e92 --- /dev/null +++ b/Inferno/sparc/include/lib9.h @@ -0,0 +1,9 @@ +#include +#include + + +/* + * Extensions for Inferno to basic libc.h + */ + +#undef __LITTLE_ENDIAN /* math/dtoa.c; longs in SPARC doubles are big-endian */ diff --git a/Inferno/sparc/include/u.h b/Inferno/sparc/include/u.h new file mode 100644 index 00000000..6c0bd3f2 --- /dev/null +++ b/Inferno/sparc/include/u.h @@ -0,0 +1,64 @@ +#define nil ((void*)0) +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned long ulong; +typedef unsigned int uint; +typedef signed char schar; +typedef long long vlong; +typedef unsigned long long uvlong; +typedef unsigned long uintptr; +typedef ushort Rune; +typedef union FPdbleword FPdbleword; +typedef long jmp_buf[2]; +#define JMPBUFSP 0 +#define JMPBUFPC 1 +#define JMPBUFDPC (-8) +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned char u8int; +typedef unsigned short u16int; +typedef unsigned int u32int; +typedef unsigned long long u64int; + +/* FCR */ +#define FPINEX (1<<23) +#define FPOVFL (1<<26) +#define FPUNFL (1<<25) +#define FPZDIV (1<<24) +#define FPRNR (0<<30) +#define FPRZ (1<<30) +#define FPINVAL (1<<27) +#define FPRPINF (2<<30) +#define FPRNINF (3<<30) +#define FPRMASK (3<<30) +#define FPPEXT 0 +#define FPPSGL 0 +#define FPPDBL 0 +#define FPPMASK 0 +/* FSR */ +#define FPAINEX (1<<5) +#define FPAZDIV (1<<6) +#define FPAUNFL (1<<7) +#define FPAOVFL (1<<8) +#define FPAINVAL (1<<9) +union FPdbleword +{ + double x; + struct { /* big endian */ + ulong hi; + ulong lo; + }; +}; + +typedef char* va_list; +#define va_start(list, start) list =\ + (sizeof(start) < 4?\ + (char*)((int*)&(start)+1):\ + (char*)(&(start)+1)) +#define va_end(list)\ + USED(list) +#define va_arg(list, mode)\ + ((sizeof(mode) == 1)?\ + ((mode*)(list += 4))[-1]:\ + (sizeof(mode) == 2)?\ + ((mode*)(list += 4))[-1]:\ + ((mode*)(list += sizeof(mode)))[-1]) diff --git a/Inferno/sparc/include/ureg.h b/Inferno/sparc/include/ureg.h new file mode 100644 index 00000000..8433eec9 --- /dev/null +++ b/Inferno/sparc/include/ureg.h @@ -0,0 +1,45 @@ +struct Ureg +{ + ulong r0; /* unnecessary; just for symmetry */ + union{ + ulong sp; /* r1 */ + ulong usp; /* r1 */ + ulong r1; + }; + ulong r2; + ulong r3; + ulong r4; + ulong r5; + ulong r6; + ulong r7; + ulong r8; + ulong r9; + ulong r10; + ulong r11; + ulong r12; + ulong r13; + ulong r14; + ulong r15; + ulong r16; + ulong r17; + ulong r18; + ulong r19; + ulong r20; + ulong r21; + ulong r22; + ulong r23; + ulong r24; + ulong r25; + ulong r26; + ulong r27; + ulong r28; + ulong r29; + ulong r30; + ulong r31; + ulong y; + ulong tbr; + ulong psr; + ulong npc; + ulong pc; + ulong pad; /* so structure is double word aligned */ +}; diff --git a/Inferno/thumb/include/lib9.h b/Inferno/thumb/include/lib9.h new file mode 100644 index 00000000..aa72ded4 --- /dev/null +++ b/Inferno/thumb/include/lib9.h @@ -0,0 +1,8 @@ +#include +#include + +/* + * Extensions for Inferno to basic libc.h + */ + +#undef __LITTLE_ENDIAN /* math/dtoa.c; longs in ARM doubles are big-endian */ diff --git a/Inferno/thumb/include/u.h b/Inferno/thumb/include/u.h new file mode 100644 index 00000000..2ab381d2 --- /dev/null +++ b/Inferno/thumb/include/u.h @@ -0,0 +1,65 @@ +#define nil ((void*)0) + +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned long ulong; +typedef unsigned int uint; +typedef signed char schar; +typedef long long vlong; +typedef unsigned long long uvlong; +typedef ushort Rune; +typedef union FPdbleword FPdbleword; +typedef long jmp_buf[2]; +#define JMPBUFSP 0 +#define JMPBUFPC 1 +#define JMPBUFDPC 0 +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned char u8int; +typedef unsigned short u16int; +typedef unsigned int u32int; +typedef unsigned long long u64int; +typedef unsigned long uintptr; + +/* FCR */ +#define FPINEX (1<<20) +#define FPUNFL (1<<19) +#define FPOVFL (1<<18) +#define FPZDIV (1<<17) +#define FPINVAL (1<<16) +#define FPRNR (0<<0) +#define FPRZ (1<<0) +#define FPRPINF (2<<0) +#define FPRNINF (3<<0) +#define FPRMASK (3<<0) +#define FPPEXT 0 +#define FPPSGL 0 +#define FPPDBL 0 +#define FPPMASK 0 +/* FSR */ +#define FPAINEX (1<<4) +#define FPAUNFL (1<<3) +#define FPAOVFL (1<<2) +#define FPAZDIV (1<<1) +#define FPAINVAL (1<<0) +union FPdbleword +{ + double x; + struct { /* big endian (on 7500) */ + ulong hi; + ulong lo; + }; +}; + +typedef char* va_list; +#define va_start(list, start) list =\ + (sizeof(start) < 4?\ + (char*)((int*)&(start)+1):\ + (char*)(&(start)+1)) +#define va_end(list)\ + USED(list) +#define va_arg(list, mode)\ + ((sizeof(mode) == 1)?\ + ((mode*)(list += 4))[-4]:\ + (sizeof(mode) == 2)?\ + ((mode*)(list += 4))[-2]:\ + ((mode*)(list += sizeof(mode)))[-1]) diff --git a/Inferno/thumb/include/ureg.h b/Inferno/thumb/include/ureg.h new file mode 100644 index 00000000..7784f4d5 --- /dev/null +++ b/Inferno/thumb/include/ureg.h @@ -0,0 +1,24 @@ +typedef struct Ureg { + uint r0; + uint r1; + uint r2; + uint r3; + uint r4; + uint r5; + uint r6; + uint r7; + uint r8; + uint r9; + uint r10; + uint r11; + uint r12; + union { + uint r13; + uint sp; + }; + uint r14; + uint link; + uint type; + uint psr; + uint pc; +} Ureg; diff --git a/Irix/mips/bin/awk b/Irix/mips/bin/awk new file mode 100755 index 00000000..af5546e9 Binary files /dev/null and b/Irix/mips/bin/awk differ diff --git a/Irix/mips/bin/mk b/Irix/mips/bin/mk new file mode 100755 index 00000000..6c822e2a Binary files /dev/null and b/Irix/mips/bin/mk differ diff --git a/Irix/mips/bin/mkext b/Irix/mips/bin/mkext new file mode 100755 index 00000000..2ea0ad21 Binary files /dev/null and b/Irix/mips/bin/mkext differ diff --git a/Irix/mips/bin/yacc b/Irix/mips/bin/yacc new file mode 100755 index 00000000..46dabda6 Binary files /dev/null and b/Irix/mips/bin/yacc differ diff --git a/Irix/mips/include/lib9.h b/Irix/mips/include/lib9.h new file mode 100644 index 00000000..11b06c13 --- /dev/null +++ b/Irix/mips/include/lib9.h @@ -0,0 +1,463 @@ +#define _LARGEFILE64_SOURCE 1 +#define _FILE_OFFSET_BITS 64 +#include +#include +#define _POSIX_SOURCE +#include +#include +#include +#include +#include +#include "math.h" +#include +#include +#include +#include +#include + +#define getwd infgetwd + +#ifndef EMU +typedef struct Proc Proc; +#endif + +#define nil ((void*)0) + +typedef unsigned char uchar; +typedef signed char schar; +typedef unsigned short Rune; +typedef long long int vlong; +typedef unsigned long long int uvlong; +typedef unsigned int u32int; +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned short u16int; +typedef unsigned char u8int; +typedef unsigned long uintptr; + +#define USED(x) if(x){}else{} +#define SET(x) + +#undef nelem +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#undef offsetof +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) +#undef assert +#define assert(x) if(x){}else _assert("x") + +/* + * most mem and string routines are declared by ANSI/POSIX files above + */ + +extern char* strecpy(char*, char*, char*); +extern char* strdup(const char*); +extern int cistrncmp(char*, char*, int); +extern int cistrcmp(char*, char*); +extern char* cistrstr(char*, char*); +extern int tokenize(char*, char**, int); + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80 /* decoding error in UTF */ +}; + +/* + * rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern int runelen(long); +extern int runenlen(Rune*, int); +extern int fullrune(char*, int); +extern int utflen(char*); +extern int utfnlen(char*, long); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); +extern char* utfutf(char*, char*); +extern char* utfecpy(char*, char*, char*); + +extern Rune* runestrcat(Rune*, Rune*); +extern Rune* runestrchr(Rune*, Rune); +extern int runestrcmp(Rune*, Rune*); +extern Rune* runestrcpy(Rune*, Rune*); +extern Rune* runestrncpy(Rune*, Rune*, long); +extern Rune* runestrecpy(Rune*, Rune*, Rune*); +extern Rune* runestrdup(Rune*); +extern Rune* runestrncat(Rune*, Rune*, long); +extern int runestrncmp(Rune*, Rune*, long); +extern Rune* runestrrchr(Rune*, Rune); +extern long runestrlen(Rune*); +extern Rune* runestrstr(Rune*, Rune*); + +extern Rune tolowerrune(Rune); +extern Rune totitlerune(Rune); +extern Rune toupperrune(Rune); +extern int isalpharune(Rune); +extern int islowerrune(Rune); +extern int isspacerune(Rune); +extern int istitlerune(Rune); +extern int isupperrune(Rune); + +/* + * malloc + */ +extern void* malloc(size_t); +extern void* mallocz(ulong, int); +extern void free(void*); +extern ulong msize(void*); +extern void* calloc(size_t, size_t); +extern void* realloc(void*, size_t); +extern void setmalloctag(void*, ulong); +extern void setrealloctag(void*, ulong); +extern ulong getmalloctag(void*); +extern ulong getrealloctag(void*); +extern void* malloctopoolblock(void*); + +/* + * print routines + */ +typedef struct Fmt Fmt; +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + ulong flags; +}; +extern vlong osnsec(void); +#define nsec osnsec + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + + FmtFlag = FmtByte << 1 +}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); +extern int runefmtstrinit(Fmt*); +extern Rune* runefmtstrflush(Fmt*); + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int dorfmt(Fmt*, Rune*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); +extern int fmtrunestrcpy(Fmt*, Rune*); +/* + * error string for %r + * supplied on per os basis, not part of fmt library + */ +extern int errfmt(Fmt *f); + +/* + * quoted strings + */ +extern char *unquotestrdup(char*); +extern Rune *unquoterunestrdup(Rune*); +extern char *quotestrdup(char*); +extern Rune *quoterunestrdup(Rune*); +extern int quotestrfmt(Fmt*); +extern int quoterunestrfmt(Fmt*); +extern void quotefmtinstall(void); +extern int (*doquote)(int); + +/* + * random number + */ +extern ulong truerand(void); +extern ulong ntruerand(ulong); + +/* + * math + */ +extern int isNaN(double); +extern int isInf(double, int); + +/* + * Time-of-day + */ + +typedef struct Tm Tm; +struct Tm { + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; + int yday; + char zone[4]; + int tzoff; +}; + +/* + * one-of-a-kind + */ +extern void _assert(char*); +extern double charstod(int(*)(void*), void*); +extern char* cleanname(char*); +extern ulong getcallerpc(void*); +extern int getfields(char*, char**, int, int, char*); +extern char* getuser(void); +extern char* getwd(char*, int); +extern double ipow10(int); +#define pow10 infpow10 +extern double pow10(int); +extern vlong strtoll(const char*, char**, int); +extern uvlong strtoull(const char*, char**, int); +extern void sysfatal(char*, ...); +extern int dec64(uchar*, int, char*, int); +extern int enc64(char*, int, uchar*, int); +extern int dec32(uchar*, int, char*, int); +extern int enc32(char*, int, uchar*, int); +extern int dec16(uchar*, int, char*, int); +extern int enc16(char*, int, uchar*, int); +extern int encodefmt(Fmt*); + +/* + * synchronization + */ +typedef +struct Lock { + ulong val; + int pid; +} Lock; + +extern ulong _tas(ulong*); + +extern void lock(Lock*); +extern void unlock(Lock*); +extern int canlock(Lock*); + +typedef struct QLock QLock; +struct QLock +{ + Lock use; /* to access Qlock structure */ + Proc *head; /* next process waiting for object */ + Proc *tail; /* last process waiting for object */ + int locked; /* flag */ +}; + +extern void qlock(QLock*); +extern void qunlock(QLock*); +extern int canqlock(QLock*); +extern void _qlockinit(ulong (*)(ulong, ulong)); /* called only by the thread library */ + +typedef +struct RWLock +{ + Lock l; /* Lock modify lock */ + QLock x; /* Mutual exclusion lock */ + QLock k; /* Lock for waiting writers */ + int readers; /* Count of readers in lock */ +} RWLock; + +extern int canrlock(RWLock*); +extern int canwlock(RWLock*); +extern void rlock(RWLock*); +extern void runlock(RWLock*); +extern void wlock(RWLock*); +extern void wunlock(RWLock*); + +/* + * network dialing + */ +#define NETPATHLEN 40 + +/* + * system calls + * + */ +#define STATMAX 65535U /* max length of machine-independent stat structure */ +#define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */ +#define ERRMAX 128 /* max length of error string */ + +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MCACHE 0x0010 /* cache some data */ +#define MMASK 0x0017 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ +#define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ + +#define AEXIST 0 /* accessible: exists */ +#define AEXEC 1 /* execute access */ +#define AWRITE 2 /* write access */ +#define AREAD 4 /* read access */ + +/* bits in Qid.type */ +#define QTDIR 0x80 /* type bit for directories */ +#define QTAPPEND 0x40 /* type bit for append only files */ +#define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ +#define QTAUTH 0x08 /* type bit for authentication file */ +#define QTFILE 0x00 /* plain file */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMAUTH 0x08000000 /* mode bit for authentication file */ +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +typedef +struct Qid +{ + uvlong path; + ulong vers; + uchar type; +} Qid; + +typedef +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +} Dir; + +extern Dir* dirstat(char*); +extern Dir* dirfstat(int); +extern int dirwstat(char*, Dir*); +extern int dirfwstat(int, Dir*); +extern long dirread(int, Dir**); +extern void nulldir(Dir*); +extern long dirreadall(int, Dir**); + +typedef +struct Waitmsg +{ + int pid; /* of loved one */ + ulong time[3]; /* of loved one & descendants */ + char *msg; +} Waitmsg; + +extern void _exits(char*); + +extern void exits(char*); +extern int create(char*, int, int); +extern int errstr(char*, uint); + +extern void perror(const char*); +extern long readn(int, void*, long); +extern int remove(const char*); +extern void rerrstr(char*, uint); +extern vlong seek(int, vlong, int); +extern void werrstr(char*, ...); + +extern char *argv0; +#define ARGBEGIN for((argv0||(argv0=*argv)),argv++,argc--;\ + argv[0] && argv[0][0]=='-' && argv[0][1];\ + argc--, argv++) {\ + char *_args, *_argt;\ + Rune _argc;\ + _args = &argv[0][1];\ + if(_args[0]=='-' && _args[1]==0){\ + argc--; argv++; break;\ + }\ + _argc = 0;\ + while(*_args && (_args += chartorune(&_argc, _args)))\ + switch(_argc) +#define ARGEND SET(_argt);USED(_argt);USED(_argc); USED(_args);}USED(argv); USED(argc); +#define ARGF() (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): 0)) +#define EARGF(x) (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0))) + +#define ARGC() _argc + +/* + * Extensions for Inferno to basic libc.h + */ + +#define setbinmode() + +/* + * Extensions for emu kernel emulation + */ +#ifdef EMU + +extern Proc** Xup; +#define up (*Xup) + +typedef struct FPU FPU; + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +struct FPU +{ + ulong fcr31; +}; + +typedef sigjmp_buf osjmpbuf; +#define ossetjmp(buf) sigsetjmp(buf, 1) + +#endif diff --git a/LICENCE b/LICENCE new file mode 100644 index 00000000..fc565e3f --- /dev/null +++ b/LICENCE @@ -0,0 +1,37 @@ +Previously Vita Nuova made the Inferno® software available under its +`Vita Nuova Liberal Source Licence' of May 2003. Because a proliferation +of licences for Open/Free software is increasingly seen as undesirable, and +because the Free Software Foundation found that licence incompatible with the GPL, +Vita Nuova now relicenses the software under a mixture of existing Free Software +licences. + +Under Vita Nuova's `dual-licence' scheme, the Inferno® software is made +available on the following terms. Files and directories in the distribution +contain NOTICE files that give (or refer to) the terms of several Free Software +licences, listed here in increasing order of liberality: + - GNU General Public License (`GPL') + - GNU Lesser General Public License (`LGPL') + - Lucent Public Licence 1.02 + - a Vita Nuova `free for all' licence based on the so-called `MIT template' +The text of each licence can be found in lib/legal. + +Some portions of the software are subject to GPL and LGPL. +Through their `copyleft' clauses impose some degree of reciprocity in terms of (for instance) +making changes and additions available in source form if you distribute software +that is subject to those licences, but only to the extent the licences require. + +Other portions are subject to the Lucent Public or Vita Nuova `free for all' licence +and do not impose `copyleft' conditions. + +For instance, the native and hosted kernels are `free for all', as are most of the +supporting libraries, but the virtual machine library and Limbo library modules are LGPL, +and the applications, including the Limbo compiler, are GPL. + +The particular choices are intended to maximise the freedom to deploy the system +in proprietary settings, and preventing clashes with licence terms for related systems +such as Plan 9 (for kernel and library code), whilst protecting the investment in +Free Software made by Vita Nuova and other contributors. + +If the terms referenced by the NOTICE files are NOT acceptable (or you just fancy +a quiet life without having to worry about them), THEN you can obtain +a more conventional Commercial Licence from Vita Nuova (support@vitanuova.com). diff --git a/Linux/386/bin/data2c b/Linux/386/bin/data2c new file mode 100755 index 00000000..8bebd48a Binary files /dev/null and b/Linux/386/bin/data2c differ diff --git a/Linux/386/bin/mk b/Linux/386/bin/mk new file mode 100755 index 00000000..792062b5 Binary files /dev/null and b/Linux/386/bin/mk differ diff --git a/Linux/386/bin/yacc b/Linux/386/bin/yacc new file mode 100755 index 00000000..396cd46e Binary files /dev/null and b/Linux/386/bin/yacc differ diff --git a/Linux/386/include/fpuctl.h b/Linux/386/include/fpuctl.h new file mode 100644 index 00000000..8389f6ee --- /dev/null +++ b/Linux/386/include/fpuctl.h @@ -0,0 +1,76 @@ +/* + * Linux 386 fpu support + * Mimic Plan9 floating point support + */ + +static void +setfcr(ulong fcr) +{ + __asm__( "xorb $0x3f, %%al\n\t" + "pushw %%ax\n\t" + "fwait\n\t" + "fldcw (%%esp)\n\t" + "popw %%ax\n\t" + : /* no output */ + : "al" (fcr) + ); +} + +static ulong +getfcr(void) +{ + ulong fcr = 0; + + __asm__( "pushl %%eax\n\t" + "fwait\n\t" + "fstcw (%%esp)\n\t" + "popl %%eax\n\t" + "xorb $0x3f, %%al\n\t" + : "=a" (fcr) + : "eax" (fcr) + ); + return fcr; +} + +static ulong +getfsr(void) +{ + ulong fsr = -1; + + __asm__( "fwait\n\t" + "fstsw (%%eax)\n\t" + "movl (%%eax), %%eax\n\t" + "andl $0xffff, %%eax\n\t" + : "=a" (fsr) + : "eax" (&fsr) + ); + return fsr; +} + +static void +setfsr(ulong fsr) +{ + __asm__("fclex\n\t"); +} + +/* FCR */ +#define FPINEX (1<<5) +#define FPUNFL ((1<<4)|(1<<1)) +#define FPOVFL (1<<3) +#define FPZDIV (1<<2) +#define FPINVAL (1<<0) +#define FPRNR (0<<10) +#define FPRZ (3<<10) +#define FPRPINF (2<<10) +#define FPRNINF (1<<10) +#define FPRMASK (3<<10) +#define FPPEXT (3<<8) +#define FPPSGL (0<<8) +#define FPPDBL (2<<8) +#define FPPMASK (3<<8) +/* FSR */ +#define FPAINEX FPINEX +#define FPAOVFL FPOVFL +#define FPAUNFL FPUNFL +#define FPAZDIV FPZDIV +#define FPAINVAL FPINVAL diff --git a/Linux/386/include/lib9.h b/Linux/386/include/lib9.h new file mode 100644 index 00000000..66c037b6 --- /dev/null +++ b/Linux/386/include/lib9.h @@ -0,0 +1,488 @@ +/* define _BSD_SOURCE to use ISO C, POSIX, and 4.3BSD things. */ +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#define _LARGEFILE_SOURCE 1 +#define _LARGEFILE64_SOURCE 1 +#define _FILE_OFFSET_BITS 64 +#include +#include +#include +#include +#define sync __os_sync +#include +#undef sync +#include +#define __NO_STRING_INLINES +#include +#include "math.h" +#include +#include +#include +#include + +#define getwd infgetwd + +#ifndef EMU +typedef struct Proc Proc; +#endif + +/* + * math module dtoa + * #define __LITTLE_ENDIAN /usr/include/endian.h under linux + */ + +#define nil ((void*)0) + +typedef unsigned char uchar; +typedef signed char schar; +typedef unsigned short Rune; +typedef long long int vlong; +typedef unsigned long long int uvlong; +typedef unsigned int u32int; +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned short u16int; +typedef unsigned char u8int; +typedef unsigned long uintptr; + +#define USED(x) if(x){}else{} +#define SET(x) + +#undef nelem +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#undef offsetof +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) +#undef assert +#define assert(x) if(x){}else _assert("x") + +/* + * most mem and string routines are declared by ANSI/POSIX files above + */ + +extern char* strecpy(char*, char*, char*); +extern char* strdup(const char*); +extern int cistrncmp(char*, char*, int); +extern int cistrcmp(char*, char*); +extern char* cistrstr(char*, char*); +extern int tokenize(char*, char**, int); + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80 /* decoding error in UTF */ +}; + +/* + * rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern int runelen(long); +extern int runenlen(Rune*, int); +extern int fullrune(char*, int); +extern int utflen(char*); +extern int utfnlen(char*, long); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); +extern char* utfutf(char*, char*); +extern char* utfecpy(char*, char*, char*); + +extern Rune* runestrcat(Rune*, Rune*); +extern Rune* runestrchr(Rune*, Rune); +extern int runestrcmp(Rune*, Rune*); +extern Rune* runestrcpy(Rune*, Rune*); +extern Rune* runestrncpy(Rune*, Rune*, long); +extern Rune* runestrecpy(Rune*, Rune*, Rune*); +extern Rune* runestrdup(Rune*); +extern Rune* runestrncat(Rune*, Rune*, long); +extern int runestrncmp(Rune*, Rune*, long); +extern Rune* runestrrchr(Rune*, Rune); +extern long runestrlen(Rune*); +extern Rune* runestrstr(Rune*, Rune*); + +extern Rune tolowerrune(Rune); +extern Rune totitlerune(Rune); +extern Rune toupperrune(Rune); +extern int isalpharune(Rune); +extern int islowerrune(Rune); +extern int isspacerune(Rune); +extern int istitlerune(Rune); +extern int isupperrune(Rune); + +/* + * malloc + */ +extern void* malloc(size_t); +extern void* mallocz(ulong, int); +extern void free(void*); +extern ulong msize(void*); +extern void* calloc(size_t, size_t); +extern void* realloc(void*, size_t); +extern void setmalloctag(void*, ulong); +extern void setrealloctag(void*, ulong); +extern ulong getmalloctag(void*); +extern ulong getrealloctag(void*); +extern void* malloctopoolblock(void*); + +/* + * print routines + */ +typedef struct Fmt Fmt; +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + ulong flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + + FmtFlag = FmtByte << 1 +}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); +extern int runefmtstrinit(Fmt*); +extern Rune* runefmtstrflush(Fmt*); + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int dorfmt(Fmt*, Rune*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); +extern int fmtrunestrcpy(Fmt*, Rune*); +/* + * error string for %r + * supplied on per os basis, not part of fmt library + */ +extern int errfmt(Fmt *f); + +/* + * quoted strings + */ +extern char *unquotestrdup(char*); +extern Rune *unquoterunestrdup(Rune*); +extern char *quotestrdup(char*); +extern Rune *quoterunestrdup(Rune*); +extern int quotestrfmt(Fmt*); +extern int quoterunestrfmt(Fmt*); +extern void quotefmtinstall(void); +extern int (*doquote)(int); + +/* + * random number + */ +extern ulong truerand(void); +extern ulong ntruerand(ulong); + +/* + * math + */ +extern int isNaN(double); +extern int isInf(double, int); + +/* + * Time-of-day + */ + +typedef struct Tm Tm; +struct Tm { + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; + int yday; + char zone[4]; + int tzoff; +}; +extern vlong osnsec(void); +#define nsec osnsec + +/* + * one-of-a-kind + */ +extern void _assert(char*); +extern double charstod(int(*)(void*), void*); +extern char* cleanname(char*); +extern ulong getcallerpc(void*); +extern int getfields(char*, char**, int, int, char*); +extern char* getuser(void); +extern char* getwd(char*, int); +extern double ipow10(int); +#define pow10 infpow10 +extern double pow10(int); +extern vlong strtoll(const char*, char**, int); +extern uvlong strtoull(const char*, char**, int); +extern void sysfatal(char*, ...); +extern int dec64(uchar*, int, char*, int); +extern int enc64(char*, int, uchar*, int); +extern int dec32(uchar*, int, char*, int); +extern int enc32(char*, int, uchar*, int); +extern int dec16(uchar*, int, char*, int); +extern int enc16(char*, int, uchar*, int); +extern int encodefmt(Fmt*); + +/* + * synchronization + */ +typedef +struct Lock { + ulong val; + int pid; +} Lock; + +extern ulong _tas(ulong*); + +extern void lock(Lock*); +extern void unlock(Lock*); +extern int canlock(Lock*); + +typedef struct QLock QLock; +struct QLock +{ + Lock use; /* to access Qlock structure */ + Proc *head; /* next process waiting for object */ + Proc *tail; /* last process waiting for object */ + int locked; /* flag */ +}; + +extern void qlock(QLock*); +extern void qunlock(QLock*); +extern int canqlock(QLock*); +extern void _qlockinit(ulong (*)(ulong, ulong)); /* called only by the thread library */ + +typedef +struct RWLock +{ + Lock l; /* Lock modify lock */ + QLock x; /* Mutual exclusion lock */ + QLock k; /* Lock for waiting writers */ + int readers; /* Count of readers in lock */ +} RWLock; + +extern int canrlock(RWLock*); +extern int canwlock(RWLock*); +extern void rlock(RWLock*); +extern void runlock(RWLock*); +extern void wlock(RWLock*); +extern void wunlock(RWLock*); + +/* + * network dialing + */ +#define NETPATHLEN 40 + +/* + * system calls + * + */ +#define STATMAX 65535U /* max length of machine-independent stat structure */ +#define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */ +#define ERRMAX 128 /* max length of error string */ + +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MCACHE 0x0010 /* cache some data */ +#define MMASK 0x0017 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ +#define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ + +#define AEXIST 0 /* accessible: exists */ +#define AEXEC 1 /* execute access */ +#define AWRITE 2 /* write access */ +#define AREAD 4 /* read access */ + +/* bits in Qid.type */ +#define QTDIR 0x80 /* type bit for directories */ +#define QTAPPEND 0x40 /* type bit for append only files */ +#define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ +#define QTAUTH 0x08 /* type bit for authentication file */ +#define QTFILE 0x00 /* plain file */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMAUTH 0x08000000 /* mode bit for authentication file */ +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +typedef +struct Qid +{ + uvlong path; + ulong vers; + uchar type; +} Qid; + +typedef +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +} Dir; + +extern Dir* dirstat(char*); +extern Dir* dirfstat(int); +extern int dirwstat(char*, Dir*); +extern int dirfwstat(int, Dir*); +extern long dirread(int, Dir**); +extern void nulldir(Dir*); +extern long dirreadall(int, Dir**); + +typedef +struct Waitmsg +{ + int pid; /* of loved one */ + ulong time[3]; /* of loved one & descendants */ + char *msg; +} Waitmsg; + +extern void _exits(char*); + +extern void exits(char*); +extern int create(char*, int, int); +extern int errstr(char*, uint); + +extern void perror(const char*); +extern long readn(int, void*, long); +extern int remove(const char*); +extern void rerrstr(char*, uint); +extern vlong seek(int, vlong, int); +extern int segflush(void*, ulong); +extern void werrstr(char*, ...); + +extern char *argv0; +#define ARGBEGIN for((argv0||(argv0=*argv)),argv++,argc--;\ + argv[0] && argv[0][0]=='-' && argv[0][1];\ + argc--, argv++) {\ + char *_args, *_argt;\ + Rune _argc;\ + _args = &argv[0][1];\ + if(_args[0]=='-' && _args[1]==0){\ + argc--; argv++; break;\ + }\ + _argc = 0;\ + while(*_args && (_args += chartorune(&_argc, _args)))\ + switch(_argc) +#define ARGEND SET(_argt);USED(_argt);USED(_argc); USED(_args);}USED(argv); USED(argc); +#define ARGF() (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): 0)) +#define EARGF(x) (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0))) + +#define ARGC() _argc + +/* + * Extensions for Inferno to basic libc.h + */ + +#define setbinmode() + +/* + * Extensions for emu kernel emulation + */ +#ifdef EMU + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +typedef struct FPU FPU; +struct FPU +{ + uchar env[28]; +}; + +/* + * Later versions of Linux seemed to need large stack for gethostbyname() + * so we had this at 128k, which is excessive. More recently, we've + * reduced it again after testing stack usage by gethostbyname. + */ +#define KSTACK (16 * 1024) + +static __inline Proc *getup(void) { + Proc *p; + __asm__( "movl %%esp, %%eax\n\t" + : "=a" (p) + ); + return *(Proc **)((unsigned long)p & ~(KSTACK - 1)); +}; + +#define up (getup()) + +typedef sigjmp_buf osjmpbuf; +#define ossetjmp(buf) sigsetjmp(buf, 1) + +#endif diff --git a/MacOSX/README b/MacOSX/README new file mode 100644 index 00000000..3fe0c7fc --- /dev/null +++ b/MacOSX/README @@ -0,0 +1,66 @@ +12-Dec-2003 +Emu can use wm with Apple's X11R6 again. + +08-Dec-2003 +Inferno Services (6660, 6666-6676, 2202) + +26-Aug-2003 +In order to run and use Inferno services appropriately on OSX, you need to ensure that the Sharing System Preferences include an entry in the Firewall section that is turned on and looks like the following line: + +Inferno services (6660, 6666-6674) + + +05-Jul-2003 +In order to support 1.4, the build will now use the default 'mk' provided with the Inferno distribution. You can also copy tcshrc to the Inferno ROOT and source the file from that directory before running or building emu. Modified mkfiles and sources are now available in the srcMacOSX archive. You may also want to change 'mkconfig' with the following diff + +% cvs diff -r 1.1 mkconfig +Index: mkconfig +=================================================================== +RCS file: /usr/local/Repository/cvs/vitanuova/inferno_v14/mkconfig,v +retrieving revision 1.1 +retrieving revision 1.3 +diff -r1.1 -r1.3 +7c7 +< ROOT=/usr/inferno +--- +> ROOT=$ROOT +17c17 +< SYSHOST=Plan9 # build system OS type (Hp, Inferno, Irix, Linux, Nt, Plan9, Solaris) +--- +> SYSHOST=MacOSX # build system OS type (Hp, Inferno, Irix, Linux, Nt, Plan9, Solaris) +25c25 +< OBJTYPE=$objtype +--- +> OBJTYPE=power + + + +16-May-2003 + +The build now uses the open sourced 'mk' from http://www.pdos.lcs.mit.edu/~rsc/software/ + +There is a file .tcshrc at the root of this tree (..) that can be used to set up the environment required to build emu. + + + +28-Feb-2001 + +The initial port has been provided by Corpus Callosum Corporation. + +This port of Inferno for Darwin, Mac OS X, and Mac OS X Server hosted environments currently provides base 'emu' support. Wm, audio, and other services are not currently ported. It has only been tested on UFS partitions and will require further testing for HFS+ support. + +Emu -c1 currently errors out as "Illegal instruction" + +Various malloc/free (from libc/System on Mac OS X/Darwin) warnings should be removed in the following version. + +eia devices will recognize /dev/ttyd.irda and /dev/ttyd.modem though both are completely untested (need to get updates to the RCX interface to test irda). + +The following modification was added to various 'mkfile' to support required redefinition of varios core functions. + + <$ROOT/mkfiles/mkalloc-$SYSHOST-$OBJTYPE + +Some of the base libraries and utils will have *.pbproj files which were used to bootstrap the initial build system on Mac OS X (Public Beta). + + + +"Mac OS X" and "Mac OS X Server" are trade marks of Apple Computer, Inc. \ No newline at end of file diff --git a/MacOSX/power/bin/data2c b/MacOSX/power/bin/data2c new file mode 100755 index 00000000..04622c35 Binary files /dev/null and b/MacOSX/power/bin/data2c differ diff --git a/MacOSX/power/bin/mk b/MacOSX/power/bin/mk new file mode 100755 index 00000000..2be3415d Binary files /dev/null and b/MacOSX/power/bin/mk differ diff --git a/MacOSX/power/bin/yacc b/MacOSX/power/bin/yacc new file mode 100755 index 00000000..fbb0bc16 Binary files /dev/null and b/MacOSX/power/bin/yacc differ diff --git a/MacOSX/power/include/fpuctl.h b/MacOSX/power/include/fpuctl.h new file mode 100644 index 00000000..c1a83845 --- /dev/null +++ b/MacOSX/power/include/fpuctl.h @@ -0,0 +1,81 @@ +/* + * MacOSX/Darwin ppc fpu support + * Mimic Plan9 floating point support + */ + +#include + +static __inline__ ulong +getfcr(void) +{ + ppc_fp_scr_t fpscr = get_fp_scr(); + return ((ulong *)&fpscr)[1]; +} + +ulong +getfsr(void) +{ + ppc_fp_scr_t fpscr = get_fp_scr(); + return ((ulong *)&fpscr)[1]; +} + +void +setfsr(ulong fsr) +{ + ppc_fp_scr_t fpscr; + // fpscr = get_fp_scr(); + (((ulong *)&fpscr)[1]) = fsr; + set_fp_scr(fpscr); +} + +void +setfcr(ulong fcr) +{ + ppc_fp_scr_t fpscr; + // fpscr = get_fp_scr(); + (((ulong *)&fpscr)[1]) = fcr; + set_fp_scr(fpscr); +} + +/* FPSCR */ +#define FPSFX (1<<31) /* exception summary (sticky) */ +#define FPSEX (1<<30) /* enabled exception summary */ +#define FPSVX (1<<29) /* invalid operation exception summary */ +#define FPSOX (1<<28) /* overflow exception OX (sticky) */ +#define FPSUX (1<<27) /* underflow exception UX (sticky) */ +#define FPSZX (1<<26) /* zero divide exception ZX (sticky) */ +#define FPSXX (1<<25) /* inexact exception XX (sticky) */ +#define FPSVXSNAN (1<<24) /* invalid operation exception for SNaN (sticky) */ +#define FPSVXISI (1<<23) /* invalid operation exception for ∞-∞ (sticky) */ +#define FPSVXIDI (1<<22) /* invalid operation exception for ∞/∞ (sticky) */ +#define FPSVXZDZ (1<<21) /* invalid operation exception for 0/0 (sticky) */ +#define FPSVXIMZ (1<<20) /* invalid operation exception for ∞*0 (sticky) */ +#define FPSVXVC (1<<19) /* invalid operation exception for invalid compare (sticky) */ +#define FPSFR (1<<18) /* fraction rounded */ +#define FPSFI (1<<17) /* fraction inexact */ +#define FPSFPRF (1<<16) /* floating point result class */ +#define FPSFPCC (0xF<<12) /* <, >, =, unordered */ +#define FPVXCVI (1<<8) /* enable exception for invalid integer convert (sticky) */ + +/* FCR */ +#define FPVE (1<<7) /* invalid operation exception enable */ +#define FPOVFL (1<<6) /* enable overflow exceptions */ +#define FPUNFL (1<<5) /* enable underflow */ +#define FPZDIV (1<<4) /* enable zero divide */ +#define FPINEX (1<<3) /* enable inexact exceptions */ +#define FPRMASK (3<<0) /* rounding mode */ +#define FPRNR (0<<0) +#define FPRZ (1<<0) +#define FPRPINF (2<<0) +#define FPRNINF (3<<0) +#define FPPEXT 0 +#define FPPSGL 0 +#define FPPDBL 0 +#define FPPMASK 0 +#define FPINVAL FPVE +/* FSR */ +#define FPAOVFL FPSOX +#define FPAINEX FPSXX +#define FPAUNFL FPSUX +#define FPAZDIV FPSZX +#define FPAINVAL FPSVX diff --git a/MacOSX/power/include/lib9.h b/MacOSX/power/include/lib9.h new file mode 100644 index 00000000..1059b629 --- /dev/null +++ b/MacOSX/power/include/lib9.h @@ -0,0 +1,495 @@ +/* + * Based on FreeBSD lib9.h + * Copyright © 1998, 1999 Lucent Technologies Inc. All rights reserved. + * Revisions Copyright © 1999, 2002 Vita Nuova Limited. All rights reserved. + * Revisions Copyright © 2002, 2003 Corpus Callosum Corporation. All rights reserved. + */ + +/* define _BSD_SOURCE to use ISO C, POSIX, and 4.4BSD things. */ +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#define _LARGEFILE64_SOURCE 1 +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include +#include +#include +// #include "math.h" +#include +#include +#include +#include + +#define nil ((void*)0) + +// typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned long ulong; +// typedef unsigned int uint; +typedef signed char schar; +typedef long long vlong; +typedef unsigned long long uvlong; +typedef ushort Rune; +typedef unsigned int u32int; +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned short u16int; +typedef unsigned char u8int; +typedef unsigned long uintptr; + +/* handle conflicts with host os libs */ +#define getwd infgetwd +#define scalb infscalb +#define div infdiv +#define panic infpanic +#define rint infrint +#define rcmd infrcmd +#undef isnan +#define pow10 infpow10 + +#ifndef EMU +typedef struct Proc Proc; +#endif + +/* + * math module dtoa + */ +#include +#define __BIG_ENDIAN + +#define USED(x) if(x){}else{} +#define SET(x) + +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) +#define assert(x) if(x){}else _assert("x") + +/* + * mem and string routines are declared by ANSI/POSIX files above + */ + +extern char* strecpy(char*, char*, char*); +extern char* strdup(const char*); +extern int cistrncmp(char*, char*, int); +extern int cistrcmp(char*, char*); +extern char* cistrstr(char*, char*); +extern int tokenize(char*, char**, int); +extern vlong strtoll(const char*, char**, int); + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80 /* decoding error in UTF */ +}; + +/* + * rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern int runelen(long); +extern int runenlen(Rune*, int); +extern int fullrune(char*, int); +extern int utflen(char*); +extern int utfnlen(char*, long); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); +extern char* utfutf(char*, char*); +extern char* utfecpy(char*, char*, char*); + +extern Rune* runestrcat(Rune*, Rune*); +extern Rune* runestrchr(Rune*, Rune); +extern int runestrcmp(Rune*, Rune*); +extern Rune* runestrcpy(Rune*, Rune*); +extern Rune* runestrncpy(Rune*, Rune*, long); +extern Rune* runestrecpy(Rune*, Rune*, Rune*); +extern Rune* runestrdup(Rune*); +extern Rune* runestrncat(Rune*, Rune*, long); +extern int runestrncmp(Rune*, Rune*, long); +extern Rune* runestrrchr(Rune*, Rune); +extern long runestrlen(Rune*); +extern Rune* runestrstr(Rune*, Rune*); + +extern Rune tolowerrune(Rune); +extern Rune totitlerune(Rune); +extern Rune toupperrune(Rune); +extern int isalpharune(Rune); +extern int islowerrune(Rune); +extern int isspacerune(Rune); +extern int istitlerune(Rune); +extern int isupperrune(Rune); + +/* + * malloc + */ +extern void* malloc(size_t); +extern void* mallocz(ulong, int); +extern void free(void*); +extern ulong msize(void*); +extern void* calloc(size_t, size_t); +extern void* realloc(void*, size_t); +extern void setmalloctag(void*, ulong); +extern void setrealloctag(void*, ulong); +extern ulong getmalloctag(void*); +extern ulong getrealloctag(void*); +extern void* malloctopoolblock(void*); + +/* + * print routines + */ +typedef struct Fmt Fmt; +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + ulong flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + + FmtFlag = FmtByte << 1 +}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); +extern int runefmtstrinit(Fmt*); +extern Rune* runefmtstrflush(Fmt*); + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int dorfmt(Fmt*, Rune*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); +extern int fmtrunestrcpy(Fmt*, Rune*); +/* + * error string for %r + * supplied on per os basis, not part of fmt library + */ +extern int errfmt(Fmt *f); + +/* + * quoted strings + */ +extern char *unquotestrdup(char*); +extern Rune *unquoterunestrdup(Rune*); +extern char *quotestrdup(char*); +extern Rune *quoterunestrdup(Rune*); +extern int quotestrfmt(Fmt*); +extern int quoterunestrfmt(Fmt*); +extern void quotefmtinstall(void); +extern int (*doquote)(int); + +/* + * random number + */ + +extern int nrand(int); +extern ulong truerand(void); +extern ulong ntruerand(ulong); + +/* + * math + */ +extern int isNaN(double); +extern int isInf(double, int); +extern double pow(double, double); + +/* + * Time-of-day + */ + +typedef struct Tm Tm; +struct Tm { + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; + int yday; + char zone[4]; + int tzoff; +}; +extern vlong osnsec(void); +#define nsec osnsec + +/* + * one-of-a-kind + */ +extern void _assert(char*); +extern double charstod(int(*)(void*), void*); +extern char* cleanname(char*); +extern double frexp(double, int*); +extern ulong getcallerpc(void*); +extern int getfields(char*, char**, int, int, char*); +extern char* getuser(void); +extern char* getwd(char*, int); +extern double ipow10(int); +extern double ldexp(double, int); +extern double modf(double, double*); +extern void perror(const char*); +extern double pow10(int); +extern uvlong strtoull(const char*, char**, int); +extern void sysfatal(char*, ...); +extern int dec64(uchar*, int, char*, int); +extern int enc64(char*, int, uchar*, int); +extern int dec32(uchar*, int, char*, int); +extern int enc32(char*, int, uchar*, int); +extern int dec16(uchar*, int, char*, int); +extern int enc16(char*, int, uchar*, int); +extern int encodefmt(Fmt*); + +/* + * synchronization + */ +typedef +struct Lock { + ulong val; + int pid; +} Lock; + +extern ulong _tas(ulong*); + +extern void lock(Lock*); +extern void unlock(Lock*); +extern int canlock(Lock*); + +typedef struct QLock QLock; +struct QLock +{ + Lock use; /* to access Qlock structure */ + Proc *head; /* next process waiting for object */ + Proc *tail; /* last process waiting for object */ + int locked; /* flag */ +}; + +extern void qlock(QLock*); +extern void qunlock(QLock*); +extern int canqlock(QLock*); +extern void _qlockinit(ulong (*)(ulong, ulong)); /* called only by the thread library */ + +typedef +struct RWLock +{ + Lock l; /* Lock modify lock */ + QLock x; /* Mutual exclusion lock */ + QLock k; /* Lock for waiting writers */ + int readers; /* Count of readers in lock */ +} RWLock; + +extern int canrlock(RWLock*); +extern int canwlock(RWLock*); +extern void rlock(RWLock*); +extern void runlock(RWLock*); +extern void wlock(RWLock*); +extern void wunlock(RWLock*); + +/* + * network dialing + */ +#define NETPATHLEN 40 + +/* + * system calls + * + */ + +#define STATMAX 65535U /* max length of machine-independent stat structure */ +#define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */ +#define ERRMAX 128 /* max length of error string */ + +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MCACHE 0x0010 /* cache some data */ +#define MMASK 0x0017 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ +#define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ + +#define AEXIST 0 /* accessible: exists */ +#define AEXEC 1 /* execute access */ +#define AWRITE 2 /* write access */ +#define AREAD 4 /* read access */ + +/* bits in Qid.type */ +#define QTDIR 0x80 /* type bit for directories */ +#define QTAPPEND 0x40 /* type bit for append only files */ +#define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ +#define QTAUTH 0x08 /* type bit for authentication file */ +#define QTFILE 0x00 /* plain file */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMAUTH 0x08000000 /* mode bit for authentication file */ +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +typedef +struct Qid +{ + uvlong path; + ulong vers; + uchar type; +} Qid; + +typedef +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +} Dir; + +extern Dir* dirstat(char*); +extern Dir* dirfstat(int); +extern int dirwstat(char*, Dir*); +extern int dirfwstat(int, Dir*); +extern long dirread(int, Dir**); +extern void nulldir(Dir*); +extern long dirreadall(int, Dir**); + +typedef +struct Waitmsg +{ + int pid; /* of loved one */ + ulong time[3]; /* of loved one & descendants */ + char *msg; +} Waitmsg; + +extern void _exits(char*); + +extern void exits(char*); +extern int create(char*, int, int); +extern int errstr(char*, uint); + +extern void perror(const char*); +extern long readn(int, void*, long); +extern int remove(const char*); +extern void rerrstr(char*, uint); +extern vlong seek(int, vlong, int); +extern int segflush(void*, ulong); +extern void werrstr(char*, ...); + +extern char *argv0; +#define ARGBEGIN for((argv0||(argv0=*argv)),argv++,argc--;\ + argv[0] && argv[0][0]=='-' && argv[0][1];\ + argc--, argv++) {\ + char *_args, *_argt;\ + Rune _argc;\ + _args = &argv[0][1];\ + if(_args[0]=='-' && _args[1]==0){\ + argc--; argv++; break;\ + }\ + _argc = 0;\ + while(*_args && (_args += chartorune(&_argc, _args)))\ + switch(_argc) +#define ARGEND SET(_argt);USED(_argt);USED(_argc); USED(_args);}USED(argv); USED(argc); +#define ARGF() (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): 0)) +#define EARGF(x) (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0))) + +#define ARGC() _argc + +/* + * Extensions for Inferno to basic libc.h + */ + +#define setbinmode() + +/* + * Extensions for emu kernel emulation + */ +#ifdef EMU + +extern Proc *getup(void); +#define up (getup()) + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ + +#include + +typedef union { + double __dbl; + ppc_fp_scr_t __src; +} FPU; + +typedef sigjmp_buf osjmpbuf; +#define ossetjmp(buf) sigsetjmp(buf, 1) +#endif diff --git a/MacOSX/tcshrc b/MacOSX/tcshrc new file mode 100644 index 00000000..94793c7d --- /dev/null +++ b/MacOSX/tcshrc @@ -0,0 +1,12 @@ +# +# needed for the build process +# +set path=( $path `pwd`/MacOSX/power/bin ) + +setenv EMU "-r`pwd` -g1024x768" + +setenv ROOT `pwd` +setenv SYSHOST MacOSX +setenv OBJTYPE power +setenv ACIDLIB $ROOT/lib/acid + diff --git a/NOTICE b/NOTICE new file mode 100644 index 00000000..ea9ee809 --- /dev/null +++ b/NOTICE @@ -0,0 +1,52 @@ +This Inferno® distribution includes software from various sources and +different portions are therefore subject to different licence terms. + +You may copy and redistribute the package as a whole, +with or without modification, subject to the terms of the Vita Nuova +Liberal Source Licence (see the file LICENCE), which applies to the +package as a whole, and to individual components unless otherwise +specified either in an individual source file or in a NOTICE file +in a directory or directory tree. If the terms of that licence are not acceptable, +you must negotiate other terms with Vita Nuova (www.vitanuova.com). + +Individual components (eg, some of the libraries, Freetype, and the +B&H fonts) might have their own NOTICE and licence files (LICENCE or +COPYING) that cover their content. Be sure to read them before +considering redistribution of them on their own. +With the exception of the B&H font licence, each is typically +some variety of Free Software licence. + +Where a given section of source code is distributed elsewhere (as a separable component) under +another licence or licences, and we include it here as well under ours, you can obviously +regard it as subject to whichever licence you like. + +When making non-trivial extracts, or your own modifications, you must +retain the copyright of the original source file or files (either that +present in the file or in a NOTICE file covering the directory or +directories in which the file resides). + +The following copyright notice covers the contents of this +distribution unless otherwise specified by a given file, directory, or +directory tree: + +Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved. +Inferno revisions Copyright © 1997-1999 Vita Nuova Limited. All rights reserved. +Inferno revisions Copyright © 2000-2006 Vita Nuova Holdings Limited. All rights reserved. +Inferno new material Copyright © 2000-2006 Vita Nuova Holdings Limited. All rights reserved. + +Inferno, Styx, Dis and Limbo are registered trademarks of Vita Nuova Holdings Limited in the USA and other countries. +Plan 9 is a registered trademark of Lucent Technologies Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License (`LGPL') as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. diff --git a/Nt/386/bin/awk.exe b/Nt/386/bin/awk.exe new file mode 100755 index 00000000..77711a2c Binary files /dev/null and b/Nt/386/bin/awk.exe differ diff --git a/Nt/386/bin/c2l.exe b/Nt/386/bin/c2l.exe new file mode 100755 index 00000000..998edf79 Binary files /dev/null and b/Nt/386/bin/c2l.exe differ diff --git a/Nt/386/bin/cp.exe b/Nt/386/bin/cp.exe new file mode 100755 index 00000000..9a314722 Binary files /dev/null and b/Nt/386/bin/cp.exe differ diff --git a/Nt/386/bin/data2c.exe b/Nt/386/bin/data2c.exe new file mode 100755 index 00000000..fe2a7819 Binary files /dev/null and b/Nt/386/bin/data2c.exe differ diff --git a/Nt/386/bin/echo.exe b/Nt/386/bin/echo.exe new file mode 100755 index 00000000..37e5def9 Binary files /dev/null and b/Nt/386/bin/echo.exe differ diff --git a/Nt/386/bin/format.exe b/Nt/386/bin/format.exe new file mode 100755 index 00000000..30ab07e1 Binary files /dev/null and b/Nt/386/bin/format.exe differ diff --git a/Nt/386/bin/gzip.exe b/Nt/386/bin/gzip.exe new file mode 100755 index 00000000..54c02aeb Binary files /dev/null and b/Nt/386/bin/gzip.exe differ diff --git a/Nt/386/bin/infdb.exe b/Nt/386/bin/infdb.exe new file mode 100755 index 00000000..f34a0d72 Binary files /dev/null and b/Nt/386/bin/infdb.exe differ diff --git a/Nt/386/bin/mk.exe b/Nt/386/bin/mk.exe new file mode 100755 index 00000000..adf8b97f Binary files /dev/null and b/Nt/386/bin/mk.exe differ diff --git a/Nt/386/bin/mkdir.exe b/Nt/386/bin/mkdir.exe new file mode 100755 index 00000000..34810a39 Binary files /dev/null and b/Nt/386/bin/mkdir.exe differ diff --git a/Nt/386/bin/mkext.exe b/Nt/386/bin/mkext.exe new file mode 100755 index 00000000..c3bc6d0e Binary files /dev/null and b/Nt/386/bin/mkext.exe differ diff --git a/Nt/386/bin/mv.exe b/Nt/386/bin/mv.exe new file mode 100755 index 00000000..14d1955f Binary files /dev/null and b/Nt/386/bin/mv.exe differ diff --git a/Nt/386/bin/rcsh.exe b/Nt/386/bin/rcsh.exe new file mode 100755 index 00000000..f845b0a5 Binary files /dev/null and b/Nt/386/bin/rcsh.exe differ diff --git a/Nt/386/bin/rm.exe b/Nt/386/bin/rm.exe new file mode 100755 index 00000000..cce5adbf Binary files /dev/null and b/Nt/386/bin/rm.exe differ diff --git a/Nt/386/bin/sed.exe b/Nt/386/bin/sed.exe new file mode 100755 index 00000000..5926b70c Binary files /dev/null and b/Nt/386/bin/sed.exe differ diff --git a/Nt/386/bin/test.exe b/Nt/386/bin/test.exe new file mode 100755 index 00000000..5f88b8d3 Binary files /dev/null and b/Nt/386/bin/test.exe differ diff --git a/Nt/386/bin/tr.exe b/Nt/386/bin/tr.exe new file mode 100755 index 00000000..b653c3dd Binary files /dev/null and b/Nt/386/bin/tr.exe differ diff --git a/Nt/386/bin/yacc.exe b/Nt/386/bin/yacc.exe new file mode 100755 index 00000000..0a833692 Binary files /dev/null and b/Nt/386/bin/yacc.exe differ diff --git a/Nt/386/include/lib9.h b/Nt/386/include/lib9.h new file mode 100755 index 00000000..75dea8cc --- /dev/null +++ b/Nt/386/include/lib9.h @@ -0,0 +1,487 @@ +#define _POSIX_SOURCE +#include +#include +#include +#include +#include "math.h" +#include +#include +#include +#include +#include +#include +#include + +#define getwd infgetwd + +#ifndef EMU +typedef struct Proc Proc; +#endif + +/* + * math module dtoa + */ +#define __LITTLE_ENDIAN + + /* there must be a Win32 header macro for endian-ness!!! */ + +/* + * disable various silly warnings + * 4018 signed/unsigned comparison + * 4245 signed/unsigned conversion + * 4244 long to char conversion + * 4068 unknown pragma + * 4090 different volatile quals + * 4554 operator precedence + * 4146 unary - on unsigned type + */ +#pragma warning( disable : 4305 4244 4102 4761 4018 4245 4244 4068 4090 4554 4146) + +#define nil ((void*)0) + +typedef unsigned char uchar; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef signed char schar; +typedef unsigned short ushort; +typedef unsigned short Rune; +typedef __int64 vlong; +typedef unsigned __int64 uvlong; +typedef unsigned int u32int; +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned short u16int; +typedef unsigned char u8int; +typedef unsigned long uintptr; + +#define USED(x) if(x){}else{} +#define SET(x) + +#undef nelem +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#undef offsetof +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) +#undef assert +#define assert(x) if(x){}else _assert("x") + +/* + * most mem and string routines are declared by ANSI/POSIX files above + */ + +extern char* strecpy(char*, char*, char*); +extern char* strdup(const char*); +extern int cistrncmp(char*, char*, int); +extern int cistrcmp(char*, char*); +extern char* cistrstr(char*, char*); +extern int tokenize(char*, char**, int); +extern vlong strtoll(const char*, char**, int); +extern uvlong strtoull(const char*, char**, int); + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80 /* decoding error in UTF */ +}; + +/* + * rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern int runelen(long); +extern int runenlen(Rune*, int); +extern int fullrune(char*, int); +extern int utflen(char*); +extern int utfnlen(char*, long); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); +extern char* utfutf(char*, char*); +extern char* utfecpy(char*, char*, char*); + +extern Rune* runestrcat(Rune*, Rune*); +extern Rune* runestrchr(Rune*, Rune); +extern int runestrcmp(Rune*, Rune*); +extern Rune* runestrcpy(Rune*, Rune*); +extern Rune* runestrncpy(Rune*, Rune*, long); +extern Rune* runestrecpy(Rune*, Rune*, Rune*); +extern Rune* runestrdup(Rune*); +extern Rune* runestrncat(Rune*, Rune*, long); +extern int runestrncmp(Rune*, Rune*, long); +extern Rune* runestrrchr(Rune*, Rune); +extern long runestrlen(Rune*); +extern Rune* runestrstr(Rune*, Rune*); + +extern Rune tolowerrune(Rune); +extern Rune totitlerune(Rune); +extern Rune toupperrune(Rune); +extern int isalpharune(Rune); +extern int islowerrune(Rune); +extern int isspacerune(Rune); +extern int istitlerune(Rune); +extern int isupperrune(Rune); + +/* + * malloc + */ +extern void* malloc(size_t); +extern void* mallocz(ulong, int); +extern void free(void*); +extern ulong msize(void*); +extern void* calloc(size_t, size_t); +extern void* realloc(void*, size_t); +extern void setmalloctag(void*, ulong); +extern void setrealloctag(void*, ulong); +extern ulong getmalloctag(void*); +extern ulong getrealloctag(void*); +extern void* malloctopoolblock(void*); + +/* + * print routines + */ +typedef struct Fmt Fmt; +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + ulong flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + + FmtFlag = FmtByte << 1 +}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); +extern int runefmtstrinit(Fmt*); +extern Rune* runefmtstrflush(Fmt*); + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int dorfmt(Fmt*, Rune*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); +extern int fmtrunestrcpy(Fmt*, Rune*); +/* + * error string for %r + * supplied on per os basis, not part of fmt library + */ +extern int errfmt(Fmt *f); + +/* + * quoted strings + */ +extern char *unquotestrdup(char*); +extern Rune *unquoterunestrdup(Rune*); +extern char *quotestrdup(char*); +extern Rune *quoterunestrdup(Rune*); +extern int quotestrfmt(Fmt*); +extern int quoterunestrfmt(Fmt*); +extern void quotefmtinstall(void); +extern int (*doquote)(int); + +/* + * random number + */ +extern ulong truerand(void); +extern ulong ntruerand(ulong); + +/* + * math + */ +extern int isNaN(double); +extern int isInf(double, int); + +#define PIO2 1.570796326794896619231e0 +#define PI (PIO2+PIO2) + +/* + * Time-of-day + */ + +typedef struct Tm Tm; +struct Tm { + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; + int yday; + char zone[4]; + int tzoff; +}; +extern vlong osnsec(void); +#define nsec osnsec + +/* + * one-of-a-kind + */ +extern void _assert(char*); +extern double charstod(int(*)(void*), void*); +extern char* cleanname(char*); +extern ulong getcallerpc(void*); +extern int getfields(char*, char**, int, int, char*); +extern char* getuser(void); +extern char* getuser(void); +extern char* getwd(char*, int); +extern char* getwd(char*, int); +extern double ipow10(int); +extern double pow10(int); +extern void sysfatal(char*, ...); +extern int dec64(uchar*, int, char*, int); +extern int enc64(char*, int, uchar*, int); +extern int dec32(uchar*, int, char*, int); +extern int enc32(char*, int, uchar*, int); +extern int dec16(uchar*, int, char*, int); +extern int enc16(char*, int, uchar*, int); +extern int encodefmt(Fmt*); + +/* + * synchronization + */ +typedef +struct Lock { + ulong val; + int pid; +} Lock; + +extern ulong _tas(ulong*); + +extern void lock(Lock*); +extern void unlock(Lock*); +extern int canlock(Lock*); + +typedef struct QLock QLock; +struct QLock +{ + Lock use; /* to access Qlock structure */ + Proc *head; /* next process waiting for object */ + Proc *tail; /* last process waiting for object */ + int locked; /* flag */ +}; + +extern void qlock(QLock*); +extern void qunlock(QLock*); +extern int canqlock(QLock*); +extern void _qlockinit(ulong (*)(ulong, ulong)); /* called only by the thread library */ + +typedef +struct RWLock +{ + Lock l; /* Lock modify lock */ + QLock x; /* Mutual exclusion lock */ + QLock k; /* Lock for waiting writers */ + int readers; /* Count of readers in lock */ +} RWLock; + +extern int canrlock(RWLock*); +extern int canwlock(RWLock*); +extern void rlock(RWLock*); +extern void runlock(RWLock*); +extern void wlock(RWLock*); +extern void wunlock(RWLock*); + +/* + * network dialing + */ +#define NETPATHLEN 40 + +/* + * system calls + * + */ +#define STATMAX 65535U /* max length of machine-independent stat structure */ +#define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */ +#define ERRMAX 128 /* max length of error string */ + +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MCACHE 0x0010 /* cache some data */ +#define MMASK 0x0017 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ +#define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ + +#define AEXIST 0 /* accessible: exists */ +#define AEXEC 1 /* execute access */ +#define AWRITE 2 /* write access */ +#define AREAD 4 /* read access */ + +/* bits in Qid.type */ +#define QTDIR 0x80 /* type bit for directories */ +#define QTAPPEND 0x40 /* type bit for append only files */ +#define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ +#define QTAUTH 0x08 /* type bit for authentication file */ +#define QTFILE 0x00 /* plain file */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMAUTH 0x08000000 /* mode bit for authentication file */ +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +typedef +struct Qid +{ + uvlong path; + ulong vers; + uchar type; +} Qid; + +typedef +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +} Dir; + +extern Dir* dirstat(char*); +extern Dir* dirfstat(int); +extern int dirwstat(char*, Dir*); +extern int dirfwstat(int, Dir*); +extern long dirread(int, Dir**); +extern void nulldir(Dir*); +extern long dirreadall(int, Dir**); + +typedef +struct Waitmsg +{ + int pid; /* of loved one */ + ulong time[3]; /* of loved one & descendants */ + char *msg; +} Waitmsg; + +extern void _exits(char*); + +extern void exits(char*); +extern int create(char*, int, int); +extern int errstr(char*, uint); + +extern long readn(int, void*, long); +extern void rerrstr(char*, uint); +extern vlong seek(int, vlong, int); +extern void werrstr(char*, ...); + +extern char *argv0; +#define ARGBEGIN for((argv0||(argv0=*argv)),argv++,argc--;\ + argv[0] && argv[0][0]=='-' && argv[0][1];\ + argc--, argv++) {\ + char *_args, *_argt;\ + Rune _argc;\ + _args = &argv[0][1];\ + if(_args[0]=='-' && _args[1]==0){\ + argc--; argv++; break;\ + }\ + _argc = 0;\ + while(*_args && (_args += chartorune(&_argc, _args)))\ + switch(_argc) +#define ARGEND (_argt=0);USED(_argt);USED(_argc); USED(_args);}USED(argv); USED(argc); +#define ARGF() (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): 0)) +#define EARGF(x) (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0))) + +#define ARGC() _argc + +/* + * Extensions for Inferno to basic libc.h + */ + +extern void setbinmode(void); +extern void* sbrk(int); + +/* + * Extensions for emu kernel emulation + */ +#ifdef EMU + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +typedef struct FPU FPU; +struct FPU +{ + uchar env[28]; +}; + +extern void sleep(int); + +/* Set up private thread space */ +extern _declspec(thread) Proc* up; +#define Sleep NTsleep + +typedef jmp_buf osjmpbuf; +#define ossetjmp(buf) setjmp(buf) + +#endif diff --git a/Plan9/386/bin/data2c b/Plan9/386/bin/data2c new file mode 100755 index 00000000..a8bd1318 Binary files /dev/null and b/Plan9/386/bin/data2c differ diff --git a/Plan9/386/include/lib9.h b/Plan9/386/include/lib9.h new file mode 100644 index 00000000..aab2d462 --- /dev/null +++ b/Plan9/386/include/lib9.h @@ -0,0 +1,680 @@ +#include +typedef unsigned long size_t; + +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) +#define assert(x) if(x){}else _assert("x") + +/* + * mem routines + */ +extern void* memccpy(void*, void*, int, ulong); +extern void* memset(void*, int, ulong); +extern int memcmp(void*, void*, ulong); +extern void* memcpy(void*, void*, ulong); +extern void* memmove(void*, void*, ulong); +extern void* memchr(void*, int, ulong); + +/* + * string routines + */ +extern char* strcat(char*, char*); +extern char* strchr(char*, int); +extern int strcmp(char*, char*); +extern char* strcpy(char*, char*); +extern char* strecpy(char*, char*, char*); +extern char* strdup(char*); +extern char* strncat(char*, char*, long); +extern char* strncpy(char*, char*, long); +extern int strncmp(char*, char*, long); +extern char* strpbrk(char*, char*); +extern char* strrchr(char*, int); +extern char* strtok(char*, char*); +extern long strlen(char*); +extern long strspn(char*, char*); +extern long strcspn(char*, char*); +extern char* strstr(char*, char*); +extern int cistrncmp(char*, char*, int); +extern int cistrcmp(char*, char*); +extern char* cistrstr(char*, char*); +extern int tokenize(char*, char**, int); + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80, /* decoding error in UTF */ +}; + +/* + * rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern int runelen(long); +extern int runenlen(Rune*, int); +extern int fullrune(char*, int); +extern int utflen(char*); +extern int utfnlen(char*, long); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); +extern char* utfutf(char*, char*); +extern char* utfecpy(char*, char*, char*); + +extern Rune* runestrcat(Rune*, Rune*); +extern Rune* runestrchr(Rune*, Rune); +extern int runestrcmp(Rune*, Rune*); +extern Rune* runestrcpy(Rune*, Rune*); +extern Rune* runestrncpy(Rune*, Rune*, long); +extern Rune* runestrecpy(Rune*, Rune*, Rune*); +extern Rune* runestrdup(Rune*); +extern Rune* runestrncat(Rune*, Rune*, long); +extern int runestrncmp(Rune*, Rune*, long); +extern Rune* runestrrchr(Rune*, Rune); +extern long runestrlen(Rune*); +extern Rune* runestrstr(Rune*, Rune*); + +extern Rune tolowerrune(Rune); +extern Rune totitlerune(Rune); +extern Rune toupperrune(Rune); +extern int isalpharune(Rune); +extern int islowerrune(Rune); +extern int isspacerune(Rune); +extern int istitlerune(Rune); +extern int isupperrune(Rune); + +/* + * malloc + */ +extern void* malloc(ulong); +extern void* mallocz(ulong, int); +extern void free(void*); +extern ulong msize(void*); +extern void* calloc(ulong, ulong); +extern void* realloc(void*, ulong); +extern void setmalloctag(void*, ulong); +extern void setrealloctag(void*, ulong); +extern ulong getmalloctag(void*); +extern ulong getrealloctag(void*); +extern void* malloctopoolblock(void*); + +/* + * print routines + */ +typedef struct Fmt Fmt; +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + ulong flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + + FmtFlag = FmtByte << 1 +}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); +extern int runefmtstrinit(Fmt*); +extern Rune* runefmtstrflush(Fmt*); + +#pragma varargck argpos fmtprint 2 +#pragma varargck argpos fprint 2 +#pragma varargck argpos print 1 +#pragma varargck argpos runeseprint 3 +#pragma varargck argpos runesmprint 1 +#pragma varargck argpos runesnprint 3 +#pragma varargck argpos runesprint 2 +#pragma varargck argpos seprint 3 +#pragma varargck argpos smprint 1 +#pragma varargck argpos snprint 3 +#pragma varargck argpos sprint 2 +#pragma varargck argpos vseprint 3 +#pragma varargck argpos vsnprint 3 + +#pragma varargck type "lld" vlong +#pragma varargck type "llx" vlong +#pragma varargck type "lld" uvlong +#pragma varargck type "llx" uvlong +#pragma varargck type "ld" long +#pragma varargck type "lx" long +#pragma varargck type "ld" ulong +#pragma varargck type "lx" ulong +#pragma varargck type "d" int +#pragma varargck type "x" int +#pragma varargck type "c" int +#pragma varargck type "C" int +#pragma varargck type "d" uint +#pragma varargck type "x" uint +#pragma varargck type "c" uint +#pragma varargck type "C" uint +#pragma varargck type "f" double +#pragma varargck type "e" double +#pragma varargck type "g" double +#pragma varargck type "s" char* +#pragma varargck type "q" char* +#pragma varargck type "S" Rune* +#pragma varargck type "Q" Rune* +#pragma varargck type "r" void +#pragma varargck type "%" void +#pragma varargck type "n" int* +#pragma varargck type "p" void* +#pragma varargck flag ',' +#pragma varargck type "<" void* +#pragma varargck type "[" void* +#pragma varargck type "H" void* + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int dorfmt(Fmt*, Rune*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); +extern int fmtrunestrcpy(Fmt*, Rune*); +/* + * error string for %r + * supplied on per os basis, not part of fmt library + */ +extern int errfmt(Fmt *f); + +/* + * quoted strings + */ +extern char *unquotestrdup(char*); +extern Rune *unquoterunestrdup(Rune*); +extern char *quotestrdup(char*); +extern Rune *quoterunestrdup(Rune*); +extern int quotestrfmt(Fmt*); +extern int quoterunestrfmt(Fmt*); +extern void quotefmtinstall(void); +extern int (*doquote)(int); + +/* + * random number + */ +extern void srand(long); +extern int rand(void); +extern int nrand(int); +extern long lrand(void); +extern long lnrand(long); +extern double frand(void); +extern ulong truerand(void); +extern ulong ntruerand(ulong); + +/* + * math + */ +extern ulong getfcr(void); +extern void setfsr(ulong); +extern ulong getfsr(void); +extern void setfcr(ulong); +extern double NaN(void); +extern double Inf(int); +extern int isNaN(double); +extern int isInf(double, int); + +extern double pow(double, double); +extern double atan2(double, double); +extern double fabs(double); +extern double atan(double); +extern double log(double); +extern double log10(double); +extern double exp(double); +extern double floor(double); +extern double ceil(double); +extern double hypot(double, double); +extern double sin(double); +extern double cos(double); +extern double tan(double); +extern double asin(double); +extern double acos(double); +extern double sinh(double); +extern double cosh(double); +extern double tanh(double); +extern double sqrt(double); +extern double fmod(double, double); + +#define HUGE 3.4028234e38 +#define PIO2 1.570796326794896619231e0 +#define PI (PIO2+PIO2) + +/* + * Time-of-day + */ + +typedef +struct Tm +{ + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; + int yday; + char zone[4]; + int tzoff; +} Tm; + +extern Tm* gmtime(long); +extern Tm* localtime(long); +extern char* asctime(Tm*); +extern char* ctime(long); +extern double cputime(void); +extern long times(long*); +extern long tm2sec(Tm*); +extern vlong nsec(void); + +/* + * one-of-a-kind + */ +enum +{ + PNPROC = 1, + PNGROUP = 2, +}; + +extern void _assert(char*); +extern int abs(int); +extern int atexit(void(*)(void)); +extern void atexitdont(void(*)(void)); +extern int atnotify(int(*)(void*, char*), int); +extern double atof(char*); +extern int atoi(char*); +extern long atol(char*); +extern double charstod(int(*)(void*), void*); +extern char* cleanname(char*); +extern int decrypt(void*, void*, int); +extern int encrypt(void*, void*, int); +extern int dec64(uchar*, int, char*, int); +extern int enc64(char*, int, uchar*, int); +extern int dec32(uchar*, int, char*, int); +extern int enc32(char*, int, uchar*, int); +extern int dec16(uchar*, int, char*, int); +extern int enc16(char*, int, uchar*, int); +extern int encodefmt(Fmt*); +extern void exits(char*); +extern double frexp(double, int*); +extern ulong getcallerpc(void*); +extern char* getenv(char*); +extern int getfields(char*, char**, int, int, char*); +extern char* getuser(void); +extern char* getwd(char*, int); +extern int iounit(int); +extern long labs(long); +extern double ldexp(double, int); +extern void longjmp(jmp_buf, int); +extern char* mktemp(char*); +extern double modf(double, double*); +extern int netcrypt(void*, void*); +extern void notejmp(void*, jmp_buf, int); +extern void perror(char*); +extern int postnote(int, int, char *); +extern double pow10(int); +extern double ipow10(int); +extern int putenv(char*, char*); +extern void qsort(void*, long, long, int (*)(void*, void*)); +extern void* sbrk(ulong); +extern int setjmp(jmp_buf); +extern double strtod(char*, char**); +extern long strtol(char*, char**, int); +extern ulong strtoul(char*, char**, int); +extern vlong strtoll(char*, char**, int); +extern uvlong strtoull(char*, char**, int); +extern void sysfatal(char*, ...); +#pragma varargck argpos sysfatal 1 +extern void syslog(int, char*, char*, ...); +#pragma varargck argpos syslog 3 +extern long time(long*); +extern int tolower(int); +extern int toupper(int); + +/* + * synchronization + */ +typedef +struct Lock { + ulong val; +} Lock; + +extern ulong _tas(ulong*); + +extern void lock(Lock*); +extern void unlock(Lock*); +extern int canlock(Lock*); + +typedef struct QLp QLp; +struct QLp +{ + int inuse; + QLp *next; + char state; +}; + +typedef +struct QLock +{ + Lock lock; + int locked; + QLp *head; + QLp *tail; +} QLock; + +extern void qlock(QLock*); +extern void qunlock(QLock*); +extern int canqlock(QLock*); +extern void _qlockinit(ulong (*)(ulong, ulong)); /* called only by the thread library */ + +typedef +struct RWLock +{ + Lock lock; + int readers; /* number of readers */ + int writer; /* number of writers */ + QLp *head; /* list of waiting processes */ + QLp *tail; +} RWLock; + +extern int canrlock(RWLock*); +extern int canwlock(RWLock*); +extern void rlock(RWLock*); +extern void runlock(RWLock*); +extern void wlock(RWLock*); +extern void wunlock(RWLock*); + +extern void** privalloc(void); +extern void privfree(void**); + +/* + * network dialing + */ +#define NETPATHLEN 40 +extern int accept(int, char*); +extern int announce(char*, char*); +extern int dial(char*, char*, char*, int*); +extern void setnetmtpt(char*, int, char*); +extern int hangup(int); +extern int listen(char*, char*); +extern char* netmkaddr(char*, char*, char*); +extern int reject(int, char*, char*); + +/* + * system calls + * + */ +#define STATMAX 65535U /* max length of machine-independent stat structure */ +#define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */ +#define ERRMAX 128 /* max length of error string */ + +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MCACHE 0x0010 /* cache some data */ +#define MMASK 0x0017 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ +#define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ + +#define AEXIST 0 /* accessible: exists */ +#define AEXEC 1 /* execute access */ +#define AWRITE 2 /* write access */ +#define AREAD 4 /* read access */ + + +/* Segattch */ +#define SG_RONLY 0040 /* read only */ +#define SG_CEXEC 0100 /* detach on exec */ + +#define NCONT 0 /* continue after note */ +#define NDFLT 1 /* terminate after note */ +#define NSAVE 2 /* clear note but hold state */ +#define NRSTR 3 /* restore saved state */ + +/* rfork */ +enum +{ + RFNAMEG = (1<<0), + RFENVG = (1<<1), + RFFDG = (1<<2), + RFNOTEG = (1<<3), + RFPROC = (1<<4), + RFMEM = (1<<5), + RFNOWAIT = (1<<6), + RFCNAMEG = (1<<10), + RFCENVG = (1<<11), + RFCFDG = (1<<12), + RFREND = (1<<13), + RFNOMNT = (1<<14) +}; + +/* bits in Qid.type */ +#define QTDIR 0x80 /* type bit for directories */ +#define QTAPPEND 0x40 /* type bit for append only files */ +#define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ +#define QTAUTH 0x08 /* type bit for authentication file */ +#define QTFILE 0x00 /* plain file */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMAUTH 0x08000000 /* mode bit for authentication file */ +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +typedef +struct Qid +{ + uvlong path; + ulong vers; + uchar type; +} Qid; + +typedef +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +} Dir; + +extern Dir* dirstat(char*); +extern Dir* dirfstat(int); +extern int dirwstat(char*, Dir*); +extern int dirfwstat(int, Dir*); +extern long dirread(int, Dir**); +extern void nulldir(Dir*); +extern long dirreadall(int, Dir**); + +typedef +struct Waitmsg +{ + int pid; /* of loved one */ + ulong time[3]; /* of loved one & descendants */ + char *msg; +} Waitmsg; + +typedef +struct IOchunk +{ + void *addr; + ulong len; +} IOchunk; + +extern void _exits(char*); + +extern void abort(void); +extern int access(char*, int); +extern long alarm(ulong); +extern int await(char*, int); +extern int bind(char*, char*, int); +extern int brk(void*); +extern int chdir(char*); +extern int close(int); +extern int create(char*, int, ulong); +extern int dup(int, int); +extern int errstr(char*, uint); +extern int exec(char*, char*[]); +extern int execl(char*, ...); +extern int fork(void); +extern int rfork(int); +extern int fauth(int, char*); +extern int fstat(int, uchar*, int); +extern int fwstat(int, uchar*, int); +extern int fversion(int, int, char*, int); +extern int mount(int, int, char*, int, char*); +extern int unmount(char*, char*); +extern int noted(int); +extern int notify(void(*)(void*, char*)); +extern int open(char*, int); +extern int fd2path(int, char*, int); +extern int pipe(int*); +extern long pread(int, void*, long, vlong); +extern long preadv(int, IOchunk*, int, vlong); +extern long pwrite(int, void*, long, vlong); +extern long pwritev(int, IOchunk*, int, vlong); +extern long read(int, void*, long); +extern long readn(int, void*, long); +extern long readv(int, IOchunk*, int); +extern int remove(char*); +extern void* sbrk(ulong); +extern long oseek(int, long, int); +extern vlong seek(int, vlong, int); +extern long segattach(int, char*, void*, ulong); +extern int segbrk(void*, void*); +extern int segdetach(void*); +extern int segflush(void*, ulong); +extern int segfree(void*, ulong); +extern int sleep(long); +extern int stat(char*, uchar*, int); +extern Waitmsg* wait(void); +extern int waitpid(void); +extern long write(int, void*, long); +extern long writev(int, IOchunk*, int); +extern int wstat(char*, uchar*, int); +extern void* rendezvous(void*, void*); + +extern int getpid(void); +extern int getppid(void); +extern void rerrstr(char*, uint); +extern char* sysname(void); +extern void werrstr(char*, ...); +#pragma varargck argpos werrstr 1 + +extern char *argv0; +#define ARGBEGIN for((argv0||(argv0=*argv)),argv++,argc--;\ + argv[0] && argv[0][0]=='-' && argv[0][1];\ + argc--, argv++) {\ + char *_args, *_argt;\ + Rune _argc;\ + _args = &argv[0][1];\ + if(_args[0]=='-' && _args[1]==0){\ + argc--; argv++; break;\ + }\ + _argc = 0;\ + while(*_args && (_args += chartorune(&_argc, _args)))\ + switch(_argc) +#define ARGEND SET(_argt);USED(_argt,_argc,_args);}USED(argv, argc); +#define ARGF() (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): 0)) +#define EARGF(x) (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0))) + +#define ARGC() _argc + + +/* + * Extensions for Inferno to basic libc.h + */ + +#define setbinmode() +#define __LITTLE_ENDIAN + +/* + * Extensions for emu kernel emulation + */ +#ifdef EMU + +extern Proc** Xup; +#define up (*Xup) + +typedef struct FPU FPU; + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +struct FPU +{ + uchar env[28]; +}; + +typedef jmp_buf osjmpbuf; +#define ossetjmp(buf) setjmp(buf) + +#endif diff --git a/Plan9/386/include/u.h b/Plan9/386/include/u.h new file mode 100644 index 00000000..5c07da93 --- /dev/null +++ b/Plan9/386/include/u.h @@ -0,0 +1,64 @@ +#define nil ((void*)0) +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned long ulong; +typedef unsigned int uint; +typedef signed char schar; +typedef long long vlong; +typedef unsigned long long uvlong; +typedef unsigned long uintptr; +typedef ushort Rune; +typedef union FPdbleword FPdbleword; +typedef long jmp_buf[2]; +#define JMPBUFSP 0 +#define JMPBUFPC 1 +#define JMPBUFDPC 0 +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned char u8int; +typedef unsigned short u16int; +typedef unsigned int u32int; +typedef unsigned long long u64int; + +/* FCR */ +#define FPINEX (1<<5) +#define FPUNFL ((1<<4)|(1<<1)) +#define FPOVFL (1<<3) +#define FPZDIV (1<<2) +#define FPINVAL (1<<0) +#define FPRNR (0<<10) +#define FPRZ (3<<10) +#define FPRPINF (2<<10) +#define FPRNINF (1<<10) +#define FPRMASK (3<<10) +#define FPPEXT (3<<8) +#define FPPSGL (0<<8) +#define FPPDBL (2<<8) +#define FPPMASK (3<<8) +/* FSR */ +#define FPAINEX FPINEX +#define FPAOVFL FPOVFL +#define FPAUNFL FPUNFL +#define FPAZDIV FPZDIV +#define FPAINVAL FPINVAL +union FPdbleword +{ + double x; + struct { /* little endian */ + ulong lo; + ulong hi; + }; +}; + +typedef char* va_list; +#define va_start(list, start) list =\ + (sizeof(start) < 4?\ + (char*)((int*)&(start)+1):\ + (char*)(&(start)+1)) +#define va_end(list)\ + USED(list) +#define va_arg(list, mode)\ + ((sizeof(mode) == 1)?\ + ((mode*)(list += 4))[-4]:\ + (sizeof(mode) == 2)?\ + ((mode*)(list += 4))[-2]:\ + ((mode*)(list += sizeof(mode)))[-1]) diff --git a/Plan9/mips/include/lib9.h b/Plan9/mips/include/lib9.h new file mode 100644 index 00000000..ae392b9a --- /dev/null +++ b/Plan9/mips/include/lib9.h @@ -0,0 +1,680 @@ +#include +typedef unsigned long size_t; + +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) +#define assert(x) if(x){}else _assert("x") + +/* + * mem routines + */ +extern void* memccpy(void*, void*, int, ulong); +extern void* memset(void*, int, ulong); +extern int memcmp(void*, void*, ulong); +extern void* memcpy(void*, void*, ulong); +extern void* memmove(void*, void*, ulong); +extern void* memchr(void*, int, ulong); + +/* + * string routines + */ +extern char* strcat(char*, char*); +extern char* strchr(char*, int); +extern int strcmp(char*, char*); +extern char* strcpy(char*, char*); +extern char* strecpy(char*, char*, char*); +extern char* strdup(char*); +extern char* strncat(char*, char*, long); +extern char* strncpy(char*, char*, long); +extern int strncmp(char*, char*, long); +extern char* strpbrk(char*, char*); +extern char* strrchr(char*, int); +extern char* strtok(char*, char*); +extern long strlen(char*); +extern long strspn(char*, char*); +extern long strcspn(char*, char*); +extern char* strstr(char*, char*); +extern int cistrncmp(char*, char*, int); +extern int cistrcmp(char*, char*); +extern char* cistrstr(char*, char*); +extern int tokenize(char*, char**, int); + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80, /* decoding error in UTF */ +}; + +/* + * rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern int runelen(long); +extern int runenlen(Rune*, int); +extern int fullrune(char*, int); +extern int utflen(char*); +extern int utfnlen(char*, long); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); +extern char* utfutf(char*, char*); +extern char* utfecpy(char*, char*, char*); + +extern Rune* runestrcat(Rune*, Rune*); +extern Rune* runestrchr(Rune*, Rune); +extern int runestrcmp(Rune*, Rune*); +extern Rune* runestrcpy(Rune*, Rune*); +extern Rune* runestrncpy(Rune*, Rune*, long); +extern Rune* runestrecpy(Rune*, Rune*, Rune*); +extern Rune* runestrdup(Rune*); +extern Rune* runestrncat(Rune*, Rune*, long); +extern int runestrncmp(Rune*, Rune*, long); +extern Rune* runestrrchr(Rune*, Rune); +extern long runestrlen(Rune*); +extern Rune* runestrstr(Rune*, Rune*); + +extern Rune tolowerrune(Rune); +extern Rune totitlerune(Rune); +extern Rune toupperrune(Rune); +extern int isalpharune(Rune); +extern int islowerrune(Rune); +extern int isspacerune(Rune); +extern int istitlerune(Rune); +extern int isupperrune(Rune); + +/* + * malloc + */ +extern void* malloc(ulong); +extern void* mallocz(ulong, int); +extern void free(void*); +extern ulong msize(void*); +extern void* calloc(ulong, ulong); +extern void* realloc(void*, ulong); +extern void setmalloctag(void*, ulong); +extern void setrealloctag(void*, ulong); +extern ulong getmalloctag(void*); +extern ulong getrealloctag(void*); +extern void* malloctopoolblock(void*); + +/* + * print routines + */ +typedef struct Fmt Fmt; +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + ulong flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + + FmtFlag = FmtByte << 1 +}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); +extern int runefmtstrinit(Fmt*); +extern Rune* runefmtstrflush(Fmt*); + +#pragma varargck argpos fmtprint 2 +#pragma varargck argpos fprint 2 +#pragma varargck argpos print 1 +#pragma varargck argpos runeseprint 3 +#pragma varargck argpos runesmprint 1 +#pragma varargck argpos runesnprint 3 +#pragma varargck argpos runesprint 2 +#pragma varargck argpos seprint 3 +#pragma varargck argpos smprint 1 +#pragma varargck argpos snprint 3 +#pragma varargck argpos sprint 2 +#pragma varargck argpos vseprint 3 +#pragma varargck argpos vsnprint 3 + +#pragma varargck type "lld" vlong +#pragma varargck type "llx" vlong +#pragma varargck type "lld" uvlong +#pragma varargck type "llx" uvlong +#pragma varargck type "ld" long +#pragma varargck type "lx" long +#pragma varargck type "ld" ulong +#pragma varargck type "lx" ulong +#pragma varargck type "d" int +#pragma varargck type "x" int +#pragma varargck type "c" int +#pragma varargck type "C" int +#pragma varargck type "d" uint +#pragma varargck type "x" uint +#pragma varargck type "c" uint +#pragma varargck type "C" uint +#pragma varargck type "f" double +#pragma varargck type "e" double +#pragma varargck type "g" double +#pragma varargck type "s" char* +#pragma varargck type "q" char* +#pragma varargck type "S" Rune* +#pragma varargck type "Q" Rune* +#pragma varargck type "r" void +#pragma varargck type "%" void +#pragma varargck type "n" int* +#pragma varargck type "p" void* +#pragma varargck flag ',' +#pragma varargck type "<" void* +#pragma varargck type "[" void* +#pragma varargck type "H" void* + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int dorfmt(Fmt*, Rune*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); +extern int fmtrunestrcpy(Fmt*, Rune*); +/* + * error string for %r + * supplied on per os basis, not part of fmt library + */ +extern int errfmt(Fmt *f); + +/* + * quoted strings + */ +extern char *unquotestrdup(char*); +extern Rune *unquoterunestrdup(Rune*); +extern char *quotestrdup(char*); +extern Rune *quoterunestrdup(Rune*); +extern int quotestrfmt(Fmt*); +extern int quoterunestrfmt(Fmt*); +extern void quotefmtinstall(void); +extern int (*doquote)(int); + +/* + * random number + */ +extern void srand(long); +extern int rand(void); +extern int nrand(int); +extern long lrand(void); +extern long lnrand(long); +extern double frand(void); +extern ulong truerand(void); +extern ulong ntruerand(ulong); + +/* + * math + */ +extern ulong getfcr(void); +extern void setfsr(ulong); +extern ulong getfsr(void); +extern void setfcr(ulong); +extern double NaN(void); +extern double Inf(int); +extern int isNaN(double); +extern int isInf(double, int); + +extern double pow(double, double); +extern double atan2(double, double); +extern double fabs(double); +extern double atan(double); +extern double log(double); +extern double log10(double); +extern double exp(double); +extern double floor(double); +extern double ceil(double); +extern double hypot(double, double); +extern double sin(double); +extern double cos(double); +extern double tan(double); +extern double asin(double); +extern double acos(double); +extern double sinh(double); +extern double cosh(double); +extern double tanh(double); +extern double sqrt(double); +extern double fmod(double, double); + +#define HUGE 3.4028234e38 +#define PIO2 1.570796326794896619231e0 +#define PI (PIO2+PIO2) + +/* + * Time-of-day + */ + +typedef +struct Tm +{ + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; + int yday; + char zone[4]; + int tzoff; +} Tm; + +extern Tm* gmtime(long); +extern Tm* localtime(long); +extern char* asctime(Tm*); +extern char* ctime(long); +extern double cputime(void); +extern long times(long*); +extern long tm2sec(Tm*); +extern vlong nsec(void); + +/* + * one-of-a-kind + */ +enum +{ + PNPROC = 1, + PNGROUP = 2, +}; + +extern void _assert(char*); +extern int abs(int); +extern int atexit(void(*)(void)); +extern void atexitdont(void(*)(void)); +extern int atnotify(int(*)(void*, char*), int); +extern double atof(char*); +extern int atoi(char*); +extern long atol(char*); +extern double charstod(int(*)(void*), void*); +extern char* cleanname(char*); +extern int decrypt(void*, void*, int); +extern int encrypt(void*, void*, int); +extern int dec64(uchar*, int, char*, int); +extern int enc64(char*, int, uchar*, int); +extern int dec32(uchar*, int, char*, int); +extern int enc32(char*, int, uchar*, int); +extern int dec16(uchar*, int, char*, int); +extern int enc16(char*, int, uchar*, int); +extern int encodefmt(Fmt*); +extern void exits(char*); +extern double frexp(double, int*); +extern ulong getcallerpc(void*); +extern char* getenv(char*); +extern int getfields(char*, char**, int, int, char*); +extern char* getuser(void); +extern char* getwd(char*, int); +extern int iounit(int); +extern long labs(long); +extern double ldexp(double, int); +extern void longjmp(jmp_buf, int); +extern char* mktemp(char*); +extern double modf(double, double*); +extern int netcrypt(void*, void*); +extern void notejmp(void*, jmp_buf, int); +extern void perror(char*); +extern int postnote(int, int, char *); +extern double pow10(int); +extern double ipow10(int); +extern int putenv(char*, char*); +extern void qsort(void*, long, long, int (*)(void*, void*)); +extern void* sbrk(ulong); +extern int setjmp(jmp_buf); +extern double strtod(char*, char**); +extern long strtol(char*, char**, int); +extern ulong strtoul(char*, char**, int); +extern vlong strtoll(char*, char**, int); +extern uvlong strtoull(char*, char**, int); +extern void sysfatal(char*, ...); +#pragma varargck argpos sysfatal 1 +extern void syslog(int, char*, char*, ...); +#pragma varargck argpos syslog 3 +extern long time(long*); +extern int tolower(int); +extern int toupper(int); + +/* + * synchronization + */ +typedef +struct Lock { + ulong val; +} Lock; + +extern ulong _tas(ulong*); + +extern void lock(Lock*); +extern void unlock(Lock*); +extern int canlock(Lock*); + +typedef struct QLp QLp; +struct QLp +{ + int inuse; + QLp *next; + char state; +}; + +typedef +struct QLock +{ + Lock lock; + int locked; + QLp *head; + QLp *tail; +} QLock; + +extern void qlock(QLock*); +extern void qunlock(QLock*); +extern int canqlock(QLock*); +extern void _qlockinit(ulong (*)(ulong, ulong)); /* called only by the thread library */ + +typedef +struct RWLock +{ + Lock lock; + int readers; /* number of readers */ + int writer; /* number of writers */ + QLp *head; /* list of waiting processes */ + QLp *tail; +} RWLock; + +extern int canrlock(RWLock*); +extern int canwlock(RWLock*); +extern void rlock(RWLock*); +extern void runlock(RWLock*); +extern void wlock(RWLock*); +extern void wunlock(RWLock*); + +extern void** privalloc(void); +extern void privfree(void**); + +/* + * network dialing + */ +#define NETPATHLEN 40 +extern int accept(int, char*); +extern int announce(char*, char*); +extern int dial(char*, char*, char*, int*); +extern void setnetmtpt(char*, int, char*); +extern int hangup(int); +extern int listen(char*, char*); +extern char* netmkaddr(char*, char*, char*); +extern int reject(int, char*, char*); + +/* + * system calls + * + */ +#define STATMAX 65535U /* max length of machine-independent stat structure */ +#define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */ +#define ERRMAX 128 /* max length of error string */ + +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MCACHE 0x0010 /* cache some data */ +#define MMASK 0x0017 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ +#define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ + +#define AEXIST 0 /* accessible: exists */ +#define AEXEC 1 /* execute access */ +#define AWRITE 2 /* write access */ +#define AREAD 4 /* read access */ + + +/* Segattch */ +#define SG_RONLY 0040 /* read only */ +#define SG_CEXEC 0100 /* detach on exec */ + +#define NCONT 0 /* continue after note */ +#define NDFLT 1 /* terminate after note */ +#define NSAVE 2 /* clear note but hold state */ +#define NRSTR 3 /* restore saved state */ + +/* rfork */ +enum +{ + RFNAMEG = (1<<0), + RFENVG = (1<<1), + RFFDG = (1<<2), + RFNOTEG = (1<<3), + RFPROC = (1<<4), + RFMEM = (1<<5), + RFNOWAIT = (1<<6), + RFCNAMEG = (1<<10), + RFCENVG = (1<<11), + RFCFDG = (1<<12), + RFREND = (1<<13), + RFNOMNT = (1<<14) +}; + +/* bits in Qid.type */ +#define QTDIR 0x80 /* type bit for directories */ +#define QTAPPEND 0x40 /* type bit for append only files */ +#define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ +#define QTAUTH 0x08 /* type bit for authentication file */ +#define QTFILE 0x00 /* plain file */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMAUTH 0x08000000 /* mode bit for authentication file */ +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +typedef +struct Qid +{ + uvlong path; + ulong vers; + uchar type; +} Qid; + +typedef +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +} Dir; + +extern Dir* dirstat(char*); +extern Dir* dirfstat(int); +extern int dirwstat(char*, Dir*); +extern int dirfwstat(int, Dir*); +extern long dirread(int, Dir**); +extern void nulldir(Dir*); +extern long dirreadall(int, Dir**); + +typedef +struct Waitmsg +{ + int pid; /* of loved one */ + ulong time[3]; /* of loved one & descendants */ + char *msg; +} Waitmsg; + +typedef +struct IOchunk +{ + void *addr; + ulong len; +} IOchunk; + +extern void _exits(char*); + +extern void abort(void); +extern int access(char*, int); +extern long alarm(ulong); +extern int await(char*, int); +extern int bind(char*, char*, int); +extern int brk(void*); +extern int chdir(char*); +extern int close(int); +extern int create(char*, int, ulong); +extern int dup(int, int); +extern int errstr(char*, uint); +extern int exec(char*, char*[]); +extern int execl(char*, ...); +extern int fork(void); +extern int rfork(int); +extern int fauth(int, char*); +extern int fstat(int, uchar*, int); +extern int fwstat(int, uchar*, int); +extern int fversion(int, int, char*, int); +extern int mount(int, int, char*, int, char*); +extern int unmount(char*, char*); +extern int noted(int); +extern int notify(void(*)(void*, char*)); +extern int open(char*, int); +extern int fd2path(int, char*, int); +extern int pipe(int*); +extern long pread(int, void*, long, vlong); +extern long preadv(int, IOchunk*, int, vlong); +extern long pwrite(int, void*, long, vlong); +extern long pwritev(int, IOchunk*, int, vlong); +extern long read(int, void*, long); +extern long readn(int, void*, long); +extern long readv(int, IOchunk*, int); +extern int remove(char*); +extern void* sbrk(ulong); +extern long oseek(int, long, int); +extern vlong seek(int, vlong, int); +extern long segattach(int, char*, void*, ulong); +extern int segbrk(void*, void*); +extern int segdetach(void*); +extern int segflush(void*, ulong); +extern int segfree(void*, ulong); +extern int sleep(long); +extern int stat(char*, uchar*, int); +extern Waitmsg* wait(void); +extern int waitpid(void); +extern long write(int, void*, long); +extern long writev(int, IOchunk*, int); +extern int wstat(char*, uchar*, int); +extern void* rendezvous(void*, void*); + +extern int getpid(void); +extern int getppid(void); +extern void rerrstr(char*, uint); +extern char* sysname(void); +extern void werrstr(char*, ...); +#pragma varargck argpos werrstr 1 + +extern char *argv0; +#define ARGBEGIN for((argv0||(argv0=*argv)),argv++,argc--;\ + argv[0] && argv[0][0]=='-' && argv[0][1];\ + argc--, argv++) {\ + char *_args, *_argt;\ + Rune _argc;\ + _args = &argv[0][1];\ + if(_args[0]=='-' && _args[1]==0){\ + argc--; argv++; break;\ + }\ + _argc = 0;\ + while(*_args && (_args += chartorune(&_argc, _args)))\ + switch(_argc) +#define ARGEND SET(_argt);USED(_argt,_argc,_args);}USED(argv, argc); +#define ARGF() (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): 0)) +#define EARGF(x) (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0))) + +#define ARGC() _argc + +/* + * Extensions for Inferno to basic libc.h + */ + +#define setbinmode() +#undef __LITTLE_ENDIAN + +/* + * Extensions for emu kernel emulation + */ +#ifdef EMU + +extern Proc** Xup; +#define up (*Xup) + +typedef struct FPU FPU; + + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +struct FPU +{ + uchar env[28]; +}; + +typedef jmp_buf osjmpbuf; +#define ossetjmp(buf) setjmp(buf) + +#endif diff --git a/Plan9/mips/include/u.h b/Plan9/mips/include/u.h new file mode 100644 index 00000000..9df12bff --- /dev/null +++ b/Plan9/mips/include/u.h @@ -0,0 +1,65 @@ +#define nil ((void*)0) +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned long ulong; +typedef unsigned int uint; +typedef signed char schar; +typedef long long vlong; +typedef unsigned long long uvlong; +typedef unsigned long uintptr; +typedef ushort Rune; +typedef union FPdbleword FPdbleword; +typedef long jmp_buf[2]; +#define JMPBUFSP 0 +#define JMPBUFPC 1 +#define JMPBUFDPC 0 +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned char u8int; +typedef unsigned short u16int; +typedef unsigned int u32int; +typedef unsigned long long u64int; + +/* FCR */ +#define FPINEX (1<<7) +#define FPUNFL (1<<8) +#define FPOVFL (1<<9) +#define FPZDIV (1<<10) +#define FPINVAL (1<<11) +#define FPRNR (0<<0) +#define FPRZ (1<<0) +#define FPRPINF (2<<0) +#define FPRNINF (3<<0) +#define FPRMASK (3<<0) +#define FPPEXT 0 +#define FPPSGL 0 +#define FPPDBL 0 +#define FPPMASK 0 +/* FSR */ +#define FPAINEX (1<<2) +#define FPAOVFL (1<<4) +#define FPAUNFL (1<<3) +#define FPAZDIV (1<<5) +#define FPAINVAL (1<<6) +union FPdbleword +{ + double x; + struct { /* big endian */ + ulong hi; + ulong lo; + }; +}; + +/* stdarg */ +typedef char* va_list; +#define va_start(list, start) list =\ + (sizeof(start) < 4?\ + (char*)((int*)&(start)+1):\ + (char*)(&(start)+1)) +#define va_end(list)\ + USED(list) +#define va_arg(list, mode)\ + ((sizeof(mode) == 1)?\ + ((mode*)(list += 4))[-1]:\ + (sizeof(mode) == 2)?\ + ((mode*)(list += 4))[-1]:\ + ((mode*)(list += sizeof(mode)))[-1]) diff --git a/Plan9/power/include/lib9.h b/Plan9/power/include/lib9.h new file mode 100644 index 00000000..38ac4d15 --- /dev/null +++ b/Plan9/power/include/lib9.h @@ -0,0 +1,687 @@ +#include +typedef unsigned long size_t; + +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) +#define assert(x) if(x){}else _assert("x") + +/* + * mem routines + */ +extern void* memccpy(void*, void*, int, ulong); +extern void* memset(void*, int, ulong); +extern int memcmp(void*, void*, ulong); +extern void* memcpy(void*, void*, ulong); +extern void* memmove(void*, void*, ulong); +extern void* memchr(void*, int, ulong); + +/* + * string routines + */ +extern char* strcat(char*, char*); +extern char* strchr(char*, int); +extern int strcmp(char*, char*); +extern char* strcpy(char*, char*); +extern char* strecpy(char*, char*, char*); +extern char* strdup(char*); +extern char* strncat(char*, char*, long); +extern char* strncpy(char*, char*, long); +extern int strncmp(char*, char*, long); +extern char* strpbrk(char*, char*); +extern char* strrchr(char*, int); +extern char* strtok(char*, char*); +extern long strlen(char*); +extern long strspn(char*, char*); +extern long strcspn(char*, char*); +extern char* strstr(char*, char*); +extern int cistrncmp(char*, char*, int); +extern int cistrcmp(char*, char*); +extern char* cistrstr(char*, char*); +extern int tokenize(char*, char**, int); + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80, /* decoding error in UTF */ +}; + +/* + * rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern int runelen(long); +extern int runenlen(Rune*, int); +extern int fullrune(char*, int); +extern int utflen(char*); +extern int utfnlen(char*, long); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); +extern char* utfutf(char*, char*); +extern char* utfecpy(char*, char*, char*); + +extern Rune* runestrcat(Rune*, Rune*); +extern Rune* runestrchr(Rune*, Rune); +extern int runestrcmp(Rune*, Rune*); +extern Rune* runestrcpy(Rune*, Rune*); +extern Rune* runestrncpy(Rune*, Rune*, long); +extern Rune* runestrecpy(Rune*, Rune*, Rune*); +extern Rune* runestrdup(Rune*); +extern Rune* runestrncat(Rune*, Rune*, long); +extern int runestrncmp(Rune*, Rune*, long); +extern Rune* runestrrchr(Rune*, Rune); +extern long runestrlen(Rune*); +extern Rune* runestrstr(Rune*, Rune*); + +extern Rune tolowerrune(Rune); +extern Rune totitlerune(Rune); +extern Rune toupperrune(Rune); +extern int isalpharune(Rune); +extern int islowerrune(Rune); +extern int isspacerune(Rune); +extern int istitlerune(Rune); +extern int isupperrune(Rune); + +/* + * malloc + */ +extern void* malloc(ulong); +extern void* mallocz(ulong, int); +extern void free(void*); +extern ulong msize(void*); +extern void* calloc(ulong, ulong); +extern void* realloc(void*, ulong); +extern void setmalloctag(void*, ulong); +extern void setrealloctag(void*, ulong); +extern ulong getmalloctag(void*); +extern ulong getrealloctag(void*); +extern void* malloctopoolblock(void*); + +/* + * print routines + */ +typedef struct Fmt Fmt; +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + ulong flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + + FmtFlag = FmtByte << 1 +}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); +extern int runefmtstrinit(Fmt*); +extern Rune* runefmtstrflush(Fmt*); + +#pragma varargck argpos fmtprint 2 +#pragma varargck argpos fprint 2 +#pragma varargck argpos print 1 +#pragma varargck argpos runeseprint 3 +#pragma varargck argpos runesmprint 1 +#pragma varargck argpos runesnprint 3 +#pragma varargck argpos runesprint 2 +#pragma varargck argpos seprint 3 +#pragma varargck argpos smprint 1 +#pragma varargck argpos snprint 3 +#pragma varargck argpos sprint 2 +#pragma varargck argpos vseprint 3 +#pragma varargck argpos vsnprint 3 + +#pragma varargck type "lld" vlong +#pragma varargck type "llx" vlong +#pragma varargck type "lld" uvlong +#pragma varargck type "llx" uvlong +#pragma varargck type "ld" long +#pragma varargck type "lx" long +#pragma varargck type "ld" ulong +#pragma varargck type "lx" ulong +#pragma varargck type "d" int +#pragma varargck type "x" int +#pragma varargck type "c" int +#pragma varargck type "C" int +#pragma varargck type "d" uint +#pragma varargck type "x" uint +#pragma varargck type "c" uint +#pragma varargck type "C" uint +#pragma varargck type "f" double +#pragma varargck type "e" double +#pragma varargck type "g" double +#pragma varargck type "s" char* +#pragma varargck type "q" char* +#pragma varargck type "S" Rune* +#pragma varargck type "Q" Rune* +#pragma varargck type "r" void +#pragma varargck type "%" void +#pragma varargck type "n" int* +#pragma varargck type "p" void* +#pragma varargck flag ',' +#pragma varargck type "<" void* +#pragma varargck type "[" void* +#pragma varargck type "H" void* + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int dorfmt(Fmt*, Rune*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); +extern int fmtrunestrcpy(Fmt*, Rune*); +/* + * error string for %r + * supplied on per os basis, not part of fmt library + */ +extern int errfmt(Fmt *f); + +/* + * quoted strings + */ +extern char *unquotestrdup(char*); +extern Rune *unquoterunestrdup(Rune*); +extern char *quotestrdup(char*); +extern Rune *quoterunestrdup(Rune*); +extern int quotestrfmt(Fmt*); +extern int quoterunestrfmt(Fmt*); +extern void quotefmtinstall(void); +extern int (*doquote)(int); + +/* + * random number + */ +extern void srand(long); +extern int rand(void); +extern int nrand(int); +extern long lrand(void); +extern long lnrand(long); +extern double frand(void); +extern ulong truerand(void); +extern ulong ntruerand(ulong); + +/* + * math + */ +extern ulong getfcr(void); +extern void setfsr(ulong); +extern ulong getfsr(void); +extern void setfcr(ulong); +extern double NaN(void); +extern double Inf(int); +extern int isNaN(double); +extern int isInf(double, int); + +extern double pow(double, double); +extern double atan2(double, double); +extern double fabs(double); +extern double atan(double); +extern double log(double); +extern double log10(double); +extern double exp(double); +extern double floor(double); +extern double ceil(double); +extern double hypot(double, double); +extern double sin(double); +extern double cos(double); +extern double tan(double); +extern double asin(double); +extern double acos(double); +extern double sinh(double); +extern double cosh(double); +extern double tanh(double); +extern double sqrt(double); +extern double fmod(double, double); + +#define HUGE 3.4028234e38 +#define PIO2 1.570796326794896619231e0 +#define PI (PIO2+PIO2) + +/* + * Time-of-day + */ + +typedef +struct Tm +{ + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; + int yday; + char zone[4]; + int tzoff; +} Tm; + +extern Tm* gmtime(long); +extern Tm* localtime(long); +extern char* asctime(Tm*); +extern char* ctime(long); +extern double cputime(void); +extern long times(long*); +extern long tm2sec(Tm*); +extern vlong nsec(void); + +/* + * one-of-a-kind + */ +enum +{ + PNPROC = 1, + PNGROUP = 2, +}; + +extern void _assert(char*); +extern int abs(int); +extern int atexit(void(*)(void)); +extern void atexitdont(void(*)(void)); +extern int atnotify(int(*)(void*, char*), int); +extern double atof(char*); +extern int atoi(char*); +extern long atol(char*); +extern double charstod(int(*)(void*), void*); +extern char* cleanname(char*); +extern int decrypt(void*, void*, int); +extern int encrypt(void*, void*, int); +extern int dec64(uchar*, int, char*, int); +extern int enc64(char*, int, uchar*, int); +extern int dec32(uchar*, int, char*, int); +extern int enc32(char*, int, uchar*, int); +extern int dec16(uchar*, int, char*, int); +extern int enc16(char*, int, uchar*, int); +extern int encodefmt(Fmt*); +extern void exits(char*); +extern double frexp(double, int*); +extern ulong getcallerpc(void*); +extern char* getenv(char*); +extern int getfields(char*, char**, int, int, char*); +extern char* getuser(void); +extern char* getwd(char*, int); +extern int iounit(int); +extern long labs(long); +extern double ldexp(double, int); +extern void longjmp(jmp_buf, int); +extern char* mktemp(char*); +extern double modf(double, double*); +extern int netcrypt(void*, void*); +extern void notejmp(void*, jmp_buf, int); +extern void perror(char*); +extern int postnote(int, int, char *); +extern double pow10(int); +extern double ipow10(int); +extern int putenv(char*, char*); +extern void qsort(void*, long, long, int (*)(void*, void*)); +extern void* sbrk(ulong); +extern int setjmp(jmp_buf); +extern double strtod(char*, char**); +extern long strtol(char*, char**, int); +extern ulong strtoul(char*, char**, int); +extern vlong strtoll(char*, char**, int); +extern uvlong strtoull(char*, char**, int); +extern void sysfatal(char*, ...); +#pragma varargck argpos sysfatal 1 +extern void syslog(int, char*, char*, ...); +#pragma varargck argpos syslog 3 +extern long time(long*); +extern int tolower(int); +extern int toupper(int); + +/* + * synchronization + */ +typedef +struct Lock { + ulong val; +} Lock; + +extern ulong _tas(ulong*); + +extern void lock(Lock*); +extern void unlock(Lock*); +extern int canlock(Lock*); + +typedef struct QLp QLp; +struct QLp +{ + int inuse; + QLp *next; + char state; +}; + +typedef +struct QLock +{ + Lock lock; + int locked; + QLp *head; + QLp *tail; +} QLock; + +extern void qlock(QLock*); +extern void qunlock(QLock*); +extern int canqlock(QLock*); +extern void _qlockinit(ulong (*)(ulong, ulong)); /* called only by the thread library */ + +typedef +struct RWLock +{ + Lock lock; + int readers; /* number of readers */ + int writer; /* number of writers */ + QLp *head; /* list of waiting processes */ + QLp *tail; +} RWLock; + +extern int canrlock(RWLock*); +extern int canwlock(RWLock*); +extern void rlock(RWLock*); +extern void runlock(RWLock*); +extern void wlock(RWLock*); +extern void wunlock(RWLock*); + +extern void** privalloc(void); +extern void privfree(void**); + +/* + * network dialing + */ +#define NETPATHLEN 40 +extern int accept(int, char*); +extern int announce(char*, char*); +extern int dial(char*, char*, char*, int*); +extern void setnetmtpt(char*, int, char*); +extern int hangup(int); +extern int listen(char*, char*); +extern char* netmkaddr(char*, char*, char*); +extern int reject(int, char*, char*); + +/* + * system calls + * + */ +#define STATMAX 65535U /* max length of machine-independent stat structure */ +#define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */ +#define ERRMAX 128 /* max length of error string */ + +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MCACHE 0x0010 /* cache some data */ +#define MMASK 0x0017 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ +#define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ + +#define AEXIST 0 /* accessible: exists */ +#define AEXEC 1 /* execute access */ +#define AWRITE 2 /* write access */ +#define AREAD 4 /* read access */ + + +/* Segattch */ +#define SG_RONLY 0040 /* read only */ +#define SG_CEXEC 0100 /* detach on exec */ + +#define NCONT 0 /* continue after note */ +#define NDFLT 1 /* terminate after note */ +#define NSAVE 2 /* clear note but hold state */ +#define NRSTR 3 /* restore saved state */ + +/* rfork */ +enum +{ + RFNAMEG = (1<<0), + RFENVG = (1<<1), + RFFDG = (1<<2), + RFNOTEG = (1<<3), + RFPROC = (1<<4), + RFMEM = (1<<5), + RFNOWAIT = (1<<6), + RFCNAMEG = (1<<10), + RFCENVG = (1<<11), + RFCFDG = (1<<12), + RFREND = (1<<13), + RFNOMNT = (1<<14) +}; + +/* bits in Qid.type */ +#define QTDIR 0x80 /* type bit for directories */ +#define QTAPPEND 0x40 /* type bit for append only files */ +#define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ +#define QTAUTH 0x08 /* type bit for authentication file */ +#define QTFILE 0x00 /* plain file */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMAUTH 0x08000000 /* mode bit for authentication file */ +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +typedef +struct Qid +{ + uvlong path; + ulong vers; + uchar type; +} Qid; + +typedef +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +} Dir; + +extern Dir* dirstat(char*); +extern Dir* dirfstat(int); +extern int dirwstat(char*, Dir*); +extern int dirfwstat(int, Dir*); +extern long dirread(int, Dir**); +extern void nulldir(Dir*); +extern long dirreadall(int, Dir**); + +typedef +struct Waitmsg +{ + int pid; /* of loved one */ + ulong time[3]; /* of loved one & descendants */ + char *msg; +} Waitmsg; + +typedef +struct IOchunk +{ + void *addr; + ulong len; +} IOchunk; + +extern void _exits(char*); + +extern void abort(void); +extern int access(char*, int); +extern long alarm(ulong); +extern int await(char*, int); +extern int bind(char*, char*, int); +extern int brk(void*); +extern int chdir(char*); +extern int close(int); +extern int create(char*, int, ulong); +extern int dup(int, int); +extern int errstr(char*, uint); +extern int exec(char*, char*[]); +extern int execl(char*, ...); +extern int fork(void); +extern int rfork(int); +extern int fauth(int, char*); +extern int fstat(int, uchar*, int); +extern int fwstat(int, uchar*, int); +extern int fversion(int, int, char*, int); +extern int mount(int, int, char*, int, char*); +extern int unmount(char*, char*); +extern int noted(int); +extern int notify(void(*)(void*, char*)); +extern int open(char*, int); +extern int fd2path(int, char*, int); +extern int pipe(int*); +extern long pread(int, void*, long, vlong); +extern long preadv(int, IOchunk*, int, vlong); +extern long pwrite(int, void*, long, vlong); +extern long pwritev(int, IOchunk*, int, vlong); +extern long read(int, void*, long); +extern long readn(int, void*, long); +extern long readv(int, IOchunk*, int); +extern int remove(char*); +extern void* sbrk(ulong); +extern long oseek(int, long, int); +extern vlong seek(int, vlong, int); +extern long segattach(int, char*, void*, ulong); +extern int segbrk(void*, void*); +extern int segdetach(void*); +extern int segflush(void*, ulong); +extern int segfree(void*, ulong); +extern int sleep(long); +extern int stat(char*, uchar*, int); +extern Waitmsg* wait(void); +extern int waitpid(void); +extern long write(int, void*, long); +extern long writev(int, IOchunk*, int); +extern int wstat(char*, uchar*, int); +extern void* rendezvous(void*, void*); + +extern int getpid(void); +extern int getppid(void); +extern void rerrstr(char*, uint); +extern char* sysname(void); +extern void werrstr(char*, ...); +#pragma varargck argpos werrstr 1 + +extern char *argv0; +#define ARGBEGIN for((argv0||(argv0=*argv)),argv++,argc--;\ + argv[0] && argv[0][0]=='-' && argv[0][1];\ + argc--, argv++) {\ + char *_args, *_argt;\ + Rune _argc;\ + _args = &argv[0][1];\ + if(_args[0]=='-' && _args[1]==0){\ + argc--; argv++; break;\ + }\ + _argc = 0;\ + while(*_args && (_args += chartorune(&_argc, _args)))\ + switch(_argc) +#define ARGEND SET(_argt);USED(_argt,_argc,_args);}USED(argv, argc); +#define ARGF() (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): 0)) +#define EARGF(x) (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0))) + +#define ARGC() _argc + +/* + * Extensions for Inferno to basic libc.h + */ + +#define setbinmode() +#undef __LITTLE_ENDIAN + +/* + * Extensions for emu kernel emulation + */ +#ifdef EMU + +extern Proc** Xup; +#define up (*Xup) + +typedef struct FPU FPU; + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +typedef struct FPU FPU; +struct FPU +{ + double fpreg[32]; + union { + double fpscrd; + struct { + ulong pad; + ulong fpscr; + }; + }; +}; + +typedef jmp_buf osjmpbuf; +#define ossetjmp(buf) setjmp(buf) + +#endif diff --git a/Plan9/power/include/u.h b/Plan9/power/include/u.h new file mode 100644 index 00000000..61e7d236 --- /dev/null +++ b/Plan9/power/include/u.h @@ -0,0 +1,84 @@ +#define nil ((void*)0) +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned long ulong; +typedef unsigned int uint; +typedef signed char schar; +typedef long long vlong; +typedef unsigned long long uvlong; +typedef unsigned long uintptr; +typedef ushort Rune; +typedef union FPdbleword FPdbleword; +typedef long jmp_buf[2]; +#define JMPBUFSP 0 +#define JMPBUFPC 1 +#define JMPBUFDPC 0 +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned char u8int; +typedef unsigned short u16int; +typedef unsigned int u32int; +typedef unsigned long long u64int; + +/* FPSCR */ +#define FPSFX (1<<31) /* exception summary (sticky) */ +#define FPSEX (1<<30) /* enabled exception summary */ +#define FPSVX (1<<29) /* invalid operation exception summary */ +#define FPSOX (1<<28) /* overflow exception OX (sticky) */ +#define FPSUX (1<<27) /* underflow exception UX (sticky) */ +#define FPSZX (1<<26) /* zero divide exception ZX (sticky) */ +#define FPSXX (1<<25) /* inexact exception XX (sticky) */ +#define FPSVXSNAN (1<<24) /* invalid operation exception for SNaN (sticky) */ +#define FPSVXISI (1<<23) /* invalid operation exception for ∞-∞ (sticky) */ +#define FPSVXIDI (1<<22) /* invalid operation exception for ∞/∞ (sticky) */ +#define FPSVXZDZ (1<<21) /* invalid operation exception for 0/0 (sticky) */ +#define FPSVXIMZ (1<<20) /* invalid operation exception for ∞*0 (sticky) */ +#define FPSVXVC (1<<19) /* invalid operation exception for invalid compare (sticky) */ +#define FPSFR (1<<18) /* fraction rounded */ +#define FPSFI (1<<17) /* fraction inexact */ +#define FPSFPRF (1<<16) /* floating point result class */ +#define FPSFPCC (0xF<<12) /* <, >, =, unordered */ +#define FPVXCVI (1<<8) /* enable exception for invalid integer convert (sticky) */ +#define FPVE (1<<7) /* invalid operation exception enable */ +#define FPOVFL (1<<6) /* enable overflow exceptions */ +#define FPUNFL (1<<5) /* enable underflow */ +#define FPZDIV (1<<4) /* enable zero divide */ +#define FPINEX (1<<3) /* enable inexact exceptions */ +#define FPRMASK (3<<0) /* rounding mode */ +#define FPRNR (0<<0) +#define FPRZ (1<<0) +#define FPRPINF (2<<0) +#define FPRNINF (3<<0) +#define FPPEXT 0 +#define FPPSGL 0 +#define FPPDBL 0 +#define FPPMASK 0 +#define FPINVAL FPVE + +#define FPAOVFL FPSOX +#define FPAINEX FPSXX +#define FPAUNFL FPSUX +#define FPAZDIV FPSZX +#define FPAINVAL FPSVX + +union FPdbleword +{ + double x; + struct { /* big endian */ + ulong hi; + ulong lo; + }; +}; + +typedef char* va_list; +#define va_start(list, start) list =\ + (sizeof(start) < 4?\ + (char*)((int*)&(start)+1):\ + (char*)(&(start)+1)) +#define va_end(list)\ + USED(list) +#define va_arg(list, mode)\ + ((sizeof(mode) == 1)?\ + ((mode*)(list += 4))[-1]:\ + (sizeof(mode) == 2)?\ + ((mode*)(list += 4))[-1]:\ + ((mode*)(list += sizeof(mode)))[-1]) diff --git a/Plan9/sparc/include/lib9.h b/Plan9/sparc/include/lib9.h new file mode 100644 index 00000000..55c99d9d --- /dev/null +++ b/Plan9/sparc/include/lib9.h @@ -0,0 +1,679 @@ +#include +typedef unsigned long size_t; + +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) +#define assert(x) if(x){}else _assert("x") + +/* + * mem routines + */ +extern void* memccpy(void*, void*, int, ulong); +extern void* memset(void*, int, ulong); +extern int memcmp(void*, void*, ulong); +extern void* memcpy(void*, void*, ulong); +extern void* memmove(void*, void*, ulong); +extern void* memchr(void*, int, ulong); + +/* + * string routines + */ +extern char* strcat(char*, char*); +extern char* strchr(char*, int); +extern int strcmp(char*, char*); +extern char* strcpy(char*, char*); +extern char* strecpy(char*, char*, char*); +extern char* strdup(char*); +extern char* strncat(char*, char*, long); +extern char* strncpy(char*, char*, long); +extern int strncmp(char*, char*, long); +extern char* strpbrk(char*, char*); +extern char* strrchr(char*, int); +extern char* strtok(char*, char*); +extern long strlen(char*); +extern long strspn(char*, char*); +extern long strcspn(char*, char*); +extern char* strstr(char*, char*); +extern int cistrncmp(char*, char*, int); +extern int cistrcmp(char*, char*); +extern char* cistrstr(char*, char*); +extern int tokenize(char*, char**, int); + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80, /* decoding error in UTF */ +}; + +/* + * rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern int runelen(long); +extern int runenlen(Rune*, int); +extern int fullrune(char*, int); +extern int utflen(char*); +extern int utfnlen(char*, long); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); +extern char* utfutf(char*, char*); +extern char* utfecpy(char*, char*, char*); + +extern Rune* runestrcat(Rune*, Rune*); +extern Rune* runestrchr(Rune*, Rune); +extern int runestrcmp(Rune*, Rune*); +extern Rune* runestrcpy(Rune*, Rune*); +extern Rune* runestrncpy(Rune*, Rune*, long); +extern Rune* runestrecpy(Rune*, Rune*, Rune*); +extern Rune* runestrdup(Rune*); +extern Rune* runestrncat(Rune*, Rune*, long); +extern int runestrncmp(Rune*, Rune*, long); +extern Rune* runestrrchr(Rune*, Rune); +extern long runestrlen(Rune*); +extern Rune* runestrstr(Rune*, Rune*); + +extern Rune tolowerrune(Rune); +extern Rune totitlerune(Rune); +extern Rune toupperrune(Rune); +extern int isalpharune(Rune); +extern int islowerrune(Rune); +extern int isspacerune(Rune); +extern int istitlerune(Rune); +extern int isupperrune(Rune); + +/* + * malloc + */ +extern void* malloc(ulong); +extern void* mallocz(ulong, int); +extern void free(void*); +extern ulong msize(void*); +extern void* calloc(ulong, ulong); +extern void* realloc(void*, ulong); +extern void setmalloctag(void*, ulong); +extern void setrealloctag(void*, ulong); +extern ulong getmalloctag(void*); +extern ulong getrealloctag(void*); +extern void* malloctopoolblock(void*); + +/* + * print routines + */ +typedef struct Fmt Fmt; +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + ulong flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + + FmtFlag = FmtByte << 1 +}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); +extern int runefmtstrinit(Fmt*); +extern Rune* runefmtstrflush(Fmt*); + +#pragma varargck argpos fmtprint 2 +#pragma varargck argpos fprint 2 +#pragma varargck argpos print 1 +#pragma varargck argpos runeseprint 3 +#pragma varargck argpos runesmprint 1 +#pragma varargck argpos runesnprint 3 +#pragma varargck argpos runesprint 2 +#pragma varargck argpos seprint 3 +#pragma varargck argpos smprint 1 +#pragma varargck argpos snprint 3 +#pragma varargck argpos sprint 2 +#pragma varargck argpos vseprint 3 +#pragma varargck argpos vsnprint 3 + +#pragma varargck type "lld" vlong +#pragma varargck type "llx" vlong +#pragma varargck type "lld" uvlong +#pragma varargck type "llx" uvlong +#pragma varargck type "ld" long +#pragma varargck type "lx" long +#pragma varargck type "ld" ulong +#pragma varargck type "lx" ulong +#pragma varargck type "d" int +#pragma varargck type "x" int +#pragma varargck type "c" int +#pragma varargck type "C" int +#pragma varargck type "d" uint +#pragma varargck type "x" uint +#pragma varargck type "c" uint +#pragma varargck type "C" uint +#pragma varargck type "f" double +#pragma varargck type "e" double +#pragma varargck type "g" double +#pragma varargck type "s" char* +#pragma varargck type "q" char* +#pragma varargck type "S" Rune* +#pragma varargck type "Q" Rune* +#pragma varargck type "r" void +#pragma varargck type "%" void +#pragma varargck type "n" int* +#pragma varargck type "p" void* +#pragma varargck flag ',' +#pragma varargck type "<" void* +#pragma varargck type "[" void* +#pragma varargck type "H" void* + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int dorfmt(Fmt*, Rune*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); +extern int fmtrunestrcpy(Fmt*, Rune*); +/* + * error string for %r + * supplied on per os basis, not part of fmt library + */ +extern int errfmt(Fmt *f); + +/* + * quoted strings + */ +extern char *unquotestrdup(char*); +extern Rune *unquoterunestrdup(Rune*); +extern char *quotestrdup(char*); +extern Rune *quoterunestrdup(Rune*); +extern int quotestrfmt(Fmt*); +extern int quoterunestrfmt(Fmt*); +extern void quotefmtinstall(void); +extern int (*doquote)(int); + +/* + * random number + */ +extern void srand(long); +extern int rand(void); +extern int nrand(int); +extern long lrand(void); +extern long lnrand(long); +extern double frand(void); +extern ulong truerand(void); +extern ulong ntruerand(ulong); + +/* + * math + */ +extern ulong getfcr(void); +extern void setfsr(ulong); +extern ulong getfsr(void); +extern void setfcr(ulong); +extern double NaN(void); +extern double Inf(int); +extern int isNaN(double); +extern int isInf(double, int); + +extern double pow(double, double); +extern double atan2(double, double); +extern double fabs(double); +extern double atan(double); +extern double log(double); +extern double log10(double); +extern double exp(double); +extern double floor(double); +extern double ceil(double); +extern double hypot(double, double); +extern double sin(double); +extern double cos(double); +extern double tan(double); +extern double asin(double); +extern double acos(double); +extern double sinh(double); +extern double cosh(double); +extern double tanh(double); +extern double sqrt(double); +extern double fmod(double, double); + +#define HUGE 3.4028234e38 +#define PIO2 1.570796326794896619231e0 +#define PI (PIO2+PIO2) + +/* + * Time-of-day + */ + +typedef +struct Tm +{ + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; + int yday; + char zone[4]; + int tzoff; +} Tm; + +extern Tm* gmtime(long); +extern Tm* localtime(long); +extern char* asctime(Tm*); +extern char* ctime(long); +extern double cputime(void); +extern long times(long*); +extern long tm2sec(Tm*); +extern vlong nsec(void); + +/* + * one-of-a-kind + */ +enum +{ + PNPROC = 1, + PNGROUP = 2, +}; + +extern void _assert(char*); +extern int abs(int); +extern int atexit(void(*)(void)); +extern void atexitdont(void(*)(void)); +extern int atnotify(int(*)(void*, char*), int); +extern double atof(char*); +extern int atoi(char*); +extern long atol(char*); +extern double charstod(int(*)(void*), void*); +extern char* cleanname(char*); +extern int decrypt(void*, void*, int); +extern int encrypt(void*, void*, int); +extern int dec64(uchar*, int, char*, int); +extern int enc64(char*, int, uchar*, int); +extern int dec32(uchar*, int, char*, int); +extern int enc32(char*, int, uchar*, int); +extern int dec16(uchar*, int, char*, int); +extern int enc16(char*, int, uchar*, int); +extern int encodefmt(Fmt*); +extern void exits(char*); +extern double frexp(double, int*); +extern ulong getcallerpc(void*); +extern char* getenv(char*); +extern int getfields(char*, char**, int, int, char*); +extern char* getuser(void); +extern char* getwd(char*, int); +extern int iounit(int); +extern long labs(long); +extern double ldexp(double, int); +extern void longjmp(jmp_buf, int); +extern char* mktemp(char*); +extern double modf(double, double*); +extern int netcrypt(void*, void*); +extern void notejmp(void*, jmp_buf, int); +extern void perror(char*); +extern int postnote(int, int, char *); +extern double pow10(int); +extern double ipow10(int); +extern int putenv(char*, char*); +extern void qsort(void*, long, long, int (*)(void*, void*)); +extern void* sbrk(ulong); +extern int setjmp(jmp_buf); +extern double strtod(char*, char**); +extern long strtol(char*, char**, int); +extern ulong strtoul(char*, char**, int); +extern vlong strtoll(char*, char**, int); +extern uvlong strtoull(char*, char**, int); +extern void sysfatal(char*, ...); +#pragma varargck argpos sysfatal 1 +extern void syslog(int, char*, char*, ...); +#pragma varargck argpos syslog 3 +extern long time(long*); +extern int tolower(int); +extern int toupper(int); + +/* + * synchronization + */ +typedef +struct Lock { + ulong val; +} Lock; + +extern ulong _tas(ulong*); + +extern void lock(Lock*); +extern void unlock(Lock*); +extern int canlock(Lock*); + +typedef struct QLp QLp; +struct QLp +{ + int inuse; + QLp *next; + char state; +}; + +typedef +struct QLock +{ + Lock lock; + int locked; + QLp *head; + QLp *tail; +} QLock; + +extern void qlock(QLock*); +extern void qunlock(QLock*); +extern int canqlock(QLock*); +extern void _qlockinit(ulong (*)(ulong, ulong)); /* called only by the thread library */ + +typedef +struct RWLock +{ + Lock lock; + int readers; /* number of readers */ + int writer; /* number of writers */ + QLp *head; /* list of waiting processes */ + QLp *tail; +} RWLock; + +extern int canrlock(RWLock*); +extern int canwlock(RWLock*); +extern void rlock(RWLock*); +extern void runlock(RWLock*); +extern void wlock(RWLock*); +extern void wunlock(RWLock*); + +extern void** privalloc(void); +extern void privfree(void**); + +/* + * network dialing + */ +#define NETPATHLEN 40 +extern int accept(int, char*); +extern int announce(char*, char*); +extern int dial(char*, char*, char*, int*); +extern void setnetmtpt(char*, int, char*); +extern int hangup(int); +extern int listen(char*, char*); +extern char* netmkaddr(char*, char*, char*); +extern int reject(int, char*, char*); + +/* + * system calls + * + */ +#define STATMAX 65535U /* max length of machine-independent stat structure */ +#define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */ +#define ERRMAX 128 /* max length of error string */ + +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MCACHE 0x0010 /* cache some data */ +#define MMASK 0x0017 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ +#define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ + +#define AEXIST 0 /* accessible: exists */ +#define AEXEC 1 /* execute access */ +#define AWRITE 2 /* write access */ +#define AREAD 4 /* read access */ + + +/* Segattch */ +#define SG_RONLY 0040 /* read only */ +#define SG_CEXEC 0100 /* detach on exec */ + +#define NCONT 0 /* continue after note */ +#define NDFLT 1 /* terminate after note */ +#define NSAVE 2 /* clear note but hold state */ +#define NRSTR 3 /* restore saved state */ + +/* rfork */ +enum +{ + RFNAMEG = (1<<0), + RFENVG = (1<<1), + RFFDG = (1<<2), + RFNOTEG = (1<<3), + RFPROC = (1<<4), + RFMEM = (1<<5), + RFNOWAIT = (1<<6), + RFCNAMEG = (1<<10), + RFCENVG = (1<<11), + RFCFDG = (1<<12), + RFREND = (1<<13), + RFNOMNT = (1<<14) +}; + +/* bits in Qid.type */ +#define QTDIR 0x80 /* type bit for directories */ +#define QTAPPEND 0x40 /* type bit for append only files */ +#define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ +#define QTAUTH 0x08 /* type bit for authentication file */ +#define QTFILE 0x00 /* plain file */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMAUTH 0x08000000 /* mode bit for authentication file */ +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +typedef +struct Qid +{ + uvlong path; + ulong vers; + uchar type; +} Qid; + +typedef +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +} Dir; + +extern Dir* dirstat(char*); +extern Dir* dirfstat(int); +extern int dirwstat(char*, Dir*); +extern int dirfwstat(int, Dir*); +extern long dirread(int, Dir**); +extern void nulldir(Dir*); +extern long dirreadall(int, Dir**); + +typedef +struct Waitmsg +{ + int pid; /* of loved one */ + ulong time[3]; /* of loved one & descendants */ + char *msg; +} Waitmsg; + +typedef +struct IOchunk +{ + void *addr; + ulong len; +} IOchunk; + +extern void _exits(char*); + +extern void abort(void); +extern int access(char*, int); +extern long alarm(ulong); +extern int await(char*, int); +extern int bind(char*, char*, int); +extern int brk(void*); +extern int chdir(char*); +extern int close(int); +extern int create(char*, int, ulong); +extern int dup(int, int); +extern int errstr(char*, uint); +extern int exec(char*, char*[]); +extern int execl(char*, ...); +extern int fork(void); +extern int rfork(int); +extern int fauth(int, char*); +extern int fstat(int, uchar*, int); +extern int fwstat(int, uchar*, int); +extern int fversion(int, int, char*, int); +extern int mount(int, int, char*, int, char*); +extern int unmount(char*, char*); +extern int noted(int); +extern int notify(void(*)(void*, char*)); +extern int open(char*, int); +extern int fd2path(int, char*, int); +extern int pipe(int*); +extern long pread(int, void*, long, vlong); +extern long preadv(int, IOchunk*, int, vlong); +extern long pwrite(int, void*, long, vlong); +extern long pwritev(int, IOchunk*, int, vlong); +extern long read(int, void*, long); +extern long readn(int, void*, long); +extern long readv(int, IOchunk*, int); +extern int remove(char*); +extern void* sbrk(ulong); +extern long oseek(int, long, int); +extern vlong seek(int, vlong, int); +extern long segattach(int, char*, void*, ulong); +extern int segbrk(void*, void*); +extern int segdetach(void*); +extern int segflush(void*, ulong); +extern int segfree(void*, ulong); +extern int sleep(long); +extern int stat(char*, uchar*, int); +extern Waitmsg* wait(void); +extern int waitpid(void); +extern long write(int, void*, long); +extern long writev(int, IOchunk*, int); +extern int wstat(char*, uchar*, int); +extern void* rendezvous(void*, void*); + +extern int getpid(void); +extern int getppid(void); +extern void rerrstr(char*, uint); +extern char* sysname(void); +extern void werrstr(char*, ...); +#pragma varargck argpos werrstr 1 + +extern char *argv0; +#define ARGBEGIN for((argv0||(argv0=*argv)),argv++,argc--;\ + argv[0] && argv[0][0]=='-' && argv[0][1];\ + argc--, argv++) {\ + char *_args, *_argt;\ + Rune _argc;\ + _args = &argv[0][1];\ + if(_args[0]=='-' && _args[1]==0){\ + argc--; argv++; break;\ + }\ + _argc = 0;\ + while(*_args && (_args += chartorune(&_argc, _args)))\ + switch(_argc) +#define ARGEND SET(_argt);USED(_argt,_argc,_args);}USED(argv, argc); +#define ARGF() (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): 0)) +#define EARGF(x) (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0))) + +#define ARGC() _argc + +/* + * Extensions for Inferno to basic libc.h + */ + +#define setbinmode() +#undef __LITTLE_ENDIAN + +/* + * Extensions for emu kernel emulation + */ +#ifdef EMU + +extern Proc** Xup; +#define up (*Xup) + +typedef struct FPU FPU; + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +struct FPU +{ + ulong fsr; +}; + +typedef jmp_buf osjmpbuf; +#define ossetjmp(buf) setjmp(buf) + +#endif diff --git a/Plan9/sparc/include/os.h b/Plan9/sparc/include/os.h new file mode 100644 index 00000000..814362b3 --- /dev/null +++ b/Plan9/sparc/include/os.h @@ -0,0 +1,17 @@ +/* + * Solaris 2.5/sparc + */ + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +typedef struct FPU FPU; +struct FPU +{ + ulong fsr; +}; + +extern Proc *getup(); +#define up (getup()) + +#define BIGEND diff --git a/Plan9/sparc/include/u.h b/Plan9/sparc/include/u.h new file mode 100644 index 00000000..6c0bd3f2 --- /dev/null +++ b/Plan9/sparc/include/u.h @@ -0,0 +1,64 @@ +#define nil ((void*)0) +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned long ulong; +typedef unsigned int uint; +typedef signed char schar; +typedef long long vlong; +typedef unsigned long long uvlong; +typedef unsigned long uintptr; +typedef ushort Rune; +typedef union FPdbleword FPdbleword; +typedef long jmp_buf[2]; +#define JMPBUFSP 0 +#define JMPBUFPC 1 +#define JMPBUFDPC (-8) +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned char u8int; +typedef unsigned short u16int; +typedef unsigned int u32int; +typedef unsigned long long u64int; + +/* FCR */ +#define FPINEX (1<<23) +#define FPOVFL (1<<26) +#define FPUNFL (1<<25) +#define FPZDIV (1<<24) +#define FPRNR (0<<30) +#define FPRZ (1<<30) +#define FPINVAL (1<<27) +#define FPRPINF (2<<30) +#define FPRNINF (3<<30) +#define FPRMASK (3<<30) +#define FPPEXT 0 +#define FPPSGL 0 +#define FPPDBL 0 +#define FPPMASK 0 +/* FSR */ +#define FPAINEX (1<<5) +#define FPAZDIV (1<<6) +#define FPAUNFL (1<<7) +#define FPAOVFL (1<<8) +#define FPAINVAL (1<<9) +union FPdbleword +{ + double x; + struct { /* big endian */ + ulong hi; + ulong lo; + }; +}; + +typedef char* va_list; +#define va_start(list, start) list =\ + (sizeof(start) < 4?\ + (char*)((int*)&(start)+1):\ + (char*)(&(start)+1)) +#define va_end(list)\ + USED(list) +#define va_arg(list, mode)\ + ((sizeof(mode) == 1)?\ + ((mode*)(list += 4))[-1]:\ + (sizeof(mode) == 2)?\ + ((mode*)(list += 4))[-1]:\ + ((mode*)(list += sizeof(mode)))[-1]) diff --git a/README.gcode b/README.gcode new file mode 100644 index 00000000..5c876b22 --- /dev/null +++ b/README.gcode @@ -0,0 +1,11 @@ +This subversion distribution contains the Inferno distribution trees starting with 20060303 EXCLUDING +derived executable files in the per-platform bin directories (except for those needed +on a given platform to build anything, such as mk or yacc). It also EXCLUDES any non-Free +components, mainly the Bigelow & Holmes fonts, which can be obtained from the primary +Inferno distribution. Since the fonts hardly ever change, you need only do that once. +The /dis files are also derived, of course, but they are included; +the extra space is modest these days, and it helps support some regression testing. + +The overall licence is GPLv2, to accord with Google understandable limitation to one licence, +since GPLv2 arguably imposes most constraints on redistribution (of the set we use in the full distribution), +but where less restrictive licence is available, and could be used, that is noted in a NOTICE file. diff --git a/Solaris/sparc/bin/data2c b/Solaris/sparc/bin/data2c new file mode 100755 index 00000000..ceaab39e Binary files /dev/null and b/Solaris/sparc/bin/data2c differ diff --git a/Solaris/sparc/bin/mk b/Solaris/sparc/bin/mk new file mode 100755 index 00000000..a30757c1 Binary files /dev/null and b/Solaris/sparc/bin/mk differ diff --git a/Solaris/sparc/bin/mkext b/Solaris/sparc/bin/mkext new file mode 100755 index 00000000..7c51ccbd Binary files /dev/null and b/Solaris/sparc/bin/mkext differ diff --git a/Solaris/sparc/bin/yacc b/Solaris/sparc/bin/yacc new file mode 100755 index 00000000..15ac558a Binary files /dev/null and b/Solaris/sparc/bin/yacc differ diff --git a/Solaris/sparc/include/fpuctl.h b/Solaris/sparc/include/fpuctl.h new file mode 100644 index 00000000..e91ee09b --- /dev/null +++ b/Solaris/sparc/include/fpuctl.h @@ -0,0 +1,75 @@ +/* This code is a little awkward. If somebody who understands Solaris + better would tell me an idiomatic way to invoke equivalent + behavior, I'd be grateful. ehg@bell-labs.com */ + +/* + * accrued exception bits in the fsr + */ +#define FPAINEX (1<<5) +#define FPAOVFL (1<<8) +#define FPAUNFL (1<<7) +#define FPAZDIV (1<<6) +#define FPAINVAL (1<<9) + +/* + * exception enable bits in the fsr + */ +#define FPINEX (1<<23) +#define FPOVFL (1<<26) +#define FPUNFL (1<<25) +#define FPZDIV (1<<24) +#define FPINVAL (1<<27) + +/* + * rounding + */ +#define FPRMASK (3<<30) +#define FPRNR (0<<30) +#define FPRNINF (3<<30) +#define FPRPINF (2<<30) +#define FPRZ (1<<30) + +/* + * precision + */ +#define FPPDBL 0 + +#define FPFCR (FPRMASK|FPINEX|FPOVFL|FPUNFL|FPZDIV|FPINVAL) +#define FPFSR (FPAINEX|FPAOVFL|FPAUNFL|FPAZDIV|FPAINVAL) + +static ulong +getfcr(void) +{ + ulong v; + + asm(" st %fsr, [%fp-8]"); + return v; +} + +static void +setfcr(ulong v) +{ + ulong vv; + + vv = (getfcr() & ~FPFCR) | (v & FPFCR); + asm(" ld [%fp-4], %fsr"); +} + +static ulong +getfsr(void) +{ + ulong v; + + asm(" st %fsr, [%fp-8]"); + return v; +} + +static void +setfsr(ulong v) +{ + ulong vv; + + vv = (getfsr() & ~FPFSR) | (v & FPFSR); + asm(" ld [%fp-4], %fsr"); +} + diff --git a/Solaris/sparc/include/lib9.h b/Solaris/sparc/include/lib9.h new file mode 100644 index 00000000..cfb8f4a0 --- /dev/null +++ b/Solaris/sparc/include/lib9.h @@ -0,0 +1,476 @@ +#define _POSIX_SOURCE +#define _POSIX_C_SOURCE 1 +#define _LARGEFILE64_SOURCE 1 +#define _FILE_OFFSET_BITS 64 +#include +#include +#include +#define __EXTENSIONS__ +#include +#undef __EXTENSIONS__ +#include +#include +#include "math.h" +#include +#include +#include +#include + +#define getwd infgetwd + +#ifndef EMU +typedef struct Proc Proc; +#endif + +#define nil ((void*)0) + +typedef unsigned char uchar; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef signed char schar; +typedef unsigned short ushort; +typedef unsigned short Rune; +typedef long long int vlong; +typedef unsigned long long int uvlong; +typedef unsigned int u32int; +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned short u16int; +typedef unsigned char u8int; +typedef unsigned long uintptr; + +#define USED(x) if(x){}else{} +#define SET(x) + +#undef nelem +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#undef offsetof +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) +#undef assert +#define assert(x) if(x){}else _assert("x") + +/* + * most mem and string routines are declared by ANSI/POSIX files above + */ + +extern char* strecpy(char*, char*, char*); +extern char* strdup(const char*); +extern int cistrncmp(char*, char*, int); +extern int cistrcmp(char*, char*); +extern char* cistrstr(char*, char*); +extern int tokenize(char*, char**, int); + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80 /* decoding error in UTF */ +}; + +/* + * rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern int runelen(long); +extern int runenlen(Rune*, int); +extern int fullrune(char*, int); +extern int utflen(char*); +extern int utfnlen(char*, long); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); +extern char* utfutf(char*, char*); +extern char* utfecpy(char*, char*, char*); + +extern Rune* runestrcat(Rune*, Rune*); +extern Rune* runestrchr(Rune*, Rune); +extern int runestrcmp(Rune*, Rune*); +extern Rune* runestrcpy(Rune*, Rune*); +extern Rune* runestrncpy(Rune*, Rune*, long); +extern Rune* runestrecpy(Rune*, Rune*, Rune*); +extern Rune* runestrdup(Rune*); +extern Rune* runestrncat(Rune*, Rune*, long); +extern int runestrncmp(Rune*, Rune*, long); +extern Rune* runestrrchr(Rune*, Rune); +extern long runestrlen(Rune*); +extern Rune* runestrstr(Rune*, Rune*); + +extern Rune tolowerrune(Rune); +extern Rune totitlerune(Rune); +extern Rune toupperrune(Rune); +extern int isalpharune(Rune); +extern int islowerrune(Rune); +extern int isspacerune(Rune); +extern int istitlerune(Rune); +extern int isupperrune(Rune); + +/* + * malloc + */ +extern void* malloc(size_t); +extern void* mallocz(ulong, int); +extern void free(void*); +extern ulong msize(void*); +extern void* calloc(size_t, size_t); +extern void* realloc(void*, size_t); +extern void setmalloctag(void*, ulong); +extern void setrealloctag(void*, ulong); +extern ulong getmalloctag(void*); +extern ulong getrealloctag(void*); +extern void* malloctopoolblock(void*); + +/* + * print routines + */ +typedef struct Fmt Fmt; +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + ulong flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + + FmtFlag = FmtByte << 1 +}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); +extern int runefmtstrinit(Fmt*); +extern Rune* runefmtstrflush(Fmt*); + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int dorfmt(Fmt*, Rune*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); +extern int fmtrunestrcpy(Fmt*, Rune*); +/* + * error string for %r + * supplied on per os basis, not part of fmt library + */ +extern int errfmt(Fmt *f); + +/* + * quoted strings + */ +extern char *unquotestrdup(char*); +extern Rune *unquoterunestrdup(Rune*); +extern char *quotestrdup(char*); +extern Rune *quoterunestrdup(Rune*); +extern int quotestrfmt(Fmt*); +extern int quoterunestrfmt(Fmt*); +extern void quotefmtinstall(void); +extern int (*doquote)(int); + +/* + * random number + */ +extern int nrand(int); + +/* + * random number + */ +extern ulong truerand(void); +extern ulong ntruerand(ulong); + +/* + * math + */ +extern int isNaN(double); +extern int isInf(double, int); +extern double pow(double, double); + +/* + * Time-of-day + */ + +typedef struct Tm Tm; +struct Tm { + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; + int yday; + char zone[4]; + int tzoff; +}; +extern vlong osnsec(void); +#define nsec osnsec + +/* + * one-of-a-kind + */ +extern void _assert(char*); +extern double charstod(int(*)(void*), void*); +extern char* cleanname(char*); +extern double frexp(double, int*); +extern ulong getcallerpc(void*); +extern int getfields(char*, char**, int, int, char*); +extern char* getuser(void); +extern char* getwd(char*, int); +extern double ipow10(int); +extern double ldexp(double, int); +extern double modf(double, double*); +#define pow10 infpow10 +extern double pow10(int); +extern vlong strtoll(const char*, char**, int); +extern uvlong strtoull(const char*, char**, int); +extern void sysfatal(char*, ...); +extern int dec64(uchar*, int, char*, int); +extern int enc64(char*, int, uchar*, int); +extern int dec32(uchar*, int, char*, int); +extern int enc32(char*, int, uchar*, int); +extern int dec16(uchar*, int, char*, int); +extern int enc16(char*, int, uchar*, int); +extern int encodefmt(Fmt*); + +/* + * synchronization + */ +typedef +struct Lock { + ulong val; + int pid; +} Lock; + +extern ulong _tas(ulong*); + +extern void lock(Lock*); +extern void unlock(Lock*); +extern int canlock(Lock*); + +typedef struct QLock QLock; +struct QLock +{ + Lock use; /* to access Qlock structure */ + Proc *head; /* next process waiting for object */ + Proc *tail; /* last process waiting for object */ + int locked; /* flag */ +}; + +extern void qlock(QLock*); +extern void qunlock(QLock*); +extern int canqlock(QLock*); +extern void _qlockinit(ulong (*)(ulong, ulong)); /* called only by the thread library */ + +typedef +struct RWLock +{ + Lock l; /* Lock modify lock */ + QLock x; /* Mutual exclusion lock */ + QLock k; /* Lock for waiting writers */ + int readers; /* Count of readers in lock */ +} RWLock; + +extern int canrlock(RWLock*); +extern int canwlock(RWLock*); +extern void rlock(RWLock*); +extern void runlock(RWLock*); +extern void wlock(RWLock*); +extern void wunlock(RWLock*); + +/* + * network dialing + */ +#define NETPATHLEN 40 + +/* + * system calls + * + */ +#define STATMAX 65535U /* max length of machine-independent stat structure */ +#define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */ +#define ERRMAX 128 /* max length of error string */ + +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MCACHE 0x0010 /* cache some data */ +#define MMASK 0x0017 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ +#define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ + +#define AEXIST 0 /* accessible: exists */ +#define AEXEC 1 /* execute access */ +#define AWRITE 2 /* write access */ +#define AREAD 4 /* read access */ + +/* bits in Qid.type */ +#define QTDIR 0x80 /* type bit for directories */ +#define QTAPPEND 0x40 /* type bit for append only files */ +#define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ +#define QTAUTH 0x08 /* type bit for authentication file */ +#define QTFILE 0x00 /* plain file */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMAUTH 0x08000000 /* mode bit for authentication file */ +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +typedef +struct Qid +{ + uvlong path; + ulong vers; + uchar type; +} Qid; + +typedef +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +} Dir; + +extern Dir* dirstat(char*); +extern Dir* dirfstat(int); +extern int dirwstat(char*, Dir*); +extern int dirfwstat(int, Dir*); +extern long dirread(int, Dir**); +extern void nulldir(Dir*); +extern long dirreadall(int, Dir**); + +typedef +struct Waitmsg +{ + int pid; /* of loved one */ + ulong time[3]; /* of loved one & descendants */ + char *msg; +} Waitmsg; + +extern void _exits(char*); + +extern void exits(char*); +extern int create(char*, int, int); +extern int errstr(char*, uint); + +extern void perror(const char*); +extern long readn(int, void*, long); +extern int remove(const char*); +extern void rerrstr(char*, uint); +extern vlong seek(int, vlong, int); +extern int segflush(void*, ulong); +extern void werrstr(char*, ...); + +extern char *argv0; +#define ARGBEGIN for((argv0||(argv0=*argv)),argv++,argc--;\ + argv[0] && argv[0][0]=='-' && argv[0][1];\ + argc--, argv++) {\ + char *_args, *_argt;\ + Rune _argc;\ + _args = &argv[0][1];\ + if(_args[0]=='-' && _args[1]==0){\ + argc--; argv++; break;\ + }\ + _argc = 0;\ + while(*_args && (_args += chartorune(&_argc, _args)))\ + switch(_argc) +#define ARGEND SET(_argt);USED(_argt);USED(_argc); USED(_args);}USED(argv); USED(argc); +#define ARGF() (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): 0)) +#define EARGF(x) (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0))) + +#define ARGC() _argc + +/* + * Extensions for Inferno to basic libc.h + */ + +#define setbinmode() + +/* + * Extensions for emu kernel emulation + */ +#ifdef EMU + +extern Proc* getup(); +#define up (getup()) + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +typedef struct FPU FPU; +struct FPU +{ + ulong fsr; +}; + +typedef sigjmp_buf osjmpbuf; +#define ossetjmp(buf) sigsetjmp(buf, 1) + +#endif diff --git a/acme/acid/Acid.dis b/acme/acid/Acid.dis new file mode 100644 index 00000000..05d87c4f Binary files /dev/null and b/acme/acid/Acid.dis differ diff --git a/acme/acid/Acid0.dis b/acme/acid/Acid0.dis new file mode 100644 index 00000000..afb83b88 Binary files /dev/null and b/acme/acid/Acid0.dis differ diff --git a/acme/acid/guide b/acme/acid/guide new file mode 100644 index 00000000..0fc99511 --- /dev/null +++ b/acme/acid/guide @@ -0,0 +1,2 @@ +Acid pid +Acid -l alef -l symsfile pid diff --git a/acme/acid/readme b/acme/acid/readme new file mode 100644 index 00000000..fcf343be --- /dev/null +++ b/acme/acid/readme @@ -0,0 +1,12 @@ +Capital A Acid is a rudimentary acme interface to the debugger acid. +It uses a win to provide an interactive window for acid. In that window, +a couple of extra acme-specific features are enabled: + +w(command) + runs the command and places its output in a new window. + e.g. w(lstk()) places the stack trace in a distinct window. + +Also, in any such window, text executed with button 2 is +presented as input to acid in the main Acid window. Thus, for +example, one may evaluate variables presented in a stack trace +by `executing' it with button 2. diff --git a/acme/bin/guide b/acme/bin/guide new file mode 100644 index 00000000..31cea12e --- /dev/null +++ b/acme/bin/guide @@ -0,0 +1,5 @@ +win +new command ... +aspell file +adiff file1 file2 +adict -d oed diff --git a/acme/bin/readme b/acme/bin/readme new file mode 100644 index 00000000..941099e8 --- /dev/null +++ b/acme/bin/readme @@ -0,0 +1,25 @@ +This directory and its subdirectory $cputype are always mounted at +the end of /bin for programs run from acme. They hold a collection +of small acme-specific applications: + +win [command] + Create an acme window to serve as a terminal, analogous + to xterm. By default, it runs the shell, rc, but it works with + any interactive program, e.g. hoc. Within the window, + commands executed with button 2 are 'executed' by sending + their text to the standard input of the command, appending + a newline if necessary. +new command + Run the non-interactive command, placing its standard and + diagnostic output in a new window. +aspell file + Run spell on the file, labeling the output with addresses so + misspelled words can be found in context using button 3. +adiff file1 file2 + Run diff on the files, labeling the output with addresses so + changes can be found in context using button 3. +adict + Interactive version of dict(1). Button 3 looks up words and + may be applied to any word in any adict window. + When a word has multiple definitions, indicate the number + (as in acme Mail) to disambiguate. diff --git a/acme/dis/adiff.dis b/acme/dis/adiff.dis new file mode 100644 index 00000000..c8e3bba8 Binary files /dev/null and b/acme/dis/adiff.dis differ diff --git a/acme/dis/agrep.dis b/acme/dis/agrep.dis new file mode 100644 index 00000000..a51e7534 Binary files /dev/null and b/acme/dis/agrep.dis differ diff --git a/acme/dis/awd.dis b/acme/dis/awd.dis new file mode 100644 index 00000000..9b9e5765 Binary files /dev/null and b/acme/dis/awd.dis differ diff --git a/acme/dis/cd.dis b/acme/dis/cd.dis new file mode 100644 index 00000000..c3acf4f5 Binary files /dev/null and b/acme/dis/cd.dis differ diff --git a/acme/dis/new.dis b/acme/dis/new.dis new file mode 100644 index 00000000..a803bc73 Binary files /dev/null and b/acme/dis/new.dis differ diff --git a/acme/dis/spout.dis b/acme/dis/spout.dis new file mode 100644 index 00000000..72732ad4 Binary files /dev/null and b/acme/dis/spout.dis differ diff --git a/acme/dis/win.dis b/acme/dis/win.dis new file mode 100644 index 00000000..ed473574 Binary files /dev/null and b/acme/dis/win.dis differ diff --git a/acme/dis/winm.dis b/acme/dis/winm.dis new file mode 100644 index 00000000..55fe635e Binary files /dev/null and b/acme/dis/winm.dis differ diff --git a/acme/edit/a.dis b/acme/edit/a.dis new file mode 100644 index 00000000..992f7caf Binary files /dev/null and b/acme/edit/a.dis differ diff --git a/acme/edit/c.dis b/acme/edit/c.dis new file mode 100644 index 00000000..48a7ef3b Binary files /dev/null and b/acme/edit/c.dis differ diff --git a/acme/edit/d.dis b/acme/edit/d.dis new file mode 100644 index 00000000..9c77295a Binary files /dev/null and b/acme/edit/d.dis differ diff --git a/acme/edit/e.dis b/acme/edit/e.dis new file mode 100644 index 00000000..8faa3808 Binary files /dev/null and b/acme/edit/e.dis differ diff --git a/acme/edit/g.dis b/acme/edit/g.dis new file mode 100644 index 00000000..15bb20ce Binary files /dev/null and b/acme/edit/g.dis differ diff --git a/acme/edit/guide b/acme/edit/guide new file mode 100644 index 00000000..5beced2e --- /dev/null +++ b/acme/edit/guide @@ -0,0 +1,4 @@ +e file | x '/regexp/' | c 'replacement' +e 'file:0,$' | x '/.*word.*\n/' | p -n +e file | pipe command args ... +New /absolute/file/name diff --git a/acme/edit/i.dis b/acme/edit/i.dis new file mode 100644 index 00000000..4b44420a Binary files /dev/null and b/acme/edit/i.dis differ diff --git a/acme/edit/p.dis b/acme/edit/p.dis new file mode 100644 index 00000000..70202e9b Binary files /dev/null and b/acme/edit/p.dis differ diff --git a/acme/edit/pipe.dis b/acme/edit/pipe.dis new file mode 100644 index 00000000..2e6fc9b1 Binary files /dev/null and b/acme/edit/pipe.dis differ diff --git a/acme/edit/readme b/acme/edit/readme new file mode 100644 index 00000000..e4d29579 --- /dev/null +++ b/acme/edit/readme @@ -0,0 +1,31 @@ +The programs collected in /acme/edit offer a sam-like command interface +to acme windows. The guide file + /acme/edit/guide +holds templates for several editing operations implemented +by external programs. These programs, composed in +a pipeline, refine the sections of a file to be modified. +Thus in sam when one says + x/.*\n/ g/foo/ p +in /acme/edit one runs + x '/.*\n/' | g '/foo/' | p +The e command, unrelated to e in sam, disambiguates file names, collects +lists of names, etc., and produces input suitable for the other tools. +For example: + e '/usr/rob/acme:0,$' | x /oldname/ | c /newname/ +changes oldname to newname in all the files loaded in acme whose names match +the literal text /usr/rob/acme. + +The commands in /acme/edit are + e + x + g + c + d + p + pipe (like sam's | , which can't be used for syntactic reasons) + +p takes a -n flag analogous to grep's -n. There is no s command. +e has a -l flag to produce line numbers instead of the default character numbers. +Its implementation is poor but sufficient for the mundane job of recreating +the occasional line number for tools like acid; its use with the other commands +in this directory is discouraged. diff --git a/acme/edit/x.dis b/acme/edit/x.dis new file mode 100644 index 00000000..2d488e83 Binary files /dev/null and b/acme/edit/x.dis differ diff --git a/acme/mail/Mail.dis b/acme/mail/Mail.dis new file mode 100644 index 00000000..6bd9a65b Binary files /dev/null and b/acme/mail/Mail.dis differ diff --git a/acme/mail/Mailpop3.dis b/acme/mail/Mailpop3.dis new file mode 100644 index 00000000..256da17b Binary files /dev/null and b/acme/mail/Mailpop3.dis differ diff --git a/acme/mail/guide b/acme/mail/guide new file mode 100644 index 00000000..2e23b9e1 --- /dev/null +++ b/acme/mail/guide @@ -0,0 +1,5 @@ +Mail /mail/box/$user/stored +Mail +Mailpop3 +mkbox /mail/box/$user/new_box +mail -'x' someaddress diff --git a/acme/mail/mkbox.dis b/acme/mail/mkbox.dis new file mode 100644 index 00000000..924fb03d Binary files /dev/null and b/acme/mail/mkbox.dis differ diff --git a/acme/mail/readme b/acme/mail/readme new file mode 100644 index 00000000..9442f57e --- /dev/null +++ b/acme/mail/readme @@ -0,0 +1,29 @@ +Mail is the single program in this directory. Its argument specifies +the mail box to read, default /mail/box/$user/mbox. +For example, running + Mail /mail/box/$user/stored +(a line in the guide file) looks at saved mail. + +Mail maintains a window containing headers for all the +messages in the mailbox and monitors the mailbox for new messages. +Using button 3 to indicate a message number opens +a window on that message. commands in the mailbox window are + Put Write the mailbox back to the file (never done automatically) + Mail Make a new message window ready to mail someone. + Takes argument names analogously to acme's New. + Del Exit Mail, after checking that mailbox isn't modified. +New messages appear at the top of the window and are highlighted upon arrival. +(The messages are numbered oldest to newest, the opposite of regular mail.) + +Message windows have a simple format: the first line, up to the first tab or newline, +holds the sender or, when sending, the addressee. Edit the line to change who the +message goes to. Message windows contain the commands + Reply Make a new window to compose a reply to this message + Delmesg Delete the message from the screen and from the mailbox + Del Delete the window, leaving the message in the mailbox + Post Send the message to the addressee + Save Save to the named mailbox, default/mail/box/$user/stored +Save takes a full file name; if that name has no slashes, the file is taken +to be in /mail/box/$user and must already exist. Use mkbox in the guide to +create target mailboxes in /mail/box/$user. +Reply and mail windows contain an obvious subset of the commands. diff --git a/appl/NOTICE b/appl/NOTICE new file mode 100644 index 00000000..84818206 --- /dev/null +++ b/appl/NOTICE @@ -0,0 +1,25 @@ +This copyright NOTICE applies to all files in this directory and +subdirectories, unless another copyright notice appears in a given +file or subdirectory. If you take substantial code from this software to use in +other programs, you must somehow include with it an appropriate +copyright notice that includes the copyright notice and the other +notices below. It is fine (and often tidier) to do that in a separate +file such as NOTICE, LICENCE or COPYING. + +Copyright © 1995-1999 Lucent Technologies Inc. +Portions Copyright © 1997-2000 Vita Nuova Limited +Portions Copyright © 2000-2006 Vita Nuova Holdings Limited + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. diff --git a/appl/acme/acme.b b/appl/acme/acme.b new file mode 100644 index 00000000..5d880f8b --- /dev/null +++ b/appl/acme/acme.b @@ -0,0 +1,1125 @@ +implement Acme; + +include "common.m"; + +sys : Sys; +bufio : Bufio; +workdir : Workdir; +drawm : Draw; +styx : Styx; +acme : Acme; +gui : Gui; +graph : Graph; +dat : Dat; +framem : Framem; +utils : Utils; +regx : Regx; +scrl : Scroll; +textm : Textm; +filem : Filem; +windowm : Windowm; +rowm : Rowm; +columnm : Columnm; +bufferm : Bufferm; +diskm : Diskm; +exec : Exec; +look : Look; +timerm : Timerm; +fsys : Fsys; +xfidm : Xfidm; +plumbmsg : Plumbmsg; +editm: Edit; +editlog: Editlog; +editcmd: Editcmd; +styxaux: Styxaux; + +sprint : import sys; +BACK, HIGH, BORD, TEXT, HTEXT, NCOL : import Framem; +Point, Rect, Font, Image, Display, Pointer: import drawm; +TRUE, FALSE, maxtab : import dat; +Ref, Reffont, Command, Timer, Lock, Cursor : import dat; +row, reffont, activecol, mouse, typetext, mousetext, barttext, argtext, seltext, button, modbutton, colbutton, arrowcursor, boxcursor, plumbed : import dat; +Xfid : import xfidm; +cmouse, ckeyboard, cwait, ccommand, ckill, cxfidalloc, cxfidfree, cerr, cplumb, cedit : import dat; +font, bflush, balloc, draw : import graph; +Arg, PNPROC, PNGROUP : import utils; +arginit, argopt, argf, error, warning, postnote : import utils; +yellow, green, red, blue, black, white, mainwin, display : import gui; +Disk : import diskm; +Row : import rowm; +Column : import columnm; +Window : import windowm; +Text, Tag, Body, Columntag : import textm; +Buffer : import bufferm; +snarfbuf : import exec; +Msg : import plumbmsg; + +tfd : ref Sys->FD; +lasttime : int; + +init(ctxt : ref Draw->Context, argl : list of string) +{ + acmectxt = ctxt; + + sys = load Sys Sys->PATH; + sys->pctl(Sys->NEWPGRP, nil); + + { + # tfd = sys->create("./time", Sys->OWRITE, 8r600); + # lasttime = sys->millisec(); + bufio = load Bufio Bufio->PATH; + workdir = load Workdir Workdir->PATH; + drawm = load Draw Draw->PATH; + + styx = load Styx Styx->PATH; + + acme = load Acme SELF; + + gui = load Gui path(Gui->PATH); + graph = load Graph path(Graph->PATH); + dat = load Dat path(Dat->PATH); + framem = load Framem path(Framem->PATH); + utils = load Utils path(Utils->PATH); + regx = load Regx path(Regx->PATH); + scrl = load Scroll path(Scroll->PATH); + textm = load Textm path(Textm->PATH); + filem = load Filem path(Filem->PATH); + windowm = load Windowm path(Windowm->PATH); + rowm = load Rowm path(Rowm->PATH); + columnm = load Columnm path(Columnm->PATH); + bufferm = load Bufferm path(Bufferm->PATH); + diskm = load Diskm path(Diskm->PATH); + exec = load Exec path(Exec->PATH); + look = load Look path(Look->PATH); + timerm = load Timerm path(Timerm->PATH); + fsys = load Fsys path(Fsys->PATH); + xfidm = load Xfidm path(Xfidm->PATH); + plumbmsg = load Plumbmsg Plumbmsg->PATH; + editm = load Edit path(Edit->PATH); + editlog = load Editlog path(Editlog->PATH); + editcmd = load Editcmd path(Editcmd->PATH); + styxaux = load Styxaux path(Styxaux->PATH); + + mods := ref Dat->Mods(sys, bufio, drawm, styx, styxaux, + acme, gui, graph, dat, framem, + utils, regx, scrl, + textm, filem, windowm, rowm, columnm, + bufferm, diskm, exec, look, timerm, + fsys, xfidm, plumbmsg, editm, editlog, editcmd); + + styx->init(); + styxaux->init(); + + utils->init(mods); + gui->init(mods); + graph->init(mods); + dat->init(mods); + framem->init(mods); + regx->init(mods); + scrl->init(mods); + textm->init(mods); + filem->init(mods); + windowm->init(mods); + rowm->init(mods); + columnm->init(mods); + bufferm->init(mods); + diskm->init(mods); + exec->init(mods); + look->init(mods); + timerm->init(mods); + fsys->init(mods); + xfidm->init(mods); + editm->init(mods); + editlog->init(mods); + editcmd->init(mods); + + utils->debuginit(); + + if (plumbmsg->init(1, "edit", Dat->PLUMBSIZE) >= 0) + plumbed = 1; + + main(argl); + + } +# exception{ +# * => +# sys->fprint(sys->fildes(2), "acme: fatal: %s\n", utils->getexc()); +# sys->print("acme: fatal: %s\n", utils->getexc()); +# shutdown("error"); +# } +} + +timing(s : string) +{ + thistime := sys->millisec(); + sys->fprint(tfd, "%s %d\n", s, thistime-lasttime); + lasttime = thistime; +} + +path(p : string) : string +{ + if (RELEASECOPY) + return p; + else { + # inlined strrchr since not loaded yet + for (n := len p - 1; n >= 0; n--) + if (p[n] == '/') + break; + if (n >= 0) + p = p[n+1:]; + return "/usr/jrf/acme/" + p; + } +} + +waitpid0, waitpid1 : int; +mainpid : int; + +fontcache : array of ref Reffont; +nfontcache : int; +reffonts : array of ref Reffont; +deffontnames := array[2] of { + "/fonts/lucidasans/euro.8.font", + "/fonts/lucm/unicode.9.font", +}; + +command : ref Command; + +WPERCOL : con 8; + +NSnarf : con 32; +snarfrune : ref Dat->Astring; + +main(argl : list of string) +{ + i, ac : int; + loadfile : string; + p : int; + c : ref Column; + arg : ref Arg; + ncol : int; + + ncol = -1; + + mainpid = sys->pctl(0, nil); + loadfile = nil; + fontnames = array[2] of string; + fontnames[0:] = deffontnames[0:2]; + f := utils->getenv("acme-font"); + if (f != nil) + fontnames[0] = f; + f = utils->getenv("acme-Font"); + if (f != nil) + fontnames[1] = f; + arg = arginit(argl); + while(ac = argopt(arg)) case(ac){ + 'b' => + dat->bartflag = TRUE; + 'c' => + ncol = int argf(arg); + 'f' => + fontnames[0] = argf(arg); + 'F' => + fontnames[1] = argf(arg); + 'l' => + loadfile = argf(arg); + } + + dat->home = utils->getenv("home"); + if (dat->home == nil) + dat->home = utils->gethome(utils->getuser()); + ts := utils->getenv("tabstop"); + if (ts != nil) + maxtab = int ts; + if (maxtab <= 0) + maxtab = 4; + snarfrune = utils->stralloc(NSnarf); + sys->pctl(Sys->FORKNS|Sys->FORKENV, nil); + utils->setenv("font", fontnames[0]); + sys->bind("/acme/dis", "/dis", Sys->MBEFORE); + wdir = workdir->init(); + if (wdir == nil) + wdir = "."; + workdir = nil; + + graph->binit(); + font = Font.open(display, fontnames[0]); + if(font == nil){ + fontnames[0] = deffontnames[0]; + font = Font.open(display, fontnames[0]); + if (font == nil) { + warning(nil, sprint("can't open font file %s: %r\n", fontnames[0])); + return; + } + } + reffont = ref Reffont; + reffont.r = Ref.init(); + reffont.f = font; + reffonts = array[2] of ref Reffont; + reffonts[0] = reffont; + reffont.r.inc(); # one to hold up 'font' variable + reffont.r.inc(); # one to hold up reffonts[0] + fontcache = array[1] of ref Reffont; + nfontcache = 1; + fontcache[0] = reffont; + + iconinit(); + usercolinit(); + timerm->timerinit(); + regx->rxinit(); + + cwait = chan of string; + ccommand = chan of ref Command; + ckill = chan of string; + cxfidalloc = chan of ref Xfid; + cxfidfree = chan of ref Xfid; + cerr = chan of string; + cplumb = chan of ref Msg; + cedit = chan of int; + + gui->spawnprocs(); + # spawn keyboardproc(); + # spawn mouseproc(); + sync := chan of int; + spawn waitproc(sys->pctl(0, nil), sync); + <- sync; + spawn plumbproc(); + + fsys->fsysinit(); + dat->disk = (dat->disk).init(); + row = rowm->newrow(); + if(loadfile != nil) { + row.qlock.lock(); # tasks->procs now + row.loadx(loadfile, TRUE); + row.qlock.unlock(); + } + else{ + row.init(mainwin.clipr); + if(ncol < 0){ + if(arg.av == nil) + ncol = 2; + else{ + ncol = (len arg.av+(WPERCOL-1))/WPERCOL; + if(ncol < 2) + ncol = 2; + } + } + if(ncol == 0) + ncol = 2; + for(i=0; istrrchr(filen, '/'); + if((p>=0 && filen[p:] == "/guide") || i/WPERCOL>=row.ncol) + readfile(c, filen); + else + readfile(row.col[i/WPERCOL], filen); + i++; + } + } + bflush(); + + spawn keyboardtask(); + spawn mousetask(); + spawn waittask(); + spawn xfidalloctask(); + + # notify(shutdown); + # waitc := chan of int; + # <-waitc; + # killprocs(); + exit; +} + +readfile(c : ref Column, s : string) +{ + w : ref Window; + r : string; + nr : int; + + w = c.add(nil, nil, -1); + (r, nr) = look->cleanname(s, len s); + w.setname(r, nr); + w.body.loadx(0, s, 1); + w.body.file.mod = FALSE; + w.dirty = FALSE; + w.settag(); + scrl->scrdraw(w.body); + w.tag.setselect(w.tag.file.buf.nc, w.tag.file.buf.nc); +} + +oknotes := array[6] of { + "delete", + "hangup", + "kill", + "exit", + "error", + nil +}; + +dumping : int; + +shutdown(msg : string) +{ + i : int; + + # notify(nil); + if(!dumping && msg != "kill" && msg != "exit" && (1 || sys->pctl(0, nil)==mainpid) && row != nil){ + dumping = TRUE; + row.dump(nil); + } + for(i=0; oknotes[i] != nil; i++) + if(utils->strncmp(oknotes[i], msg, len oknotes[i]) == 0) { + killprocs(); + exit; + } + # killprocs(); + sys->fprint(sys->fildes(2), "acme: %s\n", msg); + sys->print("acme: %s\n", msg); + # exit; +} + +acmeexit(err: string) +{ + if(err != nil) + shutdown(err); + graph->cursorswitch(nil); + if (plumbed) + plumbmsg->shutdown(); + killprocs(); + gui->killwins(); + exit; +} + +killprocs() +{ + c : ref Command; + kill := "kill"; + thispid := sys->pctl(0, nil); + fsys->fsysclose(); + + postnote(PNPROC, thispid, mousepid, kill); + postnote(PNPROC, thispid, keyboardpid, kill); + postnote(PNPROC, thispid, timerpid, kill); + postnote(PNPROC, thispid, waitpid0, kill); + postnote(PNPROC, thispid, waitpid1, kill); + postnote(PNPROC, thispid, fsyspid, kill); + postnote(PNPROC, thispid, mainpid, kill); + postnote(PNPROC, thispid, keytid, kill); + postnote(PNPROC, thispid, mousetid, kill); + postnote(PNPROC, thispid, waittid, kill); + postnote(PNPROC, thispid, xfidalloctid, kill); + # postnote(PNPROC, thispid, lockpid, kill); + postnote(PNPROC, thispid, plumbpid, kill); + + # draw(mainwin, mainwin.r, white, nil, mainwin.r.min); + + for(c=command; c != nil; c=c.next) + postnote(PNGROUP, thispid, c.pid, "kill"); + + xfidm->xfidkill(); +} + +keytid : int; +mousetid : int; +waittid : int; +xfidalloctid : int; + +keyboardtask() +{ + r : int; + timer : ref Timer; + null : ref Timer; + t : ref Text; + + { + keytid = sys->pctl(0, nil); + null = ref Timer; + null.c = chan of int; + timer = null; + typetext = nil; + for(;;){ + alt{ + <-(timer.c) => + timerm->timerstop(timer); + t = typetext; + if(t!=nil && t.what==Tag && !t.w.qlock.locked()){ + t.w.lock('K'); + t.w.commit(t); + t.w.unlock(); + bflush(); + } + timer = null; + r = <-ckeyboard => + gotkey := 1; + while (gotkey) { + typetext = row.typex(r, mouse.xy); + t = typetext; + if(t!=nil && t.col!=nil) + activecol = t.col; + if(t!=nil && t.w!=nil) + t.w.body.file.curtext = t.w.body; + if(timer != null) + spawn timerm->timerwaittask(timer); + if(t!=nil && t.what==Tag) + timer = timerm->timerstart(500); + else + timer = null; + alt { + r = <- ckeyboard => + gotkey = 1; # do this case again + * => + gotkey = 0; + } + bflush(); + } + } + } + } + exception{ + * => + shutdown(utils->getexc()); + raise; + # acmeexit(nil); + } +} + +mousetask() +{ + t, argt : ref Text; + but, ok : int; + q0, q1 : int; + w : ref Window; + m : ref Msg; + + { + mousetid = sys->pctl(0, nil); + sync := chan of int; + spawn waitproc(mousetid, sync); + <- sync; + for(;;){ + alt{ + *mouse = *<-cmouse => + row.qlock.lock(); + if (mouse.buttons & M_QUIT) { + if (row.clean(TRUE)) + acmeexit(nil); + # shutdown("kill"); + row.qlock.unlock(); + break; + } + if (mouse.buttons & M_HELP) { + warning(nil, "no help provided (yet)"); + bflush(); + row.qlock.unlock(); + break; + } + if(mouse.buttons & M_RESIZE){ + draw(mainwin, mainwin.r, white, nil, mainwin.r.min); + scrl->scrresize(); + row.reshape(mainwin.clipr); + bflush(); + row.qlock.unlock(); + break; + } + t = row.which(mouse.xy); + if(t!=mousetext && mousetext!=nil && mousetext.w!=nil){ + mousetext.w.lock('M'); + mousetext.eq0 = ~0; + mousetext.w.commit(mousetext); + mousetext.w.unlock(); + } + mousetext = t; + if(t == nil) { + bflush(); + row.qlock.unlock(); + break; + } + w = t.w; + if(t==nil || mouse.buttons==0) { + bflush(); + row.qlock.unlock(); + break; + } + if(w != nil) + w.body.file.curtext = w.body; + but = 0; + if(mouse.buttons == 1) + but = 1; + else if(mouse.buttons == 2) + but = 2; + else if(mouse.buttons == 4) + but = 3; + barttext = t; + if(t.what==Body && mouse.xy.in(t.scrollr)){ + if(but){ + w.lock('M'); + t.eq0 = ~0; + scrl->scroll(t, but); + t.w.unlock(); + } + bflush(); + row.qlock.unlock(); + break; + } + if(mouse.xy.in(t.scrollr)){ + if(but){ + if(t.what == Columntag) + row.dragcol(t.col); + else if(t.what == Tag){ + t.col.dragwin(t.w, but); + if(t.w != nil) + barttext = t.w.body; + } + if(t.col != nil) + activecol = t.col; + } + bflush(); + row.qlock.unlock(); + break; + } + if(mouse.buttons){ + if(w != nil) + w.lock('M'); + t.eq0 = ~0; + if(w != nil) + w.commit(t); + else + t.commit(TRUE); + if(mouse.buttons & 1){ + t.select(0); + if(w != nil) + w.settag(); + argtext = t; + seltext = t; + if(t.col != nil) + activecol = t.col; # button 1 only + if(t.w != nil && t == t.w.body) + dat->activewin = t.w; + }else if(mouse.buttons & 2){ + (ok, argt, q0, q1) = t.select2(q0, q1); + if(ok) + exec->execute(t, q0, q1, FALSE, argt); + }else if(mouse.buttons & 4){ + (ok, q0, q1) = t.select3(q0, q1); + if(ok) + look->look3(t, q0, q1, FALSE); + } + if(w != nil) + w.unlock(); + bflush(); + row.qlock.unlock(); + break; + } + m = <- cplumb => + if (m.kind == "text") { + attrs := plumbmsg->string2attrs(m.attr); + (found, act) := plumbmsg->lookup(attrs, "action"); + if (!found || act == nil || act == "showfile") + look->plumblook(m); + else if (act == "showdata") + look->plumbshow(m); + } + bflush(); + } + } + } + exception{ + * => + shutdown(utils->getexc()); + raise; + # acmeexit(nil); + } +} + +# list of processes that have exited but we have not heard of yet +Pid : adt { + pid : int; + msg : string; + next : cyclic ref Pid; +}; + +waittask() +{ + status : string; + c, lc : ref Command; + pid : int; + found : int; + cmd : string; + err : string; + t : ref Text; + pids : ref Pid; + + waittid = sys->pctl(0, nil); + command = nil; + for(;;){ + alt{ + err = <-cerr => + row.qlock.lock(); + warning(nil, err); + err = nil; + bflush(); + row.qlock.unlock(); + break; + cmd = <-ckill => + found = FALSE; + for(c=command; c != nil; c=c.next){ + # -1 for blank + if(c.name[0:len c.name - 1] == cmd){ + if(postnote(PNGROUP, waittid, c.pid, "kill") < 0) + warning(nil, sprint("kill %s: %r\n", cmd)); + found = TRUE; + } + } + if(!found) + warning(nil, sprint("Kill: no process %s\n", cmd)); + cmd = nil; + break; + status = <-cwait => + pid = int status; + lc = nil; + for(c=command; c != nil; c=c.next){ + if(c.pid == pid){ + if(lc != nil) + lc.next = c.next; + else + command = c.next; + break; + } + lc = c; + } + row.qlock.lock(); + t = row.tag; + t.commit(TRUE); + if(c == nil){ + # warning(nil, sprint("unknown child pid %d\n", pid)); + p := ref Pid; + p.pid = pid; + p.msg = status; + p.next = pids; + pids = p; + } + else{ + if(look->search(t, c.name, len c.name)){ + t.delete(t.q0, t.q1, TRUE); + t.setselect(0, 0); + } + if(status[len status - 1] != ':') + warning(c.md, sprint("%s\n", status)); + bflush(); + } + row.qlock.unlock(); + if(c != nil){ + if(c.iseditcmd) + cedit <- = 0; + fsys->fsysdelid(c.md); + c = nil; + } + break; + c = <-ccommand => + lastp : ref Pid = nil; + for(p := pids; p != nil; p = p.next){ + if(p.pid == c.pid){ + status = p.msg; + if(status[len status - 1] != ':') + warning(c.md, sprint("%s\n", status)); + if(lastp == nil) + pids = p.next; + else + lastp.next = p.next; + if(c.iseditcmd) + cedit <- = 0; + fsys->fsysdelid(c.md); + c = nil; + break; + } + lastp = p; + } + c.next = command; + command = c; + row.qlock.lock(); + t = row.tag; + t.commit(TRUE); + t.insert(0, c.name, len c.name, TRUE, 0); + t.setselect(0, 0); + bflush(); + row.qlock.unlock(); + break; + } + } +} + +xfidalloctask() +{ + xfree, x : ref Xfid; + + xfidalloctid = sys->pctl(0, nil); + xfree = nil; + for(;;){ + alt{ + <-cxfidalloc => + x = xfree; + if(x != nil) + xfree = x.next; + else{ + x = xfidm->newxfid(); + x.c = chan of int; + spawn x.ctl(); + } + cxfidalloc <-= x; + break; + x = <-cxfidfree => + x.next = xfree; + xfree = x; + break; + } + } +} + +frgetmouse() +{ + bflush(); + *mouse = *<-cmouse; +} + +waitproc(pid : int, sync: chan of int) +{ + fd : ref Sys->FD; + n : int; + + if (waitpid0 == 0) + waitpid0 = sys->pctl(0, nil); + else + waitpid1 = sys->pctl(0, nil); + sys->pctl(Sys->FORKFD, nil); + # w := sprint("/prog/%d/wait", pid); + w := sprint("#p/%d/wait", pid); + fd = sys->open(w, Sys->OREAD); + if (fd == nil) + error("fd == nil in waitproc"); + sync <-= 0; + buf := array[Sys->WAITLEN] of byte; + status := ""; + for(;;){ + if ((n = sys->read(fd, buf, len buf))<0) + error("bad read in waitproc"); + status = string buf[0:n]; + cwait <-= status; + } +} + +get(fix : int, save : int, setfont : int, name : string) : ref Reffont +{ + r : ref Reffont; + f : ref Font; + i : int; + + r = nil; + if(name == nil){ + name = fontnames[fix]; + r = reffonts[fix]; + } + if(r == nil){ + for(i=0; i= nfontcache) { + f = Font.open(display, name); + if(f == nil){ + warning(nil, sprint("can't open font file %s: %r\n", name)); + return nil; + } + r = ref Reffont; + r.r = Ref.init(); + r.f = f; + ofc := fontcache; + fontcache = array[nfontcache+1] of ref Reffont; + fontcache[0:] = ofc[0:nfontcache]; + ofc = nil; + fontcache[nfontcache++] = r; + } + } + if(save){ + r.r.inc(); + if(reffonts[fix] != nil) + reffonts[fix].close(); + reffonts[fix] = r; + fontnames[fix] = name; + } + if(setfont){ + reffont.f = r.f; + r.r.inc(); + reffonts[0].close(); + font = r.f; + reffonts[0] = r; + r.r.inc(); + iconinit(); + } + r.r.inc(); + return r; +} + +close(r : ref Reffont) +{ + i : int; + + if(r.r.dec() == 0){ + for(i=0; i= nfontcache) + warning(nil, "internal error: can't find font in cache\n"); + else{ + fontcache[i:] = fontcache[i+1:nfontcache]; + nfontcache--; + } + r.f = nil; + r = nil; + } +} + +arrowbits := array[64] of { + byte 16rFF, byte 16rE0, byte 16rFF, byte 16rE0, + byte 16rFF, byte 16rC0, byte 16rFF, byte 16r00, + byte 16rFF, byte 16r00, byte 16rFF, byte 16r80, + byte 16rFF, byte 16rC0, byte 16rFF, byte 16rE0, + byte 16rE7, byte 16rF0, byte 16rE3, byte 16rF8, + byte 16rC1, byte 16rFC, byte 16r00, byte 16rFE, + byte 16r00, byte 16r7F, byte 16r00, byte 16r3E, + byte 16r00, byte 16r1C, byte 16r00, byte 16r08, + + byte 16r00, byte 16r00, byte 16r7F, byte 16rC0, + byte 16r7F, byte 16r00, byte 16r7C, byte 16r00, + byte 16r7E, byte 16r00, byte 16r7F, byte 16r00, + byte 16r6F, byte 16r80, byte 16r67, byte 16rC0, + byte 16r43, byte 16rE0, byte 16r41, byte 16rF0, + byte 16r00, byte 16rF8, byte 16r00, byte 16r7C, + byte 16r00, byte 16r3E, byte 16r00, byte 16r1C, + byte 16r00, byte 16r08, byte 16r00, byte 16r00, +}; + +# outer boundary of width 1 is white +# next boundary of width 3 is black +# next boundary of width 1 is white +# inner boundary of width 4 is transparent +boxbits := array[64] of { + byte 16rFF, byte 16rFF, byte 16rFF, byte 16rFF, + byte 16rFF, byte 16rFF, byte 16rFF, byte 16rFF, + byte 16rFF, byte 16rFF, byte 16rF8, byte 16r1F, + byte 16rF8, byte 16r1F, byte 16rF8, byte 16r1F, + byte 16rF8, byte 16r1F, byte 16rF8, byte 16r1F, + byte 16rF8, byte 16r1F, byte 16rFF, byte 16rFF, + byte 16rFF, byte 16rFF, byte 16rFF, byte 16rFF, + byte 16rFF, byte 16rFF, byte 16rFF, byte 16rFF, + + + byte 16r00, byte 16r00, byte 16r7F, byte 16rFE, + byte 16r7F, byte 16rFE, byte 16r7F, byte 16rFE, + byte 16r70, byte 16r0E, byte 16r70, byte 16r0E, + byte 16r70, byte 16r0E, byte 16r70, byte 16r0E, + byte 16r70, byte 16r0E, byte 16r70, byte 16r0E, + byte 16r70, byte 16r0E, byte 16r70, byte 16r0E, + byte 16r7F, byte 16rFE, byte 16r7F, byte 16rFE, + byte 16r7F, byte 16rFE, byte 16r00, byte 16r00, +}; + +iconinit() +{ + r : Rect; + + tagcols = array[NCOL] of ref Draw->Image; + tagcols[BACK] = imagemix(display.rgb(16raa, 16rff, 16rff), white, 1, 3); # mix DPalebluegreenwith DWhite + tagcols[HIGH] = display.rgb(16r9e, 16ree, 16ree); # was DPalegreygreen + tagcols[BORD] = display.rgb(16r88, 16r88, 16rcc); # was DPurpleblue + tagcols[TEXT] = black; + tagcols[HTEXT] = black; + + textcols = array[NCOL] of ref Draw->Image; + textcols[BACK] = imagemix(display.rgb(16rff, 16rff, 16raa), white, 1, 3); # mix DPaleyellow with DWhite + textcols[HIGH] = display.rgb(16ree, 16ree, 16r9e); # was DDarkyellow + textcols[BORD] = display.rgb(16r99, 16r99, 16r4c); # was Dyellowgreen + textcols[TEXT] = black; + textcols[HTEXT] = black; + + if(button != nil) + button = modbutton = colbutton = nil; + + r = ((0, 0), (Dat->Scrollwid+2, font.height+1)); + button = balloc(r, mainwin.chans, Draw->White); + draw(button, r, tagcols[BACK], nil, r.min); + r.max.x -= 2; + draw(button, r, tagcols[BORD], nil, (0, 0)); + r = r.inset(2); + draw(button, r, tagcols[BACK], nil, (0, 0)); + + r = button.r; + modbutton = balloc(r, mainwin.chans, Draw->White); + draw(modbutton, r, tagcols[BACK], nil, r.min); + r.max.x -= 2; + draw(modbutton, r, tagcols[BORD], nil, (0, 0)); + r = r.inset(2); + draw(modbutton, r, display.rgb(16r00, 16r00, 16r99), nil, (0, 0)); # was DMedblue + + r = button.r; + colbutton = balloc(r, mainwin.chans, Draw->White); + draw(colbutton, r, tagcols[BACK], nil, r.min); + r.max.x -= 2; + draw(colbutton, r, tagcols[BORD], nil, (0, 0)); + +# arrowcursor = ref Cursor((-1, -1), (16, 32), arrowbits); + boxcursor = ref Cursor((-7, -7), (16, 32), boxbits); + + but2col = display.rgb(16raa, 16r00, 16r00); + but3col = display.rgb(16r00, 16r66, 16r00); + but2colt = white; + but3colt = white; + + graph->cursorswitch(arrowcursor); +} + +colrec : adt { + name : string; + image : ref Image; +}; + +coltab : array of colrec; + +cinit() +{ + coltab = array[6] of colrec; + coltab[0].name = "yellow"; coltab[0].image = yellow; + coltab[1].name = "green"; coltab[1].image = green; + coltab[2].name = "red"; coltab[2].image = red; + coltab[3].name = "blue"; coltab[3].image = blue; + coltab[4].name = "black"; coltab[4].image = black; + coltab[5].name = "white"; coltab[5].image = white; +} + +col(s : string, n : int) : int +{ + return ((s[n]-'0') << 4) | (s[n+1]-'0'); +} + +rgb(s : string, n : int) : (int, int, int) +{ + return (col(s, n), col(s, n+2), col(s, n+4)); +} + +imagemix(i1 : ref Image, i2 : ref Image, n1 : int, n2 : int) : ref Image +{ + # 2,2 1,3 only : generalize later + i3 := balloc(((0, 0), (2, 2)), mainwin.chans, Draw->White); + draw(i3, i3.r, i2, nil, (0, 0)); + draw(i3, ((0, 0), (1, 1)), i1, nil, (0, 0)); + if (n1 == n2) + draw(i3, ((1, 1), (2, 2)), i1, nil, (0, 0)); + i3.repl = 1; + i3.clipr = ((-1729, -1729), (1729, 1729)); + return i3; +} + +cenv(s : string, t : string, but : int, i : ref Image) : ref Image +{ + c := utils->getenv("acme-" + s + "-" + t + "-" + string but); + if (c == nil) + c = utils->getenv("acme-" + s + "-" + string but); + if (c == nil && but != 0) + c = utils->getenv("acme-" + s); + if (c != nil) { + if (c[0] == '#' && len c >= 7) { + (r, g, b) := rgb(c, 1); + i1 := display.rgb(r, g, b); + if (len c >= 15 && c[7] == '/' && c[8] == '#') { + (r, g, b) = rgb(c, 9); + i2 := display.rgb(r, g, b); + return imagemix(i1, i2, 2, 2); + } + return i1; + } + for (j := 0; j < len c; j++) + if (c[j] >= 'A' && c[j] <= 'Z') + c[j] += 'a'-'A'; + for (j = 0; j < len coltab; j++) + if (c == coltab[j].name) + return coltab[j].image; + } + return i; +} + +usercolinit() +{ + cinit(); + textcols[TEXT] = cenv("fg", "text", 0, textcols[TEXT]); + textcols[BACK] = cenv("bg", "text", 0, textcols[BACK]); + textcols[HTEXT] = cenv("fg", "text", 1, textcols[HTEXT]); + textcols[HIGH] = cenv("bg", "text", 1, textcols[HIGH]); + but2colt= cenv("fg", "text", 2, but2colt); + but2col = cenv("bg", "text", 2, but2col); + but3colt = cenv("fg", "text", 3, but3colt); + but3col = cenv("bg", "text", 3, but3col); + tagcols[TEXT] = cenv("fg", "tag", 0, tagcols[TEXT]); + tagcols[BACK] = cenv("bg", "tag", 0, tagcols[BACK]); + tagcols[HTEXT] = cenv("fg", "tag", 1, tagcols[HTEXT]); + tagcols[HIGH] = cenv("bg", "tag", 1, tagcols[HIGH]); +} + +getsnarf() +{ + # return; + fd := sys->open("/chan/snarf", sys->OREAD); + if(fd == nil) + return; + snarfbuf.reset(); + snarfbuf.loadx(0, fd); +} + +putsnarf() +{ + n : int; + + # return; + if(snarfbuf.nc == 0) + return; + fd := sys->open("/chan/snarf", sys->OWRITE); + if(fd == nil) + return; + for(i:=0; i= NSnarf) + n = NSnarf; + snarfbuf.read(i, snarfrune, 0, n); + sys->fprint(fd, "%s", snarfrune.s[0:n]); + } +} + +plumbpid : int; + +plumbproc() +{ + plumbpid = sys->pctl(0, nil); + for(;;){ + msg := Msg.recv(); + if(msg == nil){ + sys->print("Acme: can't read /chan/plumb.edit: %r\n"); + plumbpid = 0; + plumbed = 0; + return; + } + if(msg.kind != "text"){ + sys->print("Acme: can't interpret '%s' kind of message\n", msg.kind); + continue; + } +# sys->print("msg %s\n", string msg.data); + cplumb <-= msg; + } +} diff --git a/appl/acme/acme.m b/appl/acme/acme.m new file mode 100644 index 00000000..bbd586f0 --- /dev/null +++ b/appl/acme/acme.m @@ -0,0 +1,32 @@ +Acme : module { + PATH : con "/dis/acme.dis"; + + RELEASECOPY : con 1; + + M_LBUT : con 1; + M_MBUT : con 2; + M_RBUT : con 4; + M_TBS : con 8; + M_PLUMB : con 16; + M_QUIT : con 32; + M_HELP : con 64; + M_RESIZE : con 128; + M_DOUBLE : con 256; + + textcols, tagcols : array of ref Draw->Image; + but2col, but3col, but2colt, but3colt : ref Draw->Image; + + acmectxt : ref Draw->Context; + keyboardpid, mousepid, timerpid, fsyspid : int; + fontnames : array of string; + wdir : string; + + init : fn(ctxt : ref Draw->Context, argv : list of string); + timing : fn(s : string); + frgetmouse : fn(); + get : fn(p, q, r : int, b : string) : ref Dat->Reffont; + close : fn(r : ref Dat->Reffont); + acmeexit : fn(err : string); + getsnarf : fn(); + putsnarf : fn(); +}; \ No newline at end of file diff --git a/appl/acme/acme/acid/guide b/appl/acme/acme/acid/guide new file mode 100644 index 00000000..0fc99511 --- /dev/null +++ b/appl/acme/acme/acid/guide @@ -0,0 +1,2 @@ +Acid pid +Acid -l alef -l symsfile pid diff --git a/appl/acme/acme/acid/mkfile b/appl/acme/acme/acid/mkfile new file mode 100644 index 00000000..f867a177 --- /dev/null +++ b/appl/acme/acme/acid/mkfile @@ -0,0 +1,24 @@ +<../../../../mkconfig + +BIN=$ROOT/acme/acid + +DIRS=\ + src\ + +TARG=\ + guide\ + readme\ + +BINTARG=${TARG:%=$BIN/%} + +all:V: $TARG + +install:V: $BINTARG + +$BIN/guide : guide + rm -f $BIN/guide && cp guide $BIN/guide + +$BIN/readme : readme + rm -f $BIN/readme && cp readme $BIN/readme + +<$ROOT/mkfiles/mksubdirs \ No newline at end of file diff --git a/appl/acme/acme/acid/readme b/appl/acme/acme/acid/readme new file mode 100644 index 00000000..fcf343be --- /dev/null +++ b/appl/acme/acme/acid/readme @@ -0,0 +1,12 @@ +Capital A Acid is a rudimentary acme interface to the debugger acid. +It uses a win to provide an interactive window for acid. In that window, +a couple of extra acme-specific features are enabled: + +w(command) + runs the command and places its output in a new window. + e.g. w(lstk()) places the stack trace in a distinct window. + +Also, in any such window, text executed with button 2 is +presented as input to acid in the main Acid window. Thus, for +example, one may evaluate variables presented in a stack trace +by `executing' it with button 2. diff --git a/appl/acme/acme/acid/src/Acid.b b/appl/acme/acme/acid/src/Acid.b new file mode 100644 index 00000000..d923160b --- /dev/null +++ b/appl/acme/acme/acid/src/Acid.b @@ -0,0 +1,31 @@ +implement Acid; + +include "sys.m"; +include "draw.m"; +include "sh.m"; + +Acid : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +init(ctxt : ref Draw->Context, argl : list of string) +{ + sys := load Sys Sys->PATH; + stderr := sys->fildes(2); + if (len argl < 2) { + sys->fprint(stderr, "usage : Acid pid\n"); + return; + } + cmd := "/acme/dis/win"; + file := cmd + ".dis"; + c := load Command file; + if(c == nil) { + sys->fprint(stderr, "%s: %r\n", cmd); + return; + } + argl = "-l" :: argl; + argl = "acid" :: argl; + argl = "/acme/dis/Acid0" :: argl; + argl = cmd :: argl; + c->init(ctxt, argl); +} \ No newline at end of file diff --git a/appl/acme/acme/acid/src/Acid0.b b/appl/acme/acme/acid/src/Acid0.b new file mode 100644 index 00000000..887110e9 --- /dev/null +++ b/appl/acme/acme/acid/src/Acid0.b @@ -0,0 +1,86 @@ +implement Acidb; + +include "sys.m"; +include "draw.m"; +include "bufio.m"; + +sys : Sys; +bufio : Bufio; + +FD : import sys; +Iobuf : import bufio; + +Acidb : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +init(nil : ref Draw->Context, nil : list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + # TBS main(argl); +} + +False : con 0; +True : con 1; + +EVENTSIZE : con 256; + +Event : adt { + c1, c2, q0, q1, flag, nb, nr : int; + b : array of byte; + r : array of int; + # TBS byte b[EVENTSIZE*UTFmax+1]; + # TBS Rune r[EVENTSIZE+1]; +}; + +Win : adt { + winid : int; + addr : int; + body : ref Iobuf; + ctl : int; + data : int; + event : int; + buf : array of byte; + # TBS byte buf[512]; + bufp : int; + nbuf : int; + + wnew : fn(w : ref Win); + wwritebody : fn(w : ref Win, s : array of byte, n : int); + wread : fn(w : ref Win, m : int, n : int, s : array of byte); + wclean : fn(w : ref Win); + wname : fn(w : ref Win, s : array of byte); + wdormant : fn(w : ref Win); + wevent : fn(w : ref Win, e : ref Event); + wtagwrite : fn(w : ref Win, s : array of byte, n : int); + wwriteevent : fn(w : ref Win, e : ref Event); + wslave : fn(w : ref Win, c : chan of Event); + wreplace : fn(w : ref Win, s : array of byte, b : array of byte, n : int); + wselect : fn(w : ref Win, s : array of byte); + wdel : fn(w : ref Win, n : int) : int; + wreadall : fn(w : ref Win) : (int, array of byte); + + ctlwrite : fn(w : ref Win, s : array of byte); + getec : fn(w : ref Win) : int; + geten : fn(w : ref Win) : int; + geter : fn(w : ref Win, s : array of byte, r : array of int) : int; + openfile : fn(w : ref Win, b : array of byte) : int; + openbody : fn(w : ref Win, n : int); +}; + +Awin : adt { + w : Win; + + slave : fn(w : ref Awin, s : array of byte, c : chan of int); + new : fn(w : ref Awin, s : array of byte); + command : fn(w : ref Awin, s : array of byte) : int; + send : fn(w : ref Awin, m : int, s : array of byte, n : int); +}; + +srvfd : ref FD; +stdin : ref FD; +srvenv : array of byte; +# TBS byte srvenv[64]; + +srvc : chan of array of byte; diff --git a/appl/acme/acme/acid/src/mkfile b/appl/acme/acme/acid/src/mkfile new file mode 100644 index 00000000..c53a358e --- /dev/null +++ b/appl/acme/acme/acid/src/mkfile @@ -0,0 +1,20 @@ +<../../../../../mkconfig + +TARG=\ + Acid.dis\ + Acid0.dis\ +# acid.dis\ +# awin.dis\ +# util.dis\ +# win.dis\ + +MODULES=\ + +SYSMODULES=\ + sh.m\ + sys.m\ + draw.m\ + +DISBIN=$ROOT/acme/acid + +<$ROOT/mkfiles/mkdis \ No newline at end of file diff --git a/appl/acme/acme/bin/guide b/appl/acme/acme/bin/guide new file mode 100644 index 00000000..31cea12e --- /dev/null +++ b/appl/acme/acme/bin/guide @@ -0,0 +1,5 @@ +win +new command ... +aspell file +adiff file1 file2 +adict -d oed diff --git a/appl/acme/acme/bin/mkfile b/appl/acme/acme/bin/mkfile new file mode 100644 index 00000000..6ab4d64d --- /dev/null +++ b/appl/acme/acme/bin/mkfile @@ -0,0 +1,24 @@ +<../../../../mkconfig + +BIN=$ROOT/acme/bin + +DIRS=\ + src\ + +TARG=\ + guide\ + readme\ + +BINTARG=${TARG:%=$BIN/%} + +all:V: $TARG + +install:V: $BINTARG + +$BIN/guide : guide + rm -f $BIN/guide && cp guide $BIN/guide + +$BIN/readme : readme + rm -f $BIN/readme && cp readme $BIN/readme + +<$ROOT/mkfiles/mksubdirs \ No newline at end of file diff --git a/appl/acme/acme/bin/readme b/appl/acme/acme/bin/readme new file mode 100644 index 00000000..941099e8 --- /dev/null +++ b/appl/acme/acme/bin/readme @@ -0,0 +1,25 @@ +This directory and its subdirectory $cputype are always mounted at +the end of /bin for programs run from acme. They hold a collection +of small acme-specific applications: + +win [command] + Create an acme window to serve as a terminal, analogous + to xterm. By default, it runs the shell, rc, but it works with + any interactive program, e.g. hoc. Within the window, + commands executed with button 2 are 'executed' by sending + their text to the standard input of the command, appending + a newline if necessary. +new command + Run the non-interactive command, placing its standard and + diagnostic output in a new window. +aspell file + Run spell on the file, labeling the output with addresses so + misspelled words can be found in context using button 3. +adiff file1 file2 + Run diff on the files, labeling the output with addresses so + changes can be found in context using button 3. +adict + Interactive version of dict(1). Button 3 looks up words and + may be applied to any word in any adict window. + When a word has multiple definitions, indicate the number + (as in acme Mail) to disambiguate. diff --git a/appl/acme/acme/bin/src/adiff.b b/appl/acme/acme/bin/src/adiff.b new file mode 100644 index 00000000..28021f1d --- /dev/null +++ b/appl/acme/acme/bin/src/adiff.b @@ -0,0 +1,155 @@ +implement Adiff; + +include "sys.m"; +include "draw.m"; +include "sh.m"; +include "workdir.m"; +include "bufio.m"; + +Adiff : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +sys : Sys; + +Context : import Draw; +OREAD, OWRITE, QTDIR, FD, FORKFD, open, read, write, sprint, fprint, stat, fildes, dup, pctl : import sys; + +init(ctxt : ref Context, argl : list of string) +{ + sys = load Sys Sys->PATH; + workdir := load Workdir Workdir->PATH; + stderr := fildes(2); + if (len argl != 3) { + fprint(stderr, "usage: adiff file1 file2\n"); + return; + } + ncfd := open("/chan/new/ctl", OREAD); + if (ncfd == nil) { + fprint(stderr, "cannot open ctl file\n"); + return; + } + b := array[128] of byte; + n := read(ncfd, b, len b); + id := string int string b[0:n]; + f1 := hd tl argl; + f2 := hd tl tl argl; + (ok1, d1) := stat(f1); + if (ok1 < 0) { + fprint(stderr, "cannot stat %s\n", f1); + return; + } + (ok2, d2) := stat(f2); + if (ok2 < 0) { + fprint(stderr, "cannot stat %s\n", f2); + return; + } + if (d1.qid.qtype & QTDIR) + f1 = f1 + "/" + basename(f2); + else if (d2.qid.qtype & QTDIR) + f2 = f2 + "/" + basename(f1); + buf := "/chan/" + id + "/ctl"; + icfd := open(buf, OWRITE); + if (icfd == nil) { + fprint(stderr, "cannot open control file\n"); + return; + } + buf = "name " + workdir->init() + "/-diff-" + f1 + "\n"; + b = array of byte buf; + write(icfd, b, len b); + + fds := array[2] of ref FD; + if (sys->pipe(fds) < 0) { + fprint(stderr, "can't pipe\n"); + return; + } + buf = "/chan/" + id + "/body"; + bfd := open(buf, OWRITE); + if (bfd == nil) { + fprint(stderr, "cannot open body file\n"); + return; + } + spawn diff(fds[1], f1, f2, ctxt); + fds[1] = nil; + awk(fds[0], bfd, f1, f2); + b = array of byte "clean\n"; + write(icfd, b, len b); +} + +strchr(s : string, c : int) : int +{ + for (i := 0; i < len s; i++) + if (s[i] == c) + return i; + return -1; +} + +strchrs(s, pat : string) : int +{ + for (i := 0; i < len s; i++) + if (strchr(pat, s[i]) >= 0) + return i; + return -1; +} + +awk(ifd, ofd : ref FD, f1, f2 : string) +{ + bufio := load Bufio Bufio->PATH; + Iobuf : import bufio; + b := bufio->fopen(ifd, OREAD); + while ((s := b.gets('\n')) != nil) { + if (s[0] >= '1' && s[0] <= '9') { + if ((n := strchrs(s, "acd")) >= 0) + s = f1 + ":" + s[0:n] + " " + s[n:n+1] + " " + f2 + ":" + s[n+1:]; + } + fprint(ofd, "%s", s); + } +} + +diff(ofd : ref FD, f1, f2 : string, ctxt : ref Context) +{ + args : list of string; + + pctl(FORKFD, nil); + fd := open("/dev/null", OREAD); + dup(fd.fd, 0); + fd = nil; + dup(ofd.fd, 1); + dup(1, 2); + ofd = nil; + args = nil; + args = f2 :: args; + args = f1 :: args; + args = "diff" :: args; + exec("diff", args, ctxt); + exit; +} + +exec(cmd : string, argl : list of string, ctxt : ref Context) +{ + file := cmd; + if(len file<4 || file[len file-4:]!=".dis") + file += ".dis"; + c := load Command file; + if(c == nil) { + err := sprint("%r"); + if(file[0]!='/' && file[0:2]!="./"){ + c = load Command "/dis/"+file; + if(c == nil) + err = sprint("%r"); + } + if(c == nil){ + fprint(fildes(2), "%s: %s\n", cmd, err); + return; + } + } + c->init(ctxt, argl); +} + +basename(s : string) : string +{ + for (i := len s -1; i >= 0; --i) + if (s[i] == '/') + return s[i+1:]; + return s; +} diff --git a/appl/acme/acme/bin/src/agrep.b b/appl/acme/acme/bin/src/agrep.b new file mode 100644 index 00000000..79ea6ff9 --- /dev/null +++ b/appl/acme/acme/bin/src/agrep.b @@ -0,0 +1,46 @@ +implement Agrep; + +include "sys.m"; +include "draw.m"; +include "sh.m"; + +Agrep : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +init(ctxt : ref Draw->Context, argl : list of string) +{ + sys := load Sys Sys->PATH; + stderr := sys->fildes(2); + cmd := "grep"; + file := cmd + ".dis"; + c := load Command file; + if(c == nil) { + err := sys->sprint("%r"); + if(file[0]!='/' && file[0:2]!="./"){ + c = load Command "/dis/"+file; + if(c == nil) + err = sys->sprint("%r"); + } + if(c == nil){ + sys->fprint(stderr, "%s: %s\n", cmd, err); + return; + } + } + argl = tl argl; + argl = rev(argl); + argl = "/dev/null" :: argl; + argl = rev(argl); + argl = "-n" :: argl; + argl = cmd :: argl; + c->init(ctxt, argl); +} + +rev(a : list of string) : list of string +{ + b : list of string; + + for ( ; a != nil; a = tl a) + b = hd a :: b; + return b; +} diff --git a/appl/acme/acme/bin/src/awd.b b/appl/acme/acme/bin/src/awd.b new file mode 100644 index 00000000..41c1e32f --- /dev/null +++ b/appl/acme/acme/bin/src/awd.b @@ -0,0 +1,45 @@ +implement Awd; + +include "sys.m"; +include "draw.m"; +include "workdir.m"; + +Awd : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +sys : Sys; +workdir : Workdir; + +FD, OWRITE, open, write : import sys; + +init(nil : ref Draw->Context, argl : list of string) +{ + n : int; + fd : ref FD; + buf, dir, str : string; + ab : array of byte; + + sys = load Sys Sys->PATH; + workdir = load Workdir Workdir->PATH; + fd = open("/dev/acme/ctl", OWRITE); + if(fd == nil) + exit; + dir = workdir->init(); + buf = "name " + dir; + n = len buf; + if(n>0 && buf[n-1] !='/') + buf[n++] = '/'; + buf[n++] = '-'; + if(tl argl != nil) + str = hd tl argl; + else + str = "rc"; + buf += str + "\n"; + ab = array of byte buf; + write(fd, ab, len ab); + buf = "dumpdir " + dir + "\n"; + ab = array of byte buf; + write(fd, ab, len ab); + exit; +} diff --git a/appl/acme/acme/bin/src/cd.b b/appl/acme/acme/bin/src/cd.b new file mode 100644 index 00000000..31843921 --- /dev/null +++ b/appl/acme/acme/bin/src/cd.b @@ -0,0 +1,70 @@ +implement Cd; + +include "sys.m"; +include "draw.m"; +include "workdir.m"; + +Cd : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +sys : Sys; +workdir : Workdir; + +FD, OREAD, OWRITE, open, read, write, chdir, fildes, fprint : import sys; + +init(nil : ref Draw->Context, argl : list of string) +{ + n : int; + fd, stderr : ref FD; + buf, dir, str : string; + ab : array of byte; + + sys = load Sys Sys->PATH; + stderr = fildes(2); + argl = tl argl; + if (argl == nil) + argl = "/usr/" + user() :: nil; + if (tl argl != nil) { + fprint(stderr, "Usage: cd [directory]\n"); + exit; + } + if (chdir(hd argl) < 0) { + fprint(stderr, "cd: %s: %r\n", hd argl); + exit; + } + + workdir = load Workdir Workdir->PATH; + fd = open("/dev/acme/ctl", OWRITE); + if(fd == nil) + exit; + dir = workdir->init(); + buf = "name " + dir; + n = len buf; + if(n>0 && buf[n-1] !='/') + buf[n++] = '/'; + buf[n++] = '-'; + if(tl argl != nil) + str = hd tl argl; + else + str = "sh"; + buf += str + "\n"; + ab = array of byte buf; + write(fd, ab, len ab); + buf = "dumpdir " + dir + "\n"; + ab = array of byte buf; + write(fd, ab, len ab); + exit; +} + +user(): string +{ + fd := open("/dev/user", OREAD); + if(fd == nil) + return "inferno"; + buf := array[Sys->NAMEMAX] of byte; + n := read(fd, buf, len buf); + if(n <= 0) + return "inferno"; + return string buf[0:n]; +} diff --git a/appl/acme/acme/bin/src/mkfile b/appl/acme/acme/bin/src/mkfile new file mode 100644 index 00000000..3dfb52b1 --- /dev/null +++ b/appl/acme/acme/bin/src/mkfile @@ -0,0 +1,22 @@ +<../../../../../mkconfig + +TARG=\ + win.dis\ + winm.dis\ + adiff.dis\ + agrep.dis\ + new.dis\ + spout.dis\ + awd.dis\ + cd.dis\ + +MODULES=\ + +SYSMODULES=\ + sh.m\ + sys.m\ + draw.m\ + +DISBIN=$ROOT/acme/dis + +<$ROOT/mkfiles/mkdis diff --git a/appl/acme/acme/bin/src/new.b b/appl/acme/acme/bin/src/new.b new file mode 100644 index 00000000..1a88e203 --- /dev/null +++ b/appl/acme/acme/bin/src/new.b @@ -0,0 +1,70 @@ +implement New; + +include "sys.m"; +include "draw.m"; +include "sh.m"; +include "workdir.m"; + +New : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +sys : Sys; + +init(ctxt : ref Draw->Context, argl : list of string) +{ + sys = load Sys Sys->PATH; + workdir := load Workdir Workdir->PATH; + if (len argl <= 1) + return; + ncfd := sys->open("/mnt/acme/new/ctl", Sys->OREAD); + if (ncfd == nil) + return; + b := array[128] of byte; + n := sys->read(ncfd, b, len b); + id := string int string b[0:n]; + buf := "/mnt/acme/" + id + "/ctl"; + icfd := sys->open(buf, Sys->OWRITE); + if (icfd == nil) + return; + base := hd tl argl; + for (i := len base - 1; i >= 0; --i) + if (base[i] == '/') { + base = base[i+1:]; + break; + } + buf = "name " + workdir->init() + "/-" + base + "\n"; + b = array of byte buf; + sys->write(icfd, b, len b); + buf = "/mnt/acme/" + id + "/body"; + bfd := sys->open(buf, Sys->OWRITE); + if (bfd == nil) + return; + sys->dup(bfd.fd, 1); + sys->dup(1, 2); + spawn exec(hd tl argl, tl argl, ctxt); + b = array of byte "clean\n"; + sys->write(icfd, b, len b); +} + +exec(cmd : string, argl : list of string, ctxt : ref Draw->Context) +{ + file := cmd; + if(len file<4 || file[len file-4:]!=".dis") + file += ".dis"; + c := load Command file; + if(c == nil) { + err := sys->sprint("%r"); + if(file[0]!='/' && file[0:2]!="./"){ + c = load Command "/dis/"+file; + if(c == nil) + err = sys->sprint("%r"); + } + if(c == nil){ + sys->fprint(sys->fildes(2), "%s: %s\n", cmd, err); + return; + } + } + c->init(ctxt, argl); + exit; +} \ No newline at end of file diff --git a/appl/acme/acme/bin/src/spout.b b/appl/acme/acme/bin/src/spout.b new file mode 100644 index 00000000..168ddc52 --- /dev/null +++ b/appl/acme/acme/bin/src/spout.b @@ -0,0 +1,143 @@ +implement Spout; + +include "sys.m"; +include "draw.m"; +include "bufio.m"; + +Spout : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +sys : Sys; +bufio : Bufio; + +OREAD, OWRITE, ORDWR, FORKNS, FORKFD, NEWPGRP, MREPL, FD, UTFmax, pctl, open, read, write, fprint, sprint, fildes, bind, dup, byte2char, utfbytes : import sys; +Iobuf : import bufio; + +stdin, stdout, stderr : ref FD; + +init(nil : ref Draw->Context, argl : list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + stdin = fildes(0); + stdout = fildes(1); + stderr = fildes(2); + main(argl); +} + +bout : ref Iobuf; + +main(argv : list of string) +{ + fd : ref FD; + + bout = bufio->fopen(stdout, OWRITE); + if(len argv == 1) + spout(stdin, ""); + else + for(argv = tl argv; argv != nil; argv = tl argv){ + fd = open(hd argv, OREAD); + if(fd == nil){ + fprint(stderr, "spell: can't open %s: %r\n", hd argv); + continue; + } + spout(fd, hd argv); + fd = nil; + } + exit; +} + +alpha(c : int) : int +{ + return ('a'<=(c) && (c)<='z') || ('A'<=(c) && (c)<='Z'); +} + +b : ref Iobuf; + +spout(fd : ref FD, name : string) +{ + s, buf : string; + t, w : int; + inword, wordchar : int; + n, wn, c, m : int; + + b = bufio->fopen(fd, OREAD); + n = 0; + wn = 0; + while((s = b.gets('\n')) != nil){ + if(s[len s-1] != '\n') + s[len s] = '\n'; + if(s[0] == '.') { + for(c=0; c<3 && c < len s && s[c]>' '; c++) + n++; + s = s[c:]; + } + inword = 0; + w = 0; + t = 0; + do{ + c = s[t]; + wordchar = 0; + if(alpha(c)) + wordchar = 1; + if(inword && !wordchar){ + if(c=='\'' && alpha(s[t+1])) { + n++; + t++; + continue; + } + m = t-w; + if(m > 1){ + buf = s[w:w+m]; + bout.puts(sprint("%s:#%d,#%d:%s\n", name, wn, n, buf)); + } + inword = 0; + }else if(!inword && wordchar){ + wn = n; + w = t; + inword = 1; + } + if(c=='\\' && (alpha(s[t+1]) || s[t+1]=='(')){ + case(s[t+1]){ + '(' => + m = 4; + break; + 'f' => + if(s[t+2] == '(') + m = 5; + else + m = 3; + break; + 's' => + if(s[t+2] == '+' || s[t+2]=='-'){ + if(s[t+3] == '(') + m = 6; + else + m = 4; + }else{ + if(s[t+2] == '(') + m = 5; + else if(s[t+2]=='1' || s[t+2]=='2' || s[t+2]=='3') + m = 4; + else + m = 3; + } + break; + * => + m = 2; + } + while(m-- > 0){ + if(s[t] == '\n') + break; + n++; + t++; + } + continue; + } + n++; + t ++; + }while(c != '\n'); + } + bout.flush(); +} diff --git a/appl/acme/acme/bin/src/win.b b/appl/acme/acme/bin/src/win.b new file mode 100644 index 00000000..f8299ee1 --- /dev/null +++ b/appl/acme/acme/bin/src/win.b @@ -0,0 +1,764 @@ +implement Win; + +include "sys.m"; +include "draw.m"; +include "workdir.m"; +include "sh.m"; + +sys : Sys; +workdir : Workdir; + +OREAD, OWRITE, ORDWR, FORKNS, FORKENV, FORKFD, NEWPGRP, MREPL, FD, UTFmax, pctl, open, read, write, fprint, sprint, fildes, bind, dup, byte2char, utfbytes : import sys; + +Win : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +Runeself : con 16r80; + +PNPROC, PNGROUP : con iota; + +stdout, stderr : ref FD; + +drawctxt : ref Draw->Context; +finish : chan of int; + +Lock : adt { + c : chan of int; + + init : fn() : ref Lock; + lock : fn(l : self ref Lock); + unlock : fn(l : self ref Lock); +}; + +init(ctxt : ref Draw->Context, argl : list of string) +{ + sys = load Sys Sys->PATH; + workdir = load Workdir Workdir->PATH; + drawctxt = ctxt; + stdout = fildes(1); + stderr = fildes(2); + debuginit(); + finish = chan[1] of int; + spawn main(argl); + <-finish; +} + +Lock.init() : ref Lock +{ + return ref Lock(chan[1] of int); +} + +Lock.lock(l : self ref Lock) +{ + l.c <-= 1; +} + +Lock.unlock(l : self ref Lock) +{ + <-l.c; +} + +dlock : ref Lock; +debfd : ref Sys->FD; + +debuginit() +{ + # debfd = sys->create("./debugwin", Sys->OWRITE, 8r600); + # dlock = Lock.init(); +} + +debugpr(nil : string) +{ + # fprint(debfd, "%s", s); +} + +debug(nil : string) +{ + # dlock.lock(); + # fprint(debfd, "%s", s); + # dlock.unlock(); +} + +exec(cmd : string, argl : list of string) +{ + file := cmd; + if(len file<4 || file[len file-4:]!=".dis") + file += ".dis"; + + c := load Command file; + if(c == nil) { + err := sprint("%r"); + if(file[0]!='/' && file[0:2]!="./"){ + c = load Command "/dis/"+file; + if(c == nil) + err = sprint("%r"); + } + if(c == nil){ + fprint(stderr, "%s: %s\n", cmd, err); + return; + } + } + c->init(drawctxt, argl); +} + +postnote(t : int, pid : int, note : string) : int +{ + # fd := open("/prog/" + string pid + "/ctl", OWRITE); + fd := open("#p/" + string pid + "/ctl", OWRITE); + if (fd == nil) + return -1; + if (t == PNGROUP) + note += "grp"; + fprint(fd, "%s", note); + + fd = nil; + return 0; +} + +sysname(): string +{ + fd := sys->open("#c/sysname", sys->OREAD); + if(fd == nil) + return nil; + buf := array[128] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return nil; + return string buf[0:n]; +} + +EVENTSIZE : con 256; + +Event : adt { + c1 : int; + c2 : int; + q0 : int; + q1 : int; + flag : int; + nb : int; + nr : int; + b : array of byte; + r : array of int; +}; + +blank : ref Event; + +pid : int; +# pgrpfd : ref FD; +parentpid : int; + +typing : array of byte; +ntypeb : int; +ntyper : int; +ntypebreak : int; + +Q : adt { + l : ref Lock; + p : int; + k : int; +}; + +q : Q; + +newevent(n : int) : ref Event +{ + e := ref Event; + e.b = array[n*UTFmax+1] of byte; + e.r = array[n+1] of int; + return e; +} + +main(argv : list of string) +{ + program : list of string; + fd, ctlfd, eventfd, addrfd, datafd : ref FD; + id : int; + c : chan of int; + name : string; + + sys->pctl(Sys->NEWPGRP, nil); + q.l = Lock.init(); + blank = newevent(2); + blank.c1 = 'M'; + blank.c2 = 'X'; + blank.q0 = blank.q1 = blank.flag = 0; + blank.nb = blank.nr = 1; + blank.b[0] = byte ' '; + blank.b[1] = byte 0; + blank.r[0] = ' '; + blank.r[1] = 0; + pctl(FORKNS|NEWPGRP, nil); + parentpid = pctl(0, nil); + program = nil; + if(tl argv != nil) + program = tl argv; + name = nil; + if(program == nil){ + # program = "-i" :: program; + program = "sh" :: program; + name = sysname(); + } + if(name == nil){ + prog := hd program; + for (n := len prog - 1; n >= 0; n--) + if (prog[n] == '/') + break; + if(n >= 0) + name = prog[n+1:]; + else + name = prog; + argl := tl argv; + if (argl != nil) { + for(argl = tl argl; argl != nil && len(name)+1+len(hd argl)<16; argl = tl argl) + name += "_" + hd argl; + } + } + if(bind("#|", "/dev/acme", MREPL) < 0) + error("pipe"); + ctlfd = open("/chan/new/ctl", ORDWR); + buf := array[12] of byte; + if(ctlfd==nil || read(ctlfd, buf, 12)!=12) + error("ctl"); + id = int string buf; + buf = nil; + b := sprint("/chan/%d/tag", id); + fd = open(b, OWRITE); + write(fd, array of byte " Send Delete", 12); + fd = nil; + b = sprint("/chan/%d/event", id); + eventfd = open(b, ORDWR); + b = sprint("/chan/%d/addr", id); + addrfd = open(b, ORDWR); + b = sprint("/chan/%d/data", id); + datafd = open(b, ORDWR); # OCEXEC + if(eventfd==nil || addrfd==nil || datafd==nil) + error("data files"); + c = chan of int; + spawn run(program, id, c); + pid = <-c; + # b = sprint("/prog/%d/notepg", pid); + # pgrpfd = open(b, OWRITE); # OCEXEC + # if(pgrpfd == nil) + # fprint(stdout, "warning: win can't open notepg: %r\n"); + c <-= 1; + fd = open("/dev/acme/data", ORDWR); + if(fd == nil) + error("/dev/acme/data"); + wd := workdir->init(); + # b = sprint("name %s/-%s\n0\n", wd, name); + b = sprint("name %s/-%s\n", wd, name); + ab := array of byte b; + write(ctlfd, ab, len ab); + b = sprint("dumpdir %s/\n", wd); + ab = array of byte b; + write(ctlfd, ab, len ab); + b = sprint("dump %s\n", onestring(argv)); + ab = array of byte b; + write(ctlfd, ab, len ab); + ab = nil; + spawn stdinx(fd, ctlfd, eventfd, addrfd, datafd); + stdoutx(fd, addrfd, datafd); +} + +run(argv : list of string, id : int, c : chan of int) +{ + fd0, fd1 : ref FD; + + pctl(FORKENV|FORKFD|NEWPGRP, nil); # had RFMEM + c <-= pctl(0, nil); + <-c; + pctl(FORKNS, nil); + if(bind("/dev/acme/data1", "/dev/cons", MREPL) < 0){ + fprint(stderr, "can't bind /dev/cons: %r\n"); + exit; + } + fd0 = open("/dev/cons", OREAD); + fd1 = open("/dev/cons", OWRITE); + if(fd0==nil || fd1==nil){ + fprint(stderr, "can't open /dev/cons: %r\n"); + exit; + } + dup(fd0.fd, 0); + dup(fd1.fd, 1); + dup(fd1.fd, 2); + fd0 = fd1 = nil; + b := sprint("/chan/%d", id); + if(bind(b, "/dev/acme", MREPL) < 0) + error("bind /dev/acme"); + if(bind(sprint("/chan/%d/consctl", id), "/dev/consctl", MREPL) < 0) + error("bind /dev/consctl"); + exec(hd argv, argv); + exit; +} + +killing : int = 0; + +error(s : string) +{ + if(s != nil) + fprint(stderr, "win: %s: %r\n", s); + if (killing) + return; + killing = 1; + s = "kill"; + if(pid) + postnote(PNGROUP, pid, s); + # write(pgrpfd, array of byte "hangup", 6); + postnote(PNGROUP, parentpid, s); + finish <-= 1; + exit; +} + +buff := array[8192] of byte; +bufp : int; +nbuf : int; + +onestring(argv : list of string) : string +{ + s : string; + + if(argv == nil) + return ""; + for( ; argv != nil; argv = tl argv){ + s += hd argv; + if (tl argv != nil) + s += " "; + } + return s; +} + +getec(efd : ref FD) : int +{ + if(nbuf == 0){ + nbuf = read(efd, buff, len buff); + if(nbuf <= 0) + error(nil); + bufp = 0; + } + --nbuf; + return int buff[bufp++]; +} + +geten(efd : ref FD) : int +{ + n, c : int; + + n = 0; + while('0'<=(c=getec(efd)) && c<='9') + n = n*10+(c-'0'); + if(c != ' ') + error("event number syntax"); + return n; +} + +geter(efd : ref FD, buf : array of byte) : (int, int) +{ + r, m, n, ok : int; + + r = getec(efd); + buf[0] = byte r; + n = 1; + if(r < Runeself) + return (r, n); + for (;;) { + (r, m, ok) = byte2char(buf[0:n], 0); + if (m > 0) + return (r, n); + buf[n++] = byte getec(efd); + } + return (0, 0); +} + +gete(efd : ref FD, e : ref Event) +{ + i, nb : int; + + e.c1 = getec(efd); + e.c2 = getec(efd); + e.q0 = geten(efd); + e.q1 = geten(efd); + e.flag = geten(efd); + e.nr = geten(efd); + if(e.nr > EVENTSIZE) + error("event string too long"); + e.nb = 0; + for(i=0; i # write to body; can't affect us + break; + 'F' => # generated by our actions; ignore + break; + 'K' or 'M' => + case(e.c2){ + 'R' => + addtype(' ', ntyper, e.b, e.nb, e.nr); + sendtype(fd0, 1); + break; + 'I' => + if(e.q0 < q.p) + q.p += e.q1-e.q0; + else if(e.q0 <= q.p+ntyper) + typex(e, fd0, afd, dfd); + break; + 'D' => + q.p -= delete(e); + break; + 'x' or 'X' => + if(e.flag & 2) + gete(efd, e2); + if(e.flag & 8){ + gete(efd, e3); + gete(efd, e4); + } + if(e.flag&1 || (e.c2=='x' && e.nr==0 && e2.nr==0)){ + # send it straight back + fprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1); + break; + } + if(e.q0==e.q1 && (e.flag&2)){ + e2.flag = e.flag; + *e = *e2; + } + if(e.flag & 8){ + if(e.q1 != e.q0){ + send(e, fd0, cfd, afd, dfd, 0); + send(blank, fd0, cfd, afd, dfd, 0); + } + send(e3, fd0, cfd, afd, dfd, 1); + }else if(e.q1 != e.q0) + send(e, fd0, cfd, afd, dfd, 1); + break; + 'l' or 'L' => + # just send it back + if(e.flag & 2) + gete(efd, e2); + fprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1); + break; + 'd' or 'i' => + break; + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } + q.l.unlock(); + } +} + +stdoutx(fd1 : ref FD, afd : ref FD, dfd : ref FD) +{ + n, m, w, npart : int; + s, t : int; + buf, hold, x : array of byte; + r, ok : int; + + buf = array[8192+UTFmax+1] of byte; + hold = array[UTFmax] of byte; + npart = 0; + for(;;){ + n = read(fd1, buf[npart:], 8192); + if(n < 0) + error(nil); + if(n == 0) + continue; + + # squash NULs + for (s = 0; s < n; s++) + if (buf[npart+s] == byte 0) + break; + if(s < n){ + for(t=s; s0 && (int buf[n-1]&16rC0)){ + --n; + npart++; + if((int buf[n]&16rC0)!=16r80){ + if(utfbytes(buf[n:], npart) > 0){ + (r, w, ok) = byte2char(buf, n); + n += w; + npart -= w; + } + break; + } + } + if(n > 0){ + hold[0:] = buf[n:n+npart]; + buf[n] = byte 0; + q.l.lock(); + str := sprint("#%d", q.p); + x = array of byte str; + m = len x; + if(write(afd, x, m) != m) + error("stdout writing address"); + x = nil; + if(write(dfd, buf, n) != n) + error("stdout writing body"); + q.p += nrunes(buf, n); + q.l.unlock(); + buf[0:] = hold[0:npart]; + } + } +} + +delete(e : ref Event) : int +{ + q0, q1 : int; + deltap : int; + + q0 = e.q0; + q1 = e.q1; + if(q1 <= q.p) + return e.q1-e.q0; + if(q0 >= q.p+ntyper) + return 0; + deltap = 0; + if(q0 < q.p){ + deltap = q.p-q0; + q0 = 0; + }else + q0 -= q.p; + if(q1 > q.p+ntyper) + q1 = ntyper; + else + q1 -= q.p; + deltype(q0, q1); + return deltap; +} + +addtype(c : int, p0 : int, b : array of byte, nb : int, nr : int) +{ + i, w : int; + r, ok : int; + p : int; + b0 : int; + + for(i=0; i 0) + addtype(e.c1, e.q0-q.p, e.b, e.nb, e.nr); + else{ + buf = array[128] of byte; + m = e.q0; + while(m < e.q1){ + str := sprint("#%d", m); + b := array of byte str; + n = len b; + write(afd, b, n); + b = nil; + n = read(dfd, buf, len buf); + nr = nrunes(buf, n); + while(m+nr > e.q1){ + do; while(n>0 && (int buf[--n]&16rC0)==16r80); + --nr; + } + if(n == 0) + break; + addtype(e.c1, m-q.p, buf, n, nr); + m += nr; + } + } + buf = nil; + sendtype(fd0, 0); +} + +send(e : ref Event, fd0 : ref FD, cfd : ref FD, afd : ref FD, dfd : ref FD, donl : int) +{ + l, m, n, nr, lastc, end : int; + abuf, buf : array of byte; + + buf = array[128] of byte; + end = q.p+ntyper; + str := sprint("#%d", end); + abuf = array of byte str; + l = len abuf; + write(afd, abuf, l); + abuf = nil; + if(e.nr > 0){ + write(dfd, e.b, e.nb); + addtype(e.c1, ntyper, e.b, e.nb, e.nr); + lastc = e.r[e.nr-1]; + }else{ + m = e.q0; + lastc = 0; + while(m < e.q1){ + str = sprint("#%d", m); + abuf = array of byte str; + n = len abuf; + write(afd, abuf, n); + abuf = nil; + n = read(dfd, buf, len buf); + nr = nrunes(buf, n); + while(m+nr > e.q1){ + do; while(n>0 && (int buf[--n]&16rC0)==16r80); + --nr; + } + if(n == 0) + break; + str = sprint("#%d", end); + abuf = array of byte str; + l = len abuf; + write(afd, abuf, l); + abuf = nil; + write(dfd, buf, n); + addtype(e.c1, ntyper, buf, n, nr); + lastc = int buf[n-1]; + m += nr; + end += nr; + } + } + if(donl && lastc!='\n'){ + write(dfd, array of byte "\n", 1); + addtype(e.c1, ntyper, array of byte "\n", 1, 1); + } + write(cfd, array of byte "dot=addr", 8); + sendtype(fd0, 0); + buf = nil; +} + diff --git a/appl/acme/acme/bin/src/winm.b b/appl/acme/acme/bin/src/winm.b new file mode 100644 index 00000000..f7b9ae19 --- /dev/null +++ b/appl/acme/acme/bin/src/winm.b @@ -0,0 +1,791 @@ +implement Win; + +include "sys.m"; +include "draw.m"; +include "workdir.m"; +include "sh.m"; + +sys : Sys; +workdir : Workdir; + +OREAD, OWRITE, ORDWR, FORKNS, FORKENV, FORKFD, NEWPGRP, MREPL, FD, UTFmax, pctl, open, read, write, fprint, sprint, fildes, bind, dup, byte2char, utfbytes : import sys; + +Win : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +Runeself : con 16r80; + +PNPROC, PNGROUP : con iota; + +stdout, stderr : ref FD; + +drawctxt : ref Draw->Context; + +Lock : adt { + cnt : int; + chann : chan of int; + + init : fn() : ref Lock; + lock : fn(l : self ref Lock); + unlock : fn(l : self ref Lock); +}; + +lc, uc : chan of ref Lock; +lockpid : int; + +init(ctxt : ref Draw->Context, argl : list of string) +{ + sys = load Sys Sys->PATH; + workdir = load Workdir Workdir->PATH; + drawctxt = ctxt; + stdout = fildes(1); + stderr = fildes(2); + lc = chan of ref Lock; + uc = chan of ref Lock; + spawn lockmgr(); + debuginit(); + main(argl); +} + +lockmgr() +{ + l : ref Lock; + + lockpid = pctl(0, nil); + for (;;) { + alt { + l = <- lc => + if (l.cnt++ == 0) + l.chann <-= 1; + l = <- uc => + if (--l.cnt > 0) + l.chann <-= 1; + } + } +} + +Lock.init() : ref Lock +{ + return ref Lock(0, chan of int); +} + +Lock.lock(l : self ref Lock) +{ + lc <-= l; + <- l.chann; +} + +Lock.unlock(l : self ref Lock) +{ + uc <-= l; +} + +dlock : ref Lock; +debfd : ref Sys->FD; + +debuginit() +{ + # debfd = sys->create("./debugwin", Sys->OWRITE, 8r600); + # dlock = Lock.init(); +} + +debugpr(nil : string) +{ + # fprint(debfd, "%s", s); +} + +debug(nil : string) +{ + # dlock.lock(); + # fprint(debfd, "%s", s); + # dlock.unlock(); +} + +exec(cmd : string, argl : list of string) +{ + file := cmd; + if(len file<4 || file[len file-4:]!=".dis") + file += ".dis"; + + c := load Command file; + if(c == nil) { + err := sprint("%r"); + if(file[0]!='/' && file[0:2]!="./"){ + c = load Command "/dis/"+file; + if(c == nil) + err = sprint("%r"); + } + if(c == nil){ + fprint(stderr, "%s: %s\n", cmd, err); + return; + } + } + c->init(drawctxt, argl); +} + +postnote(t : int, pid : int, note : string) : int +{ + # fd := open("/prog/" + string pid + "/ctl", OWRITE); + fd := open("#p/" + string pid + "/ctl", OWRITE); + if (fd == nil) + return -1; + if (t == PNGROUP) + note += "grp"; + fprint(fd, "%s", note); + + fd = nil; + return 0; +} + +sysname(): string +{ + fd := sys->open("#c/sysname", sys->OREAD); + if(fd == nil) + return nil; + buf := array[128] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return nil; + return string buf[0:n]; +} + +EVENTSIZE : con 256; + +Event : adt { + c1 : int; + c2 : int; + q0 : int; + q1 : int; + flag : int; + nb : int; + nr : int; + b : array of byte; + r : array of int; +}; + +blank : ref Event; + +pid : int; +# pgrpfd : ref FD; +parentpid : int; + +typing : array of byte; +ntypeb : int; +ntyper : int; +ntypebreak : int; + +Q : adt { + l : ref Lock; + p : int; + k : int; +}; + +q : Q; + +newevent(n : int) : ref Event +{ + e := ref Event; + e.b = array[n*UTFmax+1] of byte; + e.r = array[n+1] of int; + return e; +} + +main(argv : list of string) +{ + spawn main1(argv); + exit; +} + +main1(argv : list of string) +{ + program : list of string; + fd, ctlfd, eventfd, addrfd, datafd : ref FD; + id : int; + c : chan of int; + name : string; + + q.l = Lock.init(); + blank = newevent(2); + blank.c1 = 'M'; + blank.c2 = 'X'; + blank.q0 = blank.q1 = blank.flag = 0; + blank.nb = blank.nr = 1; + blank.b[0] = byte ' '; + blank.b[1] = byte 0; + blank.r[0] = ' '; + blank.r[1] = 0; + pctl(FORKNS|NEWPGRP, nil); + parentpid = pctl(0, nil); + program = nil; + if(tl argv != nil) + program = tl argv; + name = nil; + if(program == nil){ + # program = "-i" :: program; + program = "sh" :: program; + name = sysname(); + } + if(name == nil){ + prog := hd program; + for (n := len prog - 1; n >= 0; n--) + if (prog[n] == '/') + break; + if(n >= 0) + name = prog[n+1:]; + else + name = prog; + argl := tl argv; + if (argl != nil) { + for(argl = tl argl; argl != nil && len(name)+1+len(hd argl)<16; argl = tl argl) + name += "_" + hd argl; + } + } + if(bind("#|", "/dev/acme", MREPL) < 0) + error("pipe"); + ctlfd = open("/chan/new/ctl", ORDWR); + buf := array[12] of byte; + if(ctlfd==nil || read(ctlfd, buf, 12)!=12) + error("ctl"); + id = int string buf; + buf = nil; + b := sprint("/chan/%d/tag", id); + fd = open(b, OWRITE); + write(fd, array of byte " Send Delete", 12); + fd = nil; + b = sprint("/chan/%d/event", id); + eventfd = open(b, ORDWR); + b = sprint("/chan/%d/addr", id); + addrfd = open(b, ORDWR); + b = sprint("/chan/%d/data", id); + datafd = open(b, ORDWR); # OCEXEC + if(eventfd==nil || addrfd==nil || datafd==nil) + error("data files"); + c = chan of int; + spawn run(program, id, c); + pid = <-c; + # b = sprint("/prog/%d/notepg", pid); + # pgrpfd = open(b, OWRITE); # OCEXEC + # if(pgrpfd == nil) + # fprint(stdout, "warning: win can't open notepg: %r\n"); + c <-= 1; + fd = open("/dev/acme/data", ORDWR); + if(fd == nil) + error("/dev/acme/data"); + wd := workdir->init(); + # b = sprint("name %s/-%s\n0\n", wd, name); + b = sprint("name %s/-%s\n", wd, name); + ab := array of byte b; + write(ctlfd, ab, len ab); + b = sprint("dumpdir %s/\n", wd); + ab = array of byte b; + write(ctlfd, ab, len ab); + b = sprint("dump %s\n", onestring(argv)); + ab = array of byte b; + write(ctlfd, ab, len ab); + ab = nil; + spawn stdinx(fd, ctlfd, eventfd, addrfd, datafd); + stdoutx(fd, addrfd, datafd); +} + +run(argv : list of string, id : int, c : chan of int) +{ + fd0, fd1 : ref FD; + + pctl(FORKENV|FORKFD|NEWPGRP, nil); # had RFMEM + c <-= pctl(0, nil); + <-c; + pctl(FORKNS, nil); + if(bind("/dev/acme/data1", "/dev/cons", MREPL) < 0){ + fprint(stderr, "can't bind /dev/cons: %r\n"); + exit; + } + fd0 = open("/dev/cons", OREAD); + fd1 = open("/dev/cons", OWRITE); + if(fd0==nil || fd1==nil){ + fprint(stderr, "can't open /dev/cons: %r\n"); + exit; + } + dup(fd0.fd, 0); + dup(fd1.fd, 1); + dup(fd1.fd, 2); + fd0 = fd1 = nil; + b := sprint("/chan/%d", id); + if(bind(b, "/dev/acme", MREPL) < 0) + error("bind /dev/acme"); + if(bind(sprint("/chan/%d/consctl", id), "/dev/consctl", MREPL) < 0) + error("bind /dev/consctl"); + exec(hd argv, argv); + exit; +} + +killing : int = 0; + +error(s : string) +{ + if(s != nil) + fprint(stderr, "win: %s: %r\n", s); + if (killing) + return; + killing = 1; + s = "kill"; + if(pid) + postnote(PNGROUP, pid, s); + # write(pgrpfd, array of byte "hangup", 6); + postnote(PNPROC, lockpid, s); + postnote(PNGROUP, parentpid, s); + exit; +} + +buff := array[8192] of byte; +bufp : int; +nbuf : int; + +onestring(argv : list of string) : string +{ + s : string; + + if(argv == nil) + return ""; + for( ; argv != nil; argv = tl argv){ + s += hd argv; + if (tl argv != nil) + s += " "; + } + return s; +} + +getec(efd : ref FD) : int +{ + if(nbuf == 0){ + nbuf = read(efd, buff, len buff); + if(nbuf <= 0) + error(nil); + bufp = 0; + } + --nbuf; + return int buff[bufp++]; +} + +geten(efd : ref FD) : int +{ + n, c : int; + + n = 0; + while('0'<=(c=getec(efd)) && c<='9') + n = n*10+(c-'0'); + if(c != ' ') + error("event number syntax"); + return n; +} + +geter(efd : ref FD, buf : array of byte) : (int, int) +{ + r, m, n, ok : int; + + r = getec(efd); + buf[0] = byte r; + n = 1; + if(r < Runeself) + return (r, n); + for (;;) { + (r, m, ok) = byte2char(buf[0:n], 0); + if (m > 0) + return (r, n); + buf[n++] = byte getec(efd); + } + return (0, 0); +} + +gete(efd : ref FD, e : ref Event) +{ + i, nb : int; + + e.c1 = getec(efd); + e.c2 = getec(efd); + e.q0 = geten(efd); + e.q1 = geten(efd); + e.flag = geten(efd); + e.nr = geten(efd); + if(e.nr > EVENTSIZE) + error("event string too long"); + e.nb = 0; + for(i=0; i # write to body; can't affect us + break; + 'F' => # generated by our actions; ignore + break; + 'K' or 'M' => + case(e.c2){ + 'R' => + addtype(' ', ntyper, e.b, e.nb, e.nr); + sendtype(fd0, 1); + break; + 'I' => + if(e.q0 < q.p) + q.p += e.q1-e.q0; + else if(e.q0 <= q.p+ntyper) + typex(e, fd0, afd, dfd); + break; + 'D' => + q.p -= delete(e); + break; + 'x' or 'X' => + if(e.flag & 2) + gete(efd, e2); + if(e.flag & 8){ + gete(efd, e3); + gete(efd, e4); + } + if(e.flag&1 || (e.c2=='x' && e.nr==0 && e2.nr==0)){ + # send it straight back + fprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1); + break; + } + if(e.q0==e.q1 && (e.flag&2)){ + e2.flag = e.flag; + *e = *e2; + } + if(e.flag & 8){ + if(e.q1 != e.q0){ + send(e, fd0, cfd, afd, dfd, 0); + send(blank, fd0, cfd, afd, dfd, 0); + } + send(e3, fd0, cfd, afd, dfd, 1); + }else if(e.q1 != e.q0) + send(e, fd0, cfd, afd, dfd, 1); + break; + 'l' or 'L' => + # just send it back + if(e.flag & 2) + gete(efd, e2); + fprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1); + break; + 'd' or 'i' => + break; + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } + q.l.unlock(); + } +} + +stdoutx(fd1 : ref FD, afd : ref FD, dfd : ref FD) +{ + n, m, w, npart : int; + s, t : int; + buf, hold, x : array of byte; + r, ok : int; + + buf = array[8192+UTFmax+1] of byte; + hold = array[UTFmax] of byte; + npart = 0; + for(;;){ + n = read(fd1, buf[npart:], 8192); + if(n < 0) + error(nil); + if(n == 0) + continue; + + # squash NULs + for (s = 0; s < n; s++) + if (buf[npart+s] == byte 0) + break; + if(s < n){ + for(t=s; s0 && (int buf[n-1]&16rC0)){ + --n; + npart++; + if((int buf[n]&16rC0)!=16r80){ + if(utfbytes(buf[n:], npart) > 0){ + (r, w, ok) = byte2char(buf, n); + n += w; + npart -= w; + } + break; + } + } + if(n > 0){ + hold[0:] = buf[n:n+npart]; + buf[n] = byte 0; + q.l.lock(); + str := sprint("#%d", q.p); + x = array of byte str; + m = len x; + if(write(afd, x, m) != m) + error("stdout writing address"); + x = nil; + if(write(dfd, buf, n) != n) + error("stdout writing body"); + q.p += nrunes(buf, n); + q.l.unlock(); + buf[0:] = hold[0:npart]; + } + } +} + +delete(e : ref Event) : int +{ + q0, q1 : int; + deltap : int; + + q0 = e.q0; + q1 = e.q1; + if(q1 <= q.p) + return e.q1-e.q0; + if(q0 >= q.p+ntyper) + return 0; + deltap = 0; + if(q0 < q.p){ + deltap = q.p-q0; + q0 = 0; + }else + q0 -= q.p; + if(q1 > q.p+ntyper) + q1 = ntyper; + else + q1 -= q.p; + deltype(q0, q1); + return deltap; +} + +addtype(c : int, p0 : int, b : array of byte, nb : int, nr : int) +{ + i, w : int; + r, ok : int; + p : int; + b0 : int; + + for(i=0; i 0) + addtype(e.c1, e.q0-q.p, e.b, e.nb, e.nr); + else{ + buf = array[128] of byte; + m = e.q0; + while(m < e.q1){ + str := sprint("#%d", m); + b := array of byte str; + n = len b; + write(afd, b, n); + b = nil; + n = read(dfd, buf, len buf); + nr = nrunes(buf, n); + while(m+nr > e.q1){ + do; while(n>0 && (int buf[--n]&16rC0)==16r80); + --nr; + } + if(n == 0) + break; + addtype(e.c1, m-q.p, buf, n, nr); + m += nr; + } + } + buf = nil; + sendtype(fd0, 0); +} + +send(e : ref Event, fd0 : ref FD, cfd : ref FD, afd : ref FD, dfd : ref FD, donl : int) +{ + l, m, n, nr, lastc, end : int; + abuf, buf : array of byte; + + buf = array[128] of byte; + end = q.p+ntyper; + str := sprint("#%d", end); + abuf = array of byte str; + l = len abuf; + write(afd, abuf, l); + abuf = nil; + if(e.nr > 0){ + write(dfd, e.b, e.nb); + addtype(e.c1, ntyper, e.b, e.nb, e.nr); + lastc = e.r[e.nr-1]; + }else{ + m = e.q0; + lastc = 0; + while(m < e.q1){ + str = sprint("#%d", m); + abuf = array of byte str; + n = len abuf; + write(afd, abuf, n); + abuf = nil; + n = read(dfd, buf, len buf); + nr = nrunes(buf, n); + while(m+nr > e.q1){ + do; while(n>0 && (int buf[--n]&16rC0)==16r80); + --nr; + } + if(n == 0) + break; + str = sprint("#%d", end); + abuf = array of byte str; + l = len abuf; + write(afd, abuf, l); + abuf = nil; + write(dfd, buf, n); + addtype(e.c1, ntyper, buf, n, nr); + lastc = int buf[n-1]; + m += nr; + end += nr; + } + } + if(donl && lastc!='\n'){ + write(dfd, array of byte "\n", 1); + addtype(e.c1, ntyper, array of byte "\n", 1, 1); + } + write(cfd, array of byte "dot=addr", 8); + sendtype(fd0, 0); + buf = nil; +} + diff --git a/appl/acme/acme/edit/guide b/appl/acme/acme/edit/guide new file mode 100644 index 00000000..5beced2e --- /dev/null +++ b/appl/acme/acme/edit/guide @@ -0,0 +1,4 @@ +e file | x '/regexp/' | c 'replacement' +e 'file:0,$' | x '/.*word.*\n/' | p -n +e file | pipe command args ... +New /absolute/file/name diff --git a/appl/acme/acme/edit/mkfile b/appl/acme/acme/edit/mkfile new file mode 100644 index 00000000..4f51f170 --- /dev/null +++ b/appl/acme/acme/edit/mkfile @@ -0,0 +1,24 @@ +<../../../../mkconfig + +BIN=$ROOT/acme/edit + +DIRS=\ + src\ + +TARG=\ + guide\ + readme\ + +BINTARG=${TARG:%=$BIN/%} + +all:V: $TARG + +install:V: $BINTARG + +$BIN/guide : guide + rm -f $BIN/guide && cp guide $BIN/guide + +$BIN/readme : readme + rm -f $BIN/readme && cp readme $BIN/readme + +<$ROOT/mkfiles/mksubdirs \ No newline at end of file diff --git a/appl/acme/acme/edit/readme b/appl/acme/acme/edit/readme new file mode 100644 index 00000000..e4d29579 --- /dev/null +++ b/appl/acme/acme/edit/readme @@ -0,0 +1,31 @@ +The programs collected in /acme/edit offer a sam-like command interface +to acme windows. The guide file + /acme/edit/guide +holds templates for several editing operations implemented +by external programs. These programs, composed in +a pipeline, refine the sections of a file to be modified. +Thus in sam when one says + x/.*\n/ g/foo/ p +in /acme/edit one runs + x '/.*\n/' | g '/foo/' | p +The e command, unrelated to e in sam, disambiguates file names, collects +lists of names, etc., and produces input suitable for the other tools. +For example: + e '/usr/rob/acme:0,$' | x /oldname/ | c /newname/ +changes oldname to newname in all the files loaded in acme whose names match +the literal text /usr/rob/acme. + +The commands in /acme/edit are + e + x + g + c + d + p + pipe (like sam's | , which can't be used for syntactic reasons) + +p takes a -n flag analogous to grep's -n. There is no s command. +e has a -l flag to produce line numbers instead of the default character numbers. +Its implementation is poor but sufficient for the mundane job of recreating +the occasional line number for tools like acid; its use with the other commands +in this directory is discouraged. diff --git a/appl/acme/acme/edit/src/a.b b/appl/acme/acme/edit/src/a.b new file mode 100644 index 00000000..ab950d71 --- /dev/null +++ b/appl/acme/acme/edit/src/a.b @@ -0,0 +1,89 @@ +implement Aa; + +include "sys.m"; +include "draw.m"; +include "bufio.m"; + +sys : Sys; +bufio : Bufio; + +ORDWR, FD, open, read, write, seek, sprint, fprint, fildes, byte2char : import sys; +Iobuf : import bufio; + +Aa : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +stdin, stderr : ref FD; + +init(nil : ref Draw->Context, argl : list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + stdin = fildes(0); + stderr = fildes(2); + main(argl); +} + +include "findfile.b"; + +prog := "a"; +bin : ref Iobuf; + +main(argv : list of string) +{ + afd, cfd, dfd : ref FD; + i, id : int; + nf, n, seq, rlen : int; + f, tf : array of File; + buf, s : string; + + if(len argv != 2){ + fprint(stderr, "usage: %s 'replacement'\n", prog); + exit; + } + +include "input.b"; + + # sort back to original order, backwards + qsort(f, nf, BSCMP); + + # change + id = -1; + afd = nil; + cfd = nil; + dfd = nil; + ab := array of byte hd tl argv; + rlen = len ab; + for(i=0; i 0){ + afd = nil; + cfd = nil; + dfd = nil; + } + id = f[i].id; + buf = sprint("/mnt/acme/%d/addr", id); + afd = open(buf, ORDWR); + if(afd == nil) + rerror(buf); + buf = sprint("/mnt/acme/%d/data", id); + dfd = open(buf, ORDWR); + if(dfd == nil) + rerror(buf); + buf = sprint("/mnt/acme/%d/ctl", id); + cfd = open(buf, ORDWR); + if(cfd == nil) + rerror(buf); + if(write(cfd, array of byte "mark\nnomark\n", 12) != 12) + rerror("setting nomark"); + } + if(fprint(afd, "#%d", f[i].q1) < 0) + rerror("writing address"); + if(write(dfd, ab, rlen) != rlen) + rerror("writing replacement"); + } + exit; +} diff --git a/appl/acme/acme/edit/src/c.b b/appl/acme/acme/edit/src/c.b new file mode 100644 index 00000000..f3bdd060 --- /dev/null +++ b/appl/acme/acme/edit/src/c.b @@ -0,0 +1,104 @@ +implement Cc; + +include "sys.m"; +include "draw.m"; +include "bufio.m"; + +sys : Sys; +bufio : Bufio; + +ORDWR, FD, open, read, write, seek, sprint, fprint, fildes, byte2char : import sys; +Iobuf : import bufio; + +Cc : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +stdin, stderr : ref FD; + +init(nil : ref Draw->Context, argl : list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + stdin = fildes(0); + stderr = fildes(2); + main(argl); +} + +prog := "c"; + +include "findfile.b"; + +bin : ref Iobuf; + +main(argv : list of string) +{ + afd, cfd, dfd : ref FD; + i, j, id : int; + r : string; + nf, n, seq, rlen : int; + f, tf : array of File; + buf, s : string; + + if(len argv != 2){ + fprint(stderr, "usage: %s 'replacement'\n", prog); + exit; + } + +include "input.b"; + + # sort back to original order, backwards + qsort(f, nf, BSCMP); + + # change + id = -1; + afd = nil; + cfd = nil; + dfd = nil; + argv1 := hd tl argv; + rlen = len argv1; + r = argv1; + i = 0; + for(j=0; j0 && r[i-1]=='\\'){ + if(r[i] == 'n') + r[--i] = '\n'; + else if(r[i]=='\\') + r[--i] = '\\'; + } + i++; + } + rlen = i; + ab := array of byte r[0:rlen]; + rlen = len ab; + for(i=0; i 0){ + afd = cfd = dfd = nil; + } + id = f[i].id; + buf = sprint("/mnt/acme/%d/addr", id); + afd = open(buf, ORDWR); + if(afd == nil) + rerror(buf); + buf = sprint("/mnt/acme/%d/data", id); + dfd = open(buf, ORDWR); + if(dfd == nil) + rerror(buf); + buf = sprint("/mnt/acme/%d/ctl", id); + cfd = open(buf, ORDWR); + if(cfd == nil) + rerror(buf); + if(write(cfd, array of byte "mark\nnomark\n", 12) != 12) + rerror("setting nomark"); + } + if(fprint(afd, "#%d,#%d", f[i].q0, f[i].q1) < 0) + rerror("writing address"); + if(write(dfd, ab, rlen) != rlen) + rerror("writing replacement"); + } + exit; +} diff --git a/appl/acme/acme/edit/src/d.b b/appl/acme/acme/edit/src/d.b new file mode 100644 index 00000000..0b663942 --- /dev/null +++ b/appl/acme/acme/edit/src/d.b @@ -0,0 +1,30 @@ +implement Dd; + +include "sys.m"; +include "draw.m"; +include "sh.m"; + +Dd : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +init(ctxt : ref Draw->Context, argl : list of string) +{ + sys := load Sys Sys->PATH; + stderr := sys->fildes(2); + if (len argl != 1) { + sys->fprint(stderr, "usage : d\n"); + return; + } + cmd := "/acme/edit/c"; + file := cmd + ".dis"; + c := load Command file; + if(c == nil) { + sys->fprint(stderr, "%s: %r\n", cmd); + return; + } + argl = nil; + argl = "" :: argl; + argl = cmd :: argl; + c->init(ctxt, argl); +} \ No newline at end of file diff --git a/appl/acme/acme/edit/src/e.b b/appl/acme/acme/edit/src/e.b new file mode 100644 index 00000000..3b71db1d --- /dev/null +++ b/appl/acme/acme/edit/src/e.b @@ -0,0 +1,154 @@ +implement Ee; + +include "sys.m"; +include "draw.m"; +include "bufio.m"; + +sys : Sys; +bufio : Bufio; + +ORDWR, FD, open, read, write, seek, sprint, fprint, fildes, byte2char : import sys; +Iobuf : import bufio; + +Ee : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +stdin, stdout, stderr : ref FD; + +init(nil : ref Draw->Context, argl : list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + stdin = fildes(0); + stdout = fildes(1); + stderr = fildes(2); + main(argl); +} + +include "findfile.b"; + +prog := "e"; + +main(argv : list of string) +{ + afd, cfd : ref FD; + i, id : int; + buf : string; + nf, n, lines, l0, l1 : int; + f, tf : array of File; + + lines = 0; + if(len argv>1 && hd tl argv == "-l"){ + lines = 1; + argv = tl argv; + } + if(len argv < 2){ + fprint(stderr, "usage: %s 'file[:address]' ...\n", prog); + exit; + } + nf = 0; + f = nil; + for(argv = tl argv; argv != nil; argv = tl argv){ + (n, tf) = findfile(hd argv); + if(n == 0) + errors("no files match pattern", hd argv); + oldf := f; + f = array[n+nf] of File; + if(f == nil) + rerror("out of memory"); + if (oldf != nil) { + f[0:] = oldf[0:nf]; + oldf = nil; + } + f[nf:] = tf[0:n]; + nf += n; + tf = nil; + } + + # convert to character positions + for(i=0; i l0) + fprint(stdout, "%s:%d,%d\n", f[i].name, l0, l1); + else + fprint(stdout, "%s:%d\n", f[i].name, l0); + }else{ + if(f[i].q1 > f[i].q0) + fprint(stdout, "%s:#%d,#%d\n", f[i].name, f[i].q0, f[i].q1); + else + fprint(stdout, "%s:#%d\n", f[i].name, f[i].q0); + } + } + exit; +} + +lineno(f : File) : (int, int) +{ + b : ref Iobuf; + n0, n1, q, r : int; + buf : string; + + buf = sprint("/mnt/acme/%d/body", f.id); + b = bufio->open(buf, bufio->OREAD); + if(b == nil){ + fprint(stderr, "%s: can't open %s: %r\n", prog, buf); + exit; + } + n0 = 1; + n1 = 1; + for(q=0; qEOF){ + fprint(stderr, "%s: early EOF on %s\n", prog, buf); + exit; + } + if(r=='\n'){ + if(q < f.q0) + n0++; + if(q+1 < f.q1) + n1++; + } + } + b.close(); + return (n0, n1); +} diff --git a/appl/acme/acme/edit/src/findfile.b b/appl/acme/acme/edit/src/findfile.b new file mode 100644 index 00000000..b8bf4c9c --- /dev/null +++ b/appl/acme/acme/edit/src/findfile.b @@ -0,0 +1,210 @@ +File : adt { + id : int; + seq : int; + ok : int; + q0, q1 : int; + name : string; + addr : string; +}; + +BSCMP, SCMP, NCMP, FCMP : con iota; + +indexfile := "/mnt/acme/index"; + +dfd: ref Sys->FD; +debug(s : string) +{ + if (dfd == nil) + dfd = sys->create("/usr/jrf/acme/debugedit", Sys->OWRITE, 8r600); + sys->fprint(dfd, "%s", s); +} + +error(s : string) +{ + fprint(stderr, "%s: %s\n", prog, s); + exit; +} + +errors(s, t : string) +{ + fprint(stderr, "%s: %s %s\n", prog, s, t); + exit; +} + +rerror(s : string) +{ + fprint(stderr, "%s: %s: %r\n", prog, s); + exit; +} + +strcmp(s, t : string) : int +{ + if (s < t) return -1; + if (s > t) return 1; + return 0; +} + +strstr(s, t : string) : int +{ + if (t == nil) + return 0; + n := len t; + if (n > len s) + return -1; + e := len s - n; + for (p := 0; p <= e; p++) + if (s[p:p+n] == t) + return p; + return -1; +} + +nrunes(s : array of byte, nb : int) : int +{ + i, n, r, b, ok : int; + + n = 0; + for(i=0; iopen(indexfile, bufio->OREAD); + else + index.seek(big 0, 0); + if(index == nil) + rerror(indexfile); + for(colon=0; colon < len pat && pat[colon]!=':'; colon++) + ; + if (colon == len pat) { + pat1 = pat; + pat2 = "."; + } + else { + pat1 = pat[0:colon]; + pat2 = pat[colon+1:]; + } + n = 0; + f = nil; + while((line=index.gets('\n')) != nil){ + if(len line < 5*12) + rerror("bad index file format"); + line = line[0:len line - 1]; + for(blank=5*12; blank < len line && line[blank]!=' '; blank++) + ; + if (blank < len line) + line = line[0:blank]; + if(strcmp(line[5*12:], pat1) == 0){ + # exact match: take that + f = nil; # should also free t->addr's + f = array[1] of File; + if(f == nil) + rerror("out of memory"); + f[0].id = int line; + f[0].name = line[5*12:]; + f[0].addr = pat2; + n = 1; + break; + } + if(strstr(line[5*12:], pat1) >= 0){ + # partial match: add to list + off := f; + f = array[n+1] of File; + if(f == nil) + rerror("out of memory"); + f[0:] = off[0:n]; + off = nil; + f[n].id = int line; + f[n].name = line[5*12:]; + f[n].addr = pat2; + n++; + } + } + return (n, f); +} + +bscmp(a : File, b : File) : int +{ + return b.seq - a.seq; +} + +scmp(a : File, b : File) : int +{ + return a.seq - b.seq; +} + +ncmp(a : File, b : File) : int +{ + return strcmp(a.name, b.name); +} + +fcmp(a : File, b : File) : int +{ + x : int; + + if (a.name < b.name) + return -1; + if (a.name > b.name) + return 1; + x = a.q0 - b.q0; + if(x != 0) + return x; + return a.q1-b.q1; +} + +gencmp(a : File, b : File, c : int) : int +{ + if (c == BSCMP) + return bscmp(a, b); + if (c == SCMP) + return scmp(a, b); + if (c == NCMP) + return ncmp(a, b); + if (c == FCMP) + return fcmp(a, b); + return 0; +} + +qsort(a : array of File, n : int, c : int) +{ + i, j : int; + t : File; + + while(n > 1) { + i = n>>1; + t = a[0]; a[0] = a[i]; a[i] = t; + i = 0; + j = n; + for(;;) { + do + i++; + while(i < n && gencmp(a[i], a[0], c) < 0); + do + j--; + while(j > 0 && gencmp(a[j], a[0], c) > 0); + if(j < i) + break; + t = a[i]; a[i] = a[j]; a[j] = t; + } + t = a[0]; a[0] = a[j]; a[j] = t; + n = n-j-1; + if(j >= n) { + qsort(a, j, c); + a = a[j+1:]; + } else { + qsort(a[j+1:], n, c); + n = j; + } + } +} \ No newline at end of file diff --git a/appl/acme/acme/edit/src/g.b b/appl/acme/acme/edit/src/g.b new file mode 100644 index 00000000..87c57ff0 --- /dev/null +++ b/appl/acme/acme/edit/src/g.b @@ -0,0 +1,95 @@ +implement Gg; + +include "sys.m"; +include "draw.m"; +include "bufio.m"; + +sys : Sys; +bufio : Bufio; + +ORDWR, FD, open, read, write, seek, sprint, fprint, fildes, byte2char : import sys; +Iobuf : import bufio; + +Gg : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +stdin, stdout, stderr : ref FD; + +init(nil : ref Draw->Context, argl : list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + stdin = fildes(0); + stdout = fildes(1); + stderr = fildes(2); + main(argl); +} + +prog := "g"; + +include "findfile.b"; + +bin : ref Iobuf; + +main(argv : list of string) +{ + afd, cfd, dfd : ref FD; + i, id, seq : int; + nf, n, plen : int; + f, tf : array of File; + buf, s : string; + + if(len argv!=2 || len hd tl argv==0 || (hd tl argv)[0]!='/'){ + fprint(stderr, "usage: %s '/regexp/'\n", prog); + exit; + } + +include "input.b"; + + # execute regexp + id = -1; + afd = nil; + dfd = nil; + cfd = nil; + bufb := array of byte hd tl argv; + plen = len bufb; + for(i=0; i 0){ + afd = cfd = dfd = nil; + } + id = f[i].id; + buf = sprint("/mnt/acme/%d/addr", id); + afd = open(buf, ORDWR); + if(afd == nil) + rerror(buf); + buf = sprint("/mnt/acme/%d/ctl", id); + cfd = open(buf, ORDWR); + if(cfd == nil) + rerror(buf); + buf = sprint("/mnt/acme/%d/data", id); + dfd = open(buf, ORDWR); + if(dfd == nil) + rerror(buf); + } + ab := array of byte f[i].addr; + n = len ab; + if(write(afd, ab, n)!=n || fprint(cfd, "limit=addr\n")<0){ + buf = sprint("%s:%s is invalid limit", f[i].name, f[i].addr); + rerror(buf); + } + if(fprint(afd, "#%d", f[i].q0) < 0) + rerror("can't set dot"); + # look for match + if(write(afd, bufb, plen) == plen){ + if(f[i].q0 == f[i].q1) + fprint(stdout, "%s:#%d\n", f[i].name, f[i].q0); + else + fprint(stdout, "%s:#%d,#%d\n", f[i].name, f[i].q0, f[i].q1); + } + } + exit; +} diff --git a/appl/acme/acme/edit/src/i.b b/appl/acme/acme/edit/src/i.b new file mode 100644 index 00000000..ecb3e01f --- /dev/null +++ b/appl/acme/acme/edit/src/i.b @@ -0,0 +1,88 @@ +implement Ii; + +include "sys.m"; +include "draw.m"; +include "bufio.m"; + +sys : Sys; +bufio : Bufio; + +ORDWR, FD, open, read, write, seek, sprint, fprint, fildes, byte2char : import sys; +Iobuf : import bufio; + +Ii : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +stdin, stderr : ref FD; + +init(nil : ref Draw->Context, argl : list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + stdin = fildes(0); + stderr = fildes(2); + main(argl); +} + +prog := "i"; + +include "findfile.b"; + +bin : ref Iobuf; + +main(argv : list of string) +{ + afd, cfd, dfd : ref FD; + i, id : int; + nf, n, seq, rlen : int; + f, tf : array of File; + s, buf : string; + + if(len argv != 2){ + fprint(stderr, "usage: %s 'replacement'\n", prog); + exit; + } + +include "input.b"; + + # sort back to original order, backwards + qsort(f, nf, BSCMP); + + # change + id = -1; + afd = nil; + cfd = nil; + dfd = nil; + ab := array of byte hd tl argv; + rlen = len ab; + for(i=0; i 0){ + afd = cfd = dfd = nil; + } + id = f[i].id; + buf = sprint("/mnt/acme/%d/addr", id); + afd = open(buf, ORDWR); + if(afd == nil) + rerror(buf); + buf = sprint("/mnt/acme/%d/data", id); + dfd = open(buf, ORDWR); + if(dfd == nil) + rerror(buf); + buf = sprint("/mnt/acme/%d/ctl", id); + cfd = open(buf, ORDWR); + if(cfd == nil) + rerror(buf); + if(write(cfd, array of byte "mark\nnomark\n", 12) != 12) + rerror("setting nomark"); + } + if(fprint(afd, "#%d", f[i].q0) < 0) + rerror("writing address"); + if(write(dfd, ab, rlen) != rlen) + rerror("writing replacement"); + } + exit; +} diff --git a/appl/acme/acme/edit/src/input.b b/appl/acme/acme/edit/src/input.b new file mode 100644 index 00000000..64ab9297 --- /dev/null +++ b/appl/acme/acme/edit/src/input.b @@ -0,0 +1,84 @@ + bin = bufio->fopen(stdin, bufio->OREAD); + seq = 0; + nf = 0; + f = nil; + while((s=bin.gets('\n')) != nil){ + s = s[0:len s - 1]; + (n, tf) = findfile(s); + if(n == 0) + errors("no files match input", s); + for(i=0; i 0 && '0'<=s[0] && s[0]<='9'){ + n = n*10+(s[0]-'0'); + s = s[1:]; + } + f[i].q0 = n; + if(len s == 0){ + f[i].q1 = n; + continue; + } + if(s[0] == ',') { + s = s[1:]; + n = 0; + while(len s > 0 && '0'<=s[0] && s[0]<='9'){ + n = n*10+(s[0]-'0'); + s = s[1:]; + } + f[i].q1 = n; + if(len s == 0) + continue; + } + } + id = f[i].id; + buf = sprint("/chan/%d/addr", id); + afd = open(buf, ORDWR); + if(afd == nil) + rerror(buf); + buf = sprint("/chan/%d/ctl", id); + cfd = open(buf, ORDWR); + if(cfd == nil) + rerror(buf); + if(write(cfd, array of byte "addr=dot\n", 9) != 9) + rerror("setting address to dot"); + ab := array of byte f[i].addr; + if(write(afd, ab, len ab) != len ab){ + fprint(stderr, "%s: %s:%s is invalid address\n", prog, f[i].name, f[i].addr); + f[i].ok = 0; + afd = cfd = nil; + continue; + } + seek(afd, big 0, 0); + bbuf := array[2*12] of byte; + if(read(afd, bbuf, len bbuf) != 2*12) + rerror("reading address"); + afd = cfd = nil; + buf = string bbuf; + bbuf = nil; + f[i].q0 = int buf; + f[i].q1 = int buf[12:]; + } diff --git a/appl/acme/acme/edit/src/mkfile b/appl/acme/acme/edit/src/mkfile new file mode 100644 index 00000000..394b6017 --- /dev/null +++ b/appl/acme/acme/edit/src/mkfile @@ -0,0 +1,23 @@ +<../../../../../mkconfig + +TARG=\ + a.dis\ + c.dis\ + d.dis\ + e.dis\ + g.dis\ + i.dis\ + p.dis\ + x.dis\ + pipe.dis\ + +MODULES=\ + +SYSMODULES=\ + sh.m\ + sys.m\ + draw.m\ + +DISBIN=$ROOT/acme/edit + +<$ROOT/mkfiles/mkdis diff --git a/appl/acme/acme/edit/src/p.b b/appl/acme/acme/edit/src/p.b new file mode 100644 index 00000000..feeb7f88 --- /dev/null +++ b/appl/acme/acme/edit/src/p.b @@ -0,0 +1,109 @@ +implement Pp; + +include "sys.m"; +include "draw.m"; +include "bufio.m"; + +sys : Sys; +bufio : Bufio; + +ORDWR, FD, open, read, write, seek, sprint, fprint, fildes, byte2char : import sys; +Iobuf : import bufio; + +Pp : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +stdin, stdout, stderr : ref FD; + +init(nil : ref Draw->Context, argl : list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + stdin = fildes(0); + stdout = fildes(1); + stderr = fildes(2); + main(argl); +} + +include "findfile.b"; + +prog := "p"; +bin : ref Iobuf; + +main(argv : list of string) +{ + afd, cfd, dfd : ref FD; + i, id : int; + m, nr, nf, n, nflag, seq : int; + f, tf : array of File; + buf, s : string; + + nflag = 0; + if(len argv==2 && hd tl argv == "-n"){ + argv = tl argv; + nflag = 1; + } + if(len argv != 1){ + fprint(stderr, "usage: %s [-n]\n", prog); + exit; + } + +include "input.b"; + + # sort back to original order + qsort(f, nf, SCMP); + + # print + id = -1; + afd = nil; + cfd = nil; + dfd = nil; + for(i=0; i 0){ + afd = cfd = dfd = nil; + } + id = f[i].id; + buf = sprint("/mnt/acme/%d/addr", id); + afd = open(buf, ORDWR); + if(afd == nil) + rerror(buf); + buf = sprint("/mnt/acme/%d/data", id); + dfd = open(buf, ORDWR); + if(dfd == nil) + rerror(buf); + buf = sprint("/mnt/acme/%d/ctl", id); + cfd = open(buf, ORDWR); + if(cfd == nil) + rerror(buf); + } + if(nflag){ + if(f[i].q1 > f[i].q0) + fprint(stdout, "%s:#%d,#%d: ", f[i].name, f[i].q0, f[i].q1); + else + fprint(stdout, "%s:#%d: ", f[i].name, f[i].q0); + } + m = f[i].q0; + while(m < f[i].q1){ + if(fprint(afd, "#%d", m) < 0){ + fprint(stderr, "%s: %s:%s is invalid address\n", prog, f[i].name, f[i].addr); + continue; + } + bbuf := array[512] of byte; + n = read(dfd, bbuf, len buf); + nr = nrunes(bbuf, n); + while(m+nr > f[i].q1){ + do; while(n>0 && (int bbuf[--n]&16rC0)==16r80); + --nr; + } + if(n == 0) + break; + write(stdout, bbuf, n); + m += nr; + } + } + exit; +} diff --git a/appl/acme/acme/edit/src/pipe.b b/appl/acme/acme/edit/src/pipe.b new file mode 100644 index 00000000..f96289d9 --- /dev/null +++ b/appl/acme/acme/edit/src/pipe.b @@ -0,0 +1,212 @@ +implement Pipe; + +include "sys.m"; +include "draw.m"; +include "bufio.m"; +include "sh.m"; + +sys : Sys; +bufio : Bufio; + +UTFmax, ORDWR, NEWFD, FD, open, read, write, seek, sprint, fprint, fildes, byte2char, pipe, dup, pctl : import sys; +Iobuf : import bufio; + +Pipe : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +stdin, stderr : ref FD; +pipectxt : ref Draw->Context; + +init(ctxt : ref Draw->Context, argl : list of string) +{ + pipectxt = ctxt; + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + stdin = fildes(0); + stderr = fildes(2); + main(argl); +} + +include "findfile.b"; + +prog := "pipe"; +bin : ref Iobuf; + +main(argv : list of string) +{ + afd, dfd, cfd : ref FD; + nf, nc, nr, npart : int; + p1, p2 : array of ref FD; + i, n, id, seq : int; + buf : string; + tmp, data : array of byte; + s : string; + r, s0 : int; + f, tf : array of File; + q, q0, q1 : int; + cpid : chan of int; + w, ok : int; + + if(len argv < 2){ + fprint(stderr, "usage: pipe command\n"); + exit; + } + +include "input.b"; + + # sort back to original order + qsort(f, nf, SCMP); + + # pipe + id = -1; + afd = nil; + cfd = nil; + dfd = nil; + tmp = array[8192+UTFmax] of byte; + if(tmp == nil) + error("malloc"); + cpid = chan of int; + for(i=0; i 0){ + afd = cfd = dfd = nil; + } + id = f[i].id; + buf = sprint("/mnt/acme/%d/addr", id); + afd = open(buf, ORDWR); + if(afd == nil) + rerror(buf); + buf = sprint("/mnt/acme/%d/data", id); + dfd = open(buf, ORDWR); + if(dfd == nil) + rerror(buf); + buf = sprint("/mnt/acme/%d/ctl", id); + cfd = open(buf, ORDWR); + if(cfd == nil) + rerror(buf); + if(write(cfd, array of byte "mark\nnomark\n", 12) != 12) + rerror("setting nomark"); + } + + if(fprint(afd, "#%ud", f[i].q0) < 0) + rerror("writing address"); + + q0 = f[i].q0; + q1 = f[i].q1; + # suck up data + data = array[(q1-q0)*UTFmax+1] of byte; + if(data == nil) + error("malloc failed\n"); + s0 = 0; + q = q0; + bbuf := array[12] of byte; + while(q < q1){ + nc = read(dfd, data[s0:], (q1-q)*UTFmax); + if(nc <= 0) + error("read error from acme"); + seek(afd, big 0, 0); + if(read(afd, bbuf, 12) != 12) + rerror("reading address"); + q = int string bbuf; + s0 += nc; + } + bbuf = nil; + s0 = 0; + for(nr=0; nr 0){ + nc += npart; + s0 = 0; + while(s0 <= nc-UTFmax){ + (r, w, ok) = byte2char(tmp, s0); + s0 += w; + q1++; + } + if(s0 > 0) + if(write(dfd, tmp, s0) != s0) + error("write error to acme"); + npart = nc - s0; + tmp[0:] = tmp[s0:s0+npart]; + } + p2[0] = nil; + if(npart){ + s0 = 0; + while(s0 < npart){ + (r, w, ok) = byte2char(tmp, s0); + s0 += w; + q1++; + } + if(write(dfd, tmp, npart) != npart) + error("write error to acme"); + } + if(fprint(afd, "#%d,#%d", q0, q1) < 0) + rerror("writing address"); + if(fprint(cfd, "dot=addr\n") < 0) + rerror("writing dot"); + data = nil; + } +} + +run(argv : list of string, p1, p2 : ref FD, c : chan of int) +{ + pctl(NEWFD, 0::1::2::p1.fd::p2.fd::nil); + dup(p1.fd, 0); + dup(p2.fd, 1); + c <-= pctl(0, nil); + exec(hd argv, argv); + fprint(stderr, "can't exec"); + exit; +} + +send(buf : array of byte, nbuf : int, fd : ref FD) +{ + if(write(fd, buf, nbuf) != nbuf) + error("write error to process"); + fd = nil; +} + +exec(cmd : string, argl : list of string) +{ + file := cmd; + if(len file<4 || file[len file-4:]!=".dis") + file += ".dis"; + + c := load Command file; + if(c == nil) { + err := sys->sprint("%r"); + if(file[0]!='/' && file[0:2]!="./"){ + c = load Command "/dis/"+file; + if(c == nil) + err = sys->sprint("%r"); + } + if(c == nil){ + sys->fprint(stderr, "%s: %s\n", cmd, err); + return; + } + } + + c->init(pipectxt, argl); +} \ No newline at end of file diff --git a/appl/acme/acme/edit/src/x.b b/appl/acme/acme/edit/src/x.b new file mode 100644 index 00000000..e665ffd2 --- /dev/null +++ b/appl/acme/acme/edit/src/x.b @@ -0,0 +1,123 @@ +implement Xx; + +include "sys.m"; +include "draw.m"; +include "bufio.m"; + +sys : Sys; +bufio : Bufio; + +ORDWR, FD, open, read, write, seek, sprint, fprint, fildes, byte2char : import sys; +Iobuf : import bufio; + +Xx : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +stdin, stdout, stderr : ref FD; + +init(nil : ref Draw->Context, argl : list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + stdin = fildes(0); + stdout = fildes(1); + stderr = fildes(2); + main(argl); +} + +include "findfile.b"; + +prog := "x"; +bin : ref Iobuf; + +main(argv : list of string) +{ + afd, cfd, dfd : ref FD; + i, id, seq : int; + nf, n, plen : int; + addr, aq0, aq1, matched : int; + f, tf : array of File; + buf, s : string; + bbuf0 : array of byte; + + if(len argv!=2 || len hd tl argv==0 || (hd tl argv)[0]!='/'){ + fprint(stderr, "usage: %s '/regexp/'\n", prog); + exit; + } + +include "input.b"; + + # execute regexp + id = -1; + afd = nil; + dfd = nil; + cfd = nil; + for(i=0; i 0){ + afd = cfd = dfd = nil; + } + id = f[i].id; + buf = sprint("/mnt/acme/%d/addr", id); + afd = open(buf, ORDWR); + if(afd == nil) + rerror(buf); + buf = sprint("/mnt/acme/%d/ctl", id); + cfd = open(buf, ORDWR); + if(cfd == nil) + rerror(buf); + buf = sprint("/mnt/acme/%d/data", id); + dfd = open(buf, ORDWR); + if(dfd == nil) + rerror(buf); + } + bbuf0 = array of byte f[i].addr; + n = len bbuf0; + if(write(afd, bbuf0, n)!=n || fprint(cfd, "limit=addr\n")<0){ + buf = sprint("%s:%s is invalid limit", f[i].name, f[i].addr); + rerror(buf); + } + if(fprint(afd, "#%d", f[i].q0) < 0) + rerror("can't set address"); + if(fprint(cfd, "dot=addr") < 0) + rerror("can't unset dot"); + addr = f[i].q0-1; + bbuf := array of byte hd tl argv; + plen = len bbuf; + matched = 0; + # scan for matches + for(;;){ + if(write(afd, bbuf, plen) != plen) + break; + seek(afd, big 0, 0); + bbuf0 = array[2*12] of byte; + if(read(afd, bbuf0, len bbuf0) != 2*12) + rerror("reading address"); + buf = string bbuf0; + bbuf0 = nil; + aq0 = int buf; + aq1 = int buf[12:]; + if(matched && aq1==aq0 && addr==aq1){ # repeated null match; advance + matched = 0; + addr++; + if(addr > f[i].q1) + break; + if(fprint(afd, "#%d", addr) < 0) + rerror("writing address"); + continue; + } + matched = 1; + if(aq0=f[i].q1 || aq1>f[i].q1) + break; + addr = aq1; + if(aq0 == aq1) + fprint(stdout, "%s:#%d\n", f[i].name, aq0); + else + fprint(stdout, "%s:#%d,#%d\n", f[i].name, aq0, aq1); + } + } + exit; +} diff --git a/appl/acme/acme/edit/src/xxx.b b/appl/acme/acme/edit/src/xxx.b new file mode 100644 index 00000000..793f6d10 --- /dev/null +++ b/appl/acme/acme/edit/src/xxx.b @@ -0,0 +1,327 @@ +implement Ee; + +include "sys.m"; +include "draw.m"; +include "bufio.m"; + +sys : Sys; +bufio : Bufio; + +ORDWR, FD, open, read, write, seek, sprint, fprint, fildes, byte2char : import sys; +Iobuf : import bufio; + +Ee : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +stdin, stdout, stderr : ref FD; + +init(ctxt : ref Draw->Context, argl : list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + stdin = fildes(0); + stdout = fildes(1); + stderr = fildes(2); + main(argl); +} + +File : adt { + id : int; + seq : int; + ok : int; + q0, q1 : int; + name : string; + addr : string; +}; + +BSCMP, SCMP, NCMP, FCMP : con iota; + +indexfile := "/usr/jrf/tmp/index"; + +dfd: ref Sys->FD; +debug(s : string) +{ + if (dfd == nil) + dfd = sys->create("/usr/jrf/acme/debugedit", Sys->OWRITE, 8r600); + sys->fprint(dfd, "%s", s); +} + +error(s : string) +{ +debug(sys->sprint("error %s\n", s)); + fprint(stderr, "%s: %s\n", prog, s); + exit; +} + +errors(s, t : string) +{ +debug(sys->sprint("errors %s %s\n", s, t)); + fprint(stderr, "%s: %s %s\n", prog, s, t); + exit; +} + +rerror(s : string) +{ +debug(sys->sprint("rerror %s\n", s)); + fprint(stderr, "%s: %s: %r\n", prog, s); + exit; +} + +strcmp(s, t : string) : int +{ + if (s < t) return -1; + if (s > t) return 1; + return 0; +} + +strstr(s, t : string) : int +{ + if (t == nil) + return 0; + n := len t; + if (n > len s) + return -1; + e := len s - n; + for (p := 0; p <= e; p++) + if (s[p:p+n] == t) + return p; + return -1; +} + +nrunes(s : array of byte, nb : int) : int +{ + i, n, r, b, ok : int; + + n = 0; + for(i=0; iopen(indexfile, bufio->OREAD); + else + index.seek(0, 0); + if(index == nil) + rerror(indexfile); + for(colon=0; colon < len pat && pat[colon]!=':'; colon++) + ; + if (colon == len pat) { + pat1 = pat; + pat2 = "."; + } + else { + pat1 = pat[0:colon]; + pat2 = pat[colon+1:]; + } + n = 0; + f = nil; + while((line=index.gets('\n')) != nil){ + if(len line < 5*12) + rerror("bad index file format"); + line = line[0:len line - 1]; + for(blank=5*12; blank < len line && line[blank]!=' '; blank++) + ; + if (blank < len line) + line = line[0:blank]; + if(strcmp(line[5*12:], pat1) == 0){ + # exact match: take that + f = nil; # should also free t->addr's + f = array[1] of File; + if(f == nil) + rerror("out of memory"); + f[0].id = int line; + f[0].name = line[5*12:]; + f[0].addr = pat2; + n = 1; + break; + } + if(strstr(line[5*12:], pat1) >= 0){ + # partial match: add to list + off := f; + f = array[n+1] of File; + if(f == nil) + rerror("out of memory"); + f[0:] = off[0:n]; + off = nil; + f[n].id = int line; + f[n].name = line[5*12:]; + f[n].addr = pat2; + n++; + } + } + return (n, f); +} + +bscmp(a : File, b : File) : int +{ + return b.seq - a.seq; +} + +scmp(a : File, b : File) : int +{ + return a.seq - b.seq; +} + +ncmp(a : File, b : File) : int +{ + return strcmp(a.name, b.name); +} + +fcmp(a : File, b : File) : int +{ + x : int; + + if (a.name < b.name) + return -1; + if (a.name > b.name) + return 1; + x = a.q0 - b.q0; + if(x != 0) + return x; + return a.q1-b.q1; +} + +gencmp(a : File, b : File, c : int) : int +{ + if (c == BSCMP) + return bscmp(a, b); + if (c == SCMP) + return scmp(a, b); + if (c == NCMP) + return ncmp(a, b); + if (c == FCMP) + return fcmp(a, b); + return 0; +} + +qsort(a : array of File, n : int, c : int) +{ + i, j : int; + t : File; + + while(n > 1) { + i = n>>1; + t = a[0]; a[0] = a[i]; a[i] = t; + i = 0; + j = n; + for(;;) { + do + i++; + while(i < n && gencmp(a[i], a[0], c) < 0); + do + j--; + while(j > 0 && gencmp(a[j], a[0], c) > 0); + if(j < i) + break; + t = a[i]; a[i] = a[j]; a[j] = t; + } + t = a[0]; a[0] = a[j]; a[j] = t; + n = n-j-1; + if(j >= n) { + qsort(a, j, c); + a = a[j+1:]; + } else { + qsort(a[j+1:], n, c); + n = j; + } + } +} + +prog := "e"; + +main(argv : list of string) +{ + afd, cfd : ref FD; + i, id : int; + buf : string; + nf, n, lines, l0, l1 : int; + f, tf : array of File; + + if(len argv < 2){ +debug(sys->sprint("usage\n")); + fprint(stderr, "usage: %s 'file[:address]' ...\n", prog); + exit; + } + nf = 0; + f = nil; + for(argv = tl argv; argv != nil; argv = tl argv){ + (n, tf) = findfile(hd argv); + if(n == 0) + errors("no files match pattern", hd argv); + oldf := f; + f = array[n+nf] of File; + if(f == nil) + rerror("out of memory"); + if (oldf != nil) { + f[0:] = oldf[0:nf]; + oldf = nil; + } + f[nf:] = tf[0:n]; + nf += n; + tf = nil; + } +debug(sys->sprint("nf=%d\n", nf)); + # convert to character positions + for(i=0; isprint("q0=%d q1=%d\n", f[i].q0, f[i].q1)); + } + + # sort + # qsort(f, nf, FCMP); + + # print + for(i=0; i f[i].q0) + fprint(stdout, "%s:#%d,#%d\n", f[i].name, f[i].q0, f[i].q1); + else + fprint(stdout, "%s:#%d\n", f[i].name, f[i].q0); + } + } +debug("e exiting\n"); + exit; +} \ No newline at end of file diff --git a/appl/acme/acme/mail/guide b/appl/acme/acme/mail/guide new file mode 100644 index 00000000..2e23b9e1 --- /dev/null +++ b/appl/acme/acme/mail/guide @@ -0,0 +1,5 @@ +Mail /mail/box/$user/stored +Mail +Mailpop3 +mkbox /mail/box/$user/new_box +mail -'x' someaddress diff --git a/appl/acme/acme/mail/mkbox.b b/appl/acme/acme/mail/mkbox.b new file mode 100644 index 00000000..c0da4a78 --- /dev/null +++ b/appl/acme/acme/mail/mkbox.b @@ -0,0 +1,25 @@ +implement Mkbox; + +include "sys.m"; +include "draw.m"; + +sys : Sys; + +FD : import sys; + +Mkbox : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +init(nil : ref Draw->Context, argl : list of string) +{ + sys = load Sys Sys->PATH; + for (argl = tl argl; argl != nil; argl = tl argl) { + nm := hd argl; + (ok, dir) := sys->stat(nm); + if (ok < 0) { + fd := sys->create(nm, Sys->OREAD, 8r600); + fd = nil; + } + } +} diff --git a/appl/acme/acme/mail/mkfile b/appl/acme/acme/mail/mkfile new file mode 100644 index 00000000..7ede0684 --- /dev/null +++ b/appl/acme/acme/mail/mkfile @@ -0,0 +1,34 @@ +<../../../../mkconfig + +BIN=$ROOT/acme/mail + +DIRS=\ + src\ + +TARG=\ + guide\ + readme\ + mkbox.dis\ + +MODULES=\ + +SYSMODULES=\ + sys.m\ + draw.m\ + +BINTARG=${TARG:%=$BIN/%} + +DISBIN=$ROOT/acme/mail + +all:V: $TARG + +install:V: $BINTARG + +$BIN/guide : guide + rm -f $BIN/guide && cp guide $BIN/guide + +$BIN/readme : readme + rm -f $BIN/readme && cp readme $BIN/readme + +<$ROOT/mkfiles/mkdis +<$ROOT/mkfiles/mksubdirs \ No newline at end of file diff --git a/appl/acme/acme/mail/readme b/appl/acme/acme/mail/readme new file mode 100644 index 00000000..9442f57e --- /dev/null +++ b/appl/acme/acme/mail/readme @@ -0,0 +1,29 @@ +Mail is the single program in this directory. Its argument specifies +the mail box to read, default /mail/box/$user/mbox. +For example, running + Mail /mail/box/$user/stored +(a line in the guide file) looks at saved mail. + +Mail maintains a window containing headers for all the +messages in the mailbox and monitors the mailbox for new messages. +Using button 3 to indicate a message number opens +a window on that message. commands in the mailbox window are + Put Write the mailbox back to the file (never done automatically) + Mail Make a new message window ready to mail someone. + Takes argument names analogously to acme's New. + Del Exit Mail, after checking that mailbox isn't modified. +New messages appear at the top of the window and are highlighted upon arrival. +(The messages are numbered oldest to newest, the opposite of regular mail.) + +Message windows have a simple format: the first line, up to the first tab or newline, +holds the sender or, when sending, the addressee. Edit the line to change who the +message goes to. Message windows contain the commands + Reply Make a new window to compose a reply to this message + Delmesg Delete the message from the screen and from the mailbox + Del Delete the window, leaving the message in the mailbox + Post Send the message to the addressee + Save Save to the named mailbox, default/mail/box/$user/stored +Save takes a full file name; if that name has no slashes, the file is taken +to be in /mail/box/$user and must already exist. Use mkbox in the guide to +create target mailboxes in /mail/box/$user. +Reply and mail windows contain an obvious subset of the commands. diff --git a/appl/acme/acme/mail/src/Mail.b b/appl/acme/acme/mail/src/Mail.b new file mode 100644 index 00000000..1b2cf7c3 --- /dev/null +++ b/appl/acme/acme/mail/src/Mail.b @@ -0,0 +1,1715 @@ +implement mail; + +include "sys.m"; +include "draw.m"; +include "bufio.m"; +include "daytime.m"; +include "sh.m"; + +mail : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +sys : Sys; +bufio : Bufio; +daytime : Daytime; + +OREAD, OWRITE, ORDWR, NEWFD, FORKFD, FORKENV, NEWPGRP, UTFmax : import Sys; +FD, Dir : import sys; +fprint, sprint, sleep, create, open, read, write, remove, stat, fstat, fwstat, fildes, pctl, pipe, dup, byte2char : import sys; +Context : import Draw; +EOF : import Bufio; +Iobuf : import bufio; +time : import daytime; + +DIRLEN : con 116; +PNPROC, PNGROUP : con iota; +False : con 0; +True : con 1; +EVENTSIZE : con 256; +Runeself : con 16r80; +OCEXEC : con 0; +CHEXCL : con 0; # 16r20000000; # for the moment +CHAPPEND : con 0; # 16r40000000; + +MAILDIR : con "/mail"; + +Win : adt { + winid : int; + addr : ref FD; + body : ref Iobuf; + ctl : ref FD; + data : ref FD; + event : ref FD; + buf : array of byte; + bufp : int; + nbuf : int; + + wnew : fn() : ref Win; + wwritebody : fn(w : self ref Win, s : string); + wread : fn(w : self ref Win, m : int, n : int) : string; + wclean : fn(w : self ref Win); + wname : fn(w : self ref Win, s : string); + wdormant : fn(w : self ref Win); + wevent : fn(w : self ref Win, e : ref Event); + wshow : fn(w : self ref Win); + wtagwrite : fn(w : self ref Win, s : string); + wwriteevent : fn(w : self ref Win, e : ref Event); + wslave : fn(w : self ref Win, c : chan of Event); + wreplace : fn(w : self ref Win, s : string, t : string); + wselect : fn(w : self ref Win, s : string); + wsetdump : fn(w : self ref Win, s : string, t : string); + wdel : fn(w : self ref Win, n : int) : int; + wreadall : fn(w : self ref Win) : string; + + ctlwrite : fn(w : self ref Win, s : string); + getec : fn(w : self ref Win) : int; + geten : fn(w : self ref Win) : int; + geter : fn(w : self ref Win, s : array of byte) : (int, int); + openfile : fn(w : self ref Win, s : string) : ref FD; + openbody : fn(w : self ref Win, n : int); +}; + +Mesg : adt { + w : ref Win; + id : int; + hdr : string; + realhdr : string; + replyto : string; + text : string; + subj : string; + next : cyclic ref Mesg; + lline1 : int; + box : cyclic ref Box; + isopen : int; + posted : int; + + read : fn(b : ref Box) : ref Mesg; + open : fn(m : self ref Mesg); + slave : fn(m : self ref Mesg); + free : fn(m : self ref Mesg); + save : fn(m : self ref Mesg, s : string); + mkreply : fn(m : self ref Mesg); + mkmail : fn(b : ref Box, s : string); + putpost : fn(m : self ref Mesg, e : ref Event); + + command : fn(m : self ref Mesg, s : string) : int; + send : fn(m : self ref Mesg); +}; + +Box : adt { + w : ref Win; + nm : int; + readonly : int; + m : cyclic ref Mesg; + file : string; + io : ref Iobuf; + clean : int; + leng : big; + cdel : chan of ref Mesg; + cevent : chan of Event; + cmore : chan of int; + + line : string; + peekline : string; + + read : fn(s : string, n : int) : ref Box; + readmore : fn(b : self ref Box); + readline : fn(b : self ref Box) : string; + unreadline : fn(b : self ref Box); + slave : fn(b : self ref Box); + mopen : fn(b : self ref Box, n : int); + rewrite : fn(b : self ref Box); + mdel : fn(b : self ref Box, m : ref Mesg); + event : fn(b : self ref Box, e : ref Event); + + command : fn(b : self ref Box, s : string) : int; +}; + +Event : adt { + c1 : int; + c2 : int; + q0 : int; + q1 : int; + flag : int; + nb : int; + nr : int; + b : array of byte; + r : array of int; +}; + +Lock : adt { + cnt : int; + chann : chan of int; + + init : fn() : ref Lock; + lock : fn(l : self ref Lock); + unlock : fn(l : self ref Lock); +}; + +Ref : adt { + l : ref Lock; + cnt : int; + + init : fn() : ref Ref; + inc : fn(r : self ref Ref) : int; +}; + +mbox : ref Box; +mboxfile : string; +usermboxfile : string; +usermboxdir : string; +lockfile : string; +user : string; +date : string; +mailctxt : ref Context; +stdout, stderr : ref FD; + +killing : int = 0; + +init(ctxt : ref Context, argl : list of string) +{ + mailctxt = ctxt; + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + daytime = load Daytime Daytime->PATH; + stdout = fildes(1); + stderr = fildes(2); + main(argl); +} + +dlock : ref Lock; +dfd : ref Sys->FD; + +debug(s : string) +{ + if (dfd == nil) { + dfd = sys->create("/usr/jrf/acme/debugmail", Sys->OWRITE, 8r600); + dlock = Lock.init(); + } + if (dfd == nil) + return; + dlock.lock(); + sys->fprint(dfd, "%s", s); + dlock.unlock(); +} + +postnote(t : int, pid : int, note : string) : int +{ + fd := open("#p/" + string pid + "/ctl", OWRITE); + if (fd == nil) + return -1; + if (t == PNGROUP) + note += "grp"; + fprint(fd, "%s", note); + fd = nil; + return 0; +} + +exec(cmd : string, argl : list of string) +{ + file := cmd; + if(len file<4 || file[len file-4:]!=".dis") + file += ".dis"; + + c := load Command file; + if(c == nil) { + err := sprint("%r"); + if(file[0]!='/' && file[0:2]!="./"){ + c = load Command "/dis/"+file; + if(c == nil) + err = sprint("%r"); + } + if(c == nil){ + fprint(stderr, "%s: %s\n", cmd, err); + return; + } + } + c->init(mailctxt, argl); +} + +swrite(fd : ref FD, s : string) : int +{ + ab := array of byte s; + m := len ab; + p := write(fd, ab, m); + if (p == m) + return len s; + if (p <= 0) + return p; + return 0; +} + +strchr(s : string, c : int) : int +{ + for (i := 0; i < len s; i++) + if (s[i] == c) + return i; + return -1; +} + +strrchr(s : string, c : int) : int +{ + for (i := len s - 1; i >= 0; i--) + if (s[i] == c) + return i; + return -1; +} + +strtoi(s : string) : (int, int) +{ + m := 0; + neg := 0; + t := 0; + ls := len s; + while (t < ls && (s[t] == ' ' || s[t] == '\t')) + t++; + if (t < ls && s[t] == '+') + t++; + else if (t < ls && s[t] == '-') { + neg = 1; + t++; + } + while (t < ls && (s[t] >= '0' && s[t] <= '9')) { + m = 10*m + s[t]-'0'; + t++; + } + if (neg) + m = -m; + return (m, t); +} + +access(s : string) : int +{ + fd := open(s, 0); + if (fd == nil) + return -1; + fd = nil; + return 0; +} + +newevent() : ref Event +{ + e := ref Event; + e.b = array[EVENTSIZE*UTFmax+1] of byte; + e.r = array[EVENTSIZE+1] of int; + return e; +} + +newmesg() : ref Mesg +{ + m := ref Mesg; + m.id = m.lline1 = m.isopen = m.posted = 0; + return m; +} + +lc, uc : chan of ref Lock; + +initlock() +{ + lc = chan of ref Lock; + uc = chan of ref Lock; + spawn lockmgr(); +} + +lockmgr() +{ + l : ref Lock; + + for (;;) { + alt { + l = <- lc => + if (l.cnt++ == 0) + l.chann <-= 1; + l = <- uc => + if (--l.cnt > 0) + l.chann <-= 1; + } + } +} + +Lock.init() : ref Lock +{ + return ref Lock(0, chan of int); +} + +Lock.lock(l : self ref Lock) +{ + lc <-= l; + <- l.chann; +} + +Lock.unlock(l : self ref Lock) +{ + uc <-= l; +} + +Ref.init() : ref Ref +{ + r := ref Ref; + r.l = Lock.init(); + r.cnt = 0; + return r; +} + +Ref.inc(r : self ref Ref) : int +{ + r.l.lock(); + i := r.cnt; + r.cnt++; + r.l.unlock(); + return i; +} + +error(s : string) +{ + if(s != nil) + fprint(stderr, "mail: %s\n", s); + rmlockfile(); +# debug(sprint("error %s\n", s)); + postnote(PNGROUP, pctl(0, nil), "kill"); + killing = 1; + exit; +} + +rmlockfile() +{ + if (lockfile != nil) { + remove(lockfile); + lockfile = nil; + } +} + +# +# try opening a lock file. If it doesn't exist try creating it. +# +openlockfile(path : string) : ref FD +{ + try : int; + fd : ref FD; + + try = 0; + for(;;){ + # fd = open(path, OWRITE); + # if(fd!=nil || ++try>3) + # return fd; + if(++try > 3) + return fd; + (ok, d) := stat(path); + if(ok >= 0) + sleep(1000); + else{ + fd = create(path, OWRITE, CHEXCL|8r666); + if(fd != nil){ + (ok, d) = fstat(fd); + if(ok >= 0){ + d.mode |= CHEXCL|8r666; + fwstat(fd, d); + } + return fd; + } + break; + } + } + return nil; +} + +tryopen(s : string, mode : int) : ref FD +{ + fd : ref FD; + try : int; + + for(try=0; try<3; try++){ + fd = open(s, mode); + if(fd != nil) + return fd; + sleep(1000); + } + return nil; +} + +run(argv : list of string, c : chan of int, p0 : ref FD) +{ + # pctl(FORKFD|NEWPGRP, nil); # had RFMEM + pctl(FORKENV|NEWFD|NEWPGRP, 0::1::2::p0.fd::nil); + c <-= pctl(0, nil); + dup(p0.fd, 0); + p0 = nil; + exec(hd argv, argv); + exit; +} + +getuser() : string +{ + fd := open("/dev/user", OREAD); + if(fd == nil) + return ""; + buf := array[128] of byte; + n := read(fd, buf, len buf); + if(n < 0) + return ""; + return string buf[0:n]; +} + +main(argv : list of string) +{ + fd : ref FD; + readonly : int; + buf : string; + + initlock(); + initreply(); + date = time(); + if(date==nil) + error("can't get current time"); + user = getuser(); + if(user == nil) + user = "Wile.E.Coyote"; + usermboxdir = MAILDIR + "/box/" + user + "/"; + usermboxfile = MAILDIR + "/box/" + user + "/mbox"; + if(len argv > 1) + mboxfile = hd tl argv; + else + mboxfile = usermboxfile; + + fd = nil; + readonly = False; + if(mboxfile == usermboxfile){ + buf = MAILDIR + "/box/" + user + "/L.reading"; + fd = openlockfile(buf); + if(fd == nil){ + fprint(stderr, "Mail: %s in use; opened read-only\n", mboxfile); + readonly = True; + } + else + lockfile = buf; + } + mbox = mbox.read(mboxfile, readonly); + spawn timeslave(fd, mbox, mbox.cmore); + mbox.slave(); + error(nil); +} + +timeslave(rlock : ref FD, b : ref Box, c : chan of int) +{ + buf := array[DIRLEN] of byte; + for(;;){ + sleep(30*1000); + if(rlock != nil && write(rlock, buf, 0)<0) + error("can't maintain L.reading: %r"); + (ok, d) := stat(mboxfile); + if (ok >= 0 && d.length > b.leng) + c <-= 0; + } +} + +Win.wnew() : ref Win +{ + w := ref Win; + buf := array[12] of byte; + w.ctl = open("/chan/new/ctl", ORDWR); + if(w.ctl==nil || read(w.ctl, buf, 12)!=12) + error("can't open window ctl file: %r"); + w.ctlwrite("noscroll\n"); + w.winid = int string buf; + w.event = w.openfile("event"); + w.addr = nil; # will be opened when needed + w.body = nil; + w.data = nil; + w.bufp = w.nbuf = 0; + w.buf = array[512] of byte; + return w; +} + +Win.openfile(w : self ref Win, f : string) : ref FD +{ + buf := sprint("/chan/%d/%s", w.winid, f); + fd := open(buf, ORDWR|OCEXEC); + if(fd == nil) + error(sprint("can't open window %s file: %r", f)); + return fd; +} + +Win.openbody(w : self ref Win, mode : int) +{ + buf := sprint("/chan/%d/body", w.winid); + w.body = bufio->open(buf, mode|OCEXEC); + if(w.body == nil) + error("can't open window body file: %r"); +} + +Win.wwritebody(w : self ref Win, s : string) +{ + n := len s; + if(w.body == nil) + w.openbody(OWRITE); + if(w.body.puts(s) != n) + error("write error to window: %r"); +} + +Win.wreplace(w : self ref Win, addr : string, repl : string) +{ + if(w.addr == nil) + w.addr = w.openfile("addr"); + if(w.data == nil) + w.data = w.openfile("data"); + if(swrite(w.addr, addr) < 0){ + fprint(stderr, "mail: warning: bad address %s:%r\n", addr); + return; + } + if(swrite(w.data, repl) != len repl) + error("writing data: %r"); +} + +nrunes(s : array of byte, nb : int) : int +{ + i, n : int; + + n = 0; + for(i=0; iq1){ + do; while(n>0 && (int b[--n]&16rC0)==16r80); + --nr; + } + if(n == 0) + break; + s += string b[0:n]; + m += nr; + } + return s; +} + +Win.wshow(w : self ref Win) +{ + w.ctlwrite("show\n"); +} + +Win.wsetdump(w : self ref Win, dir : string, cmd : string) +{ + t : string; + + if(dir != nil){ + t = "dumpdir " + dir + "\n"; + w.ctlwrite(t); + t = nil; + } + if(cmd != nil){ + t = "dump " + cmd + "\n"; + w.ctlwrite(t); + t = nil; + } +} + +Win.wselect(w : self ref Win, addr : string) +{ + if(w.addr == nil) + w.addr = w.openfile("addr"); + if(swrite(w.addr, addr) < 0) + error("writing addr"); + w.ctlwrite("dot=addr\n"); +} + +Win.wtagwrite(w : self ref Win, s : string) +{ + fd : ref FD; + + fd = w.openfile("tag"); + if(swrite(fd, s) != len s) + error("tag write: %r"); + fd = nil; +} + +Win.ctlwrite(w : self ref Win, s : string) +{ + if(swrite(w.ctl, s) != len s) + error("write error to ctl file: %r"); +} + +Win.wdel(w : self ref Win, sure : int) : int +{ + if (w == nil) + return False; + if(sure) + swrite(w.ctl, "delete\n"); + else if(swrite(w.ctl, "del\n") != 4) + return False; + w.wdormant(); + w.ctl = nil; + w.event = nil; + return True; +} + +Win.wname(w : self ref Win, s : string) +{ + w.ctlwrite("name " + s + "\n"); +} + +Win.wclean(w : self ref Win) +{ + if(w.body != nil) + w.body.flush(); + w.ctlwrite("clean\n"); +} + +Win.wdormant(w : self ref Win) +{ + w.addr = nil; + if(w.body != nil){ + w.body.close(); + w.body = nil; + } + w.data = nil; +} + +Win.getec(w : self ref Win) : int +{ + if(w.nbuf == 0){ + w.nbuf = read(w.event, w.buf, len w.buf); + if(w.nbuf <= 0 && !killing) { + error("event read error: %r"); + } + w.bufp = 0; + } + w.nbuf--; + return int w.buf[w.bufp++]; +} + +Win.geten(w : self ref Win) : int +{ + n, c : int; + + n = 0; + while('0'<=(c=w.getec()) && c<='9') + n = n*10+(c-'0'); + if(c != ' ') + error("event number syntax"); + return n; +} + +Win.geter(w : self ref Win, buf : array of byte) : (int, int) +{ + r, m, n, ok : int; + + r = w.getec(); + buf[0] = byte r; + n = 1; + if(r >= Runeself) { + for (;;) { + (r, m, ok) = byte2char(buf[0:n], 0); + if (m > 0) + return (r, n); + buf[n++] = byte w.getec(); + } + } + return (r, n); +} + +Win.wevent(w : self ref Win, e : ref Event) +{ + i, nb : int; + + e.c1 = w.getec(); + e.c2 = w.getec(); + e.q0 = w.geten(); + e.q1 = w.geten(); + e.flag = w.geten(); + e.nr = w.geten(); + if(e.nr > EVENTSIZE) + error("event string too long"); + e.nb = 0; + for(i=0; i Hdrs ( "", 0 ), +}; + +StRnCmP(s : string, t : string, n : int) : int +{ + c, d, i, j : int; + + i = j = 0; + if (len s < n || len t < n) + return -1; + while(n > 0){ + c = s[i++]; + d = t[j++]; + --n; + if(c != d){ + if('a'<=c && c<='z') + c -= 'a'-'A'; + if('a'<=d && d<='z') + d -= 'a'-'A'; + if(c != d) + return c-d; + } + } + return 0; +} + +ignore() +{ + b : ref Iobuf; + s : string; + i : int; + + ignored = True; + b = bufio->open(MAILDIR + "/lib/ignore", OREAD); + if(b == nil) + return; + for(i=0; hdrs[i].name != nil; i++) + ; + while((s = b.gets('\n')) != nil){ + s = s[0:len s - 1]; + hdrs[i].name = s; + hdrs[i].typex = Ignore; + if(++i >= NHeaders){ + fprint(stderr, "%s/lib/ignore has more than %d headers\n", MAILDIR, NHeaders); + break; + } + } + b.close(); +} + +readhdr(b : ref Box) : (string, int) +{ + i, j, n, m, typex : int; + s, t : string; + + { + if(!ignored) + ignore(); + s = b.readline(); + n = len s; + if(n <= 0) + raise("e"); + for(i=0; i0 && j == ':') + break; + if(j<'!' || '~' + return (nil, None); + } +} + +Mesg.read(b : ref Box) : ref Mesg +{ + m : ref Mesg; + s : string; + n, typex : int; + + s = b.readline(); + n = len s; + if(n <= 0) + return nil; + +{ + if(n < 5 || s[0:5] !="From ") + raise("e"); + m = newmesg(); + m.realhdr = s; + # toss 'From ' + s = s[5:]; + n -= 5; + # toss spaces/tabs + while (n > 0 && (s[0] == ' ' || s[0] == '\t')) { + s = s[1:]; + n--; + } + m.hdr = s; + # convert first blank to tab + s0 := strchr(m.hdr, ' '); + if(s0 >= 0){ + m.hdr[s0] = '\t'; + # drop trailing seconds, time zone, and year if match local year + t := n-6; + if(t <= 0) + raise("e"); + if(m.hdr[t:n-1] == date[23:]){ + m.hdr = m.hdr[0:t] + "\n"; # drop year for sure + t = -1; + s1 := strchr(m.hdr[s0:], ':'); + if(s1 >= 0) + t = strchr(m.hdr[s0+s1+1:], ':'); + if(t >= 0) # drop seconds and time zone + m.hdr = m.hdr[0:s0+s1+t+1] + "\n"; + else{ # drop time zone + t = strchr(m.hdr[s0+s1+1:], ' '); + if(t >= 0) + m.hdr = m.hdr[0:s0+s1+t+1] + "\n"; + } + n = len m.hdr; + } + } + m.lline1 = n; + m.text = nil; + # read header +loop: + for(;;){ + (s, typex) = readhdr(b); + case(typex){ + None => + break loop; + ReplyTo => + m.replyto = s[9:]; + break; + From => + if(m.replyto == nil) + m.replyto = s[5:]; + break; + Subject => + m.subj = s[8:]; + break; + Re => + m.subj = s[3:]; + break; + Date => + break; + } + m.realhdr += s; + if(typex != Ignore) + m.hdr += s; + } + # read body + for(;;){ + s = b.readline(); + n = len s; + if(n <= 0) + break; + if(len s >= 5 && s[0:5] == "From "){ + b.unreadline(); + break; + } + m.text += s; + } + # remove trailing "morF\n" + l := len m.text; + if(l>6 && m.text[l-6:] == "\nmorF\n") + m.text = m.text[0:l-5]; + m.box = b; + return m; +} +exception{ + "*" => + error("malformed header " + s); + return nil; +} +} + +Mesg.mkmail(b : ref Box, hdr : string) +{ + r : ref Mesg; + + r = newmesg(); + r.hdr = hdr + "\n"; + r.lline1 = len r.hdr; + r.text = nil; + r.box = b; + r.open(); + r.w.wdormant(); +} + +replyaddr(r : string) : string +{ + p, q, rr : int; + + rr = 0; + while(r[rr]==' ' || r[rr]=='\t') + rr++; + r = r[rr:]; + p = strchr(r, '<'); + if(p >= 0){ + q = strchr(r[p+1:], '>'); + if(q < 0) + r = r[p+1:]; + else + r = r[p+1:p+q] + "\n"; + return r; + } + p = strchr(r, '('); + if(p >= 0){ + q = strchr(r[p:], ')'); + if(q < 0) + r = r[0:p]; + else + r = r[0:p] + r[p+q+1:]; + } + return r; +} + +Mesg.mkreply(m : self ref Mesg) +{ + r : ref Mesg; + + r = newmesg(); + if(m.replyto != nil){ + r.hdr = replyaddr(m.replyto); + r.lline1 = len r.hdr; + }else{ + r.hdr = m.hdr[0:m.lline1]; + r.lline1 = m.lline1; # was len m.hdr; + } + if(m.subj != nil){ + if(StRnCmP(m.subj, "re:", 3)==0 || StRnCmP(m.subj, " re:", 4)==0) + r.text = "Subject:" + m.subj + "\n"; + else + r.text = "Subject: Re:" + m.subj + "\n"; + } + else + r.text = nil; + r.box = m.box; + r.open(); + r.w.wselect("$"); + r.w.wdormant(); +} + +Mesg.free(m : self ref Mesg) +{ + m.text = nil; + m.hdr = nil; + m.subj = nil; + m.realhdr = nil; + m.replyto = nil; + m = nil; +} + +replyid : ref Ref; + +initreply() +{ + replyid = Ref.init(); +} + +Mesg.open(m : self ref Mesg) +{ + buf: string; + + if(m.isopen) + return; + m.w = Win.wnew(); + if(m.id != 0) + m.w.wwritebody("From "); + m.w.wwritebody(m.hdr); + m.w.wwritebody(m.text); + if(m.id){ + buf = sprint("%s/%d", m.box.file , m.id); + m.w.wtagwrite("Reply Delmesg Save"); + }else{ + buf = sprint("%s/Reply%d", m.box.file, replyid.inc()); + m.w.wtagwrite("Post"); + } + m.w.wname(buf); + m.w.wclean(); + m.w.wselect("0"); + m.isopen = True; + m.posted = False; + spawn m.slave(); +} + +Mesg.putpost(m : self ref Mesg, e : ref Event) +{ + if(m.posted || m.id==0) + return; + if(e.q0 >= len m.hdr+5) # include "From " + return; + m.w.wtagwrite(" Post"); + m.posted = True; + return; +} + +Mesg.slave(m : self ref Mesg) +{ + e, e2, ea, etoss, eq : ref Event; + s : string; + na : int; + + e = newevent(); + e2 = newevent(); + ea = newevent(); + etoss = newevent(); + for(;;){ + m.w.wevent(e); + case(e.c1){ + 'E' => # write to body; can't affect us + break; + 'F' => # generated by our actions; ignore + break; + 'K' or 'M' => # type away; we don't care + case(e.c2){ + 'x' or 'X' => # mouse only + eq = e; + if(e.flag & 2){ + m.w.wevent(e2); + eq = e2; + } + if(e.flag & 8){ + m.w.wevent(ea); + m.w.wevent(etoss); + na = ea.nb; + }else + na = 0; + if(eq.q1>eq.q0 && eq.nb==0) + s = m.w.wread(eq.q0, eq.q1); + else + s = string eq.b[0:eq.nb]; + if(na) + s = s + " " + string ea.b[0:ea.nb]; + if(!m.command(s)) # send it back + m.w.wwriteevent(e); + s = nil; + break; + 'l' or 'L' => # mouse only + if(e.flag & 2) + m.w.wevent(e2); + # just send it back + m.w.wwriteevent(e); + break; + 'I' or 'D' => # modify away; we don't care + m.putpost(e); + break; + 'd' or 'i' => + break; + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } + } +} + +Mesg.command(m : self ref Mesg, s : string) : int +{ + while(s[0]==' ' || s[0]=='\t' || s[0]=='\n') + s = s[1:]; + if(s == "Post"){ + m.send(); + return True; + } + if(len s >= 4 && s[0:4] == "Save"){ + s = s[4:]; + while(s[0]==' ' || s[0]=='\t' || s[0]=='\n') + s = s[1:]; + if(s == nil) + m.save("stored"); + else{ + ss := 0; + while(ss < len s && s[ss]!=' ' && s[ss]!='\t' && s[ss]!='\n') + ss++; + m.save(s[0:ss]); + } + return True; + } + if(s == "Reply"){ + m.mkreply(); + return True; + } + if(s == "Del"){ + if(m.w.wdel(False)){ + m.isopen = False; + exit; + } + return True; + } + if(s == "Delmesg"){ + if(m.w.wdel(False)){ + m.isopen = False; + m.box.cdel <-= m; + exit; + } + return True; + } + return False; +} + +Mesg.save(m : self ref Mesg, base : string) +{ + s, buf : string; + n : int; + fd : ref FD; + b : ref Iobuf; + + if(m.id <= 0){ + fprint(stderr, "can't save reply message; mail it to yourself\n"); + return; + } + buf = nil; + if(strchr(base, '/') >= 0) + s = base; + else + s = usermboxdir + base; + { + if(access(s) < 0) + raise("e"); + fd = tryopen(s, OWRITE); + if(fd == nil) + raise("e"); + buf = nil; + b = bufio->fopen(fd, OWRITE); + # seek to end in case file isn't append-only + b.seek(big 0, 2); + # use edited headers: first line of real header followed by remainder of selected ones + for(n=0; n + buf = nil; + fprint(stderr, "mail: can't open %s: %r\n", base); + return; + } +} + +Mesg.send(m : self ref Mesg) +{ + s, buf : string; + t, u : int; + a, b : list of string; + n : int; + p : array of ref FD; + c : chan of int; + + p = array[2] of ref FD; + s = m.w.wreadall(); + a = "sendmail" :: nil; + if(len s >= 5 && s[0:5] == "From ") + s = s[5:]; + for(t=0; t < len s && s[t]!='\n' && s[t]!='\t';){ + while(t < len s && (s[t]==' ' || s[t]==',')) + t++; + u = t; + while(t < len s && s[t]!=' ' && s[t]!=',' && s[t]!='\t' && s[t]!='\n') + t++; + if(t == u) + break; + a = s[u:t] :: a; + } + b = nil; + for ( ; a != nil; a = tl a) + b = hd a :: b; + a = b; + while(t < len s && s[t]!='\n') + t++; + if(s[t] == '\n') + t++; + if(pipe(p) < 0) + error("can't pipe: %r"); + c = chan of int; + spawn run(a, c, p[0]); + <-c; + c = nil; + p[0] = nil; + n = len s - t; + if(swrite(p[1], s[t:]) != n) + fprint(stderr, "write to pipe failed: %r\n"); + p[1] = nil; + # run() frees the arg list + buf = sprint("%s/%d-R", m.box.file, m.id); + m.w.wname(buf); + m.w.wclean(); +} + +Box.read(f : string, readonly : int) : ref Box +{ + b : ref Box; + m : ref Mesg; + s, buf : string; + + b = ref Box; + b.nm = 0; + b.readonly = readonly; + b.file = f; + b.io = bufio->open(f, OREAD|OCEXEC); + if(b.io == nil) + error(sprint("can't open %s: %r", f)); + b.w = Win.wnew(); + if (!readonly) + spawn lockfilemon(b.w.winid); + while((m = m.read(b)) != nil){ + m.next = b.m; + b.m = m; + b.nm++; + m.id = b.nm; + } + b.leng = b.io.offset(); + b.io.close(); + b.io = nil; + # b.w = Win.wnew(); + for(m=b.m; m != nil; m=m.next){ + if(m.subj != nil) + buf = sprint("%d\t%s\t %s", m.id, m.hdr[0:m.lline1], m.subj); + else + buf = sprint("%d\t%s", m.id, m.hdr[0:m.lline1]); + b.w.wwritebody(buf); + } + buf = sprint("Mail/%s/", s); + b.w.wname(f); + if(b.readonly) + b.w.wtagwrite("Mail"); + else + b.w.wtagwrite("Put Mail"); + buf = "Mail " + f; + b.w.wsetdump("/acme/mail", buf); + b.w.wclean(); + b.w.wselect("0"); + b.w.wdormant(); + b.cdel= chan of ref Mesg; + b.cevent = chan of Event; + b.cmore = chan of int; + spawn b.w.wslave(b.cevent); + b.clean = True; + return b; +} + +Box.readmore(b : self ref Box) +{ + m : ref Mesg; + new : int; + buf : string; + doclose : int; + + doclose = False; + if(b.io == nil){ + b.io = bufio->open(b.file, OREAD|OCEXEC); + if(b.io == nil) + error(sprint("can't open %s: %r", b.file)); + b.io.seek(b.leng, 0); + doclose = True; + } + new = False; + while((m = m.read(b)) != nil){ + m.next = b.m; + b.m = m; + b.nm++; + m.id = b.nm; + if(m.subj != nil) + buf = sprint("%d\t%s\t %s", m.id, m.hdr[0:m.lline1], m.subj); + else + buf = sprint("%d\t%s", m.id, m.hdr[0:m.lline1]); + b.w.wreplace("0", buf); + new = True; + } + b.leng = b.io.offset(); + if(doclose){ + b.io.close(); + b.io = nil; + } + if(new){ + if(b.clean) + b.w.wclean(); + b.w.wselect("0;/.*(\\n[ \t].*)*"); + b.w.wshow(); + } + b.w.wdormant(); +} + +Box.readline(b : self ref Box) : string +{ + for (;;) { + if(b.peekline != nil){ + b.line = b.peekline; + b.peekline = nil; + }else + b.line = b.io.gets('\n'); + # nulls appear in mailboxes! + if(b.line != nil && strchr(b.line, 0) >= 0) + ; + else + break; + } + return b.line; +} + +Box.unreadline(b : self ref Box) +{ + b.peekline = b.line; +} + +Box.slave(b : self ref Box) +{ + e : ref Event; + m : ref Mesg; + + e = newevent(); + for(;;){ + alt{ + *e = <-b.cevent => + b.event(e); + break; + <-b.cmore => + b.readmore(); + break; + m = <-b.cdel => + b.mdel(m); + break; + } + } +} + +Box.event(b : self ref Box, e : ref Event) +{ + e2, ea, eq : ref Event; + s : string; + t : int; + n, na, nopen : int; + + e2 = newevent(); + ea = newevent(); + case(e.c1){ + 'E' => # write to body; can't affect us + break; + 'F' => # generated by our actions; ignore + break; + 'K' => # type away; we don't care + break; + 'M' => + case(e.c2){ + 'x' or 'X' => + if(e.flag & 2) + *e2 = <-b.cevent; + if(e.flag & 8){ + *ea = <-b.cevent; + na = ea.nb; + <- b.cevent; + }else + na = 0; + s = string e.b[0:e.nb]; + # if it's a known command, do it + if((e.flag&2) && e.nb==0) + s = string e2.b[0:e2.nb]; + if(na) + s = sprint("%s %s", s, string ea.b[0:ea.nb]); + # if it's a long message, it can't be for us anyway + if(!b.command(s)) # send it back + b.w.wwriteevent(e); + if(na) + s = nil; + break; + 'l' or 'L' => + eq = e; + if(e.flag & 2){ + *e2 = <-b.cevent; + eq = e2; + } + s = string eq.b[0:eq.nb]; + if(eq.q1>eq.q0 && eq.nb==0) + s = b.w.wread(eq.q0, eq.q1); + nopen = 0; + do{ + t = 0; + (n, t) = strtoi(s); + if(n>0 && (t == len s || s[t]==' ' || s[t]=='\t' || s[t]=='\n')){ + b.mopen(n); + nopen++; + s = s[t:]; + } + while(s != nil && s[0]!='\n') + s = s[1:]; + }while(s != nil); + if(nopen == 0) # send it back + b.w.wwriteevent(e); + break; + 'I' or 'D' or 'd' or 'i' => # modify away; we don't care + break; + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } +} + +Box.mopen(b : self ref Box, id : int) +{ + m : ref Mesg; + + for(m=b.m; m != nil; m=m.next) + if(m.id == id){ + m.open(); + break; + } +} + +Box.mdel(b : self ref Box, dm : ref Mesg) +{ + prev, m : ref Mesg; + buf : string; + + if(dm.id){ + prev = nil; + for(m=b.m; m!=nil && m!=dm; m=m.next) + prev = m; + if(m == nil) + error(sprint("message %d not found", dm.id)); + if(prev == nil) + b.m = m.next; + else + prev.next = m.next; + # remove from screen: use acme to help + buf = sprint("/^%d .*\\n(^[ \t].*\\n)*/", m.id); + b.w.wreplace(buf, ""); + } + dm.free(); + b.clean = False; +} + +Box.command(b : self ref Box, s : string) : int +{ + t : int; + m : ref Mesg; + + while(s[0]==' ' || s[0]=='\t' || s[0]=='\n') + s = s[1:]; + if(len s >= 4 && s[0:4] == "Mail"){ + s = s[4:]; + while(s != nil && (s[0]==' ' || s[0]=='\t' || s[0]=='\n')) + s = s[1:]; + t = 0; + while(t < len s && s[t] && s[t]!=' ' && s[t]!='\t' && s[t]!='\n') + t++; + m = b.m; # avoid warning message on b.m.mkmail(...) + m.mkmail(b, s[0:t]); + return True; + } + if(s == "Del"){ + + if(!b.clean){ + b.clean = True; + fprint(stderr, "mail: mailbox not written\n"); + return True; + } + rmlockfile(); + postnote(PNGROUP, pctl(0, nil), "kill"); + killing = 1; + pctl(NEWPGRP, nil); + b.w.wdel(True); + for(m=b.m; m != nil; m=m.next) + m.w.wdel(False); + exit; + return True; + } + if(s == "Put"){ + if(b.readonly) + fprint(stderr, "Mail: %s is read-only\n", b.file); + else + b.rewrite(); + return True; + } + return False; +} + +Box.rewrite(b : self ref Box) +{ + mbox, Lmbox, mboxtmp : ref FD; + i, t, ok : int; + buf : string; + s : string; + m : ref Mesg; + d : Dir; + Lmboxs : string = nil; + + if(b.clean){ + b.w.wclean(); + return; + } + t = strrchr(b.file, '/'); + if(t >= 0) + s = b.file[t+1:]; + else + s = b.file; + if(mboxfile == usermboxfile){ + buf = sprint("%sL.%s", b.file[0:t+1], s); + Lmbox = openlockfile(buf); + if(Lmbox == nil) + error(sprint("can't open lock file %s: %r", buf)); + else + Lmboxs = buf; + }else + Lmbox = nil; + buf = sprint("%s.tmp", b.file); + mbox = tryopen(mboxfile, OREAD); + if(mbox != nil){ + b.io = bufio->fopen(mbox, OREAD); + b.io.seek(b.leng, 0); + b.readmore(); + }else if(access(buf)){ + fprint(stderr, "mail: mailbox missing; using %s\n", buf); + mboxtmp = tryopen(buf, ORDWR); + b.io = bufio->fopen(mboxtmp, OREAD); + b.readmore(); + b.io.close(); + }else + error(sprint("can't open %s to rewrite: %r", s)); + remove(buf); + mboxtmp = create(buf, OWRITE, 0622|CHAPPEND|CHEXCL); + if(mboxtmp == nil) + error(sprint("can't create %s: %r", buf)); + (ok, d) = fstat(mboxtmp); + if(ok < 0) + error(sprint("can't fstat %s: %r", buf)); + d.mode |= 0622; + if(fwstat(mboxtmp, d) < 0) + error(sprint("can't change mode of %s: %r", buf)); + b.io = bufio->fopen(mboxtmp, OWRITE); + # write it backwards: stupid code + for(i=1; i<=b.nm; i++){ + for(m=b.m; m!=nil && m.id!=i; m=m.next) + ; + if(m != nil){ + b.io.puts(m.realhdr); + b.io.puts(m.text); + } + } + if(remove(mboxfile) < 0) + error(sprint("can't unlink %s: %r", mboxfile)); + d.name = s; + if(fwstat(mboxtmp, d) < 0) + error(sprint("can't change name of %s: %r", buf)); + b.leng = b.io.offset(); + b.io.close(); + mboxtmp = nil; + b.io = nil; + Lmbox = nil; + if (Lmboxs != nil) + remove(Lmboxs); + b.w.wclean(); + b.clean = True; +} + +lockfilemon(id : int) +{ + pctl(NEWPGRP, nil); + p := "/chan/" + string id; + for (;;) { + if (lockfile == nil) + error(nil); + sleep(60*1000); + (ok, d) := stat(p); + if (ok < 0) + error(nil); + } +} diff --git a/appl/acme/acme/mail/src/Mailp.b b/appl/acme/acme/mail/src/Mailp.b new file mode 100644 index 00000000..f9bb757f --- /dev/null +++ b/appl/acme/acme/mail/src/Mailp.b @@ -0,0 +1,1684 @@ +implement mailpop3; + +include "sys.m"; +include "draw.m"; +include "bufio.m"; +include "daytime.m"; +include "sh.m"; +include "pop3.m"; + +mailpop3 : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +sys : Sys; +bufio : Bufio; +daytime : Daytime; +pop3 : Pop3; + +OREAD, OWRITE, ORDWR, NEWFD, FORKENV, FORKFD, NEWPGRP, UTFmax, EXCEPTION, ONCE : import Sys; +FD, Dir, Exception : import sys; +fprint, sprint, sleep, create, open, read, write, remove, stat, fstat, fwstat, fildes, pctl, pipe, dup, byte2char : import sys; +Context : import Draw; +EOF : import Bufio; +Iobuf : import bufio; +time : import daytime; + +DIRLEN : con 116; +PNPROC, PNGROUP : con iota; +False : con 0; +True : con 1; +EVENTSIZE : con 256; +Runeself : con 16r80; +OCEXEC : con 0; +CHEXCL : con 0; # 16r20000000; +CHAPPEND : con 0; # 16r40000000; + +Win : adt { + winid : int; + addr : ref FD; + body : ref Iobuf; + ctl : ref FD; + data : ref FD; + event : ref FD; + buf : array of byte; + bufp : int; + nbuf : int; + + wnew : fn() : ref Win; + wwritebody : fn(w : self ref Win, s : string); + wread : fn(w : self ref Win, m : int, n : int) : string; + wclean : fn(w : self ref Win); + wname : fn(w : self ref Win, s : string); + wdormant : fn(w : self ref Win); + wevent : fn(w : self ref Win, e : ref Event); + wshow : fn(w : self ref Win); + wtagwrite : fn(w : self ref Win, s : string); + wwriteevent : fn(w : self ref Win, e : ref Event); + wslave : fn(w : self ref Win, c : chan of Event); + wreplace : fn(w : self ref Win, s : string, t : string); + wselect : fn(w : self ref Win, s : string); + wsetdump : fn(w : self ref Win, s : string, t : string); + wdel : fn(w : self ref Win, n : int) : int; + wreadall : fn(w : self ref Win) : string; + + ctlwrite : fn(w : self ref Win, s : string); + getec : fn(w : self ref Win) : int; + geten : fn(w : self ref Win) : int; + geter : fn(w : self ref Win, s : array of byte) : (int, int); + openfile : fn(w : self ref Win, s : string) : ref FD; + openbody : fn(w : self ref Win, n : int); +}; + +Mesg : adt { + w : ref Win; + id : int; + popno : int; + hdr : string; + realhdr : string; + replyto : string; + text : string; + subj : string; + next : cyclic ref Mesg; + lline1 : int; + box : cyclic ref Box; + isopen : int; + posted : int; + deleted : int; + + read : fn(b : ref Box) : ref Mesg; + open : fn(m : self ref Mesg); + slave : fn(m : self ref Mesg); + free : fn(m : self ref Mesg); + save : fn(m : self ref Mesg, s : string); + mkreply : fn(m : self ref Mesg); + mkmail : fn(b : ref Box, s : string); + putpost : fn(m : self ref Mesg, e : ref Event); + + command : fn(m : self ref Mesg, s : string) : int; + send : fn(m : self ref Mesg); +}; + +Box : adt { + w : ref Win; + nm : int; + readonly : int; + m : cyclic ref Mesg; +# io : ref Iobuf; + clean : int; + leng : int; + cdel : chan of ref Mesg; + cevent : chan of Event; + cmore : chan of int; + lst : list of int; + s : string; + + line : string; + popno : int; + peekline : string; + + read : fn(n : int) : ref Box; + readmore : fn(b : self ref Box); + readline : fn(b : self ref Box) : string; + unreadline : fn(b : self ref Box); + slave : fn(b : self ref Box); + mopen : fn(b : self ref Box, n : int); + rewrite : fn(b : self ref Box); + mdel : fn(b : self ref Box, m : ref Mesg); + event : fn(b : self ref Box, e : ref Event); + + command : fn(b : self ref Box, s : string) : int; +}; + +Event : adt { + c1 : int; + c2 : int; + q0 : int; + q1 : int; + flag : int; + nb : int; + nr : int; + b : array of byte; + r : array of int; +}; + +Lock : adt { + cnt : int; + chann : chan of int; + + init : fn() : ref Lock; + lock : fn(l : self ref Lock); + unlock : fn(l : self ref Lock); +}; + +Ref : adt { + l : ref Lock; + cnt : int; + + init : fn() : ref Ref; + inc : fn(r : self ref Ref) : int; +}; + +mbox : ref Box; +user : string; +date : string; +mailctxt : ref Context; +stdout, stderr : ref FD; + +killing : int = 0; + +init(ctxt : ref Context, argl : list of string) +{ + mailctxt = ctxt; + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + daytime = load Daytime Daytime->PATH; + pop3 = load Pop3 Pop3->PATH; + stdout = fildes(1); + stderr = fildes(2); + main(); +} + +dlock : ref Lock; +dfd : ref Sys->FD; + +debug(s : string) +{ + if (dfd == nil) { + dfd = sys->create("/usr/jrf/acme/debugmail", Sys->OWRITE, 8r600); + dlock = Lock.init(); + } + if (dfd == nil) + return; + dlock.lock(); + sys->fprint(dfd, "%s", s); + dlock.unlock(); +} + +postnote(t : int, pid : int, note : string) : int +{ + fd := open("#p/" + string pid + "/ctl", OWRITE); + if (fd == nil) + return -1; + if (t == PNGROUP) + note += "grp"; + fprint(fd, "%s", note); + fd = nil; + return 0; +} + +exec(cmd : string, argl : list of string) +{ + file := cmd; + if(len file<4 || file[len file-4:]!=".dis") + file += ".dis"; + + c := load Command file; + if(c == nil) { + err := sprint("%r"); + if(file[0]!='/' && file[0:2]!="./"){ + c = load Command "/dis/"+file; + if(c == nil) + err = sprint("%r"); + } + if(c == nil){ + fprint(stderr, "%s: %s\n", cmd, err); + return; + } + } + c->init(mailctxt, argl); +} + +swrite(fd : ref FD, s : string) : int +{ + ab := array of byte s; + m := len ab; + p := write(fd, ab, m); + if (p == m) + return len s; + if (p <= 0) + return p; + return 0; +} + +strchr(s : string, c : int) : int +{ + for (i := 0; i < len s; i++) + if (s[i] == c) + return i; + return -1; +} + +strrchr(s : string, c : int) : int +{ + for (i := len s - 1; i >= 0; i--) + if (s[i] == c) + return i; + return -1; +} + +strtoi(s : string) : (int, int) +{ + m := 0; + neg := 0; + t := 0; + ls := len s; + while (t < ls && (s[t] == ' ' || s[t] == '\t')) + t++; + if (t < ls && s[t] == '+') + t++; + else if (t < ls && s[t] == '-') { + neg = 1; + t++; + } + while (t < ls && (s[t] >= '0' && s[t] <= '9')) { + m = 10*m + s[t]-'0'; + t++; + } + if (neg) + m = -m; + return (m, t); +} + +access(s : string) : int +{ + fd := open(s, 0); + if (fd == nil) + return -1; + fd = nil; + return 0; +} + +newevent() : ref Event +{ + e := ref Event; + e.b = array[EVENTSIZE*UTFmax+1] of byte; + e.r = array[EVENTSIZE+1] of int; + return e; +} + +newmesg() : ref Mesg +{ + m := ref Mesg; + m.id = m.lline1 = m.isopen = m.posted = m.deleted = 0; + return m; +} + +lc, uc : chan of ref Lock; + +initlock() +{ + lc = chan of ref Lock; + uc = chan of ref Lock; + spawn lockmgr(); +} + +lockmgr() +{ + l : ref Lock; + + for (;;) { + alt { + l = <- lc => + if (l.cnt++ == 0) + l.chann <-= 1; + l = <- uc => + if (--l.cnt > 0) + l.chann <-= 1; + } + } +} + +Lock.init() : ref Lock +{ + return ref Lock(0, chan of int); +} + +Lock.lock(l : self ref Lock) +{ + lc <-= l; + <- l.chann; +} + +Lock.unlock(l : self ref Lock) +{ + uc <-= l; +} + +Ref.init() : ref Ref +{ + r := ref Ref; + r.l = Lock.init(); + r.cnt = 0; + return r; +} + +Ref.inc(r : self ref Ref) : int +{ + r.l.lock(); + i := r.cnt; + r.cnt++; + r.l.unlock(); + return i; +} + +error(s : string) +{ + if(s != nil) + fprint(stderr, "mail: %s\n", s); + postnote(PNGROUP, pctl(0, nil), "kill"); + killing = 1; + exit; +} + +tryopen(s : string, mode : int) : ref FD +{ + fd : ref FD; + try : int; + + for(try=0; try<3; try++){ + fd = open(s, mode); + if(fd != nil) + return fd; + sleep(1000); + } + return nil; +} + +run(argv : list of string, c : chan of int, p0 : ref FD) +{ + # pctl(FORKFD|NEWPGRP, nil); # had RFMEM + pctl(FORKENV|NEWFD|NEWPGRP, 0::1::2::p0.fd::nil); + c <-= pctl(0, nil); + dup(p0.fd, 0); + p0 = nil; + exec(hd argv, argv); + exit; +} + +getuser() : string +{ + fd := open("/dev/user", OREAD); + if(fd == nil) + return ""; + buf := array[128] of byte; + n := read(fd, buf, len buf); + if(n < 0) + return ""; + return string buf[0:n]; +} + +pop3conn : int = 0; +pop3bad : int = 0; +pop3lock : ref Lock; + +pop3open() +{ + pop3lock.lock(); + if (!pop3conn) { + (ok, s) := pop3->open(user, "********", nil); # password now got from user in Mailpop3.b + if (ok < 0) { + if (!pop3bad) { + fprint(stderr, "mail: could not connect to POP3 mail server : %s\n", s); + pop3bad = 1; + } + return; + } + } + pop3conn = 1; + pop3bad = 0; +} + +pop3close() +{ + if (pop3conn) { + (ok, s) := pop3->close(); + if (ok < 0) { + fprint(stderr, "mail: could not close POP3 connection : %s\n", s); + pop3lock.unlock(); + return; + } + } + pop3conn = 0; + pop3lock.unlock(); +} + +pop3stat(b : ref Box) : int +{ + (ok, s, nm, nil) := pop3->stat(); + if (ok < 0 && pop3conn) { + fprint(stderr, "mail: could not stat POP3 server : %s\n", s); + return b.leng; + } + return nm; +} + +pop3list() : list of int +{ + (ok, s, l) := pop3->msgnolist(); + if (ok < 0 && pop3conn) { + fprint(stderr, "mail: could not get list from POP3 server : %s\n", s); + return nil; + } + return l; +} + +pop3mesg(mno : int) : string +{ + (ok, s, msg) := pop3->get(mno); + if (ok < 0 && pop3conn) { + fprint(stderr, "mail: could not retrieve a message from server : %s\n", s); + return "Acme Mail : FAILED TO RETRIEVE MESSAGE\n"; + } + return msg; +} + +pop3del(mno : int) : int +{ + (ok, s) := pop3->delete(mno); + if (ok < 0) + fprint(stderr, "mail: could not delete message : %s\n", s); + return ok; +} + +pop3init(b : ref Box) +{ + b.leng = pop3stat(b); + b.lst = pop3list(); + b.s = nil; + b.popno = 0; +} + +pop3more(b : ref Box) +{ + nl : list of int; + + leng := b.leng; + b.leng = pop3stat(b); + b.lst = pop3list(); + b.s = nil; + b.popno = 0; + if (len b.lst != b.leng || b.leng <= leng) + error("bad lengths in pop3more()"); + # is this ok ? + nl = nil; + for (i := 0; i < leng; i++) { + nl = hd b.lst :: nl; + b.lst = tl b.lst; + } + # now update pop nos. + for (m := b.m; m != nil; m = m.next) { + # opopno := m.popno; + if (nl == nil) + error("message list too big"); + m.popno = hd nl; + nl = tl nl; + # debug(sys->sprint("%d : popno from %d to %d\n", m.id, opopno, m.popno)); + } + if (nl != nil) + error("message list too small"); +} + +pop3next(b : ref Box) : string +{ + mno : int = 0; + r : string; + + if (b.s == nil) { + if (b.lst == nil) + return nil; # end of box + first := b.popno == 0; + mno = hd b.lst; + b.lst = tl b.lst; + b.s = pop3mesg(mno); + b.popno = mno; + if (!first) + return nil; # end of message + } + t := strchr(b.s, '\n'); + if (t >= 0) { + r = b.s[0:t+1]; + b.s = b.s[t+1:]; + } + else { + r = b.s; + b.s = nil; + } + return r; +} + +main() +{ + readonly : int; + + initlock(); + initreply(); + date = time(); + if(date==nil) + error("can't get current time"); + user = getuser(); + if(user == nil) + user = "Wile.E.Coyote"; + readonly = False; + pop3lock = Lock.init(); + mbox = mbox.read(readonly); + spawn timeslave(mbox, mbox.cmore); + mbox.slave(); + error(nil); +} + +timeslave(b : ref Box, c : chan of int) +{ + for(;;){ + sleep(30*1000); + pop3open(); + leng := pop3stat(b); + pop3close(); + if (leng > b.leng) + c <-= 0; + } +} + +Win.wnew() : ref Win +{ + w := ref Win; + buf := array[12] of byte; + w.ctl = open("/chan/new/ctl", ORDWR); + if(w.ctl==nil || read(w.ctl, buf, 12)!=12) + error("can't open window ctl file: %r"); + w.ctlwrite("noscroll\n"); + w.winid = int string buf; + w.event = w.openfile("event"); + w.addr = nil; # will be opened when needed + w.body = nil; + w.data = nil; + w.bufp = w.nbuf = 0; + w.buf = array[512] of byte; + return w; +} + +Win.openfile(w : self ref Win, f : string) : ref FD +{ + buf := sprint("/chan/%d/%s", w.winid, f); + fd := open(buf, ORDWR|OCEXEC); + if(fd == nil) + error(sprint("can't open window %s file: %r", f)); + return fd; +} + +Win.openbody(w : self ref Win, mode : int) +{ + buf := sprint("/chan/%d/body", w.winid); + w.body = bufio->open(buf, mode|OCEXEC); + if(w.body == nil) + error("can't open window body file: %r"); +} + +Win.wwritebody(w : self ref Win, s : string) +{ + n := len s; + if(w.body == nil) + w.openbody(OWRITE); + if(w.body.puts(s) != n) + error("write error to window: %r"); +} + +Win.wreplace(w : self ref Win, addr : string, repl : string) +{ + if(w.addr == nil) + w.addr = w.openfile("addr"); + if(w.data == nil) + w.data = w.openfile("data"); + if(swrite(w.addr, addr) < 0){ + fprint(stderr, "mail: warning: bad address %s:%r\n", addr); + return; + } + if(swrite(w.data, repl) != len repl) + error("writing data: %r"); +} + +nrunes(s : array of byte, nb : int) : int +{ + i, n : int; + + n = 0; + for(i=0; iq1){ + do; while(n>0 && (int b[--n]&16rC0)==16r80); + --nr; + } + if(n == 0) + break; + s += string b[0:n]; + m += nr; + } + return s; +} + +Win.wshow(w : self ref Win) +{ + w.ctlwrite("show\n"); +} + +Win.wsetdump(w : self ref Win, dir : string, cmd : string) +{ + t : string; + + if(dir != nil){ + t = "dumpdir " + dir + "\n"; + w.ctlwrite(t); + t = nil; + } + if(cmd != nil){ + t = "dump " + cmd + "\n"; + w.ctlwrite(t); + t = nil; + } +} + +Win.wselect(w : self ref Win, addr : string) +{ + if(w.addr == nil) + w.addr = w.openfile("addr"); + if(swrite(w.addr, addr) < 0) + error("writing addr"); + w.ctlwrite("dot=addr\n"); +} + +Win.wtagwrite(w : self ref Win, s : string) +{ + fd : ref FD; + + fd = w.openfile("tag"); + if(swrite(fd, s) != len s) + error("tag write: %r"); + fd = nil; +} + +Win.ctlwrite(w : self ref Win, s : string) +{ + if(swrite(w.ctl, s) != len s) + error("write error to ctl file: %r"); +} + +Win.wdel(w : self ref Win, sure : int) : int +{ + if (w == nil) + return False; + if(sure) + swrite(w.ctl, "delete\n"); + else if(swrite(w.ctl, "del\n") != 4) + return False; + w.wdormant(); + w.ctl = nil; + w.event = nil; + return True; +} + +Win.wname(w : self ref Win, s : string) +{ + w.ctlwrite("name " + s + "\n"); +} + +Win.wclean(w : self ref Win) +{ + if(w.body != nil) + w.body.flush(); + w.ctlwrite("clean\n"); +} + +Win.wdormant(w : self ref Win) +{ + w.addr = nil; + if(w.body != nil){ + w.body.close(); + w.body = nil; + } + w.data = nil; +} + +Win.getec(w : self ref Win) : int +{ + if(w.nbuf == 0){ + w.nbuf = read(w.event, w.buf, len w.buf); + if(w.nbuf <= 0 && !killing) { + error("event read error: %r"); + } + w.bufp = 0; + } + w.nbuf--; + return int w.buf[w.bufp++]; +} + +Win.geten(w : self ref Win) : int +{ + n, c : int; + + n = 0; + while('0'<=(c=w.getec()) && c<='9') + n = n*10+(c-'0'); + if(c != ' ') + error("event number syntax"); + return n; +} + +Win.geter(w : self ref Win, buf : array of byte) : (int, int) +{ + r, m, n, ok : int; + + r = w.getec(); + buf[0] = byte r; + n = 1; + if(r >= Runeself) { + for (;;) { + (r, m, ok) = byte2char(buf[0:n], 0); + if (m > 0) + return (r, n); + buf[n++] = byte w.getec(); + } + } + return (r, n); +} + +Win.wevent(w : self ref Win, e : ref Event) +{ + i, nb : int; + + e.c1 = w.getec(); + e.c2 = w.getec(); + e.q0 = w.geten(); + e.q1 = w.geten(); + e.flag = w.geten(); + e.nr = w.geten(); + if(e.nr > EVENTSIZE) + error("event string too long"); + e.nb = 0; + for(i=0; i Hdrs ( "", 0 ), +}; + +StRnCmP(s : string, t : string, n : int) : int +{ + c, d, i, j : int; + + i = j = 0; + if (len s < n || len t < n) + return -1; + while(n > 0){ + c = s[i++]; + d = t[j++]; + --n; + if(c != d){ + if('a'<=c && c<='z') + c -= 'a'-'A'; + if('a'<=d && d<='z') + d -= 'a'-'A'; + if(c != d) + return c-d; + } + } + return 0; +} + +readhdr(b : ref Box) : (string, int) +{ + i, j, n, m, typex : int; + s, t : string; + +{ + s = b.readline(); + n = len s; + if(n <= 0) { + b.unreadline(); + raise("e"); + } + for(i=0; i0 && j == ':') + break; + if(j<'!' || '~' + return (nil, None); +} +} + +Mesg.read(b : ref Box) : ref Mesg +{ + m : ref Mesg; + s : string; + n, typex : int; + + s = b.readline(); + n = len s; + if(n <= 0) + return nil; + +{ + if(n < 5 || (s[0:5] !="From " && s[0:5] != "From:")) + raise("e"); + m = newmesg(); + m.popno = b.popno; + if (m.popno == 0) + error("bad pop3 id"); + m.realhdr = s; + # toss 'From ' + s = s[5:]; + n -= 5; + # toss spaces/tabs + while (n > 0 && (s[0] == ' ' || s[0] == '\t')) { + s = s[1:]; + n--; + } + m.hdr = s; + # convert first blank to tab + s0 := strchr(m.hdr, ' '); + if(s0 >= 0){ + m.hdr[s0] = '\t'; + # drop trailing seconds, time zone, and year if match local year + t := n-6; + if(t <= 0) + raise("e"); + if(m.hdr[t:n-1] == date[23:]){ + m.hdr = m.hdr[0:t] + "\n"; # drop year for sure + t = -1; + s1 := strchr(m.hdr[s0:], ':'); + if(s1 >= 0) + t = strchr(m.hdr[s0+s1+1:], ':'); + if(t >= 0) # drop seconds and time zone + m.hdr = m.hdr[0:s0+s1+t+1] + "\n"; + else{ # drop time zone + t = strchr(m.hdr[s0+s1+1:], ' '); + if(t >= 0) + m.hdr = m.hdr[0:s0+s1+t+1] + "\n"; + } + n = len m.hdr; + } + } + m.lline1 = n; + m.text = nil; + # read header +loop: + for(;;){ + (s, typex) = readhdr(b); + case(typex){ + None => + break loop; + ReplyTo => + m.replyto = s[9:]; + break; + From => + if(m.replyto == nil) + m.replyto = s[5:]; + break; + Subject => + m.subj = s[8:]; + break; + Re => + m.subj = s[3:]; + break; + Date => + break; + } + m.realhdr += s; + if(typex != Ignore) + m.hdr += s; + } + # read body + for(;;){ + s = b.readline(); + n = len s; + if(n <= 0) + break; +# if(len s >= 5 && (s[0:5] == "From " || s[0:5] == "From:")){ +# b.unreadline(); +# break; +# } + m.text += s; + } + # remove trailing "morF\n" + l := len m.text; + if(l>6 && m.text[l-6:] == "\nmorF\n") + m.text = m.text[0:l-5]; + m.box = b; + return m; +} +exception{ + "*" => + error("malformed header " + s); + return nil; +} +} + +Mesg.mkmail(b : ref Box, hdr : string) +{ + r : ref Mesg; + + r = newmesg(); + r.hdr = hdr + "\n"; + r.lline1 = len r.hdr; + r.text = nil; + r.box = b; + r.open(); + r.w.wdormant(); +} + +replyaddr(r : string) : string +{ + p, q, rr : int; + + rr = 0; + while(r[rr]==' ' || r[rr]=='\t') + rr++; + r = r[rr:]; + p = strchr(r, '<'); + if(p >= 0){ + q = strchr(r[p+1:], '>'); + if(q < 0) + r = r[p+1:]; + else + r = r[p+1:p+q] + "\n"; + return r; + } + p = strchr(r, '('); + if(p >= 0){ + q = strchr(r[p:], ')'); + if(q < 0) + r = r[0:p]; + else + r = r[0:p] + r[p+q+1:]; + } + return r; +} + +Mesg.mkreply(m : self ref Mesg) +{ + r : ref Mesg; + + r = newmesg(); + if(m.replyto != nil){ + r.hdr = replyaddr(m.replyto); + r.lline1 = len r.hdr; + }else{ + r.hdr = m.hdr[0:m.lline1]; + r.lline1 = m.lline1; # was len m.hdr; + } + if(m.subj != nil){ + if(StRnCmP(m.subj, "re:", 3)==0 || StRnCmP(m.subj, " re:", 4)==0) + r.text = "Subject:" + m.subj + "\n"; + else + r.text = "Subject: Re:" + m.subj + "\n"; + } + else + r.text = nil; + r.box = m.box; + r.open(); + r.w.wselect("$"); + r.w.wdormant(); +} + +Mesg.free(m : self ref Mesg) +{ + m.text = nil; + m.hdr = nil; + m.subj = nil; + m.realhdr = nil; + m.replyto = nil; + m = nil; +} + +replyid : ref Ref; + +initreply() +{ + replyid = Ref.init(); +} + +Mesg.open(m : self ref Mesg) +{ + buf, s : string; + + if(m.isopen) + return; + m.w = Win.wnew(); + if(m.id != 0) + m.w.wwritebody("From "); + m.w.wwritebody(m.hdr); + m.w.wwritebody(m.text); + if(m.id){ + buf = sprint("Mail/box/%d", m.id); + m.w.wtagwrite("Reply Delmesg Save"); + }else{ + buf = sprint("Mail/%s/Reply%d", s, replyid.inc()); + m.w.wtagwrite("Post"); + } + m.w.wname(buf); + m.w.wclean(); + m.w.wselect("0"); + m.isopen = True; + m.posted = False; + spawn m.slave(); +} + +Mesg.putpost(m : self ref Mesg, e : ref Event) +{ + if(m.posted || m.id==0) + return; + if(e.q0 >= len m.hdr+5) # include "From " + return; + m.w.wtagwrite(" Post"); + m.posted = True; + return; +} + +Mesg.slave(m : self ref Mesg) +{ + e, e2, ea, etoss, eq : ref Event; + s : string; + na : int; + + e = newevent(); + e2 = newevent(); + ea = newevent(); + etoss = newevent(); + for(;;){ + m.w.wevent(e); + case(e.c1){ + 'E' => # write to body; can't affect us + break; + 'F' => # generated by our actions; ignore + break; + 'K' or 'M' => # type away; we don't care + case(e.c2){ + 'x' or 'X' => # mouse only + eq = e; + if(e.flag & 2){ + m.w.wevent(e2); + eq = e2; + } + if(e.flag & 8){ + m.w.wevent(ea); + m.w.wevent(etoss); + na = ea.nb; + }else + na = 0; + if(eq.q1>eq.q0 && eq.nb==0) + s = m.w.wread(eq.q0, eq.q1); + else + s = string eq.b[0:eq.nb]; + if(na) + s = s + " " + string ea.b[0:ea.nb]; + if(!m.command(s)) # send it back + m.w.wwriteevent(e); + s = nil; + break; + 'l' or 'L' => # mouse only + if(e.flag & 2) + m.w.wevent(e2); + # just send it back + m.w.wwriteevent(e); + break; + 'I' or 'D' => # modify away; we don't care + m.putpost(e); + break; + 'd' or 'i' => + break; + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } + } +} + +Mesg.command(m : self ref Mesg, s : string) : int +{ + while(s[0]==' ' || s[0]=='\t' || s[0]=='\n') + s = s[1:]; + if(s == "Post"){ + m.send(); + return True; + } + if(len s >= 4 && s[0:4] == "Save"){ + s = s[4:]; + while(s[0]==' ' || s[0]=='\t' || s[0]=='\n') + s = s[1:]; + if(s == nil) + m.save("stored"); + else{ + ss := 0; + while(ss < len s && s[ss]!=' ' && s[ss]!='\t' && s[ss]!='\n') + ss++; + m.save(s[0:ss]); + } + return True; + } + if(s == "Reply"){ + m.mkreply(); + return True; + } + if(s == "Del"){ + if(m.w.wdel(False)){ + m.isopen = False; + exit; + } + return True; + } + if(s == "Delmesg"){ + if(m.w.wdel(False)){ + m.isopen = False; + m.box.cdel <-= m; + exit; + } + return True; + } + return False; +} + +Mesg.save(m : self ref Mesg, base : string) +{ + s, buf : string; + n : int; + fd : ref FD; + b : ref Iobuf; + + if(m.id <= 0){ + fprint(stderr, "can't save reply message; mail it to yourself\n"); + return; + } + buf = nil; + s = base; +{ + if(access(s) < 0) + raise("e"); + fd = tryopen(s, OWRITE); + if(fd == nil) + raise("e"); + buf = nil; + b = bufio->fopen(fd, OWRITE); + # seek to end in case file isn't append-only + b.seek(big 0, 2); + # use edited headers: first line of real header followed by remainder of selected ones + for(n=0; n + buf = nil; + fprint(stderr, "mail: can't open %s: %r\n", base); + return; +} +} + +Mesg.send(m : self ref Mesg) +{ + s, buf : string; + t, u : int; + a, b : list of string; + n : int; + p : array of ref FD; + c : chan of int; + + p = array[2] of ref FD; + s = m.w.wreadall(); + a = "sendmail" :: nil; + if(len s >= 5 && (s[0:5] == "From " || s[0:5] == "From:")) + s = s[5:]; + for(t=0; t < len s && s[t]!='\n' && s[t]!='\t';){ + while(t < len s && (s[t]==' ' || s[t]==',')) + t++; + u = t; + while(t < len s && s[t]!=' ' && s[t]!=',' && s[t]!='\t' && s[t]!='\n') + t++; + if(t == u) + break; + a = s[u:t] :: a; + } + b = nil; + for ( ; a != nil; a = tl a) + b = hd a :: b; + a = b; + while(t < len s && s[t]!='\n') + t++; + if(s[t] == '\n') + t++; + if(pipe(p) < 0) + error("can't pipe: %r"); + c = chan of int; + spawn run(a, c, p[0]); + <-c; + c = nil; + p[0] = nil; + n = len s - t; + if(swrite(p[1], s[t:]) != n) + fprint(stderr, "write to pipe failed: %r\n"); + p[1] = nil; + # run() frees the arg list + buf = sprint("Mail/box/%d-R", m.id); + m.w.wname(buf); + m.w.wclean(); +} + +Box.read(readonly : int) : ref Box +{ + b : ref Box; + m : ref Mesg; + buf : string; + + b = ref Box; + b.nm = 0; + b.leng = 0; + b.readonly = readonly; + pop3open(); + pop3init(b); + while((m = m.read(b)) != nil){ + m.next = b.m; + b.m = m; + b.nm++; + m.id = b.nm; + } + pop3close(); + if (b.leng != b.nm) + error("bad message count in Box.read()"); + b.w = Win.wnew(); + for(m=b.m; m != nil; m=m.next){ + if(m.subj != nil) + buf = sprint("%d\t%s\t %s", m.id, m.hdr[0:m.lline1], m.subj); + else + buf = sprint("%d\t%s", m.id, m.hdr[0:m.lline1]); + b.w.wwritebody(buf); + } + buf = sprint("Mail/box/"); + b.w.wname(buf); + if(b.readonly) + b.w.wtagwrite("Mail"); + else + b.w.wtagwrite("Put Mail"); + buf = "Mail " + "box"; + b.w.wsetdump("/acme/mail", buf); + b.w.wclean(); + b.w.wselect("0"); + b.w.wdormant(); + b.cdel= chan of ref Mesg; + b.cevent = chan of Event; + b.cmore = chan of int; + spawn b.w.wslave(b.cevent); + b.clean = True; + return b; +} + +Box.readmore(b : self ref Box) +{ + m : ref Mesg; + new : int; + buf : string; + + new = False; + leng := b.leng; + n := 0; + pop3open(); + pop3more(b); + while((m = m.read(b)) != nil){ + m.next = b.m; + b.m = m; + b.nm++; + n++; + m.id = b.nm; + if(m.subj != nil) + buf = sprint("%d\t%s\t %s", m.id, m.hdr[0:m.lline1], m.subj); + else + buf = sprint("%d\t%s", m.id, m.hdr[0:m.lline1]); + b.w.wreplace("0", buf); + new = True; + } + pop3close(); + if (b.leng != leng+n) + error("bad message count in Box.readmore()"); + if(new){ + if(b.clean) + b.w.wclean(); + b.w.wselect("0;/.*(\\n[ \t].*)*"); + b.w.wshow(); + } + b.w.wdormant(); +} + +Box.readline(b : self ref Box) : string +{ + for (;;) { + if(b.peekline != nil){ + b.line = b.peekline; + b.peekline = nil; + }else + b.line = pop3next(b); + # nulls appear in mailboxes! + if(b.line != nil && strchr(b.line, 0) >= 0) + ; + else + break; + } + return b.line; +} + +Box.unreadline(b : self ref Box) +{ + b.peekline = b.line; +} + +Box.slave(b : self ref Box) +{ + e : ref Event; + m : ref Mesg; + + e = newevent(); + for(;;){ + alt{ + *e = <-b.cevent => + b.event(e); + break; + <-b.cmore => + b.readmore(); + break; + m = <-b.cdel => + b.mdel(m); + break; + } + } +} + +Box.event(b : self ref Box, e : ref Event) +{ + e2, ea, eq : ref Event; + s : string; + t : int; + n, na, nopen : int; + + e2 = newevent(); + ea = newevent(); + case(e.c1){ + 'E' => # write to body; can't affect us + break; + 'F' => # generated by our actions; ignore + break; + 'K' => # type away; we don't care + break; + 'M' => + case(e.c2){ + 'x' or 'X' => + if(e.flag & 2) + *e2 = <-b.cevent; + if(e.flag & 8){ + *ea = <-b.cevent; + na = ea.nb; + <- b.cevent; + }else + na = 0; + s = string e.b[0:e.nb]; + # if it's a known command, do it + if((e.flag&2) && e.nb==0) + s = string e2.b[0:e2.nb]; + if(na) + s = sprint("%s %s", s, string ea.b[0:ea.nb]); + # if it's a long message, it can't be for us anyway + if(!b.command(s)) # send it back + b.w.wwriteevent(e); + if(na) + s = nil; + break; + 'l' or 'L' => + eq = e; + if(e.flag & 2){ + *e2 = <-b.cevent; + eq = e2; + } + s = string eq.b[0:eq.nb]; + if(eq.q1>eq.q0 && eq.nb==0) + s = b.w.wread(eq.q0, eq.q1); + nopen = 0; + do{ + t = 0; + (n, t) = strtoi(s); + if(n>0 && (t == len s || s[t]==' ' || s[t]=='\t' || s[t]=='\n')){ + b.mopen(n); + nopen++; + s = s[t:]; + } + while(s != nil && s[0]!='\n') + s = s[1:]; + }while(s != nil); + if(nopen == 0) # send it back + b.w.wwriteevent(e); + break; + 'I' or 'D' or 'd' or 'i' => # modify away; we don't care + break; + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } +} + +Box.mopen(b : self ref Box, id : int) +{ + m : ref Mesg; + + for(m=b.m; m != nil; m=m.next) + if(m.id == id){ + m.open(); + break; + } +} + +Box.mdel(b : self ref Box, dm : ref Mesg) +{ + m : ref Mesg; + buf : string; + + if(dm.id){ + for(m=b.m; m!=nil && m!=dm; m=m.next) + ; + if(m == nil) + error(sprint("message %d not found", dm.id)); + m.deleted = 1; + # remove from screen: use acme to help + buf = sprint("/^%d .*\\n(^[ \t].*\\n)*/", m.id); + b.w.wreplace(buf, ""); + } + dm.free(); + b.clean = False; +} + +Box.command(b : self ref Box, s : string) : int +{ + t : int; + m : ref Mesg; + + while(s[0]==' ' || s[0]=='\t' || s[0]=='\n') + s = s[1:]; + if(len s >= 4 && s[0:4] == "Mail"){ + s = s[4:]; + while(s != nil && (s[0]==' ' || s[0]=='\t' || s[0]=='\n')) + s = s[1:]; + t = 0; + while(t < len s && s[t] && s[t]!=' ' && s[t]!='\t' && s[t]!='\n') + t++; + m = b.m; # avoid warning message on b.m.mkmail(...) + m.mkmail(b, s[0:t]); + return True; + } + if(s == "Del"){ + + if(!b.clean){ + b.clean = True; + fprint(stderr, "mail: mailbox not written\n"); + return True; + } + postnote(PNGROUP, pctl(0, nil), "kill"); + killing = 1; + pctl(NEWPGRP, nil); + b.w.wdel(True); + for(m=b.m; m != nil; m=m.next) + m.w.wdel(False); + exit; + return True; + } + if(s == "Put"){ + if(b.readonly) + fprint(stderr, "Mail is read-only\n"); + else + b.rewrite(); + return True; + } + return False; +} + +Box.rewrite(b : self ref Box) +{ + prev, m : ref Mesg; + + if(b.clean){ + b.w.wclean(); + return; + } + prev = nil; + pop3open(); + for(m=b.m; m!=nil; m=m.next) { + if (m.deleted && pop3del(m.popno) >= 0) { + b.leng--; + if (prev == nil) + b.m=m.next; + else + prev.next=m.next; + } + else + prev = m; + } + pop3close(); + b.w.wclean(); + b.clean = True; +} diff --git a/appl/acme/acme/mail/src/Mailpop3.b b/appl/acme/acme/mail/src/Mailpop3.b new file mode 100644 index 00000000..f267d737 --- /dev/null +++ b/appl/acme/acme/mail/src/Mailpop3.b @@ -0,0 +1,1716 @@ +implement Mailpop3; + +include "sys.m"; +include "draw.m"; +include "bufio.m"; +include "daytime.m"; +include "sh.m"; +include "pop3.m"; + +Mailpop3 : module { + init : fn(ctxt : ref Draw->Context, argl : list of string); +}; + +sys : Sys; +bufio : Bufio; +daytime : Daytime; +pop3 : Pop3; + +OREAD, OWRITE, ORDWR, FORKENV, NEWFD, FORKFD, NEWPGRP, UTFmax : import Sys; +FD, Dir : import sys; +fprint, sprint, sleep, create, open, read, write, remove, stat, fstat, fwstat, fildes, pctl, pipe, dup, byte2char : import sys; +Context : import Draw; +EOF : import Bufio; +Iobuf : import bufio; +time : import daytime; + +DIRLEN : con 116; +PNPROC, PNGROUP : con iota; +False : con 0; +True : con 1; +EVENTSIZE : con 256; +Runeself : con 16r80; +OCEXEC : con 0; +CHEXCL : con 0; # 16r20000000; +CHAPPEND : con 0; # 16r40000000; + +Win : adt { + winid : int; + addr : ref FD; + body : ref Iobuf; + ctl : ref FD; + data : ref FD; + event : ref FD; + buf : array of byte; + bufp : int; + nbuf : int; + + wnew : fn() : ref Win; + wwritebody : fn(w : self ref Win, s : string); + wread : fn(w : self ref Win, m : int, n : int) : string; + wclean : fn(w : self ref Win); + wname : fn(w : self ref Win, s : string); + wdormant : fn(w : self ref Win); + wevent : fn(w : self ref Win, e : ref Event); + wshow : fn(w : self ref Win); + wtagwrite : fn(w : self ref Win, s : string); + wwriteevent : fn(w : self ref Win, e : ref Event); + wslave : fn(w : self ref Win, c : chan of Event); + wreplace : fn(w : self ref Win, s : string, t : string); + wselect : fn(w : self ref Win, s : string); + wsetdump : fn(w : self ref Win, s : string, t : string); + wdel : fn(w : self ref Win, n : int) : int; + wreadall : fn(w : self ref Win) : string; + + ctlwrite : fn(w : self ref Win, s : string); + getec : fn(w : self ref Win) : int; + geten : fn(w : self ref Win) : int; + geter : fn(w : self ref Win, s : array of byte) : (int, int); + openfile : fn(w : self ref Win, s : string) : ref FD; + openbody : fn(w : self ref Win, n : int); +}; + +Mesg : adt { + w : ref Win; + id : int; + popno : int; + hdr : string; + realhdr : string; + replyto : string; + text : string; + subj : string; + next : cyclic ref Mesg; + lline1 : int; + box : cyclic ref Box; + isopen : int; + posted : int; + deleted : int; + + read : fn(b : ref Box) : ref Mesg; + open : fn(m : self ref Mesg); + slave : fn(m : self ref Mesg); + free : fn(m : self ref Mesg); + save : fn(m : self ref Mesg, s : string); + mkreply : fn(m : self ref Mesg); + mkmail : fn(b : ref Box, s : string); + putpost : fn(m : self ref Mesg, e : ref Event); + + command : fn(m : self ref Mesg, s : string) : int; + send : fn(m : self ref Mesg); +}; + +Box : adt { + w : ref Win; + nm : int; + readonly : int; + m : cyclic ref Mesg; +# io : ref Iobuf; + clean : int; + leng : int; + cdel : chan of ref Mesg; + cevent : chan of Event; + cmore : chan of int; + lst : list of int; + s : string; + + line : string; + popno : int; + peekline : string; + + read : fn(n : int) : ref Box; + readmore : fn(b : self ref Box, lck : int); + readline : fn(b : self ref Box) : string; + unreadline : fn(b : self ref Box); + slave : fn(b : self ref Box); + mopen : fn(b : self ref Box, n : int); + rewrite : fn(b : self ref Box); + mdel : fn(b : self ref Box, m : ref Mesg); + event : fn(b : self ref Box, e : ref Event); + + command : fn(b : self ref Box, s : string) : int; +}; + +Event : adt { + c1 : int; + c2 : int; + q0 : int; + q1 : int; + flag : int; + nb : int; + nr : int; + b : array of byte; + r : array of int; +}; + +Lock : adt { + cnt : int; + chann : chan of int; + + init : fn() : ref Lock; + lock : fn(l : self ref Lock); + unlock : fn(l : self ref Lock); +}; + +Ref : adt { + l : ref Lock; + cnt : int; + + init : fn() : ref Ref; + inc : fn(r : self ref Ref) : int; +}; + +mbox : ref Box; +user : string; +pwd : string; +date : string; +mailctxt : ref Context; +stdout, stderr : ref FD; + +killing : int = 0; + +init(ctxt : ref Context, nil : list of string) +{ + mailctxt = ctxt; + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + daytime = load Daytime Daytime->PATH; + pop3 = load Pop3 Pop3->PATH; + stdout = fildes(1); + stderr = fildes(2); + main(); +} + +dlock : ref Lock; +dfd : ref Sys->FD; + +debug(s : string) +{ + if (dfd == nil) { + dfd = sys->create("/usr/jrf/acme/debugmail", Sys->OWRITE, 8r600); + dlock = Lock.init(); + } + if (dfd == nil) + return; + dlock.lock(); + sys->fprint(dfd, "%s", s); + dlock.unlock(); +} + +postnote(t : int, pid : int, note : string) : int +{ + fd := open("#p/" + string pid + "/ctl", OWRITE); + if (fd == nil) + return -1; + if (t == PNGROUP) + note += "grp"; + fprint(fd, "%s", note); + fd = nil; + return 0; +} + +exec(cmd : string, argl : list of string) +{ + file := cmd; + if(len file<4 || file[len file-4:]!=".dis") + file += ".dis"; + + c := load Command file; + if(c == nil) { + err := sprint("%r"); + if(file[0]!='/' && file[0:2]!="./"){ + c = load Command "/dis/"+file; + if(c == nil) + err = sprint("%r"); + } + if(c == nil){ + fprint(stderr, "%s: %s\n", cmd, err); + return; + } + } + c->init(mailctxt, argl); +} + +swrite(fd : ref FD, s : string) : int +{ + ab := array of byte s; + m := len ab; + p := write(fd, ab, m); + if (p == m) + return len s; + if (p <= 0) + return p; + return 0; +} + +strchr(s : string, c : int) : int +{ + for (i := 0; i < len s; i++) + if (s[i] == c) + return i; + return -1; +} + +strrchr(s : string, c : int) : int +{ + for (i := len s - 1; i >= 0; i--) + if (s[i] == c) + return i; + return -1; +} + +strtoi(s : string) : (int, int) +{ + m := 0; + neg := 0; + t := 0; + ls := len s; + while (t < ls && (s[t] == ' ' || s[t] == '\t')) + t++; + if (t < ls && s[t] == '+') + t++; + else if (t < ls && s[t] == '-') { + neg = 1; + t++; + } + while (t < ls && (s[t] >= '0' && s[t] <= '9')) { + m = 10*m + s[t]-'0'; + t++; + } + if (neg) + m = -m; + return (m, t); +} + +access(s : string) : int +{ + fd := open(s, 0); + if (fd == nil) + return -1; + fd = nil; + return 0; +} + +newevent() : ref Event +{ + e := ref Event; + e.b = array[EVENTSIZE*UTFmax+1] of byte; + e.r = array[EVENTSIZE+1] of int; + return e; +} + +newmesg() : ref Mesg +{ + m := ref Mesg; + m.id = m.lline1 = m.isopen = m.posted = m.deleted = 0; + return m; +} + +lc, uc : chan of ref Lock; + +initlock() +{ + lc = chan of ref Lock; + uc = chan of ref Lock; + spawn lockmgr(); +} + +lockmgr() +{ + l : ref Lock; + + for (;;) { + alt { + l = <- lc => + if (l.cnt++ == 0) + l.chann <-= 1; + l = <- uc => + if (--l.cnt > 0) + l.chann <-= 1; + } + } +} + +Lock.init() : ref Lock +{ + return ref Lock(0, chan of int); +} + +Lock.lock(l : self ref Lock) +{ + lc <-= l; + <- l.chann; +} + +Lock.unlock(l : self ref Lock) +{ + uc <-= l; +} + +Ref.init() : ref Ref +{ + r := ref Ref; + r.l = Lock.init(); + r.cnt = 0; + return r; +} + +Ref.inc(r : self ref Ref) : int +{ + r.l.lock(); + i := r.cnt; + r.cnt++; + r.l.unlock(); + return i; +} + +error(s : string) +{ + if(s != nil) + fprint(stderr, "mail: %s\n", s); + postnote(PNGROUP, pctl(0, nil), "kill"); + killing = 1; + exit; +} + +tryopen(s : string, mode : int) : ref FD +{ + fd : ref FD; + try : int; + + for(try=0; try<3; try++){ + fd = open(s, mode); + if(fd != nil) + return fd; + sleep(1000); + } + return nil; +} + +run(argv : list of string, c : chan of int, p0 : ref FD) +{ + # pctl(FORKFD|NEWPGRP, nil); # had RFMEM + pctl(FORKENV|NEWFD|NEWPGRP, 0::1::2::p0.fd::nil); + c <-= pctl(0, nil); + dup(p0.fd, 0); + p0 = nil; + exec(hd argv, argv); + exit; +} + +getuser() : string +{ + fd := open("/dev/user", OREAD); + if(fd == nil) + return ""; + buf := array[128] of byte; + n := read(fd, buf, len buf); + if(n < 0) + return ""; + return string buf[0:n]; +} + +pop3conn : int = 0; +pop3bad : int = 0; +pop3lock : ref Lock; + +pop3open(lck : int) +{ + if (lck) + pop3lock.lock(); + if (!pop3conn) { + (ok, s) := pop3->open(user, pwd, nil); + if (ok < 0) { + if (!pop3bad) { + fprint(stderr, "mail: could not connect to POP3 mail server : %s\n", s); + pop3bad = 1; + } + return; + } + } + pop3conn = 1; + pop3bad = 0; +} + +pop3close(unlck : int) +{ + if (pop3conn) { + (ok, s) := pop3->close(); + if (ok < 0) { + fprint(stderr, "mail: could not close POP3 connection : %s\n", s); + pop3lock.unlock(); + return; + } + } + pop3conn = 0; + if (unlck) + pop3lock.unlock(); +} + +pop3stat(b : ref Box) : int +{ + (ok, s, nm, nil) := pop3->stat(); + if (ok < 0 && pop3conn) { + fprint(stderr, "mail: could not stat POP3 server : %s\n", s); + return b.leng; + } + return nm; +} + +pop3list() : list of int +{ + (ok, s, l) := pop3->msgnolist(); + if (ok < 0 && pop3conn) { + fprint(stderr, "mail: could not get list from POP3 server : %s\n", s); + return nil; + } + return l; +} + +pop3mesg(mno : int) : string +{ + (ok, s, msg) := pop3->get(mno); + if (ok < 0 && pop3conn) { + fprint(stderr, "mail: could not retrieve a message from server : %s\n", s); + return "Acme Mail : FAILED TO RETRIEVE MESSAGE\n"; + } + return msg; +} + +pop3del(mno : int) : int +{ + (ok, s) := pop3->delete(mno); + if (ok < 0) + fprint(stderr, "mail: could not delete message : %s\n", s); + return ok; +} + +pop3init(b : ref Box) +{ + b.leng = pop3stat(b); + b.lst = pop3list(); + b.s = nil; + b.popno = 0; + if (len b.lst != b.leng) + error("bad lengths in pop3init()"); +} + +pop3more(b : ref Box) +{ + nl : list of int; + + leng := b.leng; + b.leng = pop3stat(b); + b.lst = pop3list(); + b.s = nil; + b.popno = 0; + if (len b.lst != b.leng || b.leng < leng) + error("bad lengths in pop3more()"); + # is this ok ? + nl = nil; + for (i := 0; i < leng; i++) { + nl = hd b.lst :: nl; + b.lst = tl b.lst; + } + # now update pop nos. + for (m := b.m; m != nil; m = m.next) { + # opopno := m.popno; + if (nl == nil) + error("message list too big"); + m.popno = hd nl; + nl = tl nl; + # debug(sys->sprint("%d : popno from %d to %d\n", m.id, opopno, m.popno)); + } + if (nl != nil) + error("message list too small"); +} + +pop3next(b : ref Box) : string +{ + mno : int = 0; + r : string; + + if (b.s == nil) { + if (b.lst == nil) + return nil; # end of box + first := b.popno == 0; + mno = hd b.lst; + b.lst = tl b.lst; + b.s = pop3mesg(mno); + b.popno = mno; + if (!first) + return nil; # end of message + } + t := strchr(b.s, '\n'); + if (t >= 0) { + r = b.s[0:t+1]; + b.s = b.s[t+1:]; + } + else { + r = b.s; + b.s = nil; + } + return r; +} + +main() +{ + readonly : int; + + initlock(); + initreply(); + date = time(); + if(date==nil) + error("can't get current time"); + user = getuser(); + if(user == nil) + user = "Wile.E.Coyote"; + readonly = False; + pop3lock = Lock.init(); + mbox = mbox.read(readonly); + spawn timeslave(mbox, mbox.cmore); + mbox.slave(); + error(nil); +} + +timeslave(b : ref Box, c : chan of int) +{ + for(;;){ + sleep(30*1000); + pop3open(1); + leng := pop3stat(b); + pop3close(1); + if (leng > b.leng) + c <-= 0; + } +} + +Win.wnew() : ref Win +{ + w := ref Win; + buf := array[12] of byte; + w.ctl = open("/chan/new/ctl", ORDWR); + if(w.ctl==nil || read(w.ctl, buf, 12)!=12) + error("can't open window ctl file: %r"); + w.ctlwrite("noscroll\n"); + w.winid = int string buf; + w.event = w.openfile("event"); + w.addr = nil; # will be opened when needed + w.body = nil; + w.data = nil; + w.bufp = w.nbuf = 0; + w.buf = array[512] of byte; + return w; +} + +Win.openfile(w : self ref Win, f : string) : ref FD +{ + buf := sprint("/chan/%d/%s", w.winid, f); + fd := open(buf, ORDWR|OCEXEC); + if(fd == nil) + error(sprint("can't open window %s file: %r", f)); + return fd; +} + +Win.openbody(w : self ref Win, mode : int) +{ + buf := sprint("/chan/%d/body", w.winid); + w.body = bufio->open(buf, mode|OCEXEC); + if(w.body == nil) + error("can't open window body file: %r"); +} + +Win.wwritebody(w : self ref Win, s : string) +{ + n := len s; + if(w.body == nil) + w.openbody(OWRITE); + if(w.body.puts(s) != n) + error("write error to window: %r"); +} + +Win.wreplace(w : self ref Win, addr : string, repl : string) +{ + if(w.addr == nil) + w.addr = w.openfile("addr"); + if(w.data == nil) + w.data = w.openfile("data"); + if(swrite(w.addr, addr) < 0){ + fprint(stderr, "mail: warning: bad address %s:%r\n", addr); + return; + } + if(swrite(w.data, repl) != len repl) + error("writing data: %r"); +} + +nrunes(s : array of byte, nb : int) : int +{ + i, n : int; + + n = 0; + for(i=0; iq1){ + do; while(n>0 && (int b[--n]&16rC0)==16r80); + --nr; + } + if(n == 0) + break; + s += string b[0:n]; + m += nr; + } + return s; +} + +Win.wshow(w : self ref Win) +{ + w.ctlwrite("show\n"); +} + +Win.wsetdump(w : self ref Win, dir : string, cmd : string) +{ + t : string; + + if(dir != nil){ + t = "dumpdir " + dir + "\n"; + w.ctlwrite(t); + t = nil; + } + if(cmd != nil){ + t = "dump " + cmd + "\n"; + w.ctlwrite(t); + t = nil; + } +} + +Win.wselect(w : self ref Win, addr : string) +{ + if(w.addr == nil) + w.addr = w.openfile("addr"); + if(swrite(w.addr, addr) < 0) + error("writing addr"); + w.ctlwrite("dot=addr\n"); +} + +Win.wtagwrite(w : self ref Win, s : string) +{ + fd : ref FD; + + fd = w.openfile("tag"); + if(swrite(fd, s) != len s) + error("tag write: %r"); + fd = nil; +} + +Win.ctlwrite(w : self ref Win, s : string) +{ + if(swrite(w.ctl, s) != len s) + error("write error to ctl file: %r"); +} + +Win.wdel(w : self ref Win, sure : int) : int +{ + if (w == nil) + return False; + if(sure) + swrite(w.ctl, "delete\n"); + else if(swrite(w.ctl, "del\n") != 4) + return False; + w.wdormant(); + w.ctl = nil; + w.event = nil; + return True; +} + +Win.wname(w : self ref Win, s : string) +{ + w.ctlwrite("name " + s + "\n"); +} + +Win.wclean(w : self ref Win) +{ + if(w.body != nil) + w.body.flush(); + w.ctlwrite("clean\n"); +} + +Win.wdormant(w : self ref Win) +{ + w.addr = nil; + if(w.body != nil){ + w.body.close(); + w.body = nil; + } + w.data = nil; +} + +Win.getec(w : self ref Win) : int +{ + if(w.nbuf == 0){ + w.nbuf = read(w.event, w.buf, len w.buf); + if(w.nbuf <= 0 && !killing) { + error("event read error: %r"); + } + w.bufp = 0; + } + w.nbuf--; + return int w.buf[w.bufp++]; +} + +Win.geten(w : self ref Win) : int +{ + n, c : int; + + n = 0; + while('0'<=(c=w.getec()) && c<='9') + n = n*10+(c-'0'); + if(c != ' ') + error("event number syntax"); + return n; +} + +Win.geter(w : self ref Win, buf : array of byte) : (int, int) +{ + r, m, n, ok : int; + + r = w.getec(); + buf[0] = byte r; + n = 1; + if(r >= Runeself) { + for (;;) { + (r, m, ok) = byte2char(buf[0:n], 0); + if (m > 0) + return (r, n); + buf[n++] = byte w.getec(); + } + } + return (r, n); +} + +Win.wevent(w : self ref Win, e : ref Event) +{ + i, nb : int; + + e.c1 = w.getec(); + e.c2 = w.getec(); + e.q0 = w.geten(); + e.q1 = w.geten(); + e.flag = w.geten(); + e.nr = w.geten(); + if(e.nr > EVENTSIZE) + error("event string too long"); + e.nb = 0; + for(i=0; i Hdrs ( "", 0 ), +}; + +StRnCmP(s : string, t : string, n : int) : int +{ + c, d, i, j : int; + + i = j = 0; + if (len s < n || len t < n) + return -1; + while(n > 0){ + c = s[i++]; + d = t[j++]; + --n; + if(c != d){ + if('a'<=c && c<='z') + c -= 'a'-'A'; + if('a'<=d && d<='z') + d -= 'a'-'A'; + if(c != d) + return c-d; + } + } + return 0; +} + +readhdr(b : ref Box) : (string, int) +{ + i, j, n, m, typex : int; + s, t : string; + +{ + s = b.readline(); + n = len s; + if(n <= 0) { + b.unreadline(); + raise("e"); + } + for(i=0; i0 && j == ':') + break; + if(j<'!' || '~' + return (nil, None); +} +} + +Mesg.read(b : ref Box) : ref Mesg +{ + m : ref Mesg; + s : string; + n, typex : int; + + s = b.readline(); + n = len s; + if(n <= 0) + return nil; + +{ + if(n < 5 || (s[0:5] !="From " && s[0:5] != "From:")) + raise("e"); + m = newmesg(); + m.popno = b.popno; + if (m.popno == 0) + error("bad pop3 id"); + m.realhdr = s; + # toss 'From ' + s = s[5:]; + n -= 5; + # toss spaces/tabs + while (n > 0 && (s[0] == ' ' || s[0] == '\t')) { + s = s[1:]; + n--; + } + m.hdr = s; + # convert first blank to tab + s0 := strchr(m.hdr, ' '); + if(s0 >= 0){ + m.hdr[s0] = '\t'; + # drop trailing seconds, time zone, and year if match local year + t := n-6; + if(t <= 0) + raise("e"); + if(m.hdr[t:n-1] == date[23:]){ + m.hdr = m.hdr[0:t] + "\n"; # drop year for sure + t = -1; + s1 := strchr(m.hdr[s0:], ':'); + if(s1 >= 0) + t = strchr(m.hdr[s0+s1+1:], ':'); + if(t >= 0) # drop seconds and time zone + m.hdr = m.hdr[0:s0+s1+t+1] + "\n"; + else{ # drop time zone + t = strchr(m.hdr[s0+s1+1:], ' '); + if(t >= 0) + m.hdr = m.hdr[0:s0+s1+t+1] + "\n"; + } + n = len m.hdr; + } + } + m.lline1 = n; + m.text = nil; + # read header +loop: + for(;;){ + (s, typex) = readhdr(b); + case(typex){ + None => + break loop; + ReplyTo => + m.replyto = s[9:]; + break; + From => + if(m.replyto == nil) + m.replyto = s[5:]; + break; + Subject => + m.subj = s[8:]; + break; + Re => + m.subj = s[3:]; + break; + Date => + break; + } + m.realhdr += s; + if(typex != Ignore) + m.hdr += s; + } + # read body + for(;;){ + s = b.readline(); + n = len s; + if(n <= 0) + break; +# if(len s >= 5 && (s[0:5] == "From " || s[0:5] == "From:")){ +# b.unreadline(); +# break; +# } + m.text += s; + } + # remove trailing "morF\n" + l := len m.text; + if(l>6 && m.text[l-6:] == "\nmorF\n") + m.text = m.text[0:l-5]; + m.box = b; + return m; +} +exception{ + "*" => + error("malformed header " + s); + return nil; +} +} + +Mesg.mkmail(b : ref Box, hdr : string) +{ + r : ref Mesg; + + r = newmesg(); + r.hdr = hdr + "\n"; + r.lline1 = len r.hdr; + r.text = nil; + r.box = b; + r.open(); + r.w.wdormant(); +} + +replyaddr(r : string) : string +{ + p, q, rr : int; + + rr = 0; + while(r[rr]==' ' || r[rr]=='\t') + rr++; + r = r[rr:]; + p = strchr(r, '<'); + if(p >= 0){ + q = strchr(r[p+1:], '>'); + if(q < 0) + r = r[p+1:]; + else + r = r[p+1:p+q] + "\n"; + return r; + } + p = strchr(r, '('); + if(p >= 0){ + q = strchr(r[p:], ')'); + if(q < 0) + r = r[0:p]; + else + r = r[0:p] + r[p+q+1:]; + } + return r; +} + +Mesg.mkreply(m : self ref Mesg) +{ + r : ref Mesg; + + r = newmesg(); + if(m.replyto != nil){ + r.hdr = replyaddr(m.replyto); + r.lline1 = len r.hdr; + }else{ + r.hdr = m.hdr[0:m.lline1]; + r.lline1 = m.lline1; # was len m.hdr; + } + if(m.subj != nil){ + if(StRnCmP(m.subj, "re:", 3)==0 || StRnCmP(m.subj, " re:", 4)==0) + r.text = "Subject:" + m.subj + "\n"; + else + r.text = "Subject: Re:" + m.subj + "\n"; + } + else + r.text = nil; + r.box = m.box; + r.open(); + r.w.wselect("$"); + r.w.wdormant(); +} + +Mesg.free(m : self ref Mesg) +{ + m.text = nil; + m.hdr = nil; + m.subj = nil; + m.realhdr = nil; + m.replyto = nil; + m = nil; +} + +replyid : ref Ref; + +initreply() +{ + replyid = Ref.init(); +} + +Mesg.open(m : self ref Mesg) +{ + buf, s : string; + + if(m.isopen) + return; + m.w = Win.wnew(); + if(m.id != 0) + m.w.wwritebody("From "); + m.w.wwritebody(m.hdr); + m.w.wwritebody(m.text); + if(m.id){ + buf = sprint("Mail/box/%d", m.id); + m.w.wtagwrite("Reply Delmesg Save"); + }else{ + buf = sprint("Mail/%s/Reply%d", s, replyid.inc()); + m.w.wtagwrite("Post"); + } + m.w.wname(buf); + m.w.wclean(); + m.w.wselect("0"); + m.isopen = True; + m.posted = False; + spawn m.slave(); +} + +Mesg.putpost(m : self ref Mesg, e : ref Event) +{ + if(m.posted || m.id==0) + return; + if(e.q0 >= len m.hdr+5) # include "From " + return; + m.w.wtagwrite(" Post"); + m.posted = True; + return; +} + +Mesg.slave(m : self ref Mesg) +{ + e, e2, ea, etoss, eq : ref Event; + s : string; + na : int; + + e = newevent(); + e2 = newevent(); + ea = newevent(); + etoss = newevent(); + for(;;){ + m.w.wevent(e); + case(e.c1){ + 'E' => # write to body; can't affect us + break; + 'F' => # generated by our actions; ignore + break; + 'K' or 'M' => # type away; we don't care + case(e.c2){ + 'x' or 'X' => # mouse only + eq = e; + if(e.flag & 2){ + m.w.wevent(e2); + eq = e2; + } + if(e.flag & 8){ + m.w.wevent(ea); + m.w.wevent(etoss); + na = ea.nb; + }else + na = 0; + if(eq.q1>eq.q0 && eq.nb==0) + s = m.w.wread(eq.q0, eq.q1); + else + s = string eq.b[0:eq.nb]; + if(na) + s = s + " " + string ea.b[0:ea.nb]; + if(!m.command(s)) # send it back + m.w.wwriteevent(e); + s = nil; + break; + 'l' or 'L' => # mouse only + if(e.flag & 2) + m.w.wevent(e2); + # just send it back + m.w.wwriteevent(e); + break; + 'I' or 'D' => # modify away; we don't care + m.putpost(e); + break; + 'd' or 'i' => + break; + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } + } +} + +Mesg.command(m : self ref Mesg, s : string) : int +{ + while(s[0]==' ' || s[0]=='\t' || s[0]=='\n') + s = s[1:]; + if(s == "Post"){ + m.send(); + return True; + } + if(len s >= 4 && s[0:4] == "Save"){ + s = s[4:]; + while(s[0]==' ' || s[0]=='\t' || s[0]=='\n') + s = s[1:]; + if(s == nil) + m.save("stored"); + else{ + ss := 0; + while(ss < len s && s[ss]!=' ' && s[ss]!='\t' && s[ss]!='\n') + ss++; + m.save(s[0:ss]); + } + return True; + } + if(s == "Reply"){ + m.mkreply(); + return True; + } + if(s == "Del"){ + if(m.w.wdel(False)){ + m.isopen = False; + exit; + } + return True; + } + if(s == "Delmesg"){ + if(m.w.wdel(False)){ + m.isopen = False; + m.box.cdel <-= m; + exit; + } + return True; + } + return False; +} + +Mesg.save(m : self ref Mesg, base : string) +{ + s, buf : string; + n : int; + fd : ref FD; + b : ref Iobuf; + + if(m.id <= 0){ + fprint(stderr, "can't save reply message; mail it to yourself\n"); + return; + } + buf = nil; + s = base; +{ + if(access(s) < 0) + raise("e"); + fd = tryopen(s, OWRITE); + if(fd == nil) + raise("e"); + buf = nil; + b = bufio->fopen(fd, OWRITE); + # seek to end in case file isn't append-only + b.seek(big 0, 2); + # use edited headers: first line of real header followed by remainder of selected ones + for(n=0; n + buf = nil; + fprint(stderr, "mail: can't open %s: %r\n", base); + return; +} +} + +Mesg.send(m : self ref Mesg) +{ + s, buf : string; + t, u : int; + a, b : list of string; + n : int; + p : array of ref FD; + c : chan of int; + + p = array[2] of ref FD; + s = m.w.wreadall(); + a = "sendmail" :: nil; + if(len s >= 5 && (s[0:5] == "From " || s[0:5] == "From:")) + s = s[5:]; + for(t=0; t < len s && s[t]!='\n' && s[t]!='\t';){ + while(t < len s && (s[t]==' ' || s[t]==',')) + t++; + u = t; + while(t < len s && s[t]!=' ' && s[t]!=',' && s[t]!='\t' && s[t]!='\n') + t++; + if(t == u) + break; + a = s[u:t] :: a; + } + b = nil; + for ( ; a != nil; a = tl a) + b = hd a :: b; + a = b; + while(t < len s && s[t]!='\n') + t++; + if(s[t] == '\n') + t++; + if(pipe(p) < 0) + error("can't pipe: %r"); + c = chan of int; + spawn run(a, c, p[0]); + <-c; + c = nil; + p[0] = nil; + n = len s - t; + if(swrite(p[1], s[t:]) != n) + fprint(stderr, "write to pipe failed: %r\n"); + p[1] = nil; + # run() frees the arg list + buf = sprint("Mail/box/%d-R", m.id); + m.w.wname(buf); + m.w.wclean(); +} + +Box.read(readonly : int) : ref Box +{ + b : ref Box; + m : ref Mesg; + buf : string; + + b = ref Box; + b.nm = 0; + b.leng = 0; + b.readonly = readonly; + b.w = Win.wnew(); + b.w.wwritebody("Password:"); + b.w.wname("Mail/box/"); + b.w.wclean(); + b.w.wselect("$"); + b.w.ctlwrite("noecho\n"); + b.cevent = chan of Event; + spawn b.w.wslave(b.cevent); + e := ref Event; + for (;;) { + sleep(1000); + s := b.w.wreadall(); + lens := len s; + if (lens >= 10 && s[0:9] == "Password:" && s[lens-1] == '\n') { + pwd = s[9:lens-1]; + for (i := 0; i < lens; i++) + s[i] = '\b'; + b.w.wwritebody(s); + break; + } + alt { + *e = <-b.cevent => + b.event(e); + break; + * => + break; + } + } + b.w.ctlwrite("echo\n"); + pop3open(1); + pop3init(b); + while((m = m.read(b)) != nil){ + m.next = b.m; + b.m = m; + b.nm++; + m.id = b.nm; + } + pop3close(1); + if (b.leng != b.nm) + error("bad message count in Box.read()"); + # b.w = Win.wnew(); + for(m=b.m; m != nil; m=m.next){ + if(m.subj != nil) + buf = sprint("%d\t%s\t %s", m.id, m.hdr[0:m.lline1], m.subj); + else + buf = sprint("%d\t%s", m.id, m.hdr[0:m.lline1]); + b.w.wwritebody(buf); + } + # b.w.wname("Mail/box/"); + if(b.readonly) + b.w.wtagwrite("Mail"); + else + b.w.wtagwrite("Put Mail"); + b.w.wsetdump("/acme/mail", "Mail box"); + b.w.wclean(); + b.w.wselect("0"); + b.w.wdormant(); + b.cdel= chan of ref Mesg; + b.cmore = chan of int; + b.clean = True; + return b; +} + +Box.readmore(b : self ref Box, lck : int) +{ + m : ref Mesg; + new : int; + buf : string; + + new = False; + leng := b.leng; + n := 0; + pop3open(lck); + pop3more(b); + while((m = m.read(b)) != nil){ + m.next = b.m; + b.m = m; + b.nm++; + n++; + m.id = b.nm; + if(m.subj != nil) + buf = sprint("%d\t%s\t %s", m.id, m.hdr[0:m.lline1], m.subj); + else + buf = sprint("%d\t%s", m.id, m.hdr[0:m.lline1]); + b.w.wreplace("0", buf); + new = True; + } + pop3close(1); + if (b.leng != leng+n) + error("bad message count in Box.readmore()"); + if(new){ + if(b.clean) + b.w.wclean(); + b.w.wselect("0;/.*(\\n[ \t].*)*"); + b.w.wshow(); + } + b.w.wdormant(); +} + +Box.readline(b : self ref Box) : string +{ + for (;;) { + if(b.peekline != nil){ + b.line = b.peekline; + b.peekline = nil; + }else + b.line = pop3next(b); + # nulls appear in mailboxes! + if(b.line != nil && strchr(b.line, 0) >= 0) + ; + else + break; + } + return b.line; +} + +Box.unreadline(b : self ref Box) +{ + b.peekline = b.line; +} + +Box.slave(b : self ref Box) +{ + e : ref Event; + m : ref Mesg; + + e = newevent(); + for(;;){ + alt{ + *e = <-b.cevent => + b.event(e); + break; + <-b.cmore => + b.readmore(1); + break; + m = <-b.cdel => + b.mdel(m); + break; + } + } +} + +Box.event(b : self ref Box, e : ref Event) +{ + e2, ea, eq : ref Event; + s : string; + t : int; + n, na, nopen : int; + + e2 = newevent(); + ea = newevent(); + case(e.c1){ + 'E' => # write to body; can't affect us + break; + 'F' => # generated by our actions; ignore + break; + 'K' => # type away; we don't care + break; + 'M' => + case(e.c2){ + 'x' or 'X' => + if(e.flag & 2) + *e2 = <-b.cevent; + if(e.flag & 8){ + *ea = <-b.cevent; + na = ea.nb; + <- b.cevent; + }else + na = 0; + s = string e.b[0:e.nb]; + # if it's a known command, do it + if((e.flag&2) && e.nb==0) + s = string e2.b[0:e2.nb]; + if(na) + s = sprint("%s %s", s, string ea.b[0:ea.nb]); + # if it's a long message, it can't be for us anyway + if(!b.command(s)) # send it back + b.w.wwriteevent(e); + if(na) + s = nil; + break; + 'l' or 'L' => + eq = e; + if(e.flag & 2){ + *e2 = <-b.cevent; + eq = e2; + } + s = string eq.b[0:eq.nb]; + if(eq.q1>eq.q0 && eq.nb==0) + s = b.w.wread(eq.q0, eq.q1); + nopen = 0; + do{ + t = 0; + (n, t) = strtoi(s); + if(n>0 && (t == len s || s[t]==' ' || s[t]=='\t' || s[t]=='\n')){ + b.mopen(n); + nopen++; + s = s[t:]; + } + while(s != nil && s[0]!='\n') + s = s[1:]; + }while(s != nil); + if(nopen == 0) # send it back + b.w.wwriteevent(e); + break; + 'I' or 'D' or 'd' or 'i' => # modify away; we don't care + break; + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } + * => + fprint(stdout, "unknown message %c%c\n", e.c1, e.c2); + break; + } +} + +Box.mopen(b : self ref Box, id : int) +{ + m : ref Mesg; + + for(m=b.m; m != nil; m=m.next) + if(m.id == id){ + m.open(); + break; + } +} + +Box.mdel(b : self ref Box, dm : ref Mesg) +{ + m : ref Mesg; + buf : string; + + if(dm.id){ + for(m=b.m; m!=nil && m!=dm; m=m.next) + ; + if(m == nil) + error(sprint("message %d not found", dm.id)); + m.deleted = 1; + # remove from screen: use acme to help + buf = sprint("/^%d .*\\n(^[ \t].*\\n)*/", m.id); + b.w.wreplace(buf, ""); + } + dm.free(); + b.clean = False; +} + +Box.command(b : self ref Box, s : string) : int +{ + t : int; + m : ref Mesg; + + while(s[0]==' ' || s[0]=='\t' || s[0]=='\n') + s = s[1:]; + if(len s >= 4 && s[0:4] == "Mail"){ + s = s[4:]; + while(s != nil && (s[0]==' ' || s[0]=='\t' || s[0]=='\n')) + s = s[1:]; + t = 0; + while(t < len s && s[t] && s[t]!=' ' && s[t]!='\t' && s[t]!='\n') + t++; + m = b.m; # avoid warning message on b.m.mkmail(...) + m.mkmail(b, s[0:t]); + return True; + } + if(s == "Del"){ + + if(!b.clean){ + b.clean = True; + fprint(stderr, "mail: mailbox not written\n"); + return True; + } + postnote(PNGROUP, pctl(0, nil), "kill"); + killing = 1; + pctl(NEWPGRP, nil); + b.w.wdel(True); + for(m=b.m; m != nil; m=m.next) + m.w.wdel(False); + exit; + return True; + } + if(s == "Put"){ + if(b.readonly) + fprint(stderr, "Mail is read-only\n"); + else + b.rewrite(); + return True; + } + return False; +} + +Box.rewrite(b : self ref Box) +{ + prev, m : ref Mesg; + + if(b.clean){ + b.w.wclean(); + return; + } + prev = nil; + pop3open(1); + for(m=b.m; m!=nil; m=m.next) { + if (m.deleted && pop3del(m.popno) >= 0) { + b.leng--; + if (prev == nil) + b.m=m.next; + else + prev.next=m.next; + } + else + prev = m; + } + # must update pop nos now so don't unlock pop3 + pop3close(0); + b.w.wclean(); + b.clean = True; + b.readmore(0); # updates pop nos +} diff --git a/appl/acme/acme/mail/src/mashfile b/appl/acme/acme/mail/src/mashfile new file mode 100644 index 00000000..6d9c433e --- /dev/null +++ b/appl/acme/acme/mail/src/mashfile @@ -0,0 +1,18 @@ +make -clear; + +MOD=module; +DISBIN=/acme/mail; +LFLAGS=-I/$MOD -gw; + +fn lcom { + limbo $LFLAGS $args; +}; + +TARG=mail.dis; + +*.dis :~ $1.b { lcom $1.b }; +$DISBIN/*.dis :~ $1.dis { cp $1.dis $DISBIN }; + +default : $TARG {}; +all : $TARG {}; +install : $DISBIN/*.dis {}; \ No newline at end of file diff --git a/appl/acme/acme/mail/src/mkfile b/appl/acme/acme/mail/src/mkfile new file mode 100644 index 00000000..3584e020 --- /dev/null +++ b/appl/acme/acme/mail/src/mkfile @@ -0,0 +1,16 @@ +<../../../../../mkconfig + +TARG=\ + Mail.dis\ + Mailpop3.dis\ + +MODULES=\ + +SYSMODULES=\ + sh.m\ + sys.m\ + draw.m\ + +DISBIN=$ROOT/acme/mail + +<$ROOT/mkfiles/mkdis diff --git a/appl/acme/acme/mkfile b/appl/acme/acme/mkfile new file mode 100644 index 00000000..fc75514b --- /dev/null +++ b/appl/acme/acme/mkfile @@ -0,0 +1,9 @@ +<../../../mkconfig + +DIRS=\ + acid\ + bin\ + edit\ + mail\ + +<$ROOT/mkfiles/mksubdirs \ No newline at end of file diff --git a/appl/acme/buff.b b/appl/acme/buff.b new file mode 100644 index 00000000..f5d18146 --- /dev/null +++ b/appl/acme/buff.b @@ -0,0 +1,380 @@ +implement Bufferm; + +include "common.m"; + +sys : Sys; +dat : Dat; +utils : Utils; +diskm : Diskm; +ecmd: Editcmd; + +FALSE, TRUE, XXX, Maxblock, Astring : import Dat; +Block : import Dat; +disk : import dat; +Disk : import diskm; +File: import Filem; +error, warning, min : import utils; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + dat = mods.dat; + utils = mods.utils; + diskm = mods.diskm; + ecmd = mods.editcmd; +} + +nullbuffer : Buffer; + +newbuffer() : ref Buffer +{ + b := ref nullbuffer; + return b; +} + +Slop : con 100; # room to grow with reallocation + +Buffer.sizecache(b : self ref Buffer, n : int) +{ + if(n <= b.cmax) + return; + b.cmax = n+Slop; + os := b.c; + b.c = utils->stralloc(b.cmax); + if (os != nil) { + loss := len os.s; + c := b.c; + oss := os.s; + for (i := 0; i < loss && i < b.cmax; i++) + c.s[i] = oss[i]; + utils->strfree(os); + } +} + +# +# Move cache so b.cq <= q0 < b.cq+b.cnc. +# If at very end, q0 will fall on end of cache block. +# + +Buffer.flush(b : self ref Buffer) +{ + if(b.cdirty || b.cnc==0){ + if(b.cnc == 0) + b.delblock(b.cbi); + else + b.bl[b.cbi] = disk.write(b.bl[b.cbi], b.c.s, b.cnc); + b.cdirty = FALSE; + } +} + +Buffer.setcache(b : self ref Buffer, q0 : int) +{ + blp, bl : ref Block; + i, q : int; + + if (q0 > b.nc) + error("bad assert in setcache"); + + # flush and reload if q0 is not in cache. + + if(b.nc == 0 || (b.cq<=q0 && q0= b.nbl) + error("block not found"); + } + bl = blp; + # remember position + b.cbi = i; + b.cq = q; + b.sizecache(bl.n); + b.cnc = bl.n; + #read block + disk.read(bl, b.c, b.cnc); +} + +Buffer.addblock(b : self ref Buffer, i : int, n : int) +{ + if (i > b.nbl) + error("bad assert in addblock"); + + obl := b.bl; + b.bl = array[b.nbl+1] of ref Block; + b.bl[0:] = obl[0:i]; + if(i < b.nbl) + b.bl[i+1:] = obl[i:b.nbl]; + b.bl[i] = disk.new(n); + b.nbl++; + obl = nil; +} + +Buffer.delblock(b : self ref Buffer, i : int) +{ + if (i >= b.nbl) + error("bad assert in delblock"); + + disk.release(b.bl[i]); + obl := b.bl; + b.bl = array[b.nbl-1] of ref Block; + b.bl[0:] = obl[0:i]; + if(i < b.nbl-1) + b.bl[i:] = obl[i+1:b.nbl]; + b.nbl--; + obl = nil; +} + +Buffer.insert(b : self ref Buffer, q0 : int, s : string, n : int) +{ + i, j, m, t, off, p : int; + + if (q0>b.nc) + error("bad assert in insert"); + p = 0; + while(n > 0){ + b.setcache(q0); + off = q0-b.cq; + if(b.cnc+n <= Maxblock){ + # Everything fits in one block. + t = b.cnc+n; + m = n; + if(b.bl == nil){ # allocate + if (b.cnc != 0) + error("bad assert in insert"); + b.addblock(0, t); + b.cbi = 0; + } + b.sizecache(t); + c := b.c; + # cs := c.s; + for (j = b.cnc-1; j >= off; j--) + c.s[j+m] = c.s[j]; + for (j = 0; j < m; j++) + c.s[off+j] = s[p+j]; + b.cnc = t; + } + # + # We must make a new block. If q0 is at + # the very beginning or end of this block, + # just make a new block and fill it. + # + else if(q0==b.cq || q0==b.cq+b.cnc){ + if(b.cdirty) + b.flush(); + m = min(n, Maxblock); + if(b.bl == nil){ # allocate + if (b.cnc != 0) + error("bad assert in insert"); + i = 0; + }else{ + i = b.cbi; + if(q0 > b.cq) + i++; + } + b.addblock(i, m); + b.sizecache(m); + c := b.c; + for (j = 0; j < m; j++) + c.s[j] = s[p+j]; + b.cq = q0; + b.cbi = i; + b.cnc = m; + } + else { + # + # Split the block; cut off the right side and + # let go of it. + # + + m = b.cnc-off; + if(m > 0){ + i = b.cbi+1; + b.addblock(i, m); + b.bl[i] = disk.write(b.bl[i], b.c.s[off:], m); + b.cnc -= m; + } + # + # Now at end of block. Take as much input + # as possible and tack it on end of block. + # + + m = min(n, Maxblock-b.cnc); + b.sizecache(b.cnc+m); + c := b.c; + for (j = 0; j < m; j++) + c.s[j+b.cnc] = s[p+j]; + b.cnc += m; + } + b.nc += m; + q0 += m; + p += m; + n -= m; + b.cdirty = TRUE; + } +} + +Buffer.delete(b : self ref Buffer, q0 : int, q1 : int) +{ + m, n, off : int; + + if (q0>q1 || q0>b.nc || q1>b.nc) + error("bad assert in delete"); + + while(q1 > q0){ + b.setcache(q0); + off = q0-b.cq; + if(q1 > b.cq+b.cnc) + n = b.cnc - off; + else + n = q1-q0; + m = b.cnc - (off+n); + if(m > 0) { + c := b.c; + # cs := c.s; + p := m+off; + for (j := off; j < p; j++) + c.s[j] = c.s[j+n]; + } + b.cnc -= n; + b.cdirty = TRUE; + q1 -= n; + b.nc -= n; + } +} + +# Buffer.replace(b: self ref Buffer, q0: int, q1: int, s: string, n: int) +# { +# if(q0>q1 || q0>b.nc || q1>b.nc || n != q1-q0) +# error("bad assert in replace"); +# p := 0; +# while(q1 > q0){ +# b.setcache(q0); +# off := q0-b.cq; +# if(q1 > b.cq+b.cnc) +# n = b.cnc-off; +# else +# n = q1-q0; +# c := b.c; +# for(i := 0; i < n; i++) +# c.s[i+off] = s[i+p]; +# b.cdirty = TRUE; +# q0 += n; +# p += n; +# } +# } + +pbuf : array of byte; + +bufloader(b: ref Buffer, q0: int, r: string, nr: int): int +{ + b.insert(q0, r, nr); + return nr; +} + +loadfile(fd: ref Sys->FD, q0: int, fun: int, b: ref Buffer, f: ref File): int +{ + p : array of byte; + r : string; + m, n, nb, nr : int; + q1 : int; + + if (pbuf == nil) + pbuf = array[Maxblock+Sys->UTFmax] of byte; + p = pbuf; + m = 0; + n = 1; + q1 = q0; + # + # At top of loop, may have m bytes left over from + # last pass, possibly representing a partial rune. + # + while(n > 0){ + n = sys->read(fd, p[m:], Maxblock); + if(n < 0){ + warning(nil, "read error in Buffer.load"); + break; + } + m += n; + nb = sys->utfbytes(p, m); + r = string p[0:nb]; + p[0:] = p[nb:m]; + m -= nb; + nr = len r; + if(fun == Dat->BUFL) + q1 += bufloader(b, q1, r, nr); + else + q1 += ecmd->readloader(f, q1, r, nr); + } + p = nil; + r = nil; + return q1-q0; +} + +Buffer.loadx(b : self ref Buffer, q0 : int, fd : ref Sys->FD) : int +{ + if (q0>b.nc) + error("bad assert in load"); + return loadfile(fd, q0, Dat->BUFL, b, nil); +} + +Buffer.read(b : self ref Buffer, q0 : int, s : ref Astring, p : int, n : int) +{ + m : int; + + if (q0>b.nc || q0+n>b.nc) + error("bad assert in read"); + while(n > 0){ + b.setcache(q0); + m = min(n, b.cnc-(q0-b.cq)); + c := b.c; + cs := c.s; + for (j := 0; j < m; j++) + s.s[p+j] = cs[j+q0-b.cq]; + q0 += m; + p += m; + n -= m; + } +} + +Buffer.reset(b : self ref Buffer) +{ + i : int; + + b.nc = 0; + b.cnc = 0; + b.cq = 0; + b.cdirty = 0; + b.cbi = 0; + # delete backwards to avoid n² behavior + for(i=b.nbl-1; --i>=0; ) + b.delblock(i); +} + +Buffer.close(b : self ref Buffer) +{ + b.reset(); + if (b.c != nil) { + utils->strfree(b.c); + b.c = nil; + } + b.cnc = 0; + b.bl = nil; + b.nbl = 0; +} diff --git a/appl/acme/buff.m b/appl/acme/buff.m new file mode 100644 index 00000000..606d9900 --- /dev/null +++ b/appl/acme/buff.m @@ -0,0 +1,34 @@ +Bufferm : module { + PATH : con "/dis/acme/buff.dis"; + + init : fn(mods : ref Dat->Mods); + + newbuffer : fn() : ref Buffer; + + Buffer : adt { + nc : int; + c : ref Dat->Astring; # cache + cnc : int; # bytes in cache + cmax : int; # size of allocated cache + cq : int; # position of cache + cdirty : int; # cache needs to be written + cbi : int; # index of cache Block + bl : array of ref Dat->Block; # array of blocks + nbl : int; # number of blocks + + insert : fn(b : self ref Buffer, n : int, s : string, m : int); + delete : fn(b : self ref Buffer, n : int, m : int); + # replace : fn(b : self ref Buffer, q0 : int, q1 : int, s : string, n : int); + loadx : fn(b : self ref Buffer, n : int, fd : ref Sys->FD) : int; + read : fn(b : self ref Buffer, n : int, s : ref Dat->Astring, p, m : int); + close : fn(b : self ref Buffer); + reset : fn(b : self ref Buffer); + sizecache : fn(b : self ref Buffer, n : int); + flush : fn(b : self ref Buffer); + setcache : fn(b : self ref Buffer, n : int); + addblock : fn(b : self ref Buffer, n : int, m : int); + delblock : fn(b : self ref Buffer, n : int); + }; + + loadfile: fn(fd: ref Sys->FD, q1: int, fun: int, b: ref Bufferm->Buffer, f: ref Filem->File): int; +}; diff --git a/appl/acme/col.b b/appl/acme/col.b new file mode 100644 index 00000000..c696fc5d --- /dev/null +++ b/appl/acme/col.b @@ -0,0 +1,610 @@ +implement Columnm; + +include "common.m"; + +sys : Sys; +utils : Utils; +drawm : Draw; +acme : Acme; +graph : Graph; +gui : Gui; +dat : Dat; +textm : Textm; +rowm : Rowm; +filem : Filem; +windowm : Windowm; + +FALSE, TRUE, XXX : import Dat; +Border : import Dat; +mouse, colbutton : import dat; +Point, Rect, Image : import drawm; +draw : import graph; +min, max, abs, error, clearmouse : import utils; +black, white, mainwin : import gui; +Text : import textm; +Row : import rowm; +Window : import windowm; +File : import filem; +Columntag : import Textm; +BACK : import Framem; +tagcols, textcols : import acme; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + dat = mods.dat; + utils = mods.utils; + drawm = mods.draw; + acme = mods.acme; + graph = mods.graph; + gui = mods.gui; + textm = mods.textm; + rowm = mods.rowm; + filem = mods.filem; + windowm = mods.windowm; +} + +Column.init(c : self ref Column, r : Rect) +{ + r1 : Rect; + t : ref Text; + dummy : ref File = nil; + + draw(mainwin, r, white, nil, (0, 0)); + c.r = r; + c.row = nil; + c.w = nil; + c.nw = 0; + c.tag = textm->newtext(); + t = c.tag; + t.w = nil; + t.col = c; + r1 = r; + r1.max.y = r1.min.y + (graph->font).height; + t.init(dummy.addtext(t), r1, dat->reffont, tagcols); + t.what = Columntag; + r1.min.y = r1.max.y; + r1.max.y += Border; + draw(mainwin, r1, black, nil, (0, 0)); + t.insert(0, "New Cut Paste Snarf Sort Zerox Delcol ", 38, TRUE, 0); + t.setselect(t.file.buf.nc, t.file.buf.nc); + draw(mainwin, t.scrollr, colbutton, nil, colbutton.r.min); + c.safe = TRUE; +} + +Column.add(c : self ref Column, w : ref Window, clone : ref Window, y : int) : ref Window +{ + r, r1 : Rect; + v : ref Window; + i, t : int; + + v = nil; + r = c.r; + r.min.y = c.tag.frame.r.max.y+Border; + if(y0){ # steal half of last window by default + v = c.w[c.nw-1]; + y = v.body.frame.r.min.y+v.body.frame.r.dy()/2; + } + # look for window we'll land on + for(i=0; i 0){ + if(i < c.nw) + i++; # new window will go after v + # + # if v's too small, grow it first. + # + + if(!c.safe || v.body.frame.maxlines<=3){ + c.grow(v, 1, 1); + y = v.body.frame.r.min.y+v.body.frame.r.dy()/2; + } + r = v.r; + if(i == c.nw) + t = c.r.max.y; + else + t = c.w[i].r.min.y-Border; + r.max.y = t; + draw(mainwin, r, textcols[BACK], nil, (0, 0)); + r1 = r; + y = min(y, t-(v.tag.frame.font.height+v.body.frame.font.height+Border+1)); + r1.max.y = min(y, v.body.frame.r.min.y+v.body.frame.nlines*v.body.frame.font.height); + r1.min.y = v.reshape(r1, FALSE); + r1.max.y = r1.min.y+Border; + draw(mainwin, r1, black, nil, (0, 0)); + r.min.y = r1.max.y; + } + if(w == nil){ + w = ref Window; + draw(mainwin, r, textcols[BACK], nil, (0, 0)); + w.col = c; + w.init(clone, r); + }else{ + w.col = c; + w.reshape(r, FALSE); + } + w.tag.col = c; + w.tag.row = c.row; + w.body.col = c; + w.body.row = c.row; + ocw := c.w; + c.w = array[c.nw+1] of ref Window; + c.w[0:] = ocw[0:i]; + c.w[i+1:] = ocw[i:c.nw]; + ocw = nil; + c.nw++; + c.w[i] = w; + utils->savemouse(w); + # near but not on the button + graph->cursorset(w.tag.scrollr.max.add(Point(3, 3))); + dat->barttext = w.body; + c.safe = TRUE; + return w; +} + +Column.close(c : self ref Column, w : ref Window, dofree : int) +{ + r : Rect; + i : int; + + # w is locked + if(!c.safe) + c.grow(w, 1, 1); + for(i=0; irestoremouse(w); + if(dofree){ + w.delete(); + w.close(); + } + ocw := c.w; + c.w = array[c.nw-1] of ref Window; + c.w[0:] = ocw[0:i]; + c.w[i:] = ocw[i+1:c.nw]; + ocw = nil; + c.nw--; + if(c.nw == 0){ + draw(mainwin, r, white, nil, (0, 0)); + return; + } + if(i == c.nw){ # extend last window down + w = c.w[i-1]; + r.min.y = w.r.min.y; + r.max.y = c.r.max.y; + }else{ # extend next window up + w = c.w[i]; + r.max.y = w.r.max.y; + } + draw(mainwin, r, textcols[BACK], nil, (0, 0)); + if(c.safe) + w.reshape(r, FALSE); +} + +Column.closeall(c : self ref Column) +{ + i : int; + w : ref Window; + + if(c == dat->activecol) + dat->activecol = nil; + c.tag.close(); + for(i=0; icursorset(c.tag.scrollr.min.add(c.tag.scrollr.max).div(2)); +} + +Column.reshape(c : self ref Column, r : Rect) +{ + i : int; + r1, r2 : Rect; + w : ref Window; + + clearmouse(); + r1 = r; + r1.max.y = r1.min.y + c.tag.frame.font.height; + c.tag.reshape(r1); + draw(mainwin, c.tag.scrollr, colbutton, nil, colbutton.r.min); + r1.min.y = r1.max.y; + r1.max.y += Border; + draw(mainwin, r1, black, nil, (0, 0)); + r1.max.y = r.max.y; + for(i=0; i r2) + return 1; + return 0; +} + +qsort(a : array of ref Window, n : int) +{ + i, j : int; + t : ref Window; + + while(n > 1) { + i = n>>1; + t = a[0]; a[0] = a[i]; a[i] = t; + i = 0; + j = n; + for(;;) { + do + i++; + while(i < n && colcmp(a[i], a[0]) < 0); + do + j--; + while(j > 0 && colcmp(a[j], a[0]) > 0); + if(j < i) + break; + t = a[i]; a[i] = a[j]; a[j] = t; + } + t = a[0]; a[0] = a[j]; a[j] = t; + n = n-j-1; + if(j >= n) { + qsort(a, j); + a = a[j+1:]; + } else { + qsort(a[j+1:], n); + n = j; + } + } +} + +Column.sort(c : self ref Column) +{ + i, y : int; + r, r1 : Rect; + rp : array of Rect; + w : ref Window; + wp : array of ref Window; + + if(c.nw == 0) + return; + clearmouse(); + rp = array[c.nw] of Rect; + wp = array[c.nw] of ref Window; + wp[0:] = c.w[0:c.nw]; + qsort(wp, c.nw); + for(i=0; i=0 && nl[j]){ + l = min(dnl, max(1, nl[j]/2)); + nl[j] -= l; + nl[i] += l; + dnl -= l; + } + } + } + # pack everyone above + y1 = cr.min.y; + for(j=0; ji; j--){ + v = c.w[j]; + r = v.r; + r.min.y = y2-v.tag.all.dy(); + if(nl[j]) + r.min.y -= 1 + nl[j]*v.body.frame.font.height; + r.min.y -= Border; + ny[j] = r.min.y; + y2 = r.min.y; + } + # compute new size of window + r = w.r; + r.min.y = y1; + r.max.y = r.min.y+w.tag.all.dy(); + h = w.body.frame.font.height; + if(y2-r.max.y >= 1+h+Border){ + r.max.y += 1; + r.max.y += h*((y2-r.max.y)/h); + } + # draw window + if(!c.safe || !w.r.eq(r)){ + draw(mainwin, r, textcols[BACK], nil, (0, 0)); + w.reshape(r, c.safe); + } + if(i < c.nw-1){ + r.min.y = r.max.y; + r.max.y += Border; + draw(mainwin, r, black, nil, (0, 0)); + for(j=i+1; jcursorswitch(dat->boxcursor); + b = mouse.buttons; + op = mouse.xy; + while(mouse.buttons == b) + acme->frgetmouse(); + graph->cursorswitch(dat->arrowcursor); + if(mouse.buttons){ + while(mouse.buttons) + acme->frgetmouse(); + return; + } + + for(i=0; iop.x+30 && c.row.whichcol(p) == c) + p.x += w.r.dx(); # yes: toss to next column + nc = c.row.whichcol(p); + if(nc!=nil && nc!=c){ + c.close(w, FALSE); + nc.add(w, nil, p.y); + w.mousebut(); + return; + } + if(i==0 && c.nw==1) + return; # can't do it + if((i>0 && p.yw.r.max.y) + || (i==0 && p.y>w.r.max.y)){ + # shuffle + c.close(w, FALSE); + c.add(w, nil, p.y); + w.mousebut(); + return; + } + if(i == 0) + return; + v = c.w[i-1]; + if(p.y < v.tag.all.max.y) + p.y = v.tag.all.max.y; + if(p.y > w.r.max.y-w.tag.all.dy()-Border) + p.y = w.r.max.y-w.tag.all.dy()-Border; + r = v.r; + r.max.y = p.y; + if(r.max.y > v.body.frame.r.min.y){ + r.max.y -= (r.max.y-v.body.frame.r.min.y)%v.body.frame.font.height; + if(v.body.frame.r.min.y == v.body.frame.r.max.y) + r.max.y++; + } + if(!r.eq(v.r)){ + draw(mainwin, r, textcols[BACK], nil, (0, 0)); + v.reshape(r, c.safe); + } + r.min.y = v.r.max.y; + r.max.y = r.min.y+Border; + draw(mainwin, r, black, nil, (0, 0)); + r.min.y = r.max.y; + if(i == c.nw-1) + r.max.y = c.r.max.y; + else + r.max.y = c.w[i+1].r.min.y-Border; + # r.max.y = w.r.max.y; + if(!r.eq(w.r)){ + draw(mainwin, r, textcols[BACK], nil, (0, 0)); + w.reshape(r, c.safe); + } + c.safe = TRUE; + w.mousebut(); +} + +Column.which(c : self ref Column, p : Point) : ref Text +{ + i : int; + w : ref Window; + + if(!p.in(c.r)) + return nil; + if(p.in(c.tag.all)) + return c.tag; + for(i=0; iMods); + + Column : adt { + r : Draw->Rect; + tag : cyclic ref Textm->Text; + row : cyclic ref Rowm->Row; + w : cyclic array of ref Windowm->Window; + nw : int; + safe : int; + + init : fn (c : self ref Column, r : Draw->Rect); + add : fn (c : self ref Column, w : ref Windowm->Window, w0 : ref Windowm->Window, n : int) : ref Windowm->Window; + close : fn (c : self ref Column, w : ref Windowm->Window, n : int); + closeall : fn (c : self ref Column); + reshape : fn (c : self ref Column, r : Draw->Rect); + which : fn (c : self ref Column, p : Draw->Point) : ref Textm->Text; + dragwin : fn (c : self ref Column, w : ref Windowm->Window, n : int); + grow : fn (c : self ref Column, w : ref Windowm->Window, m, n : int); + clean : fn (c : self ref Column, exiting : int) : int; + sort : fn (c : self ref Column); + mousebut : fn (c : self ref Column); + }; +}; diff --git a/appl/acme/common.m b/appl/acme/common.m new file mode 100644 index 00000000..6a64396b --- /dev/null +++ b/appl/acme/common.m @@ -0,0 +1,30 @@ +include "sys.m"; +include "bufio.m"; +include "plumbmsg.m"; +include "workdir.m"; +include "draw.m"; +include "styx.m"; +include "acme.m"; +include "dat.m"; +include "gui.m"; +include "graph.m"; +include "frame.m"; +include "util.m"; +include "regx.m"; +include "text.m"; +include "file.m"; +include "wind.m"; +include "row.m"; +include "col.m"; +include "buff.m"; +include "disk.m"; +include "xfid.m"; +include "exec.m"; +include "look.m"; +include "time.m"; +include "scrl.m"; +include "fsys.m"; +include "edit.m"; +include "elog.m"; +include "ecmd.m"; +include "styxaux.m"; diff --git a/appl/acme/dat.b b/appl/acme/dat.b new file mode 100644 index 00000000..924d16e6 --- /dev/null +++ b/appl/acme/dat.b @@ -0,0 +1,107 @@ +implement Dat; + +include "common.m"; + +sys : Sys; +acme : Acme; +utils : Utils; + +# lc, uc : chan of ref Lock; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + acme = mods.acme; + utils = mods.utils; + + mouse = ref Draw->Pointer; + mouse.buttons = mouse.msec = 0; + mouse.xy = (0, 0); + # lc = chan of ref Lock; + # uc = chan of ref Lock; + # spawn lockmgr(); +} + +# lockmgr() +# { +# l : ref Lock; +# +# acme->lockpid = sys->pctl(0, nil); +# for (;;) { +# alt { +# l = <- lc => +# if (l.cnt++ == 0) +# l.chann <-= 1; +# l = <- uc => +# if (--l.cnt > 0) +# l.chann <-= 1; +# } +# } +# } + +Lock.init() : ref Lock +{ + return ref Lock(0, chan[1] of int); + # return ref Lock(0, chan of int); +} + +Lock.lock(l : self ref Lock) +{ + l.cnt++; + l.chann <-= 0; + # lc <-= l; + # <- l.chann; +} + +Lock.unlock(l : self ref Lock) +{ + <-l.chann; + l.cnt--; + # uc <-= l; +} + +Lock.locked(l : self ref Lock) : int +{ + return l.cnt > 0; +} + +Ref.init() : ref Ref +{ + r := ref Ref; + r.l = Lock.init(); + r.cnt = 0; + return r; +} + +Ref.inc(r : self ref Ref) : int +{ + r.l.lock(); + i := r.cnt; + r.cnt++; + r.l.unlock(); + return i; +} + +Ref.dec(r : self ref Ref) : int +{ + r.l.lock(); + r.cnt--; + i := r.cnt; + r.l.unlock(); + return i; +} + +Ref.refx(r : self ref Ref) : int +{ + return r.cnt; +} + +Reffont.get(p, q, r : int, b : string) : ref Reffont +{ + return acme->get(p, q, r, b); +} + +Reffont.close(r : self ref Reffont) +{ + return acme->close(r); +} \ No newline at end of file diff --git a/appl/acme/dat.m b/appl/acme/dat.m new file mode 100644 index 00000000..66a0f592 --- /dev/null +++ b/appl/acme/dat.m @@ -0,0 +1,280 @@ +Dat : module { + PATH : con "/dis/acme/dat.dis"; + + init : fn(mods : ref Mods); + + Mods : adt { + sys : Sys; + bufio : Bufio; + draw : Draw; + styx : Styx; + styxaux : Styxaux; + acme : Acme; + gui : Gui; + graph : Graph; + dat : Dat; + framem : Framem; + utils : Utils; + regx : Regx; + scroll : Scroll; + textm : Textm; + filem : Filem; + windowm : Windowm; + rowm : Rowm; + columnm : Columnm; + bufferm : Bufferm; + diskm : Diskm; + exec : Exec; + look : Look; + timerm : Timerm; + fsys : Fsys; + xfidm : Xfidm; + plumbmsg : Plumbmsg; + edit: Edit; + editlog: Editlog; + editcmd: Editcmd; + }; + + SZSHORT : con 2; + SZINT : con 4; + + FALSE, TRUE, XXX : con iota; + + EM_NORMAL, EM_RAW, EM_MASK : con iota; + + Qdir,Qacme,Qcons,Qconsctl,Qdraw,Qeditout,Qindex,Qlabel,Qnew,QWaddr,QWbody,QWconsctl,QWctl,QWdata,QWeditout,QWevent,QWrdsel,QWwrsel,QWtag,QMAX : con iota; + + Blockincr : con 256; + Maxblock : con 8*1024; + NRange : con 10; + Infinity : con 16r7fffffff; # huge value for regexp address + + # fbufalloc() guarantees room off end of BUFSIZE + MAXRPC : con 8192+Styx->IOHDRSZ; + BUFSIZE : con MAXRPC; + EVENTSIZE : con 256; + PLUMBSIZE : con 1024; + Scrollwid : con 12; # width of scroll bar + Scrollgap : con 4; # gap right of scroll bar + Margin : con 4; # margin around text + Border : con 2; # line between rows, cols, windows + Maxtab : con 4; # size of a tab, in units of the '0' character + + Empty: con 0; + Null : con '-'; + Delete : con 'd'; + Insert : con 'i'; + Replace: con 'r'; + Filename : con 'f'; + + # editing + Inactive, Inserting, Collecting: con iota; + + # alphabets + ALPHA_LATIN: con '\0'; + ALPHA_GREEK: con '*'; + ALPHA_CYRILLIC: con '@'; + + Astring : adt { + s : string; + }; + + Lock : adt { + cnt : int; + chann : chan of int; + + init : fn() : ref Lock; + lock : fn(l : self ref Lock); + unlock : fn(l : self ref Lock); + locked : fn(l : self ref Lock) : int; + }; + +# Lockx : adt { +# sem : ref Lock->Semaphore; +# +# init : fn() : ref Lockx; +# lock : fn(l : self ref Lockx); +# unlock : fn(l : self ref Lockx); +# }; + + Ref : adt { + l : ref Lock; + cnt : int; + + init : fn() : ref Ref; + inc : fn(r : self ref Ref) : int; + dec : fn(r : self ref Ref) : int; + refx : fn(r : self ref Ref) : int; + }; + + Runestr : adt { + r: string; + nr: int; + }; + + Range : adt { + q0 : int; + q1 : int; + }; + + Block : adt { + addr : int; # disk address in bytes + n : int; # number of used runes in block + next : cyclic ref Block; # pointer to next in free list + }; + + Timer : adt { + dt : int; + c : chan of int; + next : cyclic ref Timer; + }; + + Command : adt { + pid : int; + name : string; + text : string; + av : list of string; + iseditcmd: int; + md : ref Mntdir; + next : cyclic ref Command; + }; + + Dirtab : adt { + name : string; + qtype : int; + qid : int; + perm : int; + }; + + Mntdir : adt { + id : int; + refs : int; + dir : string; + ndir : int; + next : cyclic ref Mntdir; + nincl : int; + incl : array of string; + }; + + Fid : adt { + fid : int; + busy : int; + open : int; + qid : Sys->Qid; + w : cyclic ref Windowm->Window; + dir : array of Dirtab; + next : cyclic ref Fid; + mntdir : ref Mntdir; + nrpart : int; + rpart : array of byte; + }; + + Rangeset : type array of Range; + + Expand : adt { + q0 : int; + q1 : int; + name : string; + bname : string; + jump : int; + at : ref Textm->Text; + ar : string; + a0 : int; + a1 : int; + }; + + Dirlist : adt { + r : string; + wid : int; + }; + + Reffont : adt { + r : ref Ref; + f : ref Draw->Font; + + get : fn(p : int, q : int, r : int, b : string) : ref Reffont; + close : fn(r : self ref Reffont); + }; + + Cursor : adt { + hot : Draw->Point; + size : Draw->Point; + bits : array of byte; + }; + + Smsg0 : adt { + msize : int; + version : string; + iounit: int; + qid : Sys->Qid; + count : int; + data : array of byte; + stat : Sys->Dir; + qids: array of Sys->Qid; + }; + + # loadfile function ptr + + BUFL, READL: con iota; + + # allwindows pick type + + Looper: adt{ + cp: ref Edit->Cmd; + XY: int; + w: array of ref Windowm->Window; + nw: int; + }; # only one; X and Y can't nest + + Tofile: adt { + f: ref Filem->File; + r: ref Edit->String; + }; + + Filecheck: adt{ + f: ref Filem->File; + r: string; + nr: int; + }; + + Allwin: adt{ + pick{ + LP => lp: ref Looper; + FF => ff: ref Tofile; + FC => fc: ref Filecheck; + } + }; + + seq : int; + maxtab : int; + mouse : ref Draw->Pointer; + reffont : ref Reffont; + modbutton : ref Draw->Image; + colbutton : ref Draw->Image; + button : ref Draw->Image; + arrowcursor, boxcursor : ref Cursor; + row : ref Rowm->Row; + disk : ref Diskm->Disk; + seltext : ref Textm->Text; + argtext : ref Textm->Text; + mousetext : ref Textm->Text; # global because Text.close needs to clear it + typetext : ref Textm->Text; # ditto + barttext : ref Textm->Text; # shared between mousetask and keyboardtask + bartflag : int; + activewin : ref Windowm->Window; + activecol : ref Columnm->Column; + nullrect : Draw->Rect; + home : string; + plumbed : int; + + ckeyboard : chan of int; + cmouse : chan of ref Draw->Pointer; + cwait : chan of string; + ccommand : chan of ref Command; + ckill : chan of string; + cxfidalloc : chan of ref Xfidm->Xfid; + cxfidfree : chan of ref Xfidm->Xfid; + cerr : chan of string; + cplumb : chan of ref Plumbmsg->Msg; + cedit: chan of int; +}; diff --git a/appl/acme/disk.b b/appl/acme/disk.b new file mode 100644 index 00000000..932254c1 --- /dev/null +++ b/appl/acme/disk.b @@ -0,0 +1,136 @@ +implement Diskm; + +include "common.m"; + +sys : Sys; +acme : Acme; +utils : Utils; + +SZSHORT, Block, Blockincr, Astring : import Dat; +error : import utils; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + acme = mods.acme; + utils = mods.utils; +} + +blist : ref Block; + +tempfile() : ref Sys->FD +{ + buf := sys->sprint("/tmp/X%d.%.4sacme", sys->pctl(0, nil), utils->getuser()); + for(i:='A'; i<='Z'; i++){ + buf[5] = i; + (ok, nil) := sys->stat(buf); + if(ok == 0) + continue; + fd := sys->create(buf, Sys->ORDWR|Sys->ORCLOSE, 8r600); + if(fd != nil) + return fd; + } + return nil; +} + +Disk.init() : ref Disk +{ + d : ref Disk; + + d = ref Disk; + d.free = array[Dat->Maxblock/Dat->Blockincr+1] of ref Block; + d.addr = 0; + d.fd = tempfile(); + if(d.fd == nil){ + error(sys->sprint("can't create temp file %r")); + acme->acmeexit("temp create"); + } + return d; +} + +ntosize(n : int) : (int, int) +{ + size : int; + + if (n > Dat->Maxblock) + error("bad assert in ntosize"); + size = n; + if(size & (Blockincr-1)) + size += Blockincr - (size & (Blockincr-1)); + # last bucket holds blocks of exactly Maxblock + return (size * SZSHORT, size/Blockincr); +} + +Disk.new(d : self ref Disk, n : int) : ref Block +{ + i, j, size : int; + b, bl : ref Block; + + (size, i) = ntosize(n); + b = d.free[i]; + if(b != nil) + d.free[i] = b.next; + else{ + # allocate in chunks to reduce malloc overhead + if(blist == nil){ + blist = ref Block; + bl = blist; + for(j=0; j<100-1; j++) { + bl.next = ref Block; + bl = bl.next; + } + } + b = blist; + blist = b.next; + b.addr = d.addr; + d.addr += size; + } + b.n = n; + return b; +} + +Disk.release(d : self ref Disk, b : ref Block) +{ + (nil, i) := ntosize(b.n); + b.next = d.free[i]; + d.free[i] = b; +} + +Disk.write(d : self ref Disk, bp : ref Block, r : string, n : int) : ref Block +{ + size, nsize, i : int; + b : ref Block; + ab : array of byte; + + b = bp; + (size, i) = ntosize(b.n); + (nsize, i) = ntosize(n); + if(size != nsize){ + d.release(b); + b = d.new(n); + } + if(sys->seek(d.fd, big b.addr, 0) < big 0) + error("seek error in temp file"); + ab = utils->stob(r, n); + if(sys->write(d.fd, ab, len ab) != len ab) + error("write error to temp file"); + ab = nil; + b.n = n; + return b; +} + +Disk.read(d : self ref Disk, b : ref Block, r : ref Astring, n : int) +{ + ab : array of byte; + + if (n > b.n) + error("bad assert in Disk.read"); + (nil, nil) := ntosize(b.n); + if(sys->seek(d.fd, big b.addr, 0) < big 0) + error("seek error in temp file"); + ab = array[n*SZSHORT] of byte; + if(sys->read(d.fd, ab, len ab) != len ab) + error("read error from temp file"); + utils->btos(ab, r); + ab = nil; +} diff --git a/appl/acme/disk.m b/appl/acme/disk.m new file mode 100644 index 00000000..66b2a2ba --- /dev/null +++ b/appl/acme/disk.m @@ -0,0 +1,19 @@ +Diskm : module { + PATH : con "/dis/acme/disk.dis"; + + init : fn(mods : ref Dat->Mods); + + Disk : adt { + fd : ref Sys->FD; + addr : int; # length of temp file + free : array of ref Dat->Block; + + init : fn() : ref Disk; + new : fn(d : self ref Disk, n : int) : ref Dat->Block; + release : fn(d : self ref Disk, b : ref Dat->Block); + read : fn(d : self ref Disk, b : ref Dat->Block, s : ref Dat->Astring, n : int); + write : fn(d : self ref Disk, b : ref Dat->Block, s : string, n : int) : ref Dat->Block; + }; + + tempfile: fn() : ref Sys->FD; +}; diff --git a/appl/acme/ecmd.b b/appl/acme/ecmd.b new file mode 100644 index 00000000..c7cc2408 --- /dev/null +++ b/appl/acme/ecmd.b @@ -0,0 +1,1349 @@ +implement Editcmd; + +include "common.m"; + +sys: Sys; +utils: Utils; +edit: Edit; +editlog: Editlog; +windowm: Windowm; +look: Look; +columnm: Columnm; +bufferm: Bufferm; +exec: Exec; +dat: Dat; +textm: Textm; +regx: Regx; +filem: Filem; +rowm: Rowm; + +Dir: import Sys; +Allwin, Filecheck, Tofile, Looper, Astring: import Dat; +aNo, aDot, aAll: import Edit; +C_nl, C_a, C_b, C_c, C_d, C_B, C_D, C_e, C_f, C_g, C_i, C_k, C_m, C_n, C_p, C_s, C_u, C_w, C_x, C_X, C_pipe, C_eq: import Edit; +TRUE, FALSE: import Dat; +Inactive, Inserting, Collecting: import Dat; +BUFSIZE, Runestr: import Dat; +Addr, Address, String, Cmd: import Edit; +Window: import windowm; +File: import filem; +NRange, Range, Rangeset: import Dat; +Text: import textm; +Column: import columnm; +Buffer: import bufferm; + +sprint: import sys; +elogterm, elogclose, eloginsert, elogdelete, elogreplace, elogapply: import editlog; +cmdtab, allocstring, freestring, Straddc, curtext, editing, newaddr, cmdlookup, editerror: import edit; +error, stralloc, strfree, warning, skipbl, findbl: import utils; +lookfile, cleanname, dirname: import look; +undo, run: import exec; +Ref, Lock, row, cedit: import dat; +rxcompile, rxexecute, rxbexecute: import regx; +allwindows: import rowm; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + utils = mods.utils; + edit = mods.edit; + editlog = mods.editlog; + windowm = mods.windowm; + look = mods.look; + columnm = mods.columnm; + bufferm = mods.bufferm; + exec = mods.exec; + dat = mods.dat; + textm = mods.textm; + regx = mods.regx; + filem = mods.filem; + rowm = mods.rowm; + + none.r.q0 = none.r.q1 = 0; + none.f = nil; +} + +cmdtabexec(i: int, t: ref Text, cp: ref Cmd): int +{ + case (cmdtab[i].fnc){ + C_nl => i = nl_cmd(t, cp); + C_a => i = a_cmd(t, cp); + C_b => i = b_cmd(t, cp); + C_c => i = c_cmd(t, cp); + C_d => i = d_cmd(t, cp); + C_e => i = e_cmd(t, cp); + C_f => i = f_cmd(t, cp); + C_g => i = g_cmd(t, cp); + C_i => i = i_cmd(t, cp); + C_m => i = m_cmd(t, cp); + C_p => i = p_cmd(t, cp); + C_s => i = s_cmd(t, cp); + C_u => i = u_cmd(t, cp); + C_w => i = w_cmd(t, cp); + C_x => i = x_cmd(t, cp); + C_eq => i = eq_cmd(t, cp); + C_B => i = B_cmd(t, cp); + C_D => i = D_cmd(t, cp); + C_X => i = X_cmd(t, cp); + C_pipe => i = pipe_cmd(t, cp); + * => error("bad case in cmdtabexec"); + } + return i; +} + +Glooping: int; +nest: int; +Enoname := "no file name given"; + +addr: Address; +menu: ref File; +sel: Rangeset; +collection: string; +ncollection: int; + +clearcollection() +{ + collection = nil; + ncollection = 0; +} + +resetxec() +{ + Glooping = nest = 0; + clearcollection(); +} + +mkaddr(f: ref File): Address +{ + a: Address; + + a.r.q0 = f.curtext.q0; + a.r.q1 = f.curtext.q1; + a.f = f; + return a; +} + +none: Address; + +cmdexec(t: ref Text, cp: ref Cmd): int +{ + i: int; + ap: ref Addr; + f: ref File; + w: ref Window; + dot: Address; + + if(t == nil) + w = nil; + else + w = t.w; + if(w==nil && (cp.addr==nil || cp.addr.typex!='"') && + utils->strchr("bBnqUXY!", cp.cmdc) < 0&& + !(cp.cmdc=='D' && cp.text!=nil)) + editerror("no current window"); + i = cmdlookup(cp.cmdc); # will be -1 for '{' + f = nil; + if(t!=nil && t.w!=nil){ + t = t.w.body; + f = t.file; + f.curtext = t; + } + if(i>=0 && cmdtab[i].defaddr != aNo){ + if((ap=cp.addr)==nil && cp.cmdc!='\n'){ + cp.addr = ap = newaddr(); + ap.typex = '.'; + if(cmdtab[i].defaddr == aAll) + ap.typex = '*'; + }else if(ap!=nil && ap.typex=='"' && ap.next==nil && cp.cmdc!='\n'){ + ap.next = newaddr(); + ap.next.typex = '.'; + if(cmdtab[i].defaddr == aAll) + ap.next.typex = '*'; + } + if(cp.addr!=nil){ # may be false for '\n' (only) + if(f!=nil){ + dot = mkaddr(f); + addr = cmdaddress(ap, dot, 0); + }else # a " + addr = cmdaddress(ap, none, 0); + f = addr.f; + t = f.curtext; + } + } + case(cp.cmdc){ + '{' => + dot = mkaddr(f); + if(cp.addr != nil) + dot = cmdaddress(cp.addr, dot, 0); + for(cp = cp.cmd; cp!=nil; cp = cp.next){ + t.q0 = dot.r.q0; + t.q1 = dot.r.q1; + cmdexec(t, cp); + } + break; + * => + if(i < 0) + editerror(sprint("unknown command %c in cmdexec", cp.cmdc)); + i = cmdtabexec(i, t, cp); + return i; + } + return 1; +} + +edittext(f: ref File, q: int, r: string, nr: int): string +{ + case(editing){ + Inactive => + return "permission denied"; + Inserting => + eloginsert(f, q, r, nr); + return nil; + Collecting => + collection += r[0: nr]; + ncollection += nr; + return nil; + * => + return "unknown state in edittext"; + } +} + +# string is known to be NUL-terminated +filelist(t: ref Text, r: string, nr: int): string +{ + if(nr == 0) + return nil; + (r, nr) = skipbl(r, nr); + if(r[0] != '<') + return r; + # use < command to collect text + clearcollection(); + runpipe(t, '<', r[1:], nr-1, Collecting); + return collection; +} + +a_cmd(t: ref Text, cp: ref Cmd): int +{ + return append(t.file, cp, addr.r.q1); +} + +b_cmd(nil: ref Text, cp: ref Cmd): int +{ + f: ref File; + + f = tofile(cp.text); + if(nest == 0) + pfilename(f); + curtext = f.curtext; + return TRUE; +} + +B_cmd(t: ref Text, cp: ref Cmd): int +{ + listx, r, s: string; + nr: int; + + listx = filelist(t, cp.text.r, cp.text.n); + if(listx == nil) + editerror(Enoname); + r = listx; + nr = len r; + (r, nr) = skipbl(r, nr); + if(nr == 0) + look->new(t, t, nil, 0, 0, r, 0); + else while(nr > 0){ + (s, nr) = findbl(r, nr); + look->new(t, t, nil, 0, 0, r, len r); + if(nr > 0) + (r, nr) = skipbl(s[1:], nr-1); + } + clearcollection(); + return TRUE; +} + +c_cmd(t: ref Text, cp: ref Cmd): int +{ + elogreplace(t.file, addr.r.q0, addr.r.q1, cp.text.r, cp.text.n); + return TRUE; +} + +d_cmd(t: ref Text, nil: ref Cmd): int +{ + if(addr.r.q1 > addr.r.q0) + elogdelete(t.file, addr.r.q0, addr.r.q1); + return TRUE; +} + +D1(t: ref Text) +{ + if(t.w.body.file.ntext>1 || t.w.clean(FALSE, FALSE)) + t.col.close(t.w, TRUE); +} + +D_cmd(t: ref Text, cp: ref Cmd): int +{ + listx, r, s, n: string; + nr, nn: int; + w: ref Window; + dir, rs: Runestr; + buf: string; + + listx = filelist(t, cp.text.r, cp.text.n); + if(listx == nil){ + D1(t); + return TRUE; + } + dir = dirname(t, nil, 0); + r = listx; + nr = len r; + (r, nr) = skipbl(r, nr); + do{ + (s, nr) = findbl(r, nr); + # first time through, could be empty string, meaning delete file empty name + nn = len r; + if(r[0]=='/' || nn==0 || dir.nr==0){ + rs.r = r; + rs.nr = nn; + }else{ + n = dir.r + "/" + r; + rs = cleanname(n, dir.nr+1+nn); + } + w = lookfile(rs.r, rs.nr); + if(w == nil){ + buf = sprint("no such file %s", rs.r); + rs.r = nil; + editerror(buf); + } + rs.r = nil; + D1(w.body); + if(nr > 0) + (r, nr) = skipbl(s[1:], nr-1); + }while(nr > 0); + clearcollection(); + dir.r = nil; + return TRUE; +} + +readloader(f: ref File, q0: int, r: string, nr: int): int +{ + if(nr > 0) + eloginsert(f, q0, r, nr); + return 0; +} + +e_cmd(t: ref Text , cp: ref Cmd): int +{ + name: string; + f: ref File; + i, q0, q1, nulls, samename, allreplaced, ok: int; + fd: ref Sys->FD; + s, tmp: string; + d: Dir; + + f = t.file; + q0 = addr.r.q0; + q1 = addr.r.q1; + if(cp.cmdc == 'e'){ + if(t.w.clean(TRUE, FALSE)==FALSE) + editerror(""); # winclean generated message already + q0 = 0; + q1 = f.buf.nc; + } + allreplaced = (q0==0 && q1==f.buf.nc); + name = cmdname(f, cp.text, cp.cmdc=='e'); + if(name == nil) + editerror(Enoname); + i = len name; + samename = name == t.file.name; + s = name; + name = nil; + fd = sys->open(s, Sys->OREAD); + if(fd == nil){ + tmp = sprint("can't open %s: %r", s); + s = nil; + editerror(tmp); + } + (ok, d) = sys->fstat(fd); + if(ok >=0 && (d.mode&Sys->DMDIR)){ + fd = nil; + tmp = sprint("%s is a directory", s); + s = nil; + editerror(tmp); + } + elogdelete(f, q0, q1); + nulls = 0; + bufferm->loadfile(fd, q1, Dat->READL, nil, f); + s = nil; + fd = nil; + if(nulls) + warning(nil, sprint("%s: NUL bytes elided\n", s)); + else if(allreplaced && samename) + f.editclean = TRUE; + return TRUE; +} + +f_cmd(t: ref Text, cp: ref Cmd): int +{ + name: string; + + name = cmdname(t.file, cp.text, TRUE); + name = nil; + pfilename(t.file); + return TRUE; +} + +g_cmd(t: ref Text, cp: ref Cmd): int +{ + ok: int; + + if(t.file != addr.f){ + warning(nil, "internal error: g_cmd f!=addr.f\n"); + return FALSE; + } + if(rxcompile(cp.re.r) == FALSE) + editerror("bad regexp in g command"); + (ok, sel) = rxexecute(t, nil, addr.r.q0, addr.r.q1); + if(ok ^ cp.cmdc=='v'){ + t.q0 = addr.r.q0; + t.q1 = addr.r.q1; + return cmdexec(t, cp.cmd); + } + return TRUE; +} + +i_cmd(t: ref Text, cp: ref Cmd): int +{ + return append(t.file, cp, addr.r.q0); +} + +# int +# k_cmd(File *f, Cmd *cp) +# { +# USED(cp); +# f->mark = addr.r; +# return TRUE; +# } + +copy(f: ref File, addr2: Address) +{ + p: int; + ni: int; + buf: ref Astring; + + buf = stralloc(BUFSIZE); + for(p=addr.r.q0; p BUFSIZE) + ni = BUFSIZE; + f.buf.read(p, buf, 0, ni); + eloginsert(addr2.f, addr2.r.q1, buf.s, ni); + } + strfree(buf); +} + +move(f: ref File, addr2: Address) +{ + if(addr.f!=addr2.f || addr.r.q1<=addr2.r.q0){ + elogdelete(f, addr.r.q0, addr.r.q1); + copy(f, addr2); + }else if(addr.r.q0 >= addr2.r.q1){ + copy(f, addr2); + elogdelete(f, addr.r.q0, addr.r.q1); + }else + error("move overlaps itself"); +} + +m_cmd(t: ref Text, cp: ref Cmd): int +{ + dot, addr2: Address; + + dot = mkaddr(t.file); + addr2 = cmdaddress(cp.mtaddr, dot, 0); + if(cp.cmdc == 'm') + move(t.file, addr2); + else + copy(t.file, addr2); + return TRUE; +} + +# int +# n_cmd(File *f, Cmd *cp) +# { +# int i; +# USED(f); +# USED(cp); +# for(i = 0; iname); +# filename(f); +# } +# return TRUE; +#} + +p_cmd(t: ref Text, nil: ref Cmd): int +{ + return pdisplay(t.file); +} + +s_cmd(t: ref Text, cp: ref Cmd): int +{ + i, j, k, c, m, n, nrp, didsub, ok: int; + p1, op, delta: int; + buf: ref String; + rp: array of Rangeset; + err: string; + rbuf: ref Astring; + + n = cp.num; + op= -1; + if(rxcompile(cp.re.r) == FALSE) + editerror("bad regexp in s command"); + nrp = 0; + rp = nil; + delta = 0; + didsub = FALSE; + for(p1 = addr.r.q0; p1<=addr.r.q1; ){ + (ok, sel) = rxexecute(t, nil, p1, addr.r.q1); + if(!ok) + break; + if(sel[0].q0 == sel[0].q1){ # empty match? + if(sel[0].q0 == op){ + p1++; + continue; + } + p1 = sel[0].q1+1; + }else + p1 = sel[0].q1; + op = sel[0].q1; + if(--n>0) + continue; + nrp++; + orp := rp; + rp = array[nrp] of Rangeset; + rp[0: ] = orp[0:nrp-1]; + rp[nrp-1] = copysel(sel); + orp = nil; + } + rbuf = stralloc(BUFSIZE); + buf = allocstring(0); + for(m=0; mBUFSIZE){ + err = "replacement string too long"; + rp = nil; + freestring(buf); + strfree(rbuf); + editerror(err); + return FALSE; + } + t.file.buf.read(sel[j].q0, rbuf, 0, sel[j].q1-sel[j].q0); + for(k=0; kBUFSIZE){ + err = "right hand side too long in substitution"; + rp = nil; + freestring(buf); + strfree(rbuf); + editerror(err); + return FALSE; + } + t.file.buf.read(sel[0].q0, rbuf, 0, sel[0].q1-sel[0].q0); + for(k=0; k0 && t.file.seq!=0 && t.file.seq!=oseq){ + oseq = t.file.seq; +warning(nil, sprint("seq %d\n", t.file.seq)); + undo(t, flag); + } + return TRUE; +} + +w_cmd(t: ref Text, cp: ref Cmd): int +{ + r: string; + f: ref File; + + f = t.file; + if(f.seq == dat->seq) + editerror("can't write file with pending modifications"); + r = cmdname(f, cp.text, FALSE); + if(r == nil) + editerror("no name specified for 'w' command"); + exec->putfile(f, addr.r.q0, addr.r.q1, r); + # r is freed by putfile + return TRUE; +} + +x_cmd(t: ref Text, cp: ref Cmd): int +{ + if(cp.re!=nil) + looper(t.file, cp, cp.cmdc=='x'); + else + linelooper(t.file, cp); + return TRUE; +} + +X_cmd(nil: ref Text, cp: ref Cmd): int +{ + filelooper(cp, cp.cmdc=='X'); + return TRUE; +} + +runpipe(t: ref Text, cmd: int, cr: string, ncr: int, state: int) +{ + r, s: string; + n: int; + dir: Runestr; + w: ref Window; + + (r, n) = skipbl(cr, ncr); + if(n == 0) + editerror("no command specified for >"); + w = nil; + if(state == Inserting){ + w = t.w; + t.q0 = addr.r.q0; + t.q1 = addr.r.q1; + if(cmd == '<' || cmd=='|') + elogdelete(t.file, t.q0, t.q1); + } + tmps := "z"; + tmps[0] = cmd; + s = tmps + r; + n++; + dir.r = nil; + dir.nr = 0; + if(t != nil) + dir = dirname(t, nil, 0); + if(dir.nr==1 && dir.r[0]=='.'){ # sigh + dir.r = nil; + dir.nr = 0; + } + editing = state; + if(t!=nil && t.w!=nil) + t.w.refx.inc(); # run will decref + spawn run(w, s, dir.r, dir.nr, TRUE, nil, nil, TRUE); + s = nil; + if(t!=nil && t.w!=nil) + t.w.unlock(); + row.qlock.unlock(); + <- cedit; + row.qlock.lock(); + editing = Inactive; + if(t!=nil && t.w!=nil) + t.w.lock('M'); +} + +pipe_cmd(t: ref Text, cp: ref Cmd): int +{ + runpipe(t, cp.cmdc, cp.text.r, cp.text.n, Inserting); + return TRUE; +} + +nlcount(t: ref Text, q0: int, q1: int): int +{ + nl: int; + buf: ref Astring; + i, nbuf: int; + + buf = stralloc(BUFSIZE); + nbuf = 0; + i = nl = 0; + while(q0 < q1){ + if(i == nbuf){ + nbuf = q1-q0; + if(nbuf > BUFSIZE) + nbuf = BUFSIZE; + t.file.buf.read(q0, buf, 0, nbuf); + i = 0; + } + if(buf.s[i++] == '\n') + nl++; + q0++; + } + strfree(buf); + return nl; +} + +printposn(t: ref Text, charsonly: int) +{ + l1, l2: int; + + if(t != nil && t.file != nil && t.file.name != nil) + warning(nil, t.file.name + ":"); + if(!charsonly){ + l1 = 1+nlcount(t, 0, addr.r.q0); + l2 = l1+nlcount(t, addr.r.q0, addr.r.q1); + # check if addr ends with '\n' + if(addr.r.q1>0 && addr.r.q1>addr.r.q0 && t.readc(addr.r.q1-1)=='\n') + --l2; + warning(nil, sprint("%ud", l1)); + if(l2 != l1) + warning(nil, sprint(",%ud", l2)); + warning(nil, "\n"); + # warning(nil, "; "); + return; + } + warning(nil, sprint("#%d", addr.r.q0)); + if(addr.r.q1 != addr.r.q0) + warning(nil, sprint(",#%d", addr.r.q1)); + warning(nil, "\n"); +} + +eq_cmd(t: ref Text, cp: ref Cmd): int +{ + charsonly: int; + + case(cp.text.n){ + 0 => + charsonly = FALSE; + break; + 1 => + if(cp.text.r[0] == '#'){ + charsonly = TRUE; + break; + } + * => + charsonly = TRUE; + editerror("newline expected"); + } + printposn(t, charsonly); + return TRUE; +} + +nl_cmd(t: ref Text, cp: ref Cmd): int +{ + a: Address; + f: ref File; + + f = t.file; + if(cp.addr == nil){ + # First put it on newline boundaries + a = mkaddr(f); + addr = lineaddr(0, a, -1); + a = lineaddr(0, a, 1); + addr.r.q1 = a.r.q1; + if(addr.r.q0==t.q0 && addr.r.q1==t.q1){ + a = mkaddr(f); + addr = lineaddr(1, a, 1); + } + } + t.show(addr.r.q0, addr.r.q1); + return TRUE; +} + +append(f: ref File, cp: ref Cmd, p: int): int +{ + if(cp.text.n > 0) + eloginsert(f, p, cp.text.r, cp.text.n); + return TRUE; +} + +pdisplay(f: ref File): int +{ + p1, p2: int; + np: int; + buf: ref Astring; + + p1 = addr.r.q0; + p2 = addr.r.q1; + if(p2 > f.buf.nc) + p2 = f.buf.nc; + buf = stralloc(BUFSIZE); + while(p1 < p2){ + np = p2-p1; + if(np>BUFSIZE-1) + np = BUFSIZE-1; + f.buf.read(p1, buf, 0, np); + warning(nil, sprint("%s", buf.s[0:np])); + p1 += np; + } + strfree(buf); + f.curtext.q0 = addr.r.q0; + f.curtext.q1 = addr.r.q1; + return TRUE; +} + +pfilename(f: ref File) +{ + dirty: int; + w: ref Window; + + w = f.curtext.w; + # same check for dirty as in settag, but we know ncache==0 + dirty = !w.isdir && !w.isscratch && f.mod; + warning(nil, sprint("%c%c%c %s\n", " '"[dirty], + '+', " ."[curtext!=nil && curtext.file==f], f.name)); +} + +loopcmd(f: ref File, cp: ref Cmd, rp: array of Range, nrp: int) +{ + i: int; + + for(i=0; ir.q1) + break; + tr.q0 = op; + tr.q1 = r.q1; + p = r.q1+1; # exit next loop + }else{ + if(sel[0].q0==sel[0].q1){ # empty match? + if(sel[0].q0==op){ + p++; + continue; + } + p = sel[0].q1+1; + }else + p = sel[0].q1; + if(xy) + tr = sel[0]; + else{ + tr.q0 = op; + tr.q1 = sel[0].q0; + } + } + op = sel[0].q1; + nrp++; + orp := rp; + rp = array[nrp] of Range; + rp[0: ] = orp[0: nrp-1]; + rp[nrp-1] = tr; + orp = nil; + } + loopcmd(f, cp.cmd, rp, nrp); + rp = nil; + --nest; +} + +linelooper(f: ref File, cp: ref Cmd) +{ + nrp, p: int; + r, linesel: Range; + a, a3: Address; + rp: array of Range; + + nest++; + nrp = 0; + rp = nil; + r = addr.r; + a3.f = f; + a3.r.q0 = a3.r.q1 = r.q0; + a = lineaddr(0, a3, 1); + linesel = a.r; + for(p = r.q0; p= r.q1) + break; + if(linesel.q1 >= r.q1) + linesel.q1 = r.q1; + if(linesel.q1 > linesel.q0) + if(linesel.q0>=a3.r.q1 && linesel.q1>a3.r.q1){ + a3.r = linesel; + nrp++; + orp := rp; + rp = array[nrp] of Range; + rp[0: ] = orp[0: nrp-1]; + rp[nrp-1] = linesel; + orp = nil; + continue; + } + break; + } + loopcmd(f, cp.cmd, rp, nrp); + rp = nil; + --nest; +} + +loopstruct: ref Looper; + +alllooper(w: ref Window, lp: ref Looper) +{ + t: ref Text; + cp: ref Cmd; + + cp = lp.cp; +# if(w.isscratch || w.isdir) +# return; + t = w.body; + # only use this window if it's the current window for the file + if(t.file.curtext != t) + return; +# if(w.nopen[QWevent] > 0) +# return; + # no auto-execute on files without names + if(cp.re==nil && t.file.name==nil) + return; + if(cp.re==nil || filematch(t.file, cp.re)==lp.XY){ + olpw := lp.w; + lp.w = array[lp.nw+1] of ref Window; + lp.w[0: ] = olpw[0: lp.nw]; + lp.w[lp.nw++] = w; + olpw = nil; + } +} + +filelooper(cp: ref Cmd, XY: int) +{ + i: int; + + if(Glooping++) + editerror(sprint("can't nest %c command", "YX"[XY])); + nest++; + + if(loopstruct == nil) + loopstruct = ref Looper; + loopstruct.cp = cp; + loopstruct.XY = XY; + if(loopstruct.w != nil) # error'ed out last time + loopstruct.w = nil; + loopstruct.w = nil; + loopstruct.nw = 0; + aw := ref Allwin.LP(loopstruct); + allwindows(Edit->ALLLOOPER, aw); + aw = nil; + for(i=0; i= 0){ + (ok, sel) = rxexecute(f.curtext, nil, p, 16r7FFFFFFF); + if(!ok) + editerror("no match for regexp"); + if(sel[0].q0==sel[0].q1 && sel[0].q0==p){ + if(++p>f.buf.nc) + p = 0; + (ok, sel) = rxexecute(f.curtext, nil, p, 16r7FFFFFFF); + if(!ok) + editerror("address"); + } + }else{ + (ok, sel) = rxbexecute(f.curtext, p); + if(!ok) + editerror("no match for regexp"); + if(sel[0].q0==sel[0].q1 && sel[0].q1==p){ + if(--p<0) + p = f.buf.nc; + (ok, sel) = rxbexecute(f.curtext, p); + if(!ok) + editerror("address"); + } + } +} + +cmdaddress(ap: ref Addr, a: Address, sign: int): Address +{ + f := a.f; + a1, a2: Address; + + do{ + case(ap.typex){ + 'l' or + '#' => + if(ap.typex == '#') + a = charaddr(ap.num, a, sign); + else + a = lineaddr(ap.num, a, sign); + break; + + '.' => + a = mkaddr(f); + break; + + '$' => + a.r.q0 = a.r.q1 = f.buf.nc; + break; + + '\'' => +editerror("can't handle '"); +# a.r = f.mark; + break; + + '?' => + sign = -sign; + if(sign == 0) + sign = -1; + if(sign >= 0) + v := a.r.q1; + else + v = a.r.q0; + nextmatch(f, ap.re, v, sign); + a.r = sel[0]; + break; + + '/' => + if(sign >= 0) + v := a.r.q1; + else + v = a.r.q0; + nextmatch(f, ap.re, v, sign); + a.r = sel[0]; + break; + + '"' => + f = matchfile(ap.re); + a = mkaddr(f); + break; + + '*' => + a.r.q0 = 0; + a.r.q1 = f.buf.nc; + return a; + + ',' or + ';' => + if(ap.left!=nil) + a1 = cmdaddress(ap.left, a, 0); + else{ + a1.f = a.f; + a1.r.q0 = a1.r.q1 = 0; + } + if(ap.typex == ';'){ + f = a1.f; + a = a1; + f.curtext.q0 = a1.r.q0; + f.curtext.q1 = a1.r.q1; + } + if(ap.next!=nil) + a2 = cmdaddress(ap.next, a, 0); + else{ + a2.f = a.f; + a2.r.q0 = a2.r.q1 = f.buf.nc; + } + if(a1.f != a2.f) + editerror("addresses in different files"); + a.f = a1.f; + a.r.q0 = a1.r.q0; + a.r.q1 = a2.r.q1; + if(a.r.q1 < a.r.q0) + editerror("addresses out of order"); + return a; + + '+' or + '-' => + sign = 1; + if(ap.typex == '-') + sign = -1; + if(ap.next==nil || ap.next.typex=='+' || ap.next.typex=='-') + a = lineaddr(1, a, sign); + break; + * => + error("cmdaddress"); + return a; + } + }while((ap = ap.next)!=nil); # assign = + return a; +} + +alltofile(w: ref Window, tp: ref Tofile) +{ + t: ref Text; + + if(tp.f != nil) + return; + if(w.isscratch || w.isdir) + return; + t = w.body; + # only use this window if it's the current window for the file + if(t.file.curtext != t) + return; +# if(w.nopen[QWevent] > 0) +# return; + if(tp.r.r == t.file.name) + tp.f = t.file; +} + +tofile(r: ref String): ref File +{ + t: ref Tofile; + rr: String; + + (rr.r, r.n) = skipbl(r.r, r.n); + t = ref Tofile; + t.f = nil; + t.r = ref String; + *t.r = rr; + aw := ref Allwin.FF(t); + allwindows(Edit->ALLTOFILE, aw); + aw = nil; + if(t.f == nil) + editerror(sprint("no such file\"%s\"", rr.r)); + return t.f; +} + +allmatchfile(w: ref Window, tp: ref Tofile) +{ + t: ref Text; + + if(w.isscratch || w.isdir) + return; + t = w.body; + # only use this window if it's the current window for the file + if(t.file.curtext != t) + return; +# if(w.nopen[QWevent] > 0) +# return; + if(filematch(w.body.file, tp.r)){ + if(tp.f != nil) + editerror(sprint("too many files match \"%s\"", tp.r.r)); + tp.f = w.body.file; + } +} + +matchfile(r: ref String): ref File +{ + tf: ref Tofile; + + tf = ref Tofile; + tf.f = nil; + tf.r = r; + aw := ref Allwin.FF(tf); + allwindows(Edit->ALLMATCHFILE, aw); + aw = nil; + + if(tf.f == nil) + editerror(sprint("no file matches \"%s\"", r.r)); + return tf.f; +} + +filematch(f: ref File, r: ref String): int +{ + buf: string; + w: ref Window; + match, i, dirty: int; + s: Rangeset; + + # compile expr first so if we get an error, we haven't allocated anything + if(rxcompile(r.r) == FALSE) + editerror("bad regexp in file match"); + w = f.curtext.w; + # same check for dirty as in settag, but we know ncache==0 + dirty = !w.isdir && !w.isscratch && f.mod; + buf = sprint("%c%c%c %s\n", " '"[dirty], + '+', " ."[curtext!=nil && curtext.file==f], f.name); + (match, s) = rxexecute(nil, buf, 0, i); + buf = nil; + return match; +} + +charaddr(l: int, addr: Address, sign: int): Address +{ + if(sign == 0) + addr.r.q0 = addr.r.q1 = l; + else if(sign < 0) + addr.r.q1 = addr.r.q0 -= l; + else if(sign > 0) + addr.r.q0 = addr.r.q1 += l; + if(addr.r.q0<0 || addr.r.q1>addr.f.buf.nc) + editerror("address out of range"); + return addr; +} + +lineaddr(l: int, addr: Address, sign: int): Address +{ + n: int; + c: int; + f := addr.f; + a: Address; + p: int; + + a.f = f; + if(sign >= 0){ + if(l == 0){ + if(sign==0 || addr.r.q1==0){ + a.r.q0 = a.r.q1 = 0; + return a; + } + a.r.q0 = addr.r.q1; + p = addr.r.q1-1; + }else{ + if(sign==0 || addr.r.q1==0){ + p = 0; + n = 1; + }else{ + p = addr.r.q1-1; + n = f.curtext.readc(p++)=='\n'; + } + while(n < l){ + if(p >= f.buf.nc) + editerror("address out of range"); + if(f.curtext.readc(p++) == '\n') + n++; + } + a.r.q0 = p; + } + while(p < f.buf.nc && f.curtext.readc(p++)!='\n') + ; + a.r.q1 = p; + }else{ + p = addr.r.q0; + if(l == 0) + a.r.q1 = addr.r.q0; + else{ + for(n = 0; n 0) + p--; + } + while(p > 0 && f.curtext.readc(p-1)!='\n') # lines start after a newline + p--; + a.r.q0 = p; + } + return a; +} + +allfilecheck(w: ref Window, fp: ref Filecheck) +{ + f: ref File; + + f = w.body.file; + if(w.body.file == fp.f) + return; + if(fp.r == f.name) + warning(nil, sprint("warning: duplicate file name \"%s\"\n", fp.r)); +} + +cmdname(f: ref File, str: ref String , set: int): string +{ + r, s: string; + n: int; + fc: ref Filecheck; + newname: Runestr; + + r = nil; + n = str.n; + s = str.r; + if(n == 0){ + # no name; use existing + if(f.name == nil) + return nil; + return f.name; + } + (s, n) = skipbl(s, n); + if(n == 0) + ; + else{ + if(s[0] == '/'){ + r = s; + }else{ + newname = dirname(f.curtext, s, n); + r = newname.r; + n = newname.nr; + } + fc = ref Filecheck; + fc.f = f; + fc.r = r; + fc.nr = n; + aw := ref Allwin.FC(fc); + allwindows(Edit->ALLFILECHECK, aw); + aw = nil; + if(f.name == nil) + set = TRUE; + } + + if(set && r[0: n] != f.name){ + f.mark(); + f.mod = TRUE; + f.curtext.w.dirty = TRUE; + f.curtext.w.setname(r, n); + } + return r; +} + +copysel(rs: Rangeset): Rangeset +{ + nrs := array[NRange] of Range; + for(i := 0; i < NRange; i++) + nrs[i] = rs[i]; + return nrs; +} + \ No newline at end of file diff --git a/appl/acme/ecmd.m b/appl/acme/ecmd.m new file mode 100644 index 00000000..e9367c2a --- /dev/null +++ b/appl/acme/ecmd.m @@ -0,0 +1,18 @@ +Editcmd: module { + + PATH: con "/dis/acme/ecmd.dis"; + + init : fn(mods : ref Dat->Mods); + + cmdexec: fn(a0: ref Textm->Text, a1: ref Edit->Cmd): int; + resetxec: fn(); + cmdaddress: fn(a0: ref Edit->Addr, a1: Edit->Address, a2: int): Edit->Address; + edittext: fn(f: ref Filem->File, q: int, r: string, nr: int): string; + + alllooper: fn(w: ref Windowm->Window, lp: ref Dat->Looper); + alltofile: fn(w: ref Windowm->Window, tp: ref Dat->Tofile); + allmatchfile: fn(w: ref Windowm->Window, tp: ref Dat->Tofile); + allfilecheck: fn(w: ref Windowm->Window, fp: ref Dat->Filecheck); + + readloader: fn(f: ref Filem->File, q0: int, r: string, nr: int): int; +}; \ No newline at end of file diff --git a/appl/acme/edit.b b/appl/acme/edit.b new file mode 100644 index 00000000..92faad1a --- /dev/null +++ b/appl/acme/edit.b @@ -0,0 +1,676 @@ +implement Edit; + +include "common.m"; + +sys: Sys; +dat: Dat; +utils: Utils; +textm: Textm; +windowm: Windowm; +rowm: Rowm; +scroll: Scroll; +editlog: Editlog; +editcomd: Editcmd; + +sprint, print: import sys; +FALSE, TRUE, BUFSIZE, Null, Empty, Inactive: import Dat; +warning, error, strchr: import utils; +Text: import textm; +File: import Filem; +Window: import windowm; +allwindows: import rowm; +scrdraw: import scroll; +elogterm, elogapply: import editlog; +cmdexec, resetxec: import editcomd; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + dat = mods.dat; + utils = mods.utils; + textm = mods.textm; + windowm = mods.windowm; + rowm = mods.rowm; + scroll = mods.scroll; + editlog = mods.editlog; + editcomd = mods.editcmd; + editing = Inactive; +} + +linex: con "\n"; +wordx: con "\t\n"; + +cmdtab = array[28] of { +# cmdc text regexp addr defcmd defaddr count token fn + Cmdt ( '\n', 0, 0, 0, 0, aDot, 0, nil, C_nl ), + Cmdt ( 'a', 1, 0, 0, 0, aDot, 0, nil, C_a ), + Cmdt ( 'b', 0, 0, 0, 0, aNo, 0, linex, C_b ), + Cmdt ( 'c', 1, 0, 0, 0, aDot, 0, nil, C_c ), + Cmdt ( 'd', 0, 0, 0, 0, aDot, 0, nil, C_d ), + Cmdt ( 'e', 0, 0, 0, 0, aNo, 0, wordx, C_e ), + Cmdt ( 'f', 0, 0, 0, 0, aNo, 0, wordx, C_f ), + Cmdt ( 'g', 0, 1, 0, 'p', aDot, 0, nil, C_g ), + Cmdt ( 'i', 1, 0, 0, 0, aDot, 0, nil, C_i ), + Cmdt ( 'm', 0, 0, 1, 0, aDot, 0, nil, C_m ), + Cmdt ( 'p', 0, 0, 0, 0, aDot, 0, nil, C_p ), + Cmdt ( 'r', 0, 0, 0, 0, aDot, 0, wordx, C_e ), + Cmdt ( 's', 0, 1, 0, 0, aDot, 1, nil, C_s ), + Cmdt ( 't', 0, 0, 1, 0, aDot, 0, nil, C_m ), + Cmdt ( 'u', 0, 0, 0, 0, aNo, 2, nil, C_u ), + Cmdt ( 'v', 0, 1, 0, 'p', aDot, 0, nil, C_g ), + Cmdt ( 'w', 0, 0, 0, 0, aAll, 0, wordx, C_w ), + Cmdt ( 'x', 0, 1, 0, 'p', aDot, 0, nil, C_x ), + Cmdt ( 'y', 0, 1, 0, 'p', aDot, 0, nil, C_x ), + Cmdt ( '=', 0, 0, 0, 0, aDot, 0, linex, C_eq ), + Cmdt ( 'B', 0, 0, 0, 0, aNo, 0, linex, C_B ), + Cmdt ( 'D', 0, 0, 0, 0, aNo, 0, linex, C_D ), + Cmdt ( 'X', 0, 1, 0, 'f', aNo, 0, nil, C_X ), + Cmdt ( 'Y', 0, 1, 0, 'f', aNo, 0, nil, C_X ), + Cmdt ( '<', 0, 0, 0, 0, aDot, 0, linex, C_pipe ), + Cmdt ( '|', 0, 0, 0, 0, aDot, 0, linex, C_pipe ), + Cmdt ( '>', 0, 0, 0, 0, aDot, 0, linex, C_pipe ), + # deliberately unimplemented + # Cmdt ( 'k', 0, 0, 0, 0, aDot, 0, nil, C_k ), + # Cmdt ( 'n', 0, 0, 0, 0, aNo, 0, nil, C_n ), + # Cmdt ( 'q', 0, 0, 0, 0, aNo, 0, nil, C_q ), + # Cmdt ( '!', 0, 0, 0, 0, aNo, 0, linex, C_plan9 ), + Cmdt (0, 0, 0, 0, 0, 0, 0, nil, -1 ) +}; + +cmdstartp: string; +cmdendp: int; +cmdp: int; +editerrc: chan of string; + +lastpat : ref String; +patset: int; + +# cmdlist: ref List; +# addrlist: ref List; +# stringlist: ref List; + +editwaitproc(pid : int, sync: chan of int) +{ + fd : ref Sys->FD; + n : int; + + sys->pctl(Sys->FORKFD, nil); + w := sprint("#p/%d/wait", pid); + fd = sys->open(w, Sys->OREAD); + if (fd == nil) + error("fd == nil in editwaitproc"); + sync <-= sys->pctl(0, nil); + buf := array[Sys->WAITLEN] of byte; + status := ""; + for(;;){ + if ((n = sys->read(fd, buf, len buf))<0) + error("bad read in editwaitproc"); + status = string buf[0:n]; + dat->cwait <-= status; + } +} + +editthread() +{ + cmdp: ref Cmd; + + mypid := sys->pctl(0, nil); + sync := chan of int; + spawn editwaitproc(mypid, sync); + yourpid := <- sync; + while((cmdp=parsecmd(0)) != nil){ +# ocurfile = curfile; +# loaded = curfile && !curfile->unread; + if(cmdexec(curtext, cmdp) == 0) + break; + freecmd(); + } + editerrc <-= nil; + utils->postnote(Utils->PNPROC, mypid, yourpid, "kill"); +} + +allelogterm(w: ref Window) +{ + elogterm(w.body.file); +} + +alleditinit(w: ref Window) +{ + w.tag.commit(TRUE); + w.body.commit(TRUE); + w.body.file.editclean = FALSE; +} + +allupdate(w: ref Window) +{ + t: ref Text; + i: int; + f: ref File; + + t = w.body; + f = t.file; + if(f.curtext != t) # do curtext only + return; + if(f.elog.typex == Null) + elogterm(f); + else if(f.elog.typex != Empty){ + elogapply(f); + if(f.editclean){ + f.mod = FALSE; + for(i=0; i BUFSIZE){ + warning(nil, "string too long\n"); + return; + } + + allwindows(ALLEDITINIT, nil); + cmdstartp = r[0:n]; + if(r[n-1] != '\n') + cmdstartp[n++] = '\n'; + cmdendp = n; + cmdp = 0; + if(ct.w == nil) + curtext = nil; + else + curtext = ct.w.body; + resetxec(); + if(editerrc == nil){ + editerrc = chan of string; + lastpat = allocstring(0); + } + spawn editthread(); + err = <- editerrc; + editing = Inactive; + if(err != nil) + warning(nil, sprint("Edit: %s\n", err)); + + # update everyone whose edit log has data + allwindows(ALLUPDATE, nil); +} + +getch(): int +{ + if(cmdp == cmdendp) + return -1; + return cmdstartp[cmdp++]; +} + +nextc(): int +{ + if(cmdp == cmdendp) + return -1; + return cmdstartp[cmdp]; +} + +ungetch() +{ + if(--cmdp < 0) + error("ungetch"); +} + +getnum(signok: int): int +{ + n: int; + c, sign: int; + + n = 0; + sign = 1; + if(signok>1 && nextc()=='-'){ + sign = -1; + getch(); + } + if((c=nextc())<'0' || '9'= 0) + ungetch(); + return c; +} + +# Check that list has room for one more element. +# growlist(l: ref List) +# { +# if(l.elems == nil || l.nalloc==0){ +# l.nalloc = INCR; +# l.elems = array[INCR] of Listelement; +# l.nused = 0; +# }else if(l.nused == l.nalloc){ +# old := l.elems; +# l.elems = array[l.nalloc+INCR] of Listelement; +# l.elems[0:] = old[0:l.nalloc]; +# l.nalloc += INCR; +# } +# } + +# Remove the ith element from the list +# dellist(l: ref List, i: int) +# { +# l.elems[i:] = l.elems[i+1:l.nused]; +# l.nused--; +# } + +# Add a new element, whose position is i, to the list +# inslist(l: ref List, i: int, val: int) +# { +# growlist(l); +# l.elems[i+1:] = l.elems[i:l.nused]; +# l.elems[i] = val; +# l.nused++; +# } + +# listfree(l: ref List) +# { +# l.elems = nil; +# } + +allocstring(n: int): ref String +{ + s: ref String; + + s = ref String; + s.n = n; + s.r = string array[s.n] of { * => byte '\0' }; + return s; +} + +freestring(s: ref String) +{ + s.r = nil; +} + +newcmd(): ref Cmd +{ + p: ref Cmd; + + p = ref Cmd; + # inslist(cmdlist, cmdlist.nused, p); + return p; +} + +newstring(n: int): ref String +{ + p: ref String; + + p = allocstring(n); + # inslist(stringlist, stringlist.nused, p); + return p; +} + +newaddr(): ref Addr +{ + p: ref Addr; + + p = ref Addr; + # inslist(addrlist, addrlist.nused, p); + return p; +} + +freecmd() +{ + # i: int; + + # cmdlist.elems = nil; + # addrlist.elems = nil; + # stringlist.elems = nil; + # cmdlist.nused = addrlist.nused = stringlist.nused = 0; +} + +okdelim(c: int) +{ + if(c=='\\' || ('a'<=c && c<='z') + || ('A'<=c && c<='Z') || ('0'<=c && c<='9')) + editerror(sprint("bad delimiter %c\n", c)); +} + +atnl() +{ + c: int; + + cmdskipbl(); + c = getch(); + if(c != '\n') + editerror(sprint("newline expected (saw %c)", c)); +} + +Straddc(s: ref String, c: int) +{ + s.r[s.n++] = c; +} + +getrhs(s: ref String, delim: int, cmd: int) +{ + c: int; + + while((c = getch())>0 && c!=delim && c!='\n'){ + if(c == '\\'){ + if((c=getch()) <= 0) + error("bad right hand side"); + if(c == '\n'){ + ungetch(); + c='\\'; + }else if(c == 'n') + c='\n'; + else if(c!=delim && (cmd=='s' || c!='\\')) # s does its own + Straddc(s, '\\'); + } + Straddc(s, c); + } + ungetch(); # let client read whether delimiter, '\n' or whatever +} + +collecttoken(end: string): ref String +{ + c: int; + + s := newstring(0); + + while((c=nextc())==' ' || c=='\t') + Straddc(s, getch()); # blanks significant for getname() + while((c=getch())>0 && strchr(end, c)<0) + Straddc(s, c); + if(c != '\n') + atnl(); + return s; +} + +collecttext(): ref String +{ + s: ref String; + begline, i, c, delim: int; + + s = newstring(0); + if(cmdskipbl()=='\n'){ + getch(); + i = 0; + do{ + begline = i; + while((c = getch())>0 && c!='\n'){ + i++; + Straddc(s, c); + } + i++; + Straddc(s, '\n'); + if(c < 0) + return s; + }while(s.r[begline]!='.' || s.r[begline+1]!='\n'); + s.r[s.n-2] = '\0'; + }else{ + okdelim(delim = getch()); + getrhs(s, delim, 'a'); + if(nextc()==delim) + getch(); + atnl(); + } + return s; +} + +cmdlookup(c: int): int +{ + i: int; + + for(i=0; cmdtab[i].cmdc; i++) + if(cmdtab[i].cmdc == c) + return i; + return -1; +} + +parsecmd(nest: int): ref Cmd +{ + i, c: int; + cp, ncp: ref Cmd; + cmd: ref Cmd; + + cmd = ref Cmd; + cmd.next = cmd.cmd = nil; + cmd.re = nil; + cmd.flag = cmd.num = 0; + cmd.addr = compoundaddr(); + if(cmdskipbl() == -1) + return nil; + if((c=getch())==-1) + return nil; + cmd.cmdc = c; + if(cmd.cmdc=='c' && nextc()=='d'){ # sleazy two-character case + getch(); # the 'd' + cmd.cmdc='c'|16r100; + } + i = cmdlookup(cmd.cmdc); + if(i >= 0){ + if(cmd.cmdc == '\n'){ + cp = newcmd(); + *cp = *cmd; + return cp; + # let nl_cmd work it all out + } + ct := cmdtab[i]; + if(ct.defaddr==aNo && cmd.addr != nil) + editerror("command takes no address"); + if(ct.count) + cmd.num = getnum(ct.count); + if(ct.regexp){ + # x without pattern -> .*\n, indicated by cmd.re==0 + # X without pattern is all files + if((ct.cmdc!='x' && ct.cmdc!='X') || + ((c = nextc())!=' ' && c!='\t' && c!='\n')){ + cmdskipbl(); + if((c = getch())=='\n' || c<0) + editerror("no address"); + okdelim(c); + cmd.re = getregexp(c); + if(ct.cmdc == 's'){ + cmd.text = newstring(0); + getrhs(cmd.text, c, 's'); + if(nextc() == c){ + getch(); + if(nextc() == 'g') + cmd.flag = getch(); + } + + } + } + } + if(ct.addr && (cmd.mtaddr=simpleaddr())==nil) + editerror("bad address"); + if(ct.defcmd){ + if(cmdskipbl() == '\n'){ + getch(); + cmd.cmd = newcmd(); + cmd.cmd.cmdc = ct.defcmd; + }else if((cmd.cmd = parsecmd(nest))==nil) + error("defcmd"); + }else if(ct.text) + cmd.text = collecttext(); + else if(ct.token != nil) + cmd.text = collecttoken(ct.token); + else + atnl(); + }else + case(cmd.cmdc){ + '{' => + cp = nil; + do{ + if(cmdskipbl()=='\n') + getch(); + ncp = parsecmd(nest+1); + if(cp != nil) + cp.next = ncp; + else + cmd.cmd = ncp; + }while((cp = ncp) != nil); + break; + '}' => + atnl(); + if(nest==0) + editerror("right brace with no left brace"); + return nil; + 'c'|16r100 => + editerror("unimplemented command cd"); + * => + editerror(sprint("unknown command %c", cmd.cmdc)); + } + cp = newcmd(); + *cp = *cmd; + return cp; +} + +getregexp(delim: int): ref String +{ + buf, r: ref String; + i, c: int; + + buf = allocstring(0); + for(i=0; ; i++){ + if((c = getch())=='\\'){ + if(nextc()==delim) + c = getch(); + else if(nextc()=='\\'){ + Straddc(buf, c); + c = getch(); + } + }else if(c==delim || c=='\n') + break; + if(i >= BUFSIZE) + editerror("regular expression too long"); + Straddc(buf, c); + } + if(c!=delim && c) + ungetch(); + if(buf.n > 0){ + patset = TRUE; + freestring(lastpat); + lastpat = buf; + }else + freestring(buf); + if(lastpat.n == 0) + editerror("no regular expression defined"); + r = newstring(lastpat.n); + k := lastpat.n; + for(j := 0; j < k; j++) + r.r[j] = lastpat.r[j]; # newstring put \0 at end + return r; +} + +simpleaddr(): ref Addr +{ + addr: Addr; + ap, nap: ref Addr; + + addr.next = nil; + addr.left = nil; + case(cmdskipbl()){ + '#' => + addr.typex = getch(); + addr.num = getnum(1); + break; + '0' to '9' => + addr.num = getnum(1); + addr.typex='l'; + break; + '/' or '?' or '"' => + addr.re = getregexp(addr.typex = getch()); + break; + '.' or + '$' or + '+' or + '-' or + '\'' => + addr.typex = getch(); + break; + * => + return nil; + } + if((addr.next = simpleaddr()) != nil) + case(addr.next.typex){ + '.' or + '$' or + '\'' => + if(addr.typex!='"') + editerror("bad address syntax"); + break; + '"' => + editerror("bad address syntax"); + break; + 'l' or + '#' => + if(addr.typex=='"') + break; + if(addr.typex!='+' && addr.typex!='-'){ + # insert the missing '+' + nap = newaddr(); + nap.typex='+'; + nap.next = addr.next; + addr.next = nap; + } + break; + '/' or + '?' => + if(addr.typex!='+' && addr.typex!='-'){ + # insert the missing '+' + nap = newaddr(); + nap.typex='+'; + nap.next = addr.next; + addr.next = nap; + } + break; + '+' or + '-' => + break; + * => + error("simpleaddr"); + } + ap = newaddr(); + *ap = addr; + return ap; +} + +compoundaddr(): ref Addr +{ + addr: Addr; + ap, next: ref Addr; + + addr.left = simpleaddr(); + if((addr.typex = cmdskipbl())!=',' && addr.typex!=';') + return addr.left; + getch(); + next = addr.next = compoundaddr(); + if(next != nil && (next.typex==',' || next.typex==';') && next.left==nil) + editerror("bad address syntax"); + ap = newaddr(); + *ap = addr; + return ap; +} + diff --git a/appl/acme/edit.m b/appl/acme/edit.m new file mode 100644 index 00000000..0ea3503d --- /dev/null +++ b/appl/acme/edit.m @@ -0,0 +1,85 @@ +Edit: module { + #pragma varargck argpos editerror 1 + + PATH: con "/dis/acme/edit.dis"; + + String: adt{ + n: int; + r: string; + }; + + Addr: adt{ + typex: int; # # (char addr), l (line addr), / ? . $ + - , ; + num: int; + next: cyclic ref Addr; # or right side of , and ; + re: ref String; + left: cyclic ref Addr; # left side of , and ; + }; + + Address: adt{ + r: Dat->Range; + f: ref Filem->File; + }; + + Cmd: adt{ + addr: ref Addr; # address (range of text) + re: ref String; # regular expression for e.g. 'x' + next: cyclic ref Cmd; # pointer to next element in {} + num: int; + flag: int; # whatever + cmdc: int; # command character; 'x' etc. + cmd: cyclic ref Cmd; # target of x, g, {, etc. + text: ref String; # text of a, c, i; rhs of s + mtaddr: ref Addr; # address for m, t + }; + + Cmdt: adt{ + cmdc: int; # command character + text: int; # takes a textual argument? + regexp: int; # takes a regular expression? + addr: int; # takes an address (m or t)? + defcmd: int; # default command; 0==>none + defaddr: int; # default address + count: int; # takes a count e.g. s2/// + token: string; # takes text terminated by one of these + fnc: int; # function to call with parse tree + }; + + cmdtab: array of Cmdt; + + INCR: con 25; # delta when growing list + + List: adt{ + nalloc: int; + nused: int; + pick{ + C => cmdptr: array of ref Cmd; + S => stringptr: array of ref String; + A => addrptr: array of ref Addr; + } + }; + + aNo, aDot, aAll: con iota; # default addresses + + ALLLOOPER, ALLTOFILE, ALLMATCHFILE, ALLFILECHECK, ALLELOGTERM, ALLEDITINIT, ALLUPDATE: con iota; + + C_nl, C_a, C_b, C_c, C_d, C_B, C_D, C_e, C_f, C_g, C_i, C_k, C_m, C_n, C_p, C_s, C_u, C_w, C_x, C_X, C_pipe, C_eq: con iota; + + editing: int; + curtext: ref Textm->Text; + + init : fn(mods : ref Dat->Mods); + + allocstring: fn(a0: int): ref String; + freestring: fn(a0: ref String); + getregexp: fn(a0: int): ref String; + newaddr: fn(): ref Addr; + editcmd: fn(t: ref Textm->Text, r: string, n: int); + editerror: fn(a0: string); + cmdlookup: fn(a0: int): int; + Straddc: fn(a0: ref String, a1: int); + + allelogterm: fn(w: ref Windowm->Window); + alleditinit: fn(w: ref Windowm->Window); + allupdate: fn(w: ref Windowm->Window); +}; diff --git a/appl/acme/elog.b b/appl/acme/elog.b new file mode 100644 index 00000000..8dea6b4c --- /dev/null +++ b/appl/acme/elog.b @@ -0,0 +1,353 @@ +implement Editlog; + +include "common.m"; + +sys: Sys; +utils: Utils; +buffm: Bufferm; +filem: Filem; +textm: Textm; +edit: Edit; + +sprint, fprint: import sys; +FALSE, TRUE, BUFSIZE, Empty, Null, Delete, Insert, Replace, Filename, Astring: import Dat; +File: import filem; +Buffer: import buffm; +Text: import textm; +error, warning, stralloc, strfree: import utils; +editerror: import edit; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + utils = mods.utils; + buffm = mods.bufferm; + filem = mods.filem; + textm = mods.textm; + edit = mods.edit; +} + +Wsequence := "warning: changes out of sequence\n"; +warned := FALSE; + +# +# Log of changes made by editing commands. Three reasons for this: +# 1) We want addresses in commands to apply to old file, not file-in-change. +# 2) It's difficult to track changes correctly as things move, e.g. ,x m$ +# 3) This gives an opportunity to optimize by merging adjacent changes. +# It's a little bit like the Undo/Redo log in Files, but Point 3) argues for a +# separate implementation. To do this well, we use Replace as well as +# Insert and Delete +# + +Buflog: adt{ + typex: int; # Replace, Filename + q0: int; # location of change (unused in f) + nd: int; # runes to delete + nr: int; # runes in string or file name +}; + +Buflogsize: con 7; +SHM : con 16rffff; + +pack(b: Buflog) : string +{ + a := "0123456"; + a[0] = b.typex; + a[1] = b.q0&SHM; + a[2] = (b.q0>>16)&SHM; + a[3] = b.nd&SHM; + a[4] = (b.nd>>16)&SHM; + a[5] = b.nr&SHM; + a[6] = (b.nr>>16)&SHM; + return a; +} + +scopy(s1: ref Astring, m: int, s2: string, n: int, o: int) +{ + p := o-n; + for(i := 0; i < p; i++) + s1.s[m++] = s2[n++]; +} + +# +# Minstring shouldn't be very big or we will do lots of I/O for small changes. +# Maxstring is BUFSIZE so we can fbufalloc() once and not realloc elog.r. +# +Minstring: con 16; # distance beneath which we merge changes +Maxstring: con BUFSIZE; # maximum length of change we will merge into one + +eloginit(f: ref File) +{ + if(f.elog.typex != Empty) + return; + f.elog.typex = Null; + if(f.elogbuf == nil) + f.elogbuf = buffm->newbuffer(); + # f.elogbuf = ref Buffer; + if(f.elog.r == nil) + f.elog.r = stralloc(BUFSIZE); + f.elogbuf.reset(); +} + +elogclose(f: ref File) +{ + if(f.elogbuf != nil){ + f.elogbuf.close(); + f.elogbuf = nil; + } +} + +elogreset(f: ref File) +{ + f.elog.typex = Null; + f.elog.nd = 0; + f.elog.nr = 0; +} + +elogterm(f: ref File) +{ + elogreset(f); + if(f.elogbuf != nil) + f.elogbuf.reset(); + f.elog.typex = Empty; + if(f.elog.r != nil){ + strfree(f.elog.r); + f.elog.r = nil; + } + warned = FALSE; +} + +elogflush(f: ref File) +{ + b: Buflog; + + b.typex = f.elog.typex; + b.q0 = f.elog.q0; + b.nd = f.elog.nd; + b.nr = f.elog.nr; + case(f.elog.typex){ + * => + warning(nil, sprint("unknown elog type 0x%ux\n", f.elog.typex)); + break; + Null => + break; + Insert or + Replace => + if(f.elog.nr > 0) + f.elogbuf.insert(f.elogbuf.nc, f.elog.r.s, f.elog.nr); + f.elogbuf.insert(f.elogbuf.nc, pack(b), Buflogsize); + break; + Delete => + f.elogbuf.insert(f.elogbuf.nc, pack(b), Buflogsize); + break; + } + elogreset(f); +} + +elogreplace(f: ref File, q0: int, q1: int, r: string, nr: int) +{ + gap: int; + + if(q0==q1 && nr==0) + return; + eloginit(f); + if(f.elog.typex!=Null && q0 0){ + f.buf.read(f.elog.q0+f.elog.nd, f.elog.r, f.elog.nr, gap); + f.elog.nr += gap; + } + f.elog.nd += gap + q1-q0; + scopy(f.elog.r, f.elog.nr, r, 0, nr); + f.elog.nr += nr; + return; + } + } + elogflush(f); + f.elog.typex = Replace; + f.elog.q0 = q0; + f.elog.nd = q1-q0; + f.elog.nr = nr; + if(nr > BUFSIZE) + editerror(sprint("internal error: replacement string too large(%d)", nr)); + scopy(f.elog.r, 0, r, 0, nr); +} + +eloginsert(f: ref File, q0: int, r: string, nr: int) +{ + n: int; + + if(nr == 0) + return; + eloginit(f); + if(f.elog.typex!=Null && q0 0){ + elogflush(f); + f.elog.typex = Insert; + f.elog.q0 = q0; + n = nr; + if(n > BUFSIZE) + n = BUFSIZE; + f.elog.nr = n; + scopy(f.elog.r, 0, r, 0, n); + r = r[n:]; + nr -= n; + } +} + +elogdelete(f: ref File, q0: int, q1: int) +{ + if(q0 == q1) + return; + eloginit(f); + if(f.elog.typex!=Null && q0 0){ + up = log.nc-Buflogsize; + log.read(up, a, 0, Buflogsize); + b.typex = a.s[0]; + b.q0 = a.s[1]|(a.s[2]<<16); + b.nd = a.s[3]|(a.s[4]<<16); + b.nr = a.s[5]|(a.s[6]<<16); + case(b.typex){ + * => + error(sprint("elogapply: 0x%ux\n", b.typex)); + break; + + Replace => + if(!mod){ + mod = TRUE; + f.mark(); + } + # if(b.nd == b.nr && b.nr <= BUFSIZE){ + # up -= b.nr; + # log.read(up, buf, 0, b.nr); + # t.replace(b.q0, b.q0+b.nd, buf.s, b.nr, TRUE, 0); + # break; + # } + t.delete(b.q0, b.q0+b.nd, TRUE); + up -= b.nr; + for(i=0; i BUFSIZE) + n = BUFSIZE; + log.read(up+i, buf, 0, n); + t.insert(b.q0+i, buf.s, n, TRUE, 0); + } + # t.q0 = b.q0; + # t.q1 = b.q0+b.nr; + break; + + Delete => + if(!mod){ + mod = TRUE; + f.mark(); + } + t.delete(b.q0, b.q0+b.nd, TRUE); + # t.q0 = b.q0; + # t.q1 = b.q0; + break; + + Insert => + if(!mod){ + mod = TRUE; + f.mark(); + } + up -= b.nr; + for(i=0; i BUFSIZE) + n = BUFSIZE; + log.read(up+i, buf, 0, n); + t.insert(b.q0+i, buf.s, n, TRUE, 0); + } + # t.q0 = b.q0; + # t.q1 = b.q0+b.nr; + break; + +# Filename => +# f.seq = u.seq; +# f.unsetname(epsilon); +# f.mod = u.mod; +# up -= u.n; +# if(u.n == 0) +# f.name = nil; +# else{ +# fn0 := stralloc(u.n); +# delta.read(up, fn0, 0, u.n); +# f.name = fn0.s; +# strfree(fn0); +# } +# break; +# + } + log.delete(up, log.nc); + } + strfree(buf); + strfree(a); + elogterm(f); + + t.q0 = q0; + t.q1 = q1; + if(t.q1 > f.buf.nc) # can't happen + t.q1 = f.buf.nc; +} diff --git a/appl/acme/elog.m b/appl/acme/elog.m new file mode 100644 index 00000000..b935ad6f --- /dev/null +++ b/appl/acme/elog.m @@ -0,0 +1,24 @@ +Editlog: module { + + PATH: con "/dis/acme/elog.dis"; + + Elog: adt{ + typex: int; # Delete, Insert, Filename + q0: int; # location of change (unused in f) + nd: int; # number of deleted characters + nr: int; # runes in string or file name + r: ref Dat->Astring; + }; + + init : fn(mods : ref Dat->Mods); + + elogterm: fn(a0: ref Filem->File); + elogclose: fn(a0: ref Filem->File); + eloginsert: fn(a0: ref Filem->File, a1: int, a2: string, a3: int); + elogdelete: fn(a0: ref Filem->File, a1: int, a2: int); + elogreplace: fn(a0: ref Filem->File, a1: int, a2: int, a3: string, a4: int); + elogapply: fn(a0: ref Filem->File); + +}; + + \ No newline at end of file diff --git a/appl/acme/exec.b b/appl/acme/exec.b new file mode 100644 index 00000000..204b6c37 --- /dev/null +++ b/appl/acme/exec.b @@ -0,0 +1,1350 @@ +implement Exec; + +include "common.m"; + +sys : Sys; +dat : Dat; +acme : Acme; +utils : Utils; +graph : Graph; +gui : Gui; +lookx : Look; +bufferm : Bufferm; +textm : Textm; +scrl : Scroll; +filem : Filem; +windowm : Windowm; +rowm : Rowm; +columnm : Columnm; +fsys : Fsys; +editm: Edit; + +Dir, OREAD, OWRITE : import Sys; +EVENTSIZE, QWaddr, QWdata, QWevent, Astring : import dat; +Lock, Reffont, Ref, seltext, seq, row : import dat; +warning, error, skipbl, findbl, stralloc, strfree, exec : import utils; +dirname : import lookx; +Body, Text : import textm; +File : import filem; +sprint : import sys; +TRUE, FALSE, XXX, BUFSIZE : import Dat; +Buffer : import bufferm; +Row : import rowm; +Column : import columnm; +Window : import windowm; +setalphabet: import textm; + +# snarfbuf : ref Buffer; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + dat = mods.dat; + acme = mods.acme; + utils = mods.utils; + graph = mods.graph; + gui = mods.gui; + lookx = mods.look; + bufferm = mods.bufferm; + textm = mods.textm; + scrl = mods.scroll; + filem = mods.filem; + rowm = mods.rowm; + windowm = mods.windowm; + columnm = mods.columnm; + fsys = mods.fsys; + editm = mods.edit; + + snarfbuf = bufferm->newbuffer(); +} + +Exectab : adt { + name : string; + fun : int; + mark : int; + flag1 : int; + flag2 : int; +}; + +F_ALPHABET, F_CUT, F_DEL, F_DELCOL, F_DUMP, F_EDIT, F_EXITX, F_FONTX, F_GET, F_ID, F_INCL, F_KILL, F_LIMBO, F_LINENO, F_LOCAL, F_LOOK, F_NEW, F_NEWCOL, F_PASTE, F_PUT, F_PUTALL, F_UNDO, F_SEND, F_SORT, F_TAB, F_ZEROX : con iota; + +exectab := array[] of { + Exectab ( "Alphabet", F_ALPHABET, FALSE, XXX, XXX ), + Exectab ( "Cut", F_CUT, TRUE, TRUE, TRUE ), + Exectab ( "Del", F_DEL, FALSE, FALSE, XXX ), + Exectab ( "Delcol", F_DELCOL, FALSE, XXX, XXX ), + Exectab ( "Delete", F_DEL, FALSE, TRUE, XXX ), + Exectab ( "Dump", F_DUMP, FALSE, TRUE, XXX ), + Exectab ( "Edit", F_EDIT, FALSE, XXX, XXX ), + Exectab ( "Exit", F_EXITX, FALSE, XXX, XXX ), + Exectab ( "Font", F_FONTX, FALSE, XXX, XXX ), + Exectab ( "Get", F_GET, FALSE, TRUE, XXX ), + Exectab ( "ID", F_ID, FALSE, XXX, XXX ), + Exectab ( "Incl", F_INCL, FALSE, XXX, XXX ), + Exectab ( "Kill", F_KILL, FALSE, XXX, XXX ), + Exectab ( "Limbo", F_LIMBO, FALSE, XXX, XXX ), + Exectab ( "Lineno", F_LINENO, FALSE, XXX, XXX ), + Exectab ( "Load", F_DUMP, FALSE, FALSE, XXX ), + Exectab ( "Local", F_LOCAL, FALSE, XXX, XXX ), + Exectab ( "Look", F_LOOK, FALSE, XXX, XXX ), + Exectab ( "New", F_NEW, FALSE, XXX, XXX ), + Exectab ( "Newcol", F_NEWCOL, FALSE, XXX, XXX ), + Exectab ( "Paste", F_PASTE, TRUE, TRUE, XXX ), + Exectab ( "Put", F_PUT, FALSE, XXX, XXX ), + Exectab ( "Putall", F_PUTALL, FALSE, XXX, XXX ), + Exectab ( "Redo", F_UNDO, FALSE, FALSE, XXX ), + Exectab ( "Send", F_SEND, TRUE, XXX, XXX ), + Exectab ( "Snarf", F_CUT, FALSE, TRUE, FALSE ), + Exectab ( "Sort", F_SORT, FALSE, XXX, XXX ), + Exectab ( "Tab", F_TAB, FALSE, XXX, XXX ), + Exectab ( "Undo", F_UNDO, FALSE, TRUE, XXX ), + Exectab ( "Zerox", F_ZEROX, FALSE, XXX, XXX ), + Exectab ( nil, 0, 0, 0, 0 ), +}; + +runfun(fun : int, et, t, argt : ref Text, flag1, flag2 : int, arg : string, narg : int) +{ + case (fun) { + F_ALPHABET => alphabet(et, argt, arg, narg); + F_CUT => cut(et, t, flag1, flag2); + F_DEL => del(et, flag1); + F_DELCOL => delcol(et); + F_DUMP => dump(argt, flag1, arg, narg); + F_EDIT => edit(et, argt, arg, narg); + F_EXITX => exitx(); + F_FONTX => fontx(et, t, argt, arg, narg); + F_GET => get(et, t, argt, flag1, arg, narg); + F_ID => id(et); + F_INCL => incl(et, argt, arg, narg); + F_KILL => kill(argt, arg, narg); + F_LIMBO => limbo(et); + F_LINENO => lineno(et); + F_LOCAL => local(et, argt, arg); + F_LOOK => look(et, t, argt); + F_NEW => lookx->new(et, t, argt, flag1, flag2, arg, narg); + F_NEWCOL => newcol(et); + F_PASTE => paste(et, t, flag1, flag2); + F_PUT => put(et, argt, arg, narg); + F_PUTALL => putall(); + F_UNDO => undo(et, flag1); + F_SEND => send(et, t); + F_SORT => sort(et); + F_TAB => tab(et, argt, arg, narg); + F_ZEROX => zerox(et, t); + * => error("bad case in runfun()"); + } +} + +lookup(r : string, n : int) : int +{ + nr : int; + + (r, n) = skipbl(r, n); + if(n == 0) + return -1; + (nil, nr) = findbl(r, n); + nr = n-nr; + for(i := 0; exectab[i].name != nil; i++) + if (r[0:nr] == exectab[i].name) + return i; + return -1; +} + +isexecc(c : int) : int +{ + if(lookx->isfilec(c)) + return 1; + return c=='<' || c=='|' || c=='>'; +} + +execute(t : ref Text, aq0 : int, aq1 : int, external : int, argt : ref Text) +{ + q0, q1 : int; + r : ref Astring; + s, dir, aa, a : string; + e : int; + c, n, f : int; + + q0 = aq0; + q1 = aq1; + if(q1 == q0){ # expand to find word (actually file name) + # if in selection, choose selection + if(t.q1>t.q0 && t.q0<=q0 && q0<=t.q1){ + q0 = t.q0; + q1 = t.q1; + }else{ + while(q10 && isexecc(c=t.readc(q0-1)) && c!=':') + q0--; + if(q1 == q0) + return; + } + } + r = stralloc(q1-q0); + t.file.buf.read(q0, r, 0, q1-q0); + e = lookup(r.s, q1-q0); + if(!external && t.w!=nil && t.w.nopen[QWevent]>byte 0){ + f = 0; + if(e >= 0) + f |= 1; + if(q0!=aq0 || q1!=aq1){ + t.file.buf.read(aq0, r, 0, aq1-aq0); + f |= 2; + } + (aa, a) = getbytearg(argt, TRUE, TRUE); + if(a != nil){ + if(len a > EVENTSIZE){ # too big; too bad + aa = a = nil; + warning(nil, "`argument string too long\n"); + return; + } + f |= 8; + } + c = 'x'; + if(t.what == Body) + c = 'X'; + n = aq1-aq0; + if(n <= EVENTSIZE) + t.w.event(sprint("%c%d %d %d %d %s\n", c, aq0, aq1, f, n, r.s[0:n])); + else + t.w.event(sprint("%c%d %d %d 0 \n", c, aq0, aq1, f)); + if(q0!=aq0 || q1!=aq1){ + n = q1-q0; + t.file.buf.read(q0, r, 0, n); + if(n <= EVENTSIZE) + t.w.event(sprint("%c%d %d 0 %d %s\n", c, q0, q1, n, r.s[0:n])); + else + t.w.event(sprint("%c%d %d 0 0 \n", c, q0, q1)); + } + if(a != nil){ + t.w.event(sprint("%c0 0 0 %d %s\n", c, len a, a)); + if(aa != nil) + t.w.event(sprint("%c0 0 0 %d %s\n", c, len aa, aa)); + else + t.w.event(sprint("%c0 0 0 0 \n", c)); + } + strfree(r); + r = nil; + a = aa = nil; + return; + } + if(e >= 0){ + if(exectab[e].mark && seltext!=nil) + if(seltext.what == Body){ + seq++; + seltext.w.body.file.mark(); + } + (s, n) = skipbl(r.s, q1-q0); + (s, n) = findbl(s, n); + (s, n) = skipbl(s, n); + runfun(exectab[e].fun, t, seltext, argt, exectab[e].flag1, exectab[e].flag2, s, n); + strfree(r); + r = nil; + return; + } + + (dir, n) = dirname(t, nil, 0); + if(n==1 && dir[0]=='.'){ # sigh + dir = nil; + n = 0; + } + (aa, a) = getbytearg(argt, TRUE, TRUE); + if(t.w != nil) + t.w.refx.inc(); + spawn run(t.w, r.s, dir, n, TRUE, aa, a, FALSE); +} + +printarg(argt : ref Text, q0 : int, q1 : int) : string +{ + buf : string; + + if(argt.what!=Body || argt.file.name==nil) + return nil; + if(q0 == q1) + buf = sprint("%s:#%d", argt.file.name, q0); + else + buf = sprint("%s:#%d,#%d", argt.file.name, q0, q1); + return buf; +} + +getarg(argt : ref Text, doaddr : int, dofile : int) : (string, string, int) +{ + r : ref Astring; + n : int; + e : Dat->Expand; + a : string; + ok : int; + + if(argt == nil) + return (nil, nil, 0); + a = nil; + argt.commit(TRUE); + (ok, e) = lookx->expand(argt, argt.q0, argt.q1); + if (ok) { + e.bname = nil; + if(len e.name && dofile){ + if(doaddr) + a = printarg(argt, e.q0, e.q1); + return (a, e.name, len e.name); + } + e.name = nil; + }else{ + e.q0 = argt.q0; + e.q1 = argt.q1; + } + n = e.q1 - e.q0; + r = stralloc(n); + argt.file.buf.read(e.q0, r, 0, n); + if(doaddr) + a = printarg(argt, e.q0, e.q1); + return(a, r.s, n); +} + +getbytearg(argt : ref Text, doaddr : int, dofile : int) : (string, string) +{ + r : string; + n : int; + aa : string; + + (aa, r, n) = getarg(argt, doaddr, dofile); + if(r == nil) + return (nil, nil); + return (aa, r); +} + +newcol(et : ref Text) +{ + c : ref Column; + + c = et.row.add(nil, -1); + if(c != nil) + c.add(nil, nil, -1).settag(); +} + +delcol(et : ref Text) +{ + c := et.col; + if(c==nil || !c.clean(FALSE)) + return; + for(i:=0; i 0){ + warning(nil, sys->sprint("can't delete column; %s is running an external command\n", w.body.file.name)); + return; + } + } + c.row.close(c, TRUE); +} + +del(et : ref Text, flag1 : int) +{ + if(et.col==nil || et.w == nil) + return; + if(flag1 || et.w.body.file.ntext>1 || et.w.clean(FALSE, FALSE)) + et.col.close(et.w, TRUE); +} + +sort(et : ref Text) +{ + if(et.col != nil) + et.col.sort(); +} + +seqof(w: ref Window, isundo: int): int +{ + # if it's undo, see who changed with us + if(isundo) + return w.body.file.seq; + # if it's redo, see who we'll be sync'ed up with + return w.body.file.redoseq(); +} + +undo(et : ref Text, flag1 : int) +{ + i, j: int; + c: ref Column; + w: ref Window; + seq: int; + + if(et==nil || et.w== nil) + return; + seq = seqof(et.w, flag1); + for(i=0; i0 && arg[0]!='/'){ + (dir, ndir) = dirname(t, nil, 0); + if(n==1 && dir[0]=='.'){ # sigh + dir = nil; + ndir = 0; + } + } + if(dir != nil){ + r = dir[0:ndir] + arg[0:n]; + dir = nil; + n += ndir; + }else + r = arg[0:n]; + } + return r; +} + +zerox(et : ref Text, t : ref Text) +{ + nw : ref Window; + c, locked : int; + + locked = FALSE; + if(t!=nil && t.w!=nil && t.w!=et.w){ + locked = TRUE; + c = 'M'; + if(et.w != nil) + c = et.w.owner; + t.w.lock(c); + } + if(t == nil) + t = et; + if(t==nil || t.w==nil) + return; + t = t.w.body; + if(t.w.isdir) + warning(nil, sprint("%s is a directory; Zerox illegal\n", t.file.name)); + else{ + nw = t.w.col.add(nil, t.w, -1); + # ugly: fix locks so w.unlock works + nw.lock1(t.w.owner); + } + if(locked) + t.w.unlock(); +} + +get(et : ref Text, t : ref Text, argt : ref Text, flag1 : int, arg : string, narg : int) +{ + name : string; + r : string; + i, n, dirty : int; + w : ref Window; + u : ref Text; + d : Dir; + ok : int; + + if(flag1) + if(et==nil || et.w==nil) + return; + if(!et.w.isdir && (et.w.body.file.buf.nc>0 && !et.w.clean(TRUE, FALSE))) + return; + w = et.w; + t = w.body; + name = getname(t, argt, arg, narg, FALSE); + if(name == nil){ + warning(nil, "no file name\n"); + return; + } + if(t.file.ntext>1){ + (ok, d) = sys->stat(name); + if (ok == 0 && d.qid.qtype & Sys->QTDIR) { + warning(nil, sprint("%s is a directory; can't read with multiple windows on it\n", name)); + return; + } + } + r = name; + n = len name; + for(i=0; iscrdraw(u); + } +} + +putfile(f: ref File, q0: int, q1: int, name: string) +{ + n : int; + r, s : ref Astring; + w : ref Window; + i, q : int; + fd : ref Sys->FD; + d : Dir; + ok : int; + + w = f.curtext.w; + + { + if(name == f.name){ + (ok, d) = sys->stat(name); + if(ok >= 0 && (f.dev!=d.dev || f.qidpath!=d.qid.path || f.mtimesprint("%s not written; file already exists\n", name)); + else + warning(nil, sys->sprint("%s modified since last read\n", name)); + raise "e"; + } + } + fd = sys->create(name, OWRITE, 8r664); # was 666 + if(fd == nil){ + warning(nil, sprint("can't create file %s: %r\n", name)); + raise "e"; + } + r = stralloc(BUFSIZE); + s = stralloc(BUFSIZE); + + { + (ok, d) = sys->fstat(fd); + if(ok>=0 && (d.mode&Sys->DMAPPEND) && d.length>big 0){ + warning(nil, sprint("%s not written; file is append only\n", name)); + raise "e"; + } + for(q = q0; q < q1; q += n){ + n = q1 - q; + if(n > BUFSIZE) + n = BUFSIZE; + f.buf.read(q, r, 0, n); + ab := array of byte r.s[0:n]; + if(sys->write(fd, ab, len ab) != len ab){ + ab = nil; + warning(nil, sprint("can't write file %s: %r\n", name)); + raise "e"; + } + ab = nil; + } + if(name == f.name){ + d0 : Dir; + + if(q0 != 0 || q1 != f.buf.nc){ + f.mod = TRUE; + w.dirty = TRUE; + f.unread = TRUE; + } + else{ + (ok, d0) = sys->fstat(fd); # use old values if we failed + if (ok >= 0) + d = d0; + f.qidpath = d.qid.path; + f.dev = d.dev; + f.mtime = d.mtime; + f.mod = FALSE; + w.dirty = FALSE; + f.unread = FALSE; + } + for(i=0; i + strfree(s); + strfree(r); + s = r = nil; + fd = nil; + raise "e"; + } + } + exception{ + * => + name = nil; + return; + } +} + +put(et : ref Text, argt : ref Text, arg : string, narg : int) +{ + namer : string; + name : string; + w : ref Window; + + if(et==nil || et.w==nil || et.w.isdir) + return; + w = et.w; + f := w.body.file; + + name = getname(w.body, argt, arg, narg, TRUE); + if(name == nil){ + warning(nil, "no file name\n"); + return; + } + namer = name; + putfile(f, 0, f.buf.nc, namer); + name = nil; +} + +dump(argt : ref Text, isdump : int, arg : string, narg : int) +{ + name : string; + + if(narg) + name = arg; + else + (nil, name) = getbytearg(argt, FALSE, TRUE); + if(isdump) + row.dump(name); + else { + if (!row.qlock.locked()) + error("row not locked in dump()"); + row.loadx(name, FALSE); + } + name = nil; +} + +cut(et : ref Text, t : ref Text, dosnarf : int, docut : int) +{ + q0, q1, n, locked, c : int; + r : ref Astring; + + # use current window if snarfing and its selection is non-null + if(et!=t && dosnarf && et.w!=nil){ + if(et.w.body.q1>et.w.body.q0){ + t = et.w.body; + t.file.mark(); # seq has been incremented by execute + } + else if(et.w.tag.q1>et.w.tag.q0) + t = et.w.tag; + } + if(t == nil) + return; + locked = FALSE; + if(t.w!=nil && et.w!=t.w){ + locked = TRUE; + c = 'M'; + if(et.w != nil) + c = et.w.owner; + t.w.lock(c); + } + if(t.q0 == t.q1){ + if(locked) + t.w.unlock(); + return; + } + if(dosnarf){ + q0 = t.q0; + q1 = t.q1; + snarfbuf.delete(0, snarfbuf.nc); + r = stralloc(BUFSIZE); + while(q0 < q1){ + n = q1 - q0; + if(n > BUFSIZE) + n = BUFSIZE; + t.file.buf.read(q0, r, 0, n); + snarfbuf.insert(snarfbuf.nc, r.s, n); + q0 += n; + } + strfree(r); + r = nil; + acme->putsnarf(); + } + if(docut){ + t.delete(t.q0, t.q1, TRUE); + t.setselect(t.q0, t.q0); + if(t.w != nil){ + scrl->scrdraw(t); + t.w.settag(); + } + }else if(dosnarf) # Snarf command + dat->argtext = t; + if(locked) + t.w.unlock(); +} + +paste(et : ref Text, t : ref Text, selectall : int, tobody: int) +{ + c : int; + q, q0, q1, n : int; + r : ref Astring; + + # if(tobody), use body of executing window (Paste or Send command) + if(tobody && et!=nil && et.w!=nil){ + t = et.w.body; + t.file.mark(); # seq has been incremented by execute + } + if(t == nil) + return; + + acme->getsnarf(); + if(t==nil || snarfbuf.nc==0) + return; + if(t.w!=nil && et.w!=t.w){ + c = 'M'; + if(et.w != nil) + c = et.w.owner; + t.w.lock(c); + } + cut(t, t, FALSE, TRUE); + q = 0; + q0 = t.q0; + q1 = t.q0+snarfbuf.nc; + r = stralloc(BUFSIZE); + while(q0 < q1){ + n = q1 - q0; + if(n > BUFSIZE) + n = BUFSIZE; + if(r == nil) + r = stralloc(n); + snarfbuf.read(q, r, 0, n); + t.insert(q0, r.s, n, TRUE, 0); + q += n; + q0 += n; + } + strfree(r); + r = nil; + if(selectall) + t.setselect(t.q0, q1); + else + t.setselect(q1, q1); + if(t.w != nil){ + scrl->scrdraw(t); + t.w.settag(); + } + if(t.w!=nil && et.w!=t.w) + t.w.unlock(); +} + +look(et : ref Text, t : ref Text, argt : ref Text) +{ + r : string; + s : ref Astring; + n : int; + + if(et != nil && et.w != nil){ + t = et.w.body; + (nil, r, n) = getarg(argt, FALSE, FALSE); + if(r == nil){ + n = t.q1-t.q0; + s = stralloc(n); + t.file.buf.read(t.q0, s, 0, n); + r = s.s; + } + lookx->search(t, r, n); + r = nil; + } +} + +send(et : ref Text, t : ref Text) +{ + if(et.w==nil) + return; + t = et.w.body; + if(t.q0 != t.q1) + cut(t, t, TRUE, FALSE); + t.setselect(t.file.buf.nc, t.file.buf.nc); + paste(t, t, TRUE, TRUE); + if(t.readc(t.file.buf.nc-1) != '\n'){ + t.insert(t.file.buf.nc, "\n", 1, TRUE, 0); + t.setselect(t.file.buf.nc, t.file.buf.nc); + } +} + +edit(et: ref Text, argt: ref Text, arg: string, narg: int) +{ + r: string; + leng: int; + + if(et == nil) + return; + (nil, r, leng) = getarg(argt, FALSE, TRUE); + seq++; + if(r != nil){ + editm->editcmd(et, r, leng); + r = nil; + }else + editm->editcmd(et, arg, narg); +} + +exitx() +{ + if(row.clean(TRUE)) + acme->acmeexit(nil); +} + +putall() +{ + i, j, e : int; + w : ref Window; + c : ref Column; + a : string; + + for(i=0; i byte 0) + continue; + a = w.body.file.name; + e = utils->access(a); + if(w.body.file.mod || w.body.ncache) + if(e < 0) + warning(nil, sprint("no auto-Put of %s: %r\n", a)); + else{ + w.commit(w.body); + put(w.body, nil, nil, 0); + } + a = nil; + } + } +} + +id(et : ref Text) +{ + if(et != nil && et.w != nil) + warning(nil, sprint("/mnt/acme/%d/\n", et.w.id)); +} + +limbo(et: ref Text) +{ + s := getname(et.w.body, nil, nil, 0, 0); + if(s == nil) + return; + for(l := len s; l > 0 && s[--l] != '/'; ) + ; + if(s[l] == '/') + s = s[l+1: ]; + s = "limbo -gw " + s; + (dir, n) := dirname(et, nil, 0); + if(n==1 && dir[0]=='.'){ # sigh + dir = nil; + n = 0; + } + spawn run(nil, s, dir, n, TRUE, nil, nil, FALSE); +} + +local(et : ref Text, argt : ref Text, arg : string) +{ + a, aa : string; + dir : string; + n : int; + + (aa, a) = getbytearg(argt, TRUE, TRUE); + + (dir, n) = dirname(et, nil, 0); + if(n==1 && dir[0]=='.'){ # sigh + dir = nil; + n = 0; + } + spawn run(nil, arg, dir, n, FALSE, aa, a, FALSE); +} + +kill(argt : ref Text, arg : string, narg : int) +{ + a, cmd, r : string; + na : int; + + (nil, r, na) = getarg(argt, FALSE, FALSE); + if(r != nil) + kill(nil, r, na); + # loop condition: *arg is not a blank + for(;;){ + (a, na) = findbl(arg, narg); + if(a == arg) + break; + cmd = arg[0:narg-na]; + dat->ckill <-= cmd; + (arg, narg) = skipbl(a, na); + } +} + +lineno(et : ref Text) +{ + n : int; + + if (et == nil || et.w == nil || (et = et.w.body) == nil) + return; + q0 := et.q0; + q1 := et.q1; + if (q0 < 0 || q1 < 0 || q0 > q1) + return; + ln0 := 1; + ln1 := 1; + rp := stralloc(BUFSIZE); + nc := et.file.buf.nc; + if (q0 >= nc) + q0 = nc-1; + if (q1 >= nc) + q1 = nc-1; + for (q := 0; q < q1; ) { + if (q+BUFSIZE > nc) + n = nc-q; + else + n = BUFSIZE; + et.file.buf.read(q, rp, 0, n); + for (i := 0; i < n && q < q1; i++) { + if (rp.s[i] == '\n') { + if (q < q0) + ln0++; + if (q < q1-1) + ln1++; + } + q++; + } + } + rp = nil; + if (et.file.name != nil) + file := et.file.name + ":"; + else + file = nil; + if (ln0 == ln1) + warning(nil, sprint("%s%d\n", file, ln0)); + else + warning(nil, sprint("%s%d,%d\n", file, ln0, ln1)); +} + +fontx(et : ref Text, t : ref Text, argt : ref Text, arg : string, narg : int) +{ + a, r, flag, file : string; + na, nf : int; + aa : string; + newfont : ref Reffont; + dp : ref Dat->Dirlist; + i, fix : int; + + if(et==nil || et.w==nil) + return; + t = et.w.body; + flag = nil; + file = nil; + # loop condition: *arg is not a blank + nf = 0; + for(;;){ + (a, na) = findbl(arg, narg); + if(a == arg) + break; + r = arg[0:narg-na]; + if(r == "fix" || r == "var"){ + flag = nil; + flag = r; + }else{ + file = r; + nf = narg-na; + } + (arg, narg) = skipbl(a, na); + } + (nil, r, na) = getarg(argt, FALSE, TRUE); + if(r != nil) + if(r == "fix" || r == "var"){ + flag = nil; + flag = r; + }else{ + file = r; + nf = na; + } + fix = 1; + if(flag != nil) + fix = flag == "fix"; + else if(file == nil){ + newfont = Reffont.get(FALSE, FALSE, FALSE, nil); + if(newfont != nil) + fix = newfont.f.name == t.frame.font.name; + } + if(file != nil){ + aa = file[0:nf]; + newfont = Reffont.get(fix, flag!=nil, FALSE, aa); + aa = nil; + }else + newfont = Reffont.get(fix, FALSE, FALSE, nil); + if(newfont != nil){ + graph->draw(gui->mainwin, t.w.r, acme->textcols[Framem->BACK], nil, (0, 0)); + t.reffont.close(); + t.reffont = newfont; + t.frame.font = newfont.f; + if(t.w.isdir){ + t.all.min.x++; # force recolumnation; disgusting! + for(i=0; istrwidth(newfont.f, aa); + aa = nil; + } + } + # avoid shrinking of window due to quantization + t.w.col.grow(t.w, -1, 1); + } + file = nil; + flag = nil; +} + +incl(et : ref Text, argt : ref Text, arg : string, narg : int) +{ + a, r : string; + w : ref Window; + na, n, leng : int; + + if(et==nil || et.w==nil) + return; + w = et.w; + n = 0; + (nil, r, leng) = getarg(argt, FALSE, TRUE); + if(r != nil){ + n++; + w.addincl(r, leng); + } + # loop condition: *arg is not a blank + for(;;){ + (a, na) = findbl(arg, narg); + if(a == arg) + break; + r = arg[0:narg-na]; + n++; + w.addincl(r, narg-na); + (arg, narg) = skipbl(a, na); + } + if(n==0 && w.nincl){ + for(n=w.nincl; --n>=0; ) + warning(nil, sprint("%s ", w.incl[n])); + warning(nil, "\n"); + } +} + +tab(et : ref Text, argt : ref Text, arg : string, narg : int) +{ + a, r, p : string; + w : ref Window; + na, leng, tab : int; + + if(et==nil || et.w==nil) + return; + w = et.w; + (nil, r, leng) = getarg(argt, FALSE, TRUE); + tab = 0; + if(r!=nil && leng>0){ + p = r[0:leng]; + if('0'<=p[0] && p[0]<='9') + tab = int p; + p = nil; + }else{ + (a, na) = findbl(arg, narg); + if(a != arg){ + p = arg[0:narg-na]; + if('0'<=p[0] && p[0]<='9') + tab = int p; + p = nil; + } + } + if(tab > 0){ + if(w.body.tabstop != tab){ + w.body.tabstop = tab; + w.reshape(w.r, 1); + } + }else + warning(nil, sys->sprint("%s: Tab %d\n", w.body.file.name, w.body.tabstop)); +} + +alphabet(et: ref Text, argt: ref Text, arg: string, narg: int) +{ + r: string; + leng: int; + + if(et == nil) + return; + (nil, r, leng) = getarg(argt, FALSE, FALSE); + if(r != nil) + setalphabet(r[0:leng]); + else + setalphabet(arg[0:narg]); +} + +runfeed(p : array of ref Sys->FD, c : chan of int) +{ + n : int; + buf : array of byte; + s : string; + + sys->pctl(Sys->FORKFD, nil); + c <-= 1; + # p[1] = nil; + buf = array[256] of byte; + for(;;){ + if((n = sys->read(p[0], buf, 256)) <= 0) + break; + s = string buf[0:n]; + dat->cerr <-= s; + s = nil; + } + buf = nil; + exit; +} + +run(win : ref Window, s : string, rdir : string, ndir : int, newns : int, argaddr : string, arg : string, iseditcmd: int) +{ + c : ref Dat->Command; + name, dir : string; + e, t : int; + av : list of string; + r : int; + incl : array of string; + inarg, i, nincl : int; + tfd : ref Sys->FD; + p : array of ref Sys->FD; + pc : chan of int; + winid : int; + + c = ref Dat->Command; + t = 0; + while(t < len s && (s[t]==' ' || s[t]=='\n' || s[t]=='\t')) + t++; + for(e=t; e < len s; e++) + if(s[e]==' ' || s[e]=='\n' || s[e]=='\t' ) + break; + name = s[t:e]; + e = utils->strrchr(name, '/'); + if(e >= 0) + name = name[e+1:]; + name += " "; # add blank here for ease in waittask + c.name = name; + name = nil; + pipechar := 0; + if (t < len s && (s[t] == '<' || s[t] == '|' || s[t] == '>')){ + pipechar = s[t++]; + s = s[1: ]; + } + c.pid = sys->pctl(0, nil); + c.iseditcmd = iseditcmd; + c.text = s; + dat->ccommand <-= c; + # + # must pctl() after communication because rendezvous name + # space is part of RFNAMEG. + # + + if(newns){ + wids : string = ""; + filename: string; + + if(win != nil){ + filename = win.body.file.name; + wids = string win.id; + nincl = win.nincl; + incl = array[nincl] of string; + for(i=0; iactivewin != nil) + winid = (dat->activewin).id; + } + # sys->pctl(Sys->FORKNS|Sys->FORKFD|Sys->NEWPGRP, nil); + sys->pctl(Sys->FORKNS|Sys->NEWFD|Sys->FORKENV|Sys->NEWPGRP, 0::1::2::fsys->fsyscfd()::nil); + if(rdir != nil){ + dir = rdir[0:ndir]; + sys->chdir(dir); # ignore error: probably app. window + dir = nil; + } + if(filename != nil) + utils->setenv("%", filename); + c.md = fsys->fsysmount(rdir, ndir, incl, nincl); + if(c.md == nil){ + # error("child: can't mount /mnt/acme"); + warning(nil, "can't mount /mnt/acme"); + exit; + } + if(winid > 0 && (pipechar=='|' || pipechar=='>')){ + buf := sys->sprint("/mnt/acme/%d/rdsel", winid); + tfd = sys->open(buf, OREAD); + } + else + tfd = sys->open("/dev/null", OREAD); + sys->dup(tfd.fd, 0); + tfd = nil; + if((winid > 0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){ + buf: string; + + if(iseditcmd){ + if(winid > 0) + buf = sprint("/mnt/acme/%d/editout", winid); + else + buf = sprint("/mnt/acme/editout"); + } + else + buf = sys->sprint("/mnt/acme/%d/wrsel", winid); + tfd = sys->open(buf, OWRITE); + } + else + tfd = sys->open("/dev/cons", OWRITE); + sys->dup(tfd.fd, 1); + tfd = nil; + if(winid > 0 && (pipechar=='|' || pipechar=='<')){ + tfd = sys->open("/dev/cons", OWRITE); + sys->dup(tfd.fd, 2); + } + else + sys->dup(1, 2); + tfd = nil; + utils->setenv("acmewin", wids); + }else{ + if(win != nil) + win.close(); + sys->pctl(Sys->FORKFD|Sys->NEWPGRP, nil); + if(rdir != nil){ + dir = rdir[0:ndir]; + sys->chdir(dir); # ignore error: probably app. window + dir = nil; + } + p = array[2] of ref Sys->FD; + if(sys->pipe(p) < 0){ + error("child: can't pipe"); + exit; + } + pc = chan of int; + spawn runfeed(p, pc); + <-pc; + pc = nil; + fsys->fsysclose(); + tfd = sys->open("/dev/null", OREAD); + sys->dup(tfd.fd, 0); + tfd = nil; + sys->dup(p[1].fd, 1); + sys->dup(1, 2); + p[0] = p[1] = nil; + } + + if(argaddr != nil) + utils->setenv("acmeaddr", argaddr); + hard := 0; + if(len s > 512-10) # may need to print into stack + hard = 1; + else { + inarg = FALSE; + for(e=0; e < len s; e++){ + r = s[e]; + if(r==' ' || r=='\t') + continue; + if(r < ' ') { + hard = 1; + break; + } + if(utils->strchr("#;&|^$=`'{}()<>[]*?^~`", r) >= 0) { + hard = 1; + break; + } + inarg = TRUE; + } + if (!hard) { + if(!inarg) + exit; + av = nil; + sa := -1; + for(e=0; e < len s; e++){ + r = s[e]; + if(r==' ' || r=='\t'){ + if (sa >= 0) { + av = s[sa:e] :: av; + sa = -1; + } + continue; + } + if (sa < 0) + sa = e; + } + if (sa >= 0) + av = s[sa:e] :: av; + if (arg != nil) + av = arg :: av; + av = utils->reverse(av); + c.av = av; + exec(hd av, av); + dat->cwait <-= string c.pid + " \"Exec\":"; + exit; + } + } + + if(arg != nil){ + s = sprint("%s '%s'", s, arg); # BUG: what if quote in arg? + c.text = s; + } + av = nil; + av = s :: av; + av = "-c" :: av; + av = "/dis/sh" :: av; + exec(hd av, av); + dat->cwait <-= string c.pid + " \"Exec\":"; + exit; +} + +# Nasty bug causes +# Edit ,|nonexistentcommand +# (or ,> or ,<) to lock up acme. Easy fix. Add these two lines +# to the failure case of runwaittask(): +# +# /sys/src/cmd/acme/exec.c:1287 a exec.c:1288,1289 +# else{ +# if(c->iseditcmd) +# sendul(cedit, 0); +# free(c->name); +# free(c->text); +# free(c); +# } + + diff --git a/appl/acme/exec.m b/appl/acme/exec.m new file mode 100644 index 00000000..9a93e24c --- /dev/null +++ b/appl/acme/exec.m @@ -0,0 +1,19 @@ +Exec : module { + PATH : con "/dis/acme/exec.dis"; + + snarfbuf : ref Bufferm->Buffer; + + init : fn(mods : ref Dat->Mods); + + fontx : fn(et : ref Textm->Text, t : ref Textm->Text, argt : ref Textm->Text, arg : string, narg : int); + get : fn(et, t, argt : ref Textm->Text, flag1 : int, arg : string, narg : int); + put : fn(et, argt : ref Textm->Text, arg : string, narg : int); + cut : fn(et, t : ref Textm->Text, flag1, flag2 : int); + paste : fn(et, t : ref Textm->Text, flag1 : int, flag2: int); + + getarg : fn(t : ref Textm->Text, m : int, n : int) : (string, string, int); + execute : fn(t : ref Textm->Text, aq0, aq1, external : int, argt : ref Textm->Text); + run : fn(w : ref Windowm->Window, s : string, rdir : string, ndir : int, newns : int, argaddr : string, arg : string, ise: int); + undo: fn(t: ref Textm->Text, flag: int); + putfile: fn(f: ref Filem->File, q0: int, q1: int, r: string); +}; \ No newline at end of file diff --git a/appl/acme/file.b b/appl/acme/file.b new file mode 100644 index 00000000..1f53cc66 --- /dev/null +++ b/appl/acme/file.b @@ -0,0 +1,331 @@ +implement Filem; + +include "common.m"; + +sys : Sys; +dat : Dat; +utils : Utils; +buffm : Bufferm; +textm : Textm; +editlog: Editlog; + +FALSE, TRUE, XXX, Delete, Insert, Filename, BUFSIZE, Astring : import Dat; +Buffer, newbuffer : import buffm; +Text : import textm; +error : import utils; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + dat = mods.dat; + utils = mods.utils; + buffm = mods.bufferm; + textm = mods.textm; + editlog = mods.editlog; +} + +# +# Structure of Undo list: +# The Undo structure follows any associated data, so the list +# can be read backwards: read the structure, then read whatever +# data is associated (insert string, file name) and precedes it. +# The structure includes the previous value of the modify bit +# and a sequence number; successive Undo structures with the +# same sequence number represent simultaneous changes. +# + +Undo : adt +{ + typex : int; # Delete, Insert, Filename + mod : int; # modify bit + seq : int; # sequence number + p0 : int; # location of change (unused in f) + n : int; # # runes in string or file name +}; + +Undosize : con 8; +SHM : con 16rffff; + +undotostr(t, m, s, p, n : int) : string +{ + a := "01234567"; + a[0] = t; + a[1] = m; + a[2] = s&SHM; + a[3] = (s>>16)&SHM; + a[4] = p&SHM; + a[5] = (p>>16)&SHM; + a[6] = n&SHM; + a[7] = (n>>16)&SHM; + return a; +} + +strtoundo(s: string): Undo +{ + u: Undo; + + u.typex = s[0]; + u.mod = s[1]; + u.seq = s[2]|(s[3]<<16); + u.p0 = s[4]|(s[5]<<16); + u.n = s[6]|(s[7]<<16); + return u; +} + +nullfile : File; + +File.addtext(f : self ref File, t : ref Text) : ref File +{ + if(f == nil) { + f = ref nullfile; + f.buf = newbuffer(); + f.delta = newbuffer(); + f.epsilon = newbuffer(); + f.ntext = 0; + f.unread = TRUE; + } + oft := f.text; + f.text = array[f.ntext+1] of ref Text; + f.text[0:] = oft[0:f.ntext]; + oft = nil; + f.text[f.ntext++] = t; + f.curtext = t; + return f; +} + +File.deltext(f : self ref File, t : ref Text) +{ + i : int; + + for(i=0; i f.buf.nc) + error("bad assert in File.insert"); + if(f.seq > 0) + f.uninsert(f.delta, p0, ns); + f.buf.insert(p0, s, ns); + if(ns) + f.mod = TRUE; +} + +File.uninsert(f : self ref File, delta : ref Buffer, p0 : int, ns : int) +{ + # undo an insertion by deleting + a := undotostr(Delete, f.mod, f.seq, p0, ns); + delta.insert(delta.nc, a, Undosize); +} + +File.delete(f : self ref File, p0 : int, p1 : int) +{ + if (p0>p1 || p0>f.buf.nc || p1>f.buf.nc) + error("bad assert in File.delete"); + if(f.seq > 0) + f.undelete(f.delta, p0, p1); + f.buf.delete(p0, p1); + if(p1 > p0) + f.mod = TRUE; +} + +File.undelete(f : self ref File, delta : ref Buffer, p0 : int, p1 : int) +{ + buf : ref Astring; + i, n : int; + + # undo a deletion by inserting + a := undotostr(Insert, f.mod, f.seq, p0, p1-p0); + m := p1-p0; + if(m > BUFSIZE) + m = BUFSIZE; + buf = utils->stralloc(m); + for(i=p0; i BUFSIZE) + n = BUFSIZE; + f.buf.read(i, buf, 0, n); + delta.insert(delta.nc, buf.s, n); + } + utils->strfree(buf); + buf = nil; + delta.insert(delta.nc, a, Undosize); +} + +File.setname(f : self ref File, name : string, n : int) +{ + if(f.seq > 0) + f.unsetname(f.delta); + f.name = name[0:n]; + f.unread = TRUE; +} + +File.unsetname(f : self ref File, delta : ref Buffer) +{ + # undo a file name change by restoring old name + a := undotostr(Filename, f.mod, f.seq, 0, len f.name); + if(f.name != nil) + delta.insert(delta.nc, f.name, len f.name); + delta.insert(delta.nc, a, Undosize); +} + +File.loadx(f : self ref File, p0 : int, fd : ref Sys->FD) : int +{ + if(f.seq > 0) + error("undo in file.load unimplemented"); + return f.buf.loadx(p0, fd); +} + +File.undo(f : self ref File, isundo : int, q0 : int, q1 : int) : (int, int) +{ + buf : ref Astring; + i, j, n, up : int; + stop : int; + delta, epsilon : ref Buffer; + u : Undo; + + a := utils->stralloc(Undosize); + if(isundo){ + # undo; reverse delta onto epsilon, seq decreases + delta = f.delta; + epsilon = f.epsilon; + stop = f.seq; + }else{ + # redo; reverse epsilon onto delta, seq increases + delta = f.epsilon; + epsilon = f.delta; + stop = 0; # don't know yet + } + + buf = utils->stralloc(BUFSIZE); + while(delta.nc > 0){ + up = delta.nc-Undosize; + delta.read(up, a, 0, Undosize); + u = strtoundo(a.s); + if(isundo){ + if(u.seq < stop){ + f.seq = u.seq; + utils->strfree(buf); + utils->strfree(a); + return (q0, q1); + } + }else{ + if(stop == 0) + stop = u.seq; + if(u.seq > stop){ + utils->strfree(buf); + utils->strfree(a); + return (q0, q1); + } + } + case(u.typex){ + Delete => + f.seq = u.seq; + f.undelete(epsilon, u.p0, u.p0+u.n); + f.mod = u.mod; + f.buf.delete(u.p0, u.p0+u.n); + for(j=0; j + f.seq = u.seq; + f.uninsert(epsilon, u.p0, u.n); + f.mod = u.mod; + up -= u.n; + # buf = utils->stralloc(BUFSIZE); + for(i=0; i BUFSIZE) + n = BUFSIZE; + delta.read(up+i, buf, 0, n); + f.buf.insert(u.p0+i, buf.s, n); + for(j=0; jstrfree(buf); + # buf = nil; + q0 = u.p0; + q1 = u.p0+u.n; + Filename => + f.seq = u.seq; + f.unsetname(epsilon); + f.mod = u.mod; + up -= u.n; + f.name = nil; + if(u.n == 0) + f.name = nil; + else { + fn0 := utils->stralloc(u.n); + delta.read(up, fn0, 0, u.n); + f.name = fn0.s; + utils->strfree(fn0); + fn0 = nil; + } + * => + error(sys->sprint("undo: 0x%ux", u.typex)); + error(""); + } + delta.delete(up, delta.nc); + } + utils->strfree(buf); + utils->strfree(a); + buf = nil; + if(isundo) + f.seq = 0; + return (q0, q1); +} + +File.reset(f : self ref File) +{ + f.delta.reset(); + f.epsilon.reset(); + f.seq = 0; +} + +File.close(f : self ref File) +{ + f.name = nil; + f.ntext = 0; + f.text = nil; + f.buf.close(); + f.delta.close(); + f.epsilon.close(); + editlog->elogclose(f); + f = nil; +} + +File.mark(f : self ref File) +{ + if(f.epsilon.nc) + f.epsilon.delete(0, f.epsilon.nc); + f.seq = dat->seq; +} + +File.redoseq(f : self ref File): int +{ + u: Undo; + delta: ref Buffer; + + delta = f.epsilon; + if(delta.nc == 0) + return ~0; + buf := utils->stralloc(Undosize); + delta.read(delta.nc-Undosize, buf, 0, Undosize); + u = strtoundo(buf.s); + utils->strfree(buf); + return u.seq; +} \ No newline at end of file diff --git a/appl/acme/file.m b/appl/acme/file.m new file mode 100644 index 00000000..7e68e40b --- /dev/null +++ b/appl/acme/file.m @@ -0,0 +1,40 @@ +Filem : module { + PATH : con "/dis/acme/file.dis"; + + init : fn(mods : ref Dat->Mods); + + File : adt { + buf : ref Bufferm->Buffer; # the data + delta : ref Bufferm->Buffer; # transcript of changes + epsilon : ref Bufferm->Buffer; # inversion of delta for redo + elogbuf: ref Bufferm->Buffer; # log of pending editor changes + elog: Editlog->Elog; # current pending change + name : string; # name of associated file + qidpath : big; # of file when read + mtime : int; # of file when read + dev : int; # of file when read + unread : int; # file has not been read from disk + editclean: int; # mark clean after edit command + seq : int; # if seq==0, File acts like Buffer + mod : int; + curtext : cyclic ref Textm->Text; # most recently used associated text + text : cyclic array of ref Textm->Text; # list of associated texts + ntext : int; + dumpid : int; # used in dumping zeroxed windows + + addtext : fn(f : self ref File, t : ref Textm->Text) : ref File; + deltext : fn(f : self ref File, t : ref Textm->Text); + insert : fn(f : self ref File, n : int, s : string, m : int); + delete : fn(f : self ref File, m : int, n : int); + loadx : fn(f : self ref File, p : int, fd : ref Sys->FD) : int; + setname : fn(f : self ref File, s : string, n : int); + undo : fn(f : self ref File, p : int, q : int, r : int) : (int, int); + mark : fn(f : self ref File); + reset : fn(f : self ref File); + close : fn(f : self ref File); + undelete : fn(f : self ref File, b : ref Bufferm->Buffer, m : int, n : int); + uninsert : fn(f : self ref File, b : ref Bufferm->Buffer, m : int, n : int); + unsetname : fn(f : self ref File, b : ref Bufferm->Buffer); + redoseq : fn(f: self ref File): int; + }; +}; diff --git a/appl/acme/frame.b b/appl/acme/frame.b new file mode 100644 index 00000000..5ab920a1 --- /dev/null +++ b/appl/acme/frame.b @@ -0,0 +1,1189 @@ +implement Framem; + +include "common.m"; + +sys : Sys; +drawm : Draw; +acme : Acme; +gui : Gui; +graph : Graph; +utils : Utils; +textm : Textm; + +sprint : import sys; +Point, Rect, Font, Image, Pointer : import drawm; +draw, berror, charwidth, strwidth : import graph; +black, white : import gui; + +SLOP : con 25; + +noglyphs := array[4] of { 16rFFFD, 16r80, '?', ' ' }; + +frame : ref Frame; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + drawm = mods.draw; + acme = mods.acme; + gui = mods.gui; + graph = mods.graph; + utils = mods.utils; + textm = mods.textm; + + frame = newframe(); +} + +nullframe : Frame; + +newframe() : ref Frame +{ + f := ref nullframe; + f.cols = array[NCOL] of ref Draw->Image; + return f; +} + +frdump(f : ref Frame) +{ + utils->debug(sprint("nchars=%d\n", f.nchars)); + for (i := 0; i < f.nbox; i++) { + utils->debug(sprint("box %d : ", i)); + fb := f.box[i]; + if (fb.nrune >= 0) + utils->debug(sprint("%d %d %s\n", fb.nrune, len fb.ptr, fb.ptr)); + else + utils->debug(sprint("%d\n", fb.nrune)); + } +} + +# debugcheck(f : ref Frame, n : int) +# { +# if (f.nchars != xfrstrlen(f, 0)) { +# utils->debug(sprint("%d : bad frame nchars\n", n)); +# frdump(f); +# berror(""); +# } +# } + +xfraddbox(f : ref Frame, bn : int, n : int) # add n boxes after bn, shift the rest up, + # * box[bn+n]==box[bn] +{ + i : int; + + if(bn > f.nbox) + berror("xfraddbox"); + # bn = f.nbox has same effect as bn = f.nbox-1 + if(f.nbox+n > f.nalloc) + xfrgrowbox(f, n+SLOP); + for (i=f.nbox; --i > bn; ) { + t := f.box[i+n]; + f.box[i+n] = f.box[i]; + f.box[i] = t; + } + if (bn < f.nbox) + *f.box[bn+n] = *f.box[bn]; + f.nbox+=n; +} + +xfrclosebox(f : ref Frame, n0 : int, n1 : int) # inclusive +{ + i: int; + + if(n0>=f.nbox || n1>=f.nbox || n1=f.nbox || n1>=f.nbox || n1=f.nbox || n1>=f.nbox) + berror("xfrfreebox"); + n1++; + for(i=n0; i= 0) { + f.box[i].nrune = 0; + f.box[i].ptr = nil; + } +} + +nilfrbox : Frbox; + +xfrgrowbox(f : ref Frame, delta : int) +{ + ofb := f.box; + f.box = array[f.nalloc+delta] of ref Frbox; + if(f.box == nil) + berror("xfrgrowbox"); + f.box[0:] = ofb[0:f.nalloc]; + for (i := 0; i < delta; i++) + f.box[i+f.nalloc] = ref nilfrbox; + f.nalloc += delta; + ofb = nil; +} + +dupbox(f : ref Frame, bn : int) +{ + if(f.box[bn].nrune < 0) + berror("dupbox"); + xfraddbox(f, bn, 1); + if(f.box[bn].nrune >= 0) { + f.box[bn+1].nrune = f.box[bn].nrune; + f.box[bn+1].ptr = f.box[bn].ptr; + } +} + +truncatebox(f : ref Frame, b : ref Frbox, n : int) # drop last n chars; no allocation done +{ + if(b.nrune<0 || b.nrune= 0 && len b.ptr != b.nrune) { +# frdump(f); +# berror(sprint("findbox %d %d %d\n", bn, p, q)); +# } + if(b.nrune >= 0) + nrune = b.nrune; + if(p+nrune > q) + break; + p += nrune; + } + if(p != q) + xfrsplitbox(f, bn++, q-p); + return bn; +} + +frdelete(f : ref Frame, p0 : int, p1 : int) : int +{ + pt0, pt1, ppt0 : Point; + n0, n1, n, s : int; + r : Rect; + nn0 : int; + col : ref Image; + + if(p0 >= f.nchars || p0 == p1 || f.b == nil) + return 0; + if(p1 > f.nchars) + p1 = f.nchars; + n0 = xfrfindbox(f, 0, 0, p0); + if(n0 == f.nbox) + berror("off end in frdelete"); + n1 = xfrfindbox(f, n0, p0, p1); + pt0 = xfrptofcharnb(f, p0, n0); + pt1 = frptofchar(f, p1); + if(f.p0 == f.p1) + frtick(f, frptofchar(f, f.p0), 0); + nn0 = n0; + ppt0 = pt0; + xfrfreebox(f, n0, n1-1); + f.modified = 1; + + # + # Invariants: + # pt0 points to beginning, pt1 points to end + # n0 is box containing beginning of stuff being deleted + # n1, b are box containing beginning of stuff to be kept after deletion + # cn1 is char position of n1 + # f.p0 and f.p1 are not adjusted until after all deletion is done + # region between pt0 and pt1 is clear + # + cn1 := p1; + while(pt1.x!=pt0.x && n1 0){ + if(n != b.nrune){ + xfrsplitbox(f, n1, n); + b = f.box[n1]; + } + r.max.x += b.wid; + draw(f.b, r, f.b, nil, pt1); + cn1 += b.nrune; + } + else{ + r.max.x += xfrnewwid0(f, pt0, b); + if(r.max.x > f.r.max.x) + r.max.x = f.r.max.x; + col = f.cols[BACK]; + if(f.p0<=cn1 && cn1 f.r.max.y) + berror("frptofchar in frdelete"); + if(n1 < f.nbox){ + q0, q1, q2 : int; + + q0 = pt0.y+f.font.height; + q1 = pt1.y+f.font.height; + q2 = pt2.y+f.font.height; + # rob: before was just q2 = pt1.y+f.font.height; + # q2 = pt2.y; + if(q2 > f.r.max.y) + q2 = f.r.max.y; + draw(f.b, (pt0, (pt0.x+(f.r.max.x-pt1.x), q0)), + f.b, nil, pt1); + draw(f.b, ((f.r.min.x, q0), (f.r.max.x, q0+(q2-q1))), + f.b, nil, (f.r.min.x, q1)); + frselectpaint(f, (pt2.x, pt2.y-(pt1.y-pt0.y)), pt2, f.cols[BACK]); + }else + frselectpaint(f, pt0, pt2, f.cols[BACK]); + } + xfrclosebox(f, n0, n1-1); + if(nn0>0 && f.box[nn0-1].nrune>=0 && ppt0.x-f.box[nn0-1].wid>=f.r.min.x){ + --nn0; + ppt0.x -= f.box[nn0].wid; + } + s = n0; + if(n0 < f.nbox-1) + s++; + xfrclean(f, ppt0, nn0, s); + if(f.p1 > p1) + f.p1 -= p1-p0; + else if(f.p1 > p0) + f.p1 = p0; + if(f.p0 > p1) + f.p0 -= p1-p0; + else if(f.p0 > p0) + f.p0 = p0; + f.nchars -= p1-p0; + if(f.p0 == f.p1) + frtick(f, frptofchar(f, f.p0), 1); + pt0 = frptofchar(f, f.nchars); + n = f.nlines; + f.nlines = (pt0.y-f.r.min.y)/f.font.height+(pt0.x>f.r.min.x); + return n - f.nlines; +} + +xfrredraw(f : ref Frame, pt : Point) +{ + nb : int; + + for(nb = 0; nb < f.nbox; nb++) { + b := f.box[nb]; + pt = xfrcklinewrap(f, pt, b); + if(b.nrune >= 0) + graph->stringx(f.b, pt, f.font, b.ptr, f.cols[TEXT]); + pt.x += b.wid; + } +} + +frdrawsel(f : ref Frame, pt : Point, p0 : int, p1 : int, issel : int) +{ + back, text : ref Image; + + if(f.ticked) + frtick(f, frptofchar(f, f.p0), 0); + if(p0 == p1){ + frtick(f, pt, issel); + return; + } + if(issel){ + back = f.cols[HIGH]; + text = f.cols[HTEXT]; + }else{ + back = f.cols[BACK]; + text = f.cols[TEXT]; + } + frdrawsel0(f, pt, p0, p1, back, text); +} + +frdrawsel0(f : ref Frame, pt : Point, p0 : int, p1 : int, back : ref Image, text : ref Image) +{ + b : ref Frbox; + nb, nr, w, x, trim : int; + qt : Point; + p : int; + ptr : string; + + p = 0; + trim = 0; + for(nb=0; nb= p0){ + qt = pt; + pt = xfrcklinewrap(f, pt, b); + if(pt.y > qt.y) + draw(f.b, (qt, (f.r.max.x, pt.y)), back, nil, qt); + } + ptr = b.ptr; + if(p < p0){ # beginning of region: advance into box + ptr = ptr[p0-p:]; + nr -= (p0-p); + p = p0; + } + trim = 0; + if(p+nr > p1){ # end of region: trim box + nr -= (p+nr)-p1; + trim = 1; + } + if(b.nrune<0 || nr==b.nrune) + w = b.wid; + else + w = strwidth(f.font, ptr[0:nr]); + x = pt.x+w; + if(x > f.r.max.x) + x = f.r.max.x; + draw(f.b, (pt, (x, pt.y+f.font.height)), back, nil, pt); + if(b.nrune >= 0) + graph->stringx(f.b, pt, f.font, ptr[0:nr], text); + pt.x += w; + p += nr; + } + # if this is end of last plain text box on wrapped line, fill to end of line + if(p1>p0 && nb>0 && nb0 && !trim){ + qt = pt; + pt = xfrcklinewrap(f, pt, f.box[nb]); + if(pt.y > qt.y) + draw(f.b, (qt, (f.r.max.x, pt.y)), back, nil, qt); + } +} + +frtick(f : ref Frame, pt : Point, ticked : int) +{ + r : Rect; + + if(f.ticked==ticked || f.tick==nil || !pt.in(f.r)) + return; + pt.x--; # looks best just left of where requested + r = (pt, (pt.x+FRTICKW, pt.y+f.font.height)); + if(ticked){ + draw(f.tickback, f.tickback.r, f.b, nil, pt); + draw(f.b, r, f.tick, nil, (0, 0)); + }else + draw(f.b, r, f.tickback, nil, (0, 0)); + f.ticked = ticked; +} + +xfrdraw(f : ref Frame, pt : Point) : Point +{ + nb, n : int; + + for(nb=0; nb < f.nbox; nb++){ + b := f.box[nb]; + pt = xfrcklinewrap0(f, pt, b); + if(pt.y == f.r.max.y){ + f.nchars -= xfrstrlen(f, nb); + xfrdelbox(f, nb, f.nbox-1); + break; + } + if(b.nrune > 0){ + n = xfrcanfit(f, pt, b); + if(n == 0) + berror("draw: xfrcanfit==0"); + if(n != b.nrune){ + xfrsplitbox(f, nb, n); + b = f.box[nb]; + } + pt.x += b.wid; + }else{ + if(b.bc == '\n') { + pt.x = f.r.min.x; + pt.y += f.font.height; + } + else + pt.x += xfrnewwid(f, pt, b); + } + } + return pt; +} + +xfrstrlen(f : ref Frame, nb : int) : int +{ + n, nrune : int; + + for(n=0; nbImage) +{ + f.font = ft; + f.scroll = 0; + f.maxtab = 8*charwidth(ft, '0'); + f.nbox = 0; + f.nalloc = 0; + f.nchars = 0; + f.nlines = 0; + f.p0 = 0; + f.p1 = 0; + f.box = nil; + f.lastlinefull = 0; + if(cols != nil) + for(i := 0; i < NCOL; i++) + f.cols[i] = cols[i]; + for (i = 0; i < len noglyphs; i++) { + if (charwidth(ft, noglyphs[i]) != 0) { + f.noglyph = noglyphs[i]; + break; + } + } + frsetrects(f, r, b); + if (f.tick==nil && f.cols[BACK] != nil) + frinittick(f); +} + +frinittick(f : ref Frame) +{ + ft : ref Font; + + ft = f.font; + f.tick = nil; + f.tick = graph->balloc(((0, 0), (FRTICKW, ft.height)), (gui->mainwin).chans, Draw->White); + if(f.tick == nil) + return; + f.tickback = graph->balloc(f.tick.r, (gui->mainwin).chans, Draw->White); + if(f.tickback == nil){ + f.tick = nil; + return; + } + # background color + draw(f.tick, f.tick.r, f.cols[BACK], nil, (0, 0)); + # vertical line + draw(f.tick, ((FRTICKW/2, 0), (FRTICKW/2+1, ft.height)), black, nil, (0, 0)); + # box on each end + # draw(f->tick, Rect(0, 0, FRTICKW, FRTICKW), f->cols[TEXT], nil, ZP); + # draw(f->tick, Rect(0, ft->height-FRTICKW, FRTICKW, ft->height), f->cols[TEXT], nil, ZP); +} + +frsetrects(f : ref Frame, r : Rect, b : ref Image) +{ + f.b = b; + f.entire = r; + f.r = r; + f.r.max.y -= (r.max.y-r.min.y)%f.font.height; + f.maxlines = (r.max.y-r.min.y)/f.font.height; +} + +frclear(f : ref Frame, freeall : int) +{ + if(f.nbox) + xfrdelbox(f, 0, f.nbox-1); + for (i := 0; i < f.nalloc; i++) + f.box[i] = nil; + if(freeall) + f.tick = f.tickback = nil; + f.box = nil; + f.ticked = 0; +} + +DELTA : con 25; +TMPSIZE : con 256; + +Plist : adt { + pt0 : Point; + pt1 : Point; +}; + +nalloc : int = 0; +pts : array of Plist; + +bxscan(f : ref Frame, rp : string, l : int, ppt : Point) : (Point, Point) +{ + w, c, nb, delta, nl, nr : int; + sp : int = 0; + + frame.r = f.r; + frame.b = f.b; + frame.font = f.font; + frame.maxtab = f.maxtab; + frame.nbox = 0; + frame.nchars = 0; + for(i := 0; i < NCOL; i++) + frame.cols[i] = f.cols[i]; + frame.noglyph = f.noglyph; + delta = DELTA; + nl = 0; + for(nb=0; sp= TMPSIZE) + break; + if ((cw := charwidth(frame.font, c)) == 0) { # used to be only for c == 0 + c = frame.noglyph; + cw = charwidth(frame.font, c); + nul = 1; + } + w += cw; + sp++; + nr++; + } + b = frame.box[nb]; + b.ptr = rp[ssp:sp]; + b.wid = w; + b.nrune = nr; + frame.nchars += nr; + if (nul) { + for (i = 0; i < nr; i++) + if (charwidth(frame.font, b.ptr[i]) == 0) + b.ptr[i] = frame.noglyph; + } + } + frame.nbox++; + } + ppt = xfrcklinewrap0(f, ppt, frame.box[0]); + return (xfrdraw(frame, ppt), ppt); +} + +chopframe(f : ref Frame, pt : Point, p : int, bn : int) +{ + nb, nrune : int; + + for(nb = bn; ; nb++){ + if(nb >= f.nbox) + berror("endofframe"); + b := f.box[nb]; + pt = xfrcklinewrap(f, pt, b); + if(pt.y >= f.r.max.y) + break; + nrune = b.nrune; + if(nrune < 0) + nrune = 1; + p += nrune; + pt = xfradvance(f, pt, b); + } + f.nchars = p; + f.nlines = f.maxlines; + if (nb < f.nbox) # BUG + xfrdelbox(f, nb, f.nbox-1); +} + +frinsert(f : ref Frame, rp : string, l : int, p0 : int) +{ + pt0, pt1, ppt0, ppt1, pt : Point; + s, n, n0, nn0, y : int; + r : Rect; + npts : int; + col : ref Image; + + if(p0 > f.nchars || l == 0 || f.b == nil) + return; + n0 = xfrfindbox(f, 0, 0, p0); + cn0 := p0; + nn0 = n0; + pt0 = xfrptofcharnb(f, p0, n0); + ppt0 = pt0; + (pt1, ppt0) = bxscan(f, rp, l, ppt0); + ppt1 = pt1; + if(n0 < f.nbox){ + b := f.box[n0]; + pt0 = xfrcklinewrap(f, pt0, b); # for frdrawsel() + ppt1 = xfrcklinewrap0(f, ppt1, b); + } + f.modified = 1; + # + # ppt0 and ppt1 are start and end of insertion as they will appear when + # insertion is complete. pt0 is current location of insertion position + # (p0); pt1 is terminal point (without line wrap) of insertion. + # + if(f.p0 == f.p1) + frtick(f, frptofchar(f, f.p0), 0); + + # + # Find point where old and new x's line up + # Invariants: + # pt0 is where the next box (b, n0) is now + # pt1 is where it will be after then insertion + # If pt1 goes off the rectangle, we can toss everything from there on + # + + for(npts=0; pt1.x!= pt0.x && pt1.y!=f.r.max.y && n0 0){ + n = xfrcanfit(f, pt1, b); + if(n == 0) + berror("xfrcanfit==0"); + if(n != b.nrune){ + xfrsplitbox(f, n0, n); + b = f.box[n0]; + } + } + if(npts == nalloc){ + opts := pts; + pts = array[npts+DELTA] of Plist; + pts[0:] = opts[0:npts]; + for (k := 0; k < DELTA; k++) + pts[k+npts].pt0 = pts[k+npts].pt1 = (0, 0); + opts = nil; + nalloc += DELTA; + b = f.box[n0]; + } + pts[npts].pt0 = pt0; + pts[npts].pt1 = pt1; + # has a text box overflowed off the frame? + if(pt1.y == f.r.max.y) + break; + pt0 = xfradvance(f, pt0, b); + pt1.x += xfrnewwid(f, pt1, b); + n0++; + nrune := b.nrune; + if(nrune < 0) + nrune = 1; + cn0 += nrune; + } + if(pt1.y > f.r.max.y) + berror("frinsert pt1 too far"); + if(pt1.y==f.r.max.y && n0f.r.min.x); + else if(pt1.y!=pt0.y){ + q0, q1 : int; + + y = f.r.max.y; + q0 = pt0.y+f.font.height; + q1 = pt1.y+f.font.height; + f.nlines += (q1-q0)/f.font.height; + if(f.nlines > f.maxlines) + chopframe(f, ppt1, p0, nn0); + if(pt1.y < y){ + r = f.r; + r.min.y = q1; + r.max.y = y; + if(q1 < y) + draw(f.b, r, f.b, nil, (f.r.min.x, q0)); + r.min = pt1; + r.max.x = pt1.x+(f.r.max.x-pt0.x); + r.max.y = q1; + draw(f.b, r, f.b, nil, pt0); + } + } + # + # Move the old stuff down to make room. The loop will move the stuff + # between the insertion and the point where the x's lined up. + # The draws above moved everything down after the point they lined up. + # + y = 0; + if(pt1.y == f.r.max.y) + y = pt1.y; + for(j := n0-1; --npts >= 0; --j){ + pt = pts[npts].pt1; + b := f.box[j]; + if(b.nrune > 0){ + r.min = pt; + r.max = r.min; + r.max.x += b.wid; + r.max.y += f.font.height; + draw(f.b, r, f.b, nil, pts[npts].pt0); + if(pt.y < y){ # clear bit hanging off right + r.min = pt; + r.max = pt; + r.min.x += b.wid; + r.max.x = f.r.max.x; + r.max.y += f.font.height; + if(f.p0<=cn0 && cn0= f.r.max.x) + r.max.x = f.r.max.x; + cn0--; + if(f.p0<=cn0 && cn00 && f.box[nn0-1].nrune>=0 && ppt0.x-f.box[nn0-1].wid>=f.r.min.x){ + --nn0; + ppt0.x -= f.box[nn0].wid; + } + n0 += frame.nbox; + s = n0; + if(n0 < f.nbox-1) + s++; + xfrclean(f, ppt0, nn0, s); + f.nchars += frame.nchars; + if(f.p0 >= p0) + f.p0 += frame.nchars; + if(f.p0 > f.nchars) + f.p0 = f.nchars; + if(f.p1 >= p0) + f.p1 += frame.nchars; + if(f.p1 > f.nchars) + f.p1 = f.nchars; + if(f.p0 == f.p1) + frtick(f, frptofchar(f, f.p0), 1); +} + +xfrptofcharptb(f : ref Frame, p : int, pt : Point, bn : int) : Point +{ + s : int; + l : int; + r : int; + + for( ; bn < f.nbox; bn++){ + b := f.box[bn]; + pt = xfrcklinewrap(f, pt, b); + l = b.nrune; + if(l < 0) + l = 1; + if(p < l){ + if(b.nrune > 0) + for(s = 0; p > 0; s++){ + r = b.ptr[s]; + pt.x += charwidth(f.font, r); + if(r==0 || pt.x>f.r.max.x) + berror("frptofchar"); + p--; + } + break; + } + p -= l; + pt = xfradvance(f, pt, b); + } + return pt; +} + +frptofchar(f : ref Frame, p : int) : Point +{ + return xfrptofcharptb(f, p, f.r.min, 0); +} + +xfrptofcharnb(f : ref Frame, p : int, nb : int) : Point # doesn't do final xfradvance to next line +{ + pt : Point; + nbox : int; + + nbox = f.nbox; + f.nbox = nb; + pt = xfrptofcharptb(f, p, f.r.min, 0); + f.nbox = nbox; + return pt; +} + +xfrgrid(f : ref Frame, p: Point) : Point +{ + p.y -= f.r.min.y; + p.y -= p.y%f.font.height; + p.y += f.r.min.y; + if(p.x > f.r.max.x) + p.x = f.r.max.x; + return p; +} + +frcharofpt(f : ref Frame, pt : Point) : int +{ + qt : Point; + bn, nrune : int; + s : int; + p : int; + r : int; + + pt = xfrgrid(f, pt); + qt = f.r.min; + + bn=0; + for(p=0; bn= pt.y) + break; + qt = xfradvance(f, qt, b); + nrune = b.nrune; + if(nrune < 0) + nrune = 1; + p += nrune; + } + + for(; bn pt.y) + break; + if(qt.x+b.wid > pt.x){ + if(b.nrune < 0) + qt = xfradvance(f, qt, b); + else{ + s = 0; + for(;;){ + r = b.ptr[s++]; + qt.x += charwidth(f.font, r); + if(qt.x > pt.x) + break; + p++; + } + } + }else{ + nrune = b.nrune; + if(nrune < 0) + nrune = 1; + p += nrune; + qt = xfradvance(f, qt, b); + } + } + return p; +} + +region(a, b : int) : int +{ + if(a < b) + return -1; + if(a == b) + return 0; + return 1; +} + +frselect(f : ref Frame, m : ref Pointer) # when called, button 1 is down +{ + p0, p1, q : int; + mp, pt0, pt1, qt : Point; + b, scrled, reg : int; + + mp = m.xy; + b = m.buttons; + + f.modified = 0; + frdrawsel(f, frptofchar(f, f.p0), f.p0, f.p1, 0); + p0 = p1 = frcharofpt(f, mp); + f.p0 = p0; + f.p1 = p1; + pt0 = frptofchar(f, p0); + pt1 = frptofchar(f, p1); + frdrawsel(f, pt0, p0, p1, 1); + do{ + scrled = 0; + if(f.scroll){ + if(m.xy.y < f.r.min.y){ + textm->framescroll(f, -(f.r.min.y-m.xy.y)/f.font.height-1); + p0 = f.p1; + p1 = f.p0; + scrled = 1; + }else if(m.xy.y > f.r.max.y){ + textm->framescroll(f, (m.xy.y-f.r.max.y)/f.font.height+1); + p0 = f.p0; + p1 = f.p1; + scrled = 1; + } + if(scrled){ + pt0 = frptofchar(f, p0); + pt1 = frptofchar(f, p1); + reg = region(p1, p0); + } + } + q = frcharofpt(f, m.xy); + if(p1 != q){ + if(reg != region(q, p0)){ # crossed starting point; reset + if(reg > 0) + frdrawsel(f, pt0, p0, p1, 0); + else if(reg < 0) + frdrawsel(f, pt1, p1, p0, 0); + p1 = p0; + pt1 = pt0; + reg = region(q, p0); + if(reg == 0) + frdrawsel(f, pt0, p0, p1, 1); + } + qt = frptofchar(f, q); + if(reg > 0){ + if(q > p1) + frdrawsel(f, pt1, p1, q, 1); + else if(q < p1) + frdrawsel(f, qt, q, p1, 0); + }else if(reg < 0){ + if(q > p1) + frdrawsel(f, pt1, p1, q, 0); + else + frdrawsel(f, qt, q, p1, 1); + } + p1 = q; + pt1 = qt; + } + f.modified = 0; + if(p0 < p1) { + f.p0 = p0; + f.p1 = p1; + } + else { + f.p0 = p1; + f.p1 = p0; + } + if(scrled) + textm->framescroll(f, 0); + graph->bflush(); + if(!scrled) + acme->frgetmouse(); + }while(m.buttons == b); +} + +frselectpaint(f : ref Frame, p0 : Point, p1 : Point, col : ref Image) +{ + n : int; + q0, q1 : Point; + + q0 = p0; + q1 = p1; + q0.y += f.font.height; + q1.y += f.font.height; + n = (p1.y-p0.y)/f.font.height; + if(f.b == nil) + berror("frselectpaint b==0"); + if(p0.y == f.r.max.y) + return; + if(n == 0) + draw(f.b, (p0, q1), col, nil, (0, 0)); + else{ + if(p0.x >= f.r.max.x) + p0.x = f.r.max.x-1; + draw(f.b, ((p0.x, p0.y), (f.r.max.x, q0.y)), col, nil, (0, 0)); + if(n > 1) + draw(f.b, ((f.r.min.x, q0.y), (f.r.max.x, p1.y)), + col, nil, (0, 0)); + draw(f.b, ((f.r.min.x, p1.y), (q1.x, q1.y)), + col, nil, (0, 0)); + } +} + +xfrcanfit(f : ref Frame, pt : Point, b : ref Frbox) : int +{ + left, nr : int; + p : int; + r : int; + + left = f.r.max.x-pt.x; + if(b.nrune < 0) + return b.minwid <= left; + if(left >= b.wid) + return b.nrune; + nr = 0; + for(p = 0; p < len b.ptr; p++){ + r = b.ptr[p]; + left -= charwidth(f.font, r); + if(left < 0) + return nr; + nr++; + } + berror("xfrcanfit can't"); + return 0; +} + +xfrcklinewrap(f : ref Frame, p : Point, b : ref Frbox) : Point +{ + wid : int; + + if(b.nrune < 0) + wid = b.minwid; + else + wid = b.wid; + + if(wid > f.r.max.x-p.x){ + p.x = f.r.min.x; + p.y += f.font.height; + } + return p; +} + +xfrcklinewrap0(f : ref Frame, p : Point, b : ref Frbox) : Point +{ + if(xfrcanfit(f, p, b) == 0){ + p.x = f.r.min.x; + p.y += f.font.height; + } + return p; +} + +xfrcklinewrap1(f : ref Frame, p : Point, wid : int) : Point +{ + if(wid > f.r.max.x-p.x){ + p.x = f.r.min.x; + p.y += f.font.height; + } + return p; +} + +xfradvance(f : ref Frame, p : Point, b : ref Frbox) : Point +{ + if(b.nrune<0 && b.bc=='\n'){ + p.x = f.r.min.x; + p.y += f.font.height; + }else + p.x += b.wid; + return p; +} + +xfrnewwid(f : ref Frame, pt : Point, b : ref Frbox) : int +{ + b.wid = xfrnewwid0(f, pt, b); + return b.wid; +} + +xfrnewwid0(f : ref Frame, pt : Point, b : ref Frbox) : int +{ + c, x : int; + + c = f.r.max.x; + x = pt.x; + if(b.nrune >= 0 || b.bc != '\t') + return b.wid; + if(x+b.minwid > c) + x = pt.x = f.r.min.x; + x += f.maxtab; + x -= (x-f.r.min.x)%f.maxtab; + if(x-pt.xc) + x = pt.x+b.minwid; + return x-pt.x; +} + +xfrclean(f : ref Frame, pt : Point, n0 : int, n1 : int) # look for mergeable boxes +{ + nb, c : int; + + c = f.r.max.x; + for(nb=n0; nb=0 && nb=0 && pt.x+b0.wid+b1.wid= f.r.max.y) + f.lastlinefull = 1; +} diff --git a/appl/acme/frame.m b/appl/acme/frame.m new file mode 100644 index 00000000..c581e350 --- /dev/null +++ b/appl/acme/frame.m @@ -0,0 +1,54 @@ +Framem : module { + PATH : con "/dis/acme/frame.dis"; + + BACK, HIGH, BORD, TEXT, HTEXT, NCOL : con iota; + + FRTICKW : con 3; + + init : fn(mods : ref Dat->Mods); + + newframe : fn() : ref Frame; + + Frbox : adt { + wid : int; # in pixels + nrune : int; # <0 ==> negate and treat as break char + ptr : string; + bc : int; # break char + minwid : int; + }; + + Frame : adt { + font : ref Draw->Font; # of chars in the frame + b : ref Draw->Image; # on which frame appears + cols : array of ref Draw->Image; # colours + r : Draw->Rect; # in which text appears + entire : Draw->Rect; # of full frame + box : array of ref Frbox; + scroll : int; # call framescroll function + p0 : int; + p1 : int; # selection + nbox, nalloc : int; + maxtab : int; # max size of tab, in pixels + nchars : int; # runes in frame + nlines : int; # lines with text + maxlines : int; # total # lines in frame + lastlinefull : int; # last line fills frame + modified : int; # changed since frselect() + noglyph : int; # char to use when a char has 0 width glyph + tick : ref Draw->Image; # typing tick + tickback : ref Draw->Image; # saved image under tick + ticked : int; # is tick on screen ? + }; + + frcharofpt : fn(f : ref Frame, p : Draw->Point) : int; + frptofchar : fn(f : ref Frame, c : int) : Draw->Point; + frdelete : fn(f : ref Frame, c1 : int, c2 : int) : int; + frinsert : fn(f : ref Frame, s : string, l : int, i : int); + frselect : fn(f : ref Frame, m : ref Draw->Pointer); + frinit : fn(f : ref Frame, r : Draw->Rect, f : ref Draw->Font, b : ref Draw->Image, cols : array of ref Draw->Image); + frsetrects : fn(f : ref Frame, r : Draw->Rect, b : ref Draw->Image); + frclear : fn(f : ref Frame, x : int); + frdrawsel : fn(f : ref Frame, p : Draw->Point, p0 : int, p1 : int, n : int); + frdrawsel0 : fn(f : ref Frame, p : Draw->Point, p0 : int, p1 : int, i1 : ref Draw->Image, i2 : ref Draw->Image); + frtick : fn(f : ref Frame, p : Draw->Point, n : int); +}; diff --git a/appl/acme/fsys.b b/appl/acme/fsys.b new file mode 100644 index 00000000..65ce4982 --- /dev/null +++ b/appl/acme/fsys.b @@ -0,0 +1,866 @@ +implement Fsys; + +include "common.m"; + +sys : Sys; +styx : Styx; +styxaux : Styxaux; +acme : Acme; +dat : Dat; +utils : Utils; +look : Look; +windowm : Windowm; +xfidm : Xfidm; + +QTDIR, QTFILE, QTAPPEND : import Sys; +DMDIR, DMAPPEND, Qid, ORCLOSE, OTRUNC, OREAD, OWRITE, ORDWR, Dir : import Sys; +sprint : import sys; +MAXWELEM, Rerror : import Styx; +Qdir,Qacme,Qcons,Qconsctl,Qdraw,Qeditout,Qindex,Qlabel,Qnew,QWaddr,QWbody,QWconsctl,QWctl,QWdata,QWeditout,QWevent,QWrdsel,QWwrsel,QWtag,QMAX : import Dat; +TRUE, FALSE : import Dat; +cxfidalloc, cerr : import dat; +Mntdir, Fid, Dirtab, Lock, Ref, Smsg0 : import dat; +Tmsg, Rmsg : import styx; +msize, version, fid, uname, aname, newfid, name, mode, offset, count, setmode : import styxaux; +Xfid : import xfidm; +row : import dat; +Column : import Columnm; +Window : import windowm; +lookid : import look; +warning, error : import utils; + +init(mods : ref Dat->Mods) +{ + messagesize = Styx->MAXRPC; + + sys = mods.sys; + styx = mods.styx; + styxaux = mods.styxaux; + acme = mods.acme; + dat = mods.dat; + utils = mods.utils; + look = mods.look; + windowm = mods.windowm; + xfidm = mods.xfidm; +} + +sfd, cfd : ref Sys->FD; + +Nhash : con 16; +DEBUG : con 0; + +fids := array[Nhash] of ref Fid; + +Eperm := "permission denied"; +Eexist := "file does not exist"; +Enotdir := "not a directory"; + +dirtab := array[10] of { + Dirtab ( ".", QTDIR, Qdir, 8r500|DMDIR ), + Dirtab ( "acme", QTDIR, Qacme, 8r500|DMDIR ), + Dirtab ( "cons", QTFILE, Qcons, 8r600 ), + Dirtab ( "consctl", QTFILE, Qconsctl, 8r000 ), + Dirtab ( "draw", QTDIR, Qdraw, 8r000|DMDIR ), + Dirtab ( "editout", QTFILE, Qeditout, 8r200 ), + Dirtab ( "index", QTFILE, Qindex, 8r400 ), + Dirtab ( "label", QTFILE, Qlabel, 8r600 ), + Dirtab ( "new", QTDIR, Qnew, 8r500|DMDIR ), + Dirtab ( nil, 0, 0, 0 ), +}; + +dirtabw := array[12] of { + Dirtab ( ".", QTDIR, Qdir, 8r500|DMDIR ), + Dirtab ( "addr", QTFILE, QWaddr, 8r600 ), + Dirtab ( "body", QTAPPEND, QWbody, 8r600|DMAPPEND ), + Dirtab ( "ctl", QTFILE, QWctl, 8r600 ), + Dirtab ( "consctl", QTFILE, QWconsctl, 8r200 ), + Dirtab ( "data", QTFILE, QWdata, 8r600 ), + Dirtab ( "editout", QTFILE, QWeditout, 8r200 ), + Dirtab ( "event", QTFILE, QWevent, 8r600 ), + Dirtab ( "rdsel", QTFILE, QWrdsel, 8r400 ), + Dirtab ( "wrsel", QTFILE, QWwrsel, 8r200 ), + Dirtab ( "tag", QTAPPEND, QWtag, 8r600|DMAPPEND ), + Dirtab ( nil, 0, 0, 0 ), +}; + +Mnt : adt { + qlock : ref Lock; + id : int; + md : ref Mntdir; +}; + +mnt : Mnt; +user : string; +clockfd : ref Sys->FD; +closing := 0; + +fsysinit() +{ + p : array of ref Sys->FD; + + p = array[2] of ref Sys->FD; + if(sys->pipe(p) < 0) + error("can't create pipe"); + cfd = p[0]; + sfd = p[1]; + clockfd = sys->open("/dev/time", Sys->OREAD); + user = utils->getuser(); + if (user == nil) + user = "Wile. E. Coyote"; + mnt.qlock = Lock.init(); + mnt.id = 0; + spawn fsysproc(); +} + +fsyscfd() : int +{ + return cfd.fd; +} + +QID(w, q : int) : int +{ + return (w<<8)|q; +} + +FILE(q : Qid) : int +{ + return int q.path & 16rFF; +} + +WIN(q : Qid) : int +{ + return (int q.path>>8) & 16rFFFFFF; +} + +# nullsmsg : Smsg; +nullsmsg0 : Smsg0; + +fsysproc() +{ + n, ok : int; + x : ref Xfid; + f : ref Fid; + t : Smsg0; + + acme->fsyspid = sys->pctl(0, nil); + x = nil; + for(;;){ + if(x == nil){ + cxfidalloc <-= nil; + x = <-cxfidalloc; + } + n = sys->read(sfd, x.buf, messagesize); + if(n <= 0) { + if (closing) + break; + error("i/o error on server channel"); + } + (ok, x.fcall) = Tmsg.unpack(x.buf[0:n]); + if(ok < 0) + error("convert error in convM2S"); + if(DEBUG) + utils->debug(sprint("%d:%s\n", x.tid, x.fcall.text())); + pick fc := x.fcall { + Version => + f = nil; + Auth => + f = nil; + * => + f = allocfid(fid(x.fcall)); + } + x.f = f; + pick fc := x.fcall { + Readerror => x = fsyserror(); + Flush => x = fsysflush(x); + Version => x = fsysversion(x); + Auth => x = fsysauth(x); + Attach => x = fsysattach(x, f); + Walk => x = fsyswalk(x, f); + Open => x = fsysopen(x, f); + Create => x = fsyscreate(x); + Read => x = fsysread(x, f); + Write => x = fsyswrite(x); + Clunk => x = fsysclunk(x, f); + Remove => x = fsysremove(x); + Stat => x = fsysstat(x, f); + Wstat => x = fsyswstat(x); + # Clone => x = fsysclone(x, f); + * => + x = respond(x, t, "bad fcall type"); + } + } +} + +fsysaddid(dir : string, ndir : int, incl : array of string, nincl : int) : ref Mntdir +{ + m : ref Mntdir; + id : int; + + mnt.qlock.lock(); + id = ++mnt.id; + m = ref Mntdir; + m.id = id; + m.dir = dir; + m.refs = 1; # one for Command, one will be incremented in attach + m.ndir = ndir; + m.next = mnt.md; + m.incl = incl; + m.nincl = nincl; + mnt.md = m; + mnt.qlock.unlock(); + return m; +} + +fsysdelid(idm : ref Mntdir) +{ + m, prev : ref Mntdir; + i : int; + + if(idm == nil) + return; + mnt.qlock.lock(); + if(--idm.refs > 0){ + mnt.qlock.unlock(); + return; + } + prev = nil; + for(m=mnt.md; m != nil; m=m.next){ + if(m == idm){ + if(prev != nil) + prev.next = m.next; + else + mnt.md = m.next; + for(i=0; isprint("fsysdelid: can't find id %d\n", idm.id); + cerr <-= buf; +} + +# +# Called only in exec.l:run(), from a different FD group +# +fsysmount(dir : string, ndir : int, incl : array of string, nincl : int) : ref Mntdir +{ + m : ref Mntdir; + + # close server side so don't hang if acme is half-exited + # sfd = nil; + m = fsysaddid(dir, ndir, incl, nincl); + buf := sys->sprint("%d", m.id); + if(sys->mount(cfd, nil, "/mnt/acme", Sys->MREPL, buf) < 0){ + fsysdelid(m); + return nil; + } + # cfd = nil; + sys->bind("/mnt/acme", "/chan", Sys->MBEFORE); # was MREPL + if(sys->bind("/mnt/acme", "/dev", Sys->MBEFORE) < 0){ + fsysdelid(m); + return nil; + } + return m; +} + +fsysclose() +{ + closing = 1; + # sfd = cfd = nil; +} + +respond(x : ref Xfid, t0 : Smsg0, err : string) : ref Xfid +{ + t : ref Rmsg; + + # t = nullsmsg; + tag := x.fcall.tag; + # fid := fid(x.fcall); + qid := t0.qid; + if(err != nil) + t = ref Rmsg.Error(tag, err); + else + pick fc := x.fcall { + Readerror => t = ref Rmsg.Error(tag, err); + Flush => t = ref Rmsg.Flush(tag); + Version => t = ref Rmsg.Version(tag, t0.msize, t0.version); + Auth => t = ref Rmsg.Auth(tag, qid); + # Clone => t = ref Rmsg.Clone(tag, fid); + Attach => t = ref Rmsg.Attach(tag, qid); + Walk => t = ref Rmsg.Walk(tag, t0.qids); + Open => t = ref Rmsg.Open(tag, qid, t0.iounit); + Create => t = ref Rmsg.Create(tag, qid, 0); + Read => if(t0.count == len t0.data) + t = ref Rmsg.Read(tag, t0.data); + else + t = ref Rmsg.Read(tag, t0.data[0: t0.count]); + Write => t = ref Rmsg.Write(tag, t0.count); + Clunk => t = ref Rmsg.Clunk(tag); + Remove => t = ref Rmsg.Remove(tag); + Stat => t = ref Rmsg.Stat(tag, t0.stat); + Wstat => t = ref Rmsg.Wstat(tag); + + } + # t.qid = t0.qid; + # t.count = t0.count; + # t.data = t0.data; + # t.stat = t0.stat; + # t.fid = x.fcall.fid; + # t.tag = x.fcall.tag; + buf := t.pack(); + if(buf == nil) + error("convert error in convS2M"); + if(sys->write(sfd, buf, len buf) != len buf) + error("write error in respond"); + buf = nil; + if(DEBUG) + utils->debug(sprint("%d:r: %s\n", x.tid, t.text())); + return x; +} + +# fsysnop(x : ref Xfid) : ref Xfid +# { +# t : Smsg0; +# +# return respond(x, t, nil); +# } + +fsyserror() : ref Xfid +{ + error("sys error : Terror"); + return nil; +} + +fsyssession(x : ref Xfid) : ref Xfid +{ + t : Smsg0; + + # BUG: should shut everybody down ?? + t = nullsmsg0; + return respond(x, t, nil); +} + +fsysversion(x : ref Xfid) : ref Xfid +{ + t : Smsg0; + + pick m := x.fcall { + Version => + (t.msize, t.version) = styx->compatible(m, messagesize, nil); + messagesize = t.msize; + return respond(x, t, nil); + } + return respond(x, t, "acme: bad version"); + + # ms := msize(x.fcall); + # if(ms < 256) + # return respond(x, t, "version: message size too small"); + # t.msize = messagesize = ms; + # v := version(x.fcall); + # if(len v < 6 || v[0: 6] != "9P2000") + # return respond(x, t, "unrecognized 9P version"); + # t.version = "9P2000"; + # return respond(x, t, nil); +} + +fsysauth(x : ref Xfid) : ref Xfid +{ + t : Smsg0; + + return respond(x, t, "acme: authentication not required"); +} + +fsysflush(x : ref Xfid) : ref Xfid +{ + x.c <-= Xfidm->Xflush; + return nil; +} + +fsysattach(x : ref Xfid, f : ref Fid) : ref Xfid +{ + t : Smsg0; + id : int; + m : ref Mntdir; + + if (uname(x.fcall) != user) + return respond(x, t, Eperm); + f.busy = TRUE; + f.open = FALSE; + f.qid = (Qid)(big Qdir, 0, QTDIR); + f.dir = dirtab; + f.nrpart = 0; + f.w = nil; + t.qid = f.qid; + f.mntdir = nil; + id = int aname(x.fcall); + mnt.qlock.lock(); + for(m=mnt.md; m != nil; m=m.next) + if(m.id == id){ + f.mntdir = m; + m.refs++; + break; + } + if(m == nil) + cerr <-= "unknown id in attach"; + mnt.qlock.unlock(); + return respond(x, t, nil); +} + +fsyswalk(x : ref Xfid, f : ref Fid) : ref Xfid +{ + t : Smsg0; + c, i, j, id : int; + path, qtype : int; + d, dir : array of Dirtab; + w : ref Window; + nf : ref Fid; + + if(f.open) + return respond(x, t, "walk of open file"); + if(fid(x.fcall) != newfid(x.fcall)){ + nf = allocfid(newfid(x.fcall)); + if(nf.busy) + return respond(x, t, "newfid already in use"); + nf.busy = TRUE; + nf.open = FALSE; + nf.mntdir = f.mntdir; + if(f.mntdir != nil) + f.mntdir.refs++; + nf.dir = f.dir; + nf.qid = f.qid; + nf.w = f.w; + nf.nrpart = 0; # not open, so must be zero + if(nf.w != nil) + nf.w.refx.inc(); + f = nf; # walk f + } + + qtype = QTFILE; + wqids: list of Qid; + err := string nil; + id = WIN(f.qid); + q := f.qid; + names := styxaux->names(x.fcall); + nwname := len names; + + if(nwname > 0){ + for(i = 0; i < nwname; i++){ + if((q.qtype & QTDIR) == 0){ + err = Enotdir; + break; + } + + name := names[i]; + if(name == ".."){ + path = Qdir; + qtype = QTDIR; + id = 0; + if(w != nil){ + w.close(); + w = nil; + } + if(i == MAXWELEM){ + err = "name too long"; + break; + } + q.qtype = qtype; + q.vers = 0; + q.path = big QID(id, path); + wqids = q :: wqids; + continue; + } + + # is it a numeric name? + regular := 0; + for(j=0; j < len name; j++) { + c = name[j]; + if(c<'0' || '9'Xwalk; + if(i == MAXWELEM){ + err = "name too long"; + break; + } + q.qtype = qtype; + q.vers = 0; + q.path = big QID(id, path); + wqids = q :: wqids; + continue; + } + + if(id == 0) + d = dirtab; + else + d = dirtabw; + k := 1; # skip '.' + found := 0; + for( ; d[k].name != nil; k++){ + if(name == d[k].name){ + path = d[k].qid; + qtype = d[k].qtype; + dir = d[k:]; + if(i == MAXWELEM){ + err = "name too long"; + break; + } + q.qtype = qtype; + q.vers = 0; + q.path = big QID(id, path); + wqids = q :: wqids; + found = 1; + break; + } + } + if(found) + continue; + break; # file not found + } + } + + if(i == 0 && err == nil) + err = Eexist; + } + + nwqid := len wqids; + if(nwqid > 0){ + t.qids = array[nwqid] of Qid; + for(i = nwqid-1; i >= 0; i--){ + t.qids[i] = hd wqids; + wqids = tl wqids; + } + } + if(err != nil || nwqid < nwname){ + if(nf != nil){ + nf.busy = FALSE; + fsysdelid(nf.mntdir); + } + } + else if(nwqid == nwname){ + if(w != nil){ + f.w = w; + w = nil; + } + if(dir != nil) + f.dir = dir; + f.qid = q; + } + + if(w != nil) + w.close(); + + return respond(x, t, err); +} + +fsysopen(x : ref Xfid, f : ref Fid) : ref Xfid +{ + t : Smsg0; + m : int; + + # can't truncate anything, so just disregard + setmode(x.fcall, mode(x.fcall)&~OTRUNC); + # can't execute or remove anything + if(mode(x.fcall)&ORCLOSE) + return respond(x, t, Eperm); + case(mode(x.fcall)){ + OREAD => + m = 8r400; + OWRITE => + m = 8r200; + ORDWR => + m = 8r600; + * => + return respond(x, t, Eperm); + } + if(((f.dir[0].perm&~(DMDIR|DMAPPEND))&m) != m) + return respond(x, t, Eperm); + x.c <-= Xfidm->Xopen; + return nil; +} + +fsyscreate(x : ref Xfid) : ref Xfid +{ + t : Smsg0; + + return respond(x, t, Eperm); +} + +idcmp(a, b : int) : int +{ + return a-b; +} + +qsort(a : array of int, n : int) +{ + i, j : int; + t : int; + + while(n > 1) { + i = n>>1; + t = a[0]; a[0] = a[i]; a[i] = t; + i = 0; + j = n; + for(;;) { + do + i++; + while(i < n && idcmp(a[i], a[0]) < 0); + do + j--; + while(j > 0 && idcmp(a[j], a[0]) > 0); + if(j < i) + break; + t = a[i]; a[i] = a[j]; a[j] = t; + } + t = a[0]; a[0] = a[j]; a[j] = t; + n = n-j-1; + if(j >= n) { + qsort(a, j); + a = a[j+1:]; + } else { + qsort(a[j+1:], n); + n = j; + } + } +} + +fsysread(x : ref Xfid, f : ref Fid) : ref Xfid +{ + t : Smsg0; + b : array of byte; + i, id, n, o, e, j, k, nids : int; + ids : array of int; + d : array of Dirtab; + dt : Dirtab; + c : ref Column; + clock : int; + + b = nil; + if(f.qid.qtype & QTDIR){ + # if(int offset(x.fcall) % DIRLEN) + # return respond(x, t, "illegal offset in directory"); + if(FILE(f.qid) == Qacme){ # empty dir + t.data = nil; + t.count = 0; + respond(x, t, nil); + return x; + } + o = int offset(x.fcall); + e = int offset(x.fcall)+count(x.fcall); + clock = getclock(); + b = array[messagesize] of byte; + id = WIN(f.qid); + n = 0; + if(id > 0) + d = dirtabw; + else + d = dirtab; + k = 1; # first entry is '.' + leng := 0; + for(i=0; d[k].name!=nil && ipackdir(dostat(WIN(x.f.qid), d[k], clock)); + leng = len bb; + for (kk := 0; kk < leng; kk++) + b[kk+n] = bb[kk]; + bb = nil; + if(leng <= Styx->BIT16SZ) + break; + if(i >= o) + n += leng; + k++; + } + if(id == 0){ + row.qlock.lock(); + nids = 0; + ids = nil; + for(j=0; jsprint("%d", k); + dt.qid = QID(k, 0); + dt.qtype = QTDIR; + dt.perm = DMDIR|8r700; + bb := styx->packdir(dostat(k, dt, clock)); + leng = len bb; + for (kk := 0; kk < leng; kk++) + b[kk+n] = bb[kk]; + bb = nil; + if(leng == 0) + break; + if(i >= o) + n += leng; + j++; + } + ids = nil; + } + t.data = b; + t.count = n; + respond(x, t, nil); + b = nil; + return x; + } + x.c <-= Xfidm->Xread; + return nil; +} + +fsyswrite(x : ref Xfid) : ref Xfid +{ + x.c <-= Xfidm->Xwrite; + return nil; +} + +fsysclunk(x : ref Xfid, f : ref Fid) : ref Xfid +{ + t : Smsg0; + + fsysdelid(f.mntdir); + if(f.open){ + f.busy = FALSE; + f.open = FALSE; + x.c <-= Xfidm->Xclose; + return nil; + } + if(f.w != nil) + f.w.close(); + f.busy = FALSE; + f.open = FALSE; + return respond(x, t, nil); +} + +fsysremove(x : ref Xfid) : ref Xfid +{ + t : Smsg0; + + return respond(x, t, Eperm); +} + +fsysstat(x : ref Xfid, f : ref Fid) : ref Xfid +{ + t : Smsg0; + + t.stat = dostat(WIN(x.f.qid), f.dir[0], getclock()); + return respond(x, t, nil); +} + +fsyswstat(x : ref Xfid) : ref Xfid +{ + t : Smsg0; + + return respond(x, t, Eperm); +} + +allocfid(fid : int) : ref Fid +{ + f, ff : ref Fid; + fh : int; + + ff = nil; + fh = fid&(Nhash-1); + for(f=fids[fh]; f != nil; f=f.next) + if(f.fid == fid) + return f; + else if(ff==nil && f.busy==FALSE) + ff = f; + if(ff != nil){ + ff.fid = fid; + return ff; + } + f = ref Fid; + f.busy = FALSE; + f.rpart = array[Sys->UTFmax] of byte; + f.nrpart = 0; + f.fid = fid; + f.next = fids[fh]; + fids[fh] = f; + return f; +} + +cbuf := array[32] of byte; + +getclock() : int +{ + sys->seek(clockfd, big 0, 0); + n := sys->read(clockfd, cbuf, len cbuf); + return int string cbuf[0:n]; +} + +dostat(id : int, dir : Dirtab, clock : int) : Sys->Dir +{ + d : Dir; + + d.qid.path = big QID(id, dir.qid); + d.qid.vers = 0; + d.qid.qtype = dir.qtype; + d.mode = dir.perm; + d.length = big 0; # would be nice to do better + d.name = dir.name; + d.uid = user; + d.gid = user; + d.atime = clock; + d.mtime = clock; + d.dtype = d.dev = 0; + return d; + # buf := styx->convD2M(d); + # d = nil; + # return buf; +} diff --git a/appl/acme/fsys.m b/appl/acme/fsys.m new file mode 100644 index 00000000..d964d7c4 --- /dev/null +++ b/appl/acme/fsys.m @@ -0,0 +1,18 @@ +Fsys : module { + PATH : con "/dis/acme/fsys.dis"; + + init : fn(mods : ref Dat->Mods); + + messagesize: int; + + QID : fn(w, f : int) : int; + FILE : fn(q : Sys->Qid) : int; + WIN : fn(q : Sys->Qid) : int; + + fsysinit : fn(); + fsyscfd : fn() : int; + fsysmount: fn(dir : string, ndir : int, incl : array of string, nincl : int) : ref Dat->Mntdir; + fsysdelid : fn(idm : ref Dat->Mntdir); + fsysclose: fn(); + respond : fn(x : ref Xfidm->Xfid, t : Dat->Smsg0, err : string) : ref Xfidm->Xfid; +}; \ No newline at end of file diff --git a/appl/acme/graph.b b/appl/acme/graph.b new file mode 100644 index 00000000..9b8eb738 --- /dev/null +++ b/appl/acme/graph.b @@ -0,0 +1,82 @@ +implement Graph; + +include "common.m"; + +sys : Sys; +drawm : Draw; +dat : Dat; +gui : Gui; +utils : Utils; + +Image, Point, Rect, Font, Display : import drawm; +black, white, display : import gui; +error : import utils; + +refp : ref Point; +pixarr : array of byte; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + drawm = mods.draw; + dat = mods.dat; + gui = mods.gui; + utils = mods.utils; + + refp = ref Point; + refp.x = refp.y = 0; +} + +charwidth(f : ref Font, c : int) : int +{ + s : string = "z"; + + s[0] = c; + return f.width(s); +} + +strwidth(f : ref Font, s : string) : int +{ + return f.width(s); +} + +balloc(r : Rect, c : Draw->Chans, col : int) : ref Image +{ + im := display.newimage(r, c, 0, col); + if (im == nil) + error("failed to get new image"); + return im; +} + +draw(d : ref Image, r : Rect, s : ref Image, m : ref Image, p : Point) +{ + d.draw(r, s, m, p); +} + +stringx(d : ref Image, p : Point, f : ref Font, s : string, c : ref Image) +{ + d.text(p, c, (0, 0), f, s); +} + +cursorset(p : Point) +{ + gui->cursorset(p); +} + +cursorswitch(c : ref Dat->Cursor) +{ + gui->cursorswitch(c); +} + +binit() +{ +} + +bflush() +{ +} + +berror(s : string) +{ + error(s); +} diff --git a/appl/acme/graph.m b/appl/acme/graph.m new file mode 100644 index 00000000..147771c2 --- /dev/null +++ b/appl/acme/graph.m @@ -0,0 +1,18 @@ +Graph : module { + PATH : con "/dis/acme/graph.dis"; + + init : fn(mods : ref Dat->Mods); + + balloc : fn(r : Draw->Rect, c : Draw->Chans, col : int) : ref Draw->Image; + draw : fn(d : ref Draw->Image, r : Draw->Rect, s : ref Draw->Image, m : ref Draw->Image, p : Draw->Point); + stringx : fn(d : ref Draw->Image, p : Draw->Point, f : ref Draw->Font, s : string, c : ref Draw->Image); + cursorset: fn(p : Draw->Point); + cursorswitch : fn(c : ref Dat->Cursor); + charwidth : fn(f : ref Draw->Font, c : int) : int; + strwidth : fn(f : ref Draw->Font, p : string) : int; + binit : fn(); + bflush : fn(); + berror : fn(s : string); + + font : ref Draw->Font; +}; \ No newline at end of file diff --git a/appl/acme/gui.b b/appl/acme/gui.b new file mode 100644 index 00000000..92e61f08 --- /dev/null +++ b/appl/acme/gui.b @@ -0,0 +1,126 @@ +implement Gui; + +include "common.m"; +include "tk.m"; +include "wmclient.m"; + wmclient: Wmclient; + +sys : Sys; +draw : Draw; +acme : Acme; +dat : Dat; +utils : Utils; + +Font, Point, Rect, Image, Context, Screen, Display, Pointer : import draw; +keyboardpid, mousepid : import acme; +ckeyboard, cmouse : import dat; +mousefd: ref Sys->FD; +error : import utils; + +win: ref Wmclient->Window; + +r2s(r: Rect): string +{ + return sys->sprint("%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y); +} + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + draw = mods.draw; + acme = mods.acme; + dat = mods.dat; + utils = mods.utils; + wmclient = load Wmclient Wmclient->PATH; + if(wmclient == nil) + error(sys->sprint("cannot load %s: %r", Wmclient->PATH)); + wmclient->init(); + + if(acme->acmectxt == nil) + acme->acmectxt = wmclient->makedrawcontext(); + display = (acme->acmectxt).display; + win = wmclient->window(acme->acmectxt, "Acme", Wmclient->Appl); + wmclient->win.reshape(((0, 0), (win.displayr.size().div(2)))); + cmouse = chan of ref Draw->Pointer; + ckeyboard = win.ctxt.kbd; + wmclient->win.onscreen("place"); + wmclient->win.startinput("kbd"::"ptr"::nil); + mainwin = win.image; + + yellow = display.color(Draw->Yellow); + green = display.color(Draw->Green); + red = display.color(Draw->Red); + blue = display.color(Draw->Blue); + black = display.color(Draw->Black); + white = display.color(Draw->White); +} + +spawnprocs() +{ + spawn mouseproc(); + spawn eventproc(); +} + +zpointer: Draw->Pointer; + +eventproc() +{ + for(;;) alt{ + e := <-win.ctl or + e = <-win.ctxt.ctl => + p := ref zpointer; + if(e == "exit"){ + p.buttons = Acme->M_QUIT; + cmouse <-= p; + }else{ + wmclient->win.wmctl(e); + if(win.image != mainwin){ + mainwin = win.image; + p.buttons = Acme->M_RESIZE; + cmouse <-= p; + } + } + } +} + +mouseproc() +{ + for(;;){ + p := <-win.ctxt.ptr; + if(wmclient->win.pointer(*p) == 0){ + p.buttons &= ~Acme->M_DOUBLE; + cmouse <-= p; + } + } +} + + +# consctlfd : ref Sys->FD; + +cursorset(p: Point) +{ + wmclient->win.wmctl("ptr " + string p.x + " " + string p.y); +} + +cursorswitch(cur: ref Dat->Cursor) +{ + s: string; + if(cur == nil) + s = "cursor"; + else{ + Hex: con "0123456789abcdef"; + s = sys->sprint("cursor %d %d %d %d ", cur.hot.x, cur.hot.y, cur.size.x, cur.size.y); + buf := cur.bits; + for(i := 0; i < len buf; i++){ + c := int buf[i]; + s[len s] = Hex[c >> 4]; + s[len s] = Hex[c & 16rf]; + } + } + wmclient->win.wmctl(s); +} + +killwins() +{ + wmclient->win.wmctl("exit"); +} diff --git a/appl/acme/gui.m b/appl/acme/gui.m new file mode 100644 index 00000000..b97e3b7d --- /dev/null +++ b/appl/acme/gui.m @@ -0,0 +1,15 @@ +Gui: module { + PATH: con "/dis/acme/gui.dis"; + WMPATH: con "/dis/acme/guiwm.dis"; + + display : ref Draw->Display; + mainwin : ref Draw->Image; + yellow, green, red, blue, black, white : ref Draw->Image; + + init : fn(mods : ref Dat->Mods); + spawnprocs : fn(); + cursorset : fn(p : Draw->Point); + cursorswitch: fn(c : ref Dat->Cursor); + + killwins : fn(); +}; \ No newline at end of file diff --git a/appl/acme/look.b b/appl/acme/look.b new file mode 100644 index 00000000..1004b803 --- /dev/null +++ b/appl/acme/look.b @@ -0,0 +1,743 @@ +implement Look; + +include "common.m"; + +sys : Sys; +draw : Draw; +utils : Utils; +dat : Dat; +graph : Graph; +acme : Acme; +framem : Framem; +regx : Regx; +bufferm : Bufferm; +textm : Textm; +windowm : Windowm; +columnm : Columnm; +exec : Exec; +scrl : Scroll; +plumbmsg : Plumbmsg; + +sprint : import sys; +Point : import draw; +warning, isalnum, stralloc, strfree, strchr, tgetc : import utils; +Range, TRUE, FALSE, XXX, BUFSIZE, Astring : import Dat; +Expand, seltext, row : import dat; +cursorset : import graph; +frptofchar : import framem; +isaddrc, isregexc, address : import regx; +Buffer : import bufferm; +Text : import textm; +Window : import windowm; +Column : import columnm; +Msg : import plumbmsg; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + draw = mods.draw; + utils = mods.utils; + graph = mods.graph; + acme = mods.acme; + framem = mods.framem; + regx = mods.regx; + dat = mods.dat; + bufferm = mods.bufferm; + textm = mods.textm; + windowm = mods.windowm; + columnm = mods.columnm; + exec = mods.exec; + scrl = mods.scroll; + plumbmsg = mods.plumbmsg; +} + +nuntitled : int; + +look3(t : ref Text, q0 : int, q1 : int, external : int) +{ + n, c, f : int; + ct : ref Text; + e : Expand; + r : ref Astring; + expanded : int; + + ct = seltext; + if(ct == nil) + seltext = t; + (expanded, e) = expand(t, q0, q1); + if(!external && t.w!=nil && t.w.nopen[Dat->QWevent]>byte 0){ + if(!expanded) + return; + f = 0; + if((e.at!=nil && t.w!=nil) || (e.name!=nil && lookfile(e.name, len e.name)!=nil)) + f = 1; # acme can do it without loading a file + if(q0!=e.q0 || q1!=e.q1) + f |= 2; # second (post-expand) message follows + if(e.name != nil) + f |= 4; # it's a file name + c = 'l'; + if(t.what == Textm->Body) + c = 'L'; + n = q1-q0; + if(n <= Dat->EVENTSIZE){ + r = stralloc(n); + t.file.buf.read(q0, r, 0, n); + t.w.event(sprint("%c%d %d %d %d %s\n", c, q0, q1, f, n, r.s[0:n])); + strfree(r); + r = nil; + }else + t.w.event(sprint("%c%d %d %d 0 \n", c, q0, q1, f)); + if(q0==e.q0 && q1==e.q1) + return; + if(e.name != nil){ + n = len e.name; + if(e.a1 > e.a0) + n += 1+(e.a1-e.a0); + r = stralloc(n); + for (i := 0; i < len e.name; i++) + r.s[i] = e.name[i]; + if(e.a1 > e.a0){ + r.s[len e.name] = ':'; + e.at.file.buf.read(e.a0, r, len e.name+1, e.a1-e.a0); + } + }else{ + n = e.q1 - e.q0; + r = stralloc(n); + t.file.buf.read(e.q0, r, 0, n); + } + f &= ~2; + if(n <= Dat->EVENTSIZE) + t.w.event(sprint("%c%d %d %d %d %s\n", c, e.q0, e.q1, f, n, r.s[0:n])); + else + t.w.event(sprint("%c%d %d %d 0 \n", c, e.q0, e.q1, f)); + strfree(r); + r = nil; + return; + } + if(0 && dat->plumbed){ # don't do yet : 2 acmes running => only 1 receives msg + m := ref Msg; + m.src = "acme"; + m.dst = nil; + (dir, nil) := dirname(t, nil, 0); + if(dir == ".") # sigh + dir = nil; + if(dir == nil) + dir = acme->wdir; + m.dir = dir; + m.kind = "text"; + m.attr = nil; + if(q1 == q0){ + if(t.q1>t.q0 && t.q0<=q0 && q0<=t.q1){ + q0 = t.q0; + q1 = t.q1; + }else{ + p := q0; + while(q0 > 0 && (c = tgetc(t, q0-1)) != ' ' && c != '\t' && c != '\n') + q0--; + while(q1 < t.file.buf.nc && (c = tgetc(t, q1)) != ' ' && c != '\t' && c != '\n') + q1++; + if(q1 == q0) + return; + m.attr = "click=" + string (p-q0); + } + } + r = stralloc(q1-q0); + t.file.buf.read(q0, r, 0, q1-q0); + m.data = array of byte r.s; + strfree(r); + if(m.send() >= 0) + return; + # plumber failed to match : fall through + } + if(!expanded) + return; + if(e.name != nil || e.at != nil) + (nil, e) = openfile(t, e); + else{ + if(t.w == nil) + return; + ct = t.w.body; + if(t.w != ct.w) + ct.w.lock('M'); + if(t == ct) + ct.setselect(e.q1, e.q1); + n = e.q1 - e.q0; + r = stralloc(n); + t.file.buf.read(e.q0, r, 0, n); + if(search(ct, r.s, n) && e.jump) + cursorset(frptofchar(ct.frame, ct.frame.p0).add((4, ct.frame.font.height-4))); + if(t.w != ct.w) + ct.w.unlock(); + strfree(r); + r = nil; + } + e.name = nil; + e.bname = nil; +} + +plumblook(m : ref Msg) +{ + e : Expand; + + if (len m.data > Dat->PLUMBSIZE) { + warning(nil, sys->sprint("plumb message too long : %s\n", string m.data)); + return; + } + e.q0 = e.q1 = 0; + if (len m.data == 0) + return; + e.ar = nil; + e.name = string m.data; + if(e.name[0] != '/' && m.dir != nil) + e.name = m.dir + "/" + e.name; + (e.name, nil) = cleanname(e.name, len e.name); + e.bname = e.name; + e.jump = TRUE; + e.a0 = e.a1 = 0; + (found, addr) := plumbmsg->lookup(plumbmsg->string2attrs(m.attr), "addr"); + if (found && addr != nil) { + e.ar = addr; + e.a1 = len addr; + } + openfile(nil, e); + e.at = nil; +} + +plumbshow(m : ref Msg) +{ + w := utils->newwindow(nil); + (found, name) := plumbmsg->lookup(plumbmsg->string2attrs(m.attr), "filename"); + if (!found || name == nil) { + nuntitled++; + name = "Untitled-" + string nuntitled; + } + if (name[0] != '/' && m.dir != nil) + name = m.dir + "/" + name; + (name, nil) = cleanname(name, len name); + w.setname(name, len name); + d := string m.data; + w.body.insert(0, d, len d, TRUE, FALSE); + w.body.file.mod = FALSE; + w.dirty = FALSE; + w.settag(); + scrl->scrdraw(w.body); + w.tag.setselect(w.tag.file.buf.nc, w.tag.file.buf.nc); +} + +search(ct : ref Text, r : string, n : int) : int +{ + q, nb, maxn : int; + around : int; + s : ref Astring; + b, c : int; + + if(n==0 || n>ct.file.buf.nc) + return FALSE; + if(2*n > BUFSIZE){ + warning(nil, "string too long\n"); + return FALSE; + } + maxn = utils->max(2*n, BUFSIZE); + s = utils->stralloc(BUFSIZE); + b = nb = 0; + around = 0; + q = ct.q1; + for(;;){ + if(q >= ct.file.buf.nc){ + q = 0; + around = 1; + nb = 0; + } + if(nb > 0){ + for (c = 0; c < nb; c++) + if (s.s[b+c] == r[0]) + break; + if(c >= nb){ + q += nb; + nb = 0; + if(around && q>=ct.q1) + break; + continue; + } + q += c; + nb -= c; + b += c; + } + # reload if buffer covers neither string nor rest of file + if(nb= maxn) + nb = maxn-1; + ct.file.buf.read(q, s, 0, nb); + b = 0; + } + if(n <= nb && s.s[b:b+n] == r[0:n]){ + if(ct.w != nil){ + ct.show(q, q+n); + ct.w.settag(); + }else{ + ct.q0 = q; + ct.q1 = q+n; + } + seltext = ct; + utils->strfree(s); + s = nil; + return TRUE; + } + if(around && q>=ct.q1) + break; + --nb; + b++; + q++; + } + utils->strfree(s); + s = nil; + return FALSE; +} + +isfilec(r : int) : int +{ + if(isalnum(r)) + return TRUE; + if(strchr(".-+/:", r) >= 0) + return TRUE; + return FALSE; +} + +cleanname(b : string, n : int) : (string, int) +{ + i, j, found : int; + + b = b[0:n]; + # compress multiple slashes + for(i=0; i=2 && b[n-2]=='/' && b[n-1]=='.') { + --n; + b = b[0:n]; + } + do{ + # compress xx/.. + found = FALSE; + for(i=1; i<=n-3; i++) + if(b[i:i+3] == "/.."){ + if(i==n-3 || b[i+3]=='/'){ + found = TRUE; + break; + } + } + if(found) + for(j=i-1; j>=0; --j) + if(j==0 || b[j-1]=='/'){ + i += 3; # character beyond .. + if(iaccess(a); + if(n < 0) { + a = nil; + return (nil, 0); + } + file = nil; + return cleanname(a, m+nfile); +} + +objdir : string; + +includename(t : ref Text , r : string, n : int) : (string, int) +{ + file : string; + i, nfile : int; + w : ref Window; + + { + w = t.w; + if(n==0 || r[0]=='/' || w==nil) + raise "e"; + if(n>2 && r[0]=='.' && r[1]=='/') + raise "e"; + file = nil; + nfile = 0; + (file, nfile) = includefile(".", r, n); + if (file == nil) { + (dr, dn) := dirname(t, r, n); + (file, nfile) = includefile(".", dr, dn); + } + if (file == nil) { + for(i=0; i + return (r, n); + } + return (nil, 0); +} + +dirname(t : ref Text, r : string, n : int) : (string, int) +{ + b : ref Astring; + c : int; + m, nt : int; + slash : int; + + { + b = nil; + if(t == nil || t.w == nil) + raise "e"; + nt = t.w.tag.file.buf.nc; + if(nt == 0) + raise "e"; + if(n>=1 && r[0]=='/') + raise "e"; + b = stralloc(nt+n+1); + t.w.tag.file.buf.read(0, b, 0, nt); + slash = -1; + for(m=0; m + b = nil; + if(r != nil) + return cleanname(r, n); + return (r, n); + } + return (nil, 0); +} + +expandfile(t : ref Text, q0 : int, q1 : int, e : Expand) : (int, Expand) +{ + i, n, nname, colon : int; + amin, amax : int; + r : ref Astring; + c : int; + w : ref Window; + + amax = q1; + if(q1 == q0){ + colon = -1; + while(q10 && (isfilec(c=t.readc(q0-1)) || isaddrc(c) || isregexc(c))){ + q0--; + if(colon==-1 && c==':') + colon = q0; + } + # + # if it looks like it might begin file: , consume address chars after : + # otherwise terminate expansion at : + # + + if(colon>=0 && colon= 0) + q1 = colon; + if(q1 > q0) + if(colon >= 0){ # stop at white space + for(amax=colon+1; amax, and turn that into an include + # file name if so. Should probably do it for "" too, but that's not + # restrictive enough syntax and checking for a #include earlier on the + # line would be silly. + # + + isfile := 0; + if(q0>0 && t.readc(q0-1)=='<' && q1') + (r.s, nname) = includename(t, r.s, nname); + else if(q0>0 && t.readc(q0-1)=='"' && q1access(e.bname) < 0){ + e.bname = nil; + strfree(r); + r = nil; + return (FALSE, e); + } + } + + e.name = r.s[0:nname]; + e.at = t; + e.a0 = amin+1; + (nil, e.a1, nil) = address(nil, nil, (Range)(-1,-1), (Range)(0, 0), t, nil, e.a0, amax, FALSE); + strfree(r); + r = nil; + return (TRUE, e); +} + +expand(t : ref Text, q0 : int, q1 : int) : (int, Expand) +{ + e : Expand; + ok : int; + + e.q0 = e.q1 = e.a0 = e.a1 = 0; + e.name = e.bname = nil; + e.at = nil; + # if in selection, choose selection + e.jump = TRUE; + if(q1==q0 && t.q1>t.q0 && t.q0<=q0 && q0<=t.q1){ + q0 = t.q0; + q1 = t.q1; + if(t.what == Textm->Tag) + e.jump = FALSE; + } + + (ok, e) = expandfile(t, q0, q1, e); + if (ok) + return (TRUE, e); + + if(q0 == q1){ + while(q10 && isalnum(t.readc(q0-1))) + q0--; + } + e.q0 = q0; + e.q1 = q1; + return (q1 > q0, e); +} + +lookfile(s : string, n : int) : ref Window +{ + i, j, k : int; + w : ref Window; + c : ref Column; + t : ref Text; + + # avoid terminal slash on directories + if(n > 1 && s[n-1] == '/') + --n; + for(j=0; j0 && t.file.name[k-1] == '/') + k--; + if(t.file.name[0:k] == s[0:n]){ + w = w.body.file.curtext.w; + if(w.col != nil) # protect against race deleting w + return w; + } + } + } + return nil; +} + +lookid(id : int, dump : int) : ref Window +{ + i, j : int; + w : ref Window; + c : ref Column; + + for(j=0; jnewwindow(t); + t = w.body; + w.setname(e.name, len e.name); + t.loadx(0, e.bname, 1); + t.file.mod = FALSE; + t.w.dirty = FALSE; + t.w.settag(); + t.w.tag.setselect(t.w.tag.file.buf.nc, t.w.tag.file.buf.nc); + if(ow != nil) + for(i=ow.nincl; --i>=0; ){ + n = len ow.incl[i]; + w.addincl(ow.incl[i], n); # really do want to copy here + } + } + if(e.a1 == e.a0) + eval = FALSE; + else + (eval, nil, r) = address(nil, t, (Range)(-1, -1), (Range)(t.q0, t.q1), e.at, e.ar, e.a0, e.a1, TRUE); + # was (eval, nil, r) = address(nil, t, (Range)(-1, -1), (Range)(t.q0, t.q1), e.at, nil, e.a0, e.a1, TRUE); + if(eval == FALSE){ + r.q0 = t.q0; + r.q1 = t.q1; + } + t.show(r.q0, r.q1); + t.w.settag(); + seltext = t; + if(e.jump) + cursorset(frptofchar(t.frame, t.frame.p0).add((4, t.frame.font.height-4))); + return (w, e); +} + +new(et : ref Text, t : ref Text, argt : ref Text, flag1 : int, flag2 : int, arg : string, narg : int) +{ + ndone : int; + a, f : string; + na, nf : int; + e : Expand; + + (nil, a, na) = exec->getarg(argt, FALSE, TRUE); + if(a != nil){ + new(et, t, nil, flag1, flag2, a, na); + if(narg == 0) + return; + } + # loop condition: *arg is not a blank + for(ndone=0; ; ndone++){ + (a, na) = utils->findbl(arg, narg); + if(a == arg){ + if(ndone==0 && et.col!=nil) + et.col.add(nil, nil, -1).settag(); + break; + } + nf = narg-na; + f = arg[0:nf]; # want a copy + (f, nf) = dirname(et, f, nf); + e.q0 = e.q1 = e.a0 = e.a1 = 0; + e.at = nil; + e.name = f; + e.bname = f; + e.jump = TRUE; + (nil, e) = openfile(et, e); + f = nil; + e.bname = nil; + (arg, narg) = utils->skipbl(a, na); + } +} diff --git a/appl/acme/look.m b/appl/acme/look.m new file mode 100644 index 00000000..5618c4cc --- /dev/null +++ b/appl/acme/look.m @@ -0,0 +1,17 @@ +Look : module { + PATH : con "/dis/acme/look.dis"; + + init : fn(mods : ref Dat->Mods); + + isfilec: fn(r : int) : int; + lookid : fn(n : int, b : int) : ref Windowm->Window; + lookfile : fn(s : string, n : int) : ref Windowm->Window; + dirname : fn(t : ref Textm->Text, r : string, n : int) : (string, int); + cleanname : fn(s : string, n : int) : (string, int); + new : fn(et, t, argt : ref Textm->Text, flag1, flag2 : int, arg : string, narg : int); + expand : fn(t : ref Textm->Text, q0, q1 : int) : (int, Dat->Expand); + search : fn(t : ref Textm->Text, r : string, n : int) : int; + look3 : fn(t : ref Textm->Text, q0, q1, external : int); + plumblook : fn(m : ref Plumbmsg->Msg); + plumbshow : fn(m : ref Plumbmsg->Msg); +}; diff --git a/appl/acme/mkfile b/appl/acme/mkfile new file mode 100644 index 00000000..e8184a51 --- /dev/null +++ b/appl/acme/mkfile @@ -0,0 +1,90 @@ +<../../mkconfig + +DIRS=\ + acme\ + +TARG=\ + acme.dis\ + dat.dis\ + buff.dis\ + col.dis\ + disk.dis\ + exec.dis\ + file.dis\ + fsys.dis\ + look.dis\ + regx.dis\ + row.dis\ + scrl.dis\ + text.dis\ + time.dis\ + util.dis\ + wind.dis\ + graph.dis\ + xfid.dis\ + gui.dis\ + frame.dis\ + edit.dis\ + ecmd.dis\ + elog.dis\ + styxaux.dis\ + +ICONS=\ + abcde.bit\ + +MODULES=\ + acme.m\ + buff.m\ + col.m\ + disk.m\ + exec.m\ + file.m\ + fsys.m\ + look.m\ + regx.m\ + row.m\ + scrl.m\ + text.m\ + time.m\ + util.m\ + wind.m\ + xfid.m\ + common.m\ + graph.m\ + gui.m\ + frame.m\ + dat.m\ + edit.m\ + elog.m\ + ecmd.m\ + styxaux.m\ + +SYSMODULES=\ + bufio.m\ + daytime.m\ + debug.m\ + draw.m\ + sh.m\ + string.m\ + styx.m\ + sys.m\ + tk.m\ + workdir.m\ + wmclient.m\ + +DISBIN=$ROOT/dis/acme + +all:V: acme.dis + +<$ROOT/mkfiles/mkdis +<$ROOT/mkfiles/mksubdirs + +install:V: $ROOT/dis/acme.dis + +$ROOT/dis/acme.dis: acme.dis + rm -f $target && cp acme.dis $target + +acme.dis: $MODULES $SYS_MODULES + +nuke:V: + rm -f $ROOT/dis/acme.dis diff --git a/appl/acme/regx.b b/appl/acme/regx.b new file mode 100644 index 00000000..2bb571c0 --- /dev/null +++ b/appl/acme/regx.b @@ -0,0 +1,1050 @@ +implement Regx; + +include "common.m"; + +sys : Sys; +utils : Utils; +textm : Textm; + +FALSE, TRUE, XXX : import Dat; +NRange : import Dat; +Range, Rangeset : import Dat; +error, warning, tgetc, rgetc : import utils; +Text : import textm; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + utils = mods.utils; + textm = mods.textm; +} + +None : con 0; +Fore : con '+'; +Back : con '-'; + +Char : con 0; +Line : con 1; + +isaddrc(r : int) : int +{ + if (utils->strchr("0123456789+-/$.#", r) >= 0) + return TRUE; + return FALSE; +} + +# +# quite hard: could be almost anything but white space, but we are a little conservative, +# aiming for regular expressions of alphanumerics and no white space +# +isregexc(r : int) : int +{ + if(r == 0) + return FALSE; + if(utils->isalnum(r)) + return TRUE; + if(utils->strchr("^+-.*?#,;[]()$", r)>=0) + return TRUE; + return FALSE; +} + +number(md: ref Dat->Mntdir, t : ref Text, r : Range, line : int, dir : int, size : int) : (int, Range) +{ + q0, q1 : int; + + { + if(size == Char){ + if(dir == Fore) + line = r.q1+line; # was t.file.buf.nc+line; + else if(dir == Back){ + if(r.q0==0 && line > 0) + r.q0 = t.file.buf.nc; + line = r.q0-line; # was t.file.buf.nc - line; + } + if(line<0 || line>t.file.buf.nc) + raise "e"; + return (TRUE, (line, line)); + } + (q0, q1) = r; + case(dir){ + None => + q0 = 0; + q1 = 0; + while(line>0 && q1 0) + q0 = q1; + if(line==1 && t.readc(q1-1)!='\n') # no newline at end - count it + ; + else if(line > 0) + raise "e"; + Fore => + if(q1 > 0) + while(t.readc(q1-1) != '\n') + q1++; + q0 = q1; + while(line>0 && q1 0) + q0 = q1; + if(line > 0) + raise "e"; + Back => + if(q0 < t.file.buf.nc) + while(q0>0 && t.readc(q0-1)!='\n') + q0--; + q1 = q0; + while(line>0 && q0>0){ + if(t.readc(q0-1) == '\n'){ + if(--line >= 0) + q1 = q0; + } + --q0; + } + if(line > 0) + raise "e"; + while(q0>0 && t.readc(q0-1)!='\n') + --q0; + } + return (TRUE, (q0, q1)); + } + exception{ + * => + if(md != nil) + warning(nil, "address out of range\n"); + return (FALSE, r); + } + return (FALSE, r); +} + +regexp(md: ref Dat->Mntdir, t : ref Text, lim : Range, r : Range, pat : string, dir : int) : (int, Range) +{ + found : int; + sel : Rangeset; + q : int; + + if(pat == nil && rxnull()){ + warning(md, "no previous regular expression"); + return (FALSE, r); + } + if(pat == nil || !rxcompile(pat)) + return (FALSE, r); + if(dir == Back) + (found, sel) = rxbexecute(t, r.q0); + else{ + if(lim.q0 < 0) + q = Dat->Infinity; + else + q = lim.q1; + (found, sel) = rxexecute(t, nil, r.q1, q); + } + if(!found && md == nil) + warning(nil, "no match for regexp\n"); + return (found, sel[0]); +} + +xgetc(a0 : ref Text, a1 : string, n : int) : int +{ + if (a0 == nil) + return rgetc(a1, n); + return tgetc(a0, n); +} + +address(md: ref Dat->Mntdir, t : ref Text, lim : Range, ar : Range, a0 : ref Text, a1 : string, q0 : int, q1 : int, eval : int) : (int, int, Range) +{ + dir, size : int; + prevc, c, n : int; + q : int; + pat : string; + r, nr : Range; + + r = ar; + q = q0; + dir = None; + size = Line; + c = 0; + while(q < q1){ + prevc = c; + c = xgetc(a0, a1, q++); + case(c){ + ';' => + ar = r; + if(prevc == 0) # lhs defaults to 0 + r.q0 = 0; + if(q>=q1 && t!=nil && t.file!=nil) # rhs defaults to $ + r.q1 = t.file.buf.nc; + else{ + (eval, q, nr) = address(md, t, lim, ar, a0, a1, q, q1, eval); + r.q1 = nr.q1; + } + return (eval, q, r); + ',' => + if(prevc == 0) # lhs defaults to 0 + r.q0 = 0; + if(q>=q1 && t!=nil && t.file!=nil) # rhs defaults to $ + r.q1 = t.file.buf.nc; + else{ + (eval, q, nr) = address(md, t, lim, ar, a0, a1, q, q1, eval); + r.q1 = nr.q1; + } + return (eval, q, r); + '+' or '-' => + if(eval && (prevc=='+' || prevc=='-')){ + if((nc := xgetc(a0, a1, q)) != '#' && nc != '/' && nc != '?') + (eval, r) = number(md, t, r, 1, prevc, Line); # do previous one + } + dir = c; + '.' or '$' => + if(q != q0+1) + return (eval, q-1, r); + if(eval) + if(c == '.') + r = ar; + else + r = (t.file.buf.nc, t.file.buf.nc); + if(q < q1) + dir = Fore; + else + dir = None; + '#' => + if(q==q1 || (c=xgetc(a0, a1, q++))<'0' || '9' + n = c -'0'; + while(q + pat = nil; + break2 := 0; # Ow ! + while(q + --q; + break2 = 1; + '\\' => + pat[len pat] = c; + if(q == q1) + break2 = 1; + else + c = xgetc(a0, a1, q++); + '/' => + break2 = 1; + } + if (break2) + break; + pat[len pat] = c; + } + if(eval) + (eval, r) = regexp(md, t, lim, r, pat, dir); + pat = nil; + dir = None; + size = Line; + * => + return (eval, q-1, r); + } + } + if(eval && dir != None) + (eval, r) = number(md, t, r, 1, dir, Line); # do previous one + return (eval, q, r); +} + +sel : Rangeset = array[NRange] of Range; +lastregexp : string; + +# Machine Information + +Inst : adt { + typex : int; # < 16r10000 ==> literal, otherwise action + # sid : int; + subid : int; + class : int; + # other : cyclic ref Inst; + right : cyclic ref Inst; + # left : cyclic ref Inst; + next : cyclic ref Inst; +}; + +NPROG : con 1024; +program := array[NPROG] of ref Inst; +progp : int; +startinst : ref Inst; # First inst. of program; might not be program[0] +bstartinst : ref Inst; # same for backwards machine + +Ilist : adt { + inst : ref Inst; # Instruction of the thread + se : Rangeset; + startp : int; # first char of match +}; + +NLIST : con 128; + +thl, nl : array of Ilist; # This list, next list +listx := array[2] of array of Ilist; +sempty : Rangeset = array[NRange] of Range; + +# +# Actions and Tokens +# +# 0x100xx are operators, value == precedence +# 0x200xx are tokens, i.e. operands for operators +# + +OPERATOR : con 16r10000; # Bitmask of all operators +START : con 16r10000; # Start, used for marker on stack +RBRA : con 16r10001; # Right bracket, ) +LBRA : con 16r10002; # Left bracket, ( +OR : con 16r10003; # Alternation, | +CAT : con 16r10004; # Concatentation, implicit operator +STAR : con 16r10005; # Closure, * +PLUS : con 16r10006; # a+ == aa* +QUEST : con 16r10007; # a? == a|nothing, i.e. 0 or 1 a's +ANY : con 16r20000; # Any character but newline, . +NOP : con 16r20001; # No operation, internal use only +BOL : con 16r20002; # Beginning of line, ^ +EOL : con 16r20003; # End of line, $ +CCLASS : con 16r20004; # Character class, [] +NCCLASS : con 16r20005; # Negated character class, [^] +END : con 16r20077; # Terminate: match found + +ISATOR : con 16r10000; +ISAND : con 16r20000; + +# Parser Information + +Node : adt { + first : ref Inst; + last : ref Inst; +}; + +NSTACK : con 20; +andstack := array[NSTACK] of ref Node; +andp : int; +atorstack := array[NSTACK] of int; +atorp : int; +lastwasand : int; # Last token was operand +cursubid : int; +subidstack := array[NSTACK] of int; +subidp : int; +backwards : int; +nbra : int; +exprs : string; +exprp : int; # pointer to next character in source expression +DCLASS : con 10; # allocation increment +nclass : int; # number active +Nclass : int = 0; # high water mark +class : array of string; +negateclass : int; + +nilnode : Node; +nilinst : Inst; + +rxinit() +{ + lastregexp = nil; + for (k := 0; k < NPROG; k++) + program[k] = ref nilinst; + for (k = 0; k < NSTACK; k++) + andstack[k] = ref nilnode; + for (k = 0; k < 2; k++) { + listx[k] = array[NLIST] of Ilist; + for (i := 0; i < NLIST; i++) { + listx[k][i].inst = nil; + listx[k][i].startp = 0; + listx[k][i].se = array[NRange] of Range; + for (j := 0; j < NRange; j++) + listx[k][i].se[j].q0 = listx[k][i].se[j].q1 = 0; + } + } +} + +regerror(e : string) +{ + lastregexp = nil; + buf := sys->sprint("regexp: %s\n", e); + warning(nil, buf); + raise "regerror"; +} + +newinst(t : int) : ref Inst +{ + if(progp >= NPROG) + regerror("expression too long"); + program[progp].typex = t; + program[progp].next = nil; # next was left + program[progp].right = nil; + return program[progp++]; +} + +realcompile(s : string) : ref Inst +{ + token : int; + + { + startlex(s); + atorp = 0; + andp = 0; + subidp = 0; + cursubid = 0; + lastwasand = FALSE; + # Start with a low priority operator to prime parser + pushator(START-1); + while((token=lex()) != END){ + if((token&ISATOR) == OPERATOR) + operator(token); + else + operand(token); + } + # Close with a low priority operator + evaluntil(START); + # Force END + operand(END); + evaluntil(START); + if(nbra) + regerror("unmatched `('"); + --andp; # points to first and only operand + return andstack[andp].first; + } + exception{ + "regerror" => + return nil; + } + return nil; +} + +rxcompile(r : string) : int +{ + oprogp : int; + + if(lastregexp == r) + return TRUE; + lastregexp = nil; + for (i := 0; i < nclass; i++) + class[i] = nil; + nclass = 0; + progp = 0; + backwards = FALSE; + bstartinst = nil; + startinst = realcompile(r); + if(startinst == nil) + return FALSE; + optimize(0); + oprogp = progp; + backwards = TRUE; + bstartinst = realcompile(r); + if(bstartinst == nil) + return FALSE; + optimize(oprogp); + lastregexp = r; + return TRUE; +} + +operand(t : int) +{ + i : ref Inst; + + if(lastwasand) + operator(CAT); # catenate is implicit + i = newinst(t); + if(t == CCLASS){ + if(negateclass) + i.typex = NCCLASS; # UGH + i.class = nclass-1; # UGH + } + pushand(i, i); + lastwasand = TRUE; +} + +operator(t : int) +{ + if(t==RBRA && --nbra<0) + regerror("unmatched `)'"); + if(t==LBRA){ + cursubid++; # silently ignored + nbra++; + if(lastwasand) + operator(CAT); + }else + evaluntil(t); + if(t!=RBRA) + pushator(t); + lastwasand = FALSE; + if(t==STAR || t==QUEST || t==PLUS || t==RBRA) + lastwasand = TRUE; # these look like operands +} + +pushand(f : ref Inst, l : ref Inst) +{ + if(andp >= NSTACK) + error("operand stack overflow"); + andstack[andp].first = f; + andstack[andp].last = l; + andp++; +} + +pushator(t : int) +{ + if(atorp >= NSTACK) + error("operator stack overflow"); + atorstack[atorp++]=t; + if(cursubid >= NRange) + subidstack[subidp++]= -1; + else + subidstack[subidp++]=cursubid; +} + +popand(op : int) : ref Node +{ + if(andp <= 0) + if(op){ + buf := sys->sprint("missing operand for %c", op); + regerror(buf); + }else + regerror("malformed regexp"); + return andstack[--andp]; +} + +popator() : int +{ + if(atorp <= 0) + error("operator stack underflow"); + --subidp; + return atorstack[--atorp]; +} + +evaluntil(pri : int) +{ + op1, op2 : ref Node; + inst1, inst2 : ref Inst; + + while(pri==RBRA || atorstack[atorp-1]>=pri){ + case(popator()){ + LBRA => + op1 = popand('('); + inst2 = newinst(RBRA); + inst2.subid = subidstack[subidp]; + op1.last.next = inst2; + inst1 = newinst(LBRA); + inst1.subid = subidstack[subidp]; + inst1.next = op1.first; + pushand(inst1, inst2); + return; # must have been RBRA + OR => + op2 = popand('|'); + op1 = popand('|'); + inst2 = newinst(NOP); + op2.last.next = inst2; + op1.last.next = inst2; + inst1 = newinst(OR); + inst1.right = op1.first; + inst1.next = op2.first; # next was left + pushand(inst1, inst2); + CAT => + op2 = popand(0); + op1 = popand(0); + if(backwards && op2.first.typex!=END) + (op1, op2) = (op2, op1); + op1.last.next = op2.first; + pushand(op1.first, op2.last); + STAR => + op2 = popand('*'); + inst1 = newinst(OR); + op2.last.next = inst1; + inst1.right = op2.first; + pushand(inst1, inst1); + PLUS => + op2 = popand('+'); + inst1 = newinst(OR); + op2.last.next = inst1; + inst1.right = op2.first; + pushand(op2.first, inst1); + QUEST => + op2 = popand('?'); + inst1 = newinst(OR); + inst2 = newinst(NOP); + inst1.next = inst2; # next was left + inst1.right = op2.first; + op2.last.next = inst2; + pushand(inst1, inst2); + * => + error("unknown regexp operator"); + } + } +} + +optimize(start : int) +{ + inst : int; + target : ref Inst; + + for(inst=start; program[inst].typex!=END; inst++){ + target = program[inst].next; + while(target.typex == NOP) + target = target.next; + program[inst].next = target; + } +} + +startlex(s : string) +{ + exprs = s; + exprp = 0; + nbra = 0; +} + +lex() : int +{ + c : int; + + if (exprp == len exprs) + return END; + c = exprs[exprp++]; + case(c){ + '\\' => + if(exprp < len exprs) + if((c= exprs[exprp++])=='n') + c='\n'; + '*' => + c = STAR; + '?' => + c = QUEST; + '+' => + c = PLUS; + '|' => + c = OR; + '.' => + c = ANY; + '(' => + c = LBRA; + ')' => + c = RBRA; + '^' => + c = BOL; + '$' => + c = EOL; + '[' => + c = CCLASS; + bldcclass(); + } + return c; +} + +nextrec() : int +{ + if(exprp == len exprs || (exprp == len exprs-1 && exprs[exprp]=='\\')) + regerror("malformed `[]'"); + if(exprs[exprp] == '\\'){ + exprp++; + if(exprs[exprp]=='n'){ + exprp++; + return '\n'; + } + return exprs[exprp++] | 16r10000; + } + return exprs[exprp++]; +} + +bldcclass() +{ + c1, c2 : int; + classp : string; + + # we have already seen the '[' + if(exprp < len exprs && exprs[exprp] == '^'){ + classp[len classp] = '\n'; # don't match newline in negate case + negateclass = TRUE; + exprp++; + }else + negateclass = FALSE; + while((c1 = nextrec()) != ']'){ + if(c1 == '-'){ + classp = nil; + regerror("malformed `[]'"); + } + if(exprp < len exprs && exprs[exprp] == '-'){ + exprp++; # eat '-' + if((c2 = nextrec()) == ']') { + classp = nil; + regerror("malformed '[]'"); + } + classp[len classp] = 16rFFFF; + classp[len classp] = c1; + classp[len classp] = c2; + }else + classp[len classp] = c1; + } + if(nclass == Nclass){ + Nclass += DCLASS; + oc := class; + class = array[Nclass] of string; + if (oc != nil) { + class[0:] = oc[0:Nclass-DCLASS]; + oc = nil; + } + } + class[nclass++] = classp; +} + +classmatch(classno : int, c : int, negate : int) : int +{ + p : string; + + p = class[classno]; + for (i := 0; i < len p; ) { + if(p[i] == 16rFFFF){ + if(p[i+1]<=c && c<=p[i+2]) + return !negate; + i += 3; + }else if(p[i++] == c) + return !negate; + } + return negate; +} + +# +# Note optimization in addinst: +# *l must be pending when addinst called; if *l has been looked +# at already, the optimization is a bug. +# +addinst(l : array of Ilist, inst : ref Inst, sep : Rangeset) +{ + p : int; + + for(p = 0; l[p].inst != nil; p++){ + if(l[p].inst==inst){ + if(sep[0].q0 < l[p].se[0].q0) + l[p].se[0:] = sep[0:NRange]; # this would be bug + return; # It's already there + } + } + l[p].inst = inst; + l[p].se[0:]= sep[0:NRange]; + l[p+1].inst = nil; +} + +rxnull() : int +{ + return startinst==nil || bstartinst==nil; +} + +OVERFLOW : con "overflow"; + +# either t!=nil or r!=nil, and we match the string in the appropriate place +rxexecute(t : ref Text, r: string, startp : int, eof : int) : (int, Rangeset) +{ + flag : int; + inst : ref Inst; + tlp : int; + p : int; + nnl, ntl : int; + nc, c : int; + wrapped : int; + startchar : int; + + flag = 0; + p = startp; + startchar = 0; + wrapped = 0; + nnl = 0; + if(startinst.typex=eof || p>=nc){ + case(wrapped++){ + 0 or 2 => # let loop run one more click + ; + 1 => # expired; wrap to beginning + if(sel[0].q0>=0 || eof!=Dat->Infinity) + return (sel[0].q0>=0, sel); + listx[0][0].inst = listx[1][0].inst = nil; + p = -1; + continue; + * => + return (sel[0].q0>=0, sel); + } + c = 0; + }else{ + if(((wrapped && p>=startp) || sel[0].q0>0) && nnl==0) + break; + if(t != nil) + c = t.readc(p); + else + c = r[p]; + } + # fast check for first char + if(startchar && nnl==0 && c!=startchar) + continue; + thl = listx[flag]; + nl = listx[flag^=1]; + nl[0].inst = nil; + ntl = nnl; + nnl = 0; + if(sel[0].q0<0 && (!wrapped || p= NLIST) + raise OVERFLOW; + sempty[0].q0 = p; + addinst(thl, startinst, sempty); + } + # Execute machine until this list is empty + tlp = 0; + inst = thl[0].inst; + while(inst != nil){ # assignment = + case(inst.typex){ + LBRA => + if(inst.subid>=0) + thl[tlp].se[inst.subid].q0 = p; + inst = inst.next; + continue; + RBRA => + if(inst.subid>=0) + thl[tlp].se[inst.subid].q1 = p; + inst = inst.next; + continue; + ANY => + if(c!='\n') { + if(++nnl >= NLIST) + raise OVERFLOW; + addinst(nl, inst.next, thl[tlp].se); + } + BOL => + if(p==0 || (t != nil && t.readc(p-1)=='\n') || (r != nil && r[p-1] == '\n')){ + inst = inst.next; + continue; + } + EOL => + if(c == '\n') { + inst = inst.next; + continue; + } + CCLASS => + if(c>=0 && classmatch(inst.class, c, 0)) { + if(++nnl >= NLIST) + raise OVERFLOW; + addinst(nl, inst.next, thl[tlp].se); + } + NCCLASS => + if(c>=0 && classmatch(inst.class, c, 1)) { + if(++nnl >= NLIST) + raise OVERFLOW; + addinst(nl, inst.next, thl[tlp].se); + } + OR => + # evaluate right choice later + if(++ntl >= NLIST) + raise OVERFLOW; + addinst(thl[tlp:], inst.right, thl[tlp].se); + # efficiency: advance and re-evaluate + inst = inst.next; # next was left + continue; + END => # Match! + thl[tlp].se[0].q1 = p; + newmatch(thl[tlp].se); + * => # regular character + if(inst.typex==c){ + if(++nnl >= NLIST) + raise OVERFLOW; + addinst(nl, inst.next, thl[tlp].se); + } + } + tlp++; + inst = thl[tlp].inst; + } + } + return (sel[0].q0>=0, sel); + } + exception{ + OVERFLOW => + error("regexp list overflow"); + sel[0].q0 = -1; + return (0, sel); + } + return (0, sel); +} + +newmatch(sp : Rangeset) +{ + if(sel[0].q0<0 || sp[0].q0sel[0].q1)) + sel[0:] = sp[0:NRange]; +} + +rxbexecute(t : ref Text, startp : int) : (int, Rangeset) +{ + flag : int; + inst : ref Inst; + tlp : int; + p : int; + nnl, ntl : int; + c : int; + wrapped : int; + startchar : int; + + flag = 0; + nnl = 0; + wrapped = 0; + p = startp; + startchar = 0; + if(bstartinst.typex # let loop run one more click + ; + 1 => # expired; wrap to end + if(sel[0].q0>=0) + return (sel[0].q0>=0, sel); + listx[0][0].inst = listx[1][0].inst = nil; + p = t.file.buf.nc+1; + continue; + 3 or * => + return (sel[0].q0>=0, sel); + } + c = 0; + }else{ + if(((wrapped && p<=startp) || sel[0].q0>0) && nnl==0) + break; + c = t.readc(p-1); + } + # fast check for first char + if(startchar && nnl==0 && c!=startchar) + continue; + thl = listx[flag]; + nl = listx[flag^=1]; + nl[0].inst = nil; + ntl = nnl; + nnl = 0; + if(sel[0].q0<0 && (!wrapped || p>startp)){ + # Add first instruction to this list + if(++ntl >= NLIST) + raise OVERFLOW; + # the minus is so the optimizations in addinst work + sempty[0].q0 = -p; + addinst(thl, bstartinst, sempty); + } + # Execute machine until this list is empty + tlp = 0; + inst = thl[0].inst; + while(inst != nil){ # assignment = + case(inst.typex){ + LBRA => + if(inst.subid>=0) + thl[tlp].se[inst.subid].q0 = p; + inst = inst.next; + continue; + RBRA => + if(inst.subid >= 0) + thl[tlp].se[inst.subid].q1 = p; + inst = inst.next; + continue; + ANY => + if(c != '\n') { + if(++nnl >= NLIST) + raise OVERFLOW; + addinst(nl, inst.next, thl[tlp].se); + } + BOL => + if(c=='\n' || p==0){ + inst = inst.next; + continue; + } + EOL => + if(p + if(c>0 && classmatch(inst.class, c, 0)) { + if(++nnl >= NLIST) + raise OVERFLOW; + addinst(nl, inst.next, thl[tlp].se); + } + NCCLASS => + if(c>0 && classmatch(inst.class, c, 1)) { + if(++nnl >= NLIST) + raise OVERFLOW; + addinst(nl, inst.next, thl[tlp].se); + } + OR => + # evaluate right choice later + if(++ntl >= NLIST) + raise OVERFLOW; + addinst(thl[tlp:], inst.right, thl[tlp].se); + # efficiency: advance and re-evaluate + inst = inst.next; # next was left + continue; + END => # Match! + thl[tlp].se[0].q0 = -thl[tlp].se[0].q0; # minus sign + thl[tlp].se[0].q1 = p; + bnewmatch(thl[tlp].se); + * => # regular character + if(inst.typex == c){ + if(++nnl >= NLIST) + raise OVERFLOW; + addinst(nl, inst.next, thl[tlp].se); + } + } + tlp++; + inst = thl[tlp].inst; + } + } + return (sel[0].q0>=0, sel); + } + exception{ + OVERFLOW => + error("regexp list overflow"); + sel[0].q0 = -1; + return (0, sel); + } + return (0, sel); +} + +bnewmatch(sp : Rangeset) +{ + i : int; + + if(sel[0].q0<0 || sp[0].q0>sel[0].q1 || (sp[0].q0==sel[0].q1 && sp[0].q1Mods); + + rxinit : fn(); + rxcompile: fn(r : string) : int; + rxexecute: fn(t : ref Textm->Text, r: string, startp : int, eof : int) : (int, Dat->Rangeset); + rxbexecute: fn(t : ref Textm->Text, startp : int) : (int, Dat->Rangeset); + isaddrc : fn(r : int) : int; + isregexc : fn(r : int) : int; + address : fn(md: ref Dat->Mntdir, t : ref Textm->Text, lim : Dat->Range, ar : Dat->Range, a0 : ref Textm->Text, a1 : string, q0 : int, q1 : int, eval : int) : (int, int, Dat->Range); +}; \ No newline at end of file diff --git a/appl/acme/row.b b/appl/acme/row.b new file mode 100644 index 00000000..9c857c93 --- /dev/null +++ b/appl/acme/row.b @@ -0,0 +1,767 @@ +implement Rowm; + +include "common.m"; + +sys : Sys; +bufio : Bufio; +utils : Utils; +drawm : Draw; +acme : Acme; +graph : Graph; +gui : Gui; +dat : Dat; +bufferm : Bufferm; +textm : Textm; +filem : Filem; +windowm : Windowm; +columnm : Columnm; +exec : Exec; +look : Look; +edit : Edit; +ecmd : Editcmd; + +ALLLOOPER, ALLTOFILE, ALLMATCHFILE, ALLFILECHECK, ALLELOGTERM, ALLEDITINIT, ALLUPDATE: import Edit; +sprint : import sys; +FALSE, TRUE, XXX : import Dat; +Border, BUFSIZE, Astring : import Dat; +Reffont, reffont, Lock, Ref : import dat; +row, home, mouse : import dat; +fontnames : import acme; +font, draw : import graph; +Point, Rect, Image : import drawm; +min, max, abs, error, warning, clearmouse, stralloc, strfree : import utils; +black, white, mainwin : import gui; +Buffer : import bufferm; +Tag, Rowtag, Text : import textm; +Window : import windowm; +File : import filem; +Column : import columnm; +Iobuf : import bufio; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + bufio = mods.bufio; + dat = mods.dat; + utils = mods.utils; + drawm = mods.draw; + acme = mods.acme; + graph = mods.graph; + gui = mods.gui; + bufferm = mods.bufferm; + textm = mods.textm; + filem = mods.filem; + windowm = mods.windowm; + columnm = mods.columnm; + exec = mods.exec; + look = mods.look; + edit = mods.edit; + ecmd = mods.editcmd; +} + +newrow() : ref Row +{ + r := ref Row; + r.qlock = Lock.init(); + r.r = ((0, 0), (0, 0)); + r.tag = nil; + r.col = nil; + r.ncol = 0; + return r; +} + +Row.init(row : self ref Row, r : Rect) +{ + r1 : Rect; + t : ref Text; + dummy : ref File = nil; + + draw(mainwin, r, white, nil, (0, 0)); + row.r = r; + row.col = nil; + row.ncol = 0; + r1 = r; + r1.max.y = r1.min.y + font.height; + row.tag = textm->newtext(); + t = row.tag; + t.init(dummy.addtext(t), r1, Reffont.get(FALSE, FALSE, FALSE, nil), acme->tagcols); + t.what = Rowtag; + t.row = row; + t.w = nil; + t.col = nil; + r1.min.y = r1.max.y; + r1.max.y += Border; + draw(mainwin, r1, black, nil, (0, 0)); + t.insert(0, "Newcol Kill Putall Dump Exit ", 29, TRUE, 0); + t.setselect(t.file.buf.nc, t.file.buf.nc); +} + +Row.add(row : self ref Row, c : ref Column, x : int) : ref Column +{ + r, r1 : Rect; + d : ref Column; + i : int; + + d = nil; + r = row.r; + r.min.y = row.tag.frame.r.max.y+Border; + if(x0){ #steal 40% of last column by default + d = row.col[row.ncol-1]; + x = d.r.min.x + 3*d.r.dx()/5; + } + # look for column we'll land on + for(i=0; i 0){ + if(i < row.ncol) + i++; # new column will go after d + r = d.r; + if(r.dx() < 100) + return nil; + draw(mainwin, r, white, nil, (0, 0)); + r1 = r; + r1.max.x = min(x, r.max.x-50); + if(r1.dx() < 50) + r1.max.x = r1.min.x+50; + d.reshape(r1); + r1.min.x = r1.max.x; + r1.max.x = r1.min.x+Border; + draw(mainwin, r1, black, nil, (0, 0)); + r.min.x = r1.max.x; + } + if(c == nil){ + c = ref Column; + c.init(r); + reffont.r.inc(); + }else + c.reshape(r); + c.row = row; + c.tag.row = row; + orc := row.col; + row.col = array[row.ncol+1] of ref Column; + row.col[0:] = orc[0:i]; + row.col[i+1:] = orc[i:row.ncol]; + orc = nil; + row.col[i] = c; + row.ncol++; + clearmouse(); + return c; +} + +Row.reshape(row : self ref Row, r : Rect) +{ + i, dx, odx : int; + r1, r2 : Rect; + c : ref Column; + + dx = r.dx(); + odx = row.r.dx(); + row.r = r; + r1 = r; + r1.max.y = r1.min.y + font.height; + row.tag.reshape(r1); + r1.min.y = r1.max.y; + r1.max.y += Border; + draw(mainwin, r1, black, nil, (0, 0)); + r.min.y = r1.max.y; + r1 = r; + r1.max.x = r1.min.x; + for(i=0; icursorswitch(dat->boxcursor); + b = mouse.buttons; + op = mouse.xy; + while(mouse.buttons == b) + acme->frgetmouse(); + graph->cursorswitch(dat->arrowcursor); + if(mouse.buttons){ + while(mouse.buttons) + acme->frgetmouse(); + return; + } + + for(i=0; i0 && p.xc.r.max.x)){ + # shuffle + x = c.r.min.x; + row.close(c, FALSE); + if(row.add(c, p.x) == nil) # whoops! + if(row.add(c, x) == nil) # WHOOPS! + if(row.add(c, -1)==nil){ # shit! + row.close(c, TRUE); + return; + } + c.mousebut(); + return; + } + d = row.col[i-1]; + if(p.x < d.r.min.x+80+Dat->Scrollwid) + p.x = d.r.min.x+80+Dat->Scrollwid; + if(p.x > c.r.max.x-80-Dat->Scrollwid) + p.x = c.r.max.x-80-Dat->Scrollwid; + r = d.r; + r.max.x = c.r.max.x; + draw(mainwin, r, white, nil, (0, 0)); + r.max.x = p.x; + d.reshape(r); + r = c.r; + r.min.x = p.x; + r.max.x = r.min.x; + r.max.x += Border; + draw(mainwin, r, black, nil, (0, 0)); + r.min.x = r.max.x; + r.max.x = c.r.max.x; + c.reshape(r); + c.mousebut(); +} + +Row.close(row : self ref Row, c : ref Column, dofree : int) +{ + r : Rect; + i : int; + + for(i=0; ibartflag) + t = dat->barttext; + else + t = row.which(p); + if(t!=nil && !(t.what==Tag && p.in(t.scrollr))){ + w = t.w; + if(w == nil) + t.typex(r, 0); + else{ + w.lock('K'); + w.typex(t, r); + w.unlock(); + } + } + row.qlock.unlock(); + return t; +} + +Row.clean(row : self ref Row, exiting : int) : int +{ + clean : int; + i : int; + + clean = TRUE; + for(i=0; icreate(file, Bufio->OWRITE, 8r600); + if(b == nil){ + warning(nil, sprint("can't open %s: %r\n", file)); + raise "e"; + } + r = stralloc(BUFSIZE); + b.puts(acme->wdir); b.putc('\n'); + b.puts(fontnames[0]); b.putc('\n'); + b.puts(fontnames[1]); b.putc('\n'); + for(i=0; iQWevent] > byte 0) + if(w.dumpstr == nil) + continue; + # zeroxes of external windows are tossed + if(t.file.ntext > 1) + for(n=0; nQWevent] != byte 0) { + j = c.nw; + continue; + } + } + fontname = ""; + if(t.reffont.f != font) + fontname = t.reffont.f.name; + a = t.file.name; + if(t.file.dumpid){ + dumped = FALSE; + b.puts(sprint("x%11d %11d %11d %11d %11d %s\n", i, t.file.dumpid, + w.body.q0, w.body.q1, + 100*(w.r.min.y-c.r.min.y)/c.r.dy(), + fontname)); + }else if(w.dumpstr != nil){ + dumped = FALSE; + b.puts(sprint("e%11d %11d %11d %11d %11d %s\n", i, t.file.dumpid, + 0, 0, + 100*(w.r.min.y-c.r.min.y)/c.r.dy(), + fontname)); + }else if(len a == 0){ # don't save unnamed windows + continue; + }else if((!w.dirty && utils->access(a)==0) || w.isdir){ + dumped = FALSE; + t.file.dumpid = w.id; + b.puts(sprint("f%11d %11d %11d %11d %11d %s\n", i, w.id, + w.body.q0, w.body.q1, + 100*(w.r.min.y-c.r.min.y)/c.r.dy(), + fontname)); + }else{ + dumped = TRUE; + t.file.dumpid = w.id; + b.puts(sprint("F%11d %11d %11d %11d %11d %11d %s\n", i, j, + w.body.q0, w.body.q1, + 100*(w.r.min.y-c.r.min.y)/c.r.dy(), + w.body.file.buf.nc, fontname)); + } + a = nil; + buf = w.ctlprint(); + b.puts(buf); + m = min(BUFSIZE, w.tag.file.buf.nc); + w.tag.file.buf.read(0, r, 0, m); + n = 0; + while(n Dat->BUFSIZE) + n = Dat->BUFSIZE; + t.file.buf.read(q0, r, 0, n); + b.puts(r.s[0:n]); + q0 += n; + } + } + if(w.dumpstr != nil){ + if(w.dumpdir != nil) + b.puts(sprint("%s\n%s\n", w.dumpdir, w.dumpstr)); + else + b.puts(sprint("\n%s\n", w.dumpstr)); + } + } + } + b.close(); + b = nil; + strfree(r); + r = nil; + } + exception{ + * => + return; + } +} + +rdline(b : ref Iobuf, line : int) : (int, string) +{ + l : string; + + l = b.gets('\n'); + if(l != nil) + line++; + return (line, l); +} + +Row.loadx(row : self ref Row, file : string, initing : int) +{ + i, j, line, percent, y, nr, nfontr, n, ns, ndumped, dumpid, x : int; + b, bout : ref Iobuf; + fontname : string; + l, buf, t : string; + rune : int; + r, fontr : string; + c, c1, c2 : ref Column; + q0, q1 : int; + r1, r2 : Rect; + w : ref Window; + + { + if(file == nil){ + if(home == nil){ + warning(nil, "can't find file for load: $home not defined\n"); + raise "e"; + } + buf = sprint("%s/acme.dump", home); + file = buf; + } + b = bufio->open(file, Bufio->OREAD); + if(b == nil){ + warning(nil, sprint("can't open load file %s: %r\n", file)); + raise "e"; + } + + { + # current directory + (line, l) = rdline(b, 0); + if(l == nil) + raise "e"; + l = l[0:len l - 1]; + if(sys->chdir(l) < 0){ + warning(nil, sprint("can't chdir %s\n", l)); + b.close(); + return; + } + # global fonts + for(i=0; i<2; i++){ + (line, l) = rdline(b, line); + if(l == nil) + raise "e"; + l = l[0:len l -1]; + if(l != nil && l != fontnames[i]) + Reffont.get(i, TRUE, i==0 && initing, l); + } + if(initing && row.ncol==0) + row.init(mainwin.clipr); + (line, l) = rdline(b, line); + if(l == nil) + raise "e"; + j = len l/12; + if(j<=0 || j>10) + raise "e"; + for(i=0; i=100) + raise "e"; + x = row.r.min.x+percent*row.r.dx()/100; + if(i < row.ncol){ + if(i == 0) + continue; + c1 = row.col[i-1]; + c2 = row.col[i]; + r1 = c1.r; + r2 = c2.r; + r1.max.x = x; + r2.min.x = x+Border; + if(r1.dx() < 50 || r2.dx() < 50) + continue; + draw(mainwin, (r1.min, r2.max), white, nil, (0, 0)); + c1.reshape(r1); + c2.reshape(r2); + r2.min.x = x; + r2.max.x = x+Border; + draw(mainwin, r2, black, nil, (0, 0)); + } + if(i >= row.ncol) + row.add(nil, x); + } + for(;;){ + (line, l) = rdline(b, line); + if(l == nil) + break; + dumpid = 0; + case(l[0]){ + 'e' => + if(len l < 1+5*12+1) + raise "e"; + (line, l) = rdline(b, line); # ctl line; ignored + if(l == nil) + raise "e"; + (line, l) = rdline(b, line); # directory + if(l == nil) + raise "e"; + l = l[0:len l -1]; + if(len l != 0) + r = l; + else{ + if(home == nil) + r = "./"; + else + r = home+"/"; + } + nr = len r; + (line, l) = rdline(b, line); # command + if(l == nil) + raise "e"; + t = l[0:len l -1]; + spawn exec->run(nil, t, r, nr, TRUE, nil, nil, FALSE); + # r is freed in run() + continue; + 'f' => + if(len l < 1+5*12+1) + raise "e"; + fontname = l[1+5*12:len l - 1]; + ndumped = -1; + 'F' => + if(len l < 1+6*12+1) + raise "e"; + fontname = l[1+6*12:len l - 1]; + ndumped = int l[1+5*12:1+5*12+11]; + 'x' => + if(len l < 1+5*12+1) + raise "e"; + fontname = l[1+5*12: len l - 1]; + ndumped = -1; + dumpid = int l[1+1*12:1+1*12+11]; + * => + raise "e"; + } + l = l[0:len l -1]; + if(len fontname != 0) { + fontr = fontname; + nfontr = len fontname; + } + else + (fontr, nfontr) = (nil, 0); + i = int l[1+0*12:1+0*12+11]; + j = int l[1+1*12:1+1*12+11]; + q0 = int l[1+2*12:1+2*12+11]; + q1 = int l[1+3*12:1+3*12+11]; + percent = int l[1+4*12:1+4*12+11]; + if(i<0 || i>10) + raise "e"; + if(i > row.ncol) + i = row.ncol; + c = row.col[i]; + y = c.r.min.y+(percent*c.r.dy())/100; + if(y=c.r.max.y) + y = -1; + if(dumpid == 0) + w = c.add(nil, nil, y); + else + w = c.add(nil, look->lookid(dumpid, TRUE), y); + if(w == nil) + continue; + w.dumpid = j; + (line, l) = rdline(b, line); + if(l == nil) + raise "e"; + l = l[0:len l - 1]; + r = l[5*12:len l]; + nr = len r; + ns = -1; + for(n=0; n= 0){ + # simplest thing is to put it in a file and load that + buf = sprint("/tmp/d%d.%.4sacme", sys->pctl(0, nil), utils->getuser()); + bout = bufio->create(buf, Bufio->OWRITE, 8r600); + if(bout == nil){ + warning(nil, "can't create temp file: %r\n"); + b.close(); + return; + } + for(n=0; nEOF){ + bout.close(); + bout = nil; + raise "e"; + } + bout.putc(rune); + } + bout.close(); + bout = nil; + w.body.loadx(0, buf, 1); + w.body.file.mod = TRUE; + for(n=0; nremove(buf); + buf = nil; + }else if(dumpid==0 && r[ns+1]!='+' && r[ns+1]!='-') + exec->get(w.body, nil, nil, FALSE, nil, 0); + l = r = nil; + if(fontr != nil){ + exec->fontx(w.body, nil, nil, fontr, nfontr); + fontr = nil; + } + if(q0>w.body.file.buf.nc || q1>w.body.file.buf.nc || q0>q1) + q0 = q1 = 0; + w.body.show(q0, q1); + w.maxlines = min(w.body.frame.nlines, max(w.maxlines, w.body.frame.maxlines)); + } + b.close(); + } + exception{ + * => + warning(nil, sprint("bad load file %s:%d\n", file, line)); + b.close(); + raise "e"; + } + } + exception{ + * => + return; + } +} + +allwindows(o: int, aw: ref Dat->Allwin) +{ + for(i:=0; i + pick k := aw{ + LP => ecmd->alllooper(w, k.lp); + } + ALLTOFILE => + pick k := aw{ + FF => ecmd->alltofile(w, k.ff); + } + ALLMATCHFILE => + pick k := aw{ + FF => ecmd->allmatchfile(w, k.ff); + } + ALLFILECHECK => + pick k := aw{ + FC => ecmd->allfilecheck(w, k.fc); + } + ALLELOGTERM => + edit->allelogterm(w); + ALLEDITINIT => + edit->alleditinit(w); + ALLUPDATE => + edit->allupdate(w); + } + } + } +} \ No newline at end of file diff --git a/appl/acme/row.m b/appl/acme/row.m new file mode 100644 index 00000000..2a90f5e4 --- /dev/null +++ b/appl/acme/row.m @@ -0,0 +1,29 @@ +Rowm : module { + PATH : con "/dis/acme/row.dis"; + + init : fn(mods : ref Dat->Mods); + + newrow : fn() : ref Row; + + Row : adt { + qlock : ref Dat->Lock; + r : Draw->Rect; + tag : cyclic ref Textm->Text; + col : cyclic array of ref Columnm->Column; + ncol : int; + + init : fn(r : self ref Row, re : Draw->Rect); + add : fn(r : self ref Row, c : ref Columnm->Column, n : int) : ref Columnm->Column; + close : fn(r : self ref Row, c : ref Columnm->Column, n : int); + which : fn(r : self ref Row, p : Draw->Point) : ref Textm->Text; + whichcol : fn(r : self ref Row, p : Draw->Point) : ref Columnm->Column; + reshape : fn(r : self ref Row, re : Draw->Rect); + typex : fn(r : self ref Row, ru : int, p : Draw->Point) : ref Textm->Text; + dragcol : fn(r : self ref Row, c : ref Columnm->Column); + clean : fn(r : self ref Row, exiting : int) : int; + dump : fn(r : self ref Row, b : string); + loadx : fn(r : self ref Row, b : string, n : int); + }; + + allwindows: fn(a0: int, aw: ref Dat->Allwin); +}; diff --git a/appl/acme/scrl.b b/appl/acme/scrl.b new file mode 100644 index 00000000..e25fc709 --- /dev/null +++ b/appl/acme/scrl.b @@ -0,0 +1,187 @@ +implement Scroll; + +include "common.m"; + +sys : Sys; +drawm : Draw; +acme : Acme; +graph : Graph; +utils : Utils; +gui : Gui; +dat : Dat; +framem : Framem; +textm : Textm; +timerm : Timerm; + +BORD, BACK : import Framem; +FALSE, TRUE, XXX, Maxblock : import Dat; +error, warning : import utils; +Point, Rect, Image, Display : import drawm; +draw : import graph; +black, white, display : import gui; +mouse, cmouse : import dat; +Frame : import framem; +Timer : import Dat; +Text : import textm; +frgetmouse : import acme; +mainwin : import gui; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + drawm = mods.draw; + acme = mods.acme; + graph = mods.graph; + utils = mods.utils; + gui = mods.gui; + dat = mods.dat; + framem = mods.framem; + textm = mods.textm; + timerm = mods.timerm; +} + +scrpos(r : Rect, p0 : int, p1 : int, tot : int) : Rect +{ + h : int; + q : Rect; + + q = r; + # q = r.inset(1); + h = q.max.y-q.min.y; + if(tot == 0) + return q; + if(tot > 1024*1024){ + tot >>= 10; + p0 >>= 10; + p1 >>= 10; + } + if(p0 > 0) + q.min.y += h*p0/tot; + if(p1 < tot) + q.max.y -= h*(tot-p1)/tot; + if(q.max.y < q.min.y+2){ + if(q.min.y+2 <= r.max.y) + q.max.y = q.min.y+2; + else + q.min.y = q.max.y-2; + } + return q; +} + +scrx : ref Image; + +scrresize() +{ + scrx = nil; + h := 1024; + if (display != nil) + h = display.image.r.dy(); + rr := Rect((0, 0), (32, h)); + scrx = graph->balloc(rr, mainwin.chans, Draw->White); + if(scrx == nil) + error("scroll balloc"); +} + +scrdraw(t : ref Text) +{ + r, r1, r2 : Rect; + + if(t.w==nil || t.what!=Textm->Body || t != t.w.body) + return; + if(scrx == nil) + scrresize(); + r = t.scrollr; + b := scrx; + r1 = r; + # r.min.x += 1; # border between margin and bar + r1.min.x = 0; + r1.max.x = r.dx(); + r2 = scrpos(r1, t.org, t.org+t.frame.nchars, t.file.buf.nc); + if(!r2.eq(t.lastsr)){ + t.lastsr = r2; + draw(b, r1, t.frame.cols[BORD], nil, (0, 0)); + draw(b, r2, t.frame.cols[BACK], nil, (0, 0)); + r2.min.x = r2.max.x-1; + draw(b, r2, t.frame.cols[BORD], nil, (0, 0)); + draw(t.frame.b, r, b, nil, (0, r1.min.y)); + # bflush(); + } +} + +scrsleep(dt : int) +{ + timer : ref Timer; + + timer = timerm->timerstart(dt); + graph->bflush(); + # only run from mouse task, so safe to use cmouse + alt{ + <-(timer.c) => + timerm->timerstop(timer); + *mouse = *<-cmouse => + spawn timerm->timerwaittask(timer); + } +} + +scroll(t : ref Text, but : int) +{ + p0, oldp0 : int; + s : Rect; + x, y, my, h, first : int; + + s = t.scrollr.inset(1); + h = s.max.y-s.min.y; + x = (s.min.x+s.max.x)/2; + oldp0 = ~0; + first = TRUE; + do{ + graph->bflush(); + my = mouse.xy.y; + if(my < s.min.y) + my = s.min.y; + if(my >= s.max.y) + my = s.max.y; + if(but == 2){ + y = my; + if(y > s.max.y-2) + y = s.max.y-2; + if(t.file.buf.nc > 1024*1024) + p0 = ((t.file.buf.nc>>10)*(y-s.min.y)/h)<<10; + else + p0 = t.file.buf.nc*(y-s.min.y)/h; + if(oldp0 != p0) + t.setorigin(p0, FALSE); + oldp0 = p0; + frgetmouse(); + continue; + } + if(but == 1) { + p0 = t.backnl(t.org, (my-s.min.y)/t.frame.font.height); + if(p0 == t.org) + p0 = t.backnl(t.org, 1); + } + else { + p0 = t.org+framem->frcharofpt(t.frame, (s.max.x, my)); + if(p0 == t.org) + p0 = t.forwnl(t.org, 1); + } + if(oldp0 != p0) + t.setorigin(p0, TRUE); + oldp0 = p0; + # debounce + if(first){ + graph->bflush(); + sys->sleep(200); + alt { + *mouse = *<-cmouse => + ; + * => + ; + } + first = FALSE; + } + scrsleep(80); + }while(mouse.buttons & (1<<(but-1))); + while(mouse.buttons) + frgetmouse(); +} diff --git a/appl/acme/scrl.m b/appl/acme/scrl.m new file mode 100644 index 00000000..d405eabd --- /dev/null +++ b/appl/acme/scrl.m @@ -0,0 +1,9 @@ +Scroll : module { + PATH : con "/dis/acme/scrl.dis"; + + init : fn(mods : ref Dat->Mods); + scrsleep : fn(n : int); + scrdraw : fn(t : ref Textm->Text); + scrresize : fn(); + scroll : fn(t : ref Textm->Text, but : int); +}; \ No newline at end of file diff --git a/appl/acme/styxaux.b b/appl/acme/styxaux.b new file mode 100644 index 00000000..efb7329b --- /dev/null +++ b/appl/acme/styxaux.b @@ -0,0 +1,174 @@ +implement Styxaux; + +include "sys.m"; + sys: Sys; +include "styx.m"; +include "styxaux.m"; + +Tmsg : import Styx; + +init() +{ +} + +msize(m: ref Tmsg): int +{ + pick fc := m { + Version => return fc.msize; + } + error("bad styx msize", m); + return 0; +} + +version(m: ref Tmsg): string +{ + pick fc := m { + Version => return fc.version; + } + error("bad styx version", m); + return nil; +} + +fid(m: ref Tmsg): int +{ + pick fc := m { + Readerror => return 0; + Version => return 0; + Flush => return 0; + Walk => return fc.fid; + Open => return fc.fid; + Create => return fc.fid; + Read => return fc.fid; + Write => return fc.fid; + Clunk => return fc.fid; + Remove => return fc.fid; + Stat => return fc.fid; + Wstat => return fc.fid; + Attach => return fc.fid; + } + error("bad styx fid", m); + return 0; +} + +uname(m: ref Tmsg): string +{ + pick fc := m { + Attach => return fc.uname; + } + error("bad styx uname", m); + return nil; +} + +aname(m: ref Tmsg): string +{ + pick fc := m { + Attach => return fc.aname; + } + error("bad styx aname", m); + return nil; +} + +newfid(m: ref Tmsg): int +{ + pick fc := m { + Walk => return fc.newfid; + } + error("bad styx newfd", m); + return 0; +} + +name(m: ref Tmsg): string +{ + pick fc := m { + Create => return fc.name; + } + error("bad styx name", m); + return nil; +} + +names(m: ref Tmsg): array of string +{ + pick fc := m { + Walk => return fc.names; + } + error("bad styx names", m); + return nil; +} + +mode(m: ref Tmsg): int +{ + pick fc := m { + Open => return fc.mode; + } + error("bad styx mode", m); + return 0; +} + +setmode(m: ref Tmsg, mode: int) +{ + pick fc := m { + Open => fc.mode = mode; + * => error("bad styx setmode", m); + } +} + +offset(m: ref Tmsg): big +{ + pick fc := m { + Read => return fc.offset; + Write => return fc.offset; + } + error("bad styx offset", m); + return big 0; +} + +count(m: ref Tmsg): int +{ + pick fc := m { + Read => return fc.count; + Write => return len fc.data; + } + error("bad styx count", m); + return 0; +} + +setcount(m: ref Tmsg, count: int) +{ + pick fc := m { + Read => fc.count = count; + * => error("bad styx setcount", m); + } +} + +oldtag(m: ref Tmsg): int +{ + pick fc := m { + Flush => return fc.oldtag; + } + error("bad styx oldtag", m); + return 0; +} + +data(m: ref Tmsg): array of byte +{ + pick fc := m { + Write => return fc.data; + } + error("bad styx data", m); + return nil; +} + +setdata(m: ref Tmsg, data: array of byte) +{ + pick fc := m { + Write => fc.data = data; + * => error("bad styx setdata", m); + } +} + +error(s: string, m: ref Tmsg) +{ + if(sys == nil) + sys = load Sys Sys->PATH; + sys->fprint(sys->fildes(2), "%s %d\n", s, tagof m); +} diff --git a/appl/acme/styxaux.m b/appl/acme/styxaux.m new file mode 100644 index 00000000..5cd8f7db --- /dev/null +++ b/appl/acme/styxaux.m @@ -0,0 +1,24 @@ +Styxaux : module { + PATH : con "/dis/acme/styxaux.dis"; + + init : fn(); + + msize: fn(m: ref Styx->Tmsg): int; + version: fn(m: ref Styx->Tmsg): string; + fid: fn(m: ref Styx->Tmsg): int; + uname: fn(m: ref Styx->Tmsg): string; + aname: fn(m: ref Styx->Tmsg): string; + newfid: fn(m: ref Styx->Tmsg): int; + name: fn(m: ref Styx->Tmsg): string; + names: fn(m: ref Styx->Tmsg): array of string; + mode: fn(m: ref Styx->Tmsg): int; + offset: fn(m: ref Styx->Tmsg): big; + count: fn(m: ref Styx->Tmsg): int; + oldtag: fn(m: ref Styx->Tmsg): int; + data: fn(m: ref Styx->Tmsg): array of byte; + + setmode: fn(m: ref Styx->Tmsg, mode: int); + setcount: fn(m: ref Styx->Tmsg, count: int); + setdata: fn(m: ref Styx->Tmsg, data: array of byte); + +}; diff --git a/appl/acme/text.b b/appl/acme/text.b new file mode 100644 index 00000000..c374bf8d --- /dev/null +++ b/appl/acme/text.b @@ -0,0 +1,1404 @@ +implement Textm; + +include "common.m"; +include "keyboard.m"; + +sys : Sys; +utils : Utils; +framem : Framem; +drawm : Draw; +acme : Acme; +graph : Graph; +gui : Gui; +dat : Dat; +scrl : Scroll; +bufferm : Bufferm; +filem : Filem; +columnm : Columnm; +windowm : Windowm; +exec : Exec; + +Dir, sprint : import sys; +frgetmouse : import acme; +min, warning, error, stralloc, strfree, isalnum : import utils; +Frame, frinsert, frdelete, frptofchar, frcharofpt, frselect, frdrawsel, frdrawsel0, frtick : import framem; +BUFSIZE, Astring, SZINT, TRUE, FALSE, XXX, Reffont, Dirlist,Scrollwid, Scrollgap, seq, mouse : import dat; +EM_NORMAL, EM_RAW, EM_MASK : import dat; +ALPHA_LATIN, ALPHA_GREEK, ALPHA_CYRILLIC: import Dat; +BACK, TEXT, HIGH, HTEXT : import Framem; +Flushon, Flushoff : import Draw; +Point, Display, Rect, Image : import drawm; +charwidth, bflush, draw : import graph; +black, white, mainwin, display : import gui; +Buffer : import bufferm; +File : import filem; +Column : import columnm; +Window : import windowm; +scrdraw : import scrl; + +cvlist: adt { + ld: int; + nm: string; + si: string; + so: string; +}; + +# "@@", "'EKSTYZekstyz ", "ьЕКСТЫЗекстызъЁё", + +latintab := array[] of { + cvlist( + ALPHA_LATIN, + "latin", + nil, + nil + ), + cvlist( + ALPHA_GREEK, + "greek", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + "ΑΒΞΔΕΦΓΘΙΪΚΛΜΝΟΠΨΡΣΤΥΫΩΧΗΖαβξδεφγθιϊκλμνοπψρστυϋωχηζ" + ), + cvlist( + ALPHA_CYRILLIC, + "cyrillic", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + "АБЧДЭФГШИЙХЛМНОПЕРЩЦУВЮХЯЖабчдэфгшийхлмноперщцувюхяж" + ), + cvlist(-1, nil, nil, nil) +}; + +alphabet := ALPHA_LATIN; # per window perhaps + +setalphabet(s: string) +{ + for(a := 0; latintab[a].ld != -1; a++){ + k := latintab[a].ld; + for(i := 0; latintab[i].ld != -1; i++){ + if(s == transs(latintab[i].nm, k)){ + alphabet = latintab[i].ld; + return; + } + } + } +} + +transc(c: int, k: int): int +{ + for(i := 0; latintab[i].ld != -1; i++){ + if(k == latintab[i].ld){ + si := latintab[i].si; + so := latintab[i].so; + ln := len si; + for(j := 0; j < ln; j++) + if(c == si[j]) + return so[j]; + } + } + return c; +} + +transs(s: string, k: int): string +{ + ln := len s; + for(i := 0; i < ln; i++) + s[i] = transc(s[i], k); + return s; +} + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + framem = mods.framem; + dat = mods.dat; + utils = mods.utils; + drawm = mods.draw; + acme = mods.acme; + graph = mods.graph; + gui = mods.gui; + scrl = mods.scroll; + bufferm = mods.bufferm; + filem = mods.filem; + columnm = mods.columnm; + windowm = mods.windowm; + exec = mods.exec; +} + +TABDIR : con 3; # width of tabs in directory windows + +# remove eventually +KF : con 16rF000; +Kup : con KF | 16r0E; +Kleft : con KF | 16r11; +Kright : con KF | 16r12; +Kdown : con 16r80; + +nulltext : Text; + +newtext() : ref Text +{ + t := ref nulltext; + t.frame = framem->newframe(); + return t; +} + +Text.init(t : self ref Text, f : ref File, r : Rect, rf : ref Dat->Reffont, cols : array of ref Image) +{ + t.file = f; + t.all = r; + t.scrollr = r; + t.scrollr.max.x = r.min.x+Scrollwid; + t.lastsr = dat->nullrect; + r.min.x += Scrollwid+Scrollgap; + t.eq0 = ~0; + t.ncache = 0; + t.reffont = rf; + t.tabstop = dat->maxtab; + for(i:=0; iNCOL; i++) + t.frame.cols[i] = cols[i]; + t.redraw(r, rf.f, mainwin, -1); +} + +Text.redraw(t : self ref Text, r : Rect, f : ref Draw->Font, b : ref Image, odx : int) +{ + framem->frinit(t.frame, r, f, b, t.frame.cols); + rr := t.frame.r; + rr.min.x -= Scrollwid; # back fill to scroll bar + draw(t.frame.b, rr, t.frame.cols[Framem->BACK], nil, (0, 0)); + # use no wider than 3-space tabs in a directory + maxt := dat->maxtab; + if(t.what == Body){ + if(t.w != nil && t.w.isdir) + maxt = min(TABDIR, dat->maxtab); + else + maxt = t.tabstop; + } + t.frame.maxtab = maxt*charwidth(f, '0'); + # c = '0'; + # if(t.what==Body && t.w!=nil && t.w.isdir) + # c = ' '; + # t.frame.maxtab = Dat->Maxtab*charwidth(f, c); + if(t.what==Body && t.w.isdir && odx!=t.all.dx()){ + if(t.frame.maxlines > 0){ + t.reset(); + t.columnate(t.w.dlp, t.w.ndl); + t.show(0, 0); + } + }else{ + t.fill(); + t.setselect(t.q0, t.q1); + } +} + +Text.reshape(t : self ref Text, r : Rect) : int +{ + odx : int; + + if(r.dy() > 0) + r.max.y -= r.dy()%t.frame.font.height; + else + r.max.y = r.min.y; + odx = t.all.dx(); + t.all = r; + t.scrollr = r; + t.scrollr.max.x = r.min.x+Scrollwid; + t.lastsr = dat->nullrect; + r.min.x += Scrollwid+Scrollgap; + framem->frclear(t.frame, 0); + # t.redraw(r, t.frame.font, t.frame.b, odx); + t.redraw(r, t.frame.font, mainwin, odx); + return r.max.y; +} + +Text.close(t : self ref Text) +{ + t.cache = nil; + framem->frclear(t.frame, 1); + t.file.deltext(t); + t.file = nil; + t.reffont.close(); + if(dat->argtext == t) + dat->argtext = nil; + if(dat->typetext == t) + dat->typetext = nil; + if(dat->seltext == t) + dat->seltext = nil; + if(dat->mousetext == t) + dat->mousetext = nil; + if(dat->barttext == t) + dat->barttext = nil; +} + +dircmp(da : ref Dirlist, db : ref Dirlist) : int +{ + if (da.r < db.r) + return -1; + if (da.r > db.r) + return 1; + return 0; +} + +qsort(a : array of ref Dirlist, n : int) +{ + i, j : int; + t : ref Dirlist; + + while(n > 1) { + i = n>>1; + t = a[0]; a[0] = a[i]; a[i] = t; + i = 0; + j = n; + for(;;) { + do + i++; + while(i < n && dircmp(a[i], a[0]) < 0); + do + j--; + while(j > 0 && dircmp(a[j], a[0]) > 0); + if(j < i) + break; + t = a[i]; a[i] = a[j]; a[j] = t; + } + t = a[0]; a[0] = a[j]; a[j] = t; + n = n-j-1; + if(j >= n) { + qsort(a, j); + a = a[j+1:]; + } else { + qsort(a[j+1:], n); + n = j; + } + } +} + +Text.columnate(t : self ref Text, dlp : array of ref Dirlist, ndl : int) +{ + i, j, w, colw, mint, maxt, ncol, nrow : int; + dl : ref Dirlist; + q1 : int; + + if(t.file.ntext > 1) + return; + mint = charwidth(t.frame.font, '0'); + # go for narrower tabs if set more than 3 wide + t.frame.maxtab = min(dat->maxtab, TABDIR)*mint; + maxt = t.frame.maxtab; + colw = 0; + for(i=0; i colw) + colw = w; + } + if(colw == 0) + ncol = 1; + else + ncol = utils->max(1, t.frame.r.dx()/colw); + nrow = (ndl+ncol-1)/ncol; + + q1 = 0; + for(i=0; i= ndl) + break; + w = dl.wid; + if(maxt-w%maxt < mint){ + t.file.insert(q1, "\t", 1); + q1++; + w += mint; + } + do{ + t.file.insert(q1, "\t", 1); + q1++; + w += maxt-(w%maxt); + }while(w < colw); + } + t.file.insert(q1, "\n", 1); + q1++; + } +} + +Text.loadx(t : self ref Text, q0 : int, file : string, setqid : int) : int +{ + rp : ref Astring; + dl : ref Dirlist; + dlp : array of ref Dirlist; + i, n, ndl : int; + fd : ref Sys->FD; + q, q1 : int; + d : Dir; + u : ref Text; + ok : int; + + if(t.ncache!=0 || t.file.buf.nc || t.w==nil || t!=t.w.body || (t.w.isdir && t.file.name==nil)) + error("text.load"); + + { + fd = sys->open(file, Sys->OREAD); + if(fd == nil){ + warning(nil, sprint("can't open %s: %r\n", file)); + raise "e"; + } + (ok, d) = sys->fstat(fd); + if(ok){ + warning(nil, sprint("can't fstat %s: %r\n", file)); + raise "e"; + } + if(d.qid.qtype & Sys->QTDIR){ + # this is checked in get() but it's possible the file changed underfoot + if(t.file.ntext > 1){ + warning(nil, sprint("%s is a directory; can't read with multiple windows on it\n", file)); + raise "e"; + } + t.w.isdir = TRUE; + t.w.filemenu = FALSE; + if(t.file.name[len t.file.name-1] != '/') + t.w.setname(t.file.name + "/", len t.file.name+1); + dlp = nil; + ndl = 0; + for(;;){ + (nd, dbuf) := sys->dirread(fd); + if(nd <= 0) + break; + for(i=0; iDMDIR) + dl.r = dl.r + "/"; + dl.wid = graph->strwidth(t.frame.font, dl.r); + ndl++; + odlp := dlp; + dlp = array[ndl] of ref Dirlist; + dlp[0:] = odlp[0:ndl-1]; + odlp = nil; + dlp[ndl-1] = dl; + } + } + qsort(dlp, ndl); + t.w.dlp = dlp; + t.w.ndl = ndl; + t.columnate(dlp, ndl); + q1 = t.file.buf.nc; + }else{ + tmp : int; + + t.w.isdir = FALSE; + t.w.filemenu = TRUE; + tmp = t.file.loadx(q0, fd); + q1 = q0 + tmp; + } + fd = nil; + if(setqid){ + t.file.dev = d.dev; + t.file.mtime = d.mtime; + t.file.qidpath = d.qid.path; + } + rp = stralloc(BUFSIZE); + for(q=q0; q Dat->BUFSIZE) + n = Dat->BUFSIZE; + t.file.buf.read(q, rp, 0, n); + if(q < t.org) + t.org += n; + else if(q <= t.org+t.frame.nchars) + frinsert(t.frame, rp.s, n, q-t.org); + if(t.frame.lastlinefull) + break; + } + strfree(rp); + rp = nil; + for(i=0; i u.file.buf.nc) # will be 0 because of reset(), but safety first + u.org = 0; + u.reshape(u.all); + u.backnl(u.org, 0); # go to beginning of line + } + u.setselect(q0, q0); + } + return q1-q0; + } + exception{ + * => + fd = nil; + return 0; + } + return 0; +} + +Text.bsinsert(t : self ref Text, q0 : int, r : string, n : int, tofile : int) : (int, int) +{ + tp : ref Astring; + bp, up : int; + i, initial : int; + + { + if(t.what == Tag) # can't happen but safety first: mustn't backspace over file name + raise "e"; + bp = 0; + for(i=0; istralloc(n); + for (k := 0; k < i; k++) + tp.s[k] = r[k]; + up = i; + for(; i q0) + initial = q0; + q0 -= initial; + t.delete(q0, q0+initial, tofile); + } + n = up; + t.insert(q0, tp.s, n, tofile, 0); + strfree(tp); + tp = nil; + return (q0, n); + } + raise "e"; + return(0, 0); + } + exception{ + * => + t.insert(q0, r, n, tofile, 0); + return (q0, n); + } + return (0, 0); +} + +Text.insert(t : self ref Text, q0 : int, r : string, n : int, tofile : int, echomode : int) +{ + c, i : int; + u : ref Text; + + if(tofile && t.ncache != 0) + error("text.insert"); + if(n == 0) + return; + if(tofile){ + t.file.insert(q0, r, n); + if(t.what == Body){ + t.w.dirty = TRUE; + t.w.utflastqid = -1; + } + if(t.file.ntext > 1) + for(i=0; iEVENTSIZE) + t.w.event(sprint("%c%d %d 0 %d %s\n", c, q0, q0+n, n, r[0:n])); + else + t.w.event(sprint("%c%d %d 0 0 \n", c, q0, q0+n)); + } +} + +Text.fill(t : self ref Text) +{ + rp : ref Astring; + i, n, m, nl : int; + + if(t.frame.lastlinefull || t.nofill) + return; + if(t.ncache > 0){ + if(t.w != nil) + t.w.commit(t); + else + t.commit(TRUE); + } + rp = stralloc(BUFSIZE); + do{ + n = t.file.buf.nc-(t.org+t.frame.nchars); + if(n == 0) + break; + if(n > 2000) # educated guess at reasonable amount + n = 2000; + t.file.buf.read(t.org+t.frame.nchars, rp, 0, n); + # + # it's expensive to frinsert more than we need, so + # count newlines. + # + + nl = t.frame.maxlines-t.frame.nlines; + m = 0; + for(i=0; i= nl) + break; + } + } + frinsert(t.frame, rp.s, i, t.frame.nchars); + }while(t.frame.lastlinefull == FALSE); + strfree(rp); + rp = nil; +} + +Text.delete(t : self ref Text, q0 : int, q1 : int, tofile : int) +{ + n, p0, p1 : int; + i, c : int; + u : ref Text; + + if(tofile && t.ncache != 0) + error("text.delete"); + n = q1-q0; + if(n == 0) + return; + if(tofile){ + t.file.delete(q0, q1); + if(t.what == Body){ + t.w.dirty = TRUE; + t.w.utflastqid = -1; + } + if(t.file.ntext > 1) + for(i=0; i t.frame.nchars) + p1 = t.frame.nchars; + if(q0 < t.org){ + t.org = q0; + p0 = 0; + }else + p0 = q0 - t.org; + frdelete(t.frame, p0, p1); + t.fill(); + } + if(t.w != nil){ + c = 'd'; + if(t.what == Body) + c = 'D'; + t.w.event(sprint("%c%d %d 0 0 \n", c, q0, q1)); + } +} + +onechar : ref Astring; + +Text.readc(t : self ref Text, q : int) : int +{ + if(t.cq0<=q && q 0){ + r = t.readc(q-1); + if(r == '\n'){ # eat at most one more character + if(q == t.q0) # eat the newline + --q; + break; + } + if(c == 16r17){ + eq = isalnum(r); + if(eq && skipping) # found one; stop skipping + skipping = FALSE; + else if(!eq && !skipping) + break; + } + --q; + } + return t.q0-q; +} + +Text.typex(t : self ref Text, r : int, echomode : int) +{ + q0, q1 : int; + nnb, nb, n, i : int; + u : ref Text; + + if(alphabet != ALPHA_LATIN) + r = transc(r, alphabet); + if (echomode == EM_RAW && t.what == Body) { + if (t.w != nil) { + s := "a"; + s[0] = r; + t.w.event(sprint("R0 0 0 1 %s\n", s)); + } + return; + } + if(t.what!=Body && r=='\n') + return; + case(r){ + Kdown or Keyboard->Down => + n = t.frame.maxlines/2; + q0 = t.org+frcharofpt(t.frame, (t.frame.r.min.x, t.frame.r.min.y+n*t.frame.font.height)); + t.setorigin(q0, FALSE); + return; + Kup or Keyboard->Up => + n = t.frame.maxlines/2; + q0 = t.backnl(t.org, n); + t.setorigin(q0, FALSE); + return; + Kleft or Keyboard->Left => + t.commit(TRUE); + if(t.q0 != t.q1) + t.show(t.q0, t.q0); + else if(t.q0 != 0) + t.show(t.q0-1, t.q0-1); + return; + Kright or Keyboard->Right => + t.commit(TRUE); + if(t.q0 != t.q1) + t.show(t.q1, t.q1); + else if(t.q1 != t.file.buf.nc) + t.show(t.q1+1, t.q1+1); + return; + } + if(t.what == Body){ + seq++; + t.file.mark(); + } + if(t.q1 > t.q0){ + if(t.ncache != 0) + error("text.type"); + exec->cut(t, t, TRUE, TRUE); + t.eq0 = ~0; + if (r == 16r08 || r == 16r7f){ # erase character : odd if a char then erased + t.show(t.q0, t.q0); + return; + } + } + t.show(t.q0, t.q0); + case(r){ + 16r1B => + if(t.eq0 != ~0) + t.setselect(t.eq0, t.q0); + if(t.ncache > 0){ + if(t.w != nil) + t.w.commit(t); + else + t.commit(TRUE); + } + return; + 16r08 or 16r15 or 16r17 => + # ^H: erase character or ^U: erase line or ^W: erase word + if(t.q0 == 0) + return; +if(0) # DEBUGGING + for(i=0; i 0){ + if(q1 != u.cq0+n) + error("text.type backspace"); + if(n > nb) + n = nb; + u.ncache -= n; + u.delete(q1-n, q1, FALSE); + nb -= n; + } + if(u.eq0==q1 || u.eq0==~0) + u.eq0 = q0; + if(nb && u==t) + u.delete(q0, q0+nb, TRUE); + if(u != t) + u.setselect(u.q0, u.q1); + else + t.setselect(q0, q0); + u.nofill = FALSE; + } + for(i=0; iDel => + # Delete character - forward delete + t.commit(TRUE); + if(t.q0 >= t.file.buf.nc) + return; + nnb = 1; + q0 = t.q0; + q1 = q0+nnb; + for(i=0; iscrsleep(100); + return; + } + if(dl < 0){ + q0 = t.backnl(t.org, -dl); + if(selectq > t.org+t.frame.p0) + t.setselect0(t.org+t.frame.p0, selectq); + else + t.setselect0(selectq, t.org+t.frame.p0); + }else{ + if(t.org+t.frame.nchars == t.file.buf.nc) + return; + q0 = t.org+frcharofpt(t.frame, (t.frame.r.min.x, t.frame.r.min.y+dl*t.frame.font.height)); + if(selectq > t.org+t.frame.p1) + t.setselect0(t.org+t.frame.p1, selectq); + else + t.setselect0(selectq, t.org+t.frame.p1); + } + t.setorigin(q0, TRUE); +} + + +Text.select(t : self ref Text, double : int) +{ + q0, q1 : int; + b, x, y : int; + state : int; + + selecttext = t; + # + # To have double-clicking and chording, we double-click + # immediately if it might make sense. + # + + b = mouse.buttons; + q0 = t.q0; + q1 = t.q1; + selectq = t.org+frcharofpt(t.frame, mouse.xy); + if(double || (clicktext==t && mouse.msec-clickmsec<500)) + if(q0==q1 && selectq==q0){ + (q0, q1) = t.doubleclick(q0, q1); + t.setselect(q0, q1); + bflush(); + x = mouse.xy.x; + y = mouse.xy.y; + # stay here until something interesting happens + do + frgetmouse(); + while(mouse.buttons==b && utils->abs(mouse.xy.x-x)<3 && utils->abs(mouse.xy.y-y)<3); + mouse.xy.x = x; # in case we're calling frselect + mouse.xy.y = y; + q0 = t.q0; # may have changed + q1 = t.q1; + selectq = q0; + } + if(mouse.buttons == b){ + t.frame.scroll = 1; + frselect(t.frame, mouse); + # horrible botch: while asleep, may have lost selection altogether + if(selectq > t.file.buf.nc) + selectq = t.org + t.frame.p0; + t.frame.scroll = 0; + if(selectq < t.org) + q0 = selectq; + else + q0 = t.org + t.frame.p0; + if(selectq > t.org+t.frame.nchars) + q1 = selectq; + else + q1 = t.org+t.frame.p1; + } + if(q0 == q1){ + if(q0==t.q0 && (double || clicktext==t && mouse.msec-clickmsec<500)){ + (q0, q1) = t.doubleclick(q0, q1); + clicktext = nil; + }else{ + clicktext = t; + clickmsec = mouse.msec; + } + }else + clicktext = nil; + t.setselect(q0, q1); + bflush(); + state = 0; # undo when possible; +1 for cut, -1 for paste + while(mouse.buttons){ + mouse.msec = 0; + b = mouse.buttons; + if(b & 6){ + if(state==0 && t.what==Body){ + seq++; + t.w.body.file.mark(); + } + if(b & 2){ + if(state==-1 && t.what==Body){ + t.w.undo(TRUE); + t.setselect(q0, t.q0); + state = 0; + }else if(state != 1){ + exec->cut(t, t, TRUE, TRUE); + state = 1; + } + }else{ + if(state==1 && t.what==Body){ + t.w.undo(TRUE); + t.setselect(q0, t.q1); + state = 0; + }else if(state != -1){ + exec->paste(t, t, TRUE, FALSE); + state = -1; + } + } + scrdraw(t); + utils->clearmouse(); + } + bflush(); + while(mouse.buttons == b) + frgetmouse(); + clicktext = nil; + } +} + +Text.show(t : self ref Text, q0 : int, q1 : int) +{ + qe : int; + nl : int; + q : int; + + if(t.what != Body) + return; + if(t.w!=nil && t.frame.maxlines==0) + t.col.grow(t.w, 1, 0); + t.setselect(q0, q1); + qe = t.org+t.frame.nchars; + if(t.org<=q0 && (q0QWevent]>byte 0) + nl = 3*t.frame.maxlines/4; + else + nl = t.frame.maxlines/4; + q = t.backnl(q0, nl); + # avoid going backwards if trying to go forwards - long lines! + if(!(q0>t.org && q t.org+t.frame.nchars) + t.setorigin(t.org+1, FALSE); + } +} + +region(a, b : int) : int +{ + if(a < b) + return -1; + if(a == b) + return 0; + return 1; +} + +selrestore(f : ref Frame, pt0 : Point, p0 : int, p1 : int) +{ + if(p1<=f.p0 || p0>=f.p1){ + # no overlap + frdrawsel0(f, pt0, p0, p1, f.cols[BACK], f.cols[TEXT]); + return; + } + if(p0>=f.p0 && p1<=f.p1){ + # entirely inside + frdrawsel0(f, pt0, p0, p1, f.cols[HIGH], f.cols[HTEXT]); + return; + } + # they now are known to overlap + # before selection + if(p0 < f.p0){ + frdrawsel0(f, pt0, p0, f.p0, f.cols[BACK], f.cols[TEXT]); + p0 = f.p0; + pt0 = frptofchar(f, p0); + } + # after selection + if(p1 > f.p1){ + frdrawsel0(f, frptofchar(f, f.p1), f.p1, p1, f.cols[BACK], f.cols[TEXT]); + p1 = f.p1; + } + # inside selection + frdrawsel0(f, pt0, p0, p1, f.cols[HIGH], f.cols[HTEXT]); +} + +Text.setselect(t : self ref Text, q0 : int, q1 : int) +{ + p0, p1 : int; + + # t.p0 and t.p1 are always right; t.q0 and t.q1 may be off + t.q0 = q0; + t.q1 = q1; + # compute desired p0,p1 from q0,q1 + p0 = q0-t.org; + p1 = q1-t.org; + if(p0 < 0) + p0 = 0; + if(p1 < 0) + p1 = 0; + if(p0 > t.frame.nchars) + p0 = t.frame.nchars; + if(p1 > t.frame.nchars) + p1 = t.frame.nchars; + if(p0==t.frame.p0 && p1==t.frame.p1) + return; + # screen disagrees with desired selection + if(t.frame.p1<=p0 || p1<=t.frame.p0 || p0==p1 || t.frame.p1==t.frame.p0){ + # no overlap or too easy to bother trying + frdrawsel(t.frame, frptofchar(t.frame, t.frame.p0), t.frame.p0, t.frame.p1, 0); + frdrawsel(t.frame, frptofchar(t.frame, p0), p0, p1, 1); + t.frame.p0 = p0; + t.frame.p1 = p1; + return; + } + # overlap; avoid unnecessary painting + if(p0 < t.frame.p0){ + # extend selection backwards + frdrawsel(t.frame, frptofchar(t.frame, p0), p0, t.frame.p0, 1); + }else if(p0 > t.frame.p0){ + # trim first part of selection + frdrawsel(t.frame, frptofchar(t.frame, t.frame.p0), t.frame.p0, p0, 0); + } + if(p1 > t.frame.p1){ + # extend selection forwards + frdrawsel(t.frame, frptofchar(t.frame, t.frame.p1), t.frame.p1, p1, 1); + }else if(p1 < t.frame.p1){ + # trim last part of selection + frdrawsel(t.frame, frptofchar(t.frame, p1), p1, t.frame.p1, 0); + } + t.frame.p0 = p0; + t.frame.p1 = p1; +} + +Text.setselect0(t : self ref Text, q0 : int, q1 : int) +{ + t.q0 = q0; + t.q1 = q1; +} + +xselect(f : ref Frame, mc : ref Draw->Pointer, col, colt : ref Image) : (int, int) +{ + p0, p1, q, tmp : int; + mp, pt0, pt1, qt : Point; + reg, b : int; + + # when called button 1 is down + mp = mc.xy; + b = mc.buttons; + + # remove tick + if(f.p0 == f.p1) + frtick(f, frptofchar(f, f.p0), 0); + p0 = p1 = frcharofpt(f, mp); + pt0 = frptofchar(f, p0); + pt1 = frptofchar(f, p1); + reg = 0; + frtick(f, pt0, 1); + do{ + q = frcharofpt(f, mc.xy); + if(p1 != q){ + if(p0 == p1) + frtick(f, pt0, 0); + if(reg != region(q, p0)){ # crossed starting point; reset + if(reg > 0) + selrestore(f, pt0, p0, p1); + else if(reg < 0) + selrestore(f, pt1, p1, p0); + p1 = p0; + pt1 = pt0; + reg = region(q, p0); + if(reg == 0) + frdrawsel0(f, pt0, p0, p1, col, colt); + } + qt = frptofchar(f, q); + if(reg > 0){ + if(q > p1) + frdrawsel0(f, pt1, p1, q, col, colt); + else if(q < p1) + selrestore(f, qt, q, p1); + }else if(reg < 0){ + if(q > p1) + selrestore(f, pt1, p1, q); + else + frdrawsel0(f, qt, q, p1, col, colt); + } + p1 = q; + pt1 = qt; + } + if(p0 == p1) + frtick(f, pt0, 1); + bflush(); + frgetmouse(); + }while(mc.buttons == b); + if(p1 < p0){ + tmp = p0; + p0 = p1; + p1 = tmp; + } + pt0 = frptofchar(f, p0); + if(p0 == p1) + frtick(f, pt0, 0); + selrestore(f, pt0, p0, p1); + # restore tick + if(f.p0 == f.p1) + frtick(f, frptofchar(f, f.p0), 1); + bflush(); + return (p0, p1); +} + +Text.select23(t : self ref Text, q0 : int, q1 : int, high, low : ref Image, mask : int) : (int, int, int) +{ + p0, p1 : int; + buts : int; + + (p0, p1) = xselect(t.frame, mouse, high, low); + buts = mouse.buttons; + if((buts & mask) == 0){ + q0 = p0+t.org; + q1 = p1+t.org; + } + while(mouse.buttons) + frgetmouse(); + return (buts, q0, q1); +} + +Text.select2(t : self ref Text, q0 : int, q1 : int) : (int, ref Text, int, int) +{ + buts : int; + + (buts, q0, q1) = t.select23(q0, q1, acme->but2col, acme->but2colt, 4); + if(buts & 4) + return (0, nil, q0, q1); + if(buts & 1) # pick up argument + return (1, dat->argtext, q0, q1); + return (1, nil, q0, q1); +} + +Text.select3(t : self ref Text, q0 : int, q1 : int) : (int, int, int) +{ + buts : int; + + (buts, q0, q1) = t.select23(q0, q1, acme->but3col, acme->but3colt, 1|2); + return (buts == 0, q0, q1); +} + +left := array[4] of { + "{[(<«", + "\n", + "'\"`", + nil +}; +right := array[4] of { + "}])>»", + "\n", + "'\"`", + nil +}; + +Text.doubleclick(t : self ref Text, q0 : int, q1 : int) : (int, int) +{ + c, i : int; + r, l : string; + p : int; + q : int; + res : int; + + for(i=0; left[i]!=nil; i++){ + q = q0; + l = left[i]; + r = right[i]; + # try matching character to left, looking right + if(q == 0) + c = '\n'; + else + c = t.readc(q-1); + p = utils->strchr(l, c); + if(p >= 0){ + (res, q) = t.clickmatch(c, r[p], 1, q); + if (res) + q1 = q-(c!='\n'); + return (q0, q1); + } + # try matching character to right, looking left + if(q == t.file.buf.nc) + c = '\n'; + else + c = t.readc(q); + p = utils->strchr(r, c); + if(p >= 0){ + (res, q) = t.clickmatch(c, l[p], -1, q); + if (res){ + q1 = q0+(q00 && isalnum(t.readc(q0-1))) + q0--; + return (q0, q1); +} + +Text.clickmatch(t : self ref Text, cl : int, cr : int, dir : int, q : int) : (int, int) +{ + c : int; + nest : int; + + nest = 1; + for(;;){ + if(dir > 0){ + if(q == t.file.buf.nc) + break; + c = t.readc(q); + q++; + }else{ + if(q == 0) + break; + q--; + c = t.readc(q); + } + if(c == cr){ + if(--nest==0) + return (1, q); + }else if(c == cl) + nest++; + } + return (cl=='\n' && nest==1, q); +} + +Text.forwnl(t : self ref Text, p : int, n : int) : int +{ + i, j : int; + + e := t.file.buf.nc-1; + i = n; + while(i-- > 0 && p0 && p0 && t.readc(p-1)!='\n') + n = 1; + i = n; + while(i-- > 0 && p>0){ + --p; # it's at a newline now; back over it + if(p == 0) + break; + # at 128 chars, call it a line anyway + for(j=128; --j>0 && p>0; p--) + if(t.readc(p-1)=='\n') + break; + } + return p; +} + +Text.setorigin(t : self ref Text, org : int, exact : int) +{ + i, a : int; + r : ref Astring; + n : int; + + t.frame.b.flush(Flushoff); + if(org>0 && !exact){ + # org is an estimate of the char posn; find a newline + # don't try harder than 256 chars + for(i=0; i<256 && org=0 && astralloc(n); + t.file.buf.read(org, r, 0, n); + frinsert(t.frame, r.s, n, 0); + utils->strfree(r); + r = nil; + }else + frdelete(t.frame, 0, t.frame.nchars); + t.org = org; + t.fill(); + scrdraw(t); + t.setselect(t.q0, t.q1); + if(fixup && t.frame.p1 > t.frame.p0) + frdrawsel(t.frame, frptofchar(t.frame, t.frame.p1-1), t.frame.p1-1, t.frame.p1, 1); + t.frame.b.flush(Flushon); +} + +Text.reset(t : self ref Text) +{ + t.file.seq = 0; + t.eq0 = ~0; + # do t.delete(0, t.nc, TRUE) without building backup stuff + t.setselect(t.org, t.org); + frdelete(t.frame, 0, t.frame.nchars); + t.org = 0; + t.q0 = 0; + t.q1 = 0; + t.file.reset(); + t.file.buf.reset(); +} diff --git a/appl/acme/text.m b/appl/acme/text.m new file mode 100644 index 00000000..1c258be3 --- /dev/null +++ b/appl/acme/text.m @@ -0,0 +1,65 @@ +Textm : module { + PATH : con "/dis/acme/text.dis"; + + init : fn(mods : ref Dat->Mods); + + # Text.what + Columntag, Rowtag, Tag, Body : con iota; + + newtext : fn() : ref Text; + + Text : adt { + file : cyclic ref Filem->File; + frame : ref Framem->Frame; + reffont : ref Dat->Reffont; + org : int; + q0 : int; + q1 : int; + what : int; + tabstop : int; + w : cyclic ref Windowm->Window; + scrollr : Draw->Rect; + lastsr : Draw->Rect; + all : Draw->Rect; + row : cyclic ref Rowm->Row; + col : cyclic ref Columnm->Column; + eq0 : int; # start of typing for ESC + cq0 : int; # cache position + ncache : int; # storage for insert + ncachealloc : int; + cache : string; + nofill : int; + + init : fn(t : self ref Text, f : ref Filem->File, r : Draw->Rect, rf : ref Dat->Reffont, cols : array of ref Draw->Image); + redraw : fn(t : self ref Text, r : Draw->Rect, f : ref Draw->Font, b : ref Draw->Image, n : int); + insert : fn(t : self ref Text, n : int, s : string, p : int, q : int, r : int); + bsinsert : fn(t : self ref Text, n : int, s : string, p : int, q : int) : (int, int); + delete : fn(t : self ref Text, n : int, p : int, q : int); + loadx : fn(t : self ref Text, n : int, b : string, q : int) : int; + typex : fn(t : self ref Text, r : int, echomode : int); + select : fn(t : self ref Text, d : int); + select2 : fn(t : self ref Text, p : int, q : int) : (int, ref Text, int, int); + select3 : fn(t : self ref Text, p: int, q : int) : (int, int, int); + setselect : fn(t : self ref Text, p : int, q : int); + setselect0 : fn(t : self ref Text, p : int, q : int); + show : fn(t : self ref Text, p : int, q : int); + fill : fn(t : self ref Text); + commit : fn(t : self ref Text, n : int); + setorigin : fn(t : self ref Text, p : int, q : int); + readc : fn(t : self ref Text, n : int) : int; + reset : fn(t : self ref Text); + reshape : fn(t : self ref Text, r : Draw->Rect) : int; + close : fn(t : self ref Text); + framescroll : fn(t : self ref Text, n : int); + select23 : fn(t : self ref Text, p : int, q : int, i, it : ref Draw->Image, n : int) : (int, int, int); + forwnl : fn(t : self ref Text, p : int, q : int) : int; + backnl : fn(t : self ref Text, p : int, q : int) : int; + bswidth : fn(t : self ref Text, r : int) : int; + doubleclick : fn(t : self ref Text, p : int, q : int) : (int, int); + clickmatch : fn(t : self ref Text, p : int, q : int, r : int, n : int) : (int, int); + columnate : fn(t : self ref Text, d : array of ref Dat->Dirlist, n : int); + }; + + framescroll : fn(f : ref Framem->Frame, dl : int); + setalphabet: fn(s: string); +}; \ No newline at end of file diff --git a/appl/acme/time.b b/appl/acme/time.b new file mode 100644 index 00000000..b5edda74 --- /dev/null +++ b/appl/acme/time.b @@ -0,0 +1,129 @@ +implement Timerm; + +include "common.m"; + +sys : Sys; +acme : Acme; +utils : Utils; +dat : Dat; + +millisec : import sys; +Timer : import dat; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + acme = mods.acme; + utils = mods.utils; + dat = mods.dat; +} + +ctimer : chan of ref Timer; + +timeproc() +{ + i, nt, na, dt : int; + x : ref Timer; + t : array of ref Timer; + old, new : int; + + acme->timerpid = sys->pctl(0, nil); + sys->pctl(Sys->FORKFD, nil); + t = array[10] of ref Timer; + na = 10; + nt = 0; + old = millisec(); + for(;;){ + if (nt == 0) { # don't waste cpu time + x = <-ctimer; + t[nt++] = x; + old = millisec(); + } + sys->sleep(1); # will sleep minimum incr + new = millisec(); + dt = new-old; + old = new; + if(dt < 0) # timer wrapped; go around, losing a tick + continue; + for(i=0; i + t[i:] = t[i+1:nt]; + t[nt-1] = nil; + nt--; + i--; + * => + ; + } + } + } + gotone := 1; + while (gotone) { + alt { + x = <-ctimer => + if (nt == na) { + ot := t; + t = array[na+10] of ref Timer; + t[0:] = ot[0:na]; + ot = nil; + na += 10; + } + t[nt++] = x; + old = millisec(); + * => + gotone = 0; + } + } + } +} + +timerinit() +{ + ctimer = chan of ref Timer; + spawn timeproc(); +} + +# +# timeralloc() and timerfree() don't lock, so can only be +# called from the main proc. +# + + +timer : ref Timer; + +timerstart(dt : int) : ref Timer +{ + t : ref Timer; + + t = timer; + if(t != nil) + timer = timer.next; + else{ + t = ref Timer; + t.c = chan of int; + } + t.next = nil; + t.dt = dt; + ctimer <-= t; + return t; +} + +timerstop(t : ref Timer) +{ + t.next = timer; + timer = t; +} + +timerwaittask(timer : ref Timer) +{ + <-(timer.c); + timerstop(timer); +} diff --git a/appl/acme/time.m b/appl/acme/time.m new file mode 100644 index 00000000..a9a01b13 --- /dev/null +++ b/appl/acme/time.m @@ -0,0 +1,10 @@ +Timerm : module { + PATH : con "/dis/acme/time.dis"; + + init : fn(mods : ref Dat->Mods); + + timerinit: fn(); + timerstart : fn(dt : int) : ref Dat->Timer; + timerstop : fn(t : ref Dat->Timer); + timerwaittask : fn(t : ref Dat->Timer); +}; \ No newline at end of file diff --git a/appl/acme/util.b b/appl/acme/util.b new file mode 100644 index 00000000..b2930c35 --- /dev/null +++ b/appl/acme/util.b @@ -0,0 +1,574 @@ +implement Utils; + +include "common.m"; +include "sh.m"; +include "env.m"; + +sys : Sys; +draw : Draw; +gui : Gui; +acme : Acme; +dat : Dat; +graph : Graph; +textm : Textm; +windowm : Windowm; +columnm : Columnm; +rowm : Rowm; +scrl : Scroll; +look : Look; + +RELEASECOPY : import acme; +Point, Rect : import draw; +Astring, TRUE, FALSE, Mntdir, Lock : import dat; +mouse, activecol, seltext, row : import dat; +cursorset : import graph; +mainwin : import gui; +Text : import textm; +Window : import windowm; +Column : import columnm; +Row : import rowm; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + draw = mods.draw; + gui = mods.gui; + acme = mods.acme; + dat = mods.dat; + graph = mods.graph; + textm = mods.textm; + windowm = mods.windowm; + columnm = mods.columnm; + rowm = mods.rowm; + scrl = mods.scroll; + look = mods.look; + + stderr = sys->fildes(2); +} + +min(x : int, y : int) : int +{ + if (x < y) + return x; + return y; +} + +max(x : int, y : int) : int +{ + if (x > y) + return x; + return y; +} + +abs(x : int) : int +{ + if (x < 0) + return -x; + return x; +} + +isalnum(c : int) : int +{ + # + # Hard to get absolutely right. Use what we know about ASCII + # and assume anything above the Latin control characters is + # potentially an alphanumeric. + # + if(c <= ' ') + return FALSE; + if(16r7F<=c && c<=16rA0) + return FALSE; + if(strchr("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c) >= 0) + return FALSE; + return TRUE; + # return ('a' <= c && c <= 'z') || + # ('A' <= c && c <= 'Z') || + # ('0' <= c && c <= '9'); +} + +strchr(s : string, c : int) : int +{ + for (i := 0; i < len s; i++) + if (s[i] == c) + return i; + return -1; +} + +strrchr(s : string, c : int) : int +{ + for (i := len s - 1; i >= 0; i--) + if (s[i] == c) + return i; + return -1; +} + +strncmp(s, t : string, n : int) : int +{ + if (len s > n) + s = s[0:n]; + if (len t > n) + t = t[0:n]; + if (s < t) + return -1; + if (s > t) + return 1; + return 0; +} + +env : Env; + +getenv(s : string) : string +{ + if (env == nil) + env = load Env Env->PATH; + e := env->getenv(s); + if(e != nil && e[len e - 1] == '\n') # shell bug + return e[0: len e -1]; + return e; +} + +setenv(s, t : string) +{ + if (env == nil) + env = load Env Env->PATH; + env->setenv(s, t); +} + +stob(s : string, n : int) : array of byte +{ + b := array[2*n] of byte; + for (i := 0; i < n; i++) { + b[2*i] = byte (s[i]&16rff); + b[2*i+1] = byte ((s[i]>>8)&16rff); + } + return b; +} + +btos(b : array of byte, s : ref Astring) +{ + n := (len b)/2; + for (i := 0; i < n; i++) + s.s[i] = int b[2*i] | ((int b[2*i+1])<<8); +} + +reverse(ol : list of string) : list of string +{ + nl : list of string; + + nl = nil; + while (ol != nil) { + nl = hd ol :: nl; + ol = tl ol; + } + return nl; +} + +nextarg(p : ref Arg) : int +{ + bp : string; + + if(p.av != nil){ + bp = hd p.av; + if(bp != nil && bp[0] == '-'){ + p.p = bp[1:]; + p.av = tl p.av; + return 1; + } + } + p.p = nil; + return 0; +} + +arginit(av : list of string) : ref Arg +{ + p : ref Arg; + + p = ref Arg; + p.arg0 = hd av; + p.av = tl av; + nextarg(p); + return p; +} + +argopt(p : ref Arg) : int +{ + r : int; + + if(p.p == nil && nextarg(p) == 0) + return 0; + r = p.p[0]; + p.p = p.p[1:]; + return r; +} + +argf(p : ref Arg) : string +{ + bp : string; + + if(p.p != nil){ + bp = p.p; + p.p = nil; + } else if(p.av != nil){ + bp = hd p.av; + p.av = tl p.av; + } else + bp = nil; + return bp; +} + +exec(cmd : string, argl : list of string) +{ + file := cmd; + if(len file<4 || file[len file-4:]!=".dis") + file += ".dis"; + + c := load Command file; + if(c == nil) { + err := sys->sprint("%r"); + if(file[0]!='/' && file[0:2]!="./"){ + c = load Command "/dis/"+file; + if(c == nil) + err = sys->sprint("%r"); + } + if(c == nil){ + # debug(sys->sprint("file %s not found\n", file)); + sys->fprint(stderr, "%s: %s\n", cmd, err); + return; + } + } + c->init(acme->acmectxt, argl); +} + +getuser() : string +{ + fd := sys->open("/dev/user", sys->OREAD); + if(fd == nil) + return ""; + + buf := array[128] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return ""; + + return string buf[0:n]; +} + +gethome(usr : string) : string +{ + if (usr == nil) + usr = "tmp"; + return "/usr/" + usr; +} + +postnote(t : int, this : int, pid : int, note : string) : int +{ + if (pid == this || pid == 0) + return 0; + # fd := sys->open("/prog/" + string pid + "/ctl", sys->OWRITE); + fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE); + if (fd == nil) + return -1; + if (t == PNGROUP) + note += "grp"; + sys->fprint(fd, "%s", note); + fd = nil; + return 0; +} + +error(s : string) +{ + sys->fprint(stderr, "acme: %s: %r\n", s); + debug(sys->sprint("error %s : %r\n", s)); + # s[-1] = 0; # create broken process for debugging + acme->acmeexit("error"); +} + +dlock : ref Lock; +dfd : ref Sys->FD; + +debuginit() +{ + if (RELEASECOPY) + return; + dfd = sys->create("./debug", Sys->OWRITE, 8r600); + # fd = nil; + dlock = Lock.init(); +} + +debugpr(s : string) +{ + if (RELEASECOPY) + return; + # fd := sys->open("./debug", Sys->OWRITE); + # sys->seek(fd, big 0, Sys->SEEKEND); + sys->fprint(dfd, "%s", s); + # fd = nil; +} + +debug(s : string) +{ + if (RELEASECOPY) + return; + if (dfd == nil) + return; + dlock.lock(); + debugpr(s); + dlock.unlock(); +} + +memfd : ref Sys->FD; +memb : array of byte; + +memdebug(s : string) +{ + if (RELEASECOPY) + return; + dlock.lock(); + if (memfd == nil) { + sys->bind("#c", "/usr/jrf/mnt", Sys->MBEFORE); + memfd = sys->open("/usr/jrf/mnt/memory", Sys->OREAD); + memb = array[1024] of byte; + } + sys->seek(memfd, big 0, 0); + n := sys->read(memfd, memb, len memb); + if (n <= 0) { + dlock.unlock(); + debug(sys->sprint("bad read %r\n")); + return; + } + s = s + " : " + string memb[0:n] + "\n"; + dlock.unlock(); + debug(s); + s = nil; +} + +rgetc(s : string, n : int) : int +{ + if (n < 0 || n >= len s) + return 0; + return s[n]; +} + +tgetc(t : ref Text, n : int) : int +{ + if(n >= t.file.buf.nc) + return 0; + return t.readc(n); +} + +skipbl(r : string, n : int) : (string, int) +{ + i : int = 0; + + while(n>0 && (r[i]==' ' || r[i]=='\t' || r[i]=='\n')){ + --n; + i++; + } + return (r[i:], n); +} + +findbl(r : string, n : int) : (string, int) +{ + i : int = 0; + + while(n>0 && r[i]!=' ' && r[i]!='\t' && r[i]!='\n'){ + --n; + i++; + } + return (r[i:], n); +} + +prevmouse : Point; +mousew : ref Window; + +savemouse(w : ref Window) +{ + prevmouse = mouse.xy; + mousew = w; +} + +restoremouse(w : ref Window) +{ + if(mousew!=nil && mousew==w) + cursorset(prevmouse); + mousew = nil; +} + +clearmouse() +{ + mousew = nil; +} + +# +# Heuristic city. +# +newwindow(t : ref Text) : ref Window +{ + c : ref Column; + w, bigw, emptyw : ref Window; + emptyb : ref Text; + i, y, el : int; + + if(activecol != nil) + c = activecol; + else if(seltext != nil && seltext.col != nil) + c = seltext.col; + else if(t != nil && t.col != nil) + c = t.col; + else{ + if(row.ncol==0 && row.add(nil, -1)==nil) + error("can't make column"); + c = row.col[row.ncol-1]; + } + activecol = c; + if(t==nil || t.w==nil || c.nw==0) + return c.add(nil, nil, -1); + + # find biggest window and biggest blank spot + emptyw = c.w[0]; + bigw = emptyw; + for(i=1; i= to choose one near bottom of screen + if(w.body.frame.maxlines >= bigw.body.frame.maxlines) + bigw = w; + if(w.body.frame.maxlines-w.body.frame.nlines >= emptyw.body.frame.maxlines-emptyw.body.frame.nlines) + emptyw = w; + } + emptyb = emptyw.body; + el = emptyb.frame.maxlines-emptyb.frame.nlines; + # if empty space is big, use it + if(el>15 || (el>3 && el>(bigw.body.frame.maxlines-1)/2)) + y = emptyb.frame.r.min.y+emptyb.frame.nlines*(graph->font).height; + else{ + # if this window is in column and isn't much smaller, split it + if(t.col==c && t.w.r.dy()>2*bigw.r.dy()/3) + bigw = t.w; + y = (bigw.r.min.y + bigw.r.max.y)/2; + } + w = c.add(nil, nil, y); + if(w.body.frame.maxlines < 2) + w.col.grow(w, 1, 1); + return w; +} + +stralloc(n : int) : ref Astring +{ + r := ref Astring; + ab := array[n] of { * => byte 'z' }; + r.s = string ab; + if (len r.s != n) + error("bad stralloc"); + ab = nil; + return r; +} + +strfree(s : ref Astring) +{ + s.s = nil; + s = nil; +} + +access(s : string) : int +{ + fd := sys->open(s, 0); + if (fd == nil) + return -1; + fd = nil; + return 0; +} + +errorwin(dir : string, ndir : int, incl : array of string, nincl : int) : ref Window +{ + w : ref Window; + r : string; + i, n : int; + + n = ndir; + r = dir + "+Errors"; + n += 7; + w = look->lookfile(r, n); + if(w == nil){ + w = row.col[row.ncol-1].add(nil, nil, -1); + w.filemenu = FALSE; + w.setname(r, n); + } + r = nil; + for(i=nincl; --i>=0; ) + w.addincl(incl[i], n); + return w; +} + +warning(md : ref Mntdir, s : string) +{ + n, q0, owner : int; + w : ref Window; + t : ref Text; + + debug(sys->sprint("warning %s\n", s)); + if (row == nil) { + sys->fprint(sys->fildes(2), "warning: %s\n", s); + debug(s); + debug("\n"); + return; + } + if(row.ncol == 0){ # really early error + row.init(mainwin.clipr); + row.add(nil, -1); + row.add(nil, -1); + if(row.ncol == 0) + error("initializing columns in warning()"); + } + if(md != nil){ + for(;;){ + w = errorwin(md.dir, md.ndir, md.incl, md.nincl); + w.lock('E'); + if (w.col != nil) + break; + # window was deleted too fast + w.unlock(); + } + }else + w = errorwin(nil, 0, nil, 0); + t = w.body; + owner = w.owner; + if(owner == 0) + w.owner = 'E'; + w.commit(t); + (q0, n) = t.bsinsert(t.file.buf.nc, s, len s, TRUE); + t.show(q0, q0+n); + t.w.settag(); + scrl->scrdraw(t); + w.owner = owner; + w.dirty = FALSE; + if(md != nil) + w.unlock(); +} + +getexc(): string +{ + f := "/prog/"+string sys->pctl(0, nil)+"/exception"; + if((fd := sys->open(f, Sys->OREAD)) == nil) + return nil; + b := array[8192] of byte; + if((n := sys->read(fd, b, len b)) < 0) + return nil; + return string b[0: n]; +} + +# returns pc, module, exception +readexc(): (int, string, string) +{ + s := getexc(); + if(s == nil) + return (0, nil, nil); + (m, l) := sys->tokenize(s, " "); + if(m < 3) + return (0, nil, nil); + pc := int hd l; l = tl l; + mod := hd l; l = tl l; + exc := hd l; l = tl l; + for( ; l != nil; l = tl l) + exc += " " + hd l; + return (pc, mod, exc); +} diff --git a/appl/acme/util.m b/appl/acme/util.m new file mode 100644 index 00000000..6da7afed --- /dev/null +++ b/appl/acme/util.m @@ -0,0 +1,52 @@ +Utils : module { + PATH : con "/dis/acme/util.dis"; + + stderr : ref Sys->FD; + + Arg : adt { + arg0 : string; + av : list of string; + p : string; + }; + + PNPROC, PNGROUP : con iota; + + init : fn(mods : ref Dat->Mods); + arginit : fn(av : list of string) : ref Arg; + argopt : fn(p : ref Arg) : int; + argf : fn(p : ref Arg) : string; + min : fn(a : int, b : int) : int; + max : fn(a : int, b : int) : int; + abs : fn(x : int) : int; + error : fn(s : string); + warning : fn(md : ref Dat->Mntdir, t : string); + debuginit : fn(); + debug : fn(s : string); + memdebug : fn(s : string); + postnote : fn(t : int, this : int, pid : int, note : string) : int; + exec: fn(c: string, args : list of string); + getuser : fn() : string; + gethome : fn(user : string) : string; + access : fn(s : string) : int; + isalnum : fn(c : int) : int; + savemouse : fn(w : ref Windowm->Window); + restoremouse : fn(w : ref Windowm->Window); + clearmouse : fn(); + rgetc : fn(r : string, n : int) : int; + tgetc : fn(t : ref Textm->Text, n : int) : int; + reverse : fn(l : list of string) : list of string; + stralloc : fn(n : int) : ref Dat->Astring; + strfree :fn(s : ref Dat->Astring); + strchr : fn(s : string, c : int) : int; + strrchr: fn(s : string, c : int) : int; + strncmp : fn(s, t : string, n : int) : int; + getenv : fn(s : string) : string; + setenv : fn(s, t : string); + stob : fn(s : string, n : int) : array of byte; + btos : fn(b : array of byte, s : ref Dat->Astring); + findbl : fn(s : string, n : int) : (string, int); + skipbl : fn(s : string, n : int) : (string, int); + newwindow : fn(t : ref Textm->Text) : ref Windowm->Window; + getexc: fn(): string; + readexc : fn() : (int, string, string); +}; \ No newline at end of file diff --git a/appl/acme/wind.b b/appl/acme/wind.b new file mode 100644 index 00000000..2b97fafa --- /dev/null +++ b/appl/acme/wind.b @@ -0,0 +1,554 @@ +implement Windowm; + +include "common.m"; + +sys : Sys; +utils : Utils; +drawm : Draw; +graph : Graph; +gui : Gui; +dat : Dat; +bufferm : Bufferm; +textm : Textm; +filem : Filem; +look : Look; +scrl : Scroll; +acme : Acme; + +sprint : import sys; +FALSE, TRUE, XXX, Astring : import Dat; +Reffont, reffont, Lock, Ref, button, modbutton : import dat; +Point, Rect, Image : import drawm; +min, max, error, warning, stralloc, strfree : import utils; +font, draw : import graph; +black, white, mainwin : import gui; +Buffer : import bufferm; +Body, Text, Tag : import textm; +File : import filem; +Xfid : import Xfidm; +scrdraw : import scrl; +tagcols, textcols : import acme; +BORD : import Framem; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + dat = mods.dat; + utils = mods.utils; + drawm = mods.draw; + graph = mods.graph; + gui = mods.gui; + textm = mods.textm; + filem = mods.filem; + bufferm = mods.bufferm; + look = mods.look; + scrl = mods.scroll; + acme = mods.acme; +} + +winid : int; +nullwin : Window; + +Window.init(w : self ref Window, clone : ref Window, r : Rect) +{ + r1, br : Rect; + f : ref File; + rf : ref Reffont; + rp : ref Astring; + nc : int; + dummy : ref File = nil; + + c := w.col; + *w = nullwin; + w.col = c; + w.nopen = array[Dat->QMAX] of byte; + for (i := 0; i < Dat->QMAX; i++) + w.nopen[i] = byte 0; + w.qlock = Lock.init(); + w.ctllock = Lock.init(); + w.refx = Ref.init(); + w.tag = textm->newtext(); + w.tag.w = w; + w.body = textm->newtext(); + w.body.w = w; + w.id = ++winid; + w.refx.inc(); + w.ctlfid = ~0; + w.utflastqid = -1; + r1 = r; + r1.max.y = r1.min.y + font.height; + reffont.r.inc(); + f = dummy.addtext(w.tag); + w.tag.init(f, r1, reffont, tagcols); + w.tag.what = Tag; + # tag is a copy of the contents, not a tracked image + if(clone != nil){ + w.tag.delete(0, w.tag.file.buf.nc, TRUE); + nc = clone.tag.file.buf.nc; + rp = utils->stralloc(nc); + clone.tag.file.buf.read(0, rp, 0, nc); + w.tag.insert(0, rp.s, nc, TRUE, 0); + utils->strfree(rp); + rp = nil; + w.tag.file.reset(); + w.tag.setselect(nc, nc); + } + r1 = r; + r1.min.y += font.height + 1; + if(r1.max.y < r1.min.y) + r1.max.y = r1.min.y; + f = nil; + if(clone != nil){ + f = clone.body.file; + w.body.org = clone.body.org; + w.isscratch = clone.isscratch; + rf = Reffont.get(FALSE, FALSE, FALSE, clone.body.reffont.f.name); + }else + rf = Reffont.get(FALSE, FALSE, FALSE, nil); + f = f.addtext(w.body); + w.body.what = Body; + w.body.init(f, r1, rf, textcols); + r1.min.y -= 1; + r1.max.y = r1.min.y+1; + draw(mainwin, r1, tagcols[BORD], nil, (0, 0)); + scrdraw(w.body); + w.r = r; + w.r.max.y = w.body.frame.r.max.y; + br.min = w.tag.scrollr.min; + br.max.x = br.min.x + button.r.dx(); + br.max.y = br.min.y + button.r.dy(); + draw(mainwin, br, button, nil, button.r.min); + w.filemenu = TRUE; + w.maxlines = w.body.frame.maxlines; + if(clone != nil){ + w.dirty = clone.dirty; + w.body.setselect(clone.body.q0, clone.body.q1); + w.settag(); + } +} + +Window.reshape(w : self ref Window, r : Rect, safe : int) : int +{ + r1, br : Rect; + y : int; + b : ref Image; + + r1 = r; + r1.max.y = r1.min.y + font.height; + y = r1.max.y; + if(!safe || !w.tag.frame.r.eq(r1)){ + y = w.tag.reshape(r1); + b = button; + if(w.body.file.mod && !w.isdir && !w.isscratch) + b = modbutton; + br.min = w.tag.scrollr.min; + br.max.x = br.min.x + b.r.dx(); + br.max.y = br.min.y + b.r.dy(); + draw(mainwin, br, b, nil, b.r.min); + } + if(!safe || !w.body.frame.r.eq(r1)){ + if(y+1+font.height > r.max.y){ # no body + r1.min.y = y; + r1.max.y = y; + w.body.reshape(r1); + w.r = r; + w.r.max.y = y; + return y; + } + r1 = r; + r1.min.y = y; + r1.max.y = y + 1; + draw(mainwin, r1, tagcols[BORD], nil, (0, 0)); + r1.min.y = y + 1; + r1.max.y = r.max.y; + y = w.body.reshape(r1); + w.r = r; + w.r.max.y = y; + scrdraw(w.body); + } + w.maxlines = min(w.body.frame.nlines, max(w.maxlines, w.body.frame.maxlines)); + return w.r.max.y; +} + +Window.lock1(w : self ref Window, owner : int) +{ + w.refx.inc(); + w.qlock.lock(); + w.owner = owner; +} + +Window.lock(w : self ref Window, owner : int) +{ + i : int; + f : ref File; + + f = w.body.file; + for(i=0; i=0; i--){ + w = f.text[i].w; + w.owner = 0; + w.qlock.unlock(); + w.close(); + } +} + +Window.mousebut(w : self ref Window) +{ + graph->cursorset(w.tag.scrollr.min.add(w.tag.scrollr.max).div(2)); +} + +Window.dirfree(w : self ref Window) +{ + i : int; + dl : ref Dat->Dirlist; + + if(w.isdir){ + for(i=0; iactivewin == w) + dat->activewin = nil; + for(i=0; iXnil; + } +} + +Window.undo(w : self ref Window, isundo : int) +{ + body : ref Text; + i : int; + f : ref File; + v : ref Window; + + if(w==nil) + return; + w.utflastqid = -1; + body = w.body; + (body.q0, body.q1) = body.file.undo(isundo, body.q0, body.q1); + body.show(body.q0, body.q1); + f = body.file; + for(i=0; i=6 && name[n-6:n] == "/guide") + w.isscratch = TRUE; + else if(n>=7 && name[n-7:n] == "+Errors") + w.isscratch = TRUE; + t.file.setname(name, n); + for(i=0; istralloc(n); + w.tag.file.buf.read(0, r, 0, n); + for(i=0; istrfree(r); + r = nil; + w.tag.file.mod = FALSE; + if(w.tag.q0 > i) + w.tag.q0 = i; + if(w.tag.q1 > i) + w.tag.q1 = i; + w.tag.setselect(w.tag.q0, w.tag.q1); +} + +Window.settag(w : self ref Window) +{ + i : int; + f : ref File; + + f = w.body.file; + for(i=0; i0) + v.settag1(); + } +} + +Window.settag1(w : self ref Window) +{ + ii, j, k, n, bar, dirty : int; + old : ref Astring; + new : string; + r : int; + b : ref Image; + q0, q1 : int; + br : Rect; + + if(w.tag.ncache!=0 || w.tag.file.mod) + w.commit(w.tag); # check file name; also can now modify tag + old = utils->stralloc(w.tag.file.buf.nc); + w.tag.file.buf.read(0, old, 0, w.tag.file.buf.nc); + for(ii=0; iistralloc(w.tag.file.buf.nc); + w.tag.file.buf.read(0, old, 0, w.tag.file.buf.nc); + } + new = w.body.file.name + " Del Snarf"; + if(w.filemenu){ + if(w.body.file.delta.nc>0 || w.body.ncache) + new += " Undo"; + if(w.body.file.epsilon.nc > 0) + new += " Redo"; + dirty = w.body.file.name != nil && (w.body.ncache || w.body.file.seq!=w.putseq); + if(!w.isdir && dirty) + new += " Put"; + } + if(w.isdir) + new += " Get"; + l := len w.body.file.name; + if(l >= 2 && w.body.file.name[l-2: ] == ".b") + new += " Limbo"; + new += " |"; + r = utils->strchr(old.s, '|'); + if(r >= 0) + k = r+1; + else{ + k = w.tag.file.buf.nc; + if(w.body.file.seq == 0) + new += " Look "; + } + if(new != old.s[0:k]){ + n = k; + if(n > len new) + n = len new; + for(j=0; jstrchr(old.s, '|'); + if(r >= 0){ + bar = r; + if(q0 > bar){ + bar = utils->strchr(new, '|')-bar; + w.tag.q0 = q0+bar; + w.tag.q1 = q1+bar; + } + } + } + strfree(old); + old = nil; + new = nil; + w.tag.file.mod = FALSE; + n = w.tag.file.buf.nc+w.tag.ncache; + if(w.tag.q0 > n) + w.tag.q0 = n; + if(w.tag.q1 > n) + w.tag.q1 = n; + w.tag.setselect(w.tag.q0, w.tag.q1); + b = button; + if(!w.isdir && !w.isscratch && (w.body.file.mod || w.body.ncache)) + b = modbutton; + br.min = w.tag.scrollr.min; + br.max.x = br.min.x + b.r.dx(); + br.max.y = br.min.y + b.r.dy(); + draw(mainwin, br, b, nil, b.r.min); +} + +Window.commit(w : self ref Window, t : ref Text) +{ + r : ref Astring; + i : int; + f : ref File; + + t.commit(TRUE); + f = t.file; + if(f.ntext > 1) + for(i=0; istralloc(w.tag.file.buf.nc); + w.tag.file.buf.read(0, r, 0, w.tag.file.buf.nc); + for(i=0; iseq++; + w.body.file.mark(); + w.body.file.mod = TRUE; + w.dirty = TRUE; + w.setname(r.s, i); + w.settag(); + } + utils->strfree(r); + r = nil; +} + +Window.addincl(w : self ref Window, r : string, n : int) +{ + { + (ok, d) := sys->stat(r); + if(ok < 0){ + if(r[0] == '/') + raise "e"; + (r, n) = look->dirname(w.body, r, n); + (ok, d) = sys->stat(r); + if(ok < 0) + raise "e"; + } + if((d.mode&Sys->DMDIR) == 0){ + warning(nil, sprint("%s: not a directory\n", r)); + r = nil; + return; + } + w.nincl++; + owi := w.incl; + w.incl = array[w.nincl] of string; + w.incl[1:] = owi[0:w.nincl-1]; + owi = nil; + w.incl[0] = r; + r = nil; + } + exception{ + * => + warning(nil, sprint("%s: %r\n", r)); + r = nil; + } +} + +Window.clean(w : self ref Window, conservative : int, exiting : int) : int # as it stands, conservative is always TRUE +{ + if(w.isscratch || w.isdir) # don't whine if it's a guide file, error window, etc. + return TRUE; + if((!conservative||exiting) && w.nopen[Dat->QWevent]>byte 0) + return TRUE; + if(w.dirty){ + if(w.body.file.name != nil) + warning(nil, sprint("%s modified\n", w.body.file.name)); + else{ + if(w.body.file.buf.nc < 100) # don't whine if it's too small + return TRUE; + warning(nil, "unnamed file modified\n"); + } + w.dirty = FALSE; + return FALSE; + } + return TRUE; +} + +Window.ctlprint(w : self ref Window) : string +{ + return sprint("%11d %11d %11d %11d %11d ", w.id, w.tag.file.buf.nc, + w.body.file.buf.nc, w.isdir, w.dirty); +} + +Window.event(w : self ref Window, fmt : string) +{ + n : int; + x : ref Xfid; + + if(w.nopen[Dat->QWevent] == byte 0) + return; + if(w.owner == 0) + error("no window owner"); + n = len fmt; + w.events[len w.events] = w.owner; + w.events += fmt; + w.nevents += n+1; + x = w.eventx; + if(x != nil){ + w.eventx = nil; + x.c <-= Xfidm->Xnil; + } +} diff --git a/appl/acme/wind.m b/appl/acme/wind.m new file mode 100644 index 00000000..0a58b40a --- /dev/null +++ b/appl/acme/wind.m @@ -0,0 +1,67 @@ +Windowm : module { + PATH : con "/dis/acme/wind.dis"; + + init : fn(mods : ref Dat->Mods); + + Window : adt { + qlock : ref Dat->Lock; + refx : ref Dat->Ref; + tag : cyclic ref Textm->Text; + body : cyclic ref Textm->Text; + r : Draw->Rect; + isdir : int; + isscratch : int; + filemenu : int; + dirty : int; + id : int; + addr : Dat->Range; + limit : Dat->Range; + nopen : array of byte; + nomark : int; + noscroll : int; + echomode : int; + wrselrange : Dat->Range; + rdselfd : ref Sys->FD; + col : cyclic ref Columnm->Column; + eventx : cyclic ref Xfidm->Xfid; + events : string; + nevents : int; + owner : int; + maxlines : int; + dlp : array of ref Dat->Dirlist; + ndl : int; + putseq : int; + nincl : int; + incl : array of string; + reffont : ref Dat->Reffont; + ctllock : ref Dat->Lock; + ctlfid : int; + dumpstr : string; + dumpdir : string; + dumpid : int; + utflastqid : int; + utflastboff : int; + utflastq : int; + + init : fn(w : self ref Window, w0 : ref Window, r : Draw->Rect); + lock : fn(w : self ref Window, n : int); + lock1 : fn(w : self ref Window, n : int); + unlock : fn(w : self ref Window); + typex : fn(w : self ref Window, t : ref Textm->Text, r : int); + undo : fn(w : self ref Window, n : int); + setname : fn(w : self ref Window, r : string, n : int); + settag : fn(w : self ref Window); + settag1 : fn(w : self ref Window); + commit : fn(w : self ref Window, t : ref Textm->Text); + reshape : fn(w : self ref Window, r : Draw->Rect, n : int) : int; + close : fn(w : self ref Window); + delete : fn(w : self ref Window); + clean : fn(w : self ref Window, n : int, exiting : int) : int; + dirfree : fn(w : self ref Window); + event : fn(w : self ref Window, b : string); + mousebut : fn(w : self ref Window); + addincl : fn(w : self ref Window, r : string, n : int); + cleartag : fn(w : self ref Window); + ctlprint : fn(w : self ref Window) : string; + }; +}; diff --git a/appl/acme/xfid.b b/appl/acme/xfid.b new file mode 100644 index 00000000..0533a70e --- /dev/null +++ b/appl/acme/xfid.b @@ -0,0 +1,1087 @@ +implement Xfidm; + +include "common.m"; + +sys : Sys; +dat : Dat; +graph : Graph; +utils : Utils; +regx : Regx; +bufferm : Bufferm; +diskm : Diskm; +filem : Filem; +textm : Textm; +columnm : Columnm; +scrl : Scroll; +look : Look; +exec : Exec; +windowm : Windowm; +fsys : Fsys; +editm: Edit; +ecmd: Editcmd; +styxaux: Styxaux; + +UTFmax : import Sys; +sprint : import sys; +Smsg0 : import Dat; +TRUE, FALSE, XXX, BUFSIZE, MAXRPC : import Dat; +EM_NORMAL, EM_RAW, EM_MASK : import Dat; +Qdir, Qcons, Qlabel, Qindex, Qeditout : import Dat; +QWaddr, QWdata, QWevent, QWconsctl, QWctl, QWbody, QWeditout, QWtag, QWrdsel, QWwrsel : import Dat; +seq, cxfidfree, Lock, Ref, Range, Mntdir, Astring : import dat; +error, warning, max, min, stralloc, strfree, strncmp : import utils; +address : import regx; +Buffer : import bufferm; +File : import filem; +Text : import textm; +scrdraw : import scrl; +Window : import windowm; +bflush : import graph; +Column : import columnm; +row : import dat; +FILE, QID, respond : import fsys; +oldtag, name, offset, count, data, setcount, setdata : import styxaux; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + dat = mods.dat; + graph = mods.graph; + utils = mods.utils; + regx = mods.regx; + filem = mods.filem; + bufferm = mods.bufferm; + diskm = mods.diskm; + textm = mods.textm; + columnm = mods.columnm; + scrl = mods.scroll; + look = mods.look; + exec = mods.exec; + windowm = mods.windowm; + fsys = mods.fsys; + editm = mods.edit; + ecmd = mods.editcmd; + styxaux = mods.styxaux; +} + +nullxfid : Xfid; + +newxfid() : ref Xfid +{ + x := ref Xfid; + *x = nullxfid; + x.buf = array[fsys->messagesize+UTFmax] of byte; + return x; +} + +Ctlsize : con 5*12; + +Edel := "deleted window"; +Ebadctl := "ill-formed control message"; +Ebadaddr := "bad address syntax"; +Eaddr := "address out of range"; +Einuse := "already in use"; +Ebadevent:= "bad event syntax"; + +clampaddr(w : ref Window) +{ + if(w.addr.q0 < 0) + w.addr.q0 = 0; + if(w.addr.q1 < 0) + w.addr.q1 = 0; + if(w.addr.q0 > w.body.file.buf.nc) + w.addr.q0 = w.body.file.buf.nc; + if(w.addr.q1 > w.body.file.buf.nc) + w.addr.q1 = w.body.file.buf.nc; +} + +xfidtid : array of int; +nxfidtid := 0; + +xfidkill() +{ + if (sys == nil) + return; + thispid := sys->pctl(0, nil); + for (i := 0; i < nxfidtid; i++) + utils->postnote(Utils->PNPROC, thispid, xfidtid[i], "kill"); +} + +Xfid.ctl(x : self ref Xfid) +{ + x.tid = sys->pctl(0, nil); + ox := xfidtid; + xfidtid = array[nxfidtid+1] of int; + xfidtid[0:] = ox[0:nxfidtid]; + xfidtid[nxfidtid++] = x.tid; + ox = nil; + for (;;) { + f := <- x.c; + case (f) { + Xnil => ; + Xflush => x.flush(); + Xwalk => x.walk(nil); + Xopen => x.open(); + Xclose => x.close(); + Xread => x.read(); + Xwrite => x.write(); + * => error("bad case in Xfid.ctl()"); + } + bflush(); + cxfidfree <-= x; + } +} + +Xfid.flush(x : self ref Xfid) +{ + fc : Smsg0; + i, j : int; + w : ref Window; + c : ref Column; + wx : ref Xfid; + + # search windows for matching tag + row.qlock.lock(); +loop: + for(j=0; jprocs now + w = utils->newwindow(nil); + w.settag(); + # w.refx.inc(); + # x.f.w = w; + # x.f.qid.path = big QID(w.id, Qdir); + # x.f.qid.qtype = Sys->QTDIR; + # fc.qid = x.f.qid; + row.qlock.unlock(); + # respond(x, fc, nil); + cw <-= w; +} + +Xfid.open(x : self ref Xfid) +{ + fc : Smsg0; + w : ref Window; + q : int; + + fc.iounit = 0; + w = x.f.w; + if(w != nil){ + t := w.body; + row.qlock.lock(); # tasks->procs now + w.lock('E'); + q = FILE(x.f.qid); + case(q){ + QWaddr or QWdata or QWevent => + if(w.nopen[q]++ == byte 0){ + if(q == QWaddr){ + w.addr = (Range)(0,0); + w.limit = (Range)(-1,-1); + } + if(q==QWevent && !w.isdir && w.col!=nil){ + w.filemenu = FALSE; + w.settag(); + } + } + QWrdsel => + # + # Use a temporary file. + # A pipe would be the obvious, but we can't afford the + # broken pipe notification. Using the code to read QWbody + # is n², which should probably also be fixed. Even then, + # though, we'd need to squirrel away the data in case it's + # modified during the operation, e.g. by |sort + # + if(w.rdselfd != nil){ + w.unlock(); + respond(x, fc, Einuse); + return; + } + w.rdselfd = diskm->tempfile(); + if(w.rdselfd == nil){ + w.unlock(); + respond(x, fc, "can't create temp file"); + return; + } + w.nopen[q]++; + q0 := t.q0; + q1 := t.q1; + r := utils->stralloc(BUFSIZE); + while(q0 < q1){ + n := q1 - q0; + if(n > BUFSIZE) + n = BUFSIZE; + t.file.buf.read(q0, r, 0, n); + s := array of byte r.s[0:n]; + m := len s; + if(sys->write(w.rdselfd, s, m) != m){ + warning(nil, "can't write temp file for pipe command %r\n"); + break; + } + s = nil; + q0 += n; + } + utils->strfree(r); + QWwrsel => + w.nopen[q]++; + seq++; + t.file.mark(); + exec->cut(t, t, FALSE, TRUE); + w.wrselrange = (Range)(t.q1, t.q1); + w.nomark = TRUE; + QWeditout => + if(editm->editing == FALSE){ + w.unlock(); + respond(x, fc, "permission denied"); + return; + } + w.wrselrange = (Range)(t.q1, t.q1); + break; + } + w.unlock(); + row.qlock.unlock(); + } + fc.qid = x.f.qid; + fc.iounit = fsys->messagesize-Styx->IOHDRSZ; + x.f.open = TRUE; + respond(x, fc, nil); +} + +Xfid.close(x : self ref Xfid) +{ + fc : Smsg0; + w : ref Window; + q : int; + + w = x.f.w; + # BUG in C version ? fsysclunk() has just set busy, open to FALSE + # x.f.busy = FALSE; + # if(!x.f.open){ + # if(w != nil) + # w.close(); + # respond(x, fc, nil); + # return; + # } + # x.f.open = FALSE; + if(w != nil){ + row.qlock.lock(); # tasks->procs now + w.lock('E'); + q = FILE(x.f.qid); + case(q){ + QWctl => + if(w.ctlfid!=~0 && w.ctlfid==x.f.fid){ + w.ctlfid = ~0; + w.ctllock.unlock(); + } + QWdata or QWaddr or QWevent => + # BUG: do we need to shut down Xfid? + if (q == QWdata) + w.nomark = FALSE; + if(--w.nopen[q] == byte 0){ + if(q == QWdata) + w.nomark = FALSE; + if(q==QWevent && !w.isdir && w.col!=nil){ + w.filemenu = TRUE; + w.settag(); + } + if(q == QWevent){ + w.dumpstr = nil; + w.dumpdir = nil; + } + } + QWrdsel => + w.rdselfd = nil; + QWwrsel => + w.nomark = FALSE; + t :=w.body; + # before: only did this if !w->noscroll, but that didn't seem right in practice + t.show(min(w.wrselrange.q0, t.file.buf.nc), + min(w.wrselrange.q1, t.file.buf.nc)); + scrdraw(t); + QWconsctl=> + w.echomode = EM_NORMAL; + } + w.close(); + w.unlock(); + row.qlock.unlock(); + } + respond(x, fc, nil); +} + +Xfid.read(x : self ref Xfid) +{ + fc : Smsg0; + n, q : int; + off : int; + sbuf : string; + buf : array of byte; + w : ref Window; + + sbuf = nil; + q = FILE(x.f.qid); + w = x.f.w; + if(w == nil){ + fc.count = 0; + case(q){ + Qcons or Qlabel => + ; + Qindex => + x.indexread(); + return; + * => + warning(nil, sprint("unknown qid %d\n", q)); + } + respond(x, fc, nil); + return; + } + w.lock('F'); + if(w.col == nil){ + w.unlock(); + respond(x, fc, Edel); + return; + } + off = int offset(x.fcall); + case(q){ + QWaddr => + w.body.commit(TRUE); + clampaddr(w); + sbuf = sprint("%11d %11d ", w.addr.q0, w.addr.q1); + QWbody => + x.utfread(w.body, 0, w.body.file.buf.nc, QWbody); + QWctl => + sbuf = w.ctlprint(); + QWevent => + x.eventread(w); + QWdata => + # BUG: what should happen if q1 > q0? + if(w.addr.q0 > w.body.file.buf.nc){ + respond(x, fc, Eaddr); + break; + } + w.addr.q0 += x.runeread(w.body, w.addr.q0, w.body.file.buf.nc); + w.addr.q1 = w.addr.q0; + QWtag => + x.utfread(w.tag, 0, w.tag.file.buf.nc, QWtag); + QWrdsel => + sys->seek(w.rdselfd, big off, 0); + n = count(x.fcall); + if(n > BUFSIZE) + n = BUFSIZE; + b := array[n] of byte; + n = sys->read(w.rdselfd, b, n); + if(n < 0){ + respond(x, fc, "I/O error in temp file"); + break; + } + fc.count = n; + fc.data = b; + respond(x, fc, nil); + b = nil; + * => + sbuf = sprint("unknown qid %d in read", q); + respond(x, fc, sbuf); + sbuf = nil; + } + if (sbuf != nil) { + buf = array of byte sbuf; + sbuf = nil; + n = len buf; + if(off > n) + off = n; + if(off+count(x.fcall) > n) + setcount(x.fcall, n-off); + fc.count = count(x.fcall); + fc.data = buf[off:]; + respond(x, fc, nil); + buf = nil; + } + w.unlock(); +} + +Xfid.write(x : self ref Xfid) +{ + fc : Smsg0; + c, cnt, qid, q, nb, nr, eval : int; + w : ref Window; + r : string; + a : Range; + t : ref Text; + q0, tq0, tq1 : int; + md : ref Mntdir; + + qid = FILE(x.f.qid); + w = x.f.w; + row.qlock.lock(); # tasks->procs now + if(w != nil){ + c = 'F'; + if(qid==QWtag || qid==QWbody) + c = 'E'; + w.lock(c); + if(w.col == nil){ + w.unlock(); + row.qlock.unlock(); + respond(x, fc, Edel); + return; + } + } + bodytag := 0; + case(qid){ + Qcons => + md = x.f.mntdir; + warning(md, string data(x.fcall)); + fc.count = count(x.fcall); + respond(x, fc, nil); + QWconsctl => + if (w != nil) { + r = string data(x.fcall); + if (strncmp(r, "rawon", 5) == 0) + w.echomode = EM_RAW; + else if (strncmp(r, "rawoff", 6) == 0) + w.echomode = EM_NORMAL; + } + fc.count = count(x.fcall); + respond(x, fc, nil); + Qlabel => + fc.count = count(x.fcall); + respond(x, fc, nil); + QWaddr => + r = string data(x.fcall); + nr = len r; + t = w.body; + w.commit(t); + (eval, nb, a) = address(x.f.mntdir, t, w.limit, w.addr, nil, r, 0, nr, TRUE); + r = nil; + if(nb < nr){ + respond(x, fc, Ebadaddr); + break; + } + if(!eval){ + respond(x, fc, Eaddr); + break; + } + w.addr = a; + fc.count = count(x.fcall); + respond(x, fc, nil); + Qeditout or + QWeditout => + r = string data(x.fcall); + nr = len r; + if(w!=nil) + err := ecmd->edittext(w.body.file, w.wrselrange.q1, r, nr); + else + err = ecmd->edittext(nil, 0, r, nr); + r = nil; + if(err != nil){ + respond(x, fc, err); + break; + } + fc.count = count(x.fcall); + respond(x, fc, nil); + break; + QWbody or QWwrsel => + t = w.body; + bodytag = 1; + QWctl => + x.ctlwrite(w); + QWdata => + t = w.body; + w.commit(t); + if(w.addr.q0>t.file.buf.nc || w.addr.q1>t.file.buf.nc){ + respond(x, fc, Eaddr); + break; + } + nb = sys->utfbytes(data(x.fcall), count(x.fcall)); + r = string data(x.fcall)[0:nb]; + nr = len r; + if(w.nomark == FALSE){ + seq++; + t.file.mark(); + } + q0 = w.addr.q0; + if(w.addr.q1 > q0){ + t.delete(q0, w.addr.q1, TRUE); + w.addr.q1 = q0; + } + tq0 = t.q0; + tq1 = t.q1; + t.insert(q0, r, nr, TRUE, 0); + if(tq0 >= q0) + tq0 += nr; + if(tq1 >= q0) + tq1 += nr; + if(!t.w.noscroll) + t.show(tq0, tq1); + scrdraw(t); + w.settag(); + r = nil; + w.addr.q0 += nr; + w.addr.q1 = w.addr.q0; + fc.count = count(x.fcall); + respond(x, fc, nil); + QWevent => + x.eventwrite(w); + QWtag => + t = w.tag; + bodytag = 1; + * => + r = sprint("unknown qid %d in write", qid); + respond(x, fc, r); + r = nil; + } + if (bodytag) { + q = x.f.nrpart; + cnt = count(x.fcall); + if(q > 0){ + nd := array[cnt+q] of byte; + nd[q:] = data(x.fcall)[0:cnt]; + nd[0:] = x.f.rpart[0:q]; + setdata(x.fcall, nd); + cnt += q; + x.f.nrpart = 0; + } + nb = sys->utfbytes(data(x.fcall), cnt); + r = string data(x.fcall)[0:nb]; + nr = len r; + if(nb < cnt){ + x.f.rpart = data(x.fcall)[nb:cnt]; + x.f.nrpart = cnt-nb; + } + if(nr > 0){ + t.w.commit(t); + if(qid == QWwrsel){ + q0 = w.wrselrange.q1; + if(q0 > t.file.buf.nc) + q0 = t.file.buf.nc; + }else + q0 = t.file.buf.nc; + if(qid == QWbody || qid == QWwrsel){ + if(!w.nomark){ + seq++; + t.file.mark(); + } + (q0, nr) = t.bsinsert(q0, r, nr, TRUE); + if(qid!=QWwrsel && !t.w.noscroll) + t.show(q0+nr, q0+nr); + scrdraw(t); + }else + t.insert(q0, r, nr, TRUE, 0); + w.settag(); + if(qid == QWwrsel) + w.wrselrange.q1 += nr; + r = nil; + } + fc.count = count(x.fcall); + respond(x, fc, nil); + } + if(w != nil) + w.unlock(); + row.qlock.unlock(); +} + +Xfid.ctlwrite(x : self ref Xfid, w : ref Window) +{ + fc : Smsg0; + i, m, n, nb : int; + r, err, p, pp : string; + q : int; + scrdrw, settag : int; + t : ref Text; + + err = nil; + scrdrw = FALSE; + settag = FALSE; + w.tag.commit(TRUE); + nb = sys->utfbytes(data(x.fcall), count(x.fcall)); + r = string data(x.fcall)[0:nb]; +loop : + for(n=0; nstrchr(pp, '\n'); + if(q<=0){ + err = Ebadctl; + break; + } + nm := pp[0:q]; + for(i=0; istrchr(pp, '\n'); + if(q<=0){ + err = Ebadctl; + break; + } + nm := pp[0:q]; + w.dumpstr = nm; + m += (q+1); + }else + if(strncmp(p, "dumpdir ", 8) == 0){ # set dump directory + pp = p[8:]; + m = 8; + q = utils->strchr(pp, '\n'); + if(q<=0){ + err = Ebadctl; + break; + } + nm := pp[0:q]; + w.dumpdir = nm; + m += (q+1); + }else + if(strncmp(p, "delete", 6) == 0){ # delete for sure + w.col.close(w, TRUE); + m = 6; + }else + if(strncmp(p, "del", 3) == 0){ # delete, but check dirty + if(!w.clean(TRUE, FALSE)){ + err = "file dirty"; + break; + } + w.col.close(w, TRUE); + m = 3; + }else + if(strncmp(p, "get", 3) == 0){ # get file + exec->get(w.body, nil, nil, FALSE, nil, 0); + m = 3; + }else + if(strncmp(p, "put", 3) == 0){ # put file + exec->put(w.body, nil, nil, 0); + m = 3; + }else + if(strncmp(p, "dot=addr", 8) == 0){ # set dot + w.body.commit(TRUE); + clampaddr(w); + w.body.q0 = w.addr.q0; + w.body.q1 = w.addr.q1; + w.body.setselect(w.body.q0, w.body.q1); + settag = TRUE; + m = 8; + }else + if(strncmp(p, "addr=dot", 8) == 0){ # set addr + w.addr.q0 = w.body.q0; + w.addr.q1 = w.body.q1; + m = 8; + }else + if(strncmp(p, "limit=addr", 10) == 0){ # set limit + w.body.commit(TRUE); + clampaddr(w); + w.limit.q0 = w.addr.q0; + w.limit.q1 = w.addr.q1; + m = 10; + }else + if(strncmp(p, "nomark", 6) == 0){ # turn off automatic marking + w.nomark = TRUE; + m = 6; + }else + if(strncmp(p, "mark", 4) == 0){ # mark file + seq++; + w.body.file.mark(); + settag = TRUE; + m = 4; + }else + if(strncmp(p, "noscroll", 8) == 0){ # turn off automatic scrolling + w.noscroll = TRUE; + m = 8; + }else + if(strncmp(p, "cleartag", 8) == 0){ # wipe tag right of bar + w.cleartag(); + settag = TRUE; + m = 8; + }else + if(strncmp(p, "scroll", 6) == 0){ # turn on automatic scrolling (writes to body only) + w.noscroll = FALSE; + m = 6; + }else + if(strncmp(p, "noecho", 6) == 0){ # don't echo chars - mask them + w.echomode = EM_MASK; + m = 6; + }else + if (strncmp(p, "echo", 4) == 0){ # echo chars (normal state) + w.echomode = EM_NORMAL; + m = 4; + }else{ + err = Ebadctl; + break; + } + while(m < len p && p[m] == '\n') + m++; + } + + ab := array of byte r[0:n]; + n = len ab; + ab = nil; + r = nil; + if(err != nil) + n = 0; + fc.count = n; + respond(x, fc, err); + if(settag) + w.settag(); + if(scrdrw) + scrdraw(w.body); +} + +Xfid.eventwrite(x : self ref Xfid, w : ref Window) +{ + fc : Smsg0; + m, n, nb : int; + r, err : string; + p, q : int; + t : ref Text; + c : int; + q0, q1 : int; + + err = nil; + nb = sys->utfbytes(data(x.fcall), count(x.fcall)); + r = string data(x.fcall)[0:nb]; +loop : + for(n=0; n= '0' && r[q] <= '9') + q++; + if(q == p) { + err = Ebadevent; + break; + } + p = q; + while(r[p] == ' ') + p++; + q1 = int r[p:]; + q = p; + if (r[q] == '+' || r[q] == '-') + q++; + while (r[q] >= '0' && r[q] <= '9') + q++; + if(q == p) { + err = Ebadevent; + break; + } + p = q; + while(r[p] == ' ') + p++; + if(r[p++] != '\n') { + err = Ebadevent; + break; + } + m = p-n; + if('a'<=c && c<='z') + t = w.tag; + else if('A'<=c && c<='Z') + t = w.body; + else { + err = Ebadevent; + break; + } + if(q0>t.file.buf.nc || q1>t.file.buf.nc || q0>q1) { + err = Ebadevent; + break; + } + # row.qlock.lock(); + case(c){ + 'x' or 'X' => + exec->execute(t, q0, q1, TRUE, nil); + 'l' or 'L' => + look->look3(t, q0, q1, TRUE); + * => + err = Ebadevent; + break loop; + } + # row.qlock.unlock(); + } + + ab := array of byte r[0:n]; + n = len ab; + ab = nil; + r = nil; + if(err != nil) + n = 0; + fc.count = n; + respond(x, fc, err); +} + +Xfid.utfread(x : self ref Xfid, t : ref Text, q0, q1 : int, qid : int) +{ + fc : Smsg0; + w : ref Window; + r : ref Astring; + b, b1 : array of byte; + q, off, boff : int; + m, n, nr, nb : int; + + w = t.w; + w.commit(t); + off = int offset(x.fcall); + r = stralloc(BUFSIZE); + b1 = array[MAXRPC] of byte; + n = 0; + if(qid==w.utflastqid && off>=w.utflastboff && w.utflastq<=q1){ + boff = w.utflastboff; + q = w.utflastq; + }else{ + # BUG: stupid code: scan from beginning + boff = 0; + q = q0; + } + w.utflastqid = qid; + while(q BUFSIZE) + nr = BUFSIZE; + t.file.buf.read(q, r, 0, nr); + b = array of byte r.s[0:nr]; + nb = len b; + if(boff >= off){ + m = nb; + if(boff+m > off+count(x.fcall)) + m = off+count(x.fcall) - boff; + b1[n:] = b[0:m]; + n += m; + }else if(boff+nb > off){ + if(n != 0) + error("bad count in utfrune"); + m = nb - (off-boff); + if(m > count(x.fcall)) + m = count(x.fcall); + b1[0:] = b[off-boff:off-boff+m]; + n += m; + } + b = nil; + boff += nb; + q += nr; + } + strfree(r); + r = nil; + fc.count = n; + fc.data = b1; + respond(x, fc, nil); + b1 = nil; +} + +Xfid.runeread(x : self ref Xfid, t : ref Text, q0, q1 : int) : int +{ + fc : Smsg0; + w : ref Window; + r : ref Astring; + junk, ok : int; + b, b1 : array of byte; + q, boff : int; + i, rw, m, n, nr, nb : int; + + w = t.w; + w.commit(t); + r = stralloc(BUFSIZE); + b1 = array[MAXRPC] of byte; + n = 0; + q = q0; + boff = 0; + while(q BUFSIZE) + nr = BUFSIZE; + t.file.buf.read(q, r, 0, nr); + b = array of byte r.s[0:nr]; + nb = len b; + m = nb; + if(boff+m > count(x.fcall)){ + i = count(x.fcall) - boff; + # copy whole runes only + m = 0; + nr = 0; + while(m < i){ + (junk, rw, ok) = sys->byte2char(b, m); + if(m+rw > i) + break; + m += rw; + nr++; + } + if(m == 0) + break; + } + b1[n:] = b[0:m]; + b = nil; + n += m; + boff += nb; + q += nr; + } + strfree(r); + r = nil; + fc.count = n; + fc.data = b1; + respond(x, fc, nil); + b1 = nil; + return q-q0; +} + +Xfid.eventread(x : self ref Xfid, w : ref Window) +{ + fc : Smsg0; + b : string; + i, n : int; + + i = 0; + x.flushed = FALSE; + while(w.nevents == 0){ + if(i){ + if(!x.flushed) + respond(x, fc, "window shut down"); + return; + } + w.eventx = x; + w.unlock(); + <- x.c; + w.lock('F'); + i++; + } + eveb := array of byte w.events; + ne := len eveb; + n = w.nevents; + if(ne > count(x.fcall)) { + ne = count(x.fcall); + while (sys->utfbytes(eveb, ne) != ne) + --ne; + s := string eveb[0:ne]; + n = len s; + s = nil; + } + fc.count = ne; + fc.data = eveb; + respond(x, fc, nil); + b = w.events; + w.events = w.events[n:]; + b = nil; + w.nevents -= n; + eveb = nil; +} + +Xfid.indexread(x : self ref Xfid) +{ + fc : Smsg0; + i, j, m, n, nmax, cnt, off : int; + w : ref Window; + b : array of byte; + r : ref Astring; + c : ref Column; + + row.qlock.lock(); + nmax = 0; + for(j=0; j n) + off = n; + if(off+cnt > n) + cnt = n-off; + fc.count = cnt; + fc.data = b[off:off+cnt]; + respond(x, fc, nil); + b = nil; + strfree(r); + r = nil; +} diff --git a/appl/acme/xfid.m b/appl/acme/xfid.m new file mode 100644 index 00000000..c0d10fa7 --- /dev/null +++ b/appl/acme/xfid.m @@ -0,0 +1,34 @@ +Xfidm : module { + PATH : con "/dis/acme/xfid.dis"; + + Xnil, Xflush, Xwalk, Xopen, Xclose, Xread, Xwrite : con iota; + + init : fn(mods : ref Dat->Mods); + + newxfid : fn() : ref Xfid; + xfidkill : fn(); + + Xfid : adt { + tid : int; + fcall : ref Styx->Tmsg; + next : cyclic ref Xfid; + c : chan of int; + f : cyclic ref Dat->Fid; + buf : array of byte; + flushed : int; + + ctl : fn(x : self ref Xfid); + flush: fn(x : self ref Xfid); + walk: fn(x : self ref Xfid, c: chan of ref Windowm->Window); + open: fn(x : self ref Xfid); + close: fn(x : self ref Xfid); + read: fn(x : self ref Xfid); + write: fn(x : self ref Xfid); + ctlwrite: fn(x : self ref Xfid, w : ref Windowm->Window); + eventread: fn(x : self ref Xfid, w : ref Windowm->Window); + eventwrite: fn(x : self ref Xfid, w : ref Windowm->Window); + indexread: fn(x : self ref Xfid); + utfread: fn(x : self ref Xfid, t : ref Textm->Text, m : int, n : int, qid : int); + runeread: fn(x : self ref Xfid, t : ref Textm->Text, m : int, n : int) : int; + }; +}; diff --git a/appl/alphabet/abc/abc.b b/appl/alphabet/abc/abc.b new file mode 100644 index 00000000..b8d25342 --- /dev/null +++ b/appl/alphabet/abc/abc.b @@ -0,0 +1,53 @@ +implement Mkabc, Abcmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + report: import reports; +include "alphabet.m"; +include "alphabet/abc.m"; + abc: Abc; + Value: import abc; + +Mkabc: module {}; +types(): string +{ + return "A"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + abc = checkload(load Abc Abc->PATH, Abc->PATH); + abc->init(); +} + +quit() +{ +} + +run(errorc: chan of string, nil: ref Reports->Report, + nil: list of (int, list of ref Value), + nil: list of ref Value + ): ref Value +{ + alphabet := load Alphabet Alphabet->PATH; + if(alphabet == nil){ + report(errorc, sys->sprint("abc: cannot load %q: %r", Alphabet->PATH)); + return nil; + } + alphabet->init(); + c := chan[1] of int; + c <-= 1; + return ref Value.VA((c, alphabet)); +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} diff --git a/appl/alphabet/abc/autoconvert.b b/appl/alphabet/abc/autoconvert.b new file mode 100644 index 00000000..5e542c80 --- /dev/null +++ b/appl/alphabet/abc/autoconvert.b @@ -0,0 +1,80 @@ +implement Autoconvert, Abcmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + Cmd, + n_BLOCK, n_WORD, n_SEQ, n_LIST, n_ADJ, n_VAR: import Sh; +include "alphabet/reports.m"; + reports: Reports; + report: import reports; +include "alphabet.m"; +include "alphabet/abc.m"; + abc: Abc; + Value: import abc; + +Autoconvert: module {}; +types(): string +{ + return "AAssc"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + abc = checkload(load Abc Abc->PATH, Abc->PATH); + abc->init(); +} + +quit() +{ +} + +run(errorc: chan of string, nil: ref Reports->Report, + nil: list of (int, list of ref Value), + args: list of ref Value + ): ref Value +{ + a := (hd args).A().i.alphabet; + src := (hd tl args).s().i; + dst := (hd tl tl args).s().i; + c := (hd tl tl tl args).c().i; + + # {word} -> {(src); word $1} + if(c.ntype == n_BLOCK && c.left.ntype == n_WORD){ + c = mk(n_BLOCK, + mk(n_SEQ, + mk(n_LIST, mkw(src), nil), + mk(n_ADJ, + c.left, + mk(n_VAR, mkw("1"), nil) + ) + ), + nil + ); + } + + err := a->autoconvert(src, dst, c, errorc); + if(err != nil){ + report(errorc, "abcautoconvert: "+err); + return nil; + } + return (hd args).dup(); +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} + +mk(ntype: int, left, right: ref Cmd): ref Cmd +{ + return ref Cmd(ntype, left, right, nil, nil); +} +mkw(w: string): ref Cmd +{ + return ref Cmd(n_WORD, nil, nil, w, nil); +} diff --git a/appl/alphabet/abc/autodeclare.b b/appl/alphabet/abc/autodeclare.b new file mode 100644 index 00000000..b79009ed --- /dev/null +++ b/appl/alphabet/abc/autodeclare.b @@ -0,0 +1,42 @@ +implement Autoconvert, Abcmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; +include "alphabet.m"; +include "alphabet/abc.m"; + abc: Abc; + Value: import abc; + +Autoconvert: module {}; +types(): string +{ + return "AAs"; +} + +init() +{ + abc = checkload(load Abc Abc->PATH, Abc->PATH); + abc->init(); +} + +quit() +{ +} + +run(nil: chan of string, nil: ref Reports->Report, + nil: list of (int, list of ref Value), + args: list of ref Value + ): ref Value +{ + (hd args).A().i.alphabet->setautodeclare(int (hd tl args).s().i); + return (hd args).dup(); +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} diff --git a/appl/alphabet/abc/declare.b b/appl/alphabet/abc/declare.b new file mode 100644 index 00000000..4c7bc020 --- /dev/null +++ b/appl/alphabet/abc/declare.b @@ -0,0 +1,70 @@ +implement Declare, Abcmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + report: import reports; +include "alphabet.m"; +include "alphabet/abc.m"; + abc: Abc; + Value: import abc; + +Declare: module {}; +types(): string +{ + return "AAss*-q-c"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + abc = checkload(load Abc Abc->PATH, Abc->PATH); + abc->init(); +} + +quit() +{ +} + +run(errorc: chan of string, nil: ref Reports->Report, + opts: list of (int, list of ref Value), + args: list of ref Value + ): ref Value +{ + flags := 0; + for(; opts != nil; opts = tl opts){ + case (hd opts).t0 { + 'q' => + flags |= Alphabet->ONDEMAND; + 'c' => + flags |= Alphabet->CHECK; + } + } + + n := len args; + if(n > 3){ + report(errorc, "declare: maximum of two arguments allowed"); + return nil; + } + a := (hd args).A().i.alphabet; + m := (hd tl args).s().i; + sig := ""; + if(n > 2) + sig = (hd tl tl args).s().i; + e := a->declare(m, sig, flags); + if(e != nil){ + report(errorc, "declare: "+e); + return nil; + } + return (hd args).dup(); +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} diff --git a/appl/alphabet/abc/declares.b b/appl/alphabet/abc/declares.b new file mode 100644 index 00000000..f4e58467 --- /dev/null +++ b/appl/alphabet/abc/declares.b @@ -0,0 +1,124 @@ +implement Declares, Abcmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + n_BLOCK, n_ADJ, n_VAR, n_WORD: import Sh; +include "alphabet/reports.m"; + reports: Reports; + report, Report: import reports; +include "alphabet.m"; + alphabet: Alphabet; +include "alphabet/abc.m"; + abc: Abc; + Value: import abc; +include "alphabet/abctypes.m"; + abctypes: Abctypes; + Abccvt: import abctypes; + +cvt: ref Abccvt; + +types(): string +{ + return "AAc"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + abc = checkload(load Abc Abc->PATH, Abc->PATH); + abc->init(); + alphabet = checkload(load Alphabet Alphabet->PATH, Alphabet->PATH); + alphabet->init(); + abctypes = checkload(load Abctypes Abctypes->PATH, Abctypes->PATH); + (c, nil, abccvt) := abctypes->proxy0(); + cvt = abccvt; + alphabet->loadtypeset("/abc", c, nil); + alphabet->importtype("/abc/abc"); + alphabet->importtype("/string"); + alphabet->importtype("/cmd"); + c = nil; + # note: it's faster if we provide the signatures, as we don't + # have to load the module to find out its signature just to throw + # it away again. pity about the maintenance. + + # Edit x s:(/abc/[a-z]+) (.*):declimport("\1", "\2"); + declimport("/abc/autoconvert", "abc string string cmd -> abc"); + declimport("/abc/autodeclare", "abc string -> abc"); + declimport("/abc/declare", "[-qc] abc string [string...] -> abc"); + declimport("/abc/define", "abc string cmd -> abc"); + declimport("/abc/import", "abc string [string...] -> abc"); + declimport("/abc/type", "abc string [string...] -> abc"); + declimport("/abc/typeset", "abc string -> abc"); + declimport("/abc/undeclare", "abc string [string...] -> abc"); +} + +quit() +{ + alphabet->quit(); +} + +run(errorc: chan of string, r: ref Reports->Report, + nil: list of (int, list of ref Value), + args: list of ref Value + ): ref Value +{ + (av, err) := alphabet->importvalue(cvt.int2ext((hd args).dup()), "/abc/abc"); + if(av == nil){ + report(errorc, sys->sprint("declares: cannot import abc value: %s", err)); + return nil; + } + vc := chan of ref Alphabet->Value; + spawn alphabet->eval0((hd tl args).c().i, "/abc/abc", nil, r, r.start("evaldecl"), av :: nil, vc); + av = <-vc; + if(av == nil) + return nil; + v := cvt.ext2int(av).dup(); + alphabet->av.free(1); + return v; +} + +declimport(m: string, sig: string) +{ + if((e := alphabet->declare(m, sig, Alphabet->ONDEMAND)) != nil) + raise sys->sprint("fail:cannot declare %s: %s", m, e); + alphabet->importmodule(m); +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} + +declares(a: Alphabet, decls: ref Sh->Cmd, errorc: chan of string, stopc: chan of int): string +{ + spawn reports->reportproc(reportc := chan of string, stopc, reply := chan of ref Report); + r := <-reply; + reply = nil; + spawn declaresproc(a, decls, r.start("declares"), r, vc := chan of ref Value); + r.enable(); + + v: ref Value; +wait: + for(;;)alt{ + v = <-vc => + ; + msg := <-reportc => + if(msg == nil) + break wait; + errorc <-= sys->sprint("declares: %s", msg); + } + if(v == nil) + return "declarations failed"; + return nil; +} + +declaresproc(a: Alphabet, decls: ref Sh->Cmd, errorc: chan of string, r: ref Report, vc: chan of ref Value) +{ + novals: list of ref Value; + vc <-= run(errorc, r, nil, abc->mkabc(a).dup() :: ref Value.Vc(decls) :: novals); + errorc <-= nil; +} diff --git a/appl/alphabet/abc/define.b b/appl/alphabet/abc/define.b new file mode 100644 index 00000000..d6929b5d --- /dev/null +++ b/appl/alphabet/abc/define.b @@ -0,0 +1,52 @@ +implement Define, Abcmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + report: import reports; +include "alphabet.m"; +include "alphabet/abc.m"; + abc: Abc; + Value: import abc; + +Define: module {}; +types(): string +{ + return "AAsc"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + abc = checkload(load Abc Abc->PATH, Abc->PATH); + abc->init(); +} + +quit() +{ +} + +run(errorc: chan of string, nil: ref Reports->Report, + nil: list of (int, list of ref Value), + args: list of ref Value + ): ref Value +{ + a := (hd args).A().i.alphabet; + m := (hd tl args).s().i; + c := (hd tl tl args).c().i; + if((e := a->define(m, c, errorc)) != nil){ + report(errorc, "define: error: "+e); + return nil; + } + return (hd args).dup(); +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} diff --git a/appl/alphabet/abc/eval.b b/appl/alphabet/abc/eval.b new file mode 100644 index 00000000..184aa0fd --- /dev/null +++ b/appl/alphabet/abc/eval.b @@ -0,0 +1,66 @@ +implement Evalabc, Abcmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + report, Report: import reports; +include "alphabet.m"; +include "alphabet/abc.m"; + abc: Abc; + Value: import abc; + +Evalabc: module {}; +types(): string +{ + return "rAcs*"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + abc = checkload(load Abc Abc->PATH, Abc->PATH); + abc->init(); +} + +quit() +{ +} + +run(nil: chan of string, r: ref Reports->Report, + nil: list of (int, list of ref Value), + args: list of ref Value + ): ref Value +{ + a := (hd args).A().i.alphabet; + c := (hd tl args).c().i; + vl, rvl: list of ref Alphabet->Value; + for(args = tl tl args; args != nil; args = tl args) + vl = ref (Alphabet->Value).Vs((hd args).s().i) :: vl; + for(; vl != nil; vl = tl vl) + rvl = hd vl :: rvl; + vc := chan of ref Alphabet->Value; + spawn a->eval0(c, "/status", nil, r, r.start("abceval"), rvl, vc); + v := <-vc; + if(v == nil) + return nil; + return ref Value.Vr(vr(v).i); +} + +vr(v: ref Alphabet->Value): ref (Alphabet->Value).Vr +{ + pick xv := v { + Vr => + return xv; + } + return nil; +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} diff --git a/appl/alphabet/abc/import.b b/appl/alphabet/abc/import.b new file mode 100644 index 00000000..ea6c4214 --- /dev/null +++ b/appl/alphabet/abc/import.b @@ -0,0 +1,53 @@ +implement Import, Abcmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + report: import reports; +include "alphabet.m"; +include "alphabet/abc.m"; + abc: Abc; + Value: import abc; + +Import: module {}; +types(): string +{ + return "AAss*"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + abc = checkload(load Abc Abc->PATH, Abc->PATH); + abc->init(); +} + +quit() +{ +} + +run(errorc: chan of string, nil: ref Reports->Report, + nil: list of (int, list of ref Value), + args: list of ref Value + ): ref Value +{ + av := (hd args); + a := av.A().i.alphabet; + for(args = tl args; args != nil; args = tl args){ + if((e := a->importmodule((hd args).s().i)) != nil){ + report(errorc, "import: "+(hd args).s().i+": "+e); + return nil; + } + } + return av.dup(); +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} diff --git a/appl/alphabet/abc/mkfile b/appl/alphabet/abc/mkfile new file mode 100644 index 00000000..02a918e7 --- /dev/null +++ b/appl/alphabet/abc/mkfile @@ -0,0 +1,29 @@ +<../../../mkconfig + +TARG=\ + abc.dis\ + autoconvert.dis\ + autodeclare.dis\ + declare.dis\ + declares.dis\ + define.dis\ + eval.dis\ + import.dis\ + rewrite.dis\ + type.dis\ + typeset.dis\ + undeclare.dis\ + +SYSMODULES=\ + alphabet.m\ + draw.m\ + alphabet/abc.m\ + alphabet/reports.m\ + sh.m\ + string.m\ + sys.m\ + +DISBIN=$ROOT/dis/alphabet/abc + +<$ROOT/mkfiles/mkdis +LIMBOFLAGS=-F $LIMBOFLAGS diff --git a/appl/alphabet/abc/newtypeset.b b/appl/alphabet/abc/newtypeset.b new file mode 100644 index 00000000..843eb116 --- /dev/null +++ b/appl/alphabet/abc/newtypeset.b @@ -0,0 +1,147 @@ +implement Newtypeset, Abcmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + report: import reports; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "alphabet.m"; +include "alphabet/abc.m"; + abc: Abc; + Value, Vtype: import abc; + +# types abc -> types +# returns a set of types defined in terms of the types and modules in $1 +# stdtypes types -> types +# adds the standard root types to $1 +# newtype [-u] types string string cmd -> types +# adds a new type named $2 to $1; the underlying type will be $3, and the destructor $4. +# -u flag implies values of this type cannot be duplicated. +# modules types -> modules +# returns a value suitable for defining modules in terms of types defined in $1, +# containing no module definitions. +# module modules string string cmd -> modules +# newtypeset abc string modules -> abc + +# declares adds some autoconversions: +# +# autoconvert abc types "{| types} +# autoconvert types modules "{| modules} +# +# declares "{(abc) +# autodeclare 1 | +# newtypeset $1 /images { +# abc | +# autoconvert 1 | +# newtype image /fd "{} | +# newmodule read '/fd -> image' "{ +# | /filter "{canonimage} +# } | +# newmodule rotate 'image -> image' "{ +# | /filter "{rotate} +# } | +# newmodule display 'image -> /status' "{ +# | /filter "{showimage} | /create /dev/null +# } +# } | +# type /images/image | +# import /images/rotate | +# autoconvert /string /fd "{|/read} | +# autoconvert /fd image "{|/images/read} | +# autoconvert image /status "{|/images/display} +# } +# +# -{rotate x.bit} + +Newtypeset: module {}; +types(): string +{ + return "AAsm"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + abc = checkload(load Abc Abc->PATH, Abc->PATH); + abc->init(); +} + +quit() +{ +} + +run(errorc: chan of string, nil: ref Reports->Report, + nil: list of (int, list of ref Value), + args: list of ref Value + ): ref Value +{ + a := (hd args).A().i.alphabet; + d := (hd tl args).s().i; + path := "/dis/alphabet/" + d + "/alphabet" + iob := bufio->open(, Sys->OREAD); + if(iob == nil){ + report(errorc, sys->sprint("scripttypeset: cannot open %q: %r", path)); + return nil; + } + { + (types, decls) := parse(iob); + alphabet := load Alphabet Alphabet->PATH; + if(alphabet == nil){ + report(errorc, sys->sprint("scripttypeset: cannot load %q: %r", Alphabet->PATH)); + return nil; + } + declares := load Declares Declares->PATH; + if(declares == nil){ + report(errorc, sys->sprint("scripttypeset: cannot load %q: %r", Alphabet->PATH)); + return nil; + } + if((err := declares->declares(alphabet, decls, errorc)) != nil){ + report(errorc, "scripttypeset: error on declarations: "+err); + return nil; + } + declares->quit(); + declares = nil; + if(checktypes(alphabet, types, errorc) == -1) + return nil; + spawn scripttypesetproc(alphabet, types, c := chan of ref Proxy->Typescmd[ref Alphabet->Value]); + if((err := a->loadtypeset(d, c, errorc)) != nil){ + c <-= nil; + return nil; + } + return (hd args).dup(); + } exception e { + "parse:*" => + report(errorc, sys->sprint("scripttypeset: error parsing %q: %s", path, e[6:])); + return nil; + } +} + +checktypes(alphabet: Alphabet, types: list of ref Type, errorc: chan of string): int +{ + for(; types != nil; types = tl types){ + t := hd types; + if(t.destructor != nil){ + report(errorc, "destructors not supported yet"); + } + } +} + +scripttypesetproc(alphabet: Alphabet, types: list of ref Type, c: chan of Proxy->Typescmd[ref Alphabet->Value]) +{ + while((gr := <-c) != nil){ + pick r := gr { + Alphabet => + Load => + + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} diff --git a/appl/alphabet/abc/rewrite.b b/appl/alphabet/abc/rewrite.b new file mode 100644 index 00000000..a9749258 --- /dev/null +++ b/appl/alphabet/abc/rewrite.b @@ -0,0 +1,71 @@ +implement Rewrite, Abcmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + report: import reports; +include "alphabet.m"; +include "alphabet/abc.m"; + abc: Abc; + Value: import abc; + +Rewrite: module {}; +types(): string +{ + return "cAc-ss-rs"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + abc = checkload(load Abc Abc->PATH, Abc->PATH); + abc->init(); +} + +quit() +{ +} + +run(errorc: chan of string, nil: ref Reports->Report, + opts: list of (int, list of ref Value), + args: list of ref Value + ): ref Value +{ + rtype, sig: string; + for(; opts != nil; opts = tl opts){ + case (hd opts).t0 { + 's' => + sig = (hd (hd opts).t1).s().i; + 'r' => + rtype = (hd (hd opts).t1).s().i; + } + } + a := (hd args).A().i.alphabet; + c := (hd tl args).c().i; + actsig: string; + (c, actsig) = a->rewrite(c, rtype, errorc); + if(c == nil) + return nil; + if(sig != nil){ + (ok, err) := a->typecompat(sig, actsig); + if(err != nil){ + report(errorc, "rewrite: "+err); + return nil; + } + if(ok == 0){ + report(errorc, sys->sprint("rewrite: %q is not compatible with %q", sig, actsig)); + return nil; + } + } + return ref Value.Vc(c); +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} diff --git a/appl/alphabet/abc/type.b b/appl/alphabet/abc/type.b new file mode 100644 index 00000000..c36ceb61 --- /dev/null +++ b/appl/alphabet/abc/type.b @@ -0,0 +1,53 @@ +implement Type, Abcmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + report: import reports; +include "alphabet.m"; +include "alphabet/abc.m"; + abc: Abc; + Value: import abc; + +Type: module {}; +types(): string +{ + return "AAss*"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + abc = checkload(load Abc Abc->PATH, Abc->PATH); + abc->init(); +} + +quit() +{ +} + +run(errorc: chan of string, nil: ref Reports->Report, + nil: list of (int, list of ref Value), + args: list of ref Value + ): ref Value +{ + av := (hd args); + a := av.A().i.alphabet; + for(args = tl args; args != nil; args = tl args){ + if((e := a->importtype((hd args).s().i)) != nil){ + report(errorc, "type: "+(hd args).s().i+": "+e); + return nil; + } + } + return av.dup(); +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} diff --git a/appl/alphabet/abc/typeset.b b/appl/alphabet/abc/typeset.b new file mode 100644 index 00000000..4b9152ea --- /dev/null +++ b/appl/alphabet/abc/typeset.b @@ -0,0 +1,51 @@ +implement Typeset, Abcmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + report: import reports; +include "alphabet.m"; +include "alphabet/abc.m"; + abc: Abc; + Value: import abc; + +Typeset: module {}; +types(): string +{ + return "AAs"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + abc = checkload(load Abc Abc->PATH, Abc->PATH); + abc->init(); +} + +quit() +{ +} + +run(errorc: chan of string, nil: ref Reports->Report, + nil: list of (int, list of ref Value), + args: list of ref Value + ): ref Value +{ + a := (hd args).A().i.alphabet; + e := a->loadtypeset((hd tl args).s().i, nil, errorc); + if(e != nil){ + report(errorc, "typeset: "+(hd tl args).s().i+": "+e); + return nil; + } + return (hd args).dup(); +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} diff --git a/appl/alphabet/abc/undeclare.b b/appl/alphabet/abc/undeclare.b new file mode 100644 index 00000000..c4f266b8 --- /dev/null +++ b/appl/alphabet/abc/undeclare.b @@ -0,0 +1,48 @@ +implement Undeclare, Abcmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + report: import reports; +include "alphabet.m"; +include "alphabet/abc.m"; + abc: Abc; + Value: import abc; + +Undeclare: module {}; +types(): string +{ + return "AAss*"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + abc = checkload(load Abc Abc->PATH, Abc->PATH); + abc->init(); +} + +quit() +{ +} + +run(nil: chan of string, nil: ref Reports->Report, + nil: list of (int, list of ref Value), + args: list of ref Value + ): ref Value +{ + a := (hd args).A().i.alphabet; + for(al := tl args; al != nil; al = tl al) + a->undeclare((hd al).s().i); + return (hd args).dup(); +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} diff --git a/appl/alphabet/alphabet.b b/appl/alphabet/alphabet.b new file mode 100644 index 00000000..d56ee095 --- /dev/null +++ b/appl/alphabet/alphabet.b @@ -0,0 +1,1677 @@ +implement Alphabet, Copy; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "readdir.m"; +include "sh.m"; + sh: Sh; + n_BLOCK, n_SEQ, n_LIST, n_ADJ, n_WORD, n_VAR, n_BQ2, n_PIPE: import Sh; +include "sets.m"; + sets: Sets; + Set: import sets; +include "alphabet/reports.m"; + reports: Reports; + Report: import reports; + Modulecmd, Typescmd: import Proxy; +include "alphabet.m"; + evalmod: Eval; + Context: import evalmod; + +Mainsubtypes: module { + proxy: fn(): chan of ref Proxy->Typescmd[ref Alphabet->Value]; +}; + +# to do: +# - sort out concurrent access to alphabet. +# - if multiple options are given where only one is expected, +# most modules ignore some values, where they should +# discard them correctly. this could cause a malicious user +# to hang up an alphabet expression (waiting for report to end) +# - proper implementation of endpointsrv: +# - resilience to failures +# - security of endpoints +# - no need for write(0)... (or maybe there is) +# - proper implementation of rexecsrv: +# - should be aware of user + +Debug: con 0; +autodeclare := 0; + +Module: adt { + modname: string; # used when loading on demand. + typeset: ref Typeset; + sig: string; + c: chan of ref Modulecmd[ref Value]; + m: Mainmodule; + def: ref Sh->Cmd; + defmods: ref Strhash[cyclic ref Module]; + refcount: int; + + find: fn(ctxt: ref Evalctxt, s: string): (ref Module, string); + typesig: fn(m: self ref Module): string; + run: fn(m: self ref Module, ctxt: ref Evalctxt, + errorc: chan of string, + opts: list of (int, list of ref Value), + args: list of ref Value): ref Value; + typename2c: fn(s: string): int; + mks: fn(ctxt: ref Evalctxt, s: string): ref Value; + mkc: fn(ctxt: ref Evalctxt, c: ref Sh->Cmd): ref Value; + ensureloaded: fn(m: self ref Module): string; + cvt: fn(ctxt: ref Evalctxt, v: ref Value, tc: int, errorc: chan of string): ref Value; +}; + +Evalctxt: adt { + modules: ref Strhash[ref Module]; + drawctxt: ref Draw->Context; + report: ref Report; +# stopc: chan of int; +}; + +# used for rewriting expressions. +Rvalue: adt { + i: ref Sh->Cmd; + tc: int; + refcount: int; + opts: list of (int, list of ref Rvalue); + args: list of ref Rvalue; + + dup: fn(t: self ref Rvalue): ref Rvalue; + free: fn(v: self ref Rvalue, used: int); + isstring: fn(v: self ref Rvalue): int; + gets: fn(t: self ref Rvalue): string; + type2s: fn(tc: int): string; + typec: fn(t: self ref Rvalue): int; +}; + +Rmodule: adt { + m: ref Module; + + cvt: fn(ctxt: ref Revalctxt, v: ref Rvalue, tc: int, errorc: chan of string): ref Rvalue; + find: fn(nil: ref Revalctxt, s: string): (ref Rmodule, string); + typesig: fn(m: self ref Rmodule): string; + run: fn(m: self ref Rmodule, ctxt: ref Revalctxt, errorc: chan of string, + opts: list of (int, list of ref Rvalue), args: list of ref Rvalue): ref Rvalue; + mks: fn(ctxt: ref Revalctxt, s: string): ref Rvalue; + mkc: fn(ctxt: ref Revalctxt, c: ref Sh->Cmd): ref Rvalue; + typename2c: fn(s: string): int; +}; + +Revalctxt: adt { + modules: ref Strhash[ref Module]; + used: ref Strhash[ref Module]; + defs: int; + vals: list of ref Rvalue; +}; + +Renv: adt { + items: list of ref Rvalue; + n: int; +}; + +Typeset: adt { + name: string; + c: chan of ref Typescmd[ref Value]; + types: ref Table[cyclic ref Type]; # indexed by external type character + parent: ref Typeset; + + gettype: fn(ts: self ref Typeset, tc: int): ref Type; +}; + +Type: adt { + id: int; + tc: int; + transform: list of ref Transform; + typeset: ref Typeset; + qname: string; + name: string; +}; + +Transform: adt { + dst: int; # which type we're transforming into. + all: Set; # set of all types this transformation can lead to. + expr: ref Sh->Cmd; # transformation operation. +}; + +Table: adt[T] { + items: array of list of (int, T); + nilval: T; + + new: fn(nslots: int, nilval: T): ref Table[T]; + add: fn(t: self ref Table, id: int, x: T): int; + del: fn(t: self ref Table, id: int): int; + find: fn(t: self ref Table, id: int): T; +}; + +Strhash: adt[T] { + items: array of list of (string, T); + nilval: T; + + new: fn(nslots: int, nilval: T): ref Strhash[T]; + add: fn(t: self ref Strhash, id: string, x: T); + del: fn(t: self ref Strhash, id: string); + find: fn(t: self ref Strhash, id: string): T; +}; + +Copy: module { + initcopy: fn( + typesets: list of ref Typeset, + roottypeset: ref Typeset, + modules: ref Strhash[ref Module], + typebyname: ref Strhash[ref Type], + typebyc: ref Table[ref Type], + types: array of ref Type, + currtypec: int + ): Alphabet; +}; + +typesets: list of ref Typeset; +roottypeset: ref Typeset; +modules: ref Strhash[ref Module]; +typebyname: ref Strhash[ref Type]; +typebyc: ref Table[ref Type]; # indexed by internal type character. +types: array of ref Type; # indexed by id. +currtypec := 16r25a0; # pretty graphics. + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + sys->fprint(sys->fildes(2), "alphabet: cannot load %s: %r\n", path); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + sh = load Sh Sh->PATH; + sets = checkload(load Sets Sets->PATH, Sets->PATH); + evalmod = checkload(load Eval Eval->PATH, Eval->PATH); + evalmod->init(); + reports = checkload(load Reports Reports->PATH, Reports->PATH); + + roottypeset = ref Typeset("/", nil, Table[ref Type].new(5, nil), nil); + typesets = roottypeset :: typesets; + types = array[] of { + ref Type(-1, 'c', nil, roottypeset, "/cmd", "cmd"), + ref Type(-1, 's', nil, roottypeset, "/string", "string"), + ref Type(-1, 'r', nil, roottypeset, "/status", "status"), + ref Type(-1, 'f', nil, roottypeset, "/fd", "fd"), + ref Type(-1, 'w', nil, roottypeset, "/wfd", "wfd"), + ref Type(-1, 'd', nil, roottypeset, "/data", "data"), + }; + typebyname = typebyname.new(11, nil); + typebyc = typebyc.new(11, nil); + for(i := 0; i < len types; i++){ + types[i].id = i; + typebyc.add(types[i].tc, types[i]); + typebyname.add(types[i].qname, types[i]); + roottypeset.types.add(types[i].tc, types[i]); + } +# typebyc.add('a', ref Type(-1, 'a', nil, nil, "/any", "any")); # not sure about this anymore + modules = modules.new(3, nil); +} + +initcopy( + xtypesets: list of ref Typeset, + xroottypeset: ref Typeset, + xmodules: ref Strhash[ref Module], + xtypebyname: ref Strhash[ref Type], + xtypebyc: ref Table[ref Type], + xtypes: array of ref Type, + xcurrtypec: int): Alphabet +{ + # XXX must do copy-on-write, and refcounting on typesets. + typesets = xtypesets; + roottypeset = xroottypeset; + modules = xmodules; + typebyname = xtypebyname; + typebyc = xtypebyc; + types = xtypes; + currtypec = xcurrtypec; + return load Alphabet "$self"; +} + +copy(): Alphabet +{ + a := load Copy Alphabet->PATH; + if(a == nil) + return nil; + return a->initcopy(typesets, roottypeset, modules, typebyname, typebyc, types, currtypec); +} + +setautodeclare(x: int) +{ + autodeclare = x; +} + +quit() +{ + for(ts := typesets; ts != nil; ts = tl ts) + if((hd ts).c != nil) + (hd ts).c <-= nil; + delmods(modules); +} + +delmods(mods: ref Strhash[ref Module]) +{ + for(i := 0; i < len mods.items; i++){ + for(l := mods.items[i]; l != nil; l = tl l){ + m := (hd l).t1; + if(--m.refcount == 0){ + if(m.c != nil){ + m.c <-= nil; + m.c = nil; + }else if(m.defmods != nil) + delmods(m.defmods); + else if(m.m != nil){ + m.m->quit(); + m.m = nil; + } + } + } + } +} + +# XXX could do some more checking to see whether it looks vaguely like +# a valid alphabet expression. +parse(expr: string): (ref Sh->Cmd, string) +{ + return sh->parse(expr); +} + +eval(expr: ref Sh->Cmd, + drawctxt: ref Draw->Context, + args: list of ref Value): string +{ + spawn reports->reportproc(reportc := chan of string, nil, reply := chan of ref Report); + r := <-reply; + reply = nil; + stderr := sys->fildes(2); + spawn eval0(expr, "/status", drawctxt, r, reports->r.start("eval"), args, vc := chan of ref Value); + reports->r.enable(); + v: ref Value; +wait: + for(;;)alt{ + v = <-vc => + if(v != nil) + v.r().i <-= nil; + msg := <-reportc => + if(msg == nil) + break wait; + sys->fprint(stderr, "alphabet: %s\n", msg); + } + # we'll always get the value before the report ends. + if(v == nil) + return "no value"; + return <-v.r().i; +} + +eval0(expr: ref Sh->Cmd, + dsttype: string, + drawctxt: ref Draw->Context, + r: ref Report, + errorc: chan of string, + args: list of ref Value, + vc: chan of ref Value) +{ + c: Eval->Context[ref Value, ref Module, ref Evalctxt]; + ctxt := ref Evalctxt(modules, drawctxt, r); + tc := -1; + if(dsttype != nil && (tc = Module.typename2c(dsttype)) == -1){ + report(errorc, "error: unknown type "+dsttype); + vc <-= nil; + reports->quit(errorc); + } + + v := c.eval(expr, ctxt, errorc, args); + if(tc != -1) + v = Module.cvt(ctxt, v, tc, errorc); + vc <-= v; + reports->quit(errorc); +} + +define(name: string, expr: ref Sh->Cmd, errorc: chan of string): string +{ + if(name == nil || name[0] == '/') + return "bad module name"; + m := modules.find(name); + if(m != nil) + return "module already declared"; + sig: string; + used: ref Strhash[ref Module]; + used = used.new(11, nil); + (expr, sig) = rewrite0(expr, -1, errorc, used); + if(sig == nil) + return "cannot rewrite"; + modules.add(name, ref Module(name, roottypeset, sig, nil, nil, expr, used, 1)); + return nil; +} + +typecompat(t0, t1: string): (int, string) +{ + m: ref Module; + (sig0, err) := evalmod->usage2sig(m, t0); + if(err != nil) + return (0, sys->sprint("bad usage %q: %s", t0, err)); + sig1: string; + (sig1, err) = evalmod->usage2sig(m, t1); + if(err != nil) + return (0, sys->sprint("bad usage %q: %s", t1, err)); + return (evalmod->typecompat(sig0, sig1), nil); +} + +rewrite(expr: ref Sh->Cmd, dsttype: string, errorc: chan of string): (ref Sh->Cmd, string) +{ + v: ref Value; + tc := -1; + if(dsttype != nil){ + tc = Module.typename2c(dsttype); + if(tc == -1){ + report(errorc, "error: unknown type "+dsttype); + return (nil, nil); + } + } + sig: string; + (expr, sig) = rewrite0(expr, tc, errorc, nil); + if(sig == nil) + return (nil, nil); + + return (expr, evalmod->cmdusage(v, sig)); +} + +# XXX different kinds of rewrite: +# could rewrite forcing all names to qualified +# or just leave names as they are. + +# return (expr, sig). +# add all modules used by the expression to mods if non-nil. +rewrite0(expr: ref Sh->Cmd, tc: int, errorc: chan of string, used: ref Strhash[ref Module]): (ref Sh->Cmd, string) +{ + m: ref Rmodule; + ctxt := ref Revalctxt(modules, used, 1, nil); + (sig, err) := evalmod->blocksig(m, ctxt, expr); + if(sig == nil){ + report(errorc, "error: cannot get expr type: "+err); + return (nil, nil); + } + args: list of ref Rvalue; + for(i := len sig - 1; i >= 1; i--) + args = ref Rvalue(mk(-1, nil, nil), sig[i], 1, nil, nil) :: args; # N.Vb. cmd node is never used. + + c: Eval->Context[ref Rvalue, ref Rmodule, ref Revalctxt]; + v := c.eval(expr, ctxt, errorc, args); + if(v != nil && tc != -1) + v = Rmodule.cvt(ctxt, v, tc, errorc); + if(v == nil) + return (nil, nil); + sig[0] = v.tc; + v.refcount++; + expr = gen(v, ref Renv(nil, 0)); + if(len sig > 1){ + t := mkw(Value.type2s(sig[1])); + for(i = 2; i < len sig; i++) + t = mk(n_ADJ, t, mkw(Value.type2s(sig[i]))); + expr = mk(n_BLOCK, mk(n_SEQ, mk(n_LIST, t, nil), expr.left), nil); + } + return (expr, sig); +} + +# generate the expression that gave rise to v. +# it puts in parentenv any values referred to externally. +gen(v: ref Rvalue, parentenv: ref Renv): ref Sh->Cmd +{ + v.refcount--; + if(v.refcount > 0) + return mk(n_VAR, mkw(string addenv(parentenv, v)), nil); + c := v.i; + (opts, args) := (v.opts, v.args); + if(opts == nil && args == nil) + return c; + env := parentenv; + if(genblock := needblock(v)) + env = ref Renv(nil, 0); + for(; opts != nil; opts = tl opts){ + c = mk(n_ADJ, c, mkw(sys->sprint("-%c", (hd opts).t0))); + for(a := (hd opts).t1; a != nil; a = tl a) + c = mk(n_ADJ, c, gen(hd a, env)); + } + if(args != nil && len (hd args).i.word > 1 && (hd args).i.word[0] == '-') + c = mk(n_ADJ, c, mkw("--")); # XXX potentially dodgy; some sigs don't interpret "--"? + + # use pipe notation when possible + arg0: ref Sh->Cmd; + if(args != nil){ + if((arg0 = gen(hd args, env)).ntype != n_BLOCK){ + c = mk(n_ADJ, c, arg0); + arg0 = nil; + } + args = tl args; + } + for(; args != nil; args = tl args) + c = mk(n_ADJ, c, gen(hd args, env)); + if(arg0 != nil) + c = mk(n_PIPE, arg0.left, c); + if(genblock){ + args = rev(env.items); + m := mkw(Value.type2s((hd args).tc)); + for(a := tl args; a != nil; a = tl a) + m = mk(n_ADJ, m, mkw(Value.type2s((hd a).tc))); + c = mk(n_BLOCK, mk(n_SEQ, mk(n_LIST, m, nil), c), nil); + return gen(ref Rvalue(c, v.tc, 1, nil, args), parentenv); + } + return mk(n_BLOCK, c, nil); +} + +addenv(env: ref Renv, v: ref Rvalue): int +{ + for(i := env.items; i != nil; i = tl i) + if(hd i == v) + return len i; + env.items = v :: env.items; + v.refcount++; + return ++env.n; +} + +# need a new block if we have any duplicated values we can resolve locally. +# i.e. for a particular value, if we're the only thing pointing to that value +# and its refcount is > 1 to start with. +needblock(v: ref Rvalue): int +{ + dups := getdups(v, nil); + for(d := dups; d != nil; d = tl d) + --(hd d).refcount; + r := 0; + for(d = dups; d != nil; d = tl d) + if((hd d).refcount++ == 0) + r = 1; + return r; +} + +# find all values which need $ referencing (but don't go any deeper) +getdups(v: ref Rvalue, onto: list of ref Rvalue): list of ref Rvalue +{ + if(v.refcount > 1) + return v :: onto; + for(o := v.opts; o != nil; o = tl o) + for(a := (hd o).t1; a != nil; a = tl a) + onto = getdups(hd a, onto); + for(a = v.args; a != nil; a = tl a) + onto = getdups(hd a, onto); + return onto; +} + +loadtypeset(qname: string, c: chan of ref Typescmd[ref Value], errorc: chan of string): string +{ + tsname := canon(qname); + if(gettypeset(tsname) != nil) + return nil; + (parent, name) := splitqname(tsname); + if((pts := gettypeset(parent)) == nil) + return "parent typeset not found"; + + if(pts.c != nil){ + if(c != nil) + return "typecmd channel may only be provided for top-level typesets"; + reply := chan of (chan of ref Typescmd[ref Value], string); + pts.c <-= ref Typescmd[ref Value].Loadtypes(name, reply); + err: string; + (c, err) = <-reply; + if(c == nil) + return err; + }else if(c == nil){ + tsmod := load Mainsubtypes "/dis/alphabet/"+name+"types.dis"; + if(tsmod == nil) + return sys->sprint("cannot load %q: %r", name+"types.dis"); + c = tsmod->proxy(); + } + + reply := chan of string; + c <-= ref Typescmd[ref Value].Alphabet(reply); + a := <-reply; + ts := ref Typeset(tsname, c, Table[ref Type].new(7, nil), pts); + typesets = ts :: typesets; + newtypes: list of ref Type; + for(i := 0; i < len a; i++){ + tc := a[i]; + if((t := ts.parent.gettype(tc)) == nil){ + t = ref Type(-1, -1, nil, ts, nil, nil); + sreply := chan of string; + c <-= ref Typescmd[ref Value].Type2s(tc, sreply); + t.name = <-sreply; + # XXX check that type name is syntactically valid. + t.qname = mkqname(tsname, t.name); + if(typebyname.find(t.qname) != nil) + report(errorc, sys->sprint("warning: oops: typename clash on %q", t.qname)); + else + typebyname.add(t.qname, t); + newtypes = t :: newtypes; + } + ts.types.add(tc, t); + } + id := len types; + types = (array[len types + len newtypes] of ref Type)[0:] = types; + for(; newtypes != nil; newtypes = tl newtypes){ + types[id] = hd newtypes; + typebyc.add(currtypec, hd newtypes); + types[id].tc = currtypec++; + types[id].id = id; + id++; + } + return nil; +} + +autoconvert(src, dst: string, expr: ref Sh->Cmd, errorc: chan of string): string +{ + tdst := typebyname.find(dst); + if(tdst == nil) + return "unknown type " + dst; + tsrc := typebyname.find(src); + if(tsrc == nil) + return "unknown type " + src; + if(tdst.typeset != tsrc.typeset && tdst.typeset != roottypeset && tsrc.typeset != roottypeset) + return "conversion between incompatible typesets"; + if(expr != nil && expr.ntype == n_WORD){ + # mod -> {(srctype); mod $1} + expr = mk(n_BLOCK, + mk(n_SEQ, + mk(n_LIST, mkw(src), nil), + mk(n_ADJ, + mkw(expr.word), + mk(n_VAR, mkw("1"), nil) + ) + ), + nil + ); + } + + (e, sig) := rewrite0(expr, tdst.tc, errorc, nil); + if(sig == nil) + return "cannot rewrite transformation "+sh->cmd2string(expr); + if(!evalmod->typecompat(sys->sprint("%c%c", tdst.tc, tsrc.tc), sig)) + return "incompatible module type"; + err := addconversion(tsrc, tdst, e); + if(err != nil) + return sys->sprint("bad auto-conversion %s->%s via %s: %s", + tsrc.qname, tdst.qname, sh->cmd2string(expr), err); + return nil; +} + +mk(ntype: int, left, right: ref Sh->Cmd): ref Sh->Cmd +{ + return ref Sh->Cmd(ntype, left, right, nil, nil); +} +mkw(w: string): ref Sh->Cmd +{ + return ref Sh->Cmd(n_WORD, nil, nil, w, nil); +} + +declare(qname: string, usig: string, flags: int): string +{ + return declare0(qname, usig, flags).t1; +} + +# declare a module. +# if (flags&ONDEMAND), then we don't need to actually load +# the module (although we do if (flags&CHECK) or if sig==nil, +# in order to check or find out the type signature) +declare0(qname: string, usig: string, flags: int): (ref Module, string) +{ + sig, err: string; + m: ref Module; + if(usig != nil){ + (sig, err) = evalmod->usage2sig(m, usig); + if(sig == nil) + return (nil, "bad type sig: " + err); + } + # if not a qualified name, declare it virtually + if(qname != nil && qname[0] != '/'){ + if(sig == nil) + return (nil, "virtual module declaration must include signature"); + m = ref Module(qname, nil, sig, nil, nil, nil, nil, 0); + }else{ + qname = canon(qname); + (typeset, mod) := splitqname(qname); + if((ts := gettypeset(typeset)) == nil) + return (nil, "unknown typeset"); + if((m = modules.find(qname)) != nil){ + if(m.typeset == ts) + return (m, nil); + return (nil, "already imported"); + } + m = ref Module(mod, ts, sig, nil, nil, nil, nil, 0); + if(sig == nil || (flags&CHECK) || (flags&ONDEMAND)==0){ + if((e := m.ensureloaded()) != nil) + return (nil, e); + if(flags&ONDEMAND){ + if(m.c != nil){ + m.c <-= nil; + m.c = nil; + } + m.m = nil; + } + } + } + + modules.add(qname, m); + m.refcount++; + return (m, nil); +} + +undeclare(name: string): string +{ + m := modules.find(name); + if(m == nil) + return "module not declared"; + modules.del(name); + if(--m.refcount == 0){ + if(m.c != nil){ + m.c <-= nil; + m.c = nil; + }else if(m.defmods != nil){ + delmods(m.defmods); + } + } + return nil; +} + +# get info on a module. +# return (qname, usage, def) +getmodule(name: string): (string, string, ref Sh->Cmd) +{ + (qname, sig, def) := getmodule0(name); + if(sig == nil) + return (qname, sig, def); + v: ref Value; + return (qname, evalmod->cmdusage(v, sig), def); +} + +getmodule0(name: string): (string, string, ref Sh->Cmd) +{ + m: ref Module; + if(name != nil && name[0] != '/'){ + if((m = modules.find(name)) == nil) + return (nil, nil, nil); + # XXX could add path searching here. + }else{ + name = canon(name); + (typeset, mod) := splitqname(name); + if((m = modules.find(name)) == nil){ + if(autodeclare == 0) + return (nil, nil, nil); + ts := gettypeset(typeset); + if(ts == nil) + return (nil, nil, nil); + m = ref Module(mod, ts, nil, nil, nil, nil, nil, 0); + if((e := m.ensureloaded()) != nil) + return (nil, nil, nil); + if(m.c != nil) + m.c <-= nil; + } + } + + qname := m.modname; + if(m.def == nil && m.typeset != nil) + qname = mkqname(m.typeset.name, qname); + return (qname, m.sig, m.def); +} + +getmodules(): list of string +{ + r: list of string; + for(i := 0; i < len modules.items; i++) + for(ml := modules.items[i]; ml != nil; ml = tl ml) + r = (hd ml).t0 :: r; + return r; +} + +#Cmpdeclts: adt { +# gt: fn(nil: self ref Cmpdeclts, d1, d2: ref Decltypeset): int +#}; +#Cmpdeclts.gt(nil: self ref Cmpdeclts, d1, d2: ref Decltypeset) +#{ +# return d1.name > d2.name; +#} +#Cmpstring: adt { +# gt: fn(nil: self ref Cmpdeclts, d1, d2: string): int +#}; +#Cmpstring.gt(nil: self ref Cmpstring, d1, d2: string): int +#{ +# return d1 > d2; +#} +#Cmptype: adt { +# gt: fn(nil: self ref Cmptype, d1, d2: ref Type): int +#}; +#Cmptype.gt(nil: self ref Cmptype, d1, d2: ref Type): int +#{ +# return d1.name > d2.name; +#} +# +#getdecls(): ref Declarations +#{ +# cmptype: ref Cmptype; +# d := ref Declarations(array[len typesets] of ref Decltypeset); +# i := 0; +# ta := array[len types] of ref Type; +# for(tsl := typesets; tsl != nil; tsl = tl tsl){ +# t := hd tsl; +# ts := ref Decltypeset; +# ts.name = t.name; +# +# # all types in the typeset, in alphabetical order. +# j := 0; +# for(k := 0; k < len t.types.items; k++) +# for(tt := t.types.items[k]; tt != nil; tt = tl tt) +# ta[j++] = hd tt; +# sort(cmptype, ta[0:j]); +# ts.types = array[j] of string; +# for(k = 0; k < j; k++){ +# ts.types[k] = ta[k].name; +# ts.alphabet[k] = ta[k].tc; +# } +# +# # all modules in the typeset +# c := gettypesetmodules(ts.name); +# while((m := <-c) != nil){ +# +# +# d.types = array[len types] of string; +# for(i := 0; i < len types; i++){ +# d.alphabet[i] = types[i].tc; +# d.types[i] = types[i].qname; +# } +# + +gettypesetmodules(tsname: string): chan of string +{ + ts := gettypeset(tsname); + if(ts == nil) + return nil; + r := chan of string; + if(ts.c == nil) + spawn mainmodules(r); + else + ts.c <-= ref Typescmd[ref Value].Modules(r); + return r; +} + +mainmodules(r: chan of string) +{ + if((readdir := load Readdir Readdir->PATH) != nil){ + (a, nil) := readdir->init("/dis/alphabet/main", Readdir->NAME|Readdir->COMPACT); + for(i := 0; i < len a; i++){ + m := a[i].name; + if((a[i].mode & Sys->DMDIR) == 0 && len m > 4 && m[len m - 4:] == ".dis") + r <-= m[0:len m - 4]; + } + } + r <-= nil; +} + +gettypes(ts: string): list of string +{ + r: list of string; + for(i := 0; i < len types; i++){ + if(ts == nil) + r = Value.type2s(types[i].tc) :: r; + else if (types[i].typeset.name == ts) + r = types[i].name :: r; + } + return r; +} + +gettypesets(): list of string +{ + r: list of string; + for(t := typesets; t != nil; t = tl t) + r = (hd t).name :: r; + return r; +} + +getautoconversions(): list of (string, string, ref Sh->Cmd) +{ + cl: list of (string, string, ref Sh->Cmd); + for(i := 0; i < len types; i++){ + if(types[i] == nil) + continue; + srct := Value.type2s(types[i].tc); + for(l := types[i].transform; l != nil; l = tl l) + cl = (srct, Value.type2s(types[(hd l).dst].tc), (hd l).expr) :: cl; + } + return cl; +} + +importmodule(qname: string): string +{ + qname = canon(qname); + (typeset, mod) := splitqname(qname); + if(typeset == nil) + return "unknown typeset"; + if((m := modules.find(mod)) != nil){ + if(m.typeset == nil) + return "already defined"; + if(m.typeset.name == typeset) + return nil; + return "already imported from "+m.typeset.name; + } + if((m = modules.find(qname)) == nil){ + if(autodeclare == 0) + return "module not declared"; + err: string; + (m, err) = Module.find(nil, qname); + if(m == nil) + return "cannot import: "+ err; + modules.add(qname, m); + m.refcount++; + } + modules.add(mod, m); + return nil; +} + + +gettypeset(name: string): ref Typeset +{ + name = canon(name); + for(l := typesets; l != nil; l = tl l) + if((hd l).name == name) + break; + if(l == nil) + return nil; + return hd l; +} + +importtype(qname: string): string +{ + qname = canon(qname); + (typeset, tname) := splitqname(qname); + if((ts := gettypeset(typeset)) == nil) + return "unknown typeset"; + t := typebyname.find(tname); + if(t != nil){ + if(t.typeset == ts) + return nil; + return "type already imported from " + t.typeset.name; + } + t = typebyname.find(qname); + if(t == nil) + return sys->sprint("%s does not hold type %s", typeset, tname); + typebyname.add(tname, t); + return nil; +} + +importvalue(v: ref Value, tname: string): (ref Value, string) +{ + if(v == nil || tagof v != tagof Value.Vz) + return (v, nil); + if(tname == nil || tname[0] == '/') + tname = canon(tname); + t := typebyname.find(tname); + if(t == nil) + return (nil, "no such type"); + pick xv := v { + Vz => + if(t.typeset.types.find(xv.i.typec) != t) + return (nil, "value appears to be of different type"); + xv.i.typec = t.tc; + } + return (v, nil); +} + +gettype(tc: int): ref Type +{ + return typebyc.find(tc); +} + +Typeset.gettype(ts: self ref Typeset, tc: int): ref Type +{ + return ts.types.find(tc); +} + +Module.find(ctxt: ref Evalctxt, name: string): (ref Module, string) +{ + mods := modules; + if(ctxt != nil) + mods = ctxt.modules; + m := mods.find(name); + if(m == nil){ + if(autodeclare == 0 || name == nil || name[0] != '/') + return (nil, "module not declared"); + err: string; + (m, err) = declare0(name, nil, 0); + if(m == nil) + return (nil, err); + }else if((err := m.ensureloaded()) != nil) + return (nil, err); + return (m, nil); +} + +Module.ensureloaded(m: self ref Module): string +{ + if(m.c != nil || m.m != nil || m.def != nil || m.typeset == nil) + return nil; + + sig: string; + if(m.typeset.c == nil){ + p := "/dis/alphabet/main/" + m.modname + ".dis"; + mod := load Mainmodule p; + if(mod == nil) + return sys->sprint("cannot load %q: %r", p); + { + mod->init(); + } exception e { + "fail:*" => + return sys->sprint("init %q failed: %s", m.modname, e[5:]); + } + m.m = mod; + sig = mod->typesig(); + }else{ + reply := chan of (chan of ref Modulecmd[ref Value], string); + m.typeset.c <-= ref Typescmd[ref Value].Load(m.modname, reply); + (mc, err) := <-reply; + if(mc == nil) + return sys->sprint("cannot load: %s", err); + m.c = mc; + sig = gettypesig(m); + } + if(m.sig == nil) + m.sig = sig; + else if(!evalmod->typecompat(m.sig, sig)){ + v: ref Value; + if(m.c != nil){ + m.c <-= nil; + m.c = nil; + } + m.m = nil; + return sys->sprint("%q not compatible with %q (%q vs %q, %d)", + m.modname+" "+evalmod->cmdusage(v, sig), + evalmod->cmdusage(v, m.sig), m.sig, sig, m.sig==sig); + } + return nil; +} + +Module.typesig(m: self ref Module): string +{ + return m.sig; +} + +# get the type signature of a module in its native typeset. +# it's not valid to call this on defined or virtually declared modules. +gettypesig(m: ref Module): string +{ + reply := chan of string; + m.c <-= ref Modulecmd[ref Value].Typesig(reply); + sig := <-reply; + origsig := sig; + for(i := 0; i < len sig; i++){ + tc := sig[i]; + if(tc == '-'){ + i++; + continue; + } + if(tc != '*'){ + t := m.typeset.gettype(sig[i]); + if(t == nil){ +sys->print("no type found for '%c' in sig %q\n", sig[i], origsig); + return nil; # XXX is it alright to break here? + } + sig[i] = t.tc; + } + } + return sig; +} + +Module.run(m: self ref Module, ctxt: ref Evalctxt, errorc: chan of string, opts: list of (int, list of ref Value), args: list of ref Value): ref Value +{ + if(m.c != nil){ + reply := chan of ref Value; + m.c <-= ref Modulecmd[ref Value].Run(ctxt.drawctxt, ctxt.report, errorc, opts, args, reply); + if((v := <-reply) != nil){ + pick xv := v { + Vz => + xv.i.typec = m.typeset.types.find(xv.i.typec).tc; + } + } + return v; + }else if(m.def != nil){ + c: Eval->Context[ref Value, ref Module, ref Evalctxt]; + return c.eval(m.def, ref Evalctxt(m.defmods, ctxt.drawctxt, ctxt.report), errorc, args); + }else if(m.typeset != nil){ + v := m.m->run(ctxt.drawctxt, ctxt.report, errorc, opts, args); + free(opts, args, v != nil); + return v; + } + report(errorc, "error: cannot run a virtually declared module"); + return nil; +} + +free[V](opts: list of (int, list of V), args: list of V, used: int) + for{ + V => + free: fn(v: self V, used: int); + } +{ + for(; args != nil; args = tl args) + (hd args).free(used); + for(; opts != nil; opts = tl opts) + for(args = (hd opts).t1; args != nil; args = tl args) + (hd args).free(used); +} + +Module.typename2c(s: string): int +{ + if((t := typebyname.find(s)) == nil) + return -1; + return t.tc; +} + +Module.cvt(ctxt: ref Evalctxt, v: ref Value, tc: int, errorc: chan of string): ref Value +{ + if(v == nil) + return nil; + srctc := v.typec(); + dstid := gettype(tc).id; + while((vtc := v.typec()) != tc){ + # XXX assumes v always returns a valid typec: might that be dangerous? + for(l := gettype(vtc).transform; l != nil; l = tl l) + if((hd l).all.holds(dstid)) + break; + if(l == nil){ + report(errorc, sys->sprint("error: no way to get from %s to %s", gettype(v.typec()).qname, + types[dstid].qname)); + v.free(0); + return nil; # should only happen the first time. + } + t := hd l; + c: Eval->Context[ref Value, ref Module, ref Evalctxt]; + nv := c.eval(t.expr, ctxt, errorc, v::nil); + if(nv == nil){ + report(errorc, sys->sprint("error: autoconvert %q failed", sh->cmd2string(t.expr))); + return nil; + } + v = nv; + } + return v; +} + +Module.mks(nil: ref Evalctxt, s: string): ref Value +{ + return ref Value.Vs(s); +} + +Module.mkc(nil: ref Evalctxt, c: ref Sh->Cmd): ref Value +{ + return ref Value.Vc(c); +} + +show() +{ + for(i := 0; i < len types; i++){ + if(types[i] == nil) + continue; + sys->print("%s =>\n", types[i].qname); + for(l := types[i].transform; l != nil; l = tl l) + sys->print("\t%s -> %s {%s}\n", set2s((hd l).all), types[(hd l).dst].qname, sh->cmd2string((hd l).expr)); + } +} + +set2s(set: Set): string +{ + s := "{"; + for(i := 0; i < len types; i++){ + if(set.holds(i)){ + if(len s > 1) + s[len s] = ' '; + s += types[i].qname; + } + } + return s + "}"; +} + +Value.dup(v: self ref Value): ref Value +{ + if(v == nil) + return nil; + pick xv := v { + Vr => + return nil; + Vd => + return nil; + Vf or + Vw => + return nil; + Vz => + rc := chan of ref Value; + gettype(xv.i.typec).typeset.c <-= ref Typescmd[ref Value].Dup(xv, rc); + nv := <-rc; + if(nv == nil) + return nil; + if(nv == v) + return v; + pick nxv := nv { + Vz => + if(nxv.i.typec == xv.i.typec) + return nxv; + } + sys->print("oh dear, invalid duplicated value from typeset %s\n", gettype(xv.i.typec).typeset.name); + return nil; + } + return v; +} + +Value.typec(v: self ref Value): int +{ + pick xv := v { + Vc => + return 'c'; + Vs => + return 's'; + Vr => + return 'r'; + Vf => + return 'f'; + Vw => + return 'w'; + Vd => + return 'd'; + Vz => + return xv.i.typec; + } +} + +Value.typename(v: self ref Value): string +{ + return Value.type2s(v.typec()); +} + +Value.free(v: self ref Value, used: int) +{ + if(v == nil) + return; + pick xv := v { + Vr => + if(!used) + xv.i <-= "stop"; + Vf or + Vw=> + if(!used){ + <-xv.i; + xv.i <-= nil; + } + Vd => + if(!used){ + alt{ + xv.i.stop <-= 1 => + ; + * => + ; + } + } + Vz => + gettype(xv.i.typec).typeset.c <-= ref Typescmd[ref Value].Free(xv, used, reply := chan of int); + <-reply; + } +} + +Value.isstring(v: self ref Value): int +{ + return tagof v == tagof Value.Vs; +} +Value.gets(v: self ref Value): string +{ + return v.s().i; +} +Value.c(v: self ref Value): ref Value.Vc +{ + pick xv :=v {Vc => return xv;} + raise "type error"; +} +Value.s(v: self ref Value): ref Value.Vs +{ + pick xv :=v {Vs => return xv;} + raise "type error"; +} +Value.r(v: self ref Value): ref Value.Vr +{ + pick xv :=v {Vr => return xv;} + raise "type error"; +} +Value.f(v: self ref Value): ref Value.Vf +{ + pick xv :=v {Vf => return xv;} + raise "type error"; +} +Value.w(v: self ref Value): ref Value.Vw +{ + pick xv :=v {Vw => return xv;} + raise "type error"; +} +Value.d(v: self ref Value): ref Value.Vd +{ + pick xv :=v {Vd => return xv;} + raise "type error"; +} +Value.z(v: self ref Value): ref Value.Vz +{ + pick xv :=v {Vz => return xv;} + raise "type error"; +} + +Value.type2s(tc: int): string +{ + t := gettype(tc); + if(t == nil) + return "unknown"; + if(typebyname.find(t.name) == t) + return t.name; + return t.qname; +} + +Rmodule.find(ctxt: ref Revalctxt, s: string): (ref Rmodule, string) +{ + m := ctxt.modules.find(s); + if(m == nil){ + if(autodeclare == 0 || s == nil || s[0] != '/') + return (nil, "module not declared"); + if(ctxt.modules != modules) + return (nil, "shouldn't happen: module not found in defined block"); + err: string; + (m, err) = declare0(s, nil, ONDEMAND); + if(m == nil) + return (nil, err); + } + return (ref Rmodule(m), nil); +} + +Rmodule.cvt(ctxt: ref Revalctxt, v: ref Rvalue, tc: int, errorc: chan of string): ref Rvalue +{ + if(v == nil) + return nil; + srctc := v.typec(); + dstid := gettype(tc).id; + while((vtc := v.typec()) != tc){ + # XXX assumes v always returns a valid typec: might that be dangerous? + for(l := gettype(vtc).transform; l != nil; l = tl l) + if((hd l).all.holds(dstid)) + break; + if(l == nil){ + report(errorc, sys->sprint("error: no way to get from %s to %s", gettype(v.typec()).qname, + types[dstid].qname)); + return nil; # should only happen the first time. + } + t := hd l; + c: Eval->Context[ref Rvalue, ref Rmodule, ref Revalctxt]; + v = c.eval(t.expr, ctxt, errorc, v::nil); + } + return v; +} + +Rmodule.typesig(m: self ref Rmodule): string +{ + return m.m.sig; +} + +Rmodule.typename2c(name: string): int +{ + return Module.typename2c(name); +} + +Rmodule.mks(ctxt: ref Revalctxt, s: string): ref Rvalue +{ + v := ref Rvalue(mkw(s), 's', 0, nil, nil); + ctxt.vals = v :: ctxt.vals; + return v; +} + +Rmodule.mkc(ctxt: ref Revalctxt, c: ref Sh->Cmd): ref Rvalue +{ + v := ref Rvalue(mk(n_BQ2, c, nil), 'c', 0, nil, nil); + ctxt.vals = v :: ctxt.vals; + return v; +} + +Rmodule.run(m: self ref Rmodule, ctxt: ref Revalctxt, errorc: chan of string, + opts: list of (int, list of ref Rvalue), args: list of ref Rvalue): ref Rvalue +{ + if(ctxt.defs && m.m.def != nil){ + c: Eval->Context[ref Rvalue, ref Rmodule, ref Revalctxt]; + nctxt := ref Revalctxt(m.m.defmods, ctxt.used, ctxt.defs, ctxt.vals); + v := c.eval(m.m.def, nctxt, errorc, args); + ctxt.vals = nctxt.vals; + return v; + } + name := mkqname(m.m.typeset.name, m.m.modname); + if(ctxt.used != nil){ + ctxt.used.add(name, m.m); + m.m.refcount++; + } + v := ref Rvalue(mkw(name), m.m.sig[0], 0, opts, args); + if(args == nil && opts == nil) + v.i = mk(n_BLOCK, v.i, nil); + for(; args != nil; args = tl args) + (hd args).refcount++; + for(; opts != nil; opts = tl opts) + for(args = (hd opts).t1; args != nil; args = tl args) + (hd args).refcount++; + ctxt.vals = v :: ctxt.vals; + return v; +} + +Rvalue.dup(v: self ref Rvalue): ref Rvalue +{ + return v; +} + +Rvalue.free(nil: self ref Rvalue, nil: int) +{ + # XXX perhaps there should be some way of finding out whether a particular + # type will allow duplication of values or not. +} + +Rvalue.isstring(v: self ref Rvalue): int +{ + return v.tc == 's'; +} + +Rvalue.gets(t: self ref Rvalue): string +{ + return t.i.word; +} + +Rvalue.type2s(tc: int): string +{ + return Value.type2s(tc); +} + +Rvalue.typec(t: self ref Rvalue): int +{ + return t.tc; +} + +addconversion(src, dst: ref Type, expr: ref Sh->Cmd): string +{ + # allow the same transform to be added again + for(l := src.transform; l != nil; l = tl l) + if((hd l).all.holds(dst.id)){ + if((hd l).dst == dst.id && sh->cmd2string((hd l).expr) == sh->cmd2string(expr)) + return nil; + } + + reached := array[len types/8+1] of {* => byte 0}; + if((at := ambiguous(dst, reached)) != nil) + return sys->sprint("ambiguity: %s", at); + + src.transform = ref Transform(dst.id, sets->bytes2set(reached), expr) :: src.transform; + # check we haven't created ambiguity in nodes that point to src. + for(i := 0; i < len types; i++){ + for(l = types[i].transform; l != nil; l = tl l){ + if((hd l).all.holds(src.id) && (at = ambiguous(types[i], array[len types/8+1] of {* => byte 0})) != nil){ + src.transform = tl src.transform; + return sys->sprint("ambiguity: %s", at); + } + } + } + all := (Sets->None).add(dst.id); + for(l = types[dst.id].transform; l != nil; l = tl l) + all = all.X(Sets->A|Sets->B, (hd l).all); + # add everything pointed to by dst to the all sets of those types + # that had previously pointed (indirectly) to src + for(i = 0; i < len types; i++) + for(l = types[i].transform; l != nil; l = tl l) + if((hd l).all.holds(src.id)) + (hd l).all = (hd l).all.X(Sets->A|Sets->B, all); + return nil; +} + +ambiguous(t: ref Type, reached: array of byte): string +{ + if((dt := ambiguous1(t, reached)) == nil) + return nil; + (nil, at) := findambiguous(t, dt, array[len reached] of {* =>byte 0}, "self "+types[t.id].qname); + s := hd at; + for(at = tl at; at != nil; at = tl at) + s += ", " + hd at; + return s; +} + +# a conversion is ambiguous if there's more than one +# way of reaching the same type. +# return the type at which the ambiguity is found. +ambiguous1(t: ref Type, reached: array of byte): ref Type +{ + if(bsetholds(reached, t.id)) + return t; + bsetadd(reached, t.id); + for(l := t.transform; l != nil; l = tl l) + if((at := ambiguous1(types[(hd l).dst], reached)) != nil) + return at; + return nil; +} + +findambiguous(t: ref Type, dt: ref Type, reached: array of byte, s: string): (int, list of string) +{ + a: list of string; + if(t == dt) + a = s :: nil; + if(bsetholds(reached, t.id)) + return (1, a); + bsetadd(reached, t.id); + for(l := t.transform; l != nil; l = tl l){ + (found, at) := findambiguous(types[(hd l).dst], dt, reached, + sys->sprint("%s|%s", s, sh->cmd2string((hd l).expr))); # XXX rewite correctly + for(; at != nil; at = tl at) + a = hd at :: a; + if(found) + return (1, a); + } + return (0, a); +} + +bsetholds(x: array of byte, n: int): int +{ + return int x[n >> 3] & (1 << (n & 7)); +} + +bsetadd(x: array of byte, n: int) +{ + x[n >> 3] |= byte (1 << (n & 7)); +} + +mkqname(parent, child: string): string +{ + if(parent == "/") + return parent+child; + return parent+"/"+child; +} + +# splits a canonical qname into typeset and name components. +splitqname(name: string): (string, string) +{ + if(name == nil) + return (nil, nil); + for(i := len name - 1; i >= 0; i--) + if(name[i] == '/') + break; + if(i == 0) + return ("/", name[1:]); + return (name[0:i], name[i+1:]); +} + +# compress multiple slashes into single; remove trailing slashes. +canon(name: string): string +{ + if(name == nil || name[0] != '/') + return nil; + + slash := nonslash := 0; + s := ""; + for(i := 0; i < len name; i++){ + c := name[i]; + if(c == '/') + slash = 1; + else{ + if(slash){ + s[len s] = '/'; + nonslash++; + slash = 0; + } + s[len s] = c; + } + } + if(slash && !nonslash) + s[len s] = '/'; + return s; +} + +report(errorc: chan of string, s: string) +{ + if(Debug || errorc == nil) + sys->fprint(sys->fildes(2), "%s\n", s); + if(errorc != nil) + errorc <-= s; +} + +Table[T].new(nslots: int, nilval: T): ref Table[T] +{ + if(nslots == 0) + nslots = 13; + return ref Table[T](array[nslots] of list of (int, T), nilval); +} + +Table[T].add(t: self ref Table[T], id: int, x: T): int +{ + slot := id % len t.items; + for(q := t.items[slot]; q != nil; q = tl q) + if((hd q).t0 == id) + return 0; + t.items[slot] = (id, x) :: t.items[slot]; + return 1; +} + +Table[T].del(t: self ref Table[T], id: int): int +{ + slot := id % len t.items; + + p: list of (int, T); + r := 0; + for(q := t.items[slot]; q != nil; q = tl q){ + if((hd q).t0 == id){ + p = joinip(p, tl q); + r = 1; + break; + } + p = hd q :: p; + } + t.items[slot] = p; + return r; +} + +Table[T].find(t: self ref Table[T], id: int): T +{ + for(p := t.items[id % len t.items]; p != nil; p = tl p) + if((hd p).t0 == id) + return (hd p).t1; + return t.nilval; +} + +hashfn(s: string, n: int): int +{ + h := 0; + m := len s; + for(i:=0; i len y) + (x, y) = (y, x); + for(; x != nil; x = tl x) + y = hd x :: y; + return y; +} + +# join x to y, leaving result in arbitrary order. +joinip[T](x, y: list of (int, T)): list of (int, T) +{ + if(len x > len y) + (x, y) = (y, x); + for(; x != nil; x = tl x) + y = hd x :: y; + return y; +} + +sort[S, T](s: S, a: array of T) + for{ + S => + gt: fn(s: self S, x, y: T): int; + } +{ + mergesort(s, a, array[len a] of T); +} + +mergesort[S, T](s: S, a, b: array of T) + for{ + S => + gt: fn(s: self S, x, y: T): int; + } +{ + r := len a; + if (r > 1) { + m := (r-1)/2 + 1; + mergesort(s, a[0:m], b[0:m]); + mergesort(s, a[m:], b[m:]); + b[0:] = a; + for ((i, j, k) := (0, m, 0); i < m && j < r; k++) { + if(s.gt(b[i], b[j])) + a[k] = b[j++]; + else + a[k] = b[i++]; + } + if (i < m) + a[k:] = b[i:m]; + else if (j < r) + a[k:] = b[j:r]; + } +} diff --git a/appl/alphabet/alphabet.proto b/appl/alphabet/alphabet.proto new file mode 100644 index 00000000..2c005396 --- /dev/null +++ b/appl/alphabet/alphabet.proto @@ -0,0 +1,29 @@ +# -{/fs/proto alphabet.proto | /fs/filter {/fs/or {/fs/path /dis} {/fs/not {/fs/or *.dis *.sbl}}} | /fs/write /tmp/blah} +# -{/fs/proto alphabet.proto | /fs/filter {/fs/not {/fs/or *.dis *.sbl}} | /fs/select {/fs/mode -d}} +module + alphabet.m + alphabet + + +dis + sh + alphabet.dis + alphabet + + + scheduler + workflowgen.dis +appl + alphabet + + + cmd + scheduler + workflowgen.b + tgsimple.b + mkfile +man + 1 + sh-alphabet + alphabet-main + alphabet-grid + alphabet-fs + 2 + alphabet-intro diff --git a/appl/alphabet/alphabet.shmod.b b/appl/alphabet/alphabet.shmod.b new file mode 100644 index 00000000..cbe2cbd9 --- /dev/null +++ b/appl/alphabet/alphabet.shmod.b @@ -0,0 +1,413 @@ +implement Alphabetsh, Shellbuiltin; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; + Context, Listnode: import sh; + n_WORD: import sh; +include "alphabet/reports.m"; + reports: Reports; + report, Report: import reports; +include "readdir.m"; + readdir: Readdir; +include "alphabet.m"; + alphabet: Alphabet; + Value, CHECK, ONDEMAND: import alphabet; +include "alphabet/abc.m"; + +Alphabetsh: module {}; + +myself: Shellbuiltin; + +initbuiltin(ctxt: ref Sh->Context, shmod: Sh): string +{ + sys = load Sys Sys->PATH; + myself = load Shellbuiltin "$self"; + sh = shmod; + if (myself == nil) + ctxt.fail("bad module", sys->sprint("file2chan: cannot load self: %r")); + + alphabet = load Alphabet Alphabet->PATH; + if(alphabet == nil) + ctxt.fail("bad module", sys->sprint("alphabet: cannot load %q: %r", Alphabet->PATH)); + reports = load Reports Reports->PATH; + if(reports == nil) + ctxt.fail("bad module", sys->sprint("alphabet: cannot load %q: %r", Reports->PATH)); + readdir = load Readdir Readdir->PATH; + if(readdir == nil) + ctxt.fail("bad module", sys->sprint("alphabet: cannot load %q: %r", Readdir->PATH)); + + alphabet->init(); + alphabet->setautodeclare(1); + + if((decls := ctxt.get("autodeclares")) != nil){ + for(; decls != nil; decls = tl decls){ + d := hd decls; + if(d.cmd == nil){ + err: string; + (d.cmd, err) = sh->parse(d.word); + if(err != nil){ + sys->fprint(sys->fildes(2), "alphabet: warning: bad autodeclaration: %s\n", err); + continue; + } + } + { + declares(ctxt, nil::d::nil); + }exception{ + "fail:*" => + ; + } + } + } + + ctxt.addbuiltin("declare", myself); + ctxt.addbuiltin("declares", myself); + ctxt.addbuiltin("undeclare", myself); + ctxt.addbuiltin("define", myself); + ctxt.addbuiltin("import", myself); + ctxt.addbuiltin("autodeclare", myself); + ctxt.addbuiltin("type", myself); + ctxt.addbuiltin("typeset", myself); + ctxt.addbuiltin("autoconvert", myself); + ctxt.addbuiltin("-", myself); + ctxt.addbuiltin("info", myself); + ctxt.addbuiltin("clear", myself); + +# ctxt.addsbuiltin("-", myself); + ctxt.addsbuiltin("rewrite", myself); + ctxt.addsbuiltin("modules", myself); + ctxt.addsbuiltin("types", myself); + ctxt.addsbuiltin("usage", myself); + return nil; +} + +runbuiltin(c: ref Sh->Context, nil: Sh, + cmd: list of ref Listnode, nil: int): string +{ + case (hd cmd).word { + "declare" => + return declare(c, cmd); + "declares" => + return declares(c, cmd); + "undeclare" => + return undeclare(c, cmd); + "define" => + return define(c, cmd); + "import" => + return importf(c, cmd); + "type" => + return importtype(c, cmd); + "typeset" => + return typeset(c, cmd); + "autoconvert" => + return autoconvert(c, cmd); + "autodeclare" => + if(len cmd != 2) + usage(c, "usage: autodeclare 0/1"); + alphabet->setautodeclare(int word(hd tl cmd)); + "info" => + return info(c, cmd); + "clear" => + a := load Alphabet Alphabet->PATH; + if(a == nil) + c.fail("bad module", sys->sprint("alphabet: cannot load %q: %r", Alphabet->PATH)); + alphabet->quit(); + alphabet = a; + alphabet->init(); + alphabet->setautodeclare(1); + "-" => + return eval(c, cmd); + } + return nil; +} + +whatis(nil: ref Sh->Context, nil: Sh, mod: string, wtype: int): string +{ + if(wtype == OTHER){ + (qname, sig, def) := alphabet->getmodule(mod); + if(qname == nil) + return nil; + s := sys->sprint("declare %q %q", qname, sig); + if(def != nil){ + for(i := len sig-1; i >= 0; i--){ + if(sig[i] == '>'){ + sig = sig[0:i-1]; + break; + } + } + s += sys->sprint("; define %q {(%s); %s}", qname, sig, sh->cmd2string(def)); + } + return s; + } + return nil; +} + +getself(): Shellbuiltin +{ + return myself; +} + +runsbuiltin(ctxt: ref Context, nil: Sh, + argv: list of ref Listnode): list of ref Listnode +{ + case (hd argv).word { + "rewrite" => + return rewrite(ctxt, argv); + "modules" => + return sh->stringlist2list(alphabet->getmodules()); + "types" => + ts := ""; + if(tl argv != nil) + ts = word(hd tl argv); + r := sh->stringlist2list(alphabet->gettypes(ts)); + if(r == nil) + ctxt.fail("error", sys->sprint("unknown typeset %q", ts)); + return r; + "usage" => + if(len argv != 2) + usage(ctxt, "usage qname"); + (qname, u, nil) := alphabet->getmodule(word(hd tl argv)); + if(qname == nil) + ctxt.fail("error", "module not declared"); + return ref Listnode(nil, u) :: nil; + } + return nil; +} + +usage(ctxt: ref Context, s: string) +{ + ctxt.fail("usage", "usage: " + s); +} + +declares(ctxt: ref Sh->Context, argv: list of ref Listnode): string +{ + argv = tl argv; + if(argv == nil || (hd argv).cmd == nil) + ctxt.fail("usage", "usage: declares decls"); + decls := (hd argv).cmd; + declares := load Declares Declares->PATH; + if(declares == nil) + ctxt.fail("bad module", sys->sprint("alphabet: cannot load %q: %r", Declares->PATH)); + { + declares->init(); + } exception e { + "fail:*" => + ctxt.fail("declares init", e[5:]); + } + + spawn printerrors(errorc := chan of string); + e := declares->declares(alphabet, decls, errorc, nil); + declares->quit(); + if(e != nil) + ctxt.fail("bad declaration", sys->sprint("alphabet: declaration failed: %s", e)); + return nil; +} + +rewrite(ctxt: ref Sh->Context, argv: list of ref Listnode): list of ref Listnode +{ + argv = tl argv; + n := len argv; + if(n != 1 && n != 2 || (hd argv).cmd == nil) + usage(ctxt, "rewrite {expr} [desttype]"); + spawn printerrors(errorc := chan of string); + desttype := ""; + if(n == 2) + desttype = word(hd tl argv); + (c, usage) := alphabet->rewrite((hd argv).cmd, desttype, errorc); + errorc <-= nil; + if(c == nil) + raise "fail:bad expression"; + return (ref Listnode(c, nil) :: ref Listnode(nil, usage) :: nil); +} + +# XXX add support for optional ONDEMAND and CHECK flags +declare(ctxt: ref Sh->Context, argv: list of ref Listnode): string +{ + argv = tl argv; + n := len argv; + if(n < 1 || n > 2) + usage(ctxt, "declare qname [type]"); + decltype := ""; + if(n == 2) + decltype = word(hd tl argv); + e := alphabet->declare(word(hd argv), decltype, 0); + if(e != nil) + ctxt.fail("error", sys->sprint("cannot declare %s: %s", word(hd argv), e)); + return nil; +} + +undeclare(ctxt: ref Sh->Context, argv: list of ref Listnode): string +{ + argv = tl argv; + if(argv == nil) + usage(ctxt, "undeclare name..."); + for(; argv != nil; argv = tl argv){ + if((e := alphabet->undeclare(word(hd argv))) != nil) + sys->fprint(sys->fildes(2), "alphabet: cannot undeclare %q: %s\n", word(hd argv), e); + } + return nil; +} + +# usage define name expr +define(ctxt: ref Sh->Context, argv: list of ref Listnode): string +{ + argv = tl argv; + if(len argv != 2 || (hd tl argv).cmd == nil) + usage(ctxt, "define name {expr}"); + + spawn printerrors(errorc := chan of string); + + err := alphabet->define((hd argv).word, (hd tl argv).cmd, errorc); + errorc <-= nil; + if(err != nil) + raise "fail:bad define: "+err; + return nil; +} + +importf(ctxt: ref Sh->Context, argv: list of ref Listnode): string +{ + argv = tl argv; + if(argv == nil) + usage(ctxt, "import qname..."); + errs := 0; + for(; argv != nil; argv = tl argv){ + e := alphabet->importmodule(word(hd argv)); + if(e != nil){ + sys->fprint(sys->fildes(2), "alphabet: cannot import %s: %s\n", word(hd argv), e); + errs++; + } + } + if(errs) + raise "fail:import error"; + return nil; +} + +importtype(ctxt: ref Sh->Context, argv: list of ref Listnode): string +{ + argv = tl argv; + if(argv == nil) + usage(ctxt, "type qname..."); + errs := 0; + for(; argv != nil; argv = tl argv){ + e := alphabet->importtype(word(hd argv)); + if(e != nil){ + sys->fprint(sys->fildes(2), "alphabet: cannot import type %s: %s\n", word(hd argv), e); + errs++; + } + } + if(errs) + raise "fail:type declare error"; + return nil; +} + +typeset(ctxt: ref Sh->Context, argv: list of ref Listnode): string +{ + argv = tl argv; + if(len argv != 1) + usage(ctxt, "typeset qname"); + spawn printerrors(errorc := chan of string); + e := alphabet->loadtypeset(word(hd argv), nil, errorc); # XXX errorc? + errorc <-= nil; + if(e != nil) + ctxt.fail("error", sys->sprint("cannot load typeset %q: %s", word(hd argv), e)); + return nil; +} + +autoconvert(ctxt: ref Sh->Context, argv: list of ref Listnode): string +{ + argv = tl argv; + if(len argv != 3) + usage(ctxt, "autoconvert src dst fn"); + src := word(hd argv); + dst := word(hd tl argv); + expr := (hd tl tl argv).cmd; + if(expr == nil) + expr = ref Sh->Cmd(Sh->n_WORD, nil, nil, (hd tl tl argv).word, nil); + spawn printerrors(errorc := chan of string); + e := alphabet->autoconvert(src, dst, expr, errorc); + errorc <-= nil; + if(e != nil) + ctxt.fail("error", sys->sprint("cannot autoconvert %s to %s via %s: %s", + src, dst, word(hd tl tl argv), e)); + return nil; +} + +info(ctxt: ref Sh->Context, argv: list of ref Listnode): string +{ + first := 1; + if(tl argv != nil) + usage(ctxt, "info"); + for(tsl := alphabet->gettypesets(); tsl != nil; tsl = tl tsl){ + ts := hd tsl; + r := alphabet->gettypesetmodules(ts); + if(r == nil) + continue; + if(first == 0) + sys->print("\n"); + sys->print("typeset %s\n", ts); + while((mod := <-r) != nil){ + (qname, u, nil) := alphabet->getmodule(ts+"/"+mod); + if(qname != nil) + sys->print("%s %s\n", qname, u); + } + first = 0; + } + acl := alphabet->getautoconversions(); + if(acl != nil) + sys->print("\n"); + + for(; acl != nil; acl = tl acl){ + (src, dst, via) := hd acl; + sys->print("autoconvert %q %q %s\n", src, dst, sh->cmd2string(via)); + } + return nil; +} + +eval(ctxt: ref Sh->Context, argv: list of ref Listnode): string +{ + argv = tl argv; + if(argv == nil || (hd argv).cmd == nil) + usage(ctxt, "- {expr} [arg...]"); + c := (hd argv).cmd; + if(c == nil) + c = mkw((hd argv).word); + + + args: list of ref Value; + for(argv = tl argv; argv != nil; argv = tl argv){ + if((hd argv).cmd != nil) + args = ref Value.Vc((hd argv).cmd) :: args; + else + args = ref Value.Vs((hd argv).word) :: args; + } + return alphabet->eval(c, ctxt.drawcontext, rev(args)); +} + +rev[T](x: list of T): list of T +{ + l: list of T; + for(; x != nil; x = tl x) + l = hd x :: l; + return l; +} + +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; +} + +printerrors(c: chan of string) +{ + while((s := <-c) != nil) + sys->fprint(sys->fildes(2), "e: %s\n", s); +} + +mkw(w: string): ref Sh->Cmd +{ + return ref Sh->Cmd(n_WORD, nil, nil, w, nil); +} diff --git a/appl/alphabet/auxi/endpoints.b b/appl/alphabet/auxi/endpoints.b new file mode 100644 index 00000000..5544da8b --- /dev/null +++ b/appl/alphabet/auxi/endpoints.b @@ -0,0 +1,105 @@ +implement Endpoints; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "string.m"; + str: String; +include "sh.m"; + sh: Sh; +include "alphabet/endpoints.m"; + +init() +{ + sys = load Sys Sys->PATH; + sh = load Sh Sh->PATH; + sh->initialise(); + str = load String String->PATH; +} + +DIR: con "/n/endpoint"; + +new(nil, addr: string, force: int): string # XXX don't ignore net directory +{ + if(!force && sys->stat(DIR+"/"+addr+"/clone").t0 != -1) + return nil; + if((e := sh->run(nil, "mount"::"{mntgen}"::DIR::nil)) != nil) + return "mount mntgen failed: "+e; + if((e = sh->run(nil, "endpointsrv"::addr::DIR+"/"+addr::nil)) != nil) + return "endpoint failed: "+e; + if((e = sh->run(nil, "listen"::addr::"export"::DIR+"/"+addr::nil)) != nil){ + sys->unmount(nil, DIR+"/"+addr); + return "listen failed: "+e; + } + return nil; +} + +err(e: string): Endpoint +{ + return (nil, nil, e); +} + +create(addr: string): (ref Sys->FD, Endpoint) +{ + d := DIR+"/"+addr; + fd := sys->open(d+"/clone", Sys->OREAD); + if(fd == nil) + return (nil, err(sys->sprint("cannot open %s/clone: %r", d))); + + buf := array[1024] of byte; + n := sys->read(fd, buf, len buf); + if(n <= 0) + return (nil, err("read id failed")); + s := string buf[0:n]; + (nt, toks) := sys->tokenize(s, " "); + if(nt != 2) + return (nil, err(sys->sprint("invalid id read %q", s))); + id: string; + (addr, id) = (hd toks, hd tl toks); + fd = sys->open(d+"/"+id+".in", Sys->OWRITE); + if(fd == nil) + return (nil, err(sys->sprint("cannot write to %s/%s: %r", d, id))); + return (fd, Endpoint(addr, id, nil)); +} + +open(net: string, ep: Endpoint): (ref Sys->FD, string) +{ + if(hasslash(ep.addr)) + return (nil, "bad address"); + if(hasslash(ep.id)) + return (nil, "bad id"); + d := DIR+"/"+ep.addr; + fd := sys->open(d+"/"+ep.id, Sys->OREAD); + if(fd != nil) + return (fd, nil); + e := sys->sprint("%r"); + if(sys->stat(d+"/clone").t0 != -1) + return (nil, sys->sprint("endpoint does not exist: %s", e)); + if((e = sh->run(nil, "mount"::"-A"::net+ep.addr::d::nil)) != nil) + return (nil, e); + fd = sys->open(d+"/"+ep.id, Sys->OREAD); + if(fd == nil) + return (nil, sys->sprint("endpoint does not exist: %r")); + return (fd, nil); +} + +Endpoint.text(ep: self Endpoint): string +{ + return sys->sprint("%q %q %q", ep.addr, ep.id, ep.about); +} + +Endpoint.mk(s: string): Endpoint +{ + t := str->unquoted(s); + if(len t != 3) + return err("invalid endpoint string"); + # XXX could do more validation than this. + return (hd t, hd tl t, hd tl tl t); +} + +hasslash(s: string): int +{ + for(i := 0; i < len s; i++) + if(s[i] == '/') + return 1; + return 0; +} diff --git a/appl/alphabet/auxi/endpointsrv.b b/appl/alphabet/auxi/endpointsrv.b new file mode 100644 index 00000000..ea5391e5 --- /dev/null +++ b/appl/alphabet/auxi/endpointsrv.b @@ -0,0 +1,58 @@ +implement Endpointsrv; +include "sys.m"; + sys: Sys; +include "draw.m"; + +Endpointsrv: module { + init: fn(nil: ref Draw->Context, argv: list of string); +}; + +init(nil: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + if(len argv != 3) + fatal("usage: endpointsrv addr [dir]"); + addr := hd tl argv; + dir := hd tl tl argv; + if(sys->bind("#s", dir, Sys->MREPL) == -1) + fatal(sys->sprint("cannot bind #s onto %q: %r", dir)); + + fio := sys->file2chan(dir, "clone"); + spawn endpointproc(addr, dir, fio); +} + +endpointproc(addr, dir: string, fio: ref Sys->FileIO) +{ + n := 0; + for(;;) alt { + (offset, nil, nil, rc) := <-fio.read => + if(rc != nil){ + if(offset > 0) + rc <-= (nil, nil); + else{ + mkpipe(dir, string n); + rc <-= (array of byte (addr+" "+string n++), nil); + } + } + (nil, nil, nil, wc) := <-fio.write => + if(wc != nil) + wc <-= (0, "cannot write"); + } +} + +mkpipe(dir: string, p: string) +{ + sys->bind("#|", "/tmp", Sys->MREPL); + d := Sys->nulldir; + d.name = p; + sys->wstat("/tmp/data", d); + d.name = p + ".in"; + sys->wstat("/tmp/data1", d); + sys->bind("/tmp", dir, Sys->MBEFORE); +} + +fatal(e: string) +{ + sys->fprint(sys->fildes(2), "endpointsrv: %s\n", e); + raise "fail:error"; +} diff --git a/appl/alphabet/auxi/fsfilter.b b/appl/alphabet/auxi/fsfilter.b new file mode 100644 index 00000000..a83a5c5b --- /dev/null +++ b/appl/alphabet/auxi/fsfilter.b @@ -0,0 +1,62 @@ +implement Fsfilter; +include "sys.m"; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + Fschan, Next, Quit, Skip, Down: import Fslib; + +filter[T](t: T, src, dst: Fschan) + for{ + T => + query: fn(t: self T, d: ref Sys->Dir, name: string, depth: int): int; + } +{ + names: list of string; + name: string; + indent := 0; + myreply := chan of int; +loop: + for(;;){ + (d, reply) := <-src; + if(d.dir != nil){ + p := name; + if(indent > 0){ + if(p != nil && p[len p - 1] != '/') + p[len p] = '/'; + } + if(t.query(d.dir, p + d.dir.name, indent) == 0 && indent > 0){ + reply <-= Next; + continue; + } + } + dst <-= (d, myreply); + case reply <-= <-myreply { + Quit => + break loop; + Next => + if(d.dir == nil && d.data == nil){ + if(--indent == 0) + break loop; + (name, names) = (hd names, tl names); + } + Skip => + if(--indent == 0) + break loop; + (name, names) = (hd names, tl names); + Down => + if(d.dir != nil){ + names = name :: names; + if(d.dir.mode & Sys->DMDIR){ + if(indent == 0) + name = d.dir.name; + else{ + if(name[len name - 1] != '/') + name[len name] = '/'; + name += d.dir.name; + } + } + indent++; + } + } + } +} diff --git a/appl/alphabet/auxi/mkfile b/appl/alphabet/auxi/mkfile new file mode 100644 index 00000000..db753a46 --- /dev/null +++ b/appl/alphabet/auxi/mkfile @@ -0,0 +1,21 @@ +<../../../mkconfig + +TARG=\ + endpoints.dis\ + endpointsrv.dis\ + rexecsrv.dis\ + fsfilter.dis\ + +SYSMODULES=\ + alphabet.m\ + alphabet/endpoints.m\ + alphabet/reports.m\ + draw.m\ + sh.m\ + string.m\ + sys.m\ + +DISBIN=$ROOT/dis/alphabet + +<$ROOT/mkfiles/mkdis +LIMBOFLAGS=-F $LIMBOFLAGS diff --git a/appl/alphabet/auxi/rexecsrv.b b/appl/alphabet/auxi/rexecsrv.b new file mode 100644 index 00000000..9412d617 --- /dev/null +++ b/appl/alphabet/auxi/rexecsrv.b @@ -0,0 +1,301 @@ +implement Rexecsrv; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; +include "alphabet/endpoints.m"; + endpoints: Endpoints; + Endpoint: import endpoints; +include "alphabet/reports.m"; + reports: Reports; + Report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; +include "alphabet/abc.m"; +include "alphabet/abctypes.m"; +include "string.m"; + str: String; + +Rexecsrv: module { + init: fn(nil: ref Draw->Context, argv: list of string); +}; +drawctxt: ref Draw->Context; + +init(ctxt: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + endpoints = load Endpoints Endpoints->PATH; + if(endpoints == nil) + fatal(sys->sprint("cannot load %s: %r", Endpoints->PATH)); + endpoints->init(); + sh = load Sh Sh->PATH; + if(sh == nil) + fatal(sys->sprint("cannot load %s: %r", Sh->PATH)); + sh->initialise(); + reports = load Reports Reports->PATH; + if(reports == nil) + fatal(sys->sprint("cannot load %s: %r", Reports->PATH)); + str = load String String->PATH; + if(str == nil) + fatal(sys->sprint("cannot load %s: %r", String->PATH)); + if(len argv != 3) + fatal("usage: rexecsrv dir {decls}"); + drawctxt = ctxt; + if(sys->stat("/n/endpoint/local/clone").t0 == -1) + fatal("no local endpoints available"); + dir := hd tl argv; + decls := parse(hd tl tl argv); + if(sys->bind("#s", dir, Sys->MREPL) == -1) + fatal(sys->sprint("cannot bind #s onto %q: %r", dir)); + + alphabet = declares(decls); + + fio := sys->file2chan(dir, "exec"); + sync := chan of int; + spawn rexecproc(sync, fio); + <-sync; +} + +stderr(): ref Sys->FD +{ + return sys->fildes(2); +} + +# use one alphabet module to bootstrap another +# with the desired declarations that we can use to +# execute external commands. +declares(decls: ref Sh->Cmd): Alphabet +{ + alphabet0 := load Alphabet Alphabet->PATH; + if(alphabet0 == nil) + fatal(sys->sprint("cannot load %s: %r", Alphabet->PATH)); + alphabet0->init(); + abctypes := load Abctypes Abctypes->PATH; + if(abctypes == nil) + fatal(sys->sprint("cannot load %s: %r", Abctypes->PATH)); + Abccvt: import abctypes; + abc := load Abc Abc->PATH; + if(abc == nil) + fatal(sys->sprint("cannot load %s: %r", Abc->PATH)); + abc->init(); + Value: import abc; + + (c, nil, abccvt) := abctypes->proxy0(); + + spawn reports->reportproc(errorc := chan of string, nil, reply := chan of ref Report); + r := <-reply; + if((err := alphabet0->loadtypeset("/abc", c, nil)) != nil) + fatal("cannot load typeset /abc: "+err); + alphabet0->setautodeclare(1); + spawn alphabet0->eval0( + parse("{(/cmd);"+ + "/abc/abc |"+ + "/abc/declares $1"+ + "}" + ), + "/abc/abc", + nil, + r, + r.start("evaldecls"), + ref (Alphabet->Value).Vc(decls) :: nil, + vc := chan of ref Alphabet->Value + ); + r.enable(); + av: ref Alphabet->Value; +wait: + for(;;)alt{ + av = <-vc => + ; + msg := <-errorc => + if(msg == nil) + break wait; + sys->fprint(stderr(), "rexecsrv: %s\n", msg); + } + if(av == nil) + fatal("declarations failed"); + v := abccvt.ext2int(av).dup(); + alphabet0->av.free(1); + pick xv := v { + VA => + return xv.i.alphabet; + } + return nil; +} + +parse(s: string): ref Sh->Cmd +{ + (c, err) := sh->parse(s); + if(c== nil) + fatal(sys->sprint("cannot parse %q: %s", s, err)); + return c; +} + +lc(cmd: ref Sh->Cmd): ref Sh->Listnode +{ + return ref Sh->Listnode(cmd, nil); +} + +lw(word: string): ref Sh->Listnode +{ + return ref Sh->Listnode(nil, word); +} + +# write endpoints, cmd +# read endpoints +rexecproc(sync: chan of int, fio: ref Sys->FileIO) +{ + sys->pctl(Sys->FORKNS, nil); + pending: list of (int, string); + sync <-= 1; + for(;;) alt { + (nil, data, fid, wc) := <-fio.write => + if(wc == nil) + break; + req := string data; + l := str->unquoted(req); + if(len l != 2 || Endpoint.mk(hd l).addr == nil){ + wc <-= (0, "bad request"); + break; + } + pending = (fid, req) :: pending; + wc <-= (0, nil); + (offset, nil, fid, rc) := <-fio.read => + if(rc == nil){ + (pending, nil) = removefid(fid, pending); + break; + } + if(offset > 0){ + rc <-= (nil, nil); + break; + } + req: string; + (pending, req) = removefid(fid, pending); + if(req == nil){ + rc <-= (nil, "no pending exec"); + break; + } + l := str->unquoted(req); + spawn exec(sync1 := chan of int, Endpoint.mk(hd l), hd tl l, rc); + <-sync1; + } +} + +gather(errorc: chan of string) +{ + s := ""; + while((e := <-errorc) != nil) + s += e + "\n"; + errorc <-= s; +} + +exec(sync: chan of int, ep: Endpoint, expr: string, + rc: chan of (array of byte, string)) +{ + sys->pctl(Sys->FORKNS, nil); + sync <-= 1; + + spawn gather(errorc := chan of string); + (c, err) := alphabet->parse(expr); + if(c == nil){ + rc <-= (nil, "parse error: "+err); + return; + } + usage: string; + (c, usage) = alphabet->rewrite(c, "/fd", errorc); + errorc <-= nil; + err = <-errorc; + if(c == nil){ + rc <-= (nil, err); + return; + } + if(!alphabet->typecompat("/fd -> /fd", usage).t0) + rc <-= (nil, "incompatible type: "+usage); + + fd0: ref Sys->FD; + (fd0, err) = endpoints->open(nil, ep); + if(fd0 == nil){ + rc <-= (nil, err); + return; + } + (fd1, ep1) := endpoints->create("local"); + if(fd1 == nil){ + rc <-= (nil, "cannot make endpoints: "+ep1.about); + return; + } + rc <-= (array of byte ep1.text(), nil); + + runcmd(c, fd0, fd1); +} + +fdproc(f: chan of ref Sys->FD, fd0: ref Sys->FD) +{ + f <-= fd0; + fd1 := <-f; + if(fd1 == nil) + exit; + buf := array[Sys->ATOMICIO] of byte; + while((n := sys->read(fd0, buf, len buf)) > 0) + if(sys->write(fd1, buf, n) == -1) + break; +} + +runcmd(c: ref Sh->Cmd, fd0, fd1: ref Sys->FD) +{ + f := chan of ref Sys->FD; + spawn fdproc(f, fd0); + + spawn reports->reportproc(errorc := chan of string, nil, reply := chan of ref Report); + r := <-reply; + spawn alphabet->eval0( + c, + "/fd", + drawctxt, + r, + r.start("evalcmd"), + ref (Alphabet->Value).Vf(f) :: nil, + vc := chan of ref Alphabet->Value + ); + r.enable(); + av: ref Alphabet->Value; +wait: + for(;;)alt{ + av = <-vc => + if(av == nil){ + sys->fprint(stderr(), "rexecsrv: no value received\n"); + break; + } + pick v := av { + Vf => + <-v.i; + v.i <-= fd1; + * => + sys->fprint(stderr(), "rexecsrv: can't happen: expression has wrong type '%c'\n", + alphabet->v.typec()); + } + msg := <-errorc => + if(msg == nil) + break wait; + # XXX could queue diagnostics back to caller here. + sys->fprint(stderr(), "rexecsrv: %s\n", msg); + } + sys->write(fd1, array[0] of byte, 0); +} + +removefid(fid: int, l: list of (int, string)): (list of (int, string), string) +{ + if(l == nil) + return (nil, nil); + if((hd l).t0 == fid) + return (removefid(fid, tl l).t0, (hd l).t1); + (rl, d) := removefid(fid, tl l); + return (hd l :: rl, d); +} + +fatal(e: string) +{ + sys->fprint(sys->fildes(2), "rexecsrv: %s\n", e); + raise "fail:error"; +} + diff --git a/appl/alphabet/declare.sh b/appl/alphabet/declare.sh new file mode 100644 index 00000000..7ea84062 --- /dev/null +++ b/appl/alphabet/declare.sh @@ -0,0 +1,25 @@ +load std alphabet + +type /string /fd /status /cmd /wfd + +typeset /fs +type /fs/fs /fs/entries /fs/gate /fs/selector + +typeset /grid +type /grid/endpoint + +autoconvert fd status {(fd); /print $1 1} +autoconvert string fd /read +autoconvert cmd string /unparse +autoconvert wfd fd /w2fd + +autoconvert fs entries /fs/entries +autoconvert string gate /fs/match +autoconvert entries fd /fs/print +autoconvert endpoint fd {(endpoint); /grid/local -v $1} + +fn pretty { + -{ + /echo {/pretty $1} + } ${rewrite $1 /status} +} diff --git a/appl/alphabet/eval.b b/appl/alphabet/eval.b new file mode 100644 index 00000000..b63b9f7e --- /dev/null +++ b/appl/alphabet/eval.b @@ -0,0 +1,757 @@ +implement Eval; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; + n_BLOCK, n_VAR, n_BQ, n_BQ2, n_REDIR, + n_DUP, n_LIST, n_SEQ, n_CONCAT, n_PIPE, n_ADJ, + n_WORD, n_NOWAIT, n_SQUASH, n_COUNT, + n_ASSIGN, n_LOCAL: import sh; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + +# XXX /usr/inferno/appl/alphabet/eval.b:189: function call type mismatch +# ... a remarkably uninformative error message! + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + sys->fprint(sys->fildes(2), "eval: cannot load %s: %r\n", path); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + sh = checkload(load Sh Sh->PATH, Sh->PATH); +} + +WORD, VALUE: con iota; + +# to do: +# - change value letters to more appropriate (e.g. fs->f, entries->e, gate->g). +# - allow shell $variable expansions + +Evalstate: adt[V, M, C] + for { + V => + dup: fn(t: self V): V; + free: fn(t: self V, used: int); + gets: fn(t: self V): string; + isstring: fn(t: self V): int; + type2s: fn(tc: int): string; + typec: fn(t: self V): int; + M => + find: fn(c: C, s: string): (M, string); + typesig: fn(m: self M): string; + run: fn(m: self M, c: C, + errorc: chan of string, + opts: list of (int, list of V), args: list of V): V; + mks: fn(c: C, s: string): V; + mkc: fn(c: C, cmd: ref Sh->Cmd): V; + typename2c: fn(s: string): int; + cvt: fn(c: C, v: V, tc: int, errorc: chan of string): V; + } +{ + ctxt: C; + errorc: chan of string; + + expr: fn(e: self ref Evalstate, c: ref Sh->Cmd, env: Env[V]): V; + runcmd: fn(e: self ref Evalstate, cmd: ref Sh->Cmd, arg0: V, args: list of V): V; + getargs: fn(e: self ref Evalstate, c: ref Sh->Cmd, env: Env[V]): (ref Sh->Cmd, list of V); + getvar: fn(e: self ref Evalstate, c: ref Sh->Cmd, env: Env[V]): V; +}; + +Env: adt[V] + for { + V => + free: fn(v: self V, used: int); + dup: fn(v: self V): V; + } +{ + items: array of V; + + new: fn(args: list of V, nilval: V): Env[V]; + get: fn(t: self Env, id: int): V; + discard: fn(t: self Env); +}; + +Context[V, M, Ectxt].eval(expr: ref Sh->Cmd, ctxt: Ectxt, errorc: chan of string, + args: list of V): V +{ + if(expr == nil){ + discardlist(nil, args); + return nil; + } + nilv: V; + e := ref Evalstate[V, M, Ectxt](ctxt, errorc); + { + return e.runcmd(expr, nilv, args); + } exception x { + "error:*" => + report(e.errorc, x); + return nil; + } +} + +Evalstate[V,M,C].expr(e: self ref Evalstate, c: ref Sh->Cmd, env: Env[V]): V +{ + op: ref Sh->Cmd; + args: list of V; + arg0: V; + case c.ntype { + n_PIPE => + if(c.left == nil){ + # N.B. side effect on env. + arg0 = env.items[0]; + env.items[0] = nil; + env.items = env.items[1:]; + }else + arg0 = e.expr(c.left, env); + { + (op, args) = e.getargs(c.right, env); + } exception { + "error:*" => + arg0.free(0); + raise; + } + n_ADJ or + n_WORD or + n_BLOCK or + n_BQ2 => + (op, args) = e.getargs(c, env); + * => + raise "error: expected pipe, adj or word, got " + sh->cmd2string(c); + } + + return e.runcmd(op, arg0, args); +} + +# a b c -> adj(adj('a', 'b'), 'c') +Evalstate[V,M,C].getargs(e: self ref Evalstate, c: ref Sh->Cmd, env: Env[V]): (ref Sh->Cmd, list of V) +{ + # do a quick sanity check of module/command-block type + for(d := c; d.ntype == n_ADJ; d = d.left) + ; + if(d.ntype != n_WORD && d.ntype != n_BLOCK) + raise "error: expected word or block, got "+sh->cmd2string(d); + args: list of V; + for(; c.ntype == n_ADJ; c = c.left){ + r: V; + case c.right.ntype { + n_VAR => + r = e.getvar(c.right.left, env); + n_BLOCK => + r = e.expr(c.right.left, env); + n_WORD => + r = M.mks(e.ctxt, deglob(c.right.word)); + n_BQ2 => + r = M.mkc(e.ctxt, c.right.left); + * => + discardlist(nil, args); + raise "error: syntax error: expected var, block or word. got "+sh->cmd2string(c); + } + args = r :: args; + } + return (c, args); +} + +Evalstate[V,M,C].getvar(nil: self ref Evalstate, c: ref Sh->Cmd, env: Env[V]): V +{ + if(c == nil || c.ntype != n_WORD) + raise "error: bad variable name"; + var := deglob(c.word); + v := env.get(int var); + if(v == nil) + raise sys->sprint("error: $%q not defined or cannot be reused", var); + return v; +} + +# get rid of GLOB characters left there by the shell. +deglob(s: string): string +{ + j := 0; + for (i := 0; i < len s; i++) { + if (s[i] != Sh->GLOB) { + if (i != j) # a worthy optimisation??? + s[j] = s[i]; + j++; + } + } + if (i == j) + return s; + return s[0:j]; +} + +Evalstate[V,M,C].runcmd(e: self ref Evalstate, cmd: ref Sh->Cmd, arg0: V, args: list of V): V +{ + m: M; + sig: string; + err: string; + if(cmd.ntype == n_WORD){ + (m, err) = M.find(e.ctxt, cmd.word); + if(err != nil){ + discardlist(nil, arg0::args); + raise sys->sprint("error: cannot load %q: %s", cmd.word, err); + } + sig = m.typesig(); + }else{ + (sig, cmd, err) = blocksig0(m, e.ctxt, cmd); + if(sig == nil){ + discardlist(nil, arg0::args); + raise sys->sprint("error: invalid command: %s", err); + } + } + ok: int; + opts: list of (int, list of V); + x: M; + (ok, opts, args) = cvtargs(x, e.ctxt, sig, cmd, arg0, args, e.errorc); + if(ok == -1){ + x: V; + discardlist(opts, args); + raise "error: usage: " + sh->cmd2string(cmd)+" "+cmdusage(x, sig); + } + if(m != nil){ + r := m.run(e.ctxt, e.errorc, opts, args); + if(r == nil) + raise "error: command failed"; + return r; + }else{ + v: V; # XXX prevent spurious (?) compiler error message: "type polymorphic type does not have a 'discard' function" + env := Env[V].new(args, v); + { + v = e.expr(cmd, env); + env.discard(); + return v; + } exception ex { + "error:*" => + env.discard(); + raise; + } + } +} + +# {(fd string); walk $2 | merge {unbundle $1}} +blocksig[M, Ectxt](nilm: M, ctxt: Ectxt, e: ref Sh->Cmd): (string, string) + for{ + M => + typename2c: fn(s: string): int; + find: fn(c: Ectxt, s: string): (M, string); + typesig: fn(m: self M): string; + } +{ + (sig, nil, err) := blocksig0(nilm, ctxt, e); + return (sig, err); +} + +# {(fd string); walk $2 | merge {unbundle $1}} +blocksig0[M, Ectxt](nilm: M, ctxt, e: ref Sh->Cmd): (string, ref Sh->Cmd, string) + for{ + M => + typename2c: fn(s: string): int; + find: fn(c: Ectxt, s: string): (M, string); + typesig: fn(m: self M): string; + } +{ + if(e == nil || e.ntype != n_BLOCK) + return (nil, nil, "expected block, got "+sh->cmd2string(e)); + e = e.left; + + + if(e == nil || e.ntype != n_SEQ || e.left == nil || e.left.ntype != n_LIST){ + (ptc, err) := pipesig(nilm, ctxt, e); + if(err != nil) + return (nil, nil, err); + sig := "a"; + if(ptc != -1) + sig[len sig] = ptc; + return (sig, e, nil); + } + + r := e.right; + e = e.left.left; + if(e == nil) + return ("a", r, nil); + argt: list of string; + while(e.ntype == n_ADJ){ + if(e.right.ntype != n_WORD) + return (nil, nil, "bad declaration: expected word, got "+sh->cmd2string(e.right)); + argt = deglob(e.right.word) :: argt; + e = e.left; + } + if(e.ntype != n_WORD) + return (nil, nil, "bad declaration: expected word, got "+sh->cmd2string(e)); + argt = e.word :: argt; + i := 1; + sig := "a"; + (ptc, err) := pipesig(nilm, ctxt, r); + if(err != nil) + return (nil, nil, err); + if(ptc != -1) + sig[len sig] = ptc; + + for(a := argt; a != nil; a = tl a){ + tc := M.typename2c(hd a); + if(tc == -1) + return (nil, nil, sys->sprint("unknown type %q", hd a)); + sig[len sig] = tc; + i++; + } + return (sig, r, nil); +} + +# if e represents an expression with an empty first pipe element, +# return the type of its first argument (-1 if it doesn't). +# string represents error if module doesn't have a first argument. +pipesig[M, Ectxt](nilm: M, ctxt: Ectxt, e: ref Sh->Cmd): (int, string) + for{ + M => + typename2c: fn(s: string): int; + find: fn(c: Ectxt, s: string): (M, string); + typesig: fn(m: self M): string; + } +{ + if(e == nil) + return (-1, nil); + for(; e.ntype == n_PIPE; e = e.left){ + if(e.left == nil){ + # find actual module that's being called. + for(e = e.right; e.ntype == n_ADJ; e = e.left) + ; + sig: string; + if(e.ntype == n_WORD){ + (m, err) := M.find(ctxt, e.word); + if(m == nil) + return (-1, err); + sig = m.typesig(); + } + else if(e.ntype == n_BLOCK){ + err: string; + (sig, nil, err) = blocksig0(nilm, ctxt, e); + if(sig == nil) + return (-1, err); + }else + return (-1, "expected word or block, got "+sh->cmd2string(e)); + if(len sig < 2) + return (-1, "cannot pipe into "+sh->cmd2string(e)); + return (sig[1], nil); + } + } + return (-1, nil); +} + +cvtargs[M,V,C](nil: M, ctxt: C, otype: string, cmd: ref Sh->Cmd, arg0: V, args: list of V, errorc: chan of string): (int, list of (int, list of V), list of V) + for{ + V => + typec: fn(v: self V): int; + isstring: fn(v: self V): int; + type2s: fn(tc: int): string; + gets: fn(v: self V): string; + M => + cvt: fn(c: C, v: V, tc: int, errorc: chan of string): V; + mks: fn(c: C, s: string): V; + } +{ + ok: int; + opts: list of (int, list of V); + (nil, at, t) := splittype(otype); + x: M; + (ok, opts, args) = cvtopts(x, ctxt, t, cmd, args, errorc); + if(arg0 != nil) + args = arg0 :: args; + if(ok == -1) + return (-1, opts, args); + if(len at > 0 && at[0] == '*'){ + report(errorc, sys->sprint("error: invalid type descriptor %#q for %s", at, sh->cmd2string(cmd))); + return (-1, opts, args); + } + n := len args; + if(at != nil && at[len at - 1] == '*'){ + tc := at[len at - 2]; + at = at[0:len at - 2]; + for(i := len at; i < n; i++) + at[i] = tc; + } + if(n != len at){ + report(errorc, sys->sprint("error: wrong number of arguments (%d/%d) to %s", n, len at, sh->cmd2string(cmd))); + return (-1, opts, args); + } + d: list of V; + (ok, args, d) = cvtvalues(x, ctxt, at, cmd, args, errorc); + if(ok == -1) + args = join(args, d); + return (ok, opts, args); +} + +cvtvalues[M,V,C](nil: M, ctxt: C, t: string, cmd: ref Sh->Cmd, args: list of V, errorc: chan of string): (int, list of V, list of V) + for{ + V => + type2s: fn(tc: int): string; + typec: fn(v: self V): int; + M => + cvt: fn(c: C, v: V, tc: int, errorc: chan of string): V; + } +{ + cargs: list of V; + for(i := 0; i < len t; i++){ + tc := t[i]; + if(args == nil){ + report(errorc, sys->sprint("error: missing argument of type %s for %s", V.type2s(tc), sh->cmd2string(cmd))); + return (-1, cargs, args); + } + v := M.cvt(ctxt, hd args, tc, errorc); + if(v == nil){ + report(errorc, "error: conversion failed for "+sh->cmd2string(cmd)); + return (-1, cargs, tl args); + } + cargs = v :: cargs; + args = tl args; + } + return (0, rev(cargs), args); +} + +cvtopts[M,V,C](nil: M, ctxt: C, opttype: string, cmd: ref Sh->Cmd, args: list of V, errorc: chan of string): (int, list of (int, list of V), list of V) + for{ + V => + type2s: fn(tc: int): string; + isstring: fn(v: self V): int; + typec: fn(v: self V): int; + gets: fn(v: self V): string; + M => + cvt: fn(c: C, v: V, tc: int, errorc: chan of string): V; + mks: fn(c: C, s: string): V; + } +{ + if(opttype == nil) + return (0, nil, args); + opts: list of (int, list of V); +getopts: + while(args != nil){ + s := ""; + if((hd args).isstring()){ + s = (hd args).gets(); + if(s == nil || s[0] != '-' || len s == 1) + s = nil; + else if(s == "--"){ + args = tl args; + s = nil; + } + } + if(s == nil) + return (0, opts, args); + s = s[1:]; + while(len s > 0){ + opt := s[0]; + if(((ok, t) := opttypes(opt, opttype)).t0 == -1){ + report(errorc, sys->sprint("error: unknown option -%c for %s", opt, sh->cmd2string(cmd))); + return (-1, opts, args); + } + if(t == nil){ + s = s[1:]; + opts = (opt, nil) :: opts; + }else{ + if(len s > 1) + args = M.mks(ctxt, s[1:]) :: tl args; + else + args = tl args; + vl: list of V; + x: M; + (ok, vl, args) = cvtvalues(x, ctxt, t, cmd, args, errorc); + if(ok == -1) + return (-1, opts, join(vl, args)); + opts = (opt, vl) :: opts; + continue getopts; + } + } + args = tl args; + } + return (0, opts, args); +} + +discardlist[V](ol: list of (int, list of V), vl: list of V) + for{ + V => + free: fn(v: self V, used: int); + } +{ + for(; ol != nil; ol = tl ol) + for(ovl := (hd ol).t1; ovl != nil; ovl = tl ovl) + vl = (hd ovl) :: vl; + for(; vl != nil; vl = tl vl) + (hd vl).free(0); +} + +# true if a module with type sig t1 is compatible with a caller that expects t0 +typecompat(t0, t1: string): int +{ + (rt0, at0, ot0) := splittype(t0); + (rt1, at1, ot1) := splittype(t1); + + if((rt0 != rt1 && rt0 != 'a') || at0 != at1) # XXX could do better for repeated args. + return 0; + + for(i := 1; i < len ot0; i++){ + for(j := i; j < len ot0; j++) + if(ot0[j] == '-') + break; + (ok, t) := opttypes(ot0[i], ot1); + if(ok == -1 || ot0[i+1:j] != t) + return 0; + i = j; + } + return 1; +} + +splittype(t: string): (int, string, string) +{ + if(t == nil) + return (-1, nil, nil); + for(i := 1; i < len t; i++) + if(t[i] == '-') + break; + return (t[0], t[1:i], t[i:]); +} + +opttypes(opt: int, opts: string): (int, string) +{ + for(i := 1; i < len opts; i++){ + if(opts[i] == opt && opts[i-1] == '-'){ + for(j := i+1; j < len opts; j++) + if(opts[j] == '-') + break; + return (0, opts[i+1:j]); + } + } + return (-1, nil); +} + +usage2sig[V](nil: V, u: string): (string, string) + for{ + V => + typename2c: fn(s: string): int; + } +{ + u[len u] = '\0'; + + i := 0; + t: int; + tok: string; + + # options + opts: string; + for(;;){ + (t, tok, i) = optstok(u, i); + if(t != '[') + break; + o := i; + (t, tok, i) = optstok(u, i); + if(t != '-'){ + i = o; + t = '['; + break; + } + for(j := 0; j < len tok; j++){ + opts[len opts] = '-'; + opts[len opts] = tok[j]; + } + for(;;){ + (t, tok, i) = optstok(u, i); + if(t == ']') + break; + if(t != 't') + return (nil, sys->sprint("bad option syntax, got '%c'", t)); + tc := V.typename2c(tok); + if(tc == -1) + return (nil, "unknown type: "+tok); + opts[len opts] = tc; + } + } + + # arguments + args: string; +parseargs: + for(;;){ + case t { + '>' => + break parseargs; + '[' => + (t, tok, i) = optstok(u, i); + if(t != 't') + return (nil, "bad argument syntax"); + tc := V.typename2c(tok); + if(tc == -1) + return (nil, "unknown type: "+tok); + if(((t, nil, i) = optstok(u, i)).t0 != '*') + return (nil, "bad argument syntax"); + if(((t, nil, i) = optstok(u, i)).t0 != ']') + return (nil, "bad argument syntax"); + if(((t, nil, i) = optstok(u, i)).t0 != '>') + return (nil, "bad argument syntax"); + args[len args] = tc; + args[len args] = '*'; + break parseargs; + 't' => + tc := V.typename2c(tok); + if(tc == -1) + return (nil, "unknown type: "+tok); + args[len args] = tc; + (t, tok, i) = optstok(u, i); + * => + return (nil, "no return type"); + } + } + + # return type + (t, tok, i) = optstok(u, i); + if(t != 't') + return (nil, "expected return type"); + tc := V.typename2c(tok); + if(tc == -1) + return (nil, "unknown type: "+tok); + r: string; + r[0] = tc; + r += args; + r += opts; + return (r, nil); +} + +optstok(u: string, i: int): (int, string, int) +{ + while(u[i] == ' ') + i++; + case u[i] { + '\0' => + return (-1, nil, i); + '-' => + i++; + if(u[i] == '>') + return ('>', nil, i+1); + start := i; + while((c := u[i]) != '\0'){ + if(c == ']' || c == ' ') + break; + i++; + } + return ('-', u[start:i], i); + '[' => + return (u[i], nil, i+1); + ']' => + return (u[i], nil, i+1); + '.' => + start := i; + while(u[i] == '.') + i++; + if(i - start < 3) + raise "parse:error at '.'"; + return ('*', nil, i); + * => + start := i; + while((c := u[i]) != '\0'){ + if(c == ' ' || c == ']' || c == '-' || (c == '.' && u[i+1] == '.')) + return ('t', u[start:i], i); + i++; + } + return ('t', u[start:i], i); + } +} + +cmdusage[V](nil: V, t: string): string + for{ + V => + type2s: fn(c: int): string; + } +{ + if(t == nil) + return "-> bad"; + for(oi := 0; oi < len t; oi++) + if(t[oi] == '-') + break; + s := ""; + if(oi < len t){ + single, multi: string; + for(i := oi; i < len t - 1;){ + for(j := i + 1; j < len t; j++) + if(t[j] == '-') + break; + + optargs := t[i+2:j]; + if(optargs == nil) + single[len single] = t[i+1]; + else{ + multi += sys->sprint(" [-%c", t[i+1]); + for (k := 0; k < len optargs; k++) + multi += " " + V.type2s(optargs[k]); + multi += "]"; + } + i = j; + } + if(single != nil) + s += " [-" + single + "]"; + s += multi; + } + multi := 0; + if(oi > 2 && t[oi - 1] == '*'){ + multi = 1; + oi -= 2; + } + for(k := 1; k < oi; k++) + s += " " + V.type2s(t[k]); + if(multi) + s += " [" + V.type2s(t[k]) + "...]"; + s += " -> " + V.type2s(t[0]); + if(s[0] == ' ') + s=s[1:]; + return s; +} + +Env[V].new(args: list of V, nilval: V): Env[V] +{ + if(args == nil) + return Env(nil); + e := Env[V](array[len args] of {* => nilval}); + for(i := 0; args != nil; args = tl args) + e.items[i++] = hd args; + return e; +} + +Env[V].get(t: self Env, id: int): V +{ + id--; + if(id < 0 || id >= len t.items) + return nil; + x := t.items[id]; + if((y := x.dup()) == nil){ + t.items[id] = nil; + y = x; + } + return y; +} + +Env[V].discard(t: self Env) +{ + for(i := 0; i < len t.items; i++) + t.items[i].free(0); +} + +rev[T](x: list of T): list of T +{ + l: list of T; + for(; x != nil; x = tl x) + l = hd x :: l; + return l; +} + +# join x to y, leaving result in arbitrary order. +join[T](x, y: list of T): list of T +{ + if(len x > len y) + (x, y) = (y, x); + for(; x != nil; x = tl x) + y = hd x :: y; + return y; +} diff --git a/appl/alphabet/extvalues.b b/appl/alphabet/extvalues.b new file mode 100644 index 00000000..67d4833f --- /dev/null +++ b/appl/alphabet/extvalues.b @@ -0,0 +1,49 @@ +implement Extvalues; +include "sys.m"; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; +include "alphabet.m"; + +Values[V].new(): ref Values[V] +{ + v: V; + return ref Values[V](chan[1] of int, array[4] of {* => (0, v)}, 0::1::2::3::nil); +} + +Values[V].add(vals: self ref Values, v: V): int +{ + vals.lock <-= 1; + if(vals.freeids == nil){ + n := len vals.v; + vals.v = (array[len vals.v * 3 / 2] of (int, V))[0:] = vals.v; + for(; n < len vals.v; n++) + vals.freeids = n :: vals.freeids; + } + id := hd vals.freeids; + vals.freeids = tl vals.freeids; + vals.v[id] = (1, v); +#(load Sys Sys->PATH)->print("add %d\n", id); + <-vals.lock; + return id; +} + +Values[V].inc(vals: self ref Values, id: int) +{ + vals.lock <-= 1; + vals.v[id].t0++; +#(load Sys Sys->PATH)->print("inc %d -> %d\n", id, vals.v[id].t0); + <-vals.lock; +} + +Values[V].del(vals: self ref Values, id: int) +{ + vals.lock <-= 1; + if(--vals.v[id].t0 == 0){ + vals.v[id].t1 = nil; + vals.freeids = id :: vals.freeids; + } +#(load Sys Sys->PATH)->print("del %d -> %d\n", id, vals.v[id].t0); + <-vals.lock; +} + diff --git a/appl/alphabet/fs/and.b b/appl/alphabet/fs/and.b new file mode 100644 index 00000000..da180978 --- /dev/null +++ b/appl/alphabet/fs/and.b @@ -0,0 +1,70 @@ +implement And,Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +And: module {}; + +types(): string +{ + return "pppp*"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); +} + +run(nil: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + c := chan of Gatequery; + spawn andgate(c, args); + return ref Value.Vp(c); +} + +andgate(c: Gatechan, args: list of ref Value) +{ + sub: list of Gatechan; + for(; args != nil; args = tl args) + sub = (hd args).p().i :: sub; + sub = rev(sub); + myreply := chan of int; + while(((d, reply) := <-c).t0.t0 != nil){ + for(l := sub; l != nil; l = tl l){ + (hd l) <-= (d, myreply); + if(<-myreply == 0) + break; + } + reply <-= l == nil; + } + for(; sub != nil; sub = tl sub) + hd sub <-= (Nilentry, nil); +} + +rev[T](x: list of T): list of T +{ + l: list of T; + for(; x != nil; x = tl x) + l = hd x :: l; + return l; +} diff --git a/appl/alphabet/fs/bundle.b b/appl/alphabet/fs/bundle.b new file mode 100644 index 00000000..2c692f1a --- /dev/null +++ b/appl/alphabet/fs/bundle.b @@ -0,0 +1,210 @@ +implement Bundle, Fsmodule; +include "sys.m"; + sys: Sys; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "readdir.m"; + readdir: Readdir; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, quit, report: import reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Bundle: module {}; + +# XXX if we can't open a directory, is it ever worth passing its metadata +# through anyway? + +EOF: con "end of archive\n"; + +types(): string +{ + return "fx"; +} +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: bundle: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + readdir = load Readdir Readdir->PATH; + if(readdir == nil) + badmod(Readdir->PATH); + bufio = load Bufio Bufio->PATH; + if(bufio == nil) + badmod(Readdir->PATH); + bufio->fopen(nil, Sys->OREAD); # XXX no bufio->init! + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Readdir->PATH); + fs->init(); + reports = load Reports Reports->PATH; + if(reports == nil) + badmod(Reports->PATH); +} + +run(nil: ref Draw->Context, r: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + f := chan of ref Sys->FD; + spawn bundleproc((hd args).x().i, f, r.start("bundle")); + return ref Value.Vf(f); +} + +#bundle(r: ref Report, iob: ref Iobuf, c: Fschan): chan of string +bundle(nil: ref Report, nil: ref Iobuf, nil: Fschan): chan of string +{ + return nil; +# sync := chan[1] of string; +# spawn bundleproc(c, sync, iob, r.start("bundle")); +# return sync; +} + +bundleproc(c: Fschan, f: chan of ref Sys->FD, errorc: chan of string) +{ + f <-= nil; + if((fd := <-f) == nil){ + (<-c).t1 <-= Quit; + quit(errorc); + } + iob := bufio->fopen(fd, Sys->OWRITE); + fd = nil; + (d, reply) := <-c; + if(d.dir == nil){ + report(errorc, "no root directory"); + endarchive(iob, errorc); + } + if(puts(iob, dir2header(d.dir), errorc) == -1){ + reply <-= Quit; + quit(errorc); + } + reply <-= Down; + bundledir(d.dir.name, d, c, iob, errorc); + endarchive(iob, errorc); +} + +endarchive(iob: ref Iobuf, errorc: chan of string) +{ + { + if(puts(iob, EOF, errorc) != -1) + iob.flush(); + sys->fprint(iob.fd, ""); + } exception { + "write on closed pipe" => + ; + } + quit(errorc); +} + +bundledir(path: string, d: Fsdata, + c: Fschan, + iob: ref Iobuf, errorc: chan of string) +{ + if(d.dir.mode & Sys->DMDIR){ + path[len path] = '/'; + for(;;){ + (ent, reply) := <-c; + if(ent.dir == nil){ + reply <-= Skip; + break; + } + if(puts(iob, dir2header(ent.dir), errorc) == -1){ + reply <-= Quit; + quit(errorc); + } + reply <-= Down; + bundledir(path + ent.dir.name, ent, c, iob, errorc); + } + iob.putc('\n'); + }else{ + buf: array of byte; + reply: chan of int; + length := big d.dir.length; + n := big 0; + for(;;){ + ((nil, buf), reply) = <-c; + if(buf == nil){ + reply <-= Skip; + break; + } + if(write(iob, buf, len buf, errorc) != len buf){ + reply <-= Quit; + quit(errorc); + } + n += big len buf; + if(n > length){ # should never happen + report(errorc, sys->sprint("%q is longer than expected (fatal)", path)); + reply <-= Quit; + quit(errorc); + } + if(n == length){ + reply <-= Skip; + break; + } + reply <-= Next; + } + if(n < length){ + report(errorc, sys->sprint("%q is shorter than expected (%bd/%bd); adding null bytes", path, n, length)); + buf = array[Sys->ATOMICIO] of {* => byte 0}; + while(n < length){ + nb := len buf; + if(length - n < big len buf) + nb = int (length - n); + if(write(iob, buf, nb, errorc) != nb){ + (<-c).t1 <-= Quit; + quit(errorc); + } + report(errorc, sys->sprint("added %d null bytes", nb)); + n += big nb; + } + } + } +} + +dir2header(d: ref Sys->Dir): string +{ + return sys->sprint("%q %uo %q %q %ud %bd\n", d.name, d.mode, d.uid, d.gid, d.mtime, d.length); +} + +puts(iob: ref Iobuf, s: string, errorc: chan of string): int +{ + { + if(iob.puts(s) == -1) + report(errorc, sys->sprint("write error: %r")); + return 0; + } exception { + "write on closed pipe" => + report(errorc, sys->sprint("write on closed pipe")); + return -1; + } +} + +write(iob: ref Iobuf, buf: array of byte, n: int, errorc: chan of string): int +{ + { + nw := iob.write(buf, n); + if(nw < n){ + if(nw >= 0) + report(errorc, "short write"); + else{ + report(errorc, sys->sprint("write error: %r")); + } + } + return nw; + } exception { + "write on closed pipe" => + report(errorc, "write on closed pipe"); + return -1; + } +} diff --git a/appl/alphabet/fs/bundle.m b/appl/alphabet/fs/bundle.m new file mode 100644 index 00000000..905b8288 --- /dev/null +++ b/appl/alphabet/fs/bundle.m @@ -0,0 +1,9 @@ +Bundle: module { + PATH: con "/dis/fs/bundle.dis"; + + types: fn(): string; + init: fn(); + run: fn(nil: ref Draw->Context, report: ref Reports->Report, + nil: list of Fs->Option, args: list of ref Fs->Value): ref Fs->Value; + bundle: fn(r: ref Reports->Report, iob: ref Bufio->Iobuf, c: Fs->Fschan): chan of string; +}; diff --git a/appl/alphabet/fs/chstat.b b/appl/alphabet/fs/chstat.b new file mode 100644 index 00000000..b53ca784 --- /dev/null +++ b/appl/alphabet/fs/chstat.b @@ -0,0 +1,189 @@ +implement Chstat, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; +include "alphabet/fs.m"; + fsfilter: Fsfilter; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Chstat: module {}; + +Query: adt { + gate: Gatechan; + stat: Sys->Dir; + mask: int; + cflag: int; + reply: chan of int; + + query: fn(q: self ref Query, d: ref Sys->Dir, name: string, depth: int): int; +}; + +types(): string +{ + return "xx-pp-ms-us-gs-ts-as-c"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + + fsfilter = load Fsfilter Fsfilter->PATH; + if(fsfilter == nil) + badmod(Fsfilter->PATH); +} + +run(nil: ref Draw->Context, nil: ref Reports->Report, + opts: list of Option, args: list of ref Value): ref Value +{ + ws := Sys->nulldir; + mask := 0; + gate: ref Value; + cflag := 0; + for(; opts != nil; opts = tl opts){ + o := (hd opts).args; + case (hd opts).opt { + 'p' => + gate.free(0); + gate = hd o; + 'm' => + ok: int; + m := (hd o).s().i; + (ok, mask, ws.mode) = parsemode(m); + mask &= ~Sys->DMDIR; + if(ok == 0){ + sys->fprint(sys->fildes(2), "fs: chstat: bad mode %#q\n", m); + gate.free(0); + return nil; + } + 'u' => + ws.uid = (hd o).s().i; + 'g' => + ws.gid = (hd o).s().i; + 't' => + ws.mtime = int (hd o).s().i; + 'a' => + ws.atime = int (hd o).s().i; + 'c' => + cflag++; + } + } + + dst := chan of (Fsdata, chan of int); + p: Gatechan; + if(gate != nil) + p = gate.p().i; + spawn chstatproc((hd args).x().i, dst, p, ws, mask, cflag); + return ref Value.Vx(dst); +} + +chstatproc(src, dst: Fschan, gate: Gatechan, stat: Sys->Dir, mask: int, cflag: int) +{ + fsfilter->filter(ref Query(gate, stat, mask, cflag, chan of int), src, dst); + if(gate != nil) + gate <-= ((nil, nil, 0), nil); +} + +Query.query(q: self ref Query, d: ref Sys->Dir, name: string, depth: int): int +{ + c := 1; + if(q.gate != nil){ + q.gate <-= ((d, name, depth), q.reply); + c = <-q.reply; + } + if(c){ + if(q.cflag){ + m := d.mode & 8r700; + d.mode = (d.mode & ~8r77)|(m>>3)|(m>>6); + } + stat := q.stat; + d.mode = (d.mode & ~q.mask) | (stat.mode & q.mask); + if(stat.uid != nil) + d.uid = stat.uid; + if(stat.gid != nil) + d.gid = stat.gid; + if(stat.mtime != ~0) + d.mtime = stat.mtime; + if(stat.atime != ~0) + d.atime = stat.atime; + } + return 1; +} + +# stolen from /appl/cmd/chmod.b +User: con 8r700; +Group: con 8r070; +Other: con 8r007; +All: con User | Group | Other; + +Read: con 8r444; +Write: con 8r222; +Exec: con 8r111; +parsemode(spec: string): (int, int, int) +{ + mask := Sys->DMAPPEND | Sys->DMEXCL | Sys->DMDIR | Sys->DMAUTH; +loop: + for(i := 0; i < len spec; i++){ + case spec[i] { + 'u' => + mask |= User; + 'g' => + mask |= Group; + 'o' => + mask |= Other; + 'a' => + mask |= All; + * => + break loop; + } + } + if(i == len spec) + return (0, 0, 0); + if(i == 0) + mask |= All; + + op := spec[i++]; + if(op != '+' && op != '-' && op != '=') + return (0, 0, 0); + + mode := 0; + for(; i < len spec; i++){ + case spec[i]{ + 'r' => + mode |= Read; + 'w' => + mode |= Write; + 'x' => + mode |= Exec; + 'a' => + mode |= Sys->DMAPPEND; + 'l' => + mode |= Sys->DMEXCL; + 'd' => + mode |= Sys->DMDIR; + 'A' => + mode |= Sys->DMAUTH; + * => + return (0, 0, 0); + } + } + if(op == '+' || op == '-') + mask &= mode; + if(op == '-') + mode = ~mode; + return (1, mask, mode); +} diff --git a/appl/alphabet/fs/compose.b b/appl/alphabet/fs/compose.b new file mode 100644 index 00000000..09bef812 --- /dev/null +++ b/appl/alphabet/fs/compose.b @@ -0,0 +1,105 @@ +implement Compose, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Cmpchan, + Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Compose: module {}; + +AinB: con 1<<3; +BinA: con 1<<2; +AoutB: con 1<<1; +BoutA: con 1<<0; + +A: con AinB|AoutB; +AoverB: con AinB|AoutB|BoutA; +AatopB: con AinB|BoutA; +AxorB: con AoutB|BoutA; + +B: con BinA|BoutA; +BoverA: con BinA|BoutA|AoutB; +BatopA: con BinA|AoutB; +BxorA: con BoutA|AoutB; + +ops := array[] of { + AinB => "AinB", + BinA => "BinA", + AoutB => "AoutB", + BoutA => "BoutA", + A => "A", + AoverB => "AoverB", + AatopB => "AatopB", + AxorB => "AxorB", + B => "B", + BoverA => "BoverA", + BatopA => "BatopA", +}; + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +types(): string +{ + return "ms-d"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); +} + +run(nil: ref Draw->Context, nil: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + c := chan of (ref Sys->Dir, ref Sys->Dir, chan of int); + s := (hd args).s().i; + for(i := 0; i < len ops; i++) + if(ops[i] == s) + break; + if(i == len ops){ + sys->fprint(sys->fildes(2), "fs: join: bad op %q\n", s); + return nil; + } + spawn compose(c, i, opts != nil); + return ref Value.Vm(c); +} + +compose(c: Cmpchan, op: int, dflag: int) +{ + t := array[4] of {* => 0}; + if(op & AinB) + t[2r11] = 2r01; + if(op & BinA) + t[2r11] = 2r10; + if(op & AoutB) + t[2r01] = 2r01; + if(op & BoutA) + t[2r10] = 2r10; + if(dflag){ + while(((d0, d1, reply) := <-c).t2 != nil){ + x := (d1 != nil) << 1 | d0 != nil; + r := t[d0 != nil | (d1 != nil) << 1]; + if(r == 0 && x == 2r11 && (d0.mode & d1.mode & Sys->DMDIR)) + r = 2r11; + reply <-= r; + } + }else{ + while(((d0, d1, reply) := <-c).t2 != nil) + reply <-= t[(d1 != nil) << 1 | d0 != nil]; + } +} diff --git a/appl/alphabet/fs/depth.b b/appl/alphabet/fs/depth.b new file mode 100644 index 00000000..9787fd54 --- /dev/null +++ b/appl/alphabet/fs/depth.b @@ -0,0 +1,54 @@ +implement Depth, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Depth: module {}; + +types(): string +{ + return "ps"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); +} + +run(nil: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + d := int (hd args).s().i; + if(d <= 0){ + sys->fprint(sys->fildes(2), "fs: depth: invalid depth\n"); + return nil; + } + c := chan of Gatequery; + spawn depthgate(c, d); + return ref Value.Vp(c); +} + +depthgate(c: Gatechan, d: int) +{ + while((((dir, nil, depth), reply) := <-c).t0.t0 != nil) + reply <-= depth <= d; +} diff --git a/appl/alphabet/fs/entries.b b/appl/alphabet/fs/entries.b new file mode 100644 index 00000000..6fbf78d0 --- /dev/null +++ b/appl/alphabet/fs/entries.b @@ -0,0 +1,91 @@ +implement Entries, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Entries: module {}; + +types(): string +{ + return "tx"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); +} + +run(nil: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + sc := Entrychan(chan of int, chan of Entry); + spawn entriesproc((hd args).x().i, sc); + return ref Value.Vt(sc); +} + +entriesproc(c: Fschan, sc: Entrychan) +{ + if(<-sc.sync == 0){ + (<-c).t1 <-= Quit; + exit; + } + indent := 0; + names: list of string; + name: string; +loop: + for(;;){ + (d, reply) := <-c; + if(d.dir != nil){ + p: string; + depth := indent; + if(d.dir.mode & Sys->DMDIR){ + names = name :: names; + if(indent == 0) + name = d.dir.name; + else{ + if(name[len name - 1] != '/') + name[len name] = '/'; + name += d.dir.name; + } + indent++; + reply <-= Down; + p = name; + }else{ + p = name; + if(p[len p - 1] != '/') + p[len p] = '/'; + p += d.dir.name; + reply <-= Next; + } + if(p != nil) + sc.c <-= (d.dir, p, depth); + }else{ + reply <-= Next; + if(d.dir == nil && d.data == nil){ + if(--indent == 0) + break loop; + (name, names) = (hd names, tl names); + } + } + } + sc.c <-= Nilentry; +} diff --git a/appl/alphabet/fs/exec.b b/appl/alphabet/fs/exec.b new file mode 100644 index 00000000..a7aa7460 --- /dev/null +++ b/appl/alphabet/fs/exec.b @@ -0,0 +1,172 @@ +implement Exec, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; + Context: import sh; +include "alphabet/reports.m"; + reports: Reports; + Report: import reports; +include "alphabet/fs.m"; + fs: Fs; + Option, Value, Entrychan: import fs; + +Exec: module {}; + +# usage: exec [-n nfiles] [-t endcmd] [-pP] command entries +types(): string +{ + return "rtc-ns-tc-p-P"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: exec: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); + reports = load Reports Reports->PATH; + if(reports == nil) + badmod(Reports->PATH); + sh = load Sh Sh->PATH; + if(sh == nil) + badmod(Sh->PATH); + sh->initialise(); +} + +run(drawctxt: ref Draw->Context, report: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + n := 1; + pflag := 0; + tcmd: ref Sh->Cmd; + for(; opts != nil; opts = tl opts){ + o := hd opts; + case o.opt { + 'n' => + if((n = int (hd o.args).s().i) <= 0){ + sys->fprint(sys->fildes(2), "fs: exec: invalid argument to -n\n"); + return nil; + } + 't' => + tcmd = (hd o.args).c().i; + 'p' => + pflag = 1; + 'P' => + pflag = 2; + } + } + if(pflag && n > 1){ + sys->fprint(sys->fildes(2), "fs: exec: cannot specify -p with -n %d\n", n); + return nil; + } + cmd := (hd tl args).c().i; + c := (hd args).t().i; + sync := chan of string; + spawn execproc(drawctxt, sync, n, pflag, c, cmd, tcmd, report.start("exec")); + sync <-= nil; + return ref Value.Vr(sync); +} + +execproc(drawctxt: ref Draw->Context, sync: chan of string, n, pflag: int, + c: Entrychan, cmd, tcmd: ref Sh->Cmd, errorc: chan of string) +{ + sys->pctl(Sys->NEWFD, 0::1::2::nil); + ctxt := Context.new(drawctxt); + <-sync; + if(<-sync != nil){ + c.sync <-= 0; + errorc <-= nil; + exit; + } + c.sync <-= 1; + argv := ref Sh->Listnode(cmd, nil) :: nil; + + fl: list of ref Sh->Listnode; + nf := 0; + while(((d, p, nil) := <-c.c).t0 != nil){ + fl = ref Sh->Listnode(nil, p) :: fl; + if(++nf >= n){ + ctxt.set("file", rev(fl)); + if(pflag) + setstatenv(ctxt, d, pflag); + fl = nil; + nf = 0; + {ctxt.run(argv, 0);} exception {"fail:*" =>;} + } + } + if(nf > 0){ + ctxt.set("file", rev(fl)); + {ctxt.run(argv, 0);} exception {"fail:*" =>;} + } + if(tcmd != nil){ + ctxt.set("file", nil); + {ctxt.run(ref Sh->Listnode(tcmd, nil) :: nil, 0);} exception {"fail:*" =>;} + } + errorc <-= nil; + sync <-= nil; # XXX should return result here... +} + +setenv(ctxt: ref Context, var: string, val: list of string) +{ + ctxt.set(var, sh->stringlist2list(val)); +} + +setstatenv(ctxt: ref Context, dir: ref Sys->Dir, pflag: int) +{ + setenv(ctxt, "mode", modes(dir.mode) :: nil); + setenv(ctxt, "uid", dir.uid :: nil); + setenv(ctxt, "mtime", string dir.mtime :: nil); + setenv(ctxt, "length", string dir.length :: nil); + + if(pflag > 1){ + setenv(ctxt, "name", dir.name :: nil); + setenv(ctxt, "gid", dir.gid :: nil); + setenv(ctxt, "muid", dir.muid :: nil); + setenv(ctxt, "qid", sys->sprint("16r%ubx", dir.qid.path) :: string dir.qid.vers :: nil); + setenv(ctxt, "atime", string dir.atime :: nil); + setenv(ctxt, "dtype", sys->sprint("%c", dir.dtype) :: nil); + setenv(ctxt, "dev", string dir.dev :: nil); + } +} + +mtab := array[] of { + "---", "--x", "-w-", "-wx", + "r--", "r-x", "rw-", "rwx" +}; + +modes(mode: int): string +{ + s: string; + + if(mode & Sys->DMDIR) + s = "d"; + else if(mode & Sys->DMAPPEND) + s = "a"; + else if(mode & Sys->DMAUTH) + s = "A"; + else + s = "-"; + if(mode & Sys->DMEXCL) + s += "l"; + else + s += "-"; + s += mtab[(mode>>6)&7]+mtab[(mode>>3)&7]+mtab[mode&7]; + return s; +} + +rev[T](x: list of T): list of T +{ + l: list of T; + for(; x != nil; x = tl x) + l = hd x :: l; + return l; +} diff --git a/appl/alphabet/fs/filter.b b/appl/alphabet/fs/filter.b new file mode 100644 index 00000000..af696e2f --- /dev/null +++ b/appl/alphabet/fs/filter.b @@ -0,0 +1,66 @@ +implement Filter, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; +include "alphabet/fs.m"; + fsfilter: Fsfilter; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Filter: module {}; + +Query: adt { + gate: Gatechan; + dflag: int; + reply: chan of int; + query: fn(q: self ref Query, d: ref Sys->Dir, name: string, depth: int): int; +}; + +types(): string +{ + return "xxp-d"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fsfilter = load Fsfilter Fsfilter->PATH; + if(fsfilter == nil) + badmod(Fsfilter->PATH); +} + +run(nil: ref Draw->Context, nil: ref Reports->Report, + opts: list of Option, args: list of ref Value): ref Value +{ + dst := chan of (Fsdata, chan of int); + spawn filterproc((hd args).x().i, dst, (hd tl args).p().i, opts != nil); + return ref Value.Vx(dst); +} + +filterproc(src, dst: Fschan, gate: Gatechan, dflag: int) +{ + fsfilter->filter(ref Query(gate, dflag, chan of int), src, dst); + gate <-= ((nil, nil, 0), nil); +} + +Query.query(q: self ref Query, d: ref Sys->Dir, name: string, depth: int): int +{ + if(depth == 0 || (q.dflag && (d.mode & Sys->DMDIR))) + return 1; + q.gate <-= ((d, name, depth), q.reply); + return <-q.reply; +} diff --git a/appl/alphabet/fs/ls.b b/appl/alphabet/fs/ls.b new file mode 100644 index 00000000..fdc2ddb0 --- /dev/null +++ b/appl/alphabet/fs/ls.b @@ -0,0 +1,107 @@ +implement Ls, Fsmodule; +include "sys.m"; + sys: Sys; +include "daytime.m"; + daytime: Daytime; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report: import reports; +include "alphabet/fs.m"; + fs: Fs; + Option, Value, Entrychan: import fs; + +Ls: module {}; + +types(): string +{ + return "ft-u-m"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: ls: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + reports = load Reports Reports->PATH; + if(reports == nil) + badmod(Reports->PATH); + if(fs == nil) + badmod(Fs->PATH); + fs->init(); + daytime = load Daytime Daytime->PATH; + if(daytime == nil) + badmod(Daytime->PATH); +} + +run(nil: ref Draw->Context, report: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + f := chan of ref Sys->FD; + spawn lsproc(f, opts, (hd args).t().i, report.start("/fs/ls")); + return ref Value.Vf(f); +} + +lsproc(f: chan of ref Sys->FD, opts: list of Option, c: Entrychan, errorc: chan of string) +{ + f <-= nil; + if((fd := <-f) == nil){ + c.sync <-= 0; + reports->quit(errorc); + } + now := daytime->now(); + mflag := uflag := 0; + c.sync <-= 1; + for(; opts != nil; opts = tl opts){ + case (hd opts).opt { + 'm' => + mflag = 1; + 'u' => + uflag = 1; + } + } + while(((dir, p, nil) := <-c.c).t0 != nil){ + t := dir.mtime; + if(uflag) + t = dir.atime; + s := sys->sprint("%s %c %d %s %s %bud %s %s\n", + modes(dir.mode), dir.dtype, dir.dev, + dir.uid, dir.gid, dir.length, + daytime->filet(now, dir.mtime), p); + if(mflag) + s = "[" + dir.muid + "] " + s; + sys->fprint(fd, "%s", s); + } + reports->quit(errorc); +} + +mtab := array[] of { + "---", "--x", "-w-", "-wx", + "r--", "r-x", "rw-", "rwx" +}; + +modes(mode: int): string +{ + s: string; + + if(mode & Sys->DMDIR) + s = "d"; + else if(mode & Sys->DMAPPEND) + s = "a"; + else if(mode & Sys->DMAUTH) + s = "A"; + else + s = "-"; + if(mode & Sys->DMEXCL) + s += "l"; + else + s += "-"; + s += mtab[(mode>>6)&7]+mtab[(mode>>3)&7]+mtab[mode&7]; + return s; +} diff --git a/appl/alphabet/fs/match.b b/appl/alphabet/fs/match.b new file mode 100644 index 00000000..3a82de49 --- /dev/null +++ b/appl/alphabet/fs/match.b @@ -0,0 +1,84 @@ +implement Match, Fsmodule; +include "sys.m"; + sys: Sys; +include "filepat.m"; + filepat: Filepat; +include "regex.m"; + regex: Regex; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Match: module {}; + +types(): string +{ + return "ps-a-r"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); + regex = load Regex Regex->PATH; + if(regex == nil) + badmod(Regex->PATH); + filepat = load Filepat Filepat->PATH; + if(filepat == nil) + badmod(Filepat->PATH); +} + +run(nil: ref Draw->Context, nil: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + pat := (hd args).s().i; + aflag := rflag := 0; + for(; opts != nil; opts = tl opts){ + case (hd opts).opt { + 'a' => + aflag = 1; + 'r' => + rflag = 1; + } + } + v := ref Value.Vp(chan of Gatequery); + re: Regex->Re; + if(rflag){ + err: string; + (re, err) = regex->compile(pat, 0); + if(re == nil){ + sys->fprint(sys->fildes(2), "fs: match: regex error on %#q: %s\n", pat, err); + return nil; + } + } + spawn matchproc(v.i, aflag, pat, re); + return v; +} + +matchproc(c: Gatechan, all: int, pat: string, re: Regex->Re) +{ + while((((d, name, nil), reply) := <-c).t0.t0 != nil){ + if(all == 0) + name = d.name; + if(re != nil) + reply <-= regex->execute(re, name) != nil; # XXX should anchor it? + else + reply <-= filepat->match(pat, name); + } +} diff --git a/appl/alphabet/fs/merge.b b/appl/alphabet/fs/merge.b new file mode 100644 index 00000000..62524de5 --- /dev/null +++ b/appl/alphabet/fs/merge.b @@ -0,0 +1,192 @@ +implement Merge, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Cmpchan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Merge: module {}; + +# e.g.... +# fs select {mode -d} {merge -c {compose -d AoutB} {filter {not {path /chan /dev /usr/rog /n/local /net}} /} {merge {proto FreeBSD} {proto Hp} {proto Irix} {proto Linux} {proto MacOSX} {proto Nt} {proto Nt.ti} {proto Nt.ti925} {proto Plan9} {proto Plan9.ti} {proto Plan9.ti925} {proto Solaris} {proto authsrv} {proto dl} {proto dlsrc} {proto ep7} {proto inferno} {proto inferno.ti} {proto ipaqfs} {proto minitel} {proto os} {proto scheduler.client} {proto scheduler.server} {proto sds} {proto src} {proto src.ti} {proto sword} {proto ti925.ti} {proto ti925bin} {proto tipaq} {proto umec} {proto utils} {proto utils.ti}}} >[2] /dev/null + +types(): string +{ + return "xxxx*-1-cm"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil){ + sys->fprint(sys->fildes(2), "fs: cannot load %s: %r\n", Fs->PATH); + raise "fail:bad module"; + } + fs->init(); +} + +run(nil: ref Draw->Context, nil: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + recurse := 1; + cmp: Cmpchan; + for(; opts != nil; opts = tl opts){ + case (hd opts).opt { + '1' => + recurse = 0; + 'c' => + cmp = (hd (hd opts).args).m().i; + } + } + dst := chan of (Fsdata, chan of int); + spawn mergeproc((hd args).x().i, (hd tl args).x().i, dst, recurse, cmp, tl tl args == nil); + for(args = tl tl args; args != nil; args = tl args){ + dst1 := chan of (Fsdata, chan of int); + spawn mergeproc(dst, (hd args).x().i, dst1, recurse, cmp, tl args == nil); + dst = dst1; + } + return ref Value.Vx(dst); +} + +# merge two trees; assume directories are alphabetically sorted. +mergeproc(c0, c1, dst: Fschan, recurse: int, cmp: Cmpchan, killcmp: int) +{ + myreply := chan of int; + ((d0, nil), reply0) := <-c0; + ((d1, nil), reply1) := <-c1; + + if(compare(cmp, d0, d1) == 2r10) + dst <-= ((d1, nil), myreply); + else + dst <-= ((d0, nil), myreply); + r := <-myreply; + reply0 <-= r; + reply1 <-= r; + if(r == Down){ + { + mergedir(c0, c1, dst, recurse, cmp); + } exception {"exit" =>;} + } + if(cmp != nil && killcmp) + cmp <-= (nil, nil, nil); +} + +mergedir(c0, c1, dst: Fschan, recurse: int, cmp: Cmpchan) +{ + myreply := chan of int; + reply0, reply1: chan of int; + d0, d1: ref Sys->Dir; + eof0 := eof1 := 0; + for(;;){ + if(!eof0 && d0 == nil){ + ((d0, nil), reply0) = <-c0; + if(d0 == nil){ + reply0 <-= Next; + eof0 = 1; + } + } + if(!eof1 && d1 == nil){ + ((d1, nil), reply1) = <-c1; + if(d1 == nil){ + reply1 <-= Next; + eof1 = 1; + } + } + if(eof0 && eof1) + break; + + (wd0, wd1) := (d0, d1); + if(d0 != nil && d1 != nil && d0.name != d1.name){ + if(d0.name < d1.name) + wd1 = nil; + else + wd0 = nil; + } + + wc0, wc1: Fschan; + wreply0, wreply1: chan of int; + weof0, weof1: int; + + c := compare(cmp, wd0, wd1); + if(wd0 != nil && wd1 != nil){ + if(c != 0 && recurse && (wd0.mode & wd1.mode & Sys->DMDIR) != 0){ + dst <-= ((wd0, nil), myreply); + r := <-myreply; + reply0 <-= r; + reply1 <-= r; + d0 = d1 = nil; + case r { + Quit => + raise "exit"; + Skip => + return; + Down => + mergedir(c0, c1, dst, 1, cmp); + } + continue; + } + # when we can't merge and there's a clash, choose c0 over c1, unless cmp says otherwise + if(c == 2r10){ + reply0 <-= Next; + d0 = nil; + }else{ + reply1 <-= Next; + d1 = nil; + } + } + if(c & 2r01){ + (wd0, wc0, wreply0, weof0) = (d0, c0, reply0, eof0); + (wd1, wc1, wreply1, weof1) = (d1, c1, reply1, eof1); + d0 = nil; + }else if(c & 2r10){ + (wd0, wc0, wreply0, weof0) = (d1, c1, reply1, eof1); + (wd1, wc1, wreply1, weof1) = (d0, c0, reply0, eof0); + d1 = nil; + }else{ + if(wd0 == nil){ + reply1 <-= Next; + d1 = nil; + }else{ + reply0 <-= Next; + d0 = nil; + } + continue; + } + dst <-= ((wd0, nil), myreply); + r := <-myreply; + wreply0 <-= r; + if(r == Down) + r = fs->copy(wc0, dst); # XXX hmm, maybe this should be a mergedir() + case r { + Quit or + Skip => + if(wd1 == nil && !weof1) + (nil, wreply1) = <-wc1; + wreply1 <-= r; + if(r == Quit) + raise "exit"; + return; + } + } + dst <-= ((nil, nil), myreply); + if(<-myreply == Quit) + raise "exit"; +} + +compare(cmp: Cmpchan, d0, d1: ref Sys->Dir): int +{ + mask := (d0 != nil) | (d1 != nil) << 1; + if(cmp == nil) + return mask; + reply := chan of int; + cmp <-= (d0, d1, reply); + return <-reply & mask; +} diff --git a/appl/alphabet/fs/mergewrite.b b/appl/alphabet/fs/mergewrite.b new file mode 100644 index 00000000..04e8cebb --- /dev/null +++ b/appl/alphabet/fs/mergewrite.b @@ -0,0 +1,244 @@ +implement Mergewrite, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "readdir.m"; + readdir: Readdir; +include "alphabet/reports.m"; + reports: Reports; + Report, report, quit: import reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Cmpchan, Option, + Next, Down, Skip, Quit: import Fs; + +Mergewrite: module {}; + +types(): string +{ + return "rxsm-v-n"; +} + +VERBOSE, NOWRITE, ASSUME: con 1<PATH; + readdir = load Readdir Readdir->PATH; + if(readdir == nil){ + sys->fprint(sys->fildes(2), "fs: mergewrite: cannot load %s: %r\n", Readdir->PATH); + raise "fail:bad module"; + } + readdir->init(nil, 0); + + fs = load Fs Fs->PATH; + if(fs == nil){ + sys->fprint(sys->fildes(2), "fs: mergewrite: cannot load %s: %r\n", Fs->PATH); + raise "fail:bad module"; + } + reports = load Reports Reports->PATH; + if(reports == nil){ + sys->fprint(sys->fildes(2), "fs: mergewrite: cannot load %s: %r\n", Reports->PATH); + raise "fail:bad module"; + } +} + +run(nil: ref Draw->Context, report: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + sync := chan of string; + flags := 0; + for(; opts != nil; opts = tl opts){ + case (hd opts).opt { + 'n' => + flags |= NOWRITE; + 'v' => + flags |= VERBOSE; + } + } + + spawn fswriteproc(sync, flags, (hd args).x().i, (hd tl args).s().i, (hd tl tl args).m().i, report.start("mergewrite")); + sync <-= nil; + return ref Value.Vr(sync); +} + +fswriteproc(sync: chan of string, flags: int, c: Fschan, root: string, cmp: Cmpchan, errorc: chan of string) +{ + sys->pctl(Sys->FORKNS, nil); + <-sync; + if(<-sync != nil){ + (<-c).t1 <-= Quit; + quit(errorc); + } + + ((d, nil), reply) := <-c; + if(root != nil){ + d = ref *d; + d.name = root; + } + fswritedir(d.name, cmp, d, reply, c, errorc, flags); + errorc <-= nil; + sync <-= nil; # XXX should return result here... +} + +fswritedir(path: string, cmp: Cmpchan, dir: ref Sys->Dir, dreply: chan of int, c: Fschan, + errorc: chan of string, flags: int) +{ + fd: ref Sys->FD; + if(dir.mode & Sys->DMDIR){ + made := 0; + if(flags&VERBOSE) + report(errorc, sys->sprint("create %q %uo", path, dir.mode)); + if(flags&NOWRITE){ + if(flags&ASSUME) + made = 1; + else{ + fd = sys->open(dir.name, Sys->OREAD); + if(fd == nil){ + made = 1; + flags |= ASSUME; + }else if(sys->chdir(dir.name) == -1){ + dreply <-= Next; + report(errorc, sys->sprint("cannot cd to %q: %r", path)); + return; + } + } + }else{ + fd = sys->create(dir.name, Sys->OREAD, dir.mode|8r300); + made = fd != nil; + if(fd == nil && (fd = sys->open(dir.name, Sys->OREAD)) == nil){ + dreply <-= Next; + report(errorc, sys->sprint("cannot create %q, mode %uo: %r", path, dir.mode|8r300)); + return; + } + # XXX if we haven't just made it, we should chmod the old entry u+w to enable writing. + if(sys->chdir(dir.name) == -1){ # XXX beware of names starting with '#' + dreply <-= Next; + report(errorc, sys->sprint("cannot cd to %q: %r", path)); + fd = nil; + sys->remove(dir.name); + return; + } + } + dreply <-= Down; + entries: array of ref Sys->Dir; + if(made == 0) + entries = readdir->readall(fd, Readdir->NAME|Readdir->COMPACT).t0; + i := 0; + eod := 0; + d0, d1: ref Sys->Dir; + reply: chan of int; + path[len path] = '/'; + for(;;){ + if(!eod && d0 == nil){ + ((d0, nil), reply) = <-c; + if(d0 == nil){ + reply <-= Next; + eod = 1; + } + } + if(d1 == nil && i < len entries) + d1 = entries[i++]; + if(d0 == nil && d1 == nil) + break; + + (wd0, wd1) := (d0, d1); + if(d0 != nil && d1 != nil && d0.name != d1.name){ + if(d0.name < d1.name) + wd1 = nil; + else + wd0 = nil; + } + r := compare(cmp, wd0, wd1); + if(wd1 != nil){ + if((r & 2r10) == 0){ + if(flags&VERBOSE) + report(errorc, "removing "+path+wd1.name); + if((flags&NOWRITE)==0){ + if(wd1.mode & Sys->DMDIR) + rmdir(wd1.name); + else + remove(wd1.name); + } + } + d1 = nil; + } + if(wd0 != nil){ + if((r & 2r01) == 0) + reply <-= Next; + else + fswritedir(path + wd0.name, cmp, d0, reply, c, errorc, flags); + d0 = nil; + } + } + if((flags&ASSUME)==0) + sys->chdir(".."); + if((flags&NOWRITE)==0){ + if((dir.mode & 8r300) != 8r300){ + ws := Sys->nulldir; + ws.mode = dir.mode; + if(sys->fwstat(fd, ws) == -1) + report(errorc, sys->sprint("cannot wstat %q: %r", path)); + } + } + }else{ + if(flags&VERBOSE) + report(errorc, sys->sprint("create %q %uo", path, dir.mode)); + if(flags&NOWRITE){ + dreply <-= Next; + return; + } + fd = sys->create(dir.name, Sys->OWRITE, dir.mode); + if(fd == nil){ + dreply <-= Next; + report(errorc, sys->sprint("cannot create %q, mode %uo: %r", path, dir.mode|8r300)); + return; + } + dreply <-= Down; + while((((nil, buf), reply) := <-c).t0.data != nil){ + nw := sys->write(fd, buf, len buf); + if(nw < len buf){ + if(nw == -1) + errorc <-= sys->sprint("error writing %q: %r", path); + else + errorc <-= sys->sprint("short write"); + reply <-= Skip; + break; + } + reply <-= Next; + } + reply <-= Next; + } +} + +rmdir(name: string) +{ + (d, n) := readdir->init(name, Readdir->NONE|Readdir->COMPACT); + for(i := 0; i < n; i++){ + path := name+"/"+d[i].name; + if(d[i].mode & Sys->DMDIR) + rmdir(path); + else + remove(path); + } + remove(name); +} + +remove(name: string) +{ + if(sys->remove(name) < 0) + sys->fprint(sys->fildes(2), "mergewrite: cannot remove %q: %r\n", name); +} + +compare(cmp: Cmpchan, d0, d1: ref Sys->Dir): int +{ + mask := (d0 != nil) | (d1 != nil) << 1; + if(cmp == nil) + return mask; + reply := chan of int; + cmp <-= (d0, d1, reply); + return <-reply & mask; +} diff --git a/appl/alphabet/fs/mkext.b b/appl/alphabet/fs/mkext.b new file mode 100644 index 00000000..b916aebc --- /dev/null +++ b/appl/alphabet/fs/mkext.b @@ -0,0 +1,266 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "string.m"; + str: String; +include "bundle.m"; + bundle: Bundle; +include "draw.m"; +include "sh.m"; +include "alphabet/fs.m"; + fslib: Fs; + Report, Value,quit, report: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Quit, Next, Skip, Down, + Option: import Fs; + +to do... +read file. if non-seekable, make temporary copy. +record offsets of all files +sort by filename +output in proper order. + + +types(): string +{ + return "xs"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: exec: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fs Fs->PATH; + if(fslib == nil) + badmod(Fs->PATH); + bufio = load Bufio Bufio->PATH; + if(bufio == nil) + badmod(Bufio->PATH); + str = load String String->PATH; + if(str == nil) + badmod(String->PATH); + bundle = load Bundle Bundle->PATH; + if(bundle == nil) + badmod(Bundle->PATH); + bundle->init(); +} + +run(nil: ref Draw->Context, report: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + p := (hd args).s().i; + iob: ref Bufio->Iobuf; + if(p == "-") + iob = bufio->fopen(sys->fildes(0), Sys->OREAD); + else + iob = bufio->open(p, Sys->OREAD); + if(iob == nil){ + sys->fprint(sys->fildes(2), "fs: unbundle: cannot open %q: %r\n", p); + return nil; + } + seekable := p != "-"; + if(seekable) + seekable = isseekable(iob.fd); + if( + return ref Value.Vx(mkext(report, iob, seekable, Sys->ATOMICIO)); +} + +# dodgy heuristic... avoid, or using the stat-length of pipes and net connections +isseekable(fd: ref Sys->FD): int +{ + (ok, stat) := sys->fstat(fd); + if(ok != -1 && stat.dtype == '|' || stat.dtype == 'I') + return 0; + return 1; +} + +mkext(r: ref Report, iob: ref Iobuf, seekable, blocksize: int): Fschan +{ + c := chan of (Fsdata, chan of int); + spawn unbundleproc(iob, c, seekable, blocksize, r.start("bundle")); + return c; +} + +EOF: con "end of archive\n"; + +unbundleproc(iob: ref Iobuf, c: Fschan, seekable, blocksize: int, errorc: chan of string) +{ + reply := chan of int; + p := iob.gets('\n'); + # XXX overall header? + if(p == nil || p == EOF){ + fslib->sendnulldir(c); + quit(errorc); + } + d := header2dir(p); + if(d == nil){ + fslib->sendnulldir(c); + report(errorc, "invalid first header"); + quit(errorc); + } + if((d.mode & Sys->DMDIR) == 0){ + fslib->sendnulldir(c); + report(errorc, "first entry is not a directory"); + quit(errorc); + } + c <-= ((d, nil), reply); + case r := <-reply { + Down => + unbundledir(iob, c, 0, seekable, blocksize, errorc); + c <-= ((nil, nil), reply); + <-reply; + Skip or + Next => + unbundledir(iob, c, 1, seekable, blocksize, errorc); + Quit => + break; + } + quit(errorc); +} + +unbundledir(iob: ref Iobuf, c: Fschan, + skipping, seekable, blocksize: int, errorc: chan of string): int +{ + reply := chan of int; + while((p := iob.gets('\n')) != nil){ + if(p == EOF) + break; + if(p[0] == '\n') + break; + d := header2dir(p); + if(d == nil){ + report(errorc, sys->sprint("invalid bundle header %q", p[0:len p - 1])); + return -1; + } + if(d.mode & Sys->DMDIR){ + if(skipping) + continue; + c <-= ((d, nil), reply); + case <-reply { + Quit => + quit(errorc); + Down => + r := unbundledir(iob, c, 0, seekable, blocksize, errorc); + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + if(r == -1) + return -1; + Skip => + if(unbundledir(iob, c, 1, seekable, blocksize, errorc) == -1) + return -1; + skipping = 1; + Next => + if(unbundledir(iob, c, 1, seekable, blocksize, errorc) == -1) + return -1; + } + }else{ + if(skipping){ + if(skipdata(iob, d.length, seekable) == -1) + return -1; + }else{ + case unbundlefile(iob, d, c, errorc, seekable, blocksize) { + -1 => + return -1; + Skip => + skipping = 1; + } + } + } + } + if(p == nil) + report(errorc, "unexpected eof"); + return 0; +} + +skipdata(iob: ref Iobuf, length: big, seekable: int): int +{ + if(seekable){ + iob.seek(big length, Sys->SEEKRELA); + return 0; + } + buf := array[Sys->ATOMICIO] of byte; + for(n := big 0; n < length; ){ + nb := Sys->ATOMICIO; + if(length - n < big Sys->ATOMICIO) + nb = int (length - n); + nb = iob.read(buf, nb); + if(nb <= 0) + return -1; + n += big nb; + } + return 0; +} + +unbundlefile(iob: ref Iobuf, d: ref Sys->Dir, + c: Fschan, errorc: chan of string, seekable, blocksize: int): int +{ + reply := chan of int; + c <-= ((d, nil), reply); + case <-reply { + Quit => + quit(errorc); + Skip => + if(skipdata(iob, d.length, seekable) == -1) + return -1; + return Skip; + Next => + if(skipdata(iob, d.length, seekable) == -1) + return -1; + return Next; + } + length := d.length; + for(n := big 0; n < length; ){ + nr := blocksize; + if(n + big blocksize > length) + nr = int (length - n); + buf := array[nr] of byte; + nr = iob.read(buf, nr); + if(nr <= 0){ + if(nr < 0) + report(errorc, sys->sprint("read error: %r")); + else + report(errorc, sys->sprint("premature eof")); + return -1; + }else if(nr < len buf) + buf = buf[0:nr]; + c <-= ((nil, buf), reply); + n += big nr; + case <-reply { + Quit => + quit(errorc); + Skip => + if(skipdata(iob, length - n, seekable) == -1) + return -1; + return Next; + } + } + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + return Next; +} + +header2dir(s: string): ref Sys->Dir +{ + toks := str->unquoted(s); + nf := len toks; + if(nf != 6) + return nil; + d := ref Sys->nulldir; + (d.name, toks) = (hd toks, tl toks); + (d.mode, toks) = (str->toint(hd toks, 8).t0, tl toks); + (d.uid, toks) = (hd toks, tl toks); + (d.gid, toks) = (hd toks, tl toks); + (d.mtime, toks) = (int hd toks, tl toks); + (d.length, toks) = (big hd toks, tl toks); + return d; +} diff --git a/appl/alphabet/fs/mkfile b/appl/alphabet/fs/mkfile new file mode 100644 index 00000000..684a3650 --- /dev/null +++ b/appl/alphabet/fs/mkfile @@ -0,0 +1,55 @@ +<../../../mkconfig + +TARG=\ + and.dis\ + bundle.dis\ + chstat.dis\ + compose.dis\ + depth.dis\ + entries.dis\ + exec.dis\ + filter.dis\ + ls.dis\ + match.dis\ + merge.dis\ + mergewrite.dis\ + mode.dis\ + newer.dis\ + not.dis\ + or.dis\ + path.dis\ + pipe.dis\ + print.dis\ + proto.dis\ + query.dis\ + run.dis\ + select.dis\ + setroot.dis\ + size.dis\ + unbundle.dis\ + walk.dis\ + write.dis\ + +MODULES=\ + bundle.m\ + unbundle.m\ + +SYSMODULES=\ + alphabet/fs.m\ + alphabet/reports.m\ + bufio.m\ + bundle.m\ + daytime.m\ + draw.m\ + filepat.m\ + readdir.m\ + regex.m\ + sh.m\ + string.m\ + sys.m\ + unbundle.m\ + +DISBIN=$ROOT/dis/alphabet/fs + +<$ROOT/mkfiles/mkdis +LIMBOFLAGS=-F $LIMBOFLAGS diff --git a/appl/alphabet/fs/mode.b b/appl/alphabet/fs/mode.b new file mode 100644 index 00000000..974885c7 --- /dev/null +++ b/appl/alphabet/fs/mode.b @@ -0,0 +1,125 @@ +implement Mode, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Mode: module {}; + +# XXX implement octal modes. + +User: con 8r700; +Group: con 8r070; +Other: con 8r007; +All: con User | Group | Other; + +Read: con 8r444; +Write: con 8r222; +Exec: con 8r111; + +types(): string +{ + return "ps"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); +} + +run(nil: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + spec := (hd args).s().i; + (ok, mask, mode) := parsemode(spec); + if(ok == 0){ + sys->fprint(sys->fildes(2), "fs: mode: bad mode %#q\n", spec); + return nil; + } + c := chan of Gatequery; + spawn modegate(c, mask, mode); + return ref Value.Vp(c); +} + +modegate(c: Gatechan, mask, mode: int) +{ + m := mode & mask; + while((((d, nil, nil), reply) := <-c).t0.t0 != nil) + reply <-= ((d.mode & mask) ^ m) == 0; +} + +# stolen from /appl/cmd/chmod.b +parsemode(spec: string): (int, int, int) +{ + mask := Sys->DMAPPEND | Sys->DMEXCL | Sys->DMDIR | Sys->DMAUTH; +loop: + for(i := 0; i < len spec; i++){ + case spec[i] { + 'u' => + mask |= User; + 'g' => + mask |= Group; + 'o' => + mask |= Other; + 'a' => + mask |= All; + * => + break loop; + } + } + if(i == len spec) + return (0, 0, 0); + if(i == 0) + mask |= All; + + op := spec[i++]; + if(op != '+' && op != '-' && op != '=') + return (0, 0, 0); + + mode := 0; + for(; i < len spec; i++){ + case spec[i]{ + 'r' => + mode |= Read; + 'w' => + mode |= Write; + 'x' => + mode |= Exec; + 'a' => + mode |= Sys->DMAPPEND; + 'l' => + mode |= Sys->DMEXCL; + 'd' => + mode |= Sys->DMDIR; + 'A' => + mode |= Sys->DMAUTH; + * => + return (0, 0, 0); + } + } + if(op == '+' || op == '-') + mask &= mode; + if(op == '-') + mode = ~mode; + return (1, mask, mode); +} + + diff --git a/appl/alphabet/fs/newer.b b/appl/alphabet/fs/newer.b new file mode 100644 index 00000000..a278f70a --- /dev/null +++ b/appl/alphabet/fs/newer.b @@ -0,0 +1,64 @@ +implement Newer, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Cmpchan, Option: import Fs; + +Newer: module {}; + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +types(): string +{ + return "m-d"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); +} + +# select those items in A that are newer than those in B +# or those that exist in A that don't in B. +# if -d flag is given, select all directories in A too. +run(nil: ref Draw->Context, nil: ref Report, + opts: list of Option, nil: list of ref Value): ref Value +{ + c := chan of (ref Sys->Dir, ref Sys->Dir, chan of int); + spawn newer(c, opts != nil); + return ref Value.Vm(c); +} + +newer(c: Cmpchan, dflag: int) +{ + while(((d0, d1, reply) := <-c).t2 != nil){ + r: int; + if(d0 == nil) + r = 2r10; + else if(d1 == nil) + r = 2r01; + else if(dflag && (d0.mode & Sys->DMDIR)) + r = 2r11; + else { + if(d0.mtime > d1.mtime) + r = 2r01; + else + r= 2r10; + } + reply <-= r; + } +} diff --git a/appl/alphabet/fs/not.b b/appl/alphabet/fs/not.b new file mode 100644 index 00000000..571f18a3 --- /dev/null +++ b/appl/alphabet/fs/not.b @@ -0,0 +1,53 @@ +implement Not, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Not: module {}; + +types(): string +{ + return "pp"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); +} + +run(nil: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + c := chan of Gatequery; + spawn notgate(c, (hd args).p().i); + return ref Value.Vp(c); +} + +notgate(c, sub: Gatechan) +{ + myreply := chan of int; + while(((d, reply) := <-c).t0.t0 != nil){ + sub <-= (d, myreply); + reply <-= !<-myreply; + } + sub <-= (Nilentry, nil); +} diff --git a/appl/alphabet/fs/or.b b/appl/alphabet/fs/or.b new file mode 100644 index 00000000..7a103a0b --- /dev/null +++ b/appl/alphabet/fs/or.b @@ -0,0 +1,70 @@ +implement Or, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Or: module {}; + +types(): string +{ + return "pppp*"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); +} + +run(nil: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + c := chan of Gatequery; + spawn orgate(c, args); + return ref Value.Vp(c); +} + +orgate(c: Gatechan, args: list of ref Value) +{ + sub: list of Gatechan; + for(; args != nil; args = tl args) + sub = (hd args).p().i :: sub; + sub = rev(sub); + myreply := chan of int; + while(((d, reply) := <-c).t0.t0 != nil){ + for(l := sub; l != nil; l = tl l){ + (hd l) <-= (d, myreply); + if(<-myreply) + break; + } + reply <-= l != nil; + } + for(; sub != nil; sub = tl sub) + hd sub <-= (Nilentry, nil); +} + +rev[T](x: list of T): list of T +{ + l: list of T; + for(; x != nil; x = tl x) + l = hd x :: l; + return l; +} diff --git a/appl/alphabet/fs/path.b b/appl/alphabet/fs/path.b new file mode 100644 index 00000000..1ed48378 --- /dev/null +++ b/appl/alphabet/fs/path.b @@ -0,0 +1,82 @@ +implement Path, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Path: module {}; + +types(): string +{ + return "pss*-x"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); +} + +run(nil: ref Draw->Context, nil: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + # XXX cleanname all paths? + c := chan of Gatequery; + p: list of string; + for(; args != nil; args = tl args) + p = (hd args).s().i :: p; + spawn pathgate(c, opts != nil, p); + return ref Value.Vp(c); +} + +pathgate(c: Gatechan, xflag: int, paths: list of string) +{ + if(xflag){ + while((((d, path, nil), reply) := <-c).t0.t0 != nil){ + for(q := paths; q != nil; q = tl q){ + r := 1; + p := hd q; + if(len path > len p) + r = path[len p] != '/' || path[0:len p] != p; + else if(len path == len p) + r = path != p; + if(r == 0) + break; + } + reply <-= q == nil; + } + }else{ + while((((d, path, nil), reply) := <-c).t0.t0 != nil){ + for(q := paths; q != nil; q = tl q){ + r := 0; + p := hd q; + if(len path > len p) + r = path[len p] == '/' && path[0:len p] == p; + else if(len path == len p) + r = path == p; + else + r = p[len path] == '/' && p[0:len path] == path; + if(r) + break; + } + reply <-= q != nil; + } + } +} diff --git a/appl/alphabet/fs/pipe.b b/appl/alphabet/fs/pipe.b new file mode 100644 index 00000000..9fe36ec7 --- /dev/null +++ b/appl/alphabet/fs/pipe.b @@ -0,0 +1,230 @@ +implement Pipe, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; + Context: import sh; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Option, Value, Fschan: import fs; + Skip, Next, Down, Quit: import fs; + +Pipe: module {}; + +# pipe the contents of the files in a filesystem through +# a command. -1 causes one command only to be executed. +# -p and -P (exclusive to -1) cause stat modes to be set in the shell environment. +types(): string +{ + return "rxc-1-p-P"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: exec: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); + sh = load Sh Sh->PATH; + if(sh == nil) + badmod(Sh->PATH); + sh->initialise(); +} + +run(drawctxt: ref Draw->Context, nil: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + n := 1; + oneflag := pflag := 0; + for(; opts != nil; opts = tl opts){ + o := hd opts; + case o.opt { + '1' => + oneflag = 1; + 'p' => + pflag = 1; + 'P' => + pflag = 2; + } + } + if(pflag && oneflag){ + sys->fprint(sys->fildes(2), "fs: exec: cannot specify -p with -1\n"); + return nil; + } + c := (hd args).x().i; + cmd := (hd tl args).c().i; + sync := chan of string; + spawn execproc(drawctxt, sync, oneflag, pflag, c, cmd); + sync <-= nil; + return ref Value.Vr(sync); +} + +execproc(drawctxt: ref Draw->Context, sync: chan of string, oneflag, pflag: int, + c: Fschan, cmd: ref Sh->Cmd) +{ + sys->pctl(Sys->NEWFD, 0::1::2::nil); + ctxt := Context.new(drawctxt); + <-sync; + if(<-sync != nil){ + (<-c).t1 <-= Quit; + exit; + } + argv := ref Sh->Listnode(cmd, nil) :: nil; + fd: ref Sys->FD; + result := chan of string; + if(oneflag){ + fd = popen(ctxt, argv, result); + if(fd == nil){ + (<-c).t1 <-= Quit; + sync <-= "cannot make pipe"; + exit; + } + } + + names: list of string; + name: string; + indent := 0; + for(;;){ + (d, reply) := <-c; + if(d.dir == nil){ + reply <-= Next; + if(--indent == 0){ + break; + } + (name, names) = (hd names, tl names); + continue; + } + if((d.dir.mode & Sys->DMDIR) != 0){ + reply <-= Down; + names = name :: names; + if(indent > 0 && name != nil && name[len name - 1] != '/') + name[len name] = '/'; + name += d.dir.name; + indent++; + continue; + } + if(!oneflag){ + p := name; + if(p != nil && p[len p - 1] != '/') + p[len p] = '/'; + setenv(ctxt, "file", p + d.dir.name :: nil); + if(pflag) + setstatenv(ctxt, d.dir, pflag); + fd = popen(ctxt, argv, result); + } + if(fd == nil){ + reply <-= Next; + continue; + } + reply <-= Down; + for(;;){ + data: array of byte; + ((nil, data), reply) = <-c; + reply <-= Next; + if(data == nil) + break; + n := -1; + {n = sys->write(fd, data, len data);}exception {"write on closed pipe" => ;} + if(n != len data){ + if(oneflag){ + (<-c).t1 <-= Quit; + sync <-= "truncated write"; + exit; + } + (<-c).t1 <-= Skip; + break; + } + } + if(!oneflag){ + fd = nil; + <-result; + } + } + fd = nil; + if(oneflag) + sync <-= <-result; + else + sync <-= nil; +} + +popen(ctxt: ref Context, argv: list of ref Sh->Listnode, result: chan of string): ref Sys->FD +{ + sync := chan of int; + fds := array[2] of ref Sys->FD; + sys->pipe(fds); + spawn runcmd(ctxt, argv, fds[0], sync, result); + <-sync; + return fds[1]; +} + +runcmd(ctxt: ref Context, argv: list of ref Sh->Listnode, stdin: ref Sys->FD, sync: chan of int, result: chan of string) +{ + sys->pctl(Sys->FORKFD, nil); + sys->dup(stdin.fd, 0); + stdin = nil; + sys->pctl(Sys->NEWFD, 0::1::2::nil); + ctxt = ctxt.copy(0); + sync <-= 0; + r := ctxt.run(argv, 0); + ctxt = nil; + sys->pctl(Sys->NEWFD, nil); + result <-=r; +} + +setenv(ctxt: ref Context, var: string, val: list of string) +{ + ctxt.set(var, sh->stringlist2list(val)); +} + +setstatenv(ctxt: ref Context, dir: ref Sys->Dir, pflag: int) +{ + setenv(ctxt, "mode", modes(dir.mode) :: nil); + setenv(ctxt, "uid", dir.uid :: nil); + setenv(ctxt, "mtime", string dir.mtime :: nil); + setenv(ctxt, "length", string dir.length :: nil); + + if(pflag > 1){ + setenv(ctxt, "name", dir.name :: nil); + setenv(ctxt, "gid", dir.gid :: nil); + setenv(ctxt, "muid", dir.muid :: nil); + setenv(ctxt, "qid", sys->sprint("16r%ubx", dir.qid.path) :: string dir.qid.vers :: nil); + setenv(ctxt, "atime", string dir.atime :: nil); + setenv(ctxt, "dtype", sys->sprint("%c", dir.dtype) :: nil); + setenv(ctxt, "dev", string dir.dev :: nil); + } +} + +mtab := array[] of { + "---", "--x", "-w-", "-wx", + "r--", "r-x", "rw-", "rwx" +}; + +modes(mode: int): string +{ + s: string; + + if(mode & Sys->DMDIR) + s = "d"; + else if(mode & Sys->DMAPPEND) + s = "a"; + else if(mode & Sys->DMAUTH) + s = "A"; + else + s = "-"; + if(mode & Sys->DMEXCL) + s += "l"; + else + s += "-"; + s += mtab[(mode>>6)&7]+mtab[(mode>>3)&7]+mtab[mode&7]; + return s; +} diff --git a/appl/alphabet/fs/print.b b/appl/alphabet/fs/print.b new file mode 100644 index 00000000..4c9bee59 --- /dev/null +++ b/appl/alphabet/fs/print.b @@ -0,0 +1,61 @@ +implement Print, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report: import reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Print: module {}; + +types(): string +{ + return "ft"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = load Reports Reports->PATH; + if(reports == nil) + badmod(Reports->PATH); + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); +} + +run(nil: ref Draw->Context, report: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + f := chan of ref Sys->FD; + spawn printproc(f, (hd args).t().i, report.start("/fs/print")); + return ref Value.Vf(f); +} + +printproc(f: chan of ref Sys->FD, c: Entrychan, errorc: chan of string) +{ + f <-= nil; + if((fd := <-f) == nil){ + c.sync <-= 0; + reports->quit(errorc); + } + c.sync <-= 1; + while(((d, p, nil) := <-c.c).t0 != nil) + sys->fprint(fd, "%s\n", p); + sys->fprint(fd, ""); + reports->quit(errorc); +} diff --git a/appl/alphabet/fs/proto.b b/appl/alphabet/fs/proto.b new file mode 100644 index 00000000..48e46d18 --- /dev/null +++ b/appl/alphabet/fs/proto.b @@ -0,0 +1,416 @@ +implement Proto, Fsmodule; +include "sys.m"; + sys: Sys; +include "readdir.m"; + readdir: Readdir; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "string.m"; + str: String; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, quit, report: import reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Proto: module {}; + +File: adt { + name: string; + mode: int; + owner: string; + group: string; + old: string; + flags: int; + sub: cyclic array of ref File; +}; + +Protof: adt { + indent: int; + lastline: string; + iob: ref Iobuf; +}; + +Star, Plus: con 1<fprint(sys->fildes(2), "fs: proto: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); + reports = load Reports Reports->PATH; + if(reports == nil) + badmod(Reports->PATH); + readdir = load Readdir Readdir->PATH; + if(readdir == nil) + badmod(Readdir->PATH); + bufio = load Bufio Bufio->PATH; + if(bufio == nil) + badmod(Bufio->PATH); + str = load String String->PATH; + if(str == nil) + badmod(String->PATH); +} + +run(nil: ref Draw->Context, report: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + f := (hd args).f().i; + rootpath: string; + if(opts != nil) + rootpath = (hd (hd opts).args).s().i; + if(rootpath == nil) + rootpath = "/"; + + root := ref File(rootpath, ~0, nil, nil, nil, 0, nil); + c := chan of (Fsdata, chan of int); + spawn protowalk(c, f, root, report.start("proto")); + return ref Value.Vx(c); +} + +protowalk(c: Fschan, f: chan of ref Sys->FD, root: ref File, errorc: chan of string) +{ + fd := <-f; + if(fd != nil) + f <-= nil; + else{ + sys->pipe(p := array[2] of ref Sys->FD); + f <-= p[1]; + fd = p[0]; + } + proto := ref Protof(0, nil, nil); + proto.iob = bufio->fopen(fd, Sys->OREAD); + (root.flags, root.sub) = readproto(proto, -1); + + d: ref Sys->Dir; + (ok, rd) := sys->stat(root.name); + if(ok != -1) + d = ref rd; + + protowalk1(c, root.flags, root.name, file2dir(root, d), root.sub, errorc); + quit(errorc); +} + +protowalk1(c: Fschan, flags: int, path: string, d: ref Sys->Dir, + sub: array of ref File, errorc: chan of string): int +{ + reply := chan of int; + c <-= ((d, nil), reply); + case r := <-reply { + Quit => + quit(errorc); + Next or + Skip => + return r; + } + (a, n) := readdir->init(path, Readdir->NAME|Readdir->COMPACT); + if(len a == 0){ + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + return Next; + } + j := 0; + preventry: string; + useentry: int; + for(i := 0; i < n; i += useentry){ + useentry = 1; + for(; j < len sub; j++){ + s := sub[j].name; + if(s == preventry){ + report(errorc, sys->sprint("duplicate entry %s", pathconcat(path, s))); + continue; # eliminate duplicates in proto + } + if(s >= a[i].name) + break; + # entry has not been found, but we've got a substitute version, + # so save the rest of the entries to match the rest of sub. + if(sub[j].old != nil){ + useentry = 0; + break; + } + report(errorc, sys->sprint("%s not found", pathconcat(path, s))); + } + foundsub := j < len sub && (sub[j].name == a[i].name || sub[j].old != nil); + if(foundsub || flags&Plus || + (flags&Star && (a[i].mode & Sys->DMDIR)==0)){ + f: ref File; + if(foundsub){ + f = sub[j++]; + preventry = f.name; + } + p: string; + d: ref Sys->Dir; + if(foundsub && f.old != nil){ + p = f.old; + (ok, xd) := sys->stat(p); + if(ok == -1){ + report(errorc, sys->sprint("cannot stat %q: %r", p)); + continue; + } + d = ref xd; + }else{ + p = pathconcat(path, a[i].name); + d = a[i]; + } + + d = file2dir(f, d); + r: int; + if((d.mode & Sys->DMDIR) == 0) + r = walkfile(c, p, d, errorc); + else if(flags & Plus) + r = protowalk1(c, Plus, p, d, nil, errorc); + else + r = protowalk1(c, f.flags, p, d, f.sub, errorc); + if(r == Skip) + return Next; + } + } + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + return Next; +} + +pathconcat(p, name: string): string +{ + if(p != nil && p[len p - 1] != '/') + p[len p] = '/'; + p += name; + return p; +} + +# from(ish) walk.b +walkfile(c: Fschan, path: string, d: ref Sys->Dir, errorc: chan of string): int +{ + reply := chan of int; + fd := sys->open(path, Sys->OREAD); + if(fd == nil){ + report(errorc, sys->sprint("cannot open %q: %r", path)); + return Next; + } + c <-= ((d, nil), reply); + case r := <-reply { + Quit => + quit(errorc); + Next or + Skip => + return r; + } + length := d.length; + for(n := big 0; n < length; ){ + nr := Sys->ATOMICIO; + if(n + big Sys->ATOMICIO > length) + nr = int (length - n); + buf := array[nr] of byte; + nr = sys->read(fd, buf, nr); + if(nr <= 0){ + if(nr < 0) + report(errorc, sys->sprint("error reading %q: %r", path)); + else + report(errorc, sys->sprint("%q is shorter than expected (%bd/%bd)", + path, n, length)); + break; + }else if(nr < len buf) + buf = buf[0:nr]; + c <-= ((nil, buf), reply); + case <-reply { + Quit => + quit(errorc); + Skip => + return Next; + } + n += big nr; + } + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + return Next; +} + +readproto(proto: ref Protof, indent: int): (int, array of ref File) +{ + a := array[10] of ref File; + n := 0; + flags := 0; + while((f := readline(proto, indent)) != nil){ + if(f.name == "*") + flags |= Star; + else if(f.name == "+") + flags |= Plus; + else{ + (f.flags, f.sub) = readproto(proto, proto.indent); + if(n == len a) + a = (array[n * 2] of ref File)[0:] = a; + a[n++] = f; + } + } + if(n < len a) + a = (array[n] of ref File)[0:] = a[0:n]; + mergesort(a, array[n] of ref File); + return (flags, a); +} + +readline(proto: ref Protof, indent: int): ref File +{ + s: string; + if(proto.lastline != nil){ + s = proto.lastline; + proto.lastline = nil; + }else if(proto.indent == -1) + return nil; + else if((s = proto.iob.gets('\n')) == nil){ + proto.indent = -1; + return nil; + } + spc := 0; + for(i := 0; i < len s; i++){ + c := s[i]; + if(c == ' ') + spc++; + else if(c == '\t') + spc += 8; + else + break; + } + if(i == len s || s[i] == '#' || s[i] == '\n') + return readline(proto, indent); # XXX sort out tail recursion! + if(spc <= indent){ + proto.lastline = s; + return nil; + } + proto.indent = spc; + (n, toks) := sys->tokenize(s, " \t\n"); + f := ref File(nil, ~0, nil, nil, nil, 0, nil); + (f.name, toks) = (getname(hd toks, 0), tl toks); + if(toks == nil) + return f; + (f.mode, toks) = (getmode(hd toks), tl toks); + if(toks == nil) + return f; + (f.owner, toks) = (getname(hd toks, 1), tl toks); + if(toks == nil) + return f; + (f.group, toks) = (getname(hd toks, 1), tl toks); + if(toks == nil) + return f; + (f.old, toks) = (hd toks, tl toks); + return f; +} + +mergesort(a, b: array of ref File) +{ + r := len a; + if (r > 1) { + m := (r-1)/2 + 1; + mergesort(a[0:m], b[0:m]); + mergesort(a[m:], b[m:]); + b[0:] = a; + for ((i, j, k) := (0, m, 0); i < m && j < r; k++) { + if(b[i].name > b[j].name) + a[k] = b[j++]; + else + a[k] = b[i++]; + } + if (i < m) + a[k:] = b[i:m]; + else if (j < r) + a[k:] = b[j:r]; + } +} + +getname(s: string, allowminus: int): string +{ + if(s == nil) + return nil; + if(allowminus && s == "-") + return nil; + if(s[0] == '$') + return getenv(s[1:]); + return s; +} + +getenv(s: string): string +{ + # XXX implement env variables + return nil; +} + +getmode(s: string): int +{ + s = getname(s, 1); + if(s == nil) + return ~0; + m := 0; + i := 0; + if(s[i] == 'd'){ + m |= Sys->DMDIR; + i++; + } + if(i < len s && s[i] == 'a'){ + m |= Sys->DMAPPEND; + i++; + } + if(i < len s && s[i] == 'l'){ + m |= Sys->DMEXCL; + i++; + } + (xmode, t) := str->toint(s, 8); + if(t != nil){ + # report(aux.errorc, "bad mode specification %q", s); + return ~0; + } + return xmode | m; +} + +file2dir(f: ref File, old: ref Sys->Dir): ref Sys->Dir +{ + d := ref Sys->nulldir; + if(old != nil){ + if(old.dtype != 'M'){ + d.uid = "sys"; + d.gid = "sys"; + xmode := (old.mode >> 6) & 7; + d.mode = old.mode | xmode | (xmode << 3); + }else{ + d.uid = old.uid; + d.gid = old.gid; + d.mode = old.mode; + } + d.length = old.length; + d.mtime = old.mtime; + d.atime = old.atime; + d.muid = old.muid; + d.name = old.name; + } + if(f != nil){ + d.name = f.name; + if(f.owner != nil) + d.uid = f.owner; + if(f.group != nil) + d.gid = f.group; + if(f.mode != ~0) + d.mode = f.mode; + } + return d; +} diff --git a/appl/alphabet/fs/query.b b/appl/alphabet/fs/query.b new file mode 100644 index 00000000..8d230707 --- /dev/null +++ b/appl/alphabet/fs/query.b @@ -0,0 +1,135 @@ +implement Query, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; + Context: import sh; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Option, Value, Gatechan, Gatequery, Nilentry: import fs; + +Query: module {}; + +types(): string +{ + return "pc-p-P"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: query: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); + sh = load Sh Sh->PATH; + if(sh == nil) + badmod(Sh->PATH); +} + +run(drawctxt: ref Draw->Context, nil: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + pflag := 0; + for(; opts != nil; opts = tl opts){ + o := hd opts; + case o.opt { + 'p' => + pflag = 1; + 'P' => + pflag = 2; + } + } + + v := ref Value.Vp(chan of Gatequery); + spawn querygate(drawctxt, v.i, (hd args).c().i, pflag); + v.i <-= (Nilentry, nil); + return v; +} + +querygate(drawctxt: ref Draw->Context, c: Gatechan, cmd: ref Sh->Cmd, pflag: int) +{ + sys->pctl(Sys->NEWFD, 0::1::2::nil); + ctxt := Context.new(drawctxt); + <-c; + argv := ref Sh->Listnode(cmd, nil) :: nil; + while((((d, p, nil), reply) := <-c).t0.t0 != nil){ + ctxt.set("file", ref Sh->Listnode(nil, p) :: nil); + if(pflag) + setstatenv(ctxt, d, pflag); + err := ""; + { + err = ctxt.run(argv, 0); + } exception e { + "fail:*" => + err = e; + } + reply <-= (err == nil); + } +} + +# XXX shouldn't duplicate this... + +setenv(ctxt: ref Context, var: string, val: list of string) +{ + ctxt.set(var, sh->stringlist2list(val)); +} + +setstatenv(ctxt: ref Context, dir: ref Sys->Dir, pflag: int) +{ + setenv(ctxt, "mode", modes(dir.mode) :: nil); + setenv(ctxt, "uid", dir.uid :: nil); + setenv(ctxt, "mtime", string dir.mtime :: nil); + setenv(ctxt, "length", string dir.length :: nil); + + if(pflag > 1){ + setenv(ctxt, "name", dir.name :: nil); + setenv(ctxt, "gid", dir.gid :: nil); + setenv(ctxt, "muid", dir.muid :: nil); + setenv(ctxt, "qid", sys->sprint("16r%ubx", dir.qid.path) :: string dir.qid.vers :: nil); + setenv(ctxt, "atime", string dir.atime :: nil); + setenv(ctxt, "dtype", sys->sprint("%c", dir.dtype) :: nil); + setenv(ctxt, "dev", string dir.dev :: nil); + } +} + +start(startc: chan of (string, chan of string), name: string): chan of string +{ + c := chan of string; + startc <-= (name, c); + return c; +} + +mtab := array[] of { + "---", "--x", "-w-", "-wx", + "r--", "r-x", "rw-", "rwx" +}; + +modes(mode: int): string +{ + s: string; + + if(mode & Sys->DMDIR) + s = "d"; + else if(mode & Sys->DMAPPEND) + s = "a"; + else if(mode & Sys->DMAUTH) + s = "A"; + else + s = "-"; + if(mode & Sys->DMEXCL) + s += "l"; + else + s += "-"; + s += mtab[(mode>>6)&7]+mtab[(mode>>3)&7]+mtab[mode&7]; + return s; +} diff --git a/appl/alphabet/fs/run.b b/appl/alphabet/fs/run.b new file mode 100644 index 00000000..6f6a38bb --- /dev/null +++ b/appl/alphabet/fs/run.b @@ -0,0 +1,65 @@ +implement Run, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; + Context: import sh; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Run: module {}; + +types(): string +{ + return "sc"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); + sh = load Sh Sh->PATH; + if(sh == nil) + badmod(Sh->PATH); + sh->initialise(); +} + +run(drawctxt: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + c := (hd args).c().i; + ctxt := Context.new(drawctxt); + ctxt.setlocal("s", nil); + { + ctxt.run(ref Sh->Listnode(c, nil)::nil, 0); + } exception e { + "fail:*" => + sys->fprint(sys->fildes(2), "fs: run: exception %q raised in %s\n", e[5:], sh->cmd2string(c)); + return nil; + } + sl := ctxt.get("s"); + if(sl == nil || tl sl != nil){ + sys->fprint(sys->fildes(2), "fs: run: $s has %d members; exactly one is required\n", len sl); + return nil; + } + s := (hd sl).word; + if(s == nil && (hd sl).cmd != nil) + s = sh->cmd2string((hd sl).cmd); + return ref Value.Vs(s); +} diff --git a/appl/alphabet/fs/select.b b/appl/alphabet/fs/select.b new file mode 100644 index 00000000..8a44104d --- /dev/null +++ b/appl/alphabet/fs/select.b @@ -0,0 +1,60 @@ +implement Select, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Select: module {}; +types(): string +{ + return "ttp"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); +} + +run(nil: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + dst := Entrychan(chan of int, chan of Entry); + spawn selectproc((hd args).t().i, dst, (hd tl args).p().i); + return ref Value.Vt(dst); +} + +selectproc(src, dst: Entrychan, query: Gatechan) +{ + if(<-dst.sync == 0){ + query <-= (Nilentry, nil); + src.sync <-= 0; + exit; + } + src.sync <-= 1; + reply := chan of int; + while((d := <-src.c).t0 != nil){ + query <-= (d, reply); + if(<-reply) + dst.c <-= d; + } + dst.c <-= Nilentry; + query <-= (Nilentry, nil); +} diff --git a/appl/alphabet/fs/setroot.b b/appl/alphabet/fs/setroot.b new file mode 100644 index 00000000..d04b7de5 --- /dev/null +++ b/appl/alphabet/fs/setroot.b @@ -0,0 +1,109 @@ +implement Setroot, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + Report: import Reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Setroot: module {}; + +# set the root +types(): string +{ + return "xxs-c"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); +} + +run(nil: ref Draw->Context, nil: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + root := (hd tl args).s().i; + if(root == nil && opts == nil){ + sys->fprint(sys->fildes(2), "fs: setroot: empty path\n"); + return nil; + } + v := ref Value.Vx(chan of (Fsdata, chan of int)); + spawn setroot((hd args).x().i, v.i, root, opts != nil); + return v; +} + +setroot(src, dst: Fschan, root: string, cflag: int) +{ + ((d, nil), reply) := <-src; + if(cflag){ + createroot(src, dst, root, d, reply); + }else{ + myreply := chan of int; + rd := ref *d; + rd.name = root; + dst <-= ((rd, nil), myreply); + if(<-myreply == Down){ + reply <-= Down; + fs->copy(src, dst); + } + } +} + +createroot(src, dst: Fschan, root: string, d: ref Sys->Dir, reply: chan of int) +{ + if(root == nil) + root = d.name; + (n, elems) := sys->tokenize(root, "/"); # XXX should really do a cleanname first + if(root[0] == '/'){ + elems = "/" :: elems; + n++; + } + myreply := chan of int; + lev := 0; + r := -1; + for(; elems != nil; elems = tl elems){ + rd := ref *d; + rd.name = hd elems; + dst <-= ((rd, nil), myreply); + case r = <-myreply { + Quit => + (<-src).t1 <-= Quit; + exit; + Skip => + break; + Next => + lev++; + break; + } + lev++; + } + if(r == Down){ + reply <-= Down; + if(fs->copy(src, dst) == Quit) + exit; + }else + reply <-= Quit; + while(lev-- > 1){ + dst <-= ((nil, nil), myreply); + if(<-myreply == Quit){ + (<-src).t1 <-= Quit; + exit; + } + } +} diff --git a/appl/alphabet/fs/size.b b/appl/alphabet/fs/size.b new file mode 100644 index 00000000..966bf957 --- /dev/null +++ b/appl/alphabet/fs/size.b @@ -0,0 +1,64 @@ +implement Size, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report: import reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Size: module {}; + +types(): string +{ + return "ft"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = load Reports Reports->PATH; + if(reports == nil) + badmod(Reports->PATH); + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); +} + +run(nil: ref Draw->Context, report: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + f := chan of ref Sys->FD; + spawn sizeproc(f, (hd args).t().i, report.start("size")); + return ref Value.Vf(f); +} + +sizeproc(f: chan of ref Sys->FD, c: Entrychan, errorc: chan of string) +{ + f <-= nil; + if((fd := <-f) == nil){ + c.sync <-= 0; + exit; + } + c.sync <-= 1; + + size := big 0; + while(((d, nil, nil) := <-c.c).t0 != nil) + size += d.length; + sys->fprint(fd, "%bd\n", size); + sys->fprint(fd, ""); + errorc <-= nil; +} diff --git a/appl/alphabet/fs/unbundle.b b/appl/alphabet/fs/unbundle.b new file mode 100644 index 00000000..3de7e7e2 --- /dev/null +++ b/appl/alphabet/fs/unbundle.b @@ -0,0 +1,259 @@ +implement Unbundle, Fsmodule; +include "sys.m"; + sys: Sys; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "string.m"; + str: String; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, quit, report: import reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Quit, Next, Skip, Down, + Option: import Fs; + +Unbundle: module {}; +types(): string +{ + return "xf"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: exec: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); + reports = load Reports Reports->PATH; + if(reports == nil) + badmod(Reports->PATH); + bufio = load Bufio Bufio->PATH; + if(bufio == nil) + badmod(Bufio->PATH); + str = load String String->PATH; + if(str == nil) + badmod(String->PATH); +} + +run(nil: ref Draw->Context, report: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + f := (hd args).f().i; + c := ref Value.Vx(chan of (Fsdata, chan of int)); + spawn unbundleproc((hd args).f().i, nil, c.i, -1, Sys->ATOMICIO, report.start("unbundle")); + return c; +} + +# dodgy heuristic... avoid, or using the stat-length of pipes and net connections +isseekable(fd: ref Sys->FD): int +{ + (ok, stat) := sys->fstat(fd); + if(ok != -1 && stat.dtype == '|' || stat.dtype == 'I') + return 0; + return 1; +} + +EOF: con "end of archive\n"; + +unbundleproc(f: chan of ref Sys->FD, iob: ref Iobuf, c: Fschan, + seekable, blocksize: int, errorc: chan of string) +{ + if(f != nil){ + fd := <-f; + if(fd == nil){ + sys->pipe(p := array[2] of ref Sys->FD); + f <-= p[1]; + p[1] = nil; + fd = p[0]; + }else + f <-= nil; + if(seekable == -1) + seekable = isseekable(fd); + iob = bufio->fopen(fd, Sys->OREAD); + f = nil; + } + + reply := chan of int; + p := iob.gets('\n'); + # XXX overall header? + if(p == nil || p == EOF){ + fs->sendnulldir(c); + quit(errorc); + } + d := header2dir(p); + if(d == nil){ + fs->sendnulldir(c); + report(errorc, sys->sprint("invalid first header %q", p[0:len p - 1])); + quit(errorc); + } + if((d.mode & Sys->DMDIR) == 0){ + fs->sendnulldir(c); + report(errorc, "first entry is not a directory"); + quit(errorc); + } + c <-= ((d, nil), reply); + case r := <-reply { + Down => + unbundledir(iob, c, 0, seekable, blocksize, errorc); + c <-= ((nil, nil), reply); + <-reply; + Skip or + Next => + unbundledir(iob, c, 1, seekable, blocksize, errorc); + Quit => + break; + } + quit(errorc); +} + +unbundledir(iob: ref Iobuf, c: Fschan, + skipping, seekable, blocksize: int, errorc: chan of string): int +{ + reply := chan of int; + while((p := iob.gets('\n')) != nil){ + if(p == EOF) + break; + if(p[0] == '\n') + break; + d := header2dir(p); + if(d == nil){ + report(errorc, sys->sprint("invalid bundle header %q", p[0:len p - 1])); + return -1; + } + if(d.mode & Sys->DMDIR){ + if(skipping) + continue; + c <-= ((d, nil), reply); + case <-reply { + Quit => + quit(errorc); + Down => + r := unbundledir(iob, c, 0, seekable, blocksize, errorc); + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + if(r == -1) + return -1; + Skip => + if(unbundledir(iob, c, 1, seekable, blocksize, errorc) == -1) + return -1; + skipping = 1; + Next => + if(unbundledir(iob, c, 1, seekable, blocksize, errorc) == -1) + return -1; + } + }else{ + if(skipping){ + if(skipdata(iob, d.length, seekable) == -1) + return -1; + }else{ + case unbundlefile(iob, d, c, errorc, seekable, blocksize) { + -1 => + return -1; + Skip => + skipping = 1; + } + } + } + } + if(p == nil) + report(errorc, "unexpected eof"); + return 0; +} + +skipdata(iob: ref Iobuf, length: big, seekable: int): int +{ + if(seekable){ + iob.seek(big length, Sys->SEEKRELA); + return 0; + } + buf := array[Sys->ATOMICIO] of byte; + for(n := big 0; n < length; ){ + nb := Sys->ATOMICIO; + if(length - n < big Sys->ATOMICIO) + nb = int (length - n); + nb = iob.read(buf, nb); + if(nb <= 0) + return -1; + n += big nb; + } + return 0; +} + +unbundlefile(iob: ref Iobuf, d: ref Sys->Dir, + c: Fschan, errorc: chan of string, seekable, blocksize: int): int +{ + reply := chan of int; + c <-= ((d, nil), reply); + case <-reply { + Quit => + quit(errorc); + Skip => + if(skipdata(iob, d.length, seekable) == -1) + return -1; + return Skip; + Next => + if(skipdata(iob, d.length, seekable) == -1) + return -1; + return Next; + } + length := d.length; + for(n := big 0; n < length; ){ + nr := blocksize; + if(n + big blocksize > length) + nr = int (length - n); + buf := array[nr] of byte; + nr = iob.read(buf, nr); + if(nr <= 0){ + if(nr < 0) + report(errorc, sys->sprint("read error: %r")); + else + report(errorc, sys->sprint("premature eof")); + return -1; + }else if(nr < len buf) + buf = buf[0:nr]; + c <-= ((nil, buf), reply); + n += big nr; + case <-reply { + Quit => + quit(errorc); + Skip => + if(skipdata(iob, length - n, seekable) == -1) + return -1; + return Next; + } + } + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + return Next; +} + +header2dir(s: string): ref Sys->Dir +{ + toks := str->unquoted(s); + nf := len toks; + if(nf != 6) + return nil; + d := ref Sys->nulldir; + (d.name, toks) = (hd toks, tl toks); + (d.mode, toks) = (str->toint(hd toks, 8).t0, tl toks); + (d.uid, toks) = (hd toks, tl toks); + (d.gid, toks) = (hd toks, tl toks); + (d.mtime, toks) = (int hd toks, tl toks); + (d.length, toks) = (big hd toks, tl toks); + return d; +} diff --git a/appl/alphabet/fs/unbundle.m b/appl/alphabet/fs/unbundle.m new file mode 100644 index 00000000..cb93005b --- /dev/null +++ b/appl/alphabet/fs/unbundle.m @@ -0,0 +1,9 @@ +Unbundle: module { + PATH: con "/dis/fs/bundle.dis"; + + types: fn(): string; + init: fn(); + run: fn(nil: ref Draw->Context, report: ref Report, + nil: list of Option, args: list of ref Value): ref Value; + unbundle: fn(r: ref Reports->Report, iob: ref Bufio->Iobuf, seekable: int, blocksize: int): Fs->Fschan; +}; diff --git a/appl/alphabet/fs/walk.b b/appl/alphabet/fs/walk.b new file mode 100644 index 00000000..d99d0e9f --- /dev/null +++ b/appl/alphabet/fs/walk.b @@ -0,0 +1,242 @@ +implement Walk, Fsmodule; +include "sys.m"; + sys: Sys; +include "readdir.m"; + readdir: Readdir; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, quit, report: import reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Walk: module {}; + +Loopcheck: adt { + a: array of list of ref Sys->Dir; + + new: fn(): ref Loopcheck; + enter: fn(l: self ref Loopcheck, d: ref Sys->Dir): int; + leave: fn(l: self ref Loopcheck, d: ref Sys->Dir); +}; + +types(): string +{ + return "xs-bs"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: walk: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); + reports = load Reports Reports->PATH; + if(reports == nil) + badmod(Reports->PATH); + readdir = load Readdir Readdir->PATH; + if(readdir == nil) + badmod(Readdir->PATH); +} + +run(nil: ref Draw->Context, report: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + path := (hd args).s().i; + (ok, d) := sys->stat(path); + if(ok== -1){ + sys->fprint(sys->fildes(2), "fs: walk: cannot stat %q: %r\n", path); + return nil; + } + if((d.mode & Sys->DMDIR) == 0){ + # XXX could produce an fs containing just the single file. + # would have to split the path though. + sys->fprint(sys->fildes(2), "fs: walk: %q is not a directory\n", path); + return nil; + } + sync := chan of int; + c := chan of (Fsdata, chan of int); + spawn fswalkproc(sync, path, c, Sys->ATOMICIO, report.start("walk")); + <-sync; + return ref Value.Vx(c); +} + +# XXX need to avoid loops in the filesystem... +fswalkproc(sync: chan of int, path: string, c: Fschan, blocksize: int, errorc: chan of string) +{ + sys->pctl(Sys->FORKNS, nil); + sync <-= 1; + # XXX could allow a single root file? + if(sys->chdir(path) == -1){ + report(errorc, sys->sprint("cannot cd to %q: %r", path)); + fs->sendnulldir(c); + quit(errorc); + } + (ok, d) := sys->stat("."); + if(ok == -1){ + report(errorc, sys->sprint("cannot stat %q: %r", path)); + fs->sendnulldir(c); + quit(errorc); + } + d.name = path; + reply := chan of int; + c <-= ((ref d, nil), reply); + if(<-reply == Down){ + loopcheck := Loopcheck.new(); + loopcheck.enter(ref d); + if(path[len path - 1] != '/') + path[len path] = '/'; + fswalkdir(path, c, blocksize, loopcheck, errorc); + c <-= ((nil, nil), reply); + <-reply; + } + quit(errorc); +} + +fswalkdir(path: string, c: Fschan, blocksize: int, loopcheck: ref Loopcheck, errorc: chan of string) +{ + reply := chan of int; + (a, n) := readdir->init(".", Readdir->NAME|Readdir->COMPACT); + if(n == -1){ + report(errorc, sys->sprint("cannot readdir %q: %r", path)); + return; + } + for(i := 0; i < n; i++) + if(a[i].mode & Sys->DMDIR) + if(loopcheck.enter(a[i]) == 0) + a[i].dtype = ~0; +directory: + for(i = 0; i < n; i++){ + if(a[i].mode & Sys->DMDIR){ + d := a[i]; + if(d.dtype == ~0){ + report(errorc, sys->sprint("filesystem loop at %#q", path + d.name)); + continue; + } + if(sys->chdir("./" + d.name) == -1){ + report(errorc, sys->sprint("cannot cd to %#q: %r", path + a[i].name)); + continue; + } + c <-= ((d, nil), reply); + case <-reply { + Quit => + quit(errorc); + Down => + fswalkdir(path + a[i].name + "/", c, blocksize, loopcheck, errorc); + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + Skip => + sys->chdir(".."); + i++; + break directory; + Next => + break; + } + if(sys->chdir("..") == -1) # XXX what should we do if this fails? + report(errorc, sys->sprint("failed to cd .. from %#q: %r\n", path + a[i].name)); + + } else { + if(fswalkfile(path, a[i], c, blocksize, errorc) == Skip) + break directory; + } + } + for(i = n - 1; i >= 0; i--) + if(a[i].mode & Sys->DMDIR && a[i].dtype != ~0) + loopcheck.leave(a[i]); +} + +fswalkfile(path: string, d: ref Sys->Dir, c: Fschan, blocksize: int, errorc: chan of string): int +{ + reply := chan of int; + fd := sys->open(d.name, Sys->OREAD); + if(fd == nil){ + report(errorc, sys->sprint("cannot open %q: %r", path+d.name)); + return Next; + } + c <-= ((d, nil), reply); + case <-reply { + Quit => + quit(errorc); + Skip => + return Skip; + Next => + return Next; + Down => + break; + } + length := d.length; + for(n := big 0; n < length; ){ + nr := blocksize; + if(n + big blocksize > length) + nr = int (length - n); + buf := array[nr] of byte; + nr = sys->read(fd, buf, nr); + if(nr <= 0){ + if(nr < 0) + report(errorc, sys->sprint("error reading %q: %r", path + d.name)); + else + report(errorc, sys->sprint("%q is shorter than expected (%bd/%bd)", + path + d.name, n, length)); + break; + }else if(nr < len buf) + buf = buf[0:nr]; + c <-= ((nil, buf), reply); + case <-reply { + Quit => + quit(errorc); + Skip => + return Next; + } + n += big nr; + } + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + return Next; +} + +HASHSIZE: con 32; + +issamedir(d0, d1: ref Sys->Dir): int +{ + (q0, q1) := (d0.qid, d1.qid); + return q0.path == q1.path && + q0.qtype == q1.qtype && + d0.dtype == d1.dtype && + d0.dev == d1.dev; +} + +Loopcheck.new(): ref Loopcheck +{ + return ref Loopcheck(array[HASHSIZE] of list of ref Sys->Dir); +} + +# XXX we're assuming no-one modifies the values in d behind our back... +Loopcheck.enter(l: self ref Loopcheck, d: ref Sys->Dir): int +{ + slot := int d.qid.path & (HASHSIZE-1); + for(ll := l.a[slot]; ll != nil; ll = tl ll) + if(issamedir(d, hd ll)) + return 0; + l.a[slot] = d :: l.a[slot]; + return 1; +} + +Loopcheck.leave(l: self ref Loopcheck, d: ref Sys->Dir) +{ + slot := int d.qid.path & (HASHSIZE-1); + l.a[slot] = tl l.a[slot]; +} diff --git a/appl/alphabet/fs/write.b b/appl/alphabet/fs/write.b new file mode 100644 index 00000000..272c2f71 --- /dev/null +++ b/appl/alphabet/fs/write.b @@ -0,0 +1,137 @@ +implement Write, Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Option, + Next, Down, Skip, Quit: import Fs; + +Write: module {}; +types(): string +{ + return "rxs-v"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: write: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Fs->PATH); + fs->init(); + reports = load Reports Reports->PATH; + if(reports == nil) + badmod(Reports->PATH); +} + +run(nil: ref Draw->Context, report: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + sync := chan of string; + spawn fswriteproc(sync, (hd tl args).s().i, (hd args).x().i, report.start("fswrite"), opts!=nil); + <-sync; + return ref Value.Vr(sync); +} + +fswriteproc(sync: chan of string, root: string, c: Fschan, errorc: chan of string, verbose: int) +{ + sys->pctl(Sys->FORKNS, nil); + sync <-= nil; + if(<-sync != nil){ + (<-c).t1 <-= Quit; + quit(sync, errorc); + } + + (d, reply) := <-c; + if(root != nil){ + d.dir = ref *d.dir; + d.dir.name = root; + } + fswritedir(d.dir.name, d, reply, c, errorc, verbose); + quit(sync, errorc); +} + +quit(sync: chan of string, errorc: chan of string) +{ + errorc <-= nil; + sync <-= nil; + exit; +} + +fswritedir(path: string, d: Fsdata, dreply: chan of int, c: Fschan, errorc: chan of string, verbose: int) +{ + fd: ref Sys->FD; + if(verbose) + report(errorc, sys->sprint("create %q %uo", path, d.dir.mode)); + if(d.dir.mode & Sys->DMDIR){ + created := 1; + fd = sys->create(d.dir.name, Sys->OREAD, d.dir.mode|8r777); + if(fd == nil){ + err := sys->sprint("%r"); + if((fd = sys->open(d.dir.name, Sys->OREAD)) == nil){ + dreply <-= Next; + report(errorc, sys->sprint("cannot create %q, mode %uo: %s", path, d.dir.mode|8r300, err)); + return; + }else + created = 0; + } + if(sys->chdir(d.dir.name) == -1){ # XXX beware of names starting with '#' + dreply <-= Next; + report(errorc, sys->sprint("cannot cd to %q: %r", path)); + fd = nil; + sys->remove(d.dir.name); + return; + } + dreply <-= Down; + path[len path] = '/'; + for(;;){ + (ent, reply) := <-c; + if(ent.dir == nil){ + reply <-= Next; + break; + } + fswritedir(path + ent.dir.name, ent, reply, c, errorc, verbose); + } + sys->chdir(".."); + if(created && (d.dir.mode & 8r777) != 8r777){ + ws := Sys->nulldir; + ws.mode = d.dir.mode; + if(sys->fwstat(fd, ws) == -1) + report(errorc, sys->sprint("cannot wstat %q: %r", path)); + } + }else{ + fd = sys->create(d.dir.name, Sys->OWRITE, d.dir.mode); + if(fd == nil){ + dreply <-= Next; + report(errorc, sys->sprint("cannot create %q, mode %uo: %r", path, d.dir.mode|8r300)); + return; + } + dreply <-= Down; + while((((nil, buf), reply) := <-c).t0.data != nil){ + nw := sys->write(fd, buf, len buf); + if(nw < len buf){ + if(nw == -1) + errorc <-= sys->sprint("error writing %q: %r", path); + else + errorc <-= sys->sprint("short write"); + reply <-= Skip; + break; + } + reply <-= Next; + } + reply <-= Next; + } +} diff --git a/appl/alphabet/fsdecl.sh b/appl/alphabet/fsdecl.sh new file mode 100644 index 00000000..9c63ff1d --- /dev/null +++ b/appl/alphabet/fsdecl.sh @@ -0,0 +1,13 @@ +load alphabet std + +typeset /fs + +declare /fs/walk +declare /fs/entries +declare /fs/match +declare /fs/print + +autoconvert /string /fs/fs /fs/walk +autoconvert /fs/fs /fs/entries /fs/entries +autoconvert /string /fs/gate /fs/match +autoconvert /fs/entries /fd /fs/print diff --git a/appl/alphabet/getendpoint.sh b/appl/alphabet/getendpoint.sh new file mode 100755 index 00000000..8d6cabd4 --- /dev/null +++ b/appl/alphabet/getendpoint.sh @@ -0,0 +1,13 @@ +#!/dis/sh -n +autoload=std +load std +if{! ~ $#* 1}{ + echo usage: getendpoint addr >[1=2] + raise usage +} +addr:=$1 +if{! ftest -e /n/endpoint/dsgdsfgeafreqeq}{ + mount {mntgen} /n/endpoint +} +mount -A $addr /n/endpoint/$addr +bind /n/endpoint/$addr /n/endpoint/local diff --git a/appl/alphabet/grid/farm.b b/appl/alphabet/grid/farm.b new file mode 100644 index 00000000..4aaa8758 --- /dev/null +++ b/appl/alphabet/grid/farm.b @@ -0,0 +1,144 @@ +implement Farm, Gridmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; +include "string.m"; + str: String; +include "alphabet/reports.m"; + reports: Reports; + report, Report, quit: import reports; +include "alphabet/endpoints.m"; + endpoints: Endpoints; + Endpoint: import endpoints; +include "alphabet/grid.m"; + grid: Grid; + Value: import grid; + +Farm: module {}; + +types(): string +{ + return "eesss*-A-k-a-v-bs"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + endpoints = checkload(load Endpoints Endpoints->PATH, Endpoints->PATH); + endpoints->init(); + grid = checkload(load Grid Grid->PATH, Grid->PATH); + grid->init(); + sh = checkload(load Sh Sh->PATH, Sh->PATH); + sh->initialise(); + str = checkload(load String String->PATH, String->PATH); +} + +run(nil: chan of string, r: ref Reports->Report, + opt: list of (int, list of ref Grid->Value), args: list of ref Grid->Value): ref Grid->Value +{ + ec0 := (hd args).e().i; + addr := (hd tl args).s().i; + job, opts: string; + noauth := 0; + for(; opt != nil; opt = tl opt){ + c := (hd opt).t0; + case (hd opt).t0 { + 'A' => + noauth = 1; + 'b' => + opts += " -b "+(hd (hd opt).t1).s().i; + * => + opts += sys->sprint(" -%c", (hd opt).t0); + } + } + for(args = tl tl args; args != nil; args = tl args) + job += sys->sprint(" %q", (hd args).s().i); + + spawn farmproc(sync := chan of int, addr, ec0, opts, job, noauth, r.start("farm"), ec := chan of Endpoint); + <-sync; + return ref Value.Ve(ec); +} + +farmproc(sync: chan of int, + addr: string, + ec0: chan of Endpoint, + opts: string, + job: string, + noauth: int, + errorc: chan of string, + ec1: chan of Endpoint) +{ + sys->pctl(Sys->FORKNS, nil); + sync <-= 1; + ep0 := <-ec0; + if(ep0.addr == nil){ + ec1 <-= ep0; + quit(errorc); + } + (v, e) := farm(addr, ep0, opts, job, noauth, errorc); + if(e != nil){ + endpoints->open(nil, ep0); + report(errorc, "error: "+e); + } + ec1 <-= v; + quit(errorc); +} + +Nope: con Endpoint(nil, nil, nil); + +farm(addr: string, + ep0: Endpoint, + opts: string, + job: string, + noauth: int, + errorc: chan of string): (Endpoint, string) +{ + args := addr::"/n/remote"::nil; + if(noauth) + args = "-A"::args; + if((e := sh->run(nil, "mount"::args)) != nil) + return (Nope, sys->sprint("cannot mount scheduler at %q: %s, args %s", addr, e, str->quoted(args))); + + fd := sys->open("/n/remote/admin/clone", Sys->ORDWR); + if(fd == nil) + return (Nope, sys->sprint("cannot open clone: %r")); + if((d := gets(fd)) == nil) + return (Nope, "read clone failed"); + dir := "/n/remote/admin/"+d; + if(sys->fprint(fd, "load workflow%s %q %s", opts, ep0.text(), job) == -1) + return (Nope, sys->sprint("job load failed: %r")); + if(sys->fprint(fd, "start") == -1) + return (Nope, sys->sprint("job start failed: %r")); + dfd := sys->open(dir+"/data", Sys->OREAD); + if(dfd == nil){ + sys->fprint(fd, "delete"); + return (Nope, sys->sprint("cannot open job data file: %r")); + } + s := gets(dfd); + ep1 := Endpoint.mk(s); + if(ep1.addr == nil) + return (Nope, sys->sprint("bad remote endpoint %q", s)); + report(errorc, sys->sprint("job %s started, id %s", d, gets(sys->open(dir+"/id", Sys->OREAD)))); + # XXX how is the job going to be deleted eventually + ep1.about = sys->sprint("%s | farm%s %s%s", ep0.about, opts, addr, job); + return (ep1, nil); +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} + +gets(fd: ref Sys->FD): string +{ + d := array[8192] of byte; + n := sys->read(fd, d, len d); + if(n <= 0) + return nil; + return string d[0:n]; +} diff --git a/appl/alphabet/grid/line2rec.b b/appl/alphabet/grid/line2rec.b new file mode 100644 index 00000000..2429a67d --- /dev/null +++ b/appl/alphabet/grid/line2rec.b @@ -0,0 +1,91 @@ +implement Line2rec, Gridmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet/endpoints.m"; +include "alphabet/grid.m"; + grid: Grid; + Value: import grid; + +Line2rec: module {}; + +types(): string +{ + return "bf"; +} + +init() +{ + sys = load Sys Sys->PATH; + grid = load Grid Grid->PATH; + reports = load Reports Reports->PATH; + bufio = load Bufio Bufio->PATH; +} + +quit() +{ +} + +run(nil: chan of string, r: ref Report, + nil: list of (int, list of ref Value), args: list of ref Value): ref Value +{ + f := chan of ref Sys->FD; + spawn line2recproc((hd args).f().i, f, r.start("line2rec")); + return ref Value.Vb(f); +} + +line2recproc( + f0, + f1: chan of ref Sys->FD, + errorc: chan of string) +{ + (fd0, fd1) := startfilter(f0, f1, errorc); + iob0 := bufio->fopen(fd0, Sys->OREAD); + iob1 := bufio->fopen(fd1, Sys->OWRITE); + { + while((s := iob0.gets('\n')) != nil){ + d := array of byte s; + if(iob1.puts("data "+string len d) < 0) + break; + if(iob1.write(d, len d) != len d) + break; + } + iob1.flush(); + sys->fprint(fd1, ""); + }exception{ + "write on closed pipe" => + ; + } + reports->quit(errorc); +} + +# read side (when it's an argument): +# read proposed new fd +# write actual fd for them to write to (creating pipe in necessary) +# +# write side (when you're returning it): +# write a proposed new fd (or nil if no suggestion) +# read actual fd for writing +startfilter(f0, f1: chan of ref Sys->FD, errorc: chan of string): (ref Sys->FD, ref Sys->FD) +{ + f1 <-= nil; + if((fd1 := <-f1) == nil){ + <-f0; + f0 <-= nil; + reports->quit(errorc); + } + if((fd0 := <-f0) == nil){ + sys->pipe(p := array[2] of ref Sys->FD); + f0 <-= p[1]; + fd0 = p[0]; + }else + f0 <-= nil; + return (fd0, fd1); +} diff --git a/appl/alphabet/grid/local.b b/appl/alphabet/grid/local.b new file mode 100644 index 00000000..2fca7b95 --- /dev/null +++ b/appl/alphabet/grid/local.b @@ -0,0 +1,86 @@ +implement Local,Gridmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + report, quit, Report: import reports; +include "alphabet/endpoints.m"; + endpoints: Endpoints; + Endpoint: import endpoints; +include "alphabet/grid.m"; + grid: Grid; + Value: import grid; + +Local: module {}; +types(): string +{ + return "fe-v"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + endpoints = checkload(load Endpoints Endpoints->PATH, Endpoints->PATH); + endpoints->init(); + grid = checkload(load Grid Grid->PATH, Grid->PATH); + grid->init(); +} + +run(nil: chan of string, r: ref Reports->Report, + opts: list of (int, list of ref Grid->Value), args: list of ref Grid->Value): ref Grid->Value +{ + + spawn localproc((hd args).e().i, f := chan of ref Sys->FD, opts!=nil, r.start("local")); + return ref Value.Vf(f); +} + +localproc(ec: chan of Endpoint, f: chan of ref Sys->FD, verbose: int, errorc: chan of string) +{ + ep := <-ec; + if(ep.addr == nil){ + # error should already have been printed (XXX is that the right way to do it?) + f <-= nil; + <-f; + quit(errorc); + } + if(verbose) + report(errorc, sys->sprint("endpoint %q at %q: %s", ep.id, ep.addr, ep.about)); + (fd0, err) := endpoints->open(nil, ep); + if(fd0 == nil){ + report(errorc, sys->sprint("error: local: cannot open endpoint (%q %q): %s", ep.addr, ep.id, err)); + f <-= nil; + <-f; + quit(errorc); + } + f <-= fd0; + fd1 := <-f; + if(fd1 == nil) + quit(errorc); + + buf := array[Sys->ATOMICIO] of byte; + { + while((n := sys->read(fd0, buf, len buf)) > 0){ +#sys->print("local read %d bytes\n", n); + sys->write(fd1, buf, n); + } +#sys->print("local eof %d\n", n); + sys->write(fd1, array[0] of byte, 0); + if(n < 0) + report(errorc, sys->sprint("read error: %r")); + } exception e { + "write on closed pipe" => + report(errorc, "write on closed pipe"); + ; + } + quit(errorc); +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} diff --git a/appl/alphabet/grid/mkfile b/appl/alphabet/grid/mkfile new file mode 100644 index 00000000..c8e78ffb --- /dev/null +++ b/appl/alphabet/grid/mkfile @@ -0,0 +1,22 @@ +<../../../mkconfig + +TARG=\ + farm.dis\ + line2rec.dis\ + local.dis\ + remote.dis\ + rexec.dis\ + +SYSMODULES=\ + draw.m\ + alphabet/endpoints.m\ + alphabet/grid.m\ + alphabet/reports.m\ + sh.m\ + string.m\ + sys.m\ + +DISBIN=$ROOT/dis/alphabet/grid + +<$ROOT/mkfiles/mkdis +LIMBOFLAGS=-F $LIMBOFLAGS diff --git a/appl/alphabet/grid/remote.b b/appl/alphabet/grid/remote.b new file mode 100644 index 00000000..dbdc86ef --- /dev/null +++ b/appl/alphabet/grid/remote.b @@ -0,0 +1,88 @@ +implement Remote, Gridmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + report, quit, Report: import reports; +include "alphabet/endpoints.m"; + endpoints: Endpoints; + Endpoint: import endpoints; +include "alphabet/grid.m"; + grid: Grid; + Value: import grid; + +Remote: module {}; + +types(): string +{ + return "ef-as"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + endpoints = checkload(load Endpoints Endpoints->PATH, Endpoints->PATH); + endpoints->init(); + grid = checkload(load Grid Grid->PATH, Grid->PATH); + grid->init(); +} + +run(nil: chan of string, r: ref Reports->Report, + opts: list of (int, list of ref Grid->Value), args: list of ref Grid->Value): ref Grid->Value +{ + addr := "local"; + if(opts != nil) + addr = (hd (hd opts).t1).s().i; + f := (hd args).f().i; + spawn remoteproc(ec := chan of Endpoint, f, addr, r.start("remote")); + return ref Value.Ve(ec); +} + +Noendpoint: con Endpoint(nil, nil, nil); + +remoteproc(ec: chan of Endpoint, f: chan of ref Sys->FD, addr: string, errorc: chan of string) +{ + (fd1, ep) := endpoints->create(addr); + if(fd1 == nil){ + report(errorc, "error: remote: cannot create endpoint at "+addr+": "+ep.about); + ec <-= Noendpoint; + <-f; + f <-= nil; + quit(errorc); + } + fd0 := <-f; + if(fd0 != nil) + ep.about = sys->sprint("local(%#q)", sys->fd2path(fd0)); + else + ep.about = "local(pipe)"; + ec <-= ep; + f <-= fd1; + quit(errorc); +} + +# sys->pipe(p := array[2] of ref Sys->FD); +# f <-= p[1]; +# p[1] = nil; +# buf := array[Sys->ATOMICIO] of byte; +# while((n := sys->read(p[0], buf, len buf)) > 0){ +# if(sys->write(fd, buf, n) == -1){ +# report(errorc, sys->sprint("write error: %r")); +# break; +# } +# }exception{ +# "write on closed pipe" => +# report(errorc, "got write on closed pipe"); +# } +# sys->write(fd, array[0] of byte, 0); +# quit(errorc); +#} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} diff --git a/appl/alphabet/grid/rexec.b b/appl/alphabet/grid/rexec.b new file mode 100644 index 00000000..02869659 --- /dev/null +++ b/appl/alphabet/grid/rexec.b @@ -0,0 +1,112 @@ +implement Rexec, Gridmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; +include "string.m"; + str: String; +include "alphabet/reports.m"; + reports: Reports; + report, Report, quit: import reports; +include "alphabet/endpoints.m"; + endpoints: Endpoints; + Endpoint: import endpoints; +include "alphabet/grid.m"; + grid: Grid; + Value: import grid; + +Rexec: module {}; + +types(): string +{ + return "eesc-A"; +} + +init() +{ + sys = load Sys Sys->PATH; + reports = checkload(load Reports Reports->PATH, Reports->PATH); + endpoints = checkload(load Endpoints Endpoints->PATH, Endpoints->PATH); + endpoints->init(); + grid = checkload(load Grid Grid->PATH, Grid->PATH); + grid->init(); + sh = checkload(load Sh Sh->PATH, Sh->PATH); + sh->initialise(); + str = checkload(load String String->PATH, String->PATH); +} + +run(nil: chan of string, r: ref Reports->Report, + opts: list of (int, list of ref Grid->Value), args: list of ref Grid->Value): ref Grid->Value +{ + ec0 := (hd args).e().i; + addr := (hd tl args).s().i; + cmd := (hd tl tl args).c().i; + + spawn rexecproc(sync := chan of int, addr, ec0, cmd, r.start("rexec"), opts != nil, ec1 := chan of Endpoint); + <-sync; + return ref Value.Ve(ec1); +} + +rexecproc(sync: chan of int, + addr: string, + ec0: chan of Endpoint, + cmd: ref Sh->Cmd, + errorc: chan of string, + noauth: int, + ec1: chan of Endpoint + ) +{ + sys->pctl(Sys->FORKNS, nil); + sync <-= 1; + + ep0 := <-ec0; + if(ep0.addr == nil){ + ec1 <-= ep0; + quit(errorc); + } + + (ep1, err) := exec(addr, ep0, cmd, noauth); + if(err != nil){ + endpoints->open(nil, ep0); # discard + report(errorc, err); + } + ec1 <-= ep1; + quit(errorc); +} + +Nope: con Endpoint(nil, nil, nil); + +exec(addr: string, ep0: Endpoint, cmd: ref Sh->Cmd, noauth: int): (Endpoint, string) +{ + args := addr::"/n/remote"::nil; + if(noauth) + args = "-A"::args; + if((e := sh->run(nil, "mount"::args)) != nil) + return (Nope, sys->sprint("cannot mount rexec at %q: %s", addr, e)); + + fd := sys->open("/n/remote/exec", Sys->ORDWR); + if(fd == nil) + return (Nope, sys->sprint("cannot open exec at %q: %r", addr)); + if(sys->fprint(fd, "%q %q", ep0.text(), sh->cmd2string(cmd)) == -1) + return (Nope, sys->sprint("exec write failed: %r")); + buf := array[1024] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return (Nope, sys->sprint("error reading endpoint: %r")); + if(n == 0) + return (Nope, "eof reading endpoint"); + s := string buf[0:n]; + ep1 := Endpoint.mk(s); + if(ep1.addr == nil) + return (Nope, sys->sprint("bad endpoint %#q: %s", s, ep1.about)); + ep1.about = sys->sprint("%s | rexec %q %s", ep0.about, addr, sh->cmd2string(cmd)); + return (ep1, nil); +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + raise sys->sprint("fail:cannot load %s: %r", path); +} diff --git a/appl/alphabet/main/auth.b b/appl/alphabet/main/auth.b new file mode 100644 index 00000000..d05ecb46 --- /dev/null +++ b/appl/alphabet/main/auth.b @@ -0,0 +1,157 @@ +implement Authenticate, Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "keyring.m"; + keyring: Keyring; +include "security.m"; + auth: Auth; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Authenticate: module {}; + +typesig(): string +{ + return "ww-ks-Cs-v"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + reports = load Reports Reports->PATH; + keyring = load Keyring Keyring->PATH; + auth = load Auth Auth->PATH; + auth->init(); +} + +quit() +{ +} + +After, Before, Create: con 1<Context, r: ref Reports->Report, errorc: chan of string, + opts: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + keyfile: string; + alg: string; + verbose: int; + for(; opts != nil; opts = tl opts){ + case (hd opts).t0 { + 'k' => + keyfile = (hd (hd opts).t1).s().i; + if (keyfile != nil && ! (keyfile[0] == '/' || (len keyfile > 2 && keyfile[0:2] == "./"))) + keyfile = "/usr/" + user() + "/keyring/" + keyfile; + 'C' => + alg = (hd (hd opts).t1).s().i; + 'v' => + verbose = 1; + } + } + if(keyfile == nil) + keyfile = "/usr/" + user() + "/keyring/default"; + cert := keyring->readauthinfo(keyfile); + if (cert == nil) { + report(errorc, sys->sprint("auth: cannot read %q: %r", keyfile)); + return nil; + } + w := chan of ref Sys->FD; + spawn authproc((hd args).w().i, w, cert, verbose, alg, r.start("auth")); + return ref Value.Vw(w); +} + +authproc(f0, f1: chan of ref Sys->FD, cert: ref Keyring->Authinfo, + verbose: int, alg: string, errorc: chan of string) +{ + fd0 := <-f0; + if(fd0 == nil){ + sys->pipe(p := array[2] of ref Sys->FD); + f0 <-= p[1]; + fd0 = p[0]; + }else + f0 <-= nil; + + eu: string; + (fd0, eu) = auth->client(alg, cert, fd0); + if(fd0 == nil){ + report(errorc, "authentication failed: "+eu); + f1 <-= nil; + <-f1; + reports->quit(errorc); + } + if(verbose) + report(errorc, sys->sprint("remote user %q", eu)); + f1 <-= fd0; + fd1 := <-f1; + if(fd1 == nil) + reports->quit(errorc); + wstream(fd0, fd1, errorc); + reports->quit(errorc); +} + +wstream(fd0, fd1: ref Sys->FD, errorc: chan of string) +{ + sync := chan[2] of int; + qc := chan of int; + spawn stream(fd0, fd1, sync, qc, errorc); + spawn stream(fd1, fd0, sync, qc, errorc); + <-qc; + kill(<-sync); + kill(<-sync); +} + +stream(fd0, fd1: ref Sys->FD, sync, qc: chan of int, errorc: chan of string) +{ + sync <-= sys->pctl(0, nil); + buf := array[Sys->ATOMICIO] of byte; + while((n := sys->read(fd0, buf, len buf)) > 0){ + if(sys->write(fd1, buf, n) == -1){ + report(errorc, sys->sprint("write error: %r")); + break; + } + } + qc <-= 1; + exit; +} + +kill(pid: int) +{ + sys->fprint(sys->open("#p/"+string pid+"/ctl", Sys->OWRITE), "kill"); +} + + +exists(f: string): int +{ + (ok, nil) := sys->stat(f); + return ok != -1; +} + +user(): string +{ + u := readfile("/dev/user"); + if (u == nil) + return "nobody"; + return u; +} + +readfile(f: string): string +{ + fd := sys->open(f, sys->OREAD); + if(fd == nil) + return nil; + + buf := array[128] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return nil; + + return string buf[0:n]; +} diff --git a/appl/alphabet/main/cat.b b/appl/alphabet/main/cat.b new file mode 100644 index 00000000..b19b1fd9 --- /dev/null +++ b/appl/alphabet/main/cat.b @@ -0,0 +1,78 @@ +implement Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +typesig(): string +{ + return "ff*"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + reports = load Reports Reports->PATH; +} + +quit() +{ +} + +run(nil: ref Draw->Context, r: ref Reports->Report, nil: chan of string, + nil: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + fds: list of chan of ref Sys->FD; + for(; args != nil; args = tl args) + fds = (hd args).f().i :: fds; + f := chan of ref Sys->FD; + spawn catproc(f, rev(fds), r.start("print")); + return ref Value.Vf(f); +} + +catproc(f: chan of ref Sys->FD, fds: list of chan of ref Sys->FD, reportc: chan of string) +{ + f <-= nil; + if((fd1 := <-f) == nil){ + for(; fds != nil; fds = tl fds){ + <-hd fds; + hd fds <-= nil; + } + reports->quit(reportc); + } + buf := array[8192] of byte; + for(; fds != nil; fds = tl fds){ + fd0 := <-hd fds; + if(fd0 == nil){ + p := array[2] of ref Sys->FD; + sys->pipe(p); + fd0 = p[0]; + hd fds <-= p[1]; + }else + hd fds <-= nil; + while((n := sys->read(fd0, buf, len buf)) > 0){ + sys->write(fd1, buf, n); + }exception{ + "write on closed pipe" => + ; + } + } + sys->write(fd1, array[0] of byte, 0); + reports->quit(reportc); +} + +rev[T](l: list of T): list of T +{ + r: list of T; + for(; l != nil; l = tl l) + r = hd l :: r; + return r; +} \ No newline at end of file diff --git a/appl/alphabet/main/create.b b/appl/alphabet/main/create.b new file mode 100644 index 00000000..bb1601dc --- /dev/null +++ b/appl/alphabet/main/create.b @@ -0,0 +1,55 @@ +implement Create,Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Create: module {}; + +typesig(): string +{ + return "rfs"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + reports = load Reports Reports->PATH; +} + +quit() +{ +} + +run(nil: ref Draw->Context, nil: ref Reports->Report, errorc: chan of string, + nil: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + r := chan of string; + fd := sys->create((hd tl args).s().i, Sys->OWRITE, 8r666); + if(fd == nil){ + report(errorc, sys->sprint("error: cannot create %q: %r", (hd tl args).s().i)); + return nil; + } + spawn createproc(r, (hd args).f().i, fd); + return ref Value.Vr(r); +} + +createproc(r: chan of string, f: chan of ref Sys->FD, fd: ref Sys->FD) +{ + if(<-r != nil){ + <-f; + f <-= nil; + exit; + } + <-f; + f <-= fd; + r <-= nil; +} diff --git a/appl/alphabet/main/dial.b b/appl/alphabet/main/dial.b new file mode 100644 index 00000000..e8521b45 --- /dev/null +++ b/appl/alphabet/main/dial.b @@ -0,0 +1,85 @@ +implement Dial,Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Dial: module {}; + +typesig(): string +{ + return "ws"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + reports = load Reports Reports->PATH; +} + +quit() +{ +} + +run(nil: ref Draw->Context, r: ref Reports->Report, errorc: chan of string, + nil: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + w := chan of ref Sys->FD; + addr := (hd args).s().i; + (ok, c) := sys->dial(addr, nil); + if(ok == -1){ + report(errorc, sys->sprint("dial: cannot dial %q: %r", addr)); + return nil; + } + f := chan of ref Sys->FD; + spawn dialproc(f, c.dfd, r.start("dial")); + return ref Value.Vw(f); +} + +dialproc(f: chan of ref Sys->FD, fd0: ref Sys->FD, errorc: chan of string) +{ + f <-= fd0; + fd1 := <-f; + if(fd1 == nil) + reports->quit(errorc); + wstream(fd0, fd1, errorc); + reports->quit(errorc); +} + +wstream(fd0, fd1: ref Sys->FD, errorc: chan of string) +{ + sync := chan[2] of int; + qc := chan of int; + spawn stream(fd0, fd1, sync, qc, errorc); + spawn stream(fd1, fd0, sync, qc, errorc); + <-qc; + kill(<-sync); + kill(<-sync); +} + +stream(fd0, fd1: ref Sys->FD, sync, qc: chan of int, errorc: chan of string) +{ + sync <-= sys->pctl(0, nil); + buf := array[Sys->ATOMICIO] of byte; + while((n := sys->read(fd0, buf, len buf)) > 0){ + if(sys->write(fd1, buf, n) == -1){ + report(errorc, sys->sprint("write error: %r")); + break; + } + } + qc <-= 1; + exit; +} + +kill(pid: int) +{ + sys->fprint(sys->open("#p/"+string pid+"/ctl", Sys->OWRITE), "kill"); +} diff --git a/appl/alphabet/main/echo.b b/appl/alphabet/main/echo.b new file mode 100644 index 00000000..3b01c951 --- /dev/null +++ b/appl/alphabet/main/echo.b @@ -0,0 +1,51 @@ +implement Echo, Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Echo: module {}; + +typesig(): string +{ + return "fs-n"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + reports = load Reports Reports->PATH; +} + +quit() +{ +} + +run(nil: ref Draw->Context, nil: ref Reports->Report, nil: chan of string, + opts: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + f := chan of ref Sys->FD; + s := (hd args).s().i; + if(opts == nil) + s[len s] = '\n'; + spawn echoproc(f, s); + return ref Value.Vf(f); +} + +echoproc(f: chan of ref Sys->FD, s: string) +{ + f <-= nil; + fd := <-f; + if(fd == nil) + exit; + sys->fprint(fd, "%s", s); + sys->write(fd, array[0] of byte, 0); +} diff --git a/appl/alphabet/main/export.b b/appl/alphabet/main/export.b new file mode 100644 index 00000000..5e9b986e --- /dev/null +++ b/appl/alphabet/main/export.b @@ -0,0 +1,52 @@ +implement Export,Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Export: module {}; + +typesig(): string +{ + return "ws"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + reports = load Reports Reports->PATH; +} + +quit() +{ +} + +run(nil: ref Draw->Context, r: ref Reports->Report, nil: chan of string, + nil: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + w := chan of ref Sys->FD; + addr := (hd args).s().i; + f := chan of ref Sys->FD; + spawn exportproc(f, (hd args).s().i, r.start("export")); + return ref Value.Vw(f); +} + +exportproc(f: chan of ref Sys->FD, dir: string, errorc: chan of string) +{ + f <-= nil; + fd := <-f; + if(fd == nil) + reports->quit(errorc); + errorc <-= nil; + if(sys->export(fd, dir, Sys->EXPASYNC) == -1) + report(errorc, sys->sprint("cannot export: %r")); + reports->quit(errorc); +} diff --git a/appl/alphabet/main/fd.b b/appl/alphabet/main/fd.b new file mode 100644 index 00000000..11d10828 --- /dev/null +++ b/appl/alphabet/main/fd.b @@ -0,0 +1,83 @@ +implement Fd, Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Fd: module {}; + +typesig(): string +{ + return "ws"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + reports = load Reports Reports->PATH; +} + +quit() +{ +} + +run(nil: ref Draw->Context, r: ref Reports->Report, errorc: chan of string, + nil: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + fd := sys->fildes(int (hd args).s().i); + if(fd == nil){ + report(errorc, sys->sprint("error: no such file descriptor %q", (hd args).s().i)); + return nil; + } + f := chan of ref Sys->FD; + spawn readfdproc(f, fd, r.start("stdin")); + return ref Value.Vw(f); +} + +readfdproc(f: chan of ref Sys->FD, fd0: ref Sys->FD, errorc: chan of string) +{ + f <-= fd0; + fd1 := <-f; + if(fd1 == nil) + reports->quit(errorc); + wstream(fd0, fd1, errorc); + reports->quit(errorc); +} + +wstream(fd0, fd1: ref Sys->FD, errorc: chan of string) +{ + sync := chan[2] of int; + qc := chan of int; + spawn stream(fd0, fd1, sync, qc, errorc); + spawn stream(fd1, fd0, sync, qc, errorc); + <-qc; + kill(<-sync); + kill(<-sync); +} + +stream(fd0, fd1: ref Sys->FD, sync, qc: chan of int, errorc: chan of string) +{ + sync <-= sys->pctl(0, nil); + buf := array[Sys->ATOMICIO] of byte; + while((n := sys->read(fd0, buf, len buf)) > 0){ + if(sys->write(fd1, buf, n) == -1){ + report(errorc, sys->sprint("write error: %r")); + break; + } + } + qc <-= 1; + exit; +} + +kill(pid: int) +{ + sys->fprint(sys->open("#p/"+string pid+"/ctl", Sys->OWRITE), "kill"); +} diff --git a/appl/alphabet/main/filter.b b/appl/alphabet/main/filter.b new file mode 100644 index 00000000..f532f529 --- /dev/null +++ b/appl/alphabet/main/filter.b @@ -0,0 +1,114 @@ +implement Filter, Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; + Context: import sh; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Filter: module {}; + +typesig(): string +{ + return "ffcs*"; # XXX option to suppress stderr? +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + reports = load Reports Reports->PATH; + bufio = load Bufio Bufio->PATH; + sh = load Sh Sh->PATH; + sh->initialise(); +} + +quit() +{ +} + +run(drawctxt: ref Draw->Context, report: ref Reports->Report, nil: chan of string, + nil: list of (int, list of ref Value), + args: list of ref Value): ref Value +{ + f := chan of ref Sys->FD; + a: list of ref Sh->Listnode; + for(al := tl tl args; al != nil; al = tl al) + a = ref Sh->Listnode(nil, (hd al).s().i) :: a; + spawn filterproc(drawctxt, (hd args).f().i, f, (hd tl args).c().i, rev(a), report.start("filter")); + return ref Value.Vf(f); +} + +filterproc(drawctxt: ref Draw->Context, + f0, + f1: chan of ref Sys->FD, + c: ref Sh->Cmd, + args: list of ref Sh->Listnode, + errorc: chan of string) +{ + (fd0, fd1) := startfilter(f0, f1, errorc); + sys->pipe(p := array[2] of ref Sys->FD); + spawn stderrproc(p[0], errorc); + p[0] = nil; + + # i hate this stuff. + sys->pctl(Sys->FORKFD, nil); + sys->dup(fd0.fd, 0); + sys->dup(fd1.fd, 1); + sys->dup(p[1].fd, 2); + fd0 = fd1 = nil; + p = nil; + sys->pctl(Sys->NEWFD, 0::1::2::nil); + Context.new(drawctxt).run(ref Sh->Listnode(c, nil)::args, 0); + sys->fprint(sys->fildes(2), ""); +} + +# read side (when it's an argument): +# read proposed new fd +# write actual fd for them to write to (creating pipe in necessary) +# +# write side (when you're returning it): +# write a proposed new fd (or nil if no suggestion) +# read actual fd for writing +startfilter(f0, f1: chan of ref Sys->FD, errorc: chan of string): (ref Sys->FD, ref Sys->FD) +{ + f1 <-= nil; + if((fd1 := <-f1) == nil){ + <-f0; + f0 <-= nil; + reports->quit(errorc); + } + if((fd0 := <-f0) == nil){ + sys->pipe(p := array[2] of ref Sys->FD); + f0 <-= p[1]; + fd0 = p[0]; + }else + f0 <-= nil; + return (fd0, fd1); +} + +stderrproc(fd: ref Sys->FD, errorc: chan of string) +{ + iob := bufio->fopen(fd, Sys->OREAD); + while((s := iob.gets('\n')) != nil) + if(len s > 1) + errorc <-= s[0:len s - 1]; + errorc <-= nil; +} + +rev[T](l: list of T): list of T +{ + r: list of T; + for(; l != nil; l = tl l) + r = hd l :: r; + return r; +} \ No newline at end of file diff --git a/appl/alphabet/main/genfilter.b b/appl/alphabet/main/genfilter.b new file mode 100644 index 00000000..9a920d7e --- /dev/null +++ b/appl/alphabet/main/genfilter.b @@ -0,0 +1,79 @@ +implement Myfilter, Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Myfilter: module {}; + +typesig(): string +{ + return "ff"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + reports = load Reports Reports->PATH; + bufio = load Bufio Bufio->PATH; +} + +quit() +{ +} + +run(drawctxt: ref Draw->Context, report: ref Reports->Report, nil: chan of string, + nil: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + f := chan of ref Sys->FD; + spawn filterproc(drawctxt, (hd args).f().i, f, report.start("myfilter")); + return ref Value.Vf(f); +} + +filterproc(nil: ref Draw->Context, f0, f1: chan of ref Sys->FD, errorc: chan of string) +{ + (fd0, fd1) := startfilter(f0, f1, errorc); + iob0 := bufio->fopen(fd0, Sys->OREAD); + iob1 := bufio->fopen(fd1, Sys->OWRITE); + + # XXX your filter here! + while((s := iob0.gets('\n')) != nil){ + d := array of byte s; + iob1.puts("data "+string len d+"\n"); + iob1.write(d, len d); + }exception{ + "write on closed pipe" => + ; + } + iob1.flush(); + sys->fprint(fd1, ""); + reports->quit(errorc); +} + +startfilter(f0, f1: chan of ref Sys->FD, errorc: chan of string): (ref Sys->FD, ref Sys->FD) +{ + f1 <-= nil; + if((fd1 := <-f1) == nil){ + <-f0; + f0 <-= nil; + reports->quit(errorc); + } + if((fd0 := <-f0) == nil){ + sys->pipe(p := array[2] of ref Sys->FD); + f0 <-= p[1]; + fd0 = p[0]; + }else + f0 <-= nil; + return (fd0, fd1); +} diff --git a/appl/alphabet/main/mkfile b/appl/alphabet/main/mkfile new file mode 100644 index 00000000..ff2d95fb --- /dev/null +++ b/appl/alphabet/main/mkfile @@ -0,0 +1,36 @@ +<../../../mkconfig + +TARG=\ + auth.dis\ + cat.dis\ + create.dis\ + dial.dis\ + echo.dis\ + env.dis\ + export.dis\ + fd.dis\ + filter.dis\ + mount.dis\ + par.dis\ + parse.dis\ + pretty.dis\ + print.dis\ + read.dis\ + readall.dis\ + rewrite.dis\ + seq.dis\ + unparse.dis\ + w2fd.dis\ + wait.dis\ + +SYSMODULES=\ + alphabet.m\ + alphabet/reports.m\ + draw.m\ + sh.m\ + sys.m\ + +DISBIN=$ROOT/dis/alphabet/main + +<$ROOT/mkfiles/mkdis +LIMBOFLAGS=-F $LIMBOFLAGS diff --git a/appl/alphabet/main/mount.b b/appl/alphabet/main/mount.b new file mode 100644 index 00000000..1b27617d --- /dev/null +++ b/appl/alphabet/main/mount.b @@ -0,0 +1,80 @@ +implement Mount,Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Mount: module {}; + +typesig(): string +{ + return "rws-a-b-c-xs"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + reports = load Reports Reports->PATH; +} + +quit() +{ +} + +After, Before, Create: con 1<Context, report: ref Reports->Report, nil: chan of string, + opts: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + flag := Sys->MREPL; + aname := ""; + for(; opts != nil; opts = tl opts){ + case (hd opts).t0 { + 'a' => + flag = After & (flag&Sys->MCREATE); + 'b' => + flag = Before & (flag&Sys->MCREATE); + 'c' => + flag |= Create; + 'x' => + aname = (hd (hd opts).t1).s().i; + } + } + r := chan of string; + spawn mountproc(r, (hd args).w().i, (hd tl args).s().i, aname, flag, report.start("mount")); + return ref Value.Vr(r); +} + +mountproc(r: chan of string, w: chan of ref Sys->FD, dir, aname: string, flag: int, errorc: chan of string) +{ + if(<-r != nil){ + errorc <-= nil; + <-w; + w <-= nil; + exit; + } + fd := <-w; + if(fd == nil){ + sys->pipe(p := array[2] of ref Sys->FD); + w <-= p[0]; + fd = p[1]; + }else + w <-= nil; + if(sys->mount(fd, nil, dir, flag, aname) == -1){ + e := sys->sprint("mount error on %#q: %r", dir); + report(errorc, e); + r <-= e; + exit; + } + + errorc <-= nil; + r <-= nil; +} diff --git a/appl/alphabet/main/par.b b/appl/alphabet/main/par.b new file mode 100644 index 00000000..d2b47ef3 --- /dev/null +++ b/appl/alphabet/main/par.b @@ -0,0 +1,50 @@ +implement Seq, Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Seq: module {}; + +typesig(): string +{ + return "rr*"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; +} + +quit() +{ +} + +run(nil: ref Draw->Context, nil: ref Reports->Report, nil: chan of string, + nil: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + spawn parproc(r := chan of string, args); + return ref Value.Vr(r); +} + +parproc(r: chan of string, args: list of ref Alphabet->Value) +{ + if(<-r != nil){ + for(; args != nil; args = tl args) + (hd args).r().i <-= "die!"; + }else{ + status := ""; + for(a := args; a != nil; a = tl a) + (hd a).r().i <-= nil; + for(; args != nil; args = tl args) + if((e := <-(hd args).r().i) != nil) + status = e; + r <-= status; + } +} diff --git a/appl/alphabet/main/parse.b b/appl/alphabet/main/parse.b new file mode 100644 index 00000000..c02ba124 --- /dev/null +++ b/appl/alphabet/main/parse.b @@ -0,0 +1,43 @@ +implement Parse, Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Parse: module {}; + +typesig(): string +{ + return "cs"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + sh = load Sh Sh->PATH; + sh->initialise(); +} + +quit() +{ +} + +run(nil: ref Draw->Context, nil: ref Reports->Report, errorc: chan of string, + nil: list of (int, list of ref Value), + args: list of ref Value): ref Value +{ + (c, err) := sh->parse((hd args).s().i); + if(c == nil){ + report(errorc, sys->sprint("parse: parse %q failed: %s", (hd args).s().i, err)); + return nil; + } + return ref Value.Vc(c); +} diff --git a/appl/alphabet/main/pretty.b b/appl/alphabet/main/pretty.b new file mode 100644 index 00000000..52d8b32f --- /dev/null +++ b/appl/alphabet/main/pretty.b @@ -0,0 +1,116 @@ +implement Pretty, Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; + n_BLOCK, n_VAR, n_BQ, n_BQ2, n_REDIR, + n_DUP, n_LIST, n_SEQ, n_CONCAT, n_PIPE, n_ADJ, + n_WORD, n_NOWAIT, n_SQUASH, n_COUNT, + n_ASSIGN, n_LOCAL, + GLOB: import Sh; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Pretty: module {}; + +typesig(): string +{ + return "sc"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + sh = load Sh Sh->PATH; + sh->initialise(); +} + +quit() +{ +} + +run(nil: ref Draw->Context, nil: ref Reports->Report, nil: chan of string, + nil: list of (int, list of ref Value), + args: list of ref Value): ref Value +{ + { + return ref Value.Vs(pretty((hd args).c().i, 0)); + }exception{ + "bad expr" => + return nil; + } +} + +pretty(n: ref Sh->Cmd, depth: int): string +{ + if (n == nil) + return nil; + s: string; + case n.ntype { + n_BLOCK => + s = "{\n"+tabs(depth+1)+pretty(n.left,depth+1) + "\n"+tabs(depth)+"}"; + n_VAR => + s = "$" + pretty(n.left, depth); + n_LIST => + s = "(" + pretty(n.left, depth) + ")"; + n_SEQ => + s = pretty(n.left, depth) + "\n"+tabs(depth)+pretty(n.right, depth); + n_PIPE => + s = pretty(n.left, depth) + " |\n"+tabs(depth)+pretty(n.right, depth); + n_ADJ => + s = pretty(n.left, depth) + " " + pretty(n.right, depth); + n_WORD => + s = quote(n.word, 1); + n_BQ2 => + # if we can't do it, revert to ugliness. + { + s = "\"" + pretty(n.left, depth); + } exception { + "bad expr" => + s = sh->cmd2string(n); + } + * => + raise "bad expr"; + } + return s; +} + +tabs(n: int): string +{ + s: string; + while(n-- > 0) + s[len s] = '\t'; + return s; +} + +# stolen from sh.y +quote(s: string, glob: int): string +{ + needquote := 0; + t := ""; + for (i := 0; i < len s; i++) { + case s[i] { + '{' or '}' or '(' or ')' or '`' or '&' or ';' or '=' or '>' or '<' or '#' or + '|' or '*' or '[' or '?' or '$' or '^' or ' ' or '\t' or '\n' or '\r' => + needquote = 1; + '\'' => + t[len t] = '\''; + needquote = 1; + GLOB => + if (glob) { + if (i < len s - 1) + i++; + } + } + t[len t] = s[i]; + } + if (needquote || t == nil) + t = "'" + t + "'"; + return t; +} diff --git a/appl/alphabet/main/print.b b/appl/alphabet/main/print.b new file mode 100644 index 00000000..12327934 --- /dev/null +++ b/appl/alphabet/main/print.b @@ -0,0 +1,55 @@ +implement Print,Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Print: module {}; + +typesig(): string +{ + return "rfs"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + reports = load Reports Reports->PATH; +} + +quit() +{ +} + +run(nil: ref Draw->Context, nil: ref Reports->Report, errorc: chan of string, + nil: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + r := chan of string; + fd := sys->fildes(int (hd tl args).s().i); + if(fd == nil){ + report(errorc, sys->sprint("error: no such fd %q", (hd tl args).s().i)); + return nil; + } + spawn printproc(r, (hd args).f().i, fd); + return ref Value.Vr(r); +} + +printproc(r: chan of string, f: chan of ref Sys->FD, fd: ref Sys->FD) +{ + if(<-r != nil){ + <-f; + f <-= nil; + exit; + } + <-f; + f <-= fd; + r <-= nil; +} diff --git a/appl/alphabet/main/read.b b/appl/alphabet/main/read.b new file mode 100644 index 00000000..ebc4d156 --- /dev/null +++ b/appl/alphabet/main/read.b @@ -0,0 +1,56 @@ +implement Read,Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Read: module{}; + +typesig(): string +{ + return "fs"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + reports = load Reports Reports->PATH; +} + +quit() +{ +} + +run(nil: ref Draw->Context, r: ref Reports->Report, errorc: chan of string, + nil: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + f := chan of ref Sys->FD; + file := (hd args).s().i; + if((fd0 := sys->open(file, Sys->OREAD)) == nil){ + report(errorc, sys->sprint("cannot open %q: %r", file)); + return nil; + } + spawn readproc(f, fd0, r.start("read")); + return ref Value.Vf(f); +} + +readproc(f: chan of ref Sys->FD, fd0: ref Sys->FD, errorc: chan of string) +{ + f <-= fd0; + fd1 := <-f; + if(fd1 == nil) + reports->quit(errorc); + buf := array[8192] of byte; + while((n := sys->read(fd0, buf, len buf)) > 0) + sys->write(fd1, buf, n); + sys->write(fd1, array[0] of byte, 0); + reports->quit(errorc); +} diff --git a/appl/alphabet/main/readall.b b/appl/alphabet/main/readall.b new file mode 100644 index 00000000..b8697332 --- /dev/null +++ b/appl/alphabet/main/readall.b @@ -0,0 +1,46 @@ +implement F2s, Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +F2s: module {}; + +typesig(): string +{ + return "sf"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; +} + +quit() +{ +} + +run(nil: ref Draw->Context, nil: ref Reports->Report, nil: chan of string, + nil: list of (int, list of ref Value), + args: list of ref Value): ref Value +{ + f := (hd args).f().i; + fd := <-f; + if(fd == nil){ + sys->pipe(p := array[2] of ref Sys->FD); + f <-= p[1]; + fd = p[0]; + } + s: string; + buf := array[Sys->ATOMICIO] of byte; + while((n := sys->read(fd, buf, len buf)) > 0) + s += string buf[0:n]; + return ref Value.Vs(s); +} diff --git a/appl/alphabet/main/rewrite.b b/appl/alphabet/main/rewrite.b new file mode 100644 index 00000000..96a6e205 --- /dev/null +++ b/appl/alphabet/main/rewrite.b @@ -0,0 +1,97 @@ +implement Rewrite, Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; + Context: import sh; +include "alphabet/reports.m"; + reports: Reports; + report: import reports; +include "alphabet.m"; + Value: import Alphabet; + +Rewrite: module {}; + +typesig(): string +{ + return "ccc-ds"; +} + +init() +{ + sys = load Sys Sys->PATH; + sh = load Sh Sh->PATH; + sh->initialise(); + reports = load Reports Reports->PATH; +} + +quit() +{ +} + +run(drawctxt: ref Draw->Context, nil: ref Reports->Report, errorc: chan of string, + opts: list of (int, list of ref Value), + args: list of ref Value): ref Value +{ + c := chan of ref Value; + spawn rewriteproc(drawctxt, errorc, opts, args, c); + return <-c; +} + +# we need a separate process so that we can create a shell context +# without worrying about opening an already-opened wait file. +rewriteproc(drawctxt: ref Draw->Context, errorc: chan of string, + opts: list of (int, list of ref Value), + args: list of ref Value, + c: chan of ref Value) +{ + c <-= rewrite(drawctxt, errorc, opts, args); +} + +rewrite(drawctxt: ref Draw->Context, errorc: chan of string, + opts: list of (int, list of ref Value), + args: list of ref Value): ref Value +{ + alphabet := load Alphabet Alphabet->PATH; + if(alphabet == nil){ + report(errorc, sys->sprint("rewrite: cannot load %q: %r", Alphabet->PATH)); + return nil; + } + Value: import alphabet; + alphabet->init(); + expr := (hd args).c().i; + decls := (hd tl args).c().i; + ctxt := Context.new(drawctxt); + { + ctxt.run(w("load")::w("alphabet")::nil, 0); + ctxt.run(c(decls) :: nil, 0); + dstarg: list of ref Sh->Listnode; + if(opts != nil) + dstarg = w((hd (hd opts).t1).s().i) :: nil; + ctxt.run(w("{x=${rewrite $1 $2}}") :: c(expr) :: dstarg, 0); + } exception e { + "fail:*" => + ctxt.run(w("clear")::nil, 0); + report(errorc, "rewrite failed: "+e[5:]); + return nil; + } + r := ctxt.get("x"); + if(len r != 2 || (hd r).cmd == nil){ + ctxt.run(w("clear")::nil, 0); + report(errorc, "rewrite not available, strange... (len "+string len r+")"); + return nil; + } + ctxt.run(w("clear")::nil, 0); + return ref Value.Vc((hd r).cmd); +} + +c(c: ref Sh->Cmd): ref Sh->Listnode +{ + return ref Sh->Listnode(c, nil); +} + +w(w: string): ref Sh->Listnode +{ + return ref Sh->Listnode(nil, w); +} diff --git a/appl/alphabet/main/rw.b b/appl/alphabet/main/rw.b new file mode 100644 index 00000000..74ede534 --- /dev/null +++ b/appl/alphabet/main/rw.b @@ -0,0 +1,50 @@ +implement Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, report, quit: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +typesig(): string +{ + return "fs"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + reports = load Reports Reports->PATH; +} + +run(nil: ref Draw->Context, r: ref Reports->Report, errorc: chan of string, + nil: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + f := chan of ref Sys->FD; + file := (hd args).s().i; + if((fd0 := sys->open(file, Sys->OREAD)) == nil){ + report(errorc, sys->sprint("cannot open %q: %r", file)); + return nil; + } + spawn readproc(f, fd0, r.start("read")); + return ref Value.F(f); +} + +readproc(f: chan of ref Sys->FD, fd0: ref Sys->FD, errorc: chan of string) +{ + f <-= fd0; + fd1 := <-f; + if(fd1 == nil) + quit(errorc); + buf := array[8192] of byte; + while((n := sys->read(fd0, buf, len buf)) > 0) + sys->write(fd1, buf, n); + sys->write(fd1, array[0] of byte, 0); + quit(errorc); +} diff --git a/appl/alphabet/main/seq.b b/appl/alphabet/main/seq.b new file mode 100644 index 00000000..8e062738 --- /dev/null +++ b/appl/alphabet/main/seq.b @@ -0,0 +1,66 @@ +implement Seq, Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Seq: module {}; + +typesig(): string +{ + return "rr*-a-o"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; +} + +quit() +{ +} + +run(nil: ref Draw->Context, nil: ref Reports->Report, nil: chan of string, + opts: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + stop := -1; + for(; opts != nil; opts = tl opts){ + case (hd opts).t0 { + 'a' => + stop = 0; + 'o' => + stop = 1; + } + } + spawn seqproc(r := chan of string, args, stop); + return ref Value.Vr(r); +} + +seqproc(r: chan of string, args: list of ref Alphabet->Value, stop: int) +{ + status := ""; + if(<-r == nil){ +pid := sys->pctl(0, nil); +sys->print("%d. seq %d args\n", pid, len args); + for(; args != nil; args = tl args){ + sr := (hd args).r().i; +sys->print("%d. started\n", pid); + sr <-= nil; + status = <-sr; +sys->print("%d. got status\n", pid); + if((status == nil) == stop) + break; + } + }else + r = nil; + for(; args != nil; args = tl args) + (hd args).r().i <-= "die!"; + if(r != nil) + r <-= status; +} diff --git a/appl/alphabet/main/unparse.b b/appl/alphabet/main/unparse.b new file mode 100644 index 00000000..11aa0d48 --- /dev/null +++ b/appl/alphabet/main/unparse.b @@ -0,0 +1,38 @@ +implement Unparse, Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; +include "alphabet/reports.m"; + reports: Reports; + Report, report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Unparse: module {}; + +typesig(): string +{ + return "sc"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; + sh = load Sh Sh->PATH; + sh->initialise(); +} + +quit() +{ +} + +run(nil: ref Draw->Context, nil: ref Reports->Report, nil: chan of string, + nil: list of (int, list of ref Value), + args: list of ref Value): ref Value +{ + return ref Value.Vs(sh->cmd2string((hd args).c().i)); +} diff --git a/appl/alphabet/main/w2fd.b b/appl/alphabet/main/w2fd.b new file mode 100644 index 00000000..ab7e3a70 --- /dev/null +++ b/appl/alphabet/main/w2fd.b @@ -0,0 +1,61 @@ +implement ToFD,Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report: import reports; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +ToFD: module {}; + +typesig(): string +{ + return "fw"; +} + +init() +{ + alphabet = load Alphabet Alphabet->PATH; + reports = load Reports Reports->PATH; +} + +quit() +{ +} + +run(nil: ref Draw->Context, r: ref Reports->Report, nil: chan of string, + nil: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + sys = load Sys Sys->PATH; + f := chan of ref Sys->FD; + spawn tofdproc(f, (hd args).w().i, r.start("2fd")); + return ref Value.Vf(f); +} + +tofdproc(f, w: chan of ref Sys->FD, errorc: chan of string) +{ + fd0 := <-w; + f <-= fd0; + fd1 := <-f; + if(fd1 == nil) # asked to quit? tell w to quit too. + w <-= nil; + else + if(fd0 == nil) # no proposed fd? give 'em the one we've just got. + w <-= fd1; + else{ # otherwise one-way stream from w to f. + w <-= nil; + buf := array[Sys->ATOMICIO] of byte; + while((n := sys->read(fd0, buf, len buf)) > 0){ + if(sys->write(fd1, buf, n) == -1){ + reports->report(errorc, sys->sprint("write error: %r")); + break; + } + } + } + reports->quit(errorc); +} diff --git a/appl/alphabet/main/wait.b b/appl/alphabet/main/wait.b new file mode 100644 index 00000000..04bf88c6 --- /dev/null +++ b/appl/alphabet/main/wait.b @@ -0,0 +1,35 @@ +implement Wait, Mainmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; +include "alphabet.m"; + alphabet: Alphabet; + Value: import alphabet; + +Wait: module {}; + +typesig(): string +{ + return "sr"; +} + +init() +{ + sys = load Sys Sys->PATH; + alphabet = load Alphabet Alphabet->PATH; +} + +quit() +{ +} + +run(nil: ref Draw->Context, nil: ref Reports->Report, nil: chan of string, + nil: list of (int, list of ref Alphabet->Value), + args: list of ref Alphabet->Value): ref Alphabet->Value +{ + r := (hd args).r().i; + r <-= nil; + return ref Value.Vs(<-r); +} diff --git a/appl/alphabet/mkendpoint.sh b/appl/alphabet/mkendpoint.sh new file mode 100755 index 00000000..bfd4800b --- /dev/null +++ b/appl/alphabet/mkendpoint.sh @@ -0,0 +1,14 @@ +#!/dis/sh -n +autoload=std +load std +if{! ~ $#* 1}{ + echo usage: mkendpoint addr >[1=2] + raise usage +} +addr:=$1 +if{! ftest -e /n/endpoint/dsgdsfgeafreqeq}{ + mount {mntgen} /n/endpoint +} +mount {pctl forkns; alphabet/endpointsrv $addr /n; export /n} /n/endpoint/$addr +bind /n/endpoint/$addr /n/endpoint/local +styxlisten -A $addr {export /n/endpoint/local} diff --git a/appl/alphabet/mkfile b/appl/alphabet/mkfile new file mode 100644 index 00000000..36465f49 --- /dev/null +++ b/appl/alphabet/mkfile @@ -0,0 +1,49 @@ +<../../mkconfig +DIRS=\ + typesets\ + auxi\ + abc\ + fs\ + grid\ + main\ + +TARG=\ + alphabet.dis\ + alphabet.shmod.dis\ + eval.dis\ + extvalues.dis\ + proxy.dis\ + reports.dis\ + +INS=${TARG:%=$ROOT/dis/alphabet/%} \ + $ROOT/dis/sh/alphabet.dis + +MODULES=\ + +SYSMODULES=\ + alphabet.m\ + alphabet/abc.m\ + alphabet/reports.m\ + draw.m\ + readdir.m\ + sets.m\ + sh.m\ + sys.m\ + +DISBIN=$ROOT/dis/alphabet + +<$ROOT/mkfiles/mkdis +LIMBOFLAGS=-F $LIMBOFLAGS +install:V: $INS + +nuke:V: clean + rm -f $INS + +uninstall:V: + rm -f $INS + +$ROOT/dis/sh/alphabet.dis: alphabet.shmod.dis + rm -f $ROOT/dis/sh/alphabet.dis && cp alphabet.shmod.dis $ROOT/dis/sh/alphabet.dis + +<$ROOT/mkfiles/mksubdirs + diff --git a/appl/alphabet/newtypesets b/appl/alphabet/newtypesets new file mode 100644 index 00000000..29048a6a --- /dev/null +++ b/appl/alphabet/newtypesets @@ -0,0 +1,229 @@ +arithmetic typeset: + + int + big + real + + i+i int int -> int + + {(int); {i+i $1 10}} + + + + {i+i 12 34} | {i*i 10} | { + + Expr: adt { + pick { + Op => + op: int; + l: ref Expr; + r: ref Expr; + Int => + i: int; + Big => + i: big; + Real => + i: real; + } + }; + + + {int 12} { + + when we come to run the expression, say in module + generate limbo code containing function + + gen(hd args); + gen("+"); + gen(hd tl args); + compile(); + + output limbo code might look like: + + implement M; + M: module { + f: fn(a, b: int): int; + }; + + f(a, b: int): int + { + return (a + + +graphics: + + rect point point -> rect + point string string -> point + + x point -> string + y point -> string + + r string [string...] -> rect + r.canon rect -> rect + r.min rect -> point + r.max rect -> point + r.dx rect -> string + r.dy rect -> string + r.combine rect rect -> rect + r+p rect point -> rect + r-p rect point -> rect + r.inset rect string -> rect + + image [-r] [-b string] [-c string] rect -> image + draw [-o string] image point image -> image + win [-t string] rect -> image + + tkwin [-t string] rect -> tk + tk tk string -> tk + +{(rect); r {min $1|x} {min $1|y} {max $1|x} {max $1|y}} + +if we wish to be at all efficient, we need to deal with chans +not single values. + + r: chan of Rect; + +or do we? +if we had some way of expressing combinations +of external modules, then perhaps an external +typeset could do a reasonable job of interpreting stuff. + +if a typeset can build expressions bottom-up, incrementally +out of its own components... + +when we're rewriting an expression, we could rewrite it +in terms of module units provided by the underlying +typeset... when we ask to find a module, the typeset +can return some other info as well + +we can give the underlying typeset an opportunity +to optimise the expression, if some of its arguments are +modules from the same typeset, or from a parent/grandparent +typeset. + +on Load, the typeset could be given expressions representing +each of its arguments. it then has the opportunity to rewrite +this whole expression, yielding a module customised for the +particular arguments it's been given. + +perhaps a typeset could assign ids to each module it has returned, +so that it could easily look up... +of course, the arguments to the expression consist either +of modules external to the typeset (no optimisation possible), +or of modules that have already been loaded by the typeset +(or by its parent), in which case we can retrieve info on them +and decide what sort of optimisation might be possible. + +there's a moment when you should actually have +the opportunity to compile optimised code +(when the expression is passed to another typeset's module?) + +--- + +what about expression types, and allowing access to expressions +from within the context of a particular typeset. + +perhaps any typeset could be treated as the root +typeset for the purposes of a particular expression evaluation: + +what about + + {(/grid/data /fs/fs) + /grid/local $1 | + /fs/unbundle | + /fs/merge $2 + } + +when we wish to pass $1 and $2 from our own program? + +rewritten: + /fs/merge {/fs/unbundle {/grid/local $1}} $2} + +so reduces to + + fd := {grid/local $1} # in /grid/typeset + result := {/fs/merge {/fs/unbundle $fd} $2} # in /fs typeset + +maybe not possible. (bollocks) + +--- + +typeset for the control library. + + +decl { + declare read (string >> fd) + define hello (string >> fd) {(string); read $1} + + +abc typeset + + declare [-t string] abc string string -> abc + typeset abc string -> abc + define abc string cmd -> abc + eval abc cmd -> any + +{ + abc | + declare read (string >> fd) | + define wc (fd >> fd) | + define readfile {(>>fd); read /tmp/blah} +} | {(abc); + eval $1 "{ + read + +compile string >> expr + +compile string >> (abc string >> expr) + +compile '100 + 12 * sin($1)' + + +transform fd (string >> string) >> fd + + +---- + +descendant typesets problem... + +we can't tell which types are identical. + +when we load a typeset, we have to look at its parent +typeset and use its types if the typec characters are contained there. + + +---- + +if we allow expression types, we have to be very careful... +can get recursion (equivalent to Y-combinator in λ calculus): + +declare eval (cmd->cmd) [(cmd->cmd)...] -> (cmd->cmd) + +{((cmd->cmd)->(cmd->cmd)) + {((cmd->cmd)->(cmd->cmd)) + eval $1 $1 + } "{((cmd->cmd)->(cmd->cmd)) + eval $1 $1 + } +} + +note this isn't possible without an eval operator and/or +something that admits a cyclic expression type evaluation. + +note also that if this was done in the current implementation, +it would just hang, as two runs can't be outstanding at the same time (monitor channel). + +----- + +records: + +apply1 records (data -> status) -> records +apply records (data -> status) -> status + +filter records (data -> data) -> records +filter1 records (data -> data) -> records + +discard records -> status + +| apply1 "{ + | data2fd | /fs/unbundle | /fs/write somewhere +} | apply "{ + | \ No newline at end of file diff --git a/appl/alphabet/proxy.b b/appl/alphabet/proxy.b new file mode 100644 index 00000000..4d90145b --- /dev/null +++ b/appl/alphabet/proxy.b @@ -0,0 +1,304 @@ +implement Proxy; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; +include "alphabet.m"; + +Debug: con 0; + +proxy[Ctxt,Cvt,M,V,EV](ctxt: Ctxt): ( + chan of ref Typescmd[EV], + chan of (string, chan of ref Typescmd[V]) + ) for { + M => + typesig: fn(m: self M): string; + run: fn(m: self M, ctxt: ref Draw->Context, r: ref Reports->Report, errorc: chan of string, + opts: list of (int, list of V), args: list of V): V; + quit: fn(m: self M); + Ctxt => + loadtypes: fn(ctxt: self Ctxt, name: string): (chan of ref Proxy->Typescmd[V], string); + type2s: fn(ctxt: self Ctxt, tc: int): string; + alphabet: fn(ctxt: self Ctxt): string; + modules: fn(ctxt: self Ctxt, r: chan of string); + find: fn(ctxt: self Ctxt, s: string): (M, string); + getcvt: fn(ctxt: self Ctxt): Cvt; + Cvt => + int2ext: fn(cvt: self Cvt, v: V): EV; + ext2int: fn(cvt: self Cvt, ev: EV): V; + free: fn(cvt: self Cvt, v: EV, used: int); + dup: fn(cvt: self Cvt, v: EV): EV; + } +{ + sys = load Sys Sys->PATH; + t := chan of ref Typescmd[EV]; + newts := chan of (string, chan of ref Typescmd[V]); + spawn proxyproc(ctxt, t, newts); + return (t, newts); +} + +proxyproc[Ctxt,Cvt,M,V,EV]( + ctxt: Ctxt, + t: chan of ref Typescmd[EV], + newts: chan of (string, chan of ref Typescmd[V]) + ) + for{ + M => + typesig: fn(m: self M): string; + run: fn(m: self M, ctxt: ref Draw->Context, r: ref Reports->Report, errorc: chan of string, + opts: list of (int, list of V), args: list of V): V; + quit: fn(m: self M); + Ctxt => + loadtypes: fn(ctxt: self Ctxt, name: string): (chan of ref Proxy->Typescmd[V], string); + type2s: fn(ctxt: self Ctxt, tc: int): string; + alphabet: fn(ctxt: self Ctxt): string; + modules: fn(ctxt: self Ctxt, r: chan of string); + find: fn(ctxt: self Ctxt, s: string): (M, string); + getcvt: fn(ctxt: self Ctxt): Cvt; + Cvt => + int2ext: fn(cvt: self Cvt, v: V): EV; + ext2int: fn(cvt: self Cvt, ev: EV): V; + free: fn(cvt: self Cvt, v: EV, used: int); + dup: fn(cvt: self Cvt, v: EV): EV; + } +{ + typesets: list of (string, chan of ref Typescmd[V]); + cvt := ctxt.getcvt(); + for(;;)alt{ + gr := <-t => + if(gr == nil){ + for(; typesets != nil; typesets = tl typesets) + (hd typesets).t1 <-= nil; + exit; + } + pick r := gr { + Load => + (m, err) := ctxt.find(r.cmd); + if(m == nil){ + r.reply <-= (nil, err); + }else{ + c := chan of ref Modulecmd[EV]; + spawn modproxyproc(cvt, m, c); + r.reply <-= (c, nil); + } + Alphabet => + r.reply <-= ctxt.alphabet(); + Free => + cvt.free(r.v, r.used); + r.reply <-= 0; + Dup => + r.reply <-= cvt.dup(r.v); + Type2s => + r.reply <-= ctxt.type2s(r.tc); + Loadtypes => + ts := typesets; + typesets = nil; + c: chan of ref Typescmd[V]; + for(; ts != nil; ts = tl ts){ + if((hd ts).t0 == r.name) + c = (hd ts).t1; + else + typesets = hd ts :: typesets; + } + err: string; + if(c == nil) + (c, err) = ctxt.loadtypes(r.name); + if(c == nil) + r.reply <-= (nil, err); + else{ + et := chan of ref Typescmd[EV]; + spawn extproxyproc(ctxt, ctxt.alphabet(), c, et); + r.reply <-= (et, nil); + } + Modules => + spawn ctxt.modules(r.reply); + * => + sys->fprint(sys->fildes(2), "unknown type of proxy request %d\n", tagof gr); + raise "unknown type proxy request"; + } + typesets = <-newts :: typesets => + ; + } +} + +modproxyproc[Cvt,V,EV,M](cvt: Cvt, m: M, c: chan of ref Modulecmd[EV]) + for{ + M => + typesig: fn(m: self M): string; + run: fn(m: self M, ctxt: ref Draw->Context, r: ref Reports->Report, errorc: chan of string, + opts: list of (int, list of V), args: list of V): V; + quit: fn(m: self M); + Cvt => + int2ext: fn(cvt: self Cvt, v: V): EV; + ext2int: fn(cvt: self Cvt, ev: EV): V; + free: fn(cvt: self Cvt, ev: EV, used: int); + } +{ + while((gr := <-c) != nil){ + pick r := gr { + Typesig => + r.reply <-= m.typesig(); + Run => + # XXX could start (or invoke) a new process so that we don't potentially + # block concurrency while we're starting the command. + { + iopts: list of (int, list of V); + for(o := r.opts; o != nil; o = tl o){ + il := extlist2intlist(cvt, (hd o).t1); + iopts = ((hd o).t0, il) :: iopts; + } + iopts = revip(iopts); + v := cvt.int2ext(m.run(r.ctxt, r.report, r.errorc, iopts, extlist2intlist(cvt, r.args))); + free(cvt, r.opts, r.args, v != nil); + r.reply <-= v; + } exception { + "type error" => + if(Debug) + sys->fprint(sys->fildes(2), "error: type conversion failed"); + if(r.errorc != nil) + r.errorc <-= "error: type conversion failed"; + r.reply <-= nil; + } + } + } + m.quit(); +} + +extproxyproc[Ctxt,Cvt,V,EV](ctxt: Ctxt, alphabet: string, t: chan of ref Typescmd[V], et: chan of ref Typescmd[EV]) + for{ + Ctxt => + type2s: fn(ctxt: self Ctxt, tc: int): string; + getcvt: fn(ctxt: self Ctxt): Cvt; + Cvt => + int2ext: fn(cvt: self Cvt, v: V): EV; + ext2int: fn(cvt: self Cvt, ev: EV): V; + free: fn(cvt: self Cvt, ev: EV, used: int); + dup: fn(cvt: self Cvt, ev: EV): EV; + } +{ + cvt := ctxt.getcvt(); + for(;;){ + gr := <-et; + if(gr == nil) + break; + pick r := gr { + Load => + reply := chan of (chan of ref Modulecmd[V], string); + t <-= ref Typescmd[V].Load(r.cmd, reply); + (c, err) := <-reply; + if(c == nil){ + r.reply <-= (nil, err); + }else{ + ec := chan of ref Modulecmd[EV]; + spawn extmodproxyproc(cvt, c, ec); + r.reply <-= (ec, nil); + } + Alphabet => + t <-= ref Typescmd[V].Alphabet(r.reply); + Free => + cvt.free(r.v, r.used); + Dup => + r.reply <-= cvt.dup(r.v); + Type2s => + for(i := 0; i < len alphabet; i++) + if(alphabet[i] == r.tc) + break; + if(i == len alphabet) + t <-= ref Typescmd[V].Type2s(r.tc, r.reply); + else + r.reply <-= ctxt.type2s(r.tc); + Loadtypes => + reply := chan of (chan of ref Typescmd[V], string); + t <-= ref Typescmd[V].Loadtypes(r.name, reply); + (c, err) := <-reply; + if(c == nil) + r.reply <-= (nil, err); + else{ + t <-= ref Typescmd[V].Alphabet(areply := chan of string); + ec := chan of ref Typescmd[EV]; + spawn extproxyproc(ctxt, <-areply, c, ec); + r.reply <-= (ec, nil); + } + Modules => + t <-= ref Typescmd[V].Modules(r.reply); + * => + sys->fprint(sys->fildes(2), "unknown type of proxy request %d\n", tagof gr); + raise "unknown type proxy request"; + } + } + et <-= nil; +} + +extmodproxyproc[Cvt,V,EV](cvt: Cvt, c: chan of ref Modulecmd[V], ec: chan of ref Modulecmd[EV]) + for{ + Cvt => + int2ext: fn(cvt: self Cvt, v: V): EV; + ext2int: fn(cvt: self Cvt, ev: EV): V; + free: fn(cvt: self Cvt, ev: EV, used: int); + } +{ + while((gr := <-ec) != nil){ + pick r := gr { + Typesig => + c <-= ref Modulecmd[V].Typesig(r.reply); + Run => + { + iopts: list of (int, list of V); + for(o := r.opts; o != nil; o = tl o){ + il := extlist2intlist(cvt, (hd o).t1); + iopts = ((hd o).t0, il) :: iopts; + } + iopts = revip(iopts); + c <-= ref Modulecmd[V].Run( + r.ctxt, + r.report, + r.errorc, + iopts, + extlist2intlist(cvt, r.args), + reply := chan of V + ); + v := cvt.int2ext(<-reply); + free(cvt, r.opts, r.args, v != nil); + r.reply <-= v; + } + } + } +} + + +revip[V](l: list of (int, V)): list of (int, V) +{ + m: list of (int, V); + for(; l != nil; l = tl l) + m = hd l :: m; + return m; +} + +extlist2intlist[V,EV,Cvt](cvt: Cvt, vl: list of EV): list of V + for{ + Cvt => + int2ext: fn(cvt: self Cvt, v: V): EV; + ext2int: fn(cvt: self Cvt, ev: EV): V; + } +{ + l, m: list of V; + for(; vl != nil; vl = tl vl) + l = cvt.ext2int(hd vl) :: l; + for(; l != nil; l = tl l) + m = hd l :: m; + return m; +} + +free[V,Cvt](cvt: Cvt, opts: list of (int, list of V), args: list of V, used: int) + for{ + Cvt => + free: fn(cvt: self Cvt, ev: V, used: int); + } +{ + for(; args != nil; args = tl args) + cvt.free(hd args, used); + for(; opts != nil; opts = tl opts) + for(args = (hd opts).t1; args != nil; args = tl args) + cvt.free(hd args, used); +} diff --git a/appl/alphabet/reports.b b/appl/alphabet/reports.b new file mode 100644 index 00000000..d9336d8b --- /dev/null +++ b/appl/alphabet/reports.b @@ -0,0 +1,189 @@ +implement Reports; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + +Reporter: adt { + id: int; + name: string; + stopc: chan of int; +}; + +reportproc(errorc: chan of string, stopc: chan of int, reply: chan of ref Report) +{ + if(sys == nil) + sys = load Sys Sys->PATH; + r := ref Report(chan of (string, chan of string, chan of int), chan of int); + if(stopc == nil) + stopc = chan of int; + else + sys->pctl(Sys->NEWPGRP, nil); + reply <-= r; + reportproc0(stopc, errorc, r.startc, r.enablec); +} + +Report.start(r: self ref Report, name: string): chan of string +{ + if(r == nil) + return nil; + errorc := chan of string; + r.startc <-= (name, errorc, nil); + return errorc; +} + +Report.add(r: self ref Report, name: string, errorc: chan of string, stopc: chan of int) +{ + r.startc <-= (name, errorc, stopc); +} + +Report.enable(r: self ref Report) +{ + r.enablec <-= 0; +} + +reportproc0( + stopc: chan of int, + reportc: chan of string, + startc: chan of (string, chan of string, chan of int), + enablec: chan of int + ) +{ + realc := array[2] of chan of string; + p := array[len realc] of Reporter; + a := array[0] of chan of string; + id := n := 0; + stopped := 0; +out: + for(;;) alt{ + <-stopc => + stopped = 1; + break out; + (prefix, c, stop) := <-startc => + if(n == len realc){ + if(realc == a) + a = nil; + realc = (array[n * 2] of chan of string)[0:] = realc; + p = (array[n * 2] of Reporter)[0:] = p; + if(a == nil) + a = realc; + } + realc[n] = c; + p[n] = (id++, prefix, stop); + n++; + <-enablec => + if(n == 0) + break out; + a = realc; + (x, msg) := <-a => + if(msg == nil){ + if(--n == 0) + break out; + if(n != x){ + a[x] = a[n]; + a[n] = nil; + p[x] = p[n]; + p[n] = (-1, nil, nil); + } + }else{ + if(reportc != nil){ + alt{ + reportc <-= sys->sprint("%d. %s: %s", p[x].id, p[x].name, msg) => + ; + <-stopc => + stopped = 1; + break out; + } + } + } + } + if(stopped == 0){ + if(reportc != nil){ + alt{ + reportc <-= nil => + ; + <-stopc => + stopped = 1; + } + } + } + if(stopped){ + for(i := 0; i < n; i++) + note(p[i].stopc); + note(stopc); + } +} + +quit(errorc: chan of string) +{ + if(errorc != nil) + errorc <-= nil; + exit; +} + +report(errorc: chan of string, err: string) +{ + if(errorc != nil) + errorc <-= err; +} + +newpgrp(stopc: chan of int, flags: int): chan of int +{ + if(sys == nil) + sys = load Sys Sys->PATH; + if(flags&PROPAGATE){ + if(stopc == nil) + stopc = chan[1] of int; + sys->pipe(p := array[2] of ref Sys->FD); + spawn deadman(p[1]); + sys->pctl(Sys->NEWPGRP, nil); + spawn watchproc(p[0], stopc); + }else + sys->pctl(Sys->NEWPGRP, nil); + spawn grpproc(stopc, newstopc := chan[1] of int, flags&KILL); + return newstopc; +} + +grpproc(noteparent, noteself: chan of int, kill: int) +{ + if(noteparent == nil) + noteparent = chan of int; + alt{ + <-noteparent => + note(noteparent); + <-noteself => + ; + } + note(noteself); + if(kill){ + pid := sys->pctl(0, nil); + fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); + if(fd == nil) + fd = sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE); + sys->fprint(fd, "killgrp"); + } +} + +note(c: chan of int) +{ + if(c != nil){ + alt { + c <-= 1 => + ; + * => + ; + } + } +} + +deadman(nil: ref Sys->FD) +{ + <-chan of int; +} + +watchproc(fd: ref Sys->FD, stopc: chan of int) +{ + sys->read(fd, array[1] of byte, 1); + note(stopc); +} diff --git a/appl/alphabet/rexecsrv.sh b/appl/alphabet/rexecsrv.sh new file mode 100755 index 00000000..45987e9f --- /dev/null +++ b/appl/alphabet/rexecsrv.sh @@ -0,0 +1,9 @@ +#!/dis/sh +if{! ~ $#* 2}{ + echo usage rexecsrv net!addr decls >[1=2] + raise usage +} +(addr decls) := $* +/appl/alphabet/mkendpoint.sh $addr!2222 +alphabet/rexecsrv /n/cd $decls +listen -v $addr!2223 {export /n/cd&} diff --git a/appl/alphabet/setup b/appl/alphabet/setup new file mode 100644 index 00000000..3eb9a037 --- /dev/null +++ b/appl/alphabet/setup @@ -0,0 +1,63 @@ +/appl/alphabet/rexecsrv.sh tcp!rogero {typeset /fs; import /fs/unbundle /fs/entries /fs/print} + +#################### +addr=tcp!rogero!1234 +run /appl/alphabet/declare.sh +/appl/alphabet/mkendpoint.sh $addr +echo ${rewrite { + /echo hello | + /grid/remote | + /grid/rexec tcp!rogero!1235 "{(/fd);/filter $1 "{wc}} + } +} +- { + /echo hello | + /grid/remote | + /grid/rexec tcp!rogero!1235 "{(/fd);/filter $1 "{wc}} | + /grid/local + } +# - {remote /n/local/lib/words | farm rogero!1235 "{tr -d e} } | /grid/local} +###################### + +/appl/alphabet/mkendpoint.sh tcp!rogero!9998 +load alphabet +run /appl/alphabet/declare.sh +- { + /fs/walk /tmp | + /fs/bundle | + /grid/remote | + /grid/rexec tcp!rogero!1235 "{ + (/fd) + /fs/unbundle $1 | + /fs/entries | + /fs/print + } +} + +- { + /fs/walk /tmp | + /fs/bundle | + /grid/remote | + /grid/local | + /fs/unbundle | + /fs/print +} + +############### + +the below script generates: + +alphabet: 2. bundle: write error: i/o on hungup channel +and a much truncated file. + +-{ + /fs/walk /tmp | + /fs/bundle | + /grid/remote | + /grid/rexec tcp!127.1!1235 "{ + (/fd) + /fs/unbundle $1 | + /fs/filter -d {/fs/match '*.b'} | + /fs/bundle + } | /create xx +} diff --git a/appl/alphabet/typesets/abc.b b/appl/alphabet/typesets/abc.b new file mode 100644 index 00000000..d2a060fd --- /dev/null +++ b/appl/alphabet/typesets/abc.b @@ -0,0 +1,180 @@ +# warning: autogenerated code; don't bother to change this, change mktypeset.b or abc.b instead +implement Abc; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; +include "alphabet.m"; +include "abc.m"; +mkabc(a: Alphabet): ref Value.VA +{ + r := chan[1] of int; + r <-= 1; + return ref Value.VA((r, a)); +} + +valuec := array[] of { + tagof(Value.Vm) => 'm', + tagof(Value.Vt) => 't', + tagof(Value.VA) => 'A', + tagof(Value.Vw) => 'w', + tagof(Value.Vc) => 'c', + tagof(Value.Vr) => 'r', + tagof(Value.Vf) => 'f', + tagof(Value.Vs) => 's', +}; + +init() +{ + sys = load Sys Sys->PATH; +} + +Value.type2s(c: int): string +{ + case c { + 'm' => + return "vmods"; + 't' => + return "vtypes"; + 'A' => + return "abc"; + 'w' => + return "wfd"; + 'c' => + return "cmd"; + 'r' => + return "status"; + 'f' => + return "fd"; + 's' => + return "string"; + * => + return sys->sprint("unknowntype('%c')", c); + } +} + +typeerror(tc: int, v: ref Value): string +{ + sys->fprint(sys->fildes(2), "fs: bad type conversion, expected %s, was actually %s\n", Value.type2s(tc), Value.type2s(valuec[tagof v])); + return "type conversion error"; +} + +Value.m(v: self ref Value): ref Value.Vm +{ + pick xv := v {Vm => return xv;} + raise typeerror('m', v); +} + +Value.t(v: self ref Value): ref Value.Vt +{ + pick xv := v {Vt => return xv;} + raise typeerror('t', v); +} + +Value.A(v: self ref Value): ref Value.VA +{ + pick xv := v {VA => return xv;} + raise typeerror('A', v); +} + +Value.w(v: self ref Value): ref Value.Vw +{ + pick xv := v {Vw => return xv;} + raise typeerror('w', v); +} + +Value.c(v: self ref Value): ref Value.Vc +{ + pick xv := v {Vc => return xv;} + raise typeerror('c', v); +} + +Value.r(v: self ref Value): ref Value.Vr +{ + pick xv := v {Vr => return xv;} + raise typeerror('r', v); +} + +Value.f(v: self ref Value): ref Value.Vf +{ + pick xv := v {Vf => return xv;} + raise typeerror('f', v); +} + +Value.s(v: self ref Value): ref Value.Vs +{ + pick xv := v {Vs => return xv;} + raise typeerror('s', v); +} + +Value.typec(v: self ref Value): int +{ + return valuec[tagof v]; +} + +Value.dup(xv: self ref Value): ref Value +{ + if(xv == nil) + return nil; + pick v := xv { + Vm => + v = nil; + xv = v; + Vt => + v = nil; + xv = v; + VA => + a := v.A().i; + a.refcount <-= <-a.refcount + 1; + xv = v; + Vw => + v = nil; + xv = v; + Vr => + v = nil; + xv = v; + Vf => + v = nil; + xv = v; + } + return xv; +} + +Value.free(xv: self ref Value, used: int) +{ + if(xv == nil) + return; + pick v := xv { + Vm => + if(!used){ + v.i.abc.free(0); + } + Vt => + if(!used){ + v.i.abc.free(0); + } + VA => + r := v.i.refcount <-= <-v.i.refcount - 1; + if(r == 0){ + v.i.alphabet->quit(); + v.i.alphabet = nil; + v.i.refcount = nil; + } + Vw => + if(!used){ + <-v.i; + v.i <-= nil; + } + Vr => + if(!used){ + v.i <-= "stop"; + } + Vf => + if(!used){ + <-v.i; + v.i <-= nil; + } + } +} + diff --git a/appl/alphabet/typesets/abctypes.b b/appl/alphabet/typesets/abctypes.b new file mode 100644 index 00000000..4fd5d24d --- /dev/null +++ b/appl/alphabet/typesets/abctypes.b @@ -0,0 +1,229 @@ +# warning: autogenerated code; don't bother to change this, change mktypeset.b or abc.b instead +implement Abctypes; +include "sys.m"; + sys: Sys; +include "alphabet/reports.m"; +include "draw.m"; +include "sh.m"; +include "alphabet.m"; + extvalues: Extvalues; + Values: import extvalues; + proxymod: Proxy; + Typescmd, Modulecmd: import Proxy; +include "abc.m"; + abc: Abc; + Value: import abc; +include "abctypes.m"; + +Pcontext: adt { + cvt: ref Abccvt; + ctxt: ref Context; + + loadtypes: fn(ctxt: self ref Pcontext, name: string): (chan of ref Proxy->Typescmd[ref Value], string); + type2s: fn(ctxt: self ref Pcontext, tc: int): string; + alphabet: fn(ctxt: self ref Pcontext): string; + modules: fn(ctxt: self ref Pcontext, r: chan of string); + find: fn(ctxt: self ref Pcontext, s: string): (ref Module, string); + getcvt: fn(ctxt: self ref Pcontext): ref Abccvt; +}; + +proxy(): chan of ref Typescmd[ref Alphabet->Value] +{ + return proxy0().t0; +} + +proxy0(): ( + chan of ref Typescmd[ref Alphabet->Value], + chan of (string, chan of ref Typescmd[ref Abc->Value]), + ref Abccvt + ) +{ + sys = load Sys Sys->PATH; + extvalues = checkload(load Extvalues Extvalues->PATH, Extvalues->PATH); + proxymod = checkload(load Proxy Proxy->PATH, Proxy->PATH); + abc = checkload(load Abc Abc->PATH, Abc->PATH); + abc->init(); + cvt := ref Abccvt(Values[ref Value].new()); + (t, newts) := proxymod->proxy(ref Pcontext(cvt, Context.new())); + return (t, newts, cvt); +} + +include "readdir.m"; +Context: adt { + modules: fn(ctxt: self ref Context, r: chan of string); + loadtypes: fn(ctxt: self ref Context, name: string) + : (chan of ref Proxy->Typescmd[ref Value], string); + find: fn(ctxt: self ref Context, s: string): (ref Module, string); + new: fn(): ref Context; +}; +Module: adt { + m: Abcmodule; + run: fn(m: self ref Module, ctxt: ref Draw->Context, r: ref Reports->Report, + errorc: chan of string, opts: list of (int, list of ref Value), + args: list of ref Value): ref Value; + typesig: fn(m: self ref Module): string; + quit: fn(m: self ref Module); +}; +Context.new(): ref Context +{ + return nil; +} +Context.loadtypes(nil: self ref Context, name: string): (chan of ref Typescmd[ref Value], string) +{ + p := "/dis/alphabet/abc/"+name+"types.dis"; + types := load Abcsubtypes p; + if(types == nil) + return (nil, sys->sprint("cannot load %q: %r", p)); + return (types->proxy(), nil); +} +Context.modules(nil: self ref Context, r: chan of string) +{ + if((readdir := load Readdir Readdir->PATH) != nil){ + (a, nil) := readdir->init("/dis/alphabet/abc", Readdir->NAME|Readdir->COMPACT); + for(i := 0; i < len a; i++){ + m := a[i].name; + if((a[i].mode & Sys->DMDIR) == 0 && len m > 4 && m[len m - 4:] == ".dis") + r <-= m[0:len m - 4]; + } + } + r <-= nil; +} +Context.find(nil: self ref Context, s: string): (ref Module, string) +{ + p := "/dis/alphabet/abc/"+s+".dis"; + m := load Abcmodule p; + if(m == nil) + return (nil, sys->sprint("cannot load %q: %r", p)); + { + m->init(); + } exception e { + "fail:*" => + return (nil, "init failed: " + e[5:]); + } + return (ref Module(m), nil); +} +Module.run(m: self ref Module, nil: ref Draw->Context, r: ref Reports->Report, errorc: chan of string, + opts: list of (int, list of ref Value), args: list of ref Value): ref Value +{ + return m.m->run(errorc, r, opts, args); +} +Module.typesig(m: self ref Module): string +{ + return m.m->types(); +} +Module.quit(nil: self ref Module) +{ +} +Pcontext.type2s(nil: self ref Pcontext, tc: int): string +{ + return Value.type2s(tc); +} + +Pcontext.alphabet(nil: self ref Pcontext): string +{ + return "mtAwcrfs"; +} + +Pcontext.getcvt(ctxt: self ref Pcontext): ref Abccvt +{ + return ctxt.cvt; +} + +Pcontext.find(ctxt: self ref Pcontext, s: string): (ref Module, string) +{ + return ctxt.ctxt.find(s); +} + +Pcontext.modules(ctxt: self ref Pcontext, r: chan of string) +{ + ctxt.ctxt.modules(r); +} + +Pcontext.loadtypes(ctxt: self ref Pcontext, name: string): (chan of ref Typescmd[ref Value], string) +{ + return ctxt.ctxt.loadtypes(name); +} + +Abccvt.int2ext(cvt: self ref Abccvt, gv: ref Value): ref Alphabet->Value +{ + if(gv == nil) + return nil; + pick v := gv { + Vw => + return ref (Alphabet->Value).Vw(v.i); + Vf => + return ref (Alphabet->Value).Vf(v.i); + Vr => + return ref (Alphabet->Value).Vr(v.i); + Vs => + return ref (Alphabet->Value).Vs(v.i); + Vc => + return ref (Alphabet->Value).Vc(v.i); + * => + id := cvt.values.add(gv); + return ref (Alphabet->Value).Vz((gv.typec(), id)); + } +} + +Abccvt.ext2int(cvt: self ref Abccvt, ev: ref Alphabet->Value): ref Value +{ + if(ev == nil) + return nil; + pick v := ev { + Vd => + return nil; # can't happen + Vw => + return ref Value.Vw(v.i); + Vf => + return ref Value.Vf(v.i); + Vr => + return ref Value.Vr(v.i); + Vs => + return ref Value.Vs(v.i); + Vc => + return ref Value.Vc(v.i); + Vz => + x := cvt.values.v[v.i.id].t1; + if(x == nil){ + sys->print("abctypes: bad id %d, type %c\n", v.i.id, v.i.typec); + return nil; + } + return x; + } +} + +Abccvt.free(cvt: self ref Abccvt, gv: ref Alphabet->Value, used: int) +{ + pick v := gv { + Vz => + id := v.i.id; + cvt.values.v[id].t1.free(used); + cvt.values.del(id); + } +} + +Abccvt.dup(cvt: self ref Abccvt, gv: ref Alphabet->Value): ref Alphabet->Value +{ + pick ev := gv { + Vz => + id := ev.i.id; + v := cvt.values.v[id].t1; + nv := v.dup(); + if(nv == nil) + return nil; + if(nv != v) + return ref (Alphabet->Value).Vz((ev.i.typec, cvt.values.add(nv))); + cvt.values.inc(id); + return ev; + * => + return nil; + } +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + sys->fprint(sys->fildes(2), "abctypes: cannot load %s: %r\n", path); + raise "fail:bad module"; +} diff --git a/appl/alphabet/typesets/fs.b b/appl/alphabet/typesets/fs.b new file mode 100644 index 00000000..9b50d13f --- /dev/null +++ b/appl/alphabet/typesets/fs.b @@ -0,0 +1,226 @@ +# warning: autogenerated code; don't bother to change this, change mktypeset.b or fs.b instead +implement Fs; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; +include "fs.m"; +sendnulldir(c: Fschan): int +{ + reply := chan of int; + c <-= ((ref Sys->nulldir, nil), reply); + if((r := <-reply) == Down){ + c <-= ((nil, nil), reply); + if(<-reply != Quit) + return Quit; + return Next; + } + return r; +} +# copy the contents (not the entry itself) of a directory from src to dst. +copy(src, dst: Fschan): int +{ + indent := 1; + myreply := chan of int; + for(;;){ + (d, reply) := <-src; + dst <-= (d, myreply); + r := <-myreply; + case reply <-= r { + Quit => + return Quit; + Next => + if(d.dir == nil && d.data == nil) + if(--indent == 0) + return Next; + Skip => + if(--indent == 0) + return Next; + Down => + if(d.dir != nil || d.data != nil) + indent++; + } + } +} + +valuec := array[] of { + tagof(Value.Vr) => 'r', + tagof(Value.Vd) => 'd', + tagof(Value.Vc) => 'c', + tagof(Value.Vf) => 'f', + tagof(Value.Vs) => 's', + tagof(Value.Vm) => 'm', + tagof(Value.Vp) => 'p', + tagof(Value.Vt) => 't', + tagof(Value.Vx) => 'x', +}; + +init() +{ + sys = load Sys Sys->PATH; +} + +Value.type2s(c: int): string +{ + case c { + 'r' => + return "status"; + 'd' => + return "data"; + 'c' => + return "command"; + 'f' => + return "fd"; + 's' => + return "string"; + 'm' => + return "selector"; + 'p' => + return "gate"; + 't' => + return "entries"; + 'x' => + return "fs"; + * => + return sys->sprint("unknowntype('%c')", c); + } +} + +typeerror(tc: int, v: ref Value): string +{ + sys->fprint(sys->fildes(2), "fs: bad type conversion, expected %s, was actually %s\n", Value.type2s(tc), Value.type2s(valuec[tagof v])); + return "type conversion error"; +} + +Value.r(v: self ref Value): ref Value.Vr +{ + pick xv := v {Vr => return xv;} + raise typeerror('r', v); +} + +Value.d(v: self ref Value): ref Value.Vd +{ + pick xv := v {Vd => return xv;} + raise typeerror('d', v); +} + +Value.c(v: self ref Value): ref Value.Vc +{ + pick xv := v {Vc => return xv;} + raise typeerror('c', v); +} + +Value.f(v: self ref Value): ref Value.Vf +{ + pick xv := v {Vf => return xv;} + raise typeerror('f', v); +} + +Value.s(v: self ref Value): ref Value.Vs +{ + pick xv := v {Vs => return xv;} + raise typeerror('s', v); +} + +Value.m(v: self ref Value): ref Value.Vm +{ + pick xv := v {Vm => return xv;} + raise typeerror('m', v); +} + +Value.p(v: self ref Value): ref Value.Vp +{ + pick xv := v {Vp => return xv;} + raise typeerror('p', v); +} + +Value.t(v: self ref Value): ref Value.Vt +{ + pick xv := v {Vt => return xv;} + raise typeerror('t', v); +} + +Value.x(v: self ref Value): ref Value.Vx +{ + pick xv := v {Vx => return xv;} + raise typeerror('x', v); +} + +Value.typec(v: self ref Value): int +{ + return valuec[tagof v]; +} + +Value.dup(xv: self ref Value): ref Value +{ + if(xv == nil) + return nil; + pick v := xv { + Vr => + v = nil; + xv = v; + Vd => + v = nil; + xv = v; + Vf => + v = nil; + xv = v; + Vm => + v = nil; + xv = v; + Vp => + v = nil; + xv = v; + Vt => + v = nil; + xv = v; + Vx => + v = nil; + xv = v; + } + return xv; +} + +Value.free(xv: self ref Value, used: int) +{ + if(xv == nil) + return; + pick v := xv { + Vr => + if(!used){ + v.i <-= "stop"; + } + Vd => + if(!used){ + alt{ + v.i.stop <-= 1 => + ; + * => + ; + } + } + Vf => + if(!used){ + <-v.i; + v.i <-= nil; + } + Vm => + if(!used){ + v.i <-= (nil, nil, nil); + } + Vp => + if(!used){ + v.i <-= (Nilentry, nil); + } + Vt => + if(!used){ + v.i.sync <-= 0; + } + Vx => + if(!used){ + (<-v.i).t1 <-= Quit; + } + } +} + diff --git a/appl/alphabet/typesets/fstypes.b b/appl/alphabet/typesets/fstypes.b new file mode 100644 index 00000000..086b2089 --- /dev/null +++ b/appl/alphabet/typesets/fstypes.b @@ -0,0 +1,230 @@ +# warning: autogenerated code; don't bother to change this, change mktypeset.b or fs.b instead +implement Fstypes; +include "sys.m"; + sys: Sys; +include "alphabet/reports.m"; +include "draw.m"; +include "sh.m"; +include "alphabet.m"; + extvalues: Extvalues; + Values: import extvalues; + proxymod: Proxy; + Typescmd, Modulecmd: import Proxy; +include "fs.m"; + fs: Fs; + Value: import fs; +include "fstypes.m"; + +Pcontext: adt { + cvt: ref Fscvt; + ctxt: ref Context; + + loadtypes: fn(ctxt: self ref Pcontext, name: string): (chan of ref Proxy->Typescmd[ref Value], string); + type2s: fn(ctxt: self ref Pcontext, tc: int): string; + alphabet: fn(ctxt: self ref Pcontext): string; + modules: fn(ctxt: self ref Pcontext, r: chan of string); + find: fn(ctxt: self ref Pcontext, s: string): (ref Module, string); + getcvt: fn(ctxt: self ref Pcontext): ref Fscvt; +}; + +proxy(): chan of ref Typescmd[ref Alphabet->Value] +{ + return proxy0().t0; +} + +proxy0(): ( + chan of ref Typescmd[ref Alphabet->Value], + chan of (string, chan of ref Typescmd[ref Fs->Value]), + ref Fscvt + ) +{ + sys = load Sys Sys->PATH; + extvalues = checkload(load Extvalues Extvalues->PATH, Extvalues->PATH); + proxymod = checkload(load Proxy Proxy->PATH, Proxy->PATH); + fs = checkload(load Fs Fs->PATH, Fs->PATH); + fs->init(); + cvt := ref Fscvt(Values[ref Value].new()); + (t, newts) := proxymod->proxy(ref Pcontext(cvt, Context.new())); + return (t, newts, cvt); +} + +include "readdir.m"; +Context: adt { + modules: fn(ctxt: self ref Context, r: chan of string); + loadtypes: fn(ctxt: self ref Context, name: string) + : (chan of ref Proxy->Typescmd[ref Value], string); + find: fn(ctxt: self ref Context, s: string): (ref Module, string); + new: fn(): ref Context; +}; +Module: adt { + m: Fsmodule; + run: fn(m: self ref Module, ctxt: ref Draw->Context, r: ref Reports->Report, + errorc: chan of string, opts: list of (int, list of ref Value), + args: list of ref Value): ref Value; + typesig: fn(m: self ref Module): string; + quit: fn(m: self ref Module); +}; +Context.new(): ref Context +{ + return nil; +} +Context.loadtypes(nil: self ref Context, name: string): (chan of ref Typescmd[ref Value], string) +{ + p := "/dis/alphabet/fs/"+name+"types.dis"; + types := load Fssubtypes p; + if(types == nil) + return (nil, sys->sprint("cannot load %q: %r", p)); + return (types->proxy(), nil); +} +Context.modules(nil: self ref Context, r: chan of string) +{ + if((readdir := load Readdir Readdir->PATH) != nil){ + (a, nil) := readdir->init("/dis/alphabet/fs", Readdir->NAME|Readdir->COMPACT); + for(i := 0; i < len a; i++){ + m := a[i].name; + if((a[i].mode & Sys->DMDIR) == 0 && len m > 4 && m[len m - 4:] == ".dis") + r <-= m[0:len m - 4]; + } + } + r <-= nil; +} +Context.find(nil: self ref Context, s: string): (ref Module, string) +{ + p := "/dis/alphabet/fs/"+s+".dis"; + m := load Fsmodule p; + if(m == nil) + return (nil, sys->sprint("cannot load %q: %r", p)); + { + m->init(); + } exception e { + "fail:*" => + return (nil, "init failed: " + e[5:]); + } + return (ref Module(m), nil); +} +Module.run(m: self ref Module, ctxt: ref Draw->Context, r: ref Reports->Report, nil: chan of string, + opts: list of (int, list of ref Value), args: list of ref Value): ref Value +{ + # add errorc + return m.m->run(ctxt, r, opts, args); +} +Module.typesig(m: self ref Module): string +{ + return m.m->types(); +} +Module.quit(nil: self ref Module) +{ +} +Pcontext.type2s(nil: self ref Pcontext, tc: int): string +{ + return Value.type2s(tc); +} + +Pcontext.alphabet(nil: self ref Pcontext): string +{ + return "rdcfsmptx"; +} + +Pcontext.getcvt(ctxt: self ref Pcontext): ref Fscvt +{ + return ctxt.cvt; +} + +Pcontext.find(ctxt: self ref Pcontext, s: string): (ref Module, string) +{ + return ctxt.ctxt.find(s); +} + +Pcontext.modules(ctxt: self ref Pcontext, r: chan of string) +{ + ctxt.ctxt.modules(r); +} + +Pcontext.loadtypes(ctxt: self ref Pcontext, name: string): (chan of ref Typescmd[ref Value], string) +{ + return ctxt.ctxt.loadtypes(name); +} + +Fscvt.int2ext(cvt: self ref Fscvt, gv: ref Value): ref Alphabet->Value +{ + if(gv == nil) + return nil; + pick v := gv { + Vd => + return ref (Alphabet->Value).Vd(v.i); + Vf => + return ref (Alphabet->Value).Vf(v.i); + Vr => + return ref (Alphabet->Value).Vr(v.i); + Vs => + return ref (Alphabet->Value).Vs(v.i); + Vc => + return ref (Alphabet->Value).Vc(v.i); + * => + id := cvt.values.add(gv); + return ref (Alphabet->Value).Vz((gv.typec(), id)); + } +} + +Fscvt.ext2int(cvt: self ref Fscvt, ev: ref Alphabet->Value): ref Value +{ + if(ev == nil) + return nil; + pick v := ev { + Vd => + return ref Value.Vd(v.i); + Vw => + return nil; # can't happen + Vf => + return ref Value.Vf(v.i); + Vr => + return ref Value.Vr(v.i); + Vs => + return ref Value.Vs(v.i); + Vc => + return ref Value.Vc(v.i); + Vz => + x := cvt.values.v[v.i.id].t1; + if(x == nil){ + sys->print("fstypes: bad id %d, type %c\n", v.i.id, v.i.typec); + return nil; + } + return x; + } +} + +Fscvt.free(cvt: self ref Fscvt, gv: ref Alphabet->Value, used: int) +{ + pick v := gv { + Vz => + id := v.i.id; + cvt.values.v[id].t1.free(used); + cvt.values.del(id); + } +} + +Fscvt.dup(cvt: self ref Fscvt, gv: ref Alphabet->Value): ref Alphabet->Value +{ + pick ev := gv { + Vz => + id := ev.i.id; + v := cvt.values.v[id].t1; + nv := v.dup(); + if(nv == nil) + return nil; + if(nv != v) + return ref (Alphabet->Value).Vz((ev.i.typec, cvt.values.add(nv))); + cvt.values.inc(id); + return ev; + * => + return nil; + } +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + sys->fprint(sys->fildes(2), "fstypes: cannot load %s: %r\n", path); + raise "fail:bad module"; +} diff --git a/appl/alphabet/typesets/grid.b b/appl/alphabet/typesets/grid.b new file mode 100644 index 00000000..cba8366c --- /dev/null +++ b/appl/alphabet/typesets/grid.b @@ -0,0 +1,160 @@ +# warning: autogenerated code; don't bother to change this, change mktypeset.b or grid.b instead +implement Grid; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; +include "alphabet/endpoints.m"; +include "grid.m"; +endpoints: Endpoints; + +valuec := array[] of { + tagof(Value.Vb) => 'b', + tagof(Value.Ve) => 'e', + tagof(Value.Vw) => 'w', + tagof(Value.Vc) => 'c', + tagof(Value.Vr) => 'r', + tagof(Value.Vf) => 'f', + tagof(Value.Vs) => 's', +}; + +init() +{ + sys = load Sys Sys->PATH; + endpoints = load Endpoints Endpoints->PATH; + endpoints->init(); +} + +Value.type2s(c: int): string +{ + case c { + 'b' => + return "records"; + 'e' => + return "endpoint"; + 'w' => + return "wfd"; + 'c' => + return "cmd"; + 'r' => + return "status"; + 'f' => + return "fd"; + 's' => + return "string"; + * => + return sys->sprint("unknowntype('%c')", c); + } +} + +typeerror(tc: int, v: ref Value): string +{ + sys->fprint(sys->fildes(2), "fs: bad type conversion, expected %s, was actually %s\n", Value.type2s(tc), Value.type2s(valuec[tagof v])); + return "type conversion error"; +} + +Value.b(v: self ref Value): ref Value.Vb +{ + pick xv := v {Vb => return xv;} + raise typeerror('b', v); +} + +Value.e(v: self ref Value): ref Value.Ve +{ + pick xv := v {Ve => return xv;} + raise typeerror('e', v); +} + +Value.w(v: self ref Value): ref Value.Vw +{ + pick xv := v {Vw => return xv;} + raise typeerror('w', v); +} + +Value.c(v: self ref Value): ref Value.Vc +{ + pick xv := v {Vc => return xv;} + raise typeerror('c', v); +} + +Value.r(v: self ref Value): ref Value.Vr +{ + pick xv := v {Vr => return xv;} + raise typeerror('r', v); +} + +Value.f(v: self ref Value): ref Value.Vf +{ + pick xv := v {Vf => return xv;} + raise typeerror('f', v); +} + +Value.s(v: self ref Value): ref Value.Vs +{ + pick xv := v {Vs => return xv;} + raise typeerror('s', v); +} + +Value.typec(v: self ref Value): int +{ + return valuec[tagof v]; +} + +Value.dup(xv: self ref Value): ref Value +{ + if(xv == nil) + return nil; + pick v := xv { + Vb => + v = nil; + xv = v; + Ve => + v = nil; + xv = v; + Vw => + v = nil; + xv = v; + Vr => + v = nil; + xv = v; + Vf => + v = nil; + xv = v; + } + return xv; +} + +Value.free(xv: self ref Value, used: int) +{ + if(xv == nil) + return; + pick v := xv { + Vb => + if(!used){ + <-v.i; + v.i <-= nil; + } + Ve => + if(!used){ + ep := <-v.i; + if(ep.addr != nil) + endpoints->open(nil, ep); # open and discard + } + Vw => + if(!used){ + <-v.i; + v.i <-= nil; + } + Vr => + if(!used){ + v.i <-= "stop"; + } + Vf => + if(!used){ + <-v.i; + v.i <-= nil; + } + } +} + diff --git a/appl/alphabet/typesets/gridtypes.b b/appl/alphabet/typesets/gridtypes.b new file mode 100644 index 00000000..05fc1bce --- /dev/null +++ b/appl/alphabet/typesets/gridtypes.b @@ -0,0 +1,230 @@ +# warning: autogenerated code; don't bother to change this, change mktypeset.b or grid.b instead +implement Gridtypes; +include "sys.m"; + sys: Sys; +include "alphabet/reports.m"; +include "draw.m"; +include "sh.m"; +include "alphabet.m"; + extvalues: Extvalues; + Values: import extvalues; + proxymod: Proxy; + Typescmd, Modulecmd: import Proxy; +include "alphabet/endpoints.m"; +include "grid.m"; + grid: Grid; + Value: import grid; +include "gridtypes.m"; + +Pcontext: adt { + cvt: ref Gridcvt; + ctxt: ref Context; + + loadtypes: fn(ctxt: self ref Pcontext, name: string): (chan of ref Proxy->Typescmd[ref Value], string); + type2s: fn(ctxt: self ref Pcontext, tc: int): string; + alphabet: fn(ctxt: self ref Pcontext): string; + modules: fn(ctxt: self ref Pcontext, r: chan of string); + find: fn(ctxt: self ref Pcontext, s: string): (ref Module, string); + getcvt: fn(ctxt: self ref Pcontext): ref Gridcvt; +}; + +proxy(): chan of ref Typescmd[ref Alphabet->Value] +{ + return proxy0().t0; +} + +proxy0(): ( + chan of ref Typescmd[ref Alphabet->Value], + chan of (string, chan of ref Typescmd[ref Grid->Value]), + ref Gridcvt + ) +{ + sys = load Sys Sys->PATH; + extvalues = checkload(load Extvalues Extvalues->PATH, Extvalues->PATH); + proxymod = checkload(load Proxy Proxy->PATH, Proxy->PATH); + grid = checkload(load Grid Grid->PATH, Grid->PATH); + grid->init(); + cvt := ref Gridcvt(Values[ref Value].new()); + (t, newts) := proxymod->proxy(ref Pcontext(cvt, Context.new())); + return (t, newts, cvt); +} + +include "readdir.m"; +Context: adt { + modules: fn(ctxt: self ref Context, r: chan of string); + loadtypes: fn(ctxt: self ref Context, name: string) + : (chan of ref Proxy->Typescmd[ref Value], string); + find: fn(ctxt: self ref Context, s: string): (ref Module, string); + new: fn(): ref Context; +}; +Module: adt { + m: Gridmodule; + run: fn(m: self ref Module, ctxt: ref Draw->Context, r: ref Reports->Report, + errorc: chan of string, opts: list of (int, list of ref Value), + args: list of ref Value): ref Value; + typesig: fn(m: self ref Module): string; + quit: fn(m: self ref Module); +}; +Context.new(): ref Context +{ + return nil; +} +Context.loadtypes(nil: self ref Context, name: string): (chan of ref Typescmd[ref Value], string) +{ + p := "/dis/alphabet/grid/"+name+"types.dis"; + types := load Gridsubtypes p; + if(types == nil) + return (nil, sys->sprint("cannot load %q: %r", p)); + return (types->proxy(), nil); +} +Context.modules(nil: self ref Context, r: chan of string) +{ + if((readdir := load Readdir Readdir->PATH) != nil){ + (a, nil) := readdir->init("/dis/alphabet/grid", Readdir->NAME|Readdir->COMPACT); + for(i := 0; i < len a; i++){ + m := a[i].name; + if((a[i].mode & Sys->DMDIR) == 0 && len m > 4 && m[len m - 4:] == ".dis") + r <-= m[0:len m - 4]; + } + } + r <-= nil; +} +Context.find(nil: self ref Context, s: string): (ref Module, string) +{ + p := "/dis/alphabet/grid/"+s+".dis"; + m := load Gridmodule p; + if(m == nil) + return (nil, sys->sprint("cannot load %q: %r", p)); + { + m->init(); + } exception e { + "fail:*" => + return (nil, "init failed: " + e[5:]); + } + return (ref Module(m), nil); +} +Module.run(m: self ref Module, nil: ref Draw->Context, r: ref Reports->Report, errorc: chan of string, + opts: list of (int, list of ref Value), args: list of ref Value): ref Value +{ + return m.m->run(errorc, r, opts, args); +} +Module.typesig(m: self ref Module): string +{ + return m.m->types(); +} +Module.quit(nil: self ref Module) +{ +} +Pcontext.type2s(nil: self ref Pcontext, tc: int): string +{ + return Value.type2s(tc); +} + +Pcontext.alphabet(nil: self ref Pcontext): string +{ + return "bewcrfs"; +} + +Pcontext.getcvt(ctxt: self ref Pcontext): ref Gridcvt +{ + return ctxt.cvt; +} + +Pcontext.find(ctxt: self ref Pcontext, s: string): (ref Module, string) +{ + return ctxt.ctxt.find(s); +} + +Pcontext.modules(ctxt: self ref Pcontext, r: chan of string) +{ + ctxt.ctxt.modules(r); +} + +Pcontext.loadtypes(ctxt: self ref Pcontext, name: string): (chan of ref Typescmd[ref Value], string) +{ + return ctxt.ctxt.loadtypes(name); +} + +Gridcvt.int2ext(cvt: self ref Gridcvt, gv: ref Value): ref Alphabet->Value +{ + if(gv == nil) + return nil; + pick v := gv { + Vw => + return ref (Alphabet->Value).Vw(v.i); + Vf => + return ref (Alphabet->Value).Vf(v.i); + Vr => + return ref (Alphabet->Value).Vr(v.i); + Vs => + return ref (Alphabet->Value).Vs(v.i); + Vc => + return ref (Alphabet->Value).Vc(v.i); + * => + id := cvt.values.add(gv); + return ref (Alphabet->Value).Vz((gv.typec(), id)); + } +} + +Gridcvt.ext2int(cvt: self ref Gridcvt, ev: ref Alphabet->Value): ref Value +{ + if(ev == nil) + return nil; + pick v := ev { + Vd => + return nil; # can't happen + Vw => + return ref Value.Vw(v.i); + Vf => + return ref Value.Vf(v.i); + Vr => + return ref Value.Vr(v.i); + Vs => + return ref Value.Vs(v.i); + Vc => + return ref Value.Vc(v.i); + Vz => + x := cvt.values.v[v.i.id].t1; + if(x == nil){ + sys->print("gridtypes: bad id %d, type %c\n", v.i.id, v.i.typec); + return nil; + } + return x; + } +} + +Gridcvt.free(cvt: self ref Gridcvt, gv: ref Alphabet->Value, used: int) +{ + pick v := gv { + Vz => + id := v.i.id; + cvt.values.v[id].t1.free(used); + cvt.values.del(id); + } +} + +Gridcvt.dup(cvt: self ref Gridcvt, gv: ref Alphabet->Value): ref Alphabet->Value +{ + pick ev := gv { + Vz => + id := ev.i.id; + v := cvt.values.v[id].t1; + nv := v.dup(); + if(nv == nil) + return nil; + if(nv != v) + return ref (Alphabet->Value).Vz((ev.i.typec, cvt.values.add(nv))); + cvt.values.inc(id); + return ev; + * => + return nil; + } +} + +checkload[T](m: T, path: string): T +{ + if(m != nil) + return m; + sys->fprint(sys->fildes(2), "gridtypes: cannot load %s: %r\n", path); + raise "fail:bad module"; +} diff --git a/appl/alphabet/typesets/mkfile b/appl/alphabet/typesets/mkfile new file mode 100644 index 00000000..dc0a0a81 --- /dev/null +++ b/appl/alphabet/typesets/mkfile @@ -0,0 +1,34 @@ +<../../../mkconfig + +TARG=\ + mktypeset.dis\ + abc.dis\ + abctypes.dis\ + fs.dis\ + fstypes.dis\ + grid.dis\ + gridtypes.dis\ + +SYSMODULES=\ + alphabet.m\ + alphabet/endpoints.m\ + alphabet/reports.m\ + draw.m\ + readdir.m\ + sh.m\ + sys.m\ + alphabet/abc.m\ + alphabet/abctypes.m\ + alphabet/fs.m\ + alphabet/fstypes.m\ + alphabet/grid.m\ + alphabet/gridtypes.m\ + +DISBIN=$ROOT/dis/alphabet + +<$ROOT/mkfiles/mkdis + +LIMBOFLAGS=$LIMBOFLAGS -i -F + +#%.b %types.b %.m %types.m: %.typeset +# mktypeset $stem.typeset diff --git a/appl/charon/build.b b/appl/charon/build.b new file mode 100644 index 00000000..cf3a0b09 --- /dev/null +++ b/appl/charon/build.b @@ -0,0 +1,2862 @@ +implement Build; + +include "common.m"; + +# local copies from CU +sys: Sys; +CU: CharonUtils; + ByteSource, CImage, ImageCache, color, Nameval: import CU; + +D: Draw; + Point, Rect, Image: import D; +S: String; +T: StringIntTab; +C: Ctype; +LX: Lex; + RBRA, Token, TokenSource: import LX; +U: Url; + Parsedurl: import U; +J: Script; + +ctype: array of byte; + +whitespace : con " \t\n\r"; +notwhitespace : con "^ \t\n\r"; + +# These tables must be sorted +align_tab := array[] of { T->StringInt + ("baseline", int Abaseline), + ("bottom", int Abottom), + ("center", int Acenter), + ("char", int Achar), + ("justify", int Ajustify), + ("left", int Aleft), + ("middle", int Amiddle), + ("right", int Aright), + ("top", int Atop), +}; + +input_tab := array[] of { T->StringInt + ("button", Fbutton), + ("checkbox", Fcheckbox), + ("file", Ffile), + ("hidden", Fhidden), + ("image", Fimage), + ("password", Fpassword), + ("radio", Fradio), + ("reset", Freset), + ("submit", Fsubmit), + ("text", Ftext), +}; + +clear_tab := array[] of { T->StringInt + ("all", IFcleft|IFcright), + ("left", IFcleft), + ("right", IFcright), +}; + +fscroll_tab := array[] of { T->StringInt + ("auto", FRhscrollauto|FRvscrollauto), + ("no", FRnoscroll), + ("yes", FRhscroll|FRvscroll), +}; + +# blockbrk[tag] is break info for a block level element, or one +# of a few others that get the same treatment re ending open paragraphs +# and requiring a line break / vertical space before them. +# If we want a line of space before the given element, SPBefore is OR'd in. +# If we want a line of space after the given element, SPAfter is OR'd in. +SPBefore: con byte 2; +SPAfter: con byte 4; +BL: con byte 1; +BLBA: con BL|SPBefore|SPAfter; +blockbrk := array[LX->Numtags] of { + LX->Taddress => BLBA, LX->Tblockquote => BLBA, LX->Tcenter => BL, + LX->Tdir => BLBA, LX->Tdiv => BL, LX->Tdd => BL, LX->Tdl => BLBA, + LX->Tdt => BL, LX->Tform => BLBA, + # headings and tables get breaks added manually + LX->Th1 => BL, LX->Th2 => BL, LX->Th3 => BL, + LX->Th4 => BL, LX->Th5 => BL, LX->Th6 => BL, + LX->Thr => BL, LX->Tisindex => BLBA, LX->Tli => BL, LX->Tmenu => BLBA, + LX->Tol => BLBA, LX->Tp => BLBA, LX->Tpre => BLBA, + LX->Tul => BLBA, LX->Txmp => BLBA, + * => byte 0 +}; + +# attrinfo is information about attributes. +# The AGEN value means that the attribute is generic (applies to almost all elements) +AGEN: con byte 1; +attrinfo := array[LX->Numattrs] of { + LX->Aid => AGEN, LX->Aclass => AGEN, LX->Astyle => AGEN, LX->Atitle => AGEN, + LX->Aonabort => AGEN, LX->Aonblur => AGEN, LX->Aonchange => AGEN, + LX->Aonclick => AGEN, LX->Aondblclick => AGEN, LX->Aonerror => AGEN, + LX->Aonfocus => AGEN, LX->Aonkeydown => AGEN, LX->Aonkeypress => AGEN, LX->Aonkeyup => AGEN, + LX->Aonload => AGEN, LX->Aonmousedown => AGEN, LX->Aonmousemove => AGEN, + LX->Aonmouseout => AGEN, LX->Aonmouseover => AGEN, + LX->Aonmouseup => AGEN, LX->Aonreset => AGEN, LX->Aonresize => AGEN, LX->Aonselect => AGEN, + LX->Aonsubmit => AGEN, LX->Aonunload => AGEN, + * => byte 0 +}; + +# Some constants +FRKIDMARGIN: con 6; # default margin around kid frames +IMGHSPACE: con 0; # default hspace for images (0 matches IE, Netscape) +IMGVSPACE: con 0; # default vspace for images +FLTIMGHSPACE: con 2; # default hspace for float images +TABSP: con 2; # default cellspacing for tables +TABPAD: con 2; # default cell padding for tables +LISTTAB: con 1; # number of tabs to indent lists +BQTAB: con 1; # number of tabs to indent blockquotes +HRSZ: con 2; # thickness of horizontal rules +SUBOFF: con 4; # vertical offset for subscripts +SUPOFF: con 6; # vertical offset for superscripts +NBSP: con ' '; # non-breaking space character + +dbg := 0; +warn := 0; +doscripts := 0; + +utf8 : Btos; +latin1 : Btos; + +init(cu: CharonUtils) +{ + CU = cu; + sys = load Sys Sys->PATH; + D = load Draw Draw->PATH; + S = load String String->PATH;; + T = load StringIntTab StringIntTab->PATH; + U = load Url Url->PATH; + if (U != nil) + U->init(); + C = cu->C; + J = cu->J; + LX = cu->LX; + ctype = C->ctype; + utf8 = CU->getconv("utf8"); + latin1 = CU->getconv("latin1"); + if (utf8 == nil || latin1 == nil) { + sys->print("cannot load utf8 or latin1 charset converter\n"); + raise "EXinternal:build init"; + } + dbg = int (CU->config).dbg['h']; + warn = (int (CU->config).dbg['w']) || dbg; + doscripts = (CU->config).doscripts && J != nil; +} + +# Assume f has been reset, and then had any values from HTTP headers +# filled in (e.g., base, chset). +ItemSource.new(bs: ref ByteSource, f: ref Layout->Frame, mtype: int) : ref ItemSource +{ + di := f.doc; +# sys->print("chset = %s\n", di.chset); + chset := CU->getconv(di.chset); + if (chset == nil) + chset = latin1; + ts := TokenSource.new(bs, chset, mtype); + psstk := list of { Pstate.new() }; + if(mtype != CU->TextHtml) { + ps := hd psstk; + ps.curstate &= ~IFwrap; + ps.literal = 1; + pushfontstyle(ps, FntT); + } + return ref ItemSource(ts, mtype, di, f, psstk, 0, 0, 0, 0, nil, nil, nil, nil, nil, nil, nil); +} + +ItemSource.getitems(is: self ref ItemSource) : ref Item +{ + psstk := is.psstk; + ps := hd psstk; # ps is always same as hd psstk + curtab: ref Table = nil; # curtab is always same as hd is.tabstk + if(is.tabstk != nil) + curtab = hd is.tabstk; + toks := is.toks; + is.toks = nil; + tokslen := len toks; + toki := 0; + di := is.doc; +TokLoop: + for(;; toki++) { + if(toki >= tokslen) { + outerps := lastps(psstk); + if(outerps.items.next != nil) + break; + toks = is.ts.gettoks(); + tokslen = len toks; + if(dbg) + sys->print("build: got %d tokens from token source\n", tokslen); + if(tokslen == 0) + break; + toki = 0; + } + tok := toks[toki]; + if(dbg > 1) + sys->print("build: curstate %ux, token %s\n", ps.curstate, tok.tostring()); + tag := tok.tag; + brk := byte 0; + brksp := 0; + if(tag < LX->Numtags) { + brk = blockbrk[tag]; + if((brk&SPBefore) != byte 0) + brksp = 1; + } + else if(tag < LX->Numtags+RBRA) { + brk = blockbrk[tag-RBRA]; + if((brk&SPAfter) != byte 0) + brksp = 1; + } + if(brk != byte 0) { + addbrk(ps, brksp, 0); + if(ps.inpar) { + popjust(ps); + ps.inpar = 0; + } + } + # check common case first (Data), then case statement on tag + if(tag == LX->Data) { + # Lexing didn't pay attention to SGML record boundary rules: + # \n after start tag or before end tag to be discarded. + # (Lex has already discarded all \r's). + # Some pages assume this doesn't happen in
 text,
+			# so we won't do it if literal is true.
+			# BUG: won't discard \n before a start tag that begins
+			# the next bufferful of tokens.
+			s := tok.text;
+			if(!ps.literal) {
+				i := 0;
+				j := len s;
+				if(toki > 0) {
+					pt := toks[toki-1].tag;
+					# IE and Netscape both ignore this rule (contrary to spec)
+					# if previous tag was img
+					if(pt < LX->Numtags && pt != LX->Timg && j>0 && s[0]=='\n')
+						i++;
+				}
+				if(toki < tokslen-1) {
+					nt := toks[toki+1].tag;
+					if(nt >= RBRA && nt < LX->Numtags+RBRA && j>i && s[j-1]=='\n')
+						j--;
+				}
+				if(i>0 || j drop(s, whitespace);
+				if(s != "")
+					ps.skipwhite = 0;
+			}
+			if(s != "")
+				addtext(ps, s);
+		}
+		else case tag {
+		# Some abbrevs used in following DTD comments
+		# %text = #PCDATA
+		#		| TT | I | B | U | STRIKE | BIG | SMALL | SUB | SUP
+		#		| EM | STRONG | DFN | CODE | SAMP | KBD | VAR | CITE
+		#		| A | IMG | APPLET | FONT | BASEFONT | BR | SCRIPT | MAP
+		#		| INPUT | SELECT | TEXTAREA
+		# %block = P | UL | OL | DIR | MENU | DL | PRE | DL | DIV | CENTER
+		#		| BLOCKQUOTE | FORM | ISINDEX | HR | TABLE
+		# %flow = (%text | %block)*
+		# %body.content = (%heading | %text | %block | ADDRESS)*
+
+		# 
+		# Anchors are not supposed to be nested, but you sometimes see
+		# href anchors inside destination anchors.
+		LX->Ta =>
+			if(ps.curanchor != 0) {
+				if(warn)
+					sys->print("warning: nested  or missing \n");
+				endanchor(ps, di.text);
+			}
+			name := aval(tok, LX->Aname);
+			href := aurlval(tok, LX->Ahref, nil, di.base);
+			target := astrval(tok, LX->Atarget, di.target);
+			ga := getgenattr(tok);
+			evl : list of Lex->Attr = nil;
+			if(ga != nil) {
+				evl = ga.events;
+				if(evl != nil && doscripts)
+					di.hasscripts = 1;
+			}
+			# ignore rel, rev, and title attrs
+			if(href != nil) {
+				di.anchors = ref Anchor(++is.nanchors, name, href, target, evl, 0) :: di.anchors;
+				ps.curanchor = is.nanchors;
+				ps.curfg = di.link;
+				ps.fgstk = ps.curfg :: ps.fgstk;
+				# underline, too
+				ps.ulstk = ULunder :: ps.ulstk;
+				ps.curul = ULunder;
+			}
+			if(name != nil) {
+				# add a null item to be destination
+				brkstate := ps.curstate & IFbrk;
+				additem(ps, Item.newspacer(ISPnull, 0), tok);
+				ps.curstate |= brkstate;	# not quite right
+				di.dests = ref DestAnchor(++is.nanchors, name, ps.lastit) :: di.dests;
+			}
+
+		LX->Ta+RBRA =>
+			endanchor(ps, di.text);
+
+		# 
+		# We can't do applets, so ignore PARAMS, and let
+		# the %text contents appear for the alternative rep
+		LX->Tapplet or LX->Tapplet+RBRA =>
+			if(warn && tag == LX->Tapplet)
+				sys->print("warning:  ignored\n");
+
+		# 
+		LX->Tarea =>
+			map := is.curmap;
+			if(map == nil) {
+				if(warn)
+					sys->print("warning:  not inside \n");
+				continue;
+			}
+			map.areas = Area(S->tolower(astrval(tok, LX->Ashape, "rect")),
+						aurlval(tok, LX->Ahref, nil, di.base),
+						astrval(tok, LX->Atarget, di.target),
+						dimlist(tok, LX->Acoords)) :: map.areas;
+
+		# 
+		LX->Tb or LX->Tstrong =>
+			pushfontstyle(ps, FntB);
+
+		LX->Tb+RBRA or LX->Tcite+RBRA
+		  or LX->Tcode+RBRA or LX->Tdfn+RBRA
+		  or LX->Tem+RBRA or LX->Tkbd+RBRA
+		  or LX->Ti+RBRA or LX->Tsamp+RBRA
+		  or LX->Tstrong+RBRA or LX->Ttt+RBRA
+		  or LX->Tvar+RBRA or LX->Taddress+RBRA =>
+			popfontstyle(ps);
+
+		# 
+		LX->Tbase =>
+			di.base = aurlval(tok, LX->Ahref, di.base, di.base);
+			di.target = astrval(tok, LX->Atarget, di.target);
+
+		# 
+		LX->Tbasefont =>
+			ps.adjsize = aintval(tok, LX->Asize, 3) - 3;
+
+		# 
+		LX->Tbig or LX->Tsmall =>
+			sz := ps.adjsize;
+			if(tag == LX->Tbig)
+				sz += Large;
+			else
+				sz += Small;
+			pushfontsize(ps, sz);
+
+		LX->Tbig+RBRA or  LX->Tsmall+RBRA =>
+			popfontsize(ps);
+
+		# 
+		LX->Tblockquote =>
+			changeindent(ps, BQTAB);
+
+		LX->Tblockquote+RBRA =>
+			changeindent(ps, -BQTAB);
+
+		# 
+		LX->Tbody =>
+			ps.skipping = 0;
+			bg := Background(nil, color(aval(tok, LX->Abgcolor), di.background.color));
+			bgurl := aurlval(tok, LX->Abackground, nil, di.base);
+			if(bgurl != nil) {
+				pick ni := Item.newimage(di, bgurl, nil,"", Anone, 0, 0, 0, 0, 0, 0, 1, nil, nil, nil){
+				Iimage =>
+					bg.image = ni;
+				}
+				di.images = bg.image :: di.images;
+			}
+			di.background = ps.curbg = bg;
+			ps.curbg.image = nil;
+			di.text = color(aval(tok, LX->Atext), di.text);
+			di.link = color(aval(tok, LX->Alink), di.link);
+			di.vlink = color(aval(tok, LX->Avlink), di.vlink);
+			di.alink = color(aval(tok, LX->Aalink), di.alink);
+			if(doscripts) {
+				ga := getgenattr(tok);
+				if(ga != nil && ga.events != nil) {
+					di.events = ga.events;
+					di.hasscripts = 1;
+				}
+			}
+			if(di.text != ps.curfg) {
+				ps.curfg = di.text;
+				ps.fgstk = nil;
+			}
+
+		LX->Tbody+RBRA =>
+			# HTML spec says ignore things after ,
+			# but IE and Netscape don't
+			# ps.skipping = 1;
+			;
+
+		# 
+		LX->Tbr =>
+			addlinebrk(ps, atabval(tok, LX->Aclear, clear_tab, 0));
+
+		# 
+		LX->Tcaption =>
+			if(curtab == nil) {
+				if(warn)
+					sys->print("warning:  outside \n");
+				continue;
+			}
+			if(curtab.caption != nil) {
+				if(warn)
+					sys->print("warning: more than one 
in \n"); + continue; + } + ps = Pstate.new(); + psstk = ps :: psstk; + curtab.caption_place =atabbval(tok, LX->Aalign, align_tab, Atop); + + LX->Tcaption+RBRA => + if(curtab == nil || tl psstk == nil) { + if(warn) + sys->print("warning: unexpected \n"); + continue; + } + curtab.caption = ps.items.next; + psstk = tl psstk; + ps = hd psstk; + + LX->Tcenter or LX->Tdiv => + if(tag == LX->Tcenter) + al := Acenter; + else + al = atabbval(tok, LX->Aalign, align_tab, ps.curjust); + pushjust(ps, al); + + LX->Tcenter+RBRA or LX->Tdiv+RBRA => + popjust(ps); + + # + LX->Tdd => + if(ps.hangstk == nil) { + if(warn) + sys->print("warning:
not inside + # + LX->Tdir or LX->Tmenu or LX->Tol or LX->Tul => + changeindent(ps, LISTTAB); + if(tag == LX->Tol) + tydef := LT1; + else + tydef = LTdisc; + start := aintval(tok, LX->Astart, 1); + ps.listtypestk = listtyval(tok, tydef) :: ps.listtypestk; + ps.listcntstk = start :: ps.listcntstk; + + LX->Tdir+RBRA or LX->Tmenu+RBRA + or LX->Tol+RBRA or LX->Tul+RBRA => + if(ps.listtypestk == nil) { + if(warn) + sys->print("warning: %s ended no list\n", tok.tostring()); + continue; + } + addbrk(ps, 0, 0); + ps.listtypestk = tl ps.listtypestk; + ps.listcntstk = tl ps.listcntstk; + changeindent(ps, -LISTTAB); + + # + LX->Tdl => + changeindent(ps, LISTTAB); + ps.hangstk = 0 :: ps.hangstk; + + LX->Tdl+RBRA => + if(ps.hangstk == nil) { + if(warn) + sys->print("warning: unexpected \n"); + continue; + } + changeindent(ps, -LISTTAB); + if(hd ps.hangstk != 0) + changehang(ps, -10*LISTTAB); + ps.hangstk = tl ps.hangstk; + + # + LX->Tdt => + if(ps.hangstk == nil) { + if(warn) + sys->print("warning:
not inside
\n"); + continue; + } + h := hd ps.hangstk; + ps.hangstk = tl ps.hangstk; + if(h != 0) + changehang(ps, -10*LISTTAB); + changehang(ps, 10*LISTTAB); + ps.hangstk = 1 :: ps.hangstk; + + # + LX->Tfont => + sz := stackhd(ps.fntsizestk, Normal); + (szfnd, nsz) := tok.aval(LX->Asize); + if(szfnd) { + if(S->prefix("+", nsz)) + sz = Normal + int (nsz[1:]) + ps.adjsize; + else if(S->prefix("-", nsz)) + sz = Normal - int (nsz[1:]) + ps.adjsize; + else if(nsz != "") + sz = Normal + ( int nsz - 3); + } + ps.curfg = color(aval(tok, LX->Acolor), ps.curfg); + ps.fgstk = ps.curfg :: ps.fgstk; + pushfontsize(ps, sz); + + LX->Tfont+RBRA => + if(ps.fgstk == nil) { + if(warn) + sys->print("warning: unexpected \n"); + continue; + } + ps.fgstk = tl ps.fgstk; + if(ps.fgstk == nil) + ps.curfg = di.text; + else + ps.curfg = hd ps.fgstk; + popfontsize(ps); + + # + LX->Tform => + if(is.curform != nil) { + if(warn) + sys->print("warning:
nested inside another\n"); + continue; + } + action := aurlval(tok, LX->Aaction, di.base, di.base); + name := astrval(tok, LX->Aname, aval(tok, LX->Aid)); + target := astrval(tok, LX->Atarget, di.target); + smethod := S->tolower(astrval(tok, LX->Amethod, "get")); + method := CU->HGet; + if(smethod == "post") + method = CU->HPost; + else if(smethod != "get") { + if(warn) + sys->print("warning: unknown form method %s\n", smethod); + } + (ecfnd, enctype) := tok.aval(LX->Aenctype); + if(warn && ecfnd && enctype != "application/x-www-form-urlencoded") + sys->print("form enctype %s not handled\n", enctype); + ga := getgenattr(tok); + evl : list of Lex->Attr = nil; + if(ga != nil) { + evl = ga.events; + if(evl != nil && doscripts) + di.hasscripts = 1; + } + frm := Form.new(++is.nforms, name, action, target, method, evl); + di.forms = frm :: di.forms; + is.curform = frm; + + LX->Tform+RBRA => + if(is.curform == nil) { + if(warn) + sys->print("warning: unexpected \n"); + continue; + } + # put fields back in input order + fields : list of ref Formfield = nil; + for(fl := is.curform.fields; fl != nil; fl = tl fl) + fields = hd fl :: fields; + is.curform.fields = fields; + is.curform.state = FormDone; + is.curform = nil; + + # HTML 4 + # + LX->Tframe => + if(is.kidstk == nil) { + if(warn) + sys->print("warning: not in \n"); + continue; + } + ks := hd is.kidstk; + kd := Kidinfo.new(0); + kd.src = aurlval(tok, LX->Asrc, nil, di.base); + kd.name = aval(tok, LX->Aname); + if(kd.name == "") + kd.name = "_fr" + string (++is.nframes); + kd.marginw = aintval(tok, LX->Amarginwidth, 0); + kd.marginh = aintval(tok, LX->Amarginheight, 0); + kd.framebd = aintval(tok, LX->Aframeborder, ks.framebd); + kd.flags = atabval(tok, LX->Ascrolling, fscroll_tab, kd.flags); + norsz := aboolval(tok, LX->Anoresize); + if(norsz) + kd.flags |= FRnoresize; + ks.kidinfos = kd :: ks.kidinfos; + + # HTML 4 + # + LX->Tframeset => + ks := Kidinfo.new(1); + if(is.kidstk == nil) + di.kidinfo = ks; + else { + pks := hd is.kidstk; + pks.kidinfos = ks :: pks.kidinfos; + } + is.kidstk = ks :: is.kidstk; + ks.framebd = aintval(tok, LX->Aborder, 1); + ks.rows = dimlist(tok, LX->Arows); + if(ks.rows == nil) + ks.rows = array[] of {Dimen.make(Dpercent,100)}; + ks.cols = dimlist(tok, LX->Acols); + if(ks.cols == nil) + ks.cols = array[] of {Dimen.make(Dpercent,100)}; + if(doscripts) { + ga := getgenattr(tok); + if(ga != nil && ga.events != nil) { + di.events = ga.events; + di.hasscripts = 1; + } + } + + LX->Tframeset+RBRA => + if(is.kidstk == nil) { + if(warn) + sys->print("warning: unexpected \n"); + continue; + } + ks := hd is.kidstk; + # put kids back in original order + # and add blank frames to fill out cells + n := (len ks.rows) * (len ks.cols); + nblank := n - len ks.kidinfos; + while(nblank-- > 0) + ks.kidinfos = Kidinfo.new(0) :: ks.kidinfos; + kids : list of ref Kidinfo = nil; + for(kl := ks.kidinfos; kl != nil; kl = tl kl) + kids = hd kl :: kids; + ks.kidinfos= kids; + is.kidstk = tl is.kidstk; + if(is.kidstk == nil) { + for(;;) { + toks = is.ts.gettoks(); + if(len toks == 0) + break; + } + tokslen = 0; + } + + # , etc. + LX->Th1 or LX->Th2 or LX->Th3 + or LX->Th4 or LX->Th5 or LX->Th6 => + # don't want extra space if this is first addition + # to this item list (BUG: problem if first of bufferful) + bramt := 1; + if(ps.items == ps.lastit) + bramt = 0; + addbrk(ps, bramt, IFcleft|IFcright); + # assume Th2 = Th1+1, etc. + sz := Verylarge - (tag - LX->Th1); + if(sz < Tiny) + sz = Tiny; + pushfontsize(ps, sz); + sty := stackhd(ps.fntstylestk, FntR); + if(tag == LX->Th1) + sty = FntB; + pushfontstyle(ps, sty); + pushjust(ps, atabbval(tok, LX->Aalign, align_tab, ps.curjust)); + ps.skipwhite = 1; + + LX->Th1+RBRA or LX->Th2+RBRA + or LX->Th3+RBRA or LX->Th4+RBRA + or LX->Th5+RBRA or LX->Th6+RBRA => + addbrk(ps, 1, IFcleft|IFcright); + popfontsize(ps); + popfontstyle(ps); + popjust(ps); + + LX->Thead => + # HTML spec says ignore regular markup in head, + # but Netscape and IE don't + # ps.skipping = 1; + ; + + LX->Thead+RBRA => + ps.skipping = 0; + + # + LX->Thr => + al := atabbval(tok, LX->Aalign, align_tab, Acenter); + sz := aintval(tok, LX->Asize, HRSZ); + wd := makedimen(tok, LX->Awidth); + if(wd.kind() == Dnone) + wd = Dimen.make(Dpercent, 100); + nosh := aboolval(tok, LX->Anoshade); + additem(ps, Item.newrule(al, sz, nosh, wd), tok); + addbrk(ps, 0, 0); + + # + LX->Ti or LX->Tcite or LX->Tdfn + or LX->Tem or LX->Tvar or LX->Taddress => + pushfontstyle(ps, FntI); + + # + LX->Timage or # common html error supported by other browsers + LX->Timg => + tok.tag = LX->Timg; + map : ref Map = nil; + usemap := aval(tok, LX->Ausemap); + oldcuranchor := ps.curanchor; + if(usemap != "") { + # can't handle non-local maps + if(!S->prefix("#", usemap)) { + if(warn) + sys->print("warning: can't handle non-local map %s\n", usemap); + } + else { + map = getmap(di, usemap[1:]); + if(ps.curanchor == 0) { + # make an anchor so charon's easy test for whether + # there's an action for the item works + di.anchors = ref Anchor(++is.nanchors, "", nil, di.target, nil, 0) :: di.anchors; + ps.curanchor = is.nanchors; + } + } + } + align := atabbval(tok, LX->Aalign, align_tab, Abottom); + dfltbd := 0; + if(ps.curanchor != 0) + dfltbd = 2; + src := aurlval(tok, LX->Asrc, nil, di.base); + if(src == nil) { + if(warn) + sys->print("warning: has no src attribute\n"); + ps.curanchor = oldcuranchor; + continue; + } + img := Item.newimage(di, src, + aurlval(tok, LX->Alowsrc, nil, di.base), + aval(tok, LX->Aalt), + align, + aintval(tok, LX->Awidth, 0), + aintval(tok, LX->Aheight, 0), + aintval(tok, LX->Ahspace, IMGHSPACE), + aintval(tok, LX->Avspace, IMGVSPACE), + aintval(tok, LX->Aborder, dfltbd), + aboolval(tok, LX->Aismap), + 0, # not a background image + map, + aval(tok, LX->Aname), + getgenattr(tok)); + if(align == Aleft || align == Aright) { + additem(ps, Item.newfloat(img, align), tok); + # if no hspace specified, use FLTIMGHSPACE + (fnd,nil) := tok.aval(LX->Ahspace); + if(!fnd) { + pick ii := img { + Iimage => + ii.hspace = byte FLTIMGHSPACE; + } + } + } else { + ps.skipwhite = 0; + additem(ps, img, tok); + } + if(!ps.skipping) + di.images = img :: di.images; + ps.curanchor = oldcuranchor; + + # + LX->Tinput => + if (ps.skipping) + continue; + ps.skipwhite = 0; + if(is.curform ==nil) { + if(warn) + sys->print(" not inside
\n"); + continue; + } + field := Formfield.new(atabval(tok, LX->Atype, input_tab, Ftext), + ++is.curform.nfields, # fieldid + is.curform, # form + aval(tok, LX->Aname), + aval(tok, LX->Avalue), + aintval(tok, LX->Asize, 0), + aintval(tok, LX->Amaxlength, 1000)); + if(aboolval(tok, LX->Achecked)) + field.flags = FFchecked; + + case field.ftype { + Ftext or Fpassword or Ffile => + if(field.size == 0) + field.size = 20; + Fcheckbox => + if(field.name == "") { + if(warn) + sys->print("warning: checkbox form field missing name\n"); +# continue; + } + if(field.value == "") + field.value = "1"; + Fradio => + if(field.name == "" || field.value == "") { + if(warn) + sys->print("warning: radio form field missing name or value\n"); +# continue; + } + Fsubmit => + if(field.value == "") + field.value = "Submit"; + if(field.name == "") + field.name = "_no_name_submit_"; + Fimage => + src := aurlval(tok, LX->Asrc, nil, di.base); + if(src == nil) { + if(warn) + sys->print("warning: image form field missing src\n"); +# continue; + } else { + # width and height attrs aren't specified in HTML 3.2, + # but some people provide them and they help avoid + # a relayout + field.image = Item.newimage(di, src, + aurlval(tok, LX->Alowsrc, nil, di.base), + astrval(tok, LX->Aalt, "Submit"), + atabbval(tok, LX->Aalign, align_tab, Abottom), + aintval(tok, LX->Awidth, 0), + aintval(tok, LX->Aheight, 0), + 0, 0, 0, 0, 0, nil, field.name, nil); + di.images = field.image :: di.images; + } + Freset => + if(field.value == "") + field.value = "Reset"; + Fbutton => + if(field.value == "") + field.value = " "; + } + is.curform.fields = field :: is.curform.fields; + ffit := Item.newformfield(field); + additem(ps, ffit, tok); + if(ffit.genattr != nil) { + field.events = ffit.genattr.events; + if(field.events != nil && doscripts) + di.hasscripts = 1; + } + + # + LX->Tisindex => + ps.skipwhite = 0; + prompt := astrval(tok, LX->Aprompt, "Index search terms:"); + target := astrval(tok, LX->Atarget, di.target); + additem(ps, textit(ps, prompt), tok); + frm := Form.new(++is.nforms, "", di.base, target, CU->HGet, nil); + ff := Formfield.new(Ftext, 1, frm, "_ISINDEX_", "", 50, 1000); + frm.fields = ff :: nil; + frm.nfields = 1; + di.forms = frm :: di.forms; + additem(ps, Item.newformfield(ff), tok); + addbrk(ps, 1, 0); + + # + LX->Tli => + if(ps.listtypestk == nil) { + if(warn) + sys->print("
  • not in list\n"); + continue; + } + ty := hd ps.listtypestk; + ty2 := listtyval(tok, ty); + if(ty != ty2) { + ty = ty2; + ps.listtypestk = ty2 :: tl ps.listtypestk; + } + v := aintval(tok, LX->Avalue, hd ps.listcntstk); + if(ty == LTdisc || ty == LTsquare || ty == LTcircle) + hang := 10*LISTTAB - 3; + else + hang = 10*LISTTAB - 1; + changehang(ps, hang); + addtext(ps, listmark(ty, v)); + ps.listcntstk = (v+1) :: (tl ps.listcntstk); + changehang(ps, -hang); + ps.skipwhite = 1; + + # + LX->Tmap => + is.curmap = getmap(di, aval(tok, LX->Aname)); + + LX->Tmap+RBRA => + map := is.curmap; + if(map == nil) { + if(warn) + sys->print("warning: unexpected \n"); + continue; + } + # put areas back in input order + areas : list of Area = nil; + for(al := map.areas; al != nil; al = tl al) + areas = hd al :: areas; + map.areas = areas; + is.curmap = nil; + + LX->Tmeta => + if(ps.skipping) + continue; + (fnd, equiv) := tok.aval(LX->Ahttp_equiv); + if(fnd) { + v := aval(tok, LX->Acontent); + case S->tolower(equiv) { + "set-cookie" => + if((CU->config).docookies > 0) { + url := di.src; + CU->setcookie(v, url.host, url.path); + } + "refresh" => + di.refresh = v; + "content-script-type" => + if(v == "javascript" || v == "javascript1.1" || v == "jscript") + di.scripttype = CU->TextJavascript; + # TODO: other kinds + else { + if(warn) + sys->print("unimplemented script type %s\n", v); + di.scripttype = CU->UnknownType; + } + "content-type" => + (nil, parms) := S->splitl(v, ";"); + if (parms != nil) { + nvs := Nameval.namevals(parms[1:], ';'); + (got, s) := Nameval.find(nvs, "charset"); + if (got) { +# sys->print("HTTP-EQUIV charset: %s\n", s); + btos := CU->getconv(s); + if (btos != nil) + is.ts.setchset(btos); + else if (warn) + sys->print("cannot set charset %s\n", s); + } + } + } + } + + # Nobr is NOT in HMTL 4.0, but it is ubiquitous on the web + LX->Tnobr => + ps.skipwhite = 0; + ps.curstate &= ~IFwrap; + + LX->Tnobr+RBRA => + ps.curstate |= IFwrap; + + # We do frames, so skip stuff in noframes + LX->Tnoframes => + ps.skipping = 1; + + LX->Tnoframes+RBRA => + ps.skipping = 0; + + # We do scripts (if enabled), so skip stuff in noscripts + LX->Tnoscript => + if(doscripts) + ps.skipping = 1; + + LX->Tnoscript+RBRA => + if(doscripts) + ps.skipping = 0; + + # + LX->Toption => + if(is.curform == nil || is.curform.fields == nil) { + if(warn) + sys->print("warning: