git: 9front

Download patch

ref: dfad6424529bf068837c1972bb1c17be7e7247bb
parent: 3f8bb21c10d919a37d6a632dc02fa6be48016060
parent: 8829907b4bb384b0327fa484cec9f6dfa74187ee
author: ethan <ethan@vx32vardo>
date: Fri Jul 5 17:51:35 EDT 2013

merge

--- a/lib/bullshit
+++ b/lib/bullshit
@@ -20,6 +20,7 @@
 realtime |
 realtime-java |
 just-in-time |
+full-stack |
 API
 service
 event
binary files a/lib/face/48x48x8/n/nixie.1 b/lib/face/48x48x8/n/nixie.1 differ
--- /dev/null
+++ b/lib/font/bit/germgoth/unicode.12.font
@@ -1,0 +1,146 @@
+15 11
+0xff00	0xfff8	../fixed/6x12.FF00
+0xfe00	0xfeff	../fixed/6x12.FE00
+0xfb00	0xfbff	../fixed/6x12.FB00
+0xf101	0xf201	germgoth.12.f101
+0x4d00	0x4dff	../fixed/10x20.4D00
+0x3000	0x30ff	../fixed/10x20.3000
+0x2b00	0x2bff	../fixed/10x20.2B00
+0x2a00	0x2aff	../fixed/10x20.2A00
+0x2800	0x28ff	../fixed/10x20.2800
+0x2700	0x27ff	../fixed/10x20.2700
+0x2600	0x26ff	../fixed/10x20.2600
+0x2500	0x25ff	../fixed/10x20.2500
+0x2400	0x24ff	../fixed/10x20.2400
+0x2320	0x23ff	../fixed/10x20.2300
+0x2219	0x2319	germgoth.12.2219
+0x2190	0x21ea	../misc/arrows
+0x2119	0x2189	../fixed/10x20.2100
+0x2018	0x2118	germgoth.12.2018
+0x2000	0x2017	../fixed/10x20.2000
+0x1f00	0x1fff	../fixed/10x20.1F00
+0x1e00	0x1eff	../fixed/10x20.1E00
+0x1d00	0x1dff	../fixed/10x20.1D00
+0x1600	0x16ff	../fixed/10x20.1600
+0x1300	0x13ff	../fixed/10x20.1300
+0x1200	0x12ff	../fixed/10x20.1200
+0x0e00	0x0eff	../fixed/10x20.0E00
+0x0600	0x06ff	../fixed/10x20.0600
+0x0500	0x05ff	../fixed/10x20.0500
+0x0400	0x04ff	../fixed/10x20.0400
+0x03c7	0x03ff	../fixed/10x20.0300
+0x02c6	0x03c6	germgoth.12.02c6
+0x0232	0x02c5	../fixed/10x20.0200
+0x0131	0x0231	germgoth.12.0131
+0x007f	0x007f	../palatino/R.6.1
+0x0000	0x0130	germgoth.12.0000
+0x2100	0x21ff	../fixed/7x14.2100
+0x2200	0x22ff	../fixed/7x14.2200
+0x2300	0x23ff	../fixed/7x14.2300
+0x4e00	0x4ffe	../shinonome/k12.4e00
+0x5005	0x51fe	../shinonome/k12.5005
+0x5200	0x53fa	../shinonome/k12.5200
+0x5401	0x55fe	../shinonome/k12.5401
+0x5606	0x57fc	../shinonome/k12.5606
+0x5800	0x59ff	../shinonome/k12.5800
+0x5a01	0x5bff	../shinonome/k12.5a01
+0x5c01	0x5dfe	../shinonome/k12.5c01
+0x5e02	0x5fff	../shinonome/k12.5e02
+0x600e	0x61ff	../shinonome/k12.600e
+0x6200	0x63fa	../shinonome/k12.6200
+0x6406	0x65fb	../shinonome/k12.6406
+0x6602	0x67ff	../shinonome/k12.6602
+0x6802	0x69ff	../shinonome/k12.6802
+0x6a02	0x6bf3	../shinonome/k12.6a02
+0x6c08	0x6dfb	../shinonome/k12.6c08
+0x6e05	0x6ffe	../shinonome/k12.6e05
+0x7001	0x71ff	../shinonome/k12.7001
+0x7206	0x73fe	../shinonome/k12.7206
+0x7403	0x75ff	../shinonome/k12.7403
+0x7601	0x77fc	../shinonome/k12.7601
+0x7802	0x79fb	../shinonome/k12.7802
+0x7a00	0x7bf7	../shinonome/k12.7a00
+0x7c00	0x7dfb	../shinonome/k12.7c00
+0x7e01	0x7ffc	../shinonome/k12.7e01
+0x8000	0x81fe	../shinonome/k12.8000
+0x8201	0x83fd	../shinonome/k12.8201
+0x8403	0x85fe	../shinonome/k12.8403
+0x8602	0x87fe	../shinonome/k12.8602
+0x8805	0x89f8	../shinonome/k12.8805
+0x8a00	0x8b9a	../shinonome/k12.8a00
+0x8c37	0x8dff	../shinonome/k12.8c37
+0x8e08	0x8ffd	../shinonome/k12.8e08
+0x9000	0x91ff	../shinonome/k12.9000
+0x920d	0x93e8	../shinonome/k12.920d
+0x9403	0x95e5	../shinonome/k12.9403
+0x961c	0x97ff	../shinonome/k12.961c
+0x9801	0x99ff	../shinonome/k12.9801
+0x9a01	0x9bf5	../shinonome/k12.9a01
+0x9c04	0x9dfd	../shinonome/k12.9c04
+0x9e1a	0x9fa0	../shinonome/k12.9e1a
+0xff00	0xffff	../fixed/7x14.FF00
+0x1000	0x10ff	../fixed/9x15.1000
+0x2900	0x29ff	../fixed/9x15.2900
+0xe000	0xe0ff	../fixed/9x15.E000
+0xe700	0xe7ff	../fixed/9x15.E700
+0xfc00	0xfcff	../vga/vgafc
+0xfd00	0xfdff	../vga/vgafd
+0x1100	0x11ff	../fixed/6x13.1100
+0x1400	0x14ff	../fixed/9x18.1400
+0x1500	0x15ff	../fixed/9x18.1500
+0xf6c5	0xf7c5	../dejavusans/dejavusans.12.f6c5
+0xf001	0xf101	../dejavusans/dejavusans.12.f001
+0xef00	0xf000	../dejavusans/dejavusans.12.ef00
+0xa746	0xa846	../dejavusans/dejavusans.12.a746
+0xa644	0xa744	../dejavusans/dejavusans.12.a644
+0x2d61	0x2e61	../dejavusans/dejavusans.12.2d61
+0x2c60	0x2d60	../dejavusans/dejavusans.12.2c60
+0x0f00	0x0f3f	../dejavusans/dejavusans.12.0e3f
+0x07c0	0x08c0	../dejavusans/dejavusans.12.07c0
+0x0606	0x0706	../dejavusans/dejavusans.12.0606
+0xf400	0xf500	../dejavu/dejavu.12.f400
+0x2e18	0x2f18	../dejavu/dejavu.12.2e18
+0x2b00	0x2c00	../dejavu/dejavu.12.2b00
+0x4e00	0x4fff	../jis/jis4e00.24
+0x5000	0x51ff	../jis/jis5000.24
+0x5200	0x53ff	../jis/jis5200.24
+0x5400	0x55ff	../jis/jis5400.24
+0x5600	0x57ff	../jis/jis5600.24
+0x5a00	0x5bff	../jis/jis5a00.24
+0x5c00	0x5dff	../jis/jis5c00.24
+0x5e00	0x5fff	../jis/jis5e00.24
+0x6000	0x61ff	../jis/jis6000.24
+0x6200	0x63ff	../jis/jis6200.24
+0x6400	0x65ff	../jis/jis6400.24
+0x6600	0x67ff	../jis/jis6600.24
+0x6800	0x69ff	../jis/jis6800.24
+0x6a00	0x6bff	../jis/jis6a00.24
+0x6c00	0x6dff	../jis/jis6c00.24
+0x6e00	0x6fff	../jis/jis6e00.24
+0x7000	0x71ff	../jis/jis7000.24
+0x7200	0x73ff	../jis/jis7200.24
+0x7400	0x75ff	../jis/jis7400.24
+0x7600	0x77ff	../jis/jis7600.24
+0x7800	0x79ff	../jis/jis7800.24
+0x7a00	0x7bff	../jis/jis7a00.24
+0x7c00	0x7dff	../jis/jis7c00.24
+0x7e00	0x7fff	../jis/jis7e00.24
+0x8000	0x81ff	../jis/jis8000.24
+0x8200	0x83ff	../jis/jis8200.24
+0x8400	0x85ff	../jis/jis8400.24
+0x8600	0x87ff	../jis/jis8600.24
+0x8800	0x89ff	../jis/jis8800.24
+0x8a00	0x8bff	../jis/jis8a00.24
+0x8c00	0x8dff	../jis/jis8c00.24
+0x8e00	0x8fff	../jis/jis8e00.24
+0x9200	0x93ff	../jis/jis9200.24
+0x9400	0x95ff	../jis/jis9400.24
+0x9600	0x97ff	../jis/jis9600.24
+0x9800	0x99ff	../jis/jis9800.24
+0x9a00	0x9bff	../jis/jis9a00.24
+0x9c00	0x9dff	../jis/jis9c00.24
+0x9e00	0x9fff	../jis/jis9e00.24
+0xf6c4	0xf7c4	../dejavubi/dejavubi.12.f6c4
+0xf5c5	0xf6c5	../dejavusansbi/dejavusansbi.12.f5c5
+0x1700	0x1746	../dejavusansit/dejavusansit.12.1646
+0x2010	0x21d4	../shinonome/k12.2010
--- /dev/null
+++ b/lib/font/bit/germgoth/unicode.14.font
@@ -1,0 +1,153 @@
+17 12
+0xff00	0xfff8	../fixed/6x12.FF00
+0xfe00	0xfeff	../fixed/6x12.FE00
+0xfb00	0xfbff	../fixed/6x12.FB00
+0xf101	0xf201	germgoth.14.f101
+0x4d00	0x4dff	../fixed/10x20.4D00
+0x3000	0x30ff	../fixed/10x20.3000
+0x2b00	0x2bff	../fixed/10x20.2B00
+0x2a00	0x2aff	../fixed/10x20.2A00
+0x2800	0x28ff	../fixed/10x20.2800
+0x2700	0x27ff	../fixed/10x20.2700
+0x2600	0x26ff	../fixed/10x20.2600
+0x2500	0x25ff	../fixed/10x20.2500
+0x2400	0x24ff	../fixed/10x20.2400
+0x2320	0x23ff	../fixed/10x20.2300
+0x2219	0x2319	germgoth.14.2219
+0x2190	0x21ea	../misc/arrows
+0x2119	0x2189	../fixed/10x20.2100
+0x2018	0x2118	germgoth.14.2018
+0x2000	0x2017	../fixed/10x20.2000
+0x1f00	0x1fff	../fixed/10x20.1F00
+0x1e00	0x1eff	../fixed/10x20.1E00
+0x1d00	0x1dff	../fixed/10x20.1D00
+0x1600	0x16ff	../fixed/10x20.1600
+0x1300	0x13ff	../fixed/10x20.1300
+0x1200	0x12ff	../fixed/10x20.1200
+0x0e00	0x0eff	../fixed/10x20.0E00
+0x0600	0x06ff	../fixed/10x20.0600
+0x0500	0x05ff	../fixed/10x20.0500
+0x0400	0x04ff	../fixed/10x20.0400
+0x03c7	0x03ff	../fixed/10x20.0300
+0x02c6	0x03c6	germgoth.14.02c6
+0x0232	0x02c5	../fixed/10x20.0200
+0x0131	0x0231	germgoth.14.0131
+0x007f	0x007f	../palatino/R.6.1
+0x0000	0x0130	germgoth.14.0000
+0xfff9	0xffff	../dejavu/dejavu.12.fff9
+0xfb00	0xfc00	../dejavu/dejavu.12.fb00
+0xf6c5	0xf7c5	../dejavu/dejavu.12.f6c5
+0xf400	0xf500	../dejavu/dejavu.12.f400
+0xa746	0xa846	../dejavu/dejavu.12.a746
+0xa644	0xa744	../dejavu/dejavu.12.a644
+0x2e18	0x2f18	../dejavu/dejavu.12.2e18
+0x2c60	0x2d60	../dejavu/dejavu.12.2c60
+0x2b00	0x2c00	../dejavu/dejavu.12.2b00
+0x29eb	0x2aeb	../dejavu/dejavu.12.29eb
+0x28a2	0x29a2	../dejavu/dejavu.12.28a2
+0x2310	0x2410	../dejavu/dejavu.12.2310
+0x220b	0x230b	../dejavu/dejavu.12.220b
+0x2109	0x2209	../dejavu/dejavu.12.2109
+0x10a0	0x11a0	../dejavu/dejavu.12.10a0
+0xf001	0xf101	../dejavusans/dejavusans.12.f001
+0xef00	0xf000	../dejavusans/dejavusans.12.ef00
+0x4dc0	0x4ec0	../dejavusans/dejavusans.12.4dc0
+0x2d61	0x2e61	../dejavusans/dejavusans.12.2d61
+0x2940	0x2a40	../dejavusans/dejavusans.12.2940
+0x21fc	0x22f7	../dejavusans/dejavusans.12.21fc
+0x1502	0x15ff	../dejavusans/dejavusans.12.1502
+0x1401	0x1501	../dejavusans/dejavusans.12.1401
+0x0f00	0x0f3f	../dejavusans/dejavusans.12.0e3f
+0x07c0	0x08c0	../dejavusans/dejavusans.12.07c0
+0x0606	0x0706	../dejavusans/dejavusans.12.0606
+0x4e00	0x4ffe	../shinonome/k12.4e00
+0x5005	0x51fe	../shinonome/k12.5005
+0x5200	0x53fa	../shinonome/k12.5200
+0x5401	0x55fe	../shinonome/k12.5401
+0x5606	0x57fc	../shinonome/k12.5606
+0x5800	0x59ff	../shinonome/k12.5800
+0x5a01	0x5bff	../shinonome/k12.5a01
+0x5c01	0x5dfe	../shinonome/k12.5c01
+0x5e02	0x5fff	../shinonome/k12.5e02
+0x600e	0x61ff	../shinonome/k12.600e
+0x6200	0x63fa	../shinonome/k12.6200
+0x6406	0x65fb	../shinonome/k12.6406
+0x6602	0x67ff	../shinonome/k12.6602
+0x6802	0x69ff	../shinonome/k12.6802
+0x6a02	0x6bf3	../shinonome/k12.6a02
+0x6c08	0x6dfb	../shinonome/k12.6c08
+0x6e05	0x6ffe	../shinonome/k12.6e05
+0x7001	0x71ff	../shinonome/k12.7001
+0x7206	0x73fe	../shinonome/k12.7206
+0x7403	0x75ff	../shinonome/k12.7403
+0x7601	0x77fc	../shinonome/k12.7601
+0x7802	0x79fb	../shinonome/k12.7802
+0x7a00	0x7bf7	../shinonome/k12.7a00
+0x7c00	0x7dfb	../shinonome/k12.7c00
+0x7e01	0x7ffc	../shinonome/k12.7e01
+0x8000	0x81fe	../shinonome/k12.8000
+0x8201	0x83fd	../shinonome/k12.8201
+0x8403	0x85fe	../shinonome/k12.8403
+0x8602	0x87fe	../shinonome/k12.8602
+0x8805	0x89f8	../shinonome/k12.8805
+0x8a00	0x8b9a	../shinonome/k12.8a00
+0x8c37	0x8dff	../shinonome/k12.8c37
+0x8e08	0x8ffd	../shinonome/k12.8e08
+0x9000	0x91ff	../shinonome/k12.9000
+0x920d	0x93e8	../shinonome/k12.920d
+0x9403	0x95e5	../shinonome/k12.9403
+0x961c	0x97ff	../shinonome/k12.961c
+0x9801	0x99ff	../shinonome/k12.9801
+0x9a01	0x9bf5	../shinonome/k12.9a01
+0x9c04	0x9dfd	../shinonome/k12.9c04
+0x9e1a	0x9fa0	../shinonome/k12.9e1a
+0x1000	0x10ff	../fixed/9x15.1000
+0xe000	0xe0ff	../fixed/9x15.E000
+0xe700	0xe7ff	../fixed/9x15.E700
+0x1400	0x14ff	../fixed/9x18.1400
+0xfc00	0xfcff	../fixed/10x20.FC00
+0xfd00	0xfdff	../fixed/10x20.FD00
+0x4e00	0x4fff	../jis/jis4e00.24
+0x5000	0x51ff	../jis/jis5000.24
+0x5200	0x53ff	../jis/jis5200.24
+0x5400	0x55ff	../jis/jis5400.24
+0x5600	0x57ff	../jis/jis5600.24
+0x5a00	0x5bff	../jis/jis5a00.24
+0x5c00	0x5dff	../jis/jis5c00.24
+0x5e00	0x5fff	../jis/jis5e00.24
+0x6000	0x61ff	../jis/jis6000.24
+0x6200	0x63ff	../jis/jis6200.24
+0x6400	0x65ff	../jis/jis6400.24
+0x6600	0x67ff	../jis/jis6600.24
+0x6800	0x69ff	../jis/jis6800.24
+0x6a00	0x6bff	../jis/jis6a00.24
+0x6c00	0x6dff	../jis/jis6c00.24
+0x6e00	0x6fff	../jis/jis6e00.24
+0x7000	0x71ff	../jis/jis7000.24
+0x7200	0x73ff	../jis/jis7200.24
+0x7400	0x75ff	../jis/jis7400.24
+0x7600	0x77ff	../jis/jis7600.24
+0x7800	0x79ff	../jis/jis7800.24
+0x7a00	0x7bff	../jis/jis7a00.24
+0x7c00	0x7dff	../jis/jis7c00.24
+0x7e00	0x7fff	../jis/jis7e00.24
+0x8000	0x81ff	../jis/jis8000.24
+0x8200	0x83ff	../jis/jis8200.24
+0x8400	0x85ff	../jis/jis8400.24
+0x8600	0x87ff	../jis/jis8600.24
+0x8800	0x89ff	../jis/jis8800.24
+0x8a00	0x8bff	../jis/jis8a00.24
+0x8c00	0x8dff	../jis/jis8c00.24
+0x8e00	0x8fff	../jis/jis8e00.24
+0x9200	0x93ff	../jis/jis9200.24
+0x9400	0x95ff	../jis/jis9400.24
+0x9600	0x97ff	../jis/jis9600.24
+0x9800	0x99ff	../jis/jis9800.24
+0x9a00	0x9bff	../jis/jis9a00.24
+0x9c00	0x9dff	../jis/jis9c00.24
+0x9e00	0x9fff	../jis/jis9e00.24
+0x1100	0x11ff	../fixed/6x13.1100
+0xf6c4	0xf7c4	../dejavubi/dejavubi.12.f6c4
+0xf5c5	0xf6c5	../dejavusansbi/dejavusansbi.12.f5c5
+0x1700	0x1746	../dejavusansit/dejavusansit.12.1646
+0x2010	0x21d4	../shinonome/k14.2010
--- /dev/null
+++ b/lib/font/bit/germgoth/unicode.16.font
@@ -1,0 +1,153 @@
+20 14
+0xff00	0xfff8	../fixed/6x12.FF00
+0xfe00	0xfeff	../fixed/6x12.FE00
+0xfb00	0xfbff	../fixed/6x12.FB00
+0xf101	0xf201	germgoth.16.f101
+0x4d00	0x4dff	../fixed/10x20.4D00
+0x3000	0x30ff	../fixed/10x20.3000
+0x2b00	0x2bff	../fixed/10x20.2B00
+0x2a00	0x2aff	../fixed/10x20.2A00
+0x2800	0x28ff	../fixed/10x20.2800
+0x2700	0x27ff	../fixed/10x20.2700
+0x2600	0x26ff	../fixed/10x20.2600
+0x2500	0x25ff	../fixed/10x20.2500
+0x2400	0x24ff	../fixed/10x20.2400
+0x2320	0x23ff	../fixed/10x20.2300
+0x2219	0x2319	germgoth.16.2219
+0x2190	0x21ea	../misc/arrows
+0x2119	0x2189	../fixed/10x20.2100
+0x2018	0x2118	germgoth.16.2018
+0x2000	0x2017	../fixed/10x20.2000
+0x1f00	0x1fff	../fixed/10x20.1F00
+0x1e00	0x1eff	../fixed/10x20.1E00
+0x1d00	0x1dff	../fixed/10x20.1D00
+0x1600	0x16ff	../fixed/10x20.1600
+0x1300	0x13ff	../fixed/10x20.1300
+0x1200	0x12ff	../fixed/10x20.1200
+0x0e00	0x0eff	../fixed/10x20.0E00
+0x0600	0x06ff	../fixed/10x20.0600
+0x0500	0x05ff	../fixed/10x20.0500
+0x0400	0x04ff	../fixed/10x20.0400
+0x03c7	0x03ff	../fixed/10x20.0300
+0x02c6	0x03c6	germgoth.16.02c6
+0x0232	0x02c5	../fixed/10x20.0200
+0x0131	0x0231	germgoth.16.0131
+0x007f	0x007f	../palatino/R.6.1
+0x0000	0x0130	germgoth.16.0000
+0xfff9	0xffff	../dejavu/dejavu.14.fff9
+0xfb00	0xfc00	../dejavu/dejavu.14.fb00
+0xf6c5	0xf7c5	../dejavu/dejavu.14.f6c5
+0xf400	0xf500	../dejavu/dejavu.14.f400
+0xa746	0xa846	../dejavu/dejavu.14.a746
+0xa644	0xa744	../dejavu/dejavu.14.a644
+0x2e18	0x2f18	../dejavu/dejavu.14.2e18
+0x2c60	0x2d60	../dejavu/dejavu.14.2c60
+0x2b00	0x2c00	../dejavu/dejavu.14.2b00
+0x29eb	0x2aeb	../dejavu/dejavu.14.29eb
+0x28a2	0x29a2	../dejavu/dejavu.14.28a2
+0x10a0	0x11a0	../dejavu/dejavu.14.10a0
+0xf001	0xf101	../dejavusans/dejavusans.14.f001
+0xef00	0xf000	../dejavusans/dejavusans.14.ef00
+0x4dc0	0x4ec0	../dejavusans/dejavusans.14.4dc0
+0x2d61	0x2e61	../dejavusans/dejavusans.14.2d61
+0x2104	0x21fb	../dejavusans/dejavusans.14.2104
+0x1401	0x1501	../dejavusans/dejavusans.14.1401
+0x0f00	0x0f3f	../dejavusans/dejavusans.14.0e3f
+0x4e00	0x4ffe	../shinonome/k14.4e00
+0x5005	0x51fe	../shinonome/k14.5005
+0x5200	0x53fa	../shinonome/k14.5200
+0x5401	0x55fe	../shinonome/k14.5401
+0x5606	0x57fc	../shinonome/k14.5606
+0x5800	0x59ff	../shinonome/k14.5800
+0x5a01	0x5bff	../shinonome/k14.5a01
+0x5c01	0x5dfe	../shinonome/k14.5c01
+0x5e02	0x5fff	../shinonome/k14.5e02
+0x600e	0x61ff	../shinonome/k14.600e
+0x6200	0x63fa	../shinonome/k14.6200
+0x6406	0x65fb	../shinonome/k14.6406
+0x6602	0x67ff	../shinonome/k14.6602
+0x6802	0x69ff	../shinonome/k14.6802
+0x6a02	0x6bf3	../shinonome/k14.6a02
+0x6c08	0x6dfb	../shinonome/k14.6c08
+0x6e05	0x6ffe	../shinonome/k14.6e05
+0x7001	0x71ff	../shinonome/k14.7001
+0x7206	0x73fe	../shinonome/k14.7206
+0x7403	0x75ff	../shinonome/k14.7403
+0x7601	0x77fc	../shinonome/k14.7601
+0x7802	0x79fb	../shinonome/k14.7802
+0x7a00	0x7bf7	../shinonome/k14.7a00
+0x7c00	0x7dfb	../shinonome/k14.7c00
+0x7e01	0x7ffc	../shinonome/k14.7e01
+0x8000	0x81fe	../shinonome/k14.8000
+0x8201	0x83fd	../shinonome/k14.8201
+0x8403	0x85fe	../shinonome/k14.8403
+0x8602	0x87fe	../shinonome/k14.8602
+0x8805	0x89f8	../shinonome/k14.8805
+0x8a00	0x8b9a	../shinonome/k14.8a00
+0x8c37	0x8dff	../shinonome/k14.8c37
+0x8e08	0x8ffd	../shinonome/k14.8e08
+0x9000	0x91ff	../shinonome/k14.9000
+0x920d	0x93e8	../shinonome/k14.920d
+0x9403	0x95e5	../shinonome/k14.9403
+0x961c	0x97ff	../shinonome/k14.961c
+0x9801	0x99ff	../shinonome/k14.9801
+0x9a01	0x9bf5	../shinonome/k14.9a01
+0x9c04	0x9dfd	../shinonome/k14.9c04
+0x9e1a	0x9fa0	../shinonome/k14.9e1a
+0x1000	0x10ff	../fixed/10x20.1000
+0x2200	0x227f	../misc/math1
+0x2300	0x232c	../misc/tech
+0x4e00	0x4fff	../jis/jis4e00.16
+0x5000	0x51ff	../jis/jis5000.16
+0x5200	0x53ff	../jis/jis5200.16
+0x5400	0x55ff	../jis/jis5400.16
+0x5600	0x57ff	../jis/jis5600.16
+0x5a00	0x5bff	../jis/jis5a00.16
+0x5c00	0x5dff	../jis/jis5c00.16
+0x5e00	0x5fff	../jis/jis5e00.16
+0x6000	0x61ff	../jis/jis6000.16
+0x6200	0x63ff	../jis/jis6200.16
+0x6400	0x65ff	../jis/jis6400.16
+0x6600	0x67ff	../jis/jis6600.16
+0x6800	0x69ff	../jis/jis6800.16
+0x6a00	0x6bff	../jis/jis6a00.16
+0x6c00	0x6dff	../jis/jis6c00.16
+0x6e00	0x6fff	../jis/jis6e00.16
+0x7000	0x71ff	../jis/jis7000.16
+0x7200	0x73ff	../jis/jis7200.16
+0x7400	0x75ff	../jis/jis7400.16
+0x7600	0x77ff	../jis/jis7600.16
+0x7800	0x79ff	../jis/jis7800.16
+0x7a00	0x7bff	../jis/jis7a00.16
+0x7c00	0x7dff	../jis/jis7c00.16
+0x7e00	0x7fff	../jis/jis7e00.16
+0x8000	0x81ff	../jis/jis8000.16
+0x8200	0x83ff	../jis/jis8200.16
+0x8400	0x85ff	../jis/jis8400.16
+0x8600	0x87ff	../jis/jis8600.16
+0x8800	0x89ff	../jis/jis8800.16
+0x8a00	0x8bff	../jis/jis8a00.16
+0x8c00	0x8dff	../jis/jis8c00.16
+0x8e00	0x8fff	../jis/jis8e00.16
+0x9200	0x93ff	../jis/jis9200.16
+0x9400	0x95ff	../jis/jis9400.16
+0x9600	0x97ff	../jis/jis9600.16
+0x9800	0x99ff	../jis/jis9800.16
+0x9a00	0x9bff	../jis/jis9a00.16
+0x9c00	0x9dff	../jis/jis9c00.16
+0x9e00	0x9fff	../jis/jis9e00.16
+0xfc00	0xfcff	../fixed/10x20.FC00
+0xfd00	0xfdff	../fixed/10x20.FD00
+0x2100	0x21ff	../fixed/10x20.2100
+0x1400	0x14ff	../fixed/9x18.1400
+0x1500	0x15ff	../fixed/9x18.1500
+0x2940	0x2a40	../dejavusans/dejavusans.12.2940
+0x07c0	0x08c0	../dejavusans/dejavusans.12.07c0
+0x0606	0x0706	../dejavusans/dejavusans.12.0606
+0xe000	0xe0ff	../fixed/9x15.E000
+0xe700	0xe7ff	../fixed/9x15.E700
+0x1100	0x11ff	../fixed/6x13.1100
+0xf6c4	0xf7c4	../dejavubi/dejavubi.14.f6c4
+0xf5c5	0xf6c5	../dejavusansbi/dejavusansbi.14.f5c5
+0x2010	0x21d4	../shinonome/k16.2010
+0x1700	0x1746	../dejavusansit/dejavusansit.12.1646
--- /dev/null
+++ b/lib/font/bit/germgoth/unicode.18.font
@@ -1,0 +1,153 @@
+23 16
+0xff00	0xfff8	../fixed/6x12.FF00
+0xfe00	0xfeff	../fixed/6x12.FE00
+0xfb00	0xfbff	../fixed/6x12.FB00
+0xf101	0xf201	germgoth.18.f101
+0x4d00	0x4dff	../fixed/10x20.4D00
+0x3000	0x30ff	../fixed/10x20.3000
+0x2b00	0x2bff	../fixed/10x20.2B00
+0x2a00	0x2aff	../fixed/10x20.2A00
+0x2800	0x28ff	../fixed/10x20.2800
+0x2700	0x27ff	../fixed/10x20.2700
+0x2600	0x26ff	../fixed/10x20.2600
+0x2500	0x25ff	../fixed/10x20.2500
+0x2400	0x24ff	../fixed/10x20.2400
+0x2320	0x23ff	../fixed/10x20.2300
+0x2219	0x2319	germgoth.18.2219
+0x2190	0x21ea	../misc/arrows
+0x2119	0x2189	../fixed/10x20.2100
+0x2018	0x2118	germgoth.18.2018
+0x2000	0x2017	../fixed/10x20.2000
+0x1f00	0x1fff	../fixed/10x20.1F00
+0x1e00	0x1eff	../fixed/10x20.1E00
+0x1d00	0x1dff	../fixed/10x20.1D00
+0x1600	0x16ff	../fixed/10x20.1600
+0x1300	0x13ff	../fixed/10x20.1300
+0x1200	0x12ff	../fixed/10x20.1200
+0x0e00	0x0eff	../fixed/10x20.0E00
+0x0600	0x06ff	../fixed/10x20.0600
+0x0500	0x05ff	../fixed/10x20.0500
+0x0400	0x04ff	../fixed/10x20.0400
+0x03c7	0x03ff	../fixed/10x20.0300
+0x02c6	0x03c6	germgoth.18.02c6
+0x0232	0x02c5	../fixed/10x20.0200
+0x0131	0x0231	germgoth.18.0131
+0x007f	0x007f	../palatino/R.6.1
+0x0000	0x0130	germgoth.18.0000
+0xfff9	0xffff	../dejavu/dejavu.16.fff9
+0xfb00	0xfc00	../dejavu/dejavu.16.fb00
+0xf6c5	0xf7c5	../dejavu/dejavu.16.f6c5
+0xf400	0xf500	../dejavu/dejavu.16.f400
+0xa746	0xa846	../dejavu/dejavu.16.a746
+0xa644	0xa744	../dejavu/dejavu.16.a644
+0x2e18	0x2f18	../dejavu/dejavu.16.2e18
+0x2c60	0x2d60	../dejavu/dejavu.16.2c60
+0x2b00	0x2c00	../dejavu/dejavu.16.2b00
+0x28a2	0x29a2	../dejavu/dejavu.16.28a2
+0x10a0	0x11a0	../dejavu/dejavu.16.10a0
+0xf001	0xf101	../dejavusans/dejavusans.16.f001
+0xef00	0xf000	../dejavusans/dejavusans.16.ef00
+0x4dc0	0x4ec0	../dejavusans/dejavusans.16.4dc0
+0x2d61	0x2e61	../dejavusans/dejavusans.16.2d61
+0x1401	0x1501	../dejavusans/dejavusans.16.1401
+0x0f00	0x0f3f	../dejavusans/dejavusans.16.0e3f
+0x07c0	0x08c0	../dejavusans/dejavusans.16.07c0
+0x4e00	0x4ffe	../shinonome/k16.4e00
+0x5005	0x51fe	../shinonome/k16.5005
+0x5200	0x53fa	../shinonome/k16.5200
+0x5401	0x55fe	../shinonome/k16.5401
+0x5606	0x57fc	../shinonome/k16.5606
+0x5800	0x59ff	../shinonome/k16.5800
+0x5a01	0x5bff	../shinonome/k16.5a01
+0x5c01	0x5dfe	../shinonome/k16.5c01
+0x5e02	0x5fff	../shinonome/k16.5e02
+0x600e	0x61ff	../shinonome/k16.600e
+0x6200	0x63fa	../shinonome/k16.6200
+0x6406	0x65fb	../shinonome/k16.6406
+0x6602	0x67ff	../shinonome/k16.6602
+0x6802	0x69ff	../shinonome/k16.6802
+0x6a02	0x6bf3	../shinonome/k16.6a02
+0x6c08	0x6dfb	../shinonome/k16.6c08
+0x6e05	0x6ffe	../shinonome/k16.6e05
+0x7001	0x71ff	../shinonome/k16.7001
+0x7206	0x73fe	../shinonome/k16.7206
+0x7403	0x75ff	../shinonome/k16.7403
+0x7601	0x77fc	../shinonome/k16.7601
+0x7802	0x79fb	../shinonome/k16.7802
+0x7a00	0x7bf7	../shinonome/k16.7a00
+0x7c00	0x7dfb	../shinonome/k16.7c00
+0x7e01	0x7ffc	../shinonome/k16.7e01
+0x8000	0x81fe	../shinonome/k16.8000
+0x8201	0x83fd	../shinonome/k16.8201
+0x8403	0x85fe	../shinonome/k16.8403
+0x8602	0x87fe	../shinonome/k16.8602
+0x8805	0x89f8	../shinonome/k16.8805
+0x8a00	0x8b9a	../shinonome/k16.8a00
+0x8c37	0x8dff	../shinonome/k16.8c37
+0x8e08	0x8ffd	../shinonome/k16.8e08
+0x9000	0x91ff	../shinonome/k16.9000
+0x920d	0x93e8	../shinonome/k16.920d
+0x9403	0x95e5	../shinonome/k16.9403
+0x961c	0x97ff	../shinonome/k16.961c
+0x9801	0x99ff	../shinonome/k16.9801
+0x9a01	0x9bf5	../shinonome/k16.9a01
+0x9c04	0x9dfd	../shinonome/k16.9c04
+0x9e1a	0x9fa0	../shinonome/k16.9e1a
+0x2100	0x21ea	../lucida/Letterlike.9.0
+0x2200	0x22f1	../lucida/MathOps1.9.0
+0x1000	0x10ff	../fixed/10x20.1000
+0x2100	0x21ff	../fixed/10x20.2100
+0x2300	0x23ff	../fixed/10x20.2300
+0xfc00	0xfcff	../fixed/10x20.FC00
+0xfd00	0xfdff	../fixed/10x20.FD00
+0x4e00	0x4fff	../jis/jis4e00.24
+0x5000	0x51ff	../jis/jis5000.24
+0x5200	0x53ff	../jis/jis5200.24
+0x5400	0x55ff	../jis/jis5400.24
+0x5600	0x57ff	../jis/jis5600.24
+0x5a00	0x5bff	../jis/jis5a00.24
+0x5c00	0x5dff	../jis/jis5c00.24
+0x5e00	0x5fff	../jis/jis5e00.24
+0x6000	0x61ff	../jis/jis6000.24
+0x6200	0x63ff	../jis/jis6200.24
+0x6400	0x65ff	../jis/jis6400.24
+0x6600	0x67ff	../jis/jis6600.24
+0x6800	0x69ff	../jis/jis6800.24
+0x6a00	0x6bff	../jis/jis6a00.24
+0x6c00	0x6dff	../jis/jis6c00.24
+0x6e00	0x6fff	../jis/jis6e00.24
+0x7000	0x71ff	../jis/jis7000.24
+0x7200	0x73ff	../jis/jis7200.24
+0x7400	0x75ff	../jis/jis7400.24
+0x7600	0x77ff	../jis/jis7600.24
+0x7800	0x79ff	../jis/jis7800.24
+0x7a00	0x7bff	../jis/jis7a00.24
+0x7c00	0x7dff	../jis/jis7c00.24
+0x7e00	0x7fff	../jis/jis7e00.24
+0x8000	0x81ff	../jis/jis8000.24
+0x8200	0x83ff	../jis/jis8200.24
+0x8400	0x85ff	../jis/jis8400.24
+0x8600	0x87ff	../jis/jis8600.24
+0x8800	0x89ff	../jis/jis8800.24
+0x8a00	0x8bff	../jis/jis8a00.24
+0x8c00	0x8dff	../jis/jis8c00.24
+0x8e00	0x8fff	../jis/jis8e00.24
+0x9200	0x93ff	../jis/jis9200.24
+0x9400	0x95ff	../jis/jis9400.24
+0x9600	0x97ff	../jis/jis9600.24
+0x9800	0x99ff	../jis/jis9800.24
+0x9a00	0x9bff	../jis/jis9a00.24
+0x9c00	0x9dff	../jis/jis9c00.24
+0x9e00	0x9fff	../jis/jis9e00.24
+0x29eb	0x2aeb	../dejavu/dejavu.14.29eb
+0x1400	0x14ff	../fixed/9x18.1400
+0x1500	0x15ff	../fixed/9x18.1500
+0x2940	0x2a40	../dejavusans/dejavusans.12.2940
+0x0606	0x0706	../dejavusans/dejavusans.12.0606
+0xe000	0xe0ff	../fixed/9x15.E000
+0xe700	0xe7ff	../fixed/9x15.E700
+0x1100	0x11ff	../fixed/6x13.1100
+0xf6c4	0xf7c4	../dejavubi/dejavubi.16.f6c4
+0x1700	0x1746	../dejavusansbd/dejavusansbd.16.1646
+0xf5c5	0xf6c5	../dejavusansbi/dejavusansbi.16.f5c5
+0x2010	0x21d4	../shinonome/k16.2010
--- a/lib/rob
+++ b/lib/rob
@@ -138,3 +138,4 @@
 I guess that wasn't very helpful.
 The proposed fix is perhaps not the best one.
 It's only an issue on mailing lists and discussion groups.
+Let me put in a word about the Apple wireless trackpad.
--- a/rc/bin/sysinfo
+++ b/rc/bin/sysinfo
@@ -1,13 +1,11 @@
 #!/bin/rc
 rfork e
-
 fn logprog{
 	echo % `{echo $"* | sed 's/#/''#''/g'}
 	$*
 	echo
 }
-
-fn work{
+fn print{
 	echo '% cd ''#ec''; for(i in *){echo $i ''='' `{cat $i}}'
 	cd '#ec'; for(i in *){echo $i '=' `{cat $i}}
 	echo
@@ -42,11 +40,42 @@
 	logprog cat '#A/volume'
 	logprog cat /mnt/apm/ctl
 }
-
-switch($1){
-case -m
-	{echo Subject: `{cat '#P/cputype'}; work} >[2=1] |
-		user=/dev/null upas/smtp 9front.org $user hardware@9front.org
-case *
-	work
+fn post{
+	file=/tmp/sysinfo.$user.$pid
+	if(! ~ $#e 0)
+		echo $e >$file
+	print >>$file >[2=1] 
+	@{
+		rfork n
+		webfs
+		hpost -u http://sysinfo.9front.org -p / a_body@$file submit:submit fake:fake a_func:add_post url: |
+			grep -e '\/body\"' |
+			sed 1q | sed 's/^.*href=\"//g; s/body\".*$/body/g; s/^/http:\/\/sysinfo.9front.org/g'
+		rm -f $file
+	}
 }
+argv0=$0
+fn usage {
+	echo usage: $argv0 '[ -e e-mail ] [ -p ]' >[1=2]
+	exit usage
+}
+e=()
+p=()
+while(~ $1 -*){
+	switch($1){
+	case -e
+		e=$2
+		shift
+	case -p
+		p=1
+		shift
+	case *
+		usage
+	}
+	shift
+}
+if(~ $p 1)
+	post
+	
+if not
+	print
--- a/sys/games/lib/fortunes
+++ b/sys/games/lib/fortunes
@@ -4876,3 +4876,16 @@
 While many are concerned about the reach of PRISM overseas, the Finnish Foreign Minister says he plans to continue using Outlook for email.
 As The Web Improves, Apple's Platforms Look Increasingly Broken
 (#go-nuts) cronos → damn, how many apis are there?
+What does German soldiers during World War II have in common with the UNIX philosophy of “worse is better”? 
+Introducing WOW! AT&T World of Women: our newest website
+Bringing Israelis And Palestinians Together With Mobile Apps
+18:10 < mischief> is there a way to execute plan 9 programs in linuxemu?
+For example, whilst he was playing to me, a favourite cat came in, upon which he immediately left his harpsichord, nor could we bring him back for a considerable time.
+Subject: [dev] daemon for DWM
+Subject: [inferno-list] Goth - Inferno in Go
+/* die */
+Subject: [dev] lisp
+As always, do not blindly enter commands you do not understand into your computer. -- OpenBSD FAQ
+When Will Architects Speak Up for Women’s Rights?
+So much for capitalism being the great "driver" of the economy.
+For each droplet of ink the printer creates a fairly strong spike of electrical energy, and these spikes provide a signature specific to each printer model.
--- a/sys/include/ape/draw.h
+++ b/sys/include/ape/draw.h
@@ -408,6 +408,7 @@
 extern int		cmap2rgba(int);
 extern void		icossin(int, int*, int*);
 extern void		icossin2(int, int, int*, int*);
+extern int		badrect(Rectangle);
 
 /*
  * Graphics
--- a/sys/include/draw.h
+++ b/sys/include/draw.h
@@ -401,6 +401,7 @@
 extern int		cmap2rgba(int);
 extern void		icossin(int, int*, int*);
 extern void		icossin2(int, int, int*, int*);
+extern int		badrect(Rectangle);
 
 /*
  * Graphics
--- a/sys/man/1/jpg
+++ b/sys/man/1/jpg
@@ -1,6 +1,6 @@
 .TH JPG 1
 .SH NAME
-jpg, gif, png, ppm, bmp, v210, yuv, ico, tga, tojpg, togeordi, togif, toppm, topng, toico \- view and convert pictures
+jpg, gif, png, tif, ppm, bmp, v210, yuv, ico, tga, tojpg, togeordi, togif, toppm, topng, toico \- view and convert pictures
 .SH SYNOPSIS
 .B jpg
 [
@@ -23,6 +23,13 @@
 .I file ...
 ]
 .br
+.B tif
+[
+.B -39cdektv
+] [
+.I file ...
+]
+.br
 .B ppm
 [
 .B -39cdektv
@@ -137,6 +144,7 @@
 .IR Jpg ,
 .IR gif ,
 .IR png ,
+.IR tif ,
 .IR ppm ,
 .IR bmp ,
 .IR tga ,
@@ -156,8 +164,14 @@
 The default behavior of
 .IR jpg ,
 .IR gif ,
+.IR png ,
+.IR tif ,
+.IR ppm ,
+.IR bmp ,
+.IR tga ,
+.IR v210 ,
 and
-.IR ppm
+.IR yuv
 is to display the
 .IR file ,
 or standard input if no file is named.
@@ -227,11 +241,12 @@
 .PP
 The
 .IR tojpg ,
-.IR togif
-and
+.IR togif ,
 .IR toppm
-programs go the other way: they convert from Plan 9 images to JPEG, GIF and PPM,
-and have no display capability.
+and
+.IR topng
+programs go the other way: they convert from Plan 9 images to JPEG, GIF,
+PPM and PNG and have no display capability.
 They all accept an option
 .B -c
 to set the comment field of the resulting file.
@@ -322,6 +337,8 @@
 .B http://www.w3.org/Graphics/GIF/spec-gif89a.txt
 .br
 .B http://www.w3.org/TR/2003/REC-PNG-20031110
+.br
+.B http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
 .br
 .B http://netpbm.sourceforge.net/doc/ppm.html
 .br
--- a/sys/man/1/sysinfo
+++ b/sys/man/1/sysinfo
@@ -4,8 +4,11 @@
 .SH SYNOPSIS
 .B sysinfo
 [
-.B -m
+.B -e e-mail
 ]
+[
+.B -p
+]
 .br
 .B sysupdate
 [
@@ -19,11 +22,20 @@
 stdout.
 .PP
 The
-.B -m
-flag causes the output to be piped to a mail command,
-whose recipient is
-.B hardware@9front.org.
-This flag is useful for reporting new installs to the
+.B -p
+flag causes the output to be posted at a website
+archive,
+.B http://sysinfo.9front.org,
+which in turn forwards the message to a
+mailing list,
+.B 9front-sysinfo@9front.org.
+A URL pointing to the archived output is returned.
+The
+.B -e
+flag causes a reply-to e-mail address to be included
+in the message (the e-mail address is not divulged
+to the mailing list).
+These flags are useful for reporting new installs to the
 development team.
 .PP
 .I Sysupdate
--- a/sys/man/1/vnc
+++ b/sys/man/1/vnc
@@ -194,41 +194,6 @@
 .B -v
 print verbose output to standard error.
 .PD
-.PP
-The VNC protocol represents keyboard input as
-key up/down events.
-Plan 9 does not expose the state of the
-Ctl and Shift keys except as it can be inferred
-from receipt of control or shifted characters.
-It does not expose the state of the Alt key at all,
-since the Alt key is used to compose Unicode characters
-(see
-.IR keyboard (6)).
-.I Vncv
-correctly handles the sending of control and shifted
-characters.
-To support systems that use key sequences like Alt-X
-(or worse, Alt-mouse-click), typing the Plan 9 compose
-sequences
-.B Alt
-.B Z
-.B A
-(for Alt), 
-.B Alt
-.B Z
-.B C
-(for Ctrl),
-and
-.B Alt
-.B Z
-.B S
-(for Shift)
-will send a ``key down'' message for
-the given key.
-A corresponding ``key up'' message
-will be sent after the next key is pressed,
-or when the sequence is retyped,
-whichever happens first.
 .SH SOURCE
 .B /sys/src/cmd/vnc
 .SH "SEE ALSO
--- a/sys/man/8/hjfs
+++ b/sys/man/8/hjfs
@@ -27,6 +27,8 @@
 .I name
 .RI [ options ]
 .PP
+.B users
+.PP
 .B sync
 .PP
 .B debug-chdeind
@@ -113,10 +115,6 @@
 The options are
 .TF =leaderxx
 .TP
-.B ?
-Print the entry for
-.IR name .
-.TP
 .B :
 Add a group: add the name to
 .B /adm/users
@@ -156,6 +154,12 @@
 command the file server overwrites
 .B /adm/users
 to reflect the internal state of the user table.
+.PP
+.I Users
+reads the contents of file 
+.B /adm/users
+to initialize the file server's internal representation of the
+users structure.
 .PP
 .I Sync
 writes dirty blocks in memory to the magnetic disk cache.
--- a/sys/man/8/plan9.ini
+++ b/sys/man/8/plan9.ini
@@ -392,11 +392,18 @@
 .B /boot.
 To select the access point, the
 .B essid=
-parameter can be specified at boot or set during runtime
+and
+.B bssid=
+parameters can be specified at boot or set during runtime
 like:
 .EX
 	echo essid left-armpit >/net/ether1/clone
 .EE
+If both
+.B essid=
+and
+.B bssid=
+are specified, both must match.
 Scan results appear in the
 .B ifstats
 file and can be read out like:
@@ -403,7 +410,10 @@
 .EX
 	cat /net/ether1/ifstats
 .EE
-Ad-hoc mode or encryption is currently not supported.
+Ad-hoc mode or WEP encryption is currently not supported.
+To enable WPA/WPA2 encryption, see
+.IR wpa (8)
+for details.
 .SS DISKS, TAPES
 (S)ATA controllers are autodetected.
 .SS \fL*nodma=\fP
@@ -720,6 +730,9 @@
 Prints a summary of the multiprocessor APIC interrupt configuration.
 .SS \fL*nomsi=\fP
 Disables message signaled interrupts.
+.SS \fL*notsc=\fP
+Disables the use of the per processor timestamp counter registers
+as high resolution clock.
 .SS \fL*pcimaxbno=value\fP
 This puts a limit on the maximum bus number probed
 on a PCI bus (default 7).
--- a/sys/src/9/ip/ethermedium.c
+++ b/sys/src/9/ip/ethermedium.c
@@ -354,11 +354,12 @@
 			nexterror();
 		}
 		ifc->in++;
-		bp->rp += ifc->m->hsize;
-		if(ifc->lifc == nil)
+		if(ifc->lifc == nil || BLEN(bp) <= ifc->m->hsize)
 			freeb(bp);
-		else
+		else {
+			bp->rp += ifc->m->hsize;
 			ipiput4(er->f, ifc, bp);
+		}
 		runlock(ifc);
 		poperror();
 	}
@@ -393,11 +394,12 @@
 			nexterror();
 		}
 		ifc->in++;
-		bp->rp += ifc->m->hsize;
-		if(ifc->lifc == nil)
+		if(ifc->lifc == nil || BLEN(bp) <= ifc->m->hsize)
 			freeb(bp);
-		else
+		else {
+			bp->rp += ifc->m->hsize;
 			ipiput6(er->f, ifc, bp);
+		}
 		runlock(ifc);
 		poperror();
 	}
--- a/sys/src/9/pc/apic.c
+++ b/sys/src/9/pc/apic.c
@@ -89,7 +89,8 @@
 
 static ulong* lapicbase;
 
-struct
+typedef struct Apictimer Apictimer;
+struct Apictimer
 {
 	uvlong	hz;
 	ulong	max;
@@ -96,8 +97,10 @@
 	ulong	min;
 	ulong	div;
 	int	tdx;
-} lapictimer;
+};
 
+static Apictimer lapictimer[MAXMACH];
+
 static ulong
 lapicr(int r)
 {
@@ -115,6 +118,10 @@
 void
 lapiconline(void)
 {
+	Apictimer *a;
+
+	a = &lapictimer[m->machno];
+
 	/*
 	 * Reload the timer to de-synchronise the processors,
 	 * then lower the task priority to allow interrupts to be
@@ -121,9 +128,15 @@
 	 * accepted by the APIC.
 	 */
 	microdelay((TK2MS(1)*1000/conf.nmach) * m->machno);
-	lapicw(LapicTICR, lapictimer.max);
+	lapicw(LapicTICR, a->max);
 	lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER));
 
+	/*
+	 * not strickly neccesary, but reported (osdev.org) to be
+	 * required for some machines.
+	 */
+	lapicw(LapicTDCR, lapictdxtab[a->tdx]);
+
 	lapicw(LapicTPR, 0);
 }
 
@@ -133,35 +146,44 @@
 static void
 lapictimerinit(void)
 {
+	uvlong x, v, hz;
+	Apictimer *a;
+	int s;
+
+	s = splhi();
+	a = &lapictimer[m->machno];
+	a->tdx = 0;
 Retry:
-	lapicw(LapicTDCR, lapictdxtab[lapictimer.tdx]);
 	lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER));
+	lapicw(LapicTDCR, lapictdxtab[a->tdx]);
 
-	if(lapictimer.hz == 0ULL){
-		uvlong x, v, hz;
+	x = fastticks(&hz);
+	x += hz/10;
+	lapicw(LapicTICR, 0xffffffff);
+	do{
+		v = fastticks(nil);
+	}while(v < x);
 
-		x = fastticks(&hz);
-		x += hz/10;
-		lapicw(LapicTICR, 0xffffffff);
-		do{
-			v = fastticks(nil);
-		}while(v < x);
-
-		v = (0xffffffffUL-lapicr(LapicTCCR))*10;
-		if(v > hz-(hz/10)){
-			if(v > hz+(hz/10) && lapictimer.tdx < nelem(lapictdxtab)-1){
-				lapictimer.tdx++;
-				goto Retry;
-			}
-			v = hz;
+	v = (0xffffffffUL-lapicr(LapicTCCR))*10;
+	if(v > hz-(hz/10)){
+		if(v > hz+(hz/10) && a->tdx < nelem(lapictdxtab)-1){
+			a->tdx++;
+			goto Retry;
 		}
-		assert(v != 0);
-
-		lapictimer.hz = v;
-		lapictimer.div = hz/lapictimer.hz;
-		lapictimer.max = lapictimer.hz/HZ;
-		lapictimer.min = lapictimer.hz/(100*HZ);
+		v = hz;
 	}
+
+	assert(v >= (100*HZ));
+
+	a->hz = v;
+	a->div = hz/a->hz;
+	a->max = a->hz/HZ;
+	a->min = a->hz/(100*HZ);
+
+	splx(s);
+
+	v = (v+500000LL)/1000000LL;
+	print("cpu%d: lapic clock at %lludMHz\n", m->machno, v);
 }
 
 void
@@ -371,25 +393,19 @@
 lapictimerset(uvlong next)
 {
 	vlong period;
-	int x;
+	Apictimer *a;
 
-	x = splhi();
-	lock(&m->apictimerlock);
-
-	period = lapictimer.max;
+	a = &lapictimer[m->machno];
+	period = a->max;
 	if(next != 0){
 		period = next - fastticks(nil);
-		period /= lapictimer.div;
-
-		if(period < lapictimer.min)
-			period = lapictimer.min;
-		else if(period > lapictimer.max - lapictimer.min)
-			period = lapictimer.max;
+		period /= a->div;
+		if(period < a->min)
+			period = a->min;
+		else if(period > a->max - a->min)
+			period = a->max;
 	}
 	lapicw(LapicTICR, period);
-
-	unlock(&m->apictimerlock);
-	splx(x);
 }
 
 void
--- a/sys/src/9/pc/archacpi.c
+++ b/sys/src/9/pc/archacpi.c
@@ -37,6 +37,10 @@
 	uchar	data[];
 };
 
+enum {
+	Tblsz	= 4+4+1+1+6+8+4+4+4,
+};
+
 static Rsd *rsd;
 
 /* physical addresses visited by maptable() */
@@ -82,7 +86,7 @@
 
 static uint
 tbldlen(Tbl *t){
-	return get32(t->len) - sizeof(Tbl);
+	return get32(t->len) - Tblsz;
 }
 
 static void
@@ -109,7 +113,7 @@
 	if((t = vmap(pa, 8)) == nil)
 		return;
 	l = get32(t->len);
-	if(l < sizeof(Tbl)){
+	if(l < Tblsz){
 		vunmap(t, 8);
 		return;
 	}
@@ -396,7 +400,7 @@
 			a->addr = va;
 			a->lintr[0] = ApicIMASK;
 			a->lintr[1] = ApicIMASK;
-			a->flags = (p[4] & PcmpEN);
+			a->flags = p[4] & PcmpEN;
 			if(a->flags & PcmpEN){
 				a->machno = machno++;
 
@@ -515,7 +519,7 @@
 		return 1;
 	if((cp = getconf("*nomp")) != nil && strcmp(cp, "0") != 0)
 		return 1;
-	if(cpuserver && m->havetsc)
+	if(m->havetsc && getconf("*notsc") == nil)
 		archacpi.fastclock = tscticks;
 	return 0;
 }
--- a/sys/src/9/pc/archmp.c
+++ b/sys/src/9/pc/archmp.c
@@ -395,7 +395,7 @@
 		return 1;
 	}
 
-	if(cpuserver && m->havetsc)
+	if(m->havetsc && getconf("*notsc") == nil)
 		archmp.fastclock = tscticks;
 
 	return 0;
--- a/sys/src/9/pc/dat.h
+++ b/sys/src/9/pc/dat.h
@@ -242,7 +242,6 @@
 
 	int	loopconst;
 
-	Lock	apictimerlock;
 	int	cpumhz;
 	uvlong	cyclefreq;		/* Frequency of user readable cycle counter */
 	uvlong	cpuhz;
--- a/sys/src/9/pc/etheriwl.c
+++ b/sys/src/9/pc/etheriwl.c
@@ -1723,6 +1723,8 @@
 	filter = FilterNoDecrypt | FilterMulticast | FilterBeacon;
 	if(ctlr->prom){
 		filter |= FilterPromisc;
+		if(bss != nil)
+			ctlr->channel = bss->channel;
 		bss = nil;
 	}
 	if(bss != nil){
@@ -1743,9 +1745,17 @@
 	}
 	flags = RFlagTSF | RFlagCTSToSelf | RFlag24Ghz | RFlagAuto;
 
-	if(0) print("rxon: bssid %E, aid %x, channel %d, filter %x, flags %x\n",
-		ctlr->bssid, ctlr->aid, ctlr->channel, filter, flags);
+	if(ctlr->aid != 0)
+		setled(ctlr, 2, 0, 1);		/* on when associated */
+	else if(memcmp(ctlr->bssid, edev->bcast, Eaddrlen) != 0)
+		setled(ctlr, 2, 10, 10);	/* slow blink when connecting */
+	else
+		setled(ctlr, 2, 5, 5);		/* fast blink when scanning */
 
+	if(ctlr->wifi->debug)
+		print("#l%d: rxon: bssid %E, aid %x, channel %d, filter %x, flags %x\n",
+			edev->ctlrno, ctlr->bssid, ctlr->aid, ctlr->channel, filter, flags);
+
 	memset(p = c, 0, sizeof(c));
 	memmove(p, edev->ea, 6); p += 8;	/* myaddr */
 	memmove(p, ctlr->bssid, 6); p += 8;	/* bssid */
@@ -1833,7 +1843,6 @@
 	Wifipkt *w;
 	char *err;
 
-	w = (Wifipkt*)b->rp;
 	edev = wifi->ether;
 	ctlr = edev->ctlr;
 
@@ -1844,15 +1853,20 @@
 		return;
 	}
 
-	if(ctlr->prom == 0)
-	if(wn->aid != ctlr->aid
-	|| wn->channel != ctlr->channel
-	|| memcmp(wn->bssid, ctlr->bssid, Eaddrlen) != 0)
+	if((wn->channel != ctlr->channel)
+	|| (!ctlr->prom && (wn->aid != ctlr->aid || memcmp(wn->bssid, ctlr->bssid, Eaddrlen) != 0)))
 		rxon(edev, wn);
 
+	if(b == nil){
+		/* association note has no data to transmit */
+		qunlock(ctlr);
+		return;
+	}
+
 	rate = 0;
 	flags = 0;
 	nodeid = ctlr->bcastnodeid;
+	w = (Wifipkt*)b->rp;
 	if((w->a1[0] & 1) == 0){
 		flags |= TFlagNeedACK;
 
@@ -1943,14 +1957,21 @@
 static void
 setoptions(Ether *edev)
 {
+	char buf[64], *p;
 	Ctlr *ctlr;
-	char buf[64];
 	int i;
 
 	ctlr = edev->ctlr;
 	for(i = 0; i < edev->nopt; i++){
-		if(strncmp(edev->opt[i], "essid=", 6) == 0){
-			snprint(buf, sizeof(buf), "essid %s", edev->opt[i]+6);
+		snprint(buf, sizeof(buf), "%s", edev->opt[i]);
+		p = strchr(buf, '=');
+		if(p != nil)
+			*p = 0;
+		if(strcmp(buf, "debug") == 0
+		|| strcmp(buf, "essid") == 0
+		|| strcmp(buf, "bssid") == 0){
+			if(p != nil)
+				*p = ' ';
 			if(!waserror()){
 				wifictl(ctlr->wifi, buf, strlen(buf));
 				poperror();
@@ -1974,60 +1995,8 @@
 }
 
 static void
-iwlproc(void *arg)
-{
-	Ether *edev;
-	Ctlr *ctlr;
-	Wifi *wifi;
-	Wnode *bss;
-
-	edev = arg;
-	ctlr = edev->ctlr;
-	wifi = ctlr->wifi;
-
-	for(;;){
-		/* hop channels for catching beacons */
-		setled(ctlr, 2, 5, 5);
-		while(wifi->bss == nil){
-			qlock(ctlr);
-			if(wifi->bss != nil){
-				qunlock(ctlr);
-				break;
-			}
-			ctlr->channel = 1 + ctlr->channel % 11;
-			ctlr->aid = 0;
-			rxon(edev, nil);
-			qunlock(ctlr);
-			tsleep(&up->sleep, return0, 0, 1000);
-		}
-
-		/* wait for association */
-		setled(ctlr, 2, 10, 10);
-		while((bss = wifi->bss) != nil){
-			if(bss->aid != 0)
-				break;
-			tsleep(&up->sleep, return0, 0, 1000);
-		}
-
-		if(bss == nil)
-			continue;
-
-		/* wait for disassociation */
-		edev->link = 1;
-		setled(ctlr, 2, 0, 1);
-		while((bss = wifi->bss) != nil){
-			if(bss->aid == 0)
-				break;
-			tsleep(&up->sleep, return0, 0, 1000);
-		}
-		edev->link = 0;
-	}
-}
-
-static void
 iwlattach(Ether *edev)
 {
-	char name[32];
 	FWImage *fw;
 	Ctlr *ctlr;
 	char *err;
@@ -2071,9 +2040,6 @@
 		ctlr->aid = 0;
 
 		setoptions(edev);
-
-		snprint(name, sizeof(name), "#l%diwl", edev->ctlrno);
-		kproc(name, iwlproc, edev);
 
 		ctlr->attached = 1;
 	}
--- a/sys/src/9/pc/mtrr.c
+++ b/sys/src/9/pc/mtrr.c
@@ -290,6 +290,8 @@
 	qlock(&mtrrlk);
 	slot = -1;
 	vcnt = cap & Capvcnt;
+	if(vcnt > Nmtrr)
+		vcnt = Nmtrr;
 	for(i = 0; i < vcnt; i++){
 		mtrrget(&mtrr, i);
 		mok = mtrrdec(&mtrr, &mp, &msize, &mtype);
@@ -330,6 +332,8 @@
 	n += snprint(buf+n, bufsize-n, "cache default %s\n",
 		type2str(def & Deftype));
 	vcnt = cap & Capvcnt;
+	if(vcnt > Nmtrr)
+		vcnt = Nmtrr;
 	for(i = 0; i < vcnt; i++){
 		mtrrget(&mtrr, i);
 		if (mtrrdec(&mtrr, &base, &size, &type))
--- a/sys/src/9/pc/wifi.c
+++ b/sys/src/9/pc/wifi.c
@@ -29,10 +29,11 @@
 	SNAPHDRSIZE = 8,
 };
 
-static char Snone[] = "new";
 static char Sconn[] = "connecting";
 static char Sauth[] = "authenticated";
-static char Sunauth[] = "unauthentictaed";
+static char Sneedauth[] = "need authentication";
+static char Sunauth[] = "unauthenticated";
+
 static char Sassoc[] = "associated";
 static char Sunassoc[] = "unassociated";
 static char Sblocked[] = "blocked";	/* no keys negotiated. only pass EAPOL frames */
@@ -132,6 +133,8 @@
 	Wifipkt *w;
 	uint seq;
 
+	wn->lastsend = MACHP(0)->ticks;
+
 	seq = incref(&wifi->txseq);
 	seq <<= 4;
 
@@ -170,7 +173,7 @@
 			wn->lastseen = MACHP(0)->ticks;
 			return wn;
 		}
-		if(wn->lastseen < nn->lastseen)
+		if((long)(wn->lastseen - nn->lastseen) < 0)
 			nn = wn;
 	}
 	if(!new)
@@ -182,6 +185,51 @@
 }
 
 static void
+wifiprobe(Wifi *wifi, Wnode *wn)
+{
+	Wifipkt *w;
+	Block *b;
+	uchar *p;
+	int n;
+
+	n = strlen(wifi->essid);
+	if(n == 0){
+		/* no specific essid, just tell driver to tune channel */
+		(*wifi->transmit)(wifi, wn, nil);
+		return;
+	}
+
+	b = allocb(WIFIHDRSIZE + 512);
+	w = (Wifipkt*)b->wp;
+	w->fc[0] = 0x40;	/* probe request */
+	w->fc[1] = 0x00;	/* STA->STA */
+	memmove(w->a1, wifi->ether->bcast, Eaddrlen);	/* ??? */
+	memmove(w->a2, wifi->ether->ea, Eaddrlen);
+	memmove(w->a3, wifi->ether->bcast, Eaddrlen);
+	b->wp += WIFIHDRSIZE;
+	p = b->wp;
+
+	*p++ = 0x00;	/* set */
+	*p++ = n;
+	memmove(p, wifi->essid, n);
+	p += n;
+
+	*p++ = 1;	/* RATES (BUG: these are all lies!) */
+	*p++ = 4;
+	*p++ = 0x82;
+	*p++ = 0x84;
+	*p++ = 0x8b;
+	*p++ = 0x96;
+
+	*p++ = 0x03;	/* ds parameter set */
+	*p++ = 1;
+	*p++ = wn->channel;
+
+	b->wp = p;
+	wifitx(wifi, wn, b);
+}
+
+static void
 sendauth(Wifi *wifi, Wnode *bss)
 {
 	Wifipkt *w;
@@ -204,6 +252,9 @@
 	*p++ = 0;	/* status */
 	*p++ = 0;
 	b->wp = p;
+
+	bss->aid = 0;
+
 	wifitx(wifi, bss, b);
 }
 
@@ -252,6 +303,21 @@
 }
 
 static void
+setstatus(Wifi *wifi, Wnode *wn, char *new)
+{
+	char *old;
+
+	old = wn->status;
+	wn->status = new;
+	if(wifi->debug && new != old)
+		print("#l%d: status %E: %s -> %s (from pc=%#p)\n",
+			wifi->ether->ctlrno, 
+			wn->bssid, 
+			old, new,
+			getcallerpc(&wifi));
+}
+
+static void
 recvassoc(Wifi *wifi, Wnode *wn, uchar *d, int len)
 {
 	uint s;
@@ -266,14 +332,13 @@
 	case 0x00:
 		wn->aid = d[0] | d[1]<<8;
 		if(wn->rsnelen > 0)
-			wifi->status = Sblocked;
+			setstatus(wifi, wn, Sblocked);
 		else
-			wifi->status = Sassoc;
+			setstatus(wifi, wn, Sassoc);
 		break;
 	default:
 		wn->aid = 0;
-		wifi->status = Sunassoc;
-		return;
+		setstatus(wifi, wn, Sunassoc);
 	}
 }
 
@@ -280,6 +345,7 @@
 static void
 recvbeacon(Wifi *, Wnode *wn, uchar *d, int len)
 {
+	static uchar wpa1oui[4] = { 0x00, 0x50, 0xf2, 0x01 };
 	uchar *e, *x;
 	uchar t, m[256/8];
 
@@ -304,7 +370,7 @@
 		m[t/8] |= 1<<(t%8);
 
 		switch(t){
-		case 0:	/* SSID */
+		case 0x00:	/* SSID */
 			len = 0;
 			while(len < Essidlen && d+len < x && d[len] != 0)
 				len++;
@@ -315,15 +381,67 @@
 				wn->ssid[len] = 0;
 			}
 			break;
-		case 3:	/* DSPARAMS */
+		case 0x03:	/* DSPARAMS */
 			if(d != x)
 				wn->channel = d[0];
 			break;
+		case 0xdd:	/* vendor specific */
+			len = x - d;
+			if(len < sizeof(wpa1oui) || memcmp(d, wpa1oui, sizeof(wpa1oui)) != 0)
+				break;
+			/* no break */
+		case 0x30:	/* RSN information */
+			len = x - &d[-2];
+			memmove(wn->brsne, &d[-2], len);
+			wn->brsnelen = len;
+			break;
 		}
 	}
 }
 
 static void
+wifideauth(Wifi *wifi, Wnode *wn)
+{
+	Ether *ether;
+	Netfile *f;
+	int i;
+
+	/* deassociate node, clear keys */
+	setstatus(wifi, wn, Sunauth);
+	memset(wn->rxkey, 0, sizeof(wn->rxkey));
+	memset(wn->txkey, 0, sizeof(wn->txkey));
+	wn->aid = 0;
+
+	if(wn == wifi->bss){
+		/* notify driver about node aid association */
+		(*wifi->transmit)(wifi, wn, nil);
+
+		/* notify aux/wpa with a zero length write that we got deassociated from the ap */
+		ether = wifi->ether;
+		for(i=0; i<ether->nfile; i++){
+			f = ether->f[i];
+			if(f == nil || f->in == nil || f->inuse == 0 || f->type != 0x888e)
+				continue;
+			qwrite(f->in, 0, 0);
+		}
+	}
+}
+
+/* check if a node qualifies as our bss matching bssid and essid */
+static int
+goodbss(Wifi *wifi, Wnode *wn)
+{
+	if(memcmp(wifi->bssid, wifi->ether->bcast, Eaddrlen) != 0){
+		if(memcmp(wifi->bssid, wn->bssid, Eaddrlen) != 0)
+			return 0;	/* bssid doesnt match */
+	} else if(wifi->essid[0] == 0)
+		return 0;	/* both bssid and essid unspecified */
+	if(wifi->essid[0] != 0 && strcmp(wifi->essid, wn->ssid) != 0)
+		return 0;	/* essid doesnt match */
+	return 1;
+}
+
+static void
 wifiproc(void *arg)
 {
 	Wifi *wifi;
@@ -341,7 +459,7 @@
 		w = (Wifipkt*)b->rp;
 		if(w->fc[1] & 0x40){
 			/* encrypted */
-			if((wn = nodelookup(wifi, w->a2, 1)) == nil)
+			if((wn = nodelookup(wifi, w->a2, 0)) == nil)
 				continue;
 			if((b = wifidecrypt(wifi, wn, b)) != nil){
 				w = (Wifipkt*)b->rp;
@@ -355,42 +473,57 @@
 		/* management */
 		if((w->fc[0] & 0x0c) != 0x00)
 			continue;
+
 		switch(w->fc[0] & 0xf0){
 		case 0x50:	/* probe response */
+			if(wifi->debug)
+				print("#l%d: got probe from %E\n", wifi->ether->ctlrno, w->a3);
+			/* no break */
 		case 0x80:	/* beacon */
 			if((wn = nodelookup(wifi, w->a3, 1)) == nil)
 				continue;
 			b->rp += wifihdrlen(w);
 			recvbeacon(wifi, wn, b->rp, BLEN(b));
-			if(wifi->bss == nil && wifi->essid[0] != 0 && strcmp(wifi->essid, wn->ssid) == 0){
-				wifi->bss = wn;
-				wifi->status = Sconn;
+
+			if(wifi->bss == nil
+			&& TK2MS(MACHP(0)->ticks - wn->lastsend) > 1000
+			&& goodbss(wifi, wn)){
+				setstatus(wifi, wn, Sconn);
 				sendauth(wifi, wn);
 			}
 			continue;
 		}
+
 		if(memcmp(w->a1, wifi->ether->ea, Eaddrlen))
 			continue;
 		if((wn = nodelookup(wifi, w->a3, 0)) == nil)
 			continue;
-		if(wn != wifi->bss)
-			continue;
 		switch(w->fc[0] & 0xf0){
 		case 0x10:	/* assoc response */
 		case 0x30:	/* reassoc response */
 			b->rp += wifihdrlen(w);
 			recvassoc(wifi, wn, b->rp, BLEN(b));
+			/* notify driver about node aid association */
+			if(wn == wifi->bss)
+				(*wifi->transmit)(wifi, wn, nil);
 			break;
 		case 0xb0:	/* auth */
-			wifi->status = Sauth;
-			sendassoc(wifi, wn);
+			if(wifi->debug)
+				print("#l%d: got auth from %E\n", wifi->ether->ctlrno, wn->bssid);
+			if(wn->brsnelen > 0 && wn->rsnelen == 0)
+				setstatus(wifi, wn, Sneedauth);
+			else
+				setstatus(wifi, wn, Sauth);
+			if(wifi->bss == nil && goodbss(wifi, wn)){
+				wifi->bss = wn;
+				if(wn->status == Sauth)
+					sendassoc(wifi, wn);
+			}
 			break;
 		case 0xc0:	/* deauth */
-			wifi->status = Sunauth;
-			memset(wn->rxkey, 0, sizeof(wn->rxkey));
-			memset(wn->txkey, 0, sizeof(wn->txkey));
-			wn->aid = 0;
-			sendauth(wifi, wn);
+			if(wifi->debug)
+				print("#l%d: got deauth from %E\n", wifi->ether->ctlrno, wn->bssid);
+			wifideauth(wifi, wn);
 			break;
 		}
 	}
@@ -413,11 +546,11 @@
 	memmove(&e, b->rp, ETHERHDRSIZE);
 	b->rp += ETHERHDRSIZE;
 
-	if(wifi->status == Sblocked){
+	if(wn->status == Sblocked){
 		/* only pass EAPOL frames when port is blocked */
 		if((e.type[0]<<8 | e.type[1]) != 0x888e)
 			goto drop;
-	} else if(wifi->status != Sassoc)
+	} else if(wn->status != Sassoc)
 		goto drop;
 
 	b = padblock(b, WIFIHDRSIZE + SNAPHDRSIZE);
@@ -457,6 +590,56 @@
 	pexit("ether out queue closed", 0);
 }
 
+static void
+wifsproc(void *arg)
+{
+	Ether *ether;
+	Wifi *wifi;
+	Wnode wnscan;
+	Wnode *wn;
+	ulong now, tmout;
+
+	wifi = arg;
+	ether = wifi->ether;
+
+	wn = &wnscan;
+	memset(wn, 0, sizeof(*wn));
+	memmove(wn->bssid, ether->bcast, Eaddrlen);
+
+	while(waserror())
+		;
+Scan:
+	/* scan for access point */
+	while(wifi->bss == nil){
+		ether->link = 0;
+		wnscan.channel = 1 + wnscan.channel % 11;
+		wifiprobe(wifi, &wnscan);
+		tsleep(&up->sleep, return0, 0, 1000);
+	}
+
+	/* maintain access point */
+	tmout = 0;
+	while((wn = wifi->bss) != nil){
+		ether->link = (wn->status == Sassoc) || (wn->status == Sblocked);
+		now = MACHP(0)->ticks;
+		if(wn->status != Sneedauth && TK2SEC(now - wn->lastseen) > 60 || goodbss(wifi, wn) == 0){
+			wifideauth(wifi, wn);
+			wifi->bss = nil;
+			break;
+		}
+		if(TK2MS(now - wn->lastsend) > 1000){
+			if(wn->status == Sauth && (++tmout & 7) == 0)
+				wifideauth(wifi, wn);	/* stuck in auth, start over */
+			if(wn->status == Sconn || wn->status == Sunauth)
+				sendauth(wifi, wn);
+			if(wn->status == Sauth)
+				sendassoc(wifi, wn);
+		}
+		tsleep(&up->sleep, return0, 0, 500);
+	}
+	goto Scan;
+}
+
 Wifi*
 wifiattach(Ether *ether, void (*transmit)(Wifi*, Wnode*, Block*))
 {
@@ -473,12 +656,16 @@
 	}
 	wifi->ether = ether;
 	wifi->transmit = transmit;
-	wifi->status = Snone;
 
+	wifi->essid[0] = 0;
+	memmove(wifi->bssid, ether->bcast, Eaddrlen);
+
 	snprint(name, sizeof(name), "#l%dwifi", ether->ctlrno);
 	kproc(name, wifiproc, wifi);
 	snprint(name, sizeof(name), "#l%dwifo", ether->ctlrno);
 	kproc(name, wifoproc, wifi);
+	snprint(name, sizeof(name), "#l%dwifs", ether->ctlrno);
+	kproc(name, wifsproc, wifi);
 
 	return wifi;
 }
@@ -541,10 +728,10 @@
 }
 
 enum {
+	CMdebug,
 	CMessid,
 	CMauth,
-	CMunblock,
-
+	CMbssid,
 	CMrxkey0,
 	CMrxkey1,
 	CMrxkey2,
@@ -555,8 +742,10 @@
 
 static Cmdtab wifictlmsg[] =
 {
+	CMdebug,	"debug",	0,
 	CMessid,	"essid",	0,
 	CMauth,		"auth",		0,
+	CMbssid,	"bssid",	0,
 
 	CMrxkey0,	"rxkey0",	0,	/* group keys */
 	CMrxkey1,	"rxkey1",	0,
@@ -572,10 +761,12 @@
 long
 wifictl(Wifi *wifi, void *buf, long n)
 {
+	uchar addr[Eaddrlen];
 	Cmdbuf *cb;
 	Cmdtab *ct;
 	Wnode *wn;
 	Wkey *k;
+	int i;
 
 	cb = nil;
 	if(waserror()){
@@ -582,13 +773,14 @@
 		free(cb);
 		nexterror();
 	}
+	if(wifi->debug)
+		print("#l%d: wifictl: %.*s\n", wifi->ether->ctlrno, (int)n, buf);
+	memmove(addr, wifi->ether->bcast, Eaddrlen);
 	wn = wifi->bss;
 	cb = parsecmd(buf, n);
 	ct = lookupcmd(cb, wifictlmsg, nelem(wifictlmsg));
-	if(ct->index != CMessid){
-		if(ct->index >= CMrxkey0 && cb->nf > 1){
-			uchar addr[Eaddrlen];
-
+	if(ct->index >= CMauth){
+		if(cb->nf > 1 && (ct->index == CMbssid || ct->index >= CMrxkey0)){
 			if(parseether(addr, cb->f[1]) == 0){
 				cb->f++;
 				cb->nf--;
@@ -595,28 +787,52 @@
 				wn = nodelookup(wifi, addr, 0);
 			}
 		}
-		if(wn == nil)
+		if(wn == nil && ct->index != CMbssid)
 			error("missing node");
 	}
 	switch(ct->index){
+	case CMdebug:
+		if(cb->f[1] != nil)
+			wifi->debug = atoi(cb->f[1]);
+		else
+			wifi->debug ^= 1;
+		print("#l%d: debug: %d\n", wifi->ether->ctlrno, wifi->debug);
+		break;
 	case CMessid:
-		if(cb->f[1] == nil){
-			wifi->essid[0] = 0;
-			wifi->bss = nil;
-			wifi->status = Snone;
-		} else {
+		if(cb->f[1] != nil)
 			strncpy(wifi->essid, cb->f[1], Essidlen);
-			for(wn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++)
-				if(strcmp(wifi->essid, wn->ssid) == 0){
-					wifi->bss = wn;
-					wifi->status = Sconn;
-					sendauth(wifi, wn);
-					break;
-				}
+		else
+			wifi->essid[0] = 0;
+	Findbss:
+		wn = wifi->bss;
+		if(wn != nil){
+			if(goodbss(wifi, wn))
+				break;
+			wifideauth(wifi, wn);
 		}
+		wifi->bss = nil;
+		if(wifi->essid[0] == 0 && memcmp(wifi->bssid, wifi->ether->bcast, Eaddrlen) == 0)
+			break;
+		for(wn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++)
+			if(goodbss(wifi, wn)){
+				setstatus(wifi, wn, Sconn);
+				sendauth(wifi, wn);
+			}
+		/* wait 3 seconds for authentication response */
+		for(i=0; i < 30; i++){
+			if(wifi->bss != nil)
+				goto done;
+			if(!waserror()){
+				tsleep(&up->sleep, return0, 0, 100);
+				poperror();
+			}
+		}
+		error("connect timeout");
 		break;
+	case CMbssid:
+		memmove(wifi->bssid, addr, Eaddrlen);
+		goto Findbss;
 	case CMauth:
-		wifi->status = Sauth;
 		memset(wn->rxkey, 0, sizeof(wn->rxkey));
 		memset(wn->txkey, 0, sizeof(wn->txkey));
 		if(cb->f[1] == nil)
@@ -623,7 +839,13 @@
 			wn->rsnelen = 0;
 		else
 			wn->rsnelen = hextob(cb->f[1], nil, wn->rsne, sizeof(wn->rsne));
-		sendassoc(wifi, wn);
+		if(wn->aid == 0){
+			setstatus(wifi, wn, Sconn);
+			sendauth(wifi, wn);
+		} else {
+			setstatus(wifi, wn, Sauth);
+			sendassoc(wifi, wn);
+		}
 		break;
 	case CMrxkey0: case CMrxkey1: case CMrxkey2: case CMrxkey3: case CMrxkey4:
 	case CMtxkey0:
@@ -633,10 +855,11 @@
 			k = &wn->txkey[ct->index - CMtxkey0];
 		if(cb->f[1] == nil || parsekey(k, cb->f[1]) != 0)
 			error("bad key");
-		if(ct->index >= CMtxkey0 && wifi->status == Sblocked && wifi->bss == wn)
-			wifi->status = Sassoc;
+		if(ct->index >= CMtxkey0 && wn->status == Sblocked)
+			setstatus(wifi, wn, Sassoc);
 		break;
 	}
+done:
 	poperror();
 	free(cb);
 	return n;
@@ -649,14 +872,36 @@
 	char *s, *p, *e;
 	Wnode *wn;
 	long now;
+	int i;
 
 	p = s = smalloc(4096);
 	e = s + 4096;
 
-	p = seprint(p, e, "status: %s\n", wifi->status);
-	p = seprint(p, e, "essid: %s\n", wifi->essid);
 	wn = wifi->bss;
-	p = seprint(p, e, "bssid: %E\n", wn != nil ? wn->bssid : zeros);
+	if(wn != nil){
+		p = seprint(p, e, "essid: %s\n", wn->ssid);
+		p = seprint(p, e, "bssid: %E\n", wn->bssid);
+		p = seprint(p, e, "status: %s\n", wn->status);
+		p = seprint(p, e, "channel: %.2d\n", wn->channel);
+
+		/* only print key ciphers and key length */
+		for(i = 0; i<nelem(wn->rxkey); i++)
+			p = seprint(p, e, "rxkey%d: %s:[%d]\n", i,
+				ciphers[wn->rxkey[i].cipher], wn->rxkey[i].len);
+		for(i = 0; i<nelem(wn->txkey); i++)
+			p = seprint(p, e, "txkey%d: %s:[%d]\n", i,
+				ciphers[wn->txkey[i].cipher], wn->txkey[i].len);
+
+		if(wn->brsnelen > 0){
+			p = seprint(p, e, "brsne: ");
+			for(i=0; i<wn->brsnelen; i++)
+				p = seprint(p, e, "%.2X", wn->brsne[i]);
+			p = seprint(p, e, "\n");
+		}
+	} else {
+		p = seprint(p, e, "essid: %s\n", wifi->essid);
+		p = seprint(p, e, "bssid: %E\n", wifi->bssid);
+	}
 
 	now = MACHP(0)->ticks;
 	for(wn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++){
--- a/sys/src/9/pc/wifi.h
+++ b/sys/src/9/pc/wifi.h
@@ -26,16 +26,23 @@
 	uchar	bssid[Eaddrlen];
 	char	ssid[Essidlen+2];
 
+	char	*status;
+
 	int	rsnelen;
-	uchar	rsne[256];
+	uchar	rsne[258];
 	Wkey	txkey[1];
 	Wkey	rxkey[5];
 
+	int	aid;		/* association id */
+	ulong	lastsend;
+	ulong	lastseen;
+
+	/* stuff from beacon */
 	int	ival;
 	int	cap;
-	int	aid;
 	int	channel;
-	long	lastseen;
+	int	brsnelen;
+	uchar	brsne[258];
 };
 
 struct Wifi
@@ -42,12 +49,18 @@
 {
 	Ether	*ether;
 
+	int	debug;
+
 	Queue	*iq;
-	char	*status;
+	ulong	watchdog;
 	Ref	txseq;
 	void	(*transmit)(Wifi*, Wnode*, Block*);
 
+	/* for searching */
+	uchar	bssid[Eaddrlen];
 	char	essid[Essidlen+2];
+
+	/* effective base station */
 	Wnode	*bss;
 
 	Wnode	node[32];
--- a/sys/src/9/port/alarm.c
+++ b/sys/src/9/port/alarm.c
@@ -37,7 +37,7 @@
 }
 
 /*
- *  called every clock tick
+ *  called every clock tick on cpu0
  */
 void
 checkalarms(void)
--- a/sys/src/9/port/devcons.c
+++ b/sys/src/9/port/devcons.c
@@ -73,8 +73,7 @@
  */
 struct {
 	Lock lk;
-//	char buf[16384];		/* normal */
-	char buf[256*1024];		/* for acpi debugging */
+	char buf[16384];
 	uint n;
 } kmesg;
 
--- a/sys/src/9/port/devmnt.c
+++ b/sys/src/9/port/devmnt.c
@@ -794,8 +794,12 @@
 	if(m->msize == 0)
 		panic("msize");
 	n = convS2M(&r->request, r->rpc, m->msize);
-	if(n < 0)
-		panic("bad message type in mountio");
+	if(n <= 0){
+		print("mountio: proc %s %lud: convS2M returned %d for tag %d fid %d T%d\n",
+			up->text, up->pid, n, r->request.tag, r->request.fid, r->request.type);
+		error(Emountrpc);
+	}
+		
 	if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
 		error(Emountrpc);
 	r->stime = fastticks(nil);
--- a/sys/src/9/port/devshr.c
+++ b/sys/src/9/port/devshr.c
@@ -89,7 +89,7 @@
 {
 	if(decref(mpt))
 		return;
-	if(mpt->m.to)
+	if(mpt->m.to != nil)
 		cclose(mpt->m.to);
 	free(mpt->name);
 	free(mpt->owner);
@@ -148,9 +148,9 @@
 	nc = devclone(c);
 	sch = smalloc(sizeof(*sch));
 	memmove(sch, och, sizeof(*sch));
-	if(sch->shr)
+	if(sch->shr != nil)
 		incref(sch->shr);
-	if(sch->mpt)
+	if(sch->mpt != nil)
 		incref(sch->mpt);
 	sch->chan = nc;
 	nc->aux = sch;
@@ -165,9 +165,9 @@
 	sch = tosch(c);
 	c->aux = nil;
 	sch->chan = nil;
-	if(sch->mpt)
+	if(sch->mpt != nil)
 		putmpt(sch->mpt);
-	if(sch->shr)
+	if(sch->shr != nil)
 		putshr(sch->shr);
 	free(sch);	
 }
@@ -226,7 +226,7 @@
 			sch->shr = nil;
 		} else if(sch->level == Qcroot || sch->level == Qroot) {
 			qlock(&shrslk);
-			for(shr = shrs; shr; shr = shr->next)
+			for(shr = shrs; shr != nil; shr = shr->next)
 				if(strcmp(nam, shr->name) == 0){
 					incref(shr);
 					break;
@@ -242,7 +242,7 @@
 			shr = sch->shr;
 			h = &shr->umh;
 			rlock(&h->lock);
-			for(m = h->mount; m; m = m->next){
+			for(m = h->mount; m != nil; m = m->next){
 				mpt = tompt(m);
 				if(strcmp(nam, mpt->name) == 0){
 					incref(mpt);
@@ -259,7 +259,7 @@
 			h = &shr->umh;
 			wq2 = nil;
 			rlock(&h->lock);
-			for(m = h->mount; m && wq2 == nil; m = m->next){
+			for(m = h->mount; m != nil && wq2 == nil; m = m->next){
 				if(m->to == nil)
 					continue;
 				if(waserror())
@@ -302,7 +302,7 @@
 	case Qroot:
 	case Qcroot:
 		qlock(&shrslk);
-		for(shr = shrs; shr && s; shr = shr->next)
+		for(shr = shrs; shr != nil && s > 0; shr = shr->next)
 			s--;
 		if(shr == nil){
 			qunlock(&shrslk);
@@ -321,7 +321,7 @@
 		shr = sch->shr;
 		h = &shr->umh;
 		rlock(&h->lock);
-		for(m = h->mount; m && s; m = m->next)
+		for(m = h->mount; m != nil && s > 0; m = m->next)
 			s--;
 		if(m == nil){
 			runlock(&h->lock);
@@ -469,7 +469,7 @@
 			qunlock(&shrslk);
 			nexterror();
 		}
-		for(shr = shrs; shr; shr = shr->next)
+		for(shr = shrs; shr != nil; shr = shr->next)
 			if(strcmp(name, shr->name) == 0)
 				error(Eexist);
 
@@ -494,7 +494,7 @@
 	case Qcshr:
 		if(up->pgrp->noattach)
 			error(Enoattach);
-		if((perm & DMDIR) || openmode(omode) != OWRITE)
+		if((perm & DMDIR) != 0 || openmode(omode) != OWRITE)
 			error(Eperm);
 
 		shr = sch->shr;
@@ -511,7 +511,7 @@
 			wunlock(&h->lock);
 			nexterror();
 		}
-		for(m = h->mount; m; m = m->next){
+		for(m = h->mount; m != nil; m = m->next){
 			mpt = tompt(m);
 			if(strcmp(name, mpt->name) == 0)
 				error(Eexist);
@@ -574,13 +574,13 @@
 		h = &shr->umh;
 		qlock(&shrslk);
 		rlock(&h->lock);
-		if(h->mount){
+		if(h->mount != nil){
 			runlock(&h->lock);
 			qunlock(&shrslk);
 			error("directory not empty");
 		}
 		runlock(&h->lock);
-		for(sl = &shrs; *sl; sl = &((*sl)->next))
+		for(sl = &shrs; *sl != nil; sl = &((*sl)->next))
 			if(*sl == shr){
 				*sl = shr->next;
 				shr->next = nil;
@@ -594,7 +594,7 @@
 		m = &mpt->m;
 		h = &shr->umh;
 		wlock(&h->lock);
-		for(ml = &h->mount; *ml; ml = &((*ml)->next))
+		for(ml = &h->mount; *ml != nil; ml = &((*ml)->next))
 			if(*ml == m){
 				*ml = m->next;
 				m->next = nil;
@@ -618,6 +618,15 @@
 	Ent *ent;
 	Dir d;
 
+	strs = smalloc(n);
+	if(waserror()){
+		free(strs);
+		nexterror();
+	}
+	n = convM2D(dp, n, &d, strs);
+	if(n == 0)
+		error(Eshortstat);
+
 	h = nil;
 	sch = tosch(c);
 	switch(sch->level){
@@ -645,19 +654,7 @@
 	if(strcmp(ent->owner, up->user) && !iseve())
 		error(Eperm);
 
-	strs = smalloc(n);
-	if(waserror()){
-		free(strs);
-		nexterror();
-	}
-	n = convM2D(dp, n, &d, strs);
-	if(n == 0)
-		error(Eshortstat);
-	if(d.mode != ~0UL)
-		ent->perm = d.mode & 0777;
-	if(d.uid && *d.uid)
-		kstrdup(&ent->owner, d.uid);
-	if(d.name && *d.name && strcmp(ent->name, d.name) != 0) {
+	if(d.name != nil && *d.name && strcmp(ent->name, d.name) != 0) {
 		if(strchr(d.name, '/') != nil)
 			error(Ebadchar);
 		if(strlen(d.name) >= sizeof(up->genbuf))
@@ -664,8 +661,10 @@
 			error(Etoolong);
 		kstrdup(&ent->name, d.name);
 	}
-	poperror();
-	free(strs);
+	if(d.uid != nil && *d.uid)
+		kstrdup(&ent->owner, d.uid);
+	if(d.mode != ~0UL)
+		ent->perm = d.mode & 0777;
 
 	switch(sch->level){
 	case Qcshr:
@@ -677,6 +676,10 @@
 		wunlock(&h->lock);
 		break;
 	}
+
+	poperror();
+	free(strs);
+
 	return n;
 }
 
@@ -771,7 +774,7 @@
 	m->to = c0;
 	wunlock(&h->lock);
 
-	if(bc)
+	if(bc != nil)
 		cclose(bc);
 
 	return n;
@@ -810,7 +813,7 @@
 static void
 chowner(Ent *ent, char *old, char *new)
 {
-	if(ent->owner!=nil && strcmp(old, ent->owner)==0)
+	if(ent->owner != nil && strcmp(old, ent->owner) == 0)
 		kstrdup(&ent->owner, new);
 }
 
@@ -821,9 +824,9 @@
 	Mount *m;
 
 	qlock(&shrslk);
-	for(shr = shrs; shr; shr = shr->next){
+	for(shr = shrs; shr != nil; shr = shr->next){
 		wlock(&shr->umh.lock);
-		for(m = shr->umh.mount; m; m = m->next)
+		for(m = shr->umh.mount; m != nil; m = m->next)
 			chowner(tompt(m), old, new);
 		wunlock(&shr->umh.lock);
 		chowner(shr, old, new);
--- a/sys/src/9/port/devsrv.c
+++ b/sys/src/9/port/devsrv.c
@@ -25,9 +25,11 @@
 srvlookup(char *name, ulong qidpath)
 {
 	Srv *sp;
-	for(sp = srv; sp; sp = sp->link)
-		if(sp->path == qidpath || (name && strcmp(sp->name, name) == 0))
+
+	for(sp = srv; sp != nil; sp = sp->link) {
+		if(sp->path == qidpath || (name != nil && strcmp(sp->name, name) == 0))
 			return sp;
+	}
 	return nil;
 }
 
@@ -43,13 +45,13 @@
 	}
 
 	qlock(&srvlk);
-	if(name)
+	if(name != nil)
 		sp = srvlookup(name, -1);
 	else {
-		for(sp = srv; sp && s; sp = sp->link)
+		for(sp = srv; sp != nil && s > 0; sp = sp->link)
 			s--;
 	}
-	if(sp == 0 || name && (strlen(sp->name) >= sizeof(up->genbuf))) {
+	if(sp == nil || (name != nil && (strlen(sp->name) >= sizeof(up->genbuf)))) {
 		qunlock(&srvlk);
 		return -1;
 	}
@@ -91,13 +93,18 @@
 	Srv *sp;
 	char *s;
 
-	for(sp = srv; sp; sp = sp->link)
+	s = nil;
+	qlock(&srvlk);
+	for(sp = srv; sp != nil; sp = sp->link) {
 		if(sp->chan == c){
-			s = smalloc(3+strlen(sp->name)+1);
-			sprint(s, "#s/%s", sp->name);
-			return s;
+			s = malloc(3+strlen(sp->name)+1);
+			if(s != nil)
+				sprint(s, "#s/%s", sp->name);
+			break;
 		}
-	return nil;
+	}
+	qunlock(&srvlk);
+	return s;
 }
 
 static Chan*
@@ -104,6 +111,7 @@
 srvopen(Chan *c, int omode)
 {
 	Srv *sp;
+	Chan *nc;
 
 	if(c->qid.type == QTDIR){
 		if(omode & ORCLOSE)
@@ -122,7 +130,7 @@
 	}
 
 	sp = srvlookup(nil, c->qid.path);
-	if(sp == 0 || sp->chan == 0)
+	if(sp == nil || sp->chan == nil)
 		error(Eshutdown);
 
 	if(omode&OTRUNC)
@@ -131,17 +139,19 @@
 		error(Eperm);
 	devpermcheck(sp->owner, sp->perm, omode);
 
-	cclose(c);
-	incref(sp->chan);
+	nc = sp->chan;
+	incref(nc);
+
 	qunlock(&srvlk);
 	poperror();
-	return sp->chan;
+
+	cclose(c);
+	return nc;
 }
 
 static Chan*
 srvcreate(Chan *c, char *name, int omode, ulong perm)
 {
-	char *sname;
 	Srv *sp;
 
 	if(openmode(omode) != OWRITE)
@@ -151,35 +161,35 @@
 		error(Etoolong);
 
 	sp = smalloc(sizeof *sp);
-	sname = smalloc(strlen(name)+1);
+	kstrdup(&sp->name, name);
+	kstrdup(&sp->owner, up->user);
 
 	qlock(&srvlk);
 	if(waserror()){
-		free(sp);
-		free(sname);
 		qunlock(&srvlk);
+		free(sp->owner);
+		free(sp->name);
+		free(sp);
 		nexterror();
 	}
-	if(sp == nil || sname == nil)
-		error(Enomem);
-	if(srvlookup(name, -1))
+	if(srvlookup(name, -1) != nil)
 		error(Eexist);
 
+	sp->perm = perm&0777;
 	sp->path = qidpath++;
-	sp->link = srv;
-	strcpy(sname, name);
-	sp->name = sname;
-	c->qid.type = QTFILE;
+
 	c->qid.path = sp->path;
+	c->qid.type = QTFILE;
+
+	sp->link = srv;
 	srv = sp;
+
 	qunlock(&srvlk);
 	poperror();
 
-	kstrdup(&sp->owner, up->user);
-	sp->perm = perm&0777;
-
 	c->flag |= COPEN;
 	c->mode = OWRITE;
+
 	return c;
 }
 
@@ -197,13 +207,12 @@
 		nexterror();
 	}
 	l = &srv;
-	for(sp = *l; sp; sp = sp->link) {
+	for(sp = *l; sp != nil; sp = *l) {
 		if(sp->path == c->qid.path)
 			break;
-
 		l = &sp->link;
 	}
-	if(sp == 0)
+	if(sp == nil)
 		error(Enonexist);
 
 	/*
@@ -219,10 +228,12 @@
 		error(Eperm);
 
 	*l = sp->link;
+	sp->link = nil;
+
 	qunlock(&srvlk);
 	poperror();
 
-	if(sp->chan)
+	if(sp->chan != nil)
 		cclose(sp->chan);
 	free(sp->owner);
 	free(sp->name);
@@ -233,36 +244,35 @@
 srvwstat(Chan *c, uchar *dp, int n)
 {
 	char *strs;
-	Dir d;
 	Srv *sp;
+	Dir d;
 
 	if(c->qid.type & QTDIR)
 		error(Eperm);
 
-	strs = nil;
+	strs = smalloc(n);
+	if(waserror()){
+		free(strs);
+		nexterror();
+	}
+	n = convM2D(dp, n, &d, strs);
+	if(n == 0)
+		error(Eshortstat);
+
 	qlock(&srvlk);
 	if(waserror()){
 		qunlock(&srvlk);
-		free(strs);
 		nexterror();
 	}
 
 	sp = srvlookup(nil, c->qid.path);
-	if(sp == 0)
+	if(sp == nil)
 		error(Enonexist);
 
 	if(strcmp(sp->owner, up->user) != 0 && !iseve())
 		error(Eperm);
 
-	strs = smalloc(n);
-	n = convM2D(dp, n, &d, strs);
-	if(n == 0)
-		error(Eshortstat);
-	if(d.mode != ~0UL)
-		sp->perm = d.mode & 0777;
-	if(d.uid && *d.uid)
-		kstrdup(&sp->owner, d.uid);
-	if(d.name && *d.name && strcmp(sp->name, d.name) != 0) {
+	if(d.name != nil && *d.name && strcmp(sp->name, d.name) != 0) {
 		if(strchr(d.name, '/') != nil)
 			error(Ebadchar);
 		if(strlen(d.name) >= sizeof(up->genbuf))
@@ -269,9 +279,17 @@
 			error(Etoolong);
 		kstrdup(&sp->name, d.name);
 	}
+	if(d.uid != nil && *d.uid)
+		kstrdup(&sp->owner, d.uid);
+	if(d.mode != ~0UL)
+		sp->perm = d.mode & 0777;
+
 	qunlock(&srvlk);
+	poperror();
+
 	free(strs);
 	poperror();
+
 	return n;
 }
 
@@ -325,13 +343,14 @@
 	if(c1->qid.type & QTAUTH)
 		error("cannot post auth file in srv");
 	sp = srvlookup(nil, c->qid.path);
-	if(sp == 0)
+	if(sp == nil)
 		error(Enonexist);
 
-	if(sp->chan)
+	if(sp->chan != nil)
 		error(Ebadusefd);
 
 	sp->chan = c1;
+
 	qunlock(&srvlk);
 	poperror();
 	return n;
@@ -364,8 +383,9 @@
 	Srv *sp;
 
 	qlock(&srvlk);
-	for(sp = srv; sp; sp = sp->link)
-		if(sp->owner!=nil && strcmp(old, sp->owner)==0)
+	for(sp = srv; sp != nil; sp = sp->link) {
+		if(sp->owner != nil && strcmp(old, sp->owner) == 0)
 			kstrdup(&sp->owner, new);
+	}
 	qunlock(&srvlk);
 }
--- a/sys/src/9/port/portclock.c
+++ b/sys/src/9/port/portclock.c
@@ -153,7 +153,8 @@
 		exit(0);
 	}
 
-	checkalarms();
+	if(m->machno == 0)
+		checkalarms();
 
 	if(up && up->state == Running)
 		hzsched();	/* in proc.c */
--- a/sys/src/cmd/9nfs/9p.c
+++ b/sys/src/cmd/9nfs/9p.c
@@ -44,6 +44,8 @@
 		clog("xmesg read error: %r\n");
 		return -1;
 	}
+	if(n == 0)
+		goto again;
 	if(convM2S(s->data, n, &s->f) <= 0){
 		clog("xmesg bad convM2S %d %.2x %.2x %.2x %.2x\n",
 			n, ((uchar*)s->data)[0], ((uchar*)s->data)[1],
--- a/sys/src/cmd/abaco/util.c
+++ b/sys/src/cmd/abaco/util.c
@@ -866,7 +866,7 @@
 int
 findctype(char *b, int l, char *keyword, char *s)
 {
-	char *p, *e;
+	char *p, *e, c;
 	int i;
 
 	p = cistrstr(s, keyword);
@@ -882,14 +882,18 @@
 		p++;
 	if(!*p)
 		return -1;
-	if(*p == '"'){
+	switch (c = *p){
+	case '"':
+	case '\'':
 		p++;
-		e = strchr(p, '"');
+		e = strchr(p, c);
 		if(!e)
 			return -1;
-	}else
+		break;
+	default:
 		for(e = p; *e < 127 && *e > ' ' ; e++)
 			;
+	}
 	i = e-p;
 	if(i < 1)
 		return -1;
--- a/sys/src/cmd/acme/fsys.c
+++ b/sys/src/cmd/acme/fsys.c
@@ -148,7 +148,8 @@
 	x = nil;
 	for(;;){
 		buf = emalloc(messagesize+UTFmax);	/* overflow for appending partial rune in xfidwrite */
-		n = read9pmsg(sfd, buf, messagesize);
+		while((n = read9pmsg(sfd, buf, messagesize)) == 0 && !closing)
+			;
 		if(n <= 0){
 			if(closing)
 				break;
--- a/sys/src/cmd/auth/asn12dsa.c
+++ b/sys/src/cmd/auth/asn12dsa.c
@@ -18,7 +18,7 @@
 	uchar *buf;
 	int fd;
 	long n, tot;
-	char *tag, *file;
+	char *tag;
 	DSApriv *key;
 
 	fmtinstall('B', mpfmt);
@@ -35,13 +35,12 @@
 	if(argc != 0 && argc != 1)
 		usage();
 
-	if(argc == 1)
-		file = argv[0];
-	else
-		file = "/dev/stdin";
+	fd = 0;
+	if(argc == 1){
+		if((fd = open(*argv, OREAD)) < 0)
+			sysfatal("open %s: %r", *argv);
+	}
 
-	if((fd = open(file, OREAD)) < 0)
-		sysfatal("open %s: %r", file);
 	buf = nil;
 	tot = 0;
 	for(;;){
--- a/sys/src/cmd/aux/consolefs.c
+++ b/sys/src/cmd/aux/consolefs.c
@@ -681,8 +681,9 @@
 		}
 		free(d);
 		r = allocreq(fs, messagesize);
-		n = read9pmsg(fs->fd, r->buf, messagesize);
-		if(n <= 0)
+		while((n = read9pmsg(fs->fd, r->buf, messagesize)) == 0)
+			;
+		if(n < 0)
 			fatal("unmounted");
 
 		if(convM2S(r->buf, n, &r->f) == 0){
--- a/sys/src/cmd/aux/depend.c
+++ b/sys/src/cmd/aux/depend.c
@@ -365,9 +365,10 @@
 	for(;;){
 		r = allocreq(messagesize);
 		qlock(&iolock);
-		n = read9pmsg(fs->fd, r->buf, messagesize);
+		while((n = read9pmsg(fs->fd, r->buf, messagesize)) == 0)
+			;
 		qunlock(&iolock);
-		if(n <= 0)
+		if(n < 0)
 			fatal("read9pmsg error: %r");
 
 		if(convM2S(r->buf, n, &r->f) == 0){
--- a/sys/src/cmd/aux/timesync.c
+++ b/sys/src/cmd/aux/timesync.c
@@ -288,8 +288,8 @@
 	/* figure out our time interface and initial frequency */
 	inittime();
 	gettime(0, 0, &hz);
-	minhz = hz/10;
-	maxhz = hz*10;
+	minhz = hz / 2;
+	maxhz = hz * 2;
 	myprec = getclockprecision(hz);
 
 	/* convert the accuracy from nanoseconds to ticks */
--- a/sys/src/cmd/aux/wpa.c
+++ b/sys/src/cmd/aux/wpa.c
@@ -66,18 +66,26 @@
 char	essid[32+1];
 uvlong	lastrepc;
 
+uchar rsntkipoui[4] = {0x00, 0x0F, 0xAC, 0x02};
+uchar rsnccmpoui[4] = {0x00, 0x0F, 0xAC, 0x04};
+uchar rsnapskoui[4] = {0x00, 0x0F, 0xAC, 0x02};
+
 uchar	rsnie[] = {
 	0x30,			/* RSN */
 	0x14,			/* length */
 	0x01, 0x00,		/* version 1 */
 	0x00, 0x0F, 0xAC, 0x04,	/* group cipher suite CCMP */
-	0x01, 0x00,		/* peerwise cipher suite count 1 */
-	0x00, 0x0F, 0xAC, 0x04,	/* peerwise cipher suite CCMP */
+	0x01, 0x00,		/* pairwise cipher suite count 1 */
+	0x00, 0x0F, 0xAC, 0x04,	/* pairwise cipher suite CCMP */
 	0x01, 0x00,		/* authentication suite count 1 */
 	0x00, 0x0F, 0xAC, 0x02,	/* authentication suite PSK */
 	0x00, 0x00,		/* capabilities */
 };
 
+uchar wpa1oui[4]    = {0x00, 0x50, 0xF2, 0x01};
+uchar wpatkipoui[4] = {0x00, 0x50, 0xF2, 0x02};
+uchar wpaapskoui[4] = {0x00, 0x50, 0xF2, 0x02};
+
 uchar	wpaie[] = {
 	0xdd,			/* vendor specific */
 	0x16,			/* length */
@@ -84,14 +92,37 @@
 	0x00, 0x50, 0xf2, 0x01,	/* WPAIE type 1 */
 	0x01, 0x00,		/* version 1 */
 	0x00, 0x50, 0xf2, 0x02,	/* group cipher suite TKIP */
-	0x01, 0x00,		/* peerwise cipher suite count 1 */
-	0x00, 0x50, 0xf2, 0x02,	/* peerwise cipher suite TKIP */
+	0x01, 0x00,		/* pairwise cipher suite count 1 */
+	0x00, 0x50, 0xf2, 0x02,	/* pairwise cipher suite TKIP */
 	0x01, 0x00,		/* authentication suite count 1 */
 	0x00, 0x50, 0xf2, 0x02,	/* authentication suite PSK */
 };
 
+int
+hextob(char *s, char **sp, uchar *b, int n)
+{
+	int r;
+
+	n <<= 1;
+	for(r = 0; r < n && *s; s++){
+		*b <<= 4;
+		if(*s >= '0' && *s <= '9')
+			*b |= (*s - '0');
+		else if(*s >= 'a' && *s <= 'f')
+			*b |= 10+(*s - 'a');
+		else if(*s >= 'A' && *s <= 'F')
+			*b |= 10+(*s - 'A');
+		else break;
+		if((++r & 1) == 0)
+			b++;
+	}
+	if(sp != nil)
+		*sp = s;
+	return r >> 1;
+}
+
 char*
-getessid(void)
+getifstats(char *key, char *val, int nval)
 {
 	char buf[8*1024], *f[2], *p, *e;
 	int fd, n;
@@ -99,24 +130,175 @@
 	snprint(buf, sizeof(buf), "%s/ifstats", devdir);
 	if((fd = open(buf, OREAD)) < 0)
 		return nil;
-	n = read(fd, buf, sizeof(buf)-1);
+	n = readn(fd, buf, sizeof(buf)-1);
 	close(fd);
-	if(n > 0){
-		buf[n] = 0;
-		for(p = buf; (e = strchr(p, '\n')) != nil; p = e){
-			*e++ = 0;
-			if(tokenize(p, f, 2) != 2)
-				continue;
-			if(strcmp(f[0], "essid:") != 0)
-				continue;
-			strncpy(essid, f[1], 32);
-			return essid;
-		}
+	if(n <= 0)
+		return nil;
+	buf[n] = 0;
+	for(p = buf; (e = strchr(p, '\n')) != nil; p = e){
+		*e++ = 0;
+		if(tokenize(p, f, 2) != 2)
+			continue;
+		if(strcmp(f[0], key) != 0)
+			continue;
+		strncpy(val, f[1], nval);
+		val[nval-1] = 0;
+		return val;
 	}
 	return nil;
 }
 
+char*
+getessid(void)
+{
+	return getifstats("essid:", essid, sizeof(essid));
+}
+
 int
+buildrsne(uchar rsne[258])
+{
+	char buf[1024];
+	uchar brsne[258];
+	int brsnelen;
+	uchar *p, *w, *e;
+	int i, n;
+
+	if(getifstats("brsne:", buf, sizeof(buf)) == nil)
+		return 0;	/* not an error, might be old kernel */
+
+	brsnelen = hextob(buf, nil, brsne, sizeof(brsne));
+	if(brsnelen <= 4){
+trunc:		sysfatal("invalid or truncated RSNE; brsne: %s", buf);
+		return 0;
+	}
+
+	w = rsne;
+	p = brsne;
+	e = p + brsnelen;
+	if(p[0] == 0x30){
+		p += 2;
+
+		/* RSN */
+		*w++ = 0x30;
+		*w++ = 0;	/* length */
+	} else if(p[0] == 0xDD){
+		p += 2;
+		if((e - p) < 4 || memcmp(p, wpa1oui, 4) != 0){
+			sysfatal("unrecognized WPAIE type; brsne: %s", buf);
+			return 0;
+		}
+
+		/* WPA */
+		*w++ = 0xDD;
+		*w++ = 0;	/* length */
+
+		memmove(w, wpa1oui, 4);
+		w += 4;
+		p += 4;
+	} else {
+		sysfatal("unrecognized RSNE type; brsne: %s", buf);
+		return 0;
+	}
+
+	if((e - p) < 6)
+		goto trunc;
+
+	*w++ = *p++;		/* version */
+	*w++ = *p++;
+
+	if(rsne[0] == 0x30){
+		if(memcmp(p, rsnccmpoui, 4) == 0)
+			groupcipher = &ccmp;
+		else if(memcmp(p, rsntkipoui, 4) == 0)
+			groupcipher = &tkip;
+		else {
+			sysfatal("unrecognized RSN group cipher; brsne: %s", buf);
+			return 0;
+		}
+	} else {
+		if(memcmp(p, wpatkipoui, 4) != 0){
+			sysfatal("unrecognized WPA group cipher; brsne: %s", buf);
+			return 0;
+		}
+		groupcipher = &tkip;
+	}
+
+	memmove(w, p, 4);	/* group cipher */
+	w += 4;
+	p += 4;
+
+	if((e - p) < 6)
+		goto trunc;
+
+	*w++ = 0x01;		/* # of peer ciphers */
+	*w++ = 0x00;
+	n = *p++;
+	n |= *p++ << 8;
+
+	if(n <= 0)
+		goto trunc;
+
+	peercipher = &tkip;
+	for(i=0; i<n; i++){
+		if((e - p) < 4)
+			goto trunc;
+
+		if(rsne[0] == 0x30 && memcmp(p, rsnccmpoui, 4) == 0 && peercipher == &tkip)
+			peercipher = &ccmp;
+		p += 4;
+	}
+	if(peercipher == &ccmp)
+		memmove(w, rsnccmpoui, 4);
+	else if(rsne[0] == 0x30)
+		memmove(w, rsntkipoui, 4);
+	else
+		memmove(w, wpatkipoui, 4);
+	w += 4;
+
+	if((e - p) < 6)
+		goto trunc;
+
+	*w++ = 0x01;		/* # of auth suites */
+	*w++ = 0x00;
+	n = *p++;
+	n |= *p++ << 8;
+
+	if(n <= 0)
+		goto trunc;
+
+	for(i=0; i<n; i++){
+		if((e - p) < 4)
+			goto trunc;
+
+		/* look for PSK oui */
+		if(rsne[0] == 0x30){
+			if(memcmp(p, rsnapskoui, 4) == 0)
+				break;
+		} else {
+			if(memcmp(p, wpaapskoui, 4) == 0)
+				break;
+		}
+		p += 4;
+	}
+	if(i >= n){
+		sysfatal("auth suite is not PSK; brsne: %s", buf);
+		return 0;
+	}
+
+	memmove(w, p, 4);
+	w += 4;
+
+	if(rsne[0] == 0x30){
+		/* RSN caps */
+		*w++ = 0x00;
+		*w++ = 0x00;
+	}
+
+	rsne[1] = (w - rsne) - 2;
+	return w - rsne;
+}
+
+int
 getptk(	uchar smac[Eaddrlen], uchar amac[Eaddrlen], 
 	uchar snonce[Noncelen],  uchar anonce[Noncelen], 
 	uchar ptk[PTKlen])
@@ -148,8 +330,10 @@
 		goto out;
 	if((ret = auth_rpc(rpc, "read", nil, 0)) != ARok)
 		goto out;
-	if(rpc->narg != PTKlen)
+	if(rpc->narg != PTKlen){
+		ret = -1;
 		goto out;
+	}
 	memmove(ptk, rpc->arg, PTKlen);
 	ret = 0;
 out:
@@ -294,7 +478,7 @@
 }
 
 void
-reply(uchar smac[Eaddrlen], uchar amac[Eaddrlen], int flags, Keydescr *kd, uchar *data, int datalen)
+reply(int eapver, uchar smac[Eaddrlen], uchar amac[Eaddrlen], int flags, Keydescr *kd, uchar *data, int datalen)
 {
 	uchar buf[4096], *m, *p = buf;
 
@@ -304,7 +488,7 @@
 	*p++ = 0x8e;
 
 	m = p;
-	*p++ = 0x01;
+	*p++ = eapver;
 	*p++ = 0x03;
 	datalen += Keydescrlen;
 	*p++ = datalen >> 8;
@@ -325,7 +509,7 @@
 	if(flags & Fmic)
 		calcmic(kd, m, p - m);
 	if(debug != 0){
-		fprint(2, "\nreply %E -> %E: ", smac, amac);
+		fprint(2, "\nreply(v%d) %E -> %E: ", eapver, smac, amac);
 		dumpkeydescr(kd);
 	}
 	datalen = p - buf;
@@ -353,11 +537,10 @@
 	fmtinstall('H', Hfmt);
 	fmtinstall('E', eipfmt);
 
-	/* default is WPA */
-	rsne = wpaie;
-	rsnelen = sizeof(wpaie);
-	peercipher = &tkip;
-	groupcipher = &tkip;
+	rsne = nil;
+	rsnelen = -1;
+	peercipher = nil;
+	groupcipher = nil;
 
 	ARGBEGIN {
 	case 'd':
@@ -415,6 +598,24 @@
 		free(s);
 	}
 
+	if(rsnelen <= 0){
+		static uchar brsne[258];
+
+		rsne = brsne;
+		rsnelen = buildrsne(rsne);
+	}
+
+	if(rsnelen <= 0){
+		/* default is WPA */
+		rsne = wpaie;
+		rsnelen = sizeof(wpaie);
+		peercipher = &tkip;
+		groupcipher = &tkip;
+	}
+
+	if(debug)
+		fprint(2, "rsne: %.*H\n", rsnelen, rsne);
+
 	/*
 	 * we use write() instead of fprint so message gets  written
 	 * at once and not chunked up on fprint buffer.
@@ -437,12 +638,20 @@
 	
 	for(;;){
 		uchar smac[Eaddrlen], amac[Eaddrlen], snonce[Noncelen], anonce[Noncelen], *p, *e, *m;
-		int proto, flags, vers, datalen;
+		int proto, eapver, flags, vers, datalen;
 		uvlong repc, rsc, tsc;
 		Keydescr *kd;
 
 		if((n = read(fd, buf, sizeof(buf))) < 0)
 			sysfatal("read: %r");
+
+		if(n == 0){
+			if(debug != 0)
+				fprint(2, "got deassociation\n");
+			lastrepc = 0ULL;
+			continue;
+		}
+
 		p = buf;
 		e = buf+n;
 		if(n < 2*Eaddrlen + 2)
@@ -455,18 +664,26 @@
 
 		m = p;
 		n = e - p;
-		if(n < 4 || p[0] != 0x01 || p[1] != 0x03)
+		if(n < 4)
 			continue;
+		eapver = p[0];
+		if((eapver != 0x01 && eapver != 0x02) || p[1] != 0x03)
+			continue;
+
+		if(debug != 0)
+			fprint(2, "\nrecv(v%d) %E <- %E: ", eapver, smac, amac);
+
 		n = p[2]<<8 | p[3];
 		p += 4;
-		if(n < Keydescrlen || p + n > e)
+		if(n < Keydescrlen || p + n > e){
+			if(debug != 0)
+				fprint(2, "bad kd size\n");
 			continue;
+		}
 		e = p + n;
 		kd = (Keydescr*)p;
-		if(debug){
-			fprint(2, "\nrecv %E <- %E: ", smac, amac);
+		if(debug != 0)
 			dumpkeydescr(kd);
-		}
 
 		if(kd->type[0] != 0xFE && kd->type[0] != 0x02)
 			continue;
@@ -483,14 +700,17 @@
 
 			memmove(anonce, kd->nonce, sizeof(anonce));
 			genrandom(snonce, sizeof(snonce));
-			if(getptk(smac, amac, snonce, anonce, ptk) < 0)
+			if(getptk(smac, amac, snonce, anonce, ptk) != 0){
+				if(debug != 0)
+					fprint(2, "getptk: %r\n");
 				continue;
+			}
 
 			/* ack key exchange with mic */
 			memset(kd->rsc, 0, sizeof(kd->rsc));
 			memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
 			memmove(kd->nonce, snonce, sizeof(kd->nonce));
-			reply(smac, amac, (flags & ~(Fack|Fins)) | Fmic, kd, rsne, rsnelen);
+			reply(eapver, smac, amac, (flags & ~(Fack|Fins)) | Fmic, kd, rsne, rsnelen);
 		} else {
 			uchar gtk[GTKlen];
 			int gtklen, gtkkid;
@@ -577,22 +797,19 @@
 					tsc = rsc;
 					rsc = 0LL;
 				}
-				/* install peerwise receive key */
+				/* install pairwise receive key */
 				if(fprint(cfd, "rxkey %.*H %s:%.*H@%llux", Eaddrlen, amac,
 					peercipher->name, peercipher->keylen, ptk+32, tsc) < 0)
 					sysfatal("write rxkey: %r");
 
-				/* pick random 16bit tsc value for transmit */
-				tsc = 1 + (truerand() & 0x7fff);
+				tsc = 0LL;
 				memset(kd->rsc, 0, sizeof(kd->rsc));
-				kd->rsc[0] = tsc;
-				kd->rsc[1] = tsc>>8;
 				memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
 				memset(kd->nonce, 0, sizeof(kd->nonce));
-				reply(smac, amac, flags & ~(Fack|Fenc|Fsec), kd, nil, 0);
+				reply(eapver, smac, amac, flags & ~(Fack|Fenc|Fins), kd, nil, 0);
 				sleep(100);
 
-				/* install peerwise transmit key */ 
+				/* install pairwise transmit key */ 
 				if(fprint(cfd, "txkey %.*H %s:%.*H@%llux", Eaddrlen, amac,
 					peercipher->name, peercipher->keylen, ptk+32, tsc) < 0)
 					sysfatal("write txkey: %r");
@@ -612,7 +829,7 @@
 				memset(kd->rsc, 0, sizeof(kd->rsc));
 				memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
 				memset(kd->nonce, 0, sizeof(kd->nonce));
-				reply(smac, amac, flags & ~(Fenc|Fack), kd, nil, 0);
+				reply(eapver, smac, amac, flags & ~(Fenc|Fack), kd, nil, 0);
 			} else
 				continue;
 
--- a/sys/src/cmd/cfs/cfs.c
+++ b/sys/src/cmd/cfs/cfs.c
@@ -808,8 +808,9 @@
 	char buf[128];
 
 	olen = p->len;
-	p->len = read9pmsg(p->fd[0], datarcv, sizeof(datarcv));
-	if(p->len <= 0){
+	while((p->len = read9pmsg(p->fd[0], datarcv, sizeof(datarcv))) == 0)
+		;
+	if(p->len < 0){
 		snprint(buf, sizeof buf, "read9pmsg(%d)->%ld: %r",
 			p->fd[0], p->len);
 		error(buf);
--- a/sys/src/cmd/cpu.c
+++ b/sys/src/cmd/cpu.c
@@ -1090,11 +1090,13 @@
 	ncpunote = 0;
 	for(;;){
 		n = read9pmsg(fd, buf, sizeof(buf));
-		if(n <= 0){
+		if(n < 0){
 			if(dbg)
 				fprint(2, "read9pmsg(%d) returns %d: %r\n", fd, n);
 			break;
 		}
+		if(n == 0)
+			continue;
 		if(convM2S(buf, n, &f) <= BIT16SZ)
 			break;
 		if(dbg)
--- a/sys/src/cmd/disk/9660/dump9660.c
+++ b/sys/src/cmd/disk/9660/dump9660.c
@@ -307,11 +307,10 @@
 		 * Patch in root directories.
 		 */
 		setroot(cd, cd->iso9660pvd, iroot.block, iroot.length);
-		setvolsize(cd, cd->iso9660pvd, (vlong)cd->nextblock * Blocksize);
+		setvolsize(cd, cd->iso9660pvd, cd->nextblock);
 		if(cd->flags & CDjoliet){
 			setroot(cd, cd->jolietsvd, jroot.block, jroot.length);
-			setvolsize(cd, cd->jolietsvd,
-				(vlong)cd->nextblock * Blocksize);
+			setvolsize(cd, cd->jolietsvd, cd->nextblock);
 		}
 	}else{
 		/*
@@ -341,11 +340,10 @@
 		 * Patch in new root directory entry.
 		 */
 		setroot(cd, cd->iso9660pvd, idumproot.block, idumproot.length);
-		setvolsize(cd, cd->iso9660pvd, (vlong)cd->nextblock * Blocksize);
+		setvolsize(cd, cd->iso9660pvd, cd->nextblock);
 		if(cd->flags & CDjoliet){
 			setroot(cd, cd->jolietsvd, jdumproot.block, jdumproot.length);
-			setvolsize(cd, cd->jolietsvd,
-				(vlong)cd->nextblock * Blocksize);
+			setvolsize(cd, cd->jolietsvd, cd->nextblock);
 		}
 	}
 	writepathtables(cd);	
--- a/sys/src/cmd/exportfs/exportfs.c
+++ b/sys/src/cmd/exportfs/exportfs.c
@@ -388,8 +388,9 @@
 		if(r == 0)
 			fatal("Out of service buffers");
 			
-		n = localread9pmsg(netfd, r->buf, messagesize, ini);
-		if(n <= 0)
+		while((n = localread9pmsg(netfd, r->buf, messagesize, ini)) == 0)
+			;
+		if(n < 0)
 			fatal(nil);
 		if(convM2S(r->buf, n, &r->work) == 0)
 			fatal("convM2S format error");
--- a/sys/src/cmd/hjfs/auth.c
+++ b/sys/src/cmd/hjfs/auth.c
@@ -465,3 +465,4 @@
 		createuserdir(fs, argv[1], uid);
 	return 1;
 }
+
--- a/sys/src/cmd/hjfs/cons.c
+++ b/sys/src/cmd/hjfs/cons.c
@@ -322,6 +322,13 @@
 	return -1;
 }
 
+int
+cmdusers(int, char**)
+{
+	readusers(fsmain);
+	return 0;
+}
+
 extern int cmdnewuser(int, char **);
 
 Cmd cmds[] = {
@@ -334,6 +341,7 @@
 	{"sync", 1, cmdsync},
 	{"halt", 1, cmdhalt},
 	{"newuser", 0, cmdnewuser},
+	{"users", 1, cmdusers},
 	{"echo", 2, cmdecho},
 	{"df", 1, cmddf},
 	{"debug-deind", 2, cmddebugdeind},
--- a/sys/src/cmd/hjfs/fns.h
+++ b/sys/src/cmd/hjfs/fns.h
@@ -53,3 +53,4 @@
 int	ingroup(Fs *, short, short, int);
 void	workerinit(void);
 void	writeusers(Fs *);
+void	readusers(Fs *);
--- a/sys/src/cmd/hjfs/fs1.c
+++ b/sys/src/cmd/hjfs/fs1.c
@@ -214,7 +214,7 @@
 	dprint("writeusers: %r\n");
 }
 
-static void
+void
 readusers(Fs *fs)
 {
 	Chan *ch;
--- a/sys/src/cmd/iostats/iostats.c
+++ b/sys/src/cmd/iostats/iostats.c
@@ -164,7 +164,8 @@
 		if(r == 0)
 			fatal("Out of service buffers");
 
-		n = read9pmsg(p[1], r->buf, sizeof(r->buf));
+		while((n = read9pmsg(p[1], r->buf, sizeof(r->buf))) == 0 && !done)
+			;
 		if(done)
 			break;
 		if(n < 0)
--- a/sys/src/cmd/ip/ftpfs/ftpfs.c
+++ b/sys/src/cmd/ip/ftpfs/ftpfs.c
@@ -262,12 +262,14 @@
 
 	while(!dying){
 		n = read9pmsg(mfd, mdata, messagesize);
-		if(n <= 0){
+		if(n < 0){
 			errstr(buf, sizeof buf);
 			if(buf[0]=='\0' || strstr(buf, "hungup"))
 				exits("");
 			fatal("mount read: %s\n", buf);
 		}
+		if(n == 0)
+			continue;
 		if(convM2S(mdata, n, &thdr) == 0)
 			continue;
 
--- a/sys/src/cmd/jpg/imagefile.h
+++ b/sys/src/cmd/jpg/imagefile.h
@@ -50,9 +50,11 @@
 
 
 Rawimage**	readjpg(int, int);
-Rawimage**	Breadjpg(Biobuf *b, int);
+Rawimage**	Breadjpg(Biobuf*, int);
 Rawimage**	readpng(int, int);
-Rawimage**	Breadpng(Biobuf *b, int);
+Rawimage**	Breadpng(Biobuf*, int);
+Rawimage**	readtif(int);
+Rawimage**	Breadtif(Biobuf*);
 Rawimage**	readgif(int, int);
 Rawimage**	readpixmap(int, int);
 Rawimage*	torgbv(Rawimage*, int);
--- a/sys/src/cmd/jpg/mkfile
+++ b/sys/src/cmd/jpg/mkfile
@@ -9,6 +9,7 @@
 	toppm\
 	png\
 	topng\
+	tif\
 	yuv\
 	ico\
 	toico\
@@ -55,6 +56,7 @@
 $O.toppm:	writeppm.$O multichan.$O toppm.$O
 $O.png:		$IMFILES readpng.$O png.$O
 $O.topng:	writepng.$O topng.$O
+$O.tif:		$IMFILES readtif.$O tif.$O
 $O.yuv:		$IMFILES readyuv.$O yuv.$O
 $O.bmp:		$IMFILES readbmp.$O bmp.$O
 $O.v210:	$IMFILES readv210.$O v210.$O
--- /dev/null
+++ b/sys/src/cmd/jpg/readtif.c
@@ -1,0 +1,1793 @@
+/*
+* code/documentation:
+* http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
+* http://paulbourke.net/dataformats/tiff/
+* http://www.fileformat.info/format/tiff/egff.htm
+* http://www.fileformat.info/mirror/egff/ch09_05.htm
+* http://www.itu.int/rec/T-REC-T.4-199904-S/en
+* http://www.itu.int/rec/T-REC-T.6-198811-I/en
+*
+* fax codes and lzw:
+* http://www.remotesensing.org/libtiff/
+*/
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include "imagefile.h"
+
+enum {
+	II = 0x4949, /* little-endian */
+	MM = 0x4d4d, /* big-endian */
+	TIF = 0x002a /* tiff magic number */
+};
+
+enum {
+	Byte = 1,
+	Short = 3,
+	Long = 4
+};
+
+enum {
+	Width = 0x0100,
+	Length = 0x0101,
+	Bits = 0x0102,
+
+	Compression = 0x0103,
+	Nocomp = 0x0001,
+	Huffman = 0x0002,
+	T4enc = 0x0003,
+	T6enc = 0x0004,
+	Lzwenc = 0x0005,
+	Packbits = 0x8005,
+
+	Photometric = 0x0106,
+	Whitezero = 0x0000,
+	Blackzero = 0x0001,
+	Rgb = 0x0002,
+	Palette = 0x0003,
+
+	Fill = 0x010a,
+	Strips = 0x0111,
+	Orientation = 0x0112,
+	Samples = 0x0115,
+	Rows = 0x0116,
+	Counts = 0x0117,
+	Planar = 0x011c,
+	T4opts = 0x0124,
+	T6opts = 0x0125,
+	Predictor = 0x13d,
+	Color = 0x0140
+};
+
+enum {
+	Nfaxcodes = 10,
+	Nfaxtab = 105
+};
+
+enum {
+	Clrcode = 256,
+	Eoicode = 257,
+	Tabsz = 1<<12
+};
+
+typedef struct Tab Tab;
+typedef struct Fax Fax;
+typedef struct Code Code;
+typedef struct Lzw Lzw;
+typedef struct Fld Fld;
+typedef struct Tif Tif;
+
+struct Tab {
+	int len;
+	int code;
+	int run; /* run length */
+};
+
+struct Fax {
+	ulong n;
+	int m;
+	int st; /* state */
+	Tab *tab[2];
+	int ntab; /* position in tab */
+	Tab *eol;
+	int (*getbit)(Fax *);
+	int *l1;
+	int *l2;
+	ulong nl;
+	uchar *data;
+	ulong next; /* next strip offset in data */
+};
+
+struct Code {
+	uchar val;
+	Code *next;
+};
+
+struct Lzw {
+	Code tab[Tabsz];
+	int ntab;
+	int len; /* code length */
+	ulong n;
+	int m;
+	uchar *data;
+	ulong next; /* next strip offset in data */
+	/* remaining allocated codes */
+	Code *first;
+	Code *last;
+};
+
+struct Fld {
+	uint tag;
+	uint typ;
+	ulong cnt;
+	ulong off; /* value offset */
+	ulong *val;
+	ulong nval;
+};
+
+struct Tif {
+	Biobuf *fd;
+	uint end; /* endianness */
+	uchar tmp[4];
+	uchar *buf;
+	ulong nbuf;
+	int eof; /* reached end of image */
+	ulong n; /* offset in buf array */
+	ulong off;
+	uint nfld;
+	Fld *fld;
+	ulong (*byte2)(uchar *);
+	ulong (*byte4)(uchar *);
+
+	/* field data */
+	ulong dx;
+	ulong dy;
+	ulong depth;
+	ulong comp;
+	uchar *(*uncompress)(Tif *);
+	ulong orientation;
+	ulong photo;
+	int (*decode)(Tif *, Rawimage *, uchar *);
+	ulong fill;
+	ulong *strips;
+	ulong nstrips;
+	ulong samples;
+	ulong rows;
+	ulong *counts;
+	ulong ncounts;
+	ulong planar;
+	ulong *color; /* color map */
+	ulong ncolor;
+	ulong t4;
+	ulong t6;
+	ulong predictor;
+
+	/* image data */
+	uchar *data;
+	ulong ndata;
+};
+
+/*
+* imported from libdraw/arith.c to permit
+* extern log2 function
+*/
+static int log2[] = {
+	-1, 0, 1, -1, 2, -1, -1, -1, 3,
+	-1, -1, -1, -1, -1, -1, -1, 4,
+	-1, -1, -1, -1, -1, -1, -1, 4 /* BUG */,
+	-1, -1, -1, -1, -1, -1, -1, 5
+};
+
+static Tab faxwhite[Nfaxtab] = {
+	{4, 0x7, 2}, /* 0111 */
+	{4, 0x8, 3}, /* 1000 */
+	{4, 0xb, 4}, /* 1011 */
+	{4, 0xc, 5}, /* 1100 */
+	{4, 0xe, 6}, /* 1110 */
+	{4, 0xf, 7}, /* 1111 */
+	{5, 0x12, 128}, /* 1001 0 */
+	{5, 0x13, 8}, /* 1001 1 */
+	{5, 0x14, 9}, /* 1010 0 */
+	{5, 0x1b, 64}, /* 1101 1 */
+	{5, 0x7, 10}, /* 0011 1 */
+	{5, 0x8, 11}, /* 0100 0 */
+	{6, 0x17, 192}, /* 0101 11 */
+	{6, 0x18, 1664}, /* 0110 00 */
+	{6, 0x2a, 16}, /* 1010 10 */
+	{6, 0x2b, 17}, /* 1010 11 */
+	{6, 0x3, 13}, /* 0000 11 */
+	{6, 0x34, 14}, /* 1101 00 */
+	{6, 0x35, 15}, /* 1101 01 */
+	{6, 0x7, 1}, /* 0001 11 */
+	{6, 0x8, 12}, /* 0010 00 */
+	{7, 0x13, 26}, /* 0010 011 */
+	{7, 0x17, 21}, /* 0010 111 */
+	{7, 0x18, 28}, /* 0011 000 */
+	{7, 0x24, 27}, /* 0100 100 */
+	{7, 0x27, 18}, /* 0100 111 */
+	{7, 0x28, 24}, /* 0101 000 */
+	{7, 0x2b, 25}, /* 0101 011 */
+	{7, 0x3, 22}, /* 0000 011 */
+	{7, 0x37, 256}, /* 0110 111 */
+	{7, 0x4, 23}, /* 0000 100 */
+	{7, 0x8, 20}, /* 0001 000 */
+	{7, 0xc, 19}, /* 0001 100 */
+	{8, 0x12, 33}, /* 0001 0010 */
+	{8, 0x13, 34}, /* 0001 0011 */
+	{8, 0x14, 35}, /* 0001 0100 */
+	{8, 0x15, 36}, /* 0001 0101 */
+	{8, 0x16, 37}, /* 0001 0110 */
+	{8, 0x17, 38}, /* 0001 0111 */
+	{8, 0x1a, 31}, /* 0001 1010 */
+	{8, 0x1b, 32}, /* 0001 1011 */
+	{8, 0x2, 29}, /* 0000 0010 */
+	{8, 0x24, 53}, /* 0010 0100 */
+	{8, 0x25, 54}, /* 0010 0101 */
+	{8, 0x28, 39}, /* 0010 1000 */
+	{8, 0x29, 40}, /* 0010 1001 */
+	{8, 0x2a, 41}, /* 0010 1010 */
+	{8, 0x2b, 42}, /* 0010 1011 */
+	{8, 0x2c, 43}, /* 0010 1100 */
+	{8, 0x2d, 44}, /* 0010 1101 */
+	{8, 0x3, 30}, /* 0000 0011 */
+	{8, 0x32, 61}, /* 0011 0010 */
+	{8, 0x33, 62}, /* 0011 0011 */
+	{8, 0x34, 63}, /* 0011 0100 */
+	{8, 0x35, 0}, /* 0011 0101 */
+	{8, 0x36, 320}, /* 0011 0110 */
+	{8, 0x37, 384}, /* 0011 0111 */
+	{8, 0x4, 45}, /* 0000 0100 */
+	{8, 0x4a, 59}, /* 0100 1010 */
+	{8, 0x4b, 60}, /* 0100 1011 */
+	{8, 0x5, 46}, /* 0000 0101 */
+	{8, 0x52, 49}, /* 0101 0010 */
+	{8, 0x53, 50}, /* 0101 0011 */
+	{8, 0x54, 51}, /* 0101 0100 */
+	{8, 0x55, 52}, /* 0101 0101 */
+	{8, 0x58, 55}, /* 0101 1000 */
+	{8, 0x59, 56}, /* 0101 1001 */
+	{8, 0x5a, 57}, /* 0101 1010 */
+	{8, 0x5b, 58}, /* 0101 1011 */
+	{8, 0x64, 448}, /* 0110 0100 */
+	{8, 0x65, 512}, /* 0110 0101 */
+	{8, 0x67, 640}, /* 0110 0111 */
+	{8, 0x68, 576}, /* 0110 1000 */
+	{8, 0xa, 47}, /* 0000 1010 */
+	{8, 0xb, 48}, /* 0000 1011 */
+	{9, 0x98, 1472}, /* 0100 1100 0 */
+	{9, 0x99, 1536}, /* 0100 1100 1 */
+	{9, 0x9a, 1600}, /* 0100 1101 0 */
+	{9, 0x9b, 1728}, /* 0100 1101 1 */
+	{9, 0xcc, 704}, /* 0110 0110 0 */
+	{9, 0xcd, 768}, /* 0110 0110 1 */
+	{9, 0xd2, 832}, /* 0110 1001 0 */
+	{9, 0xd3, 896}, /* 0110 1001 1 */
+	{9, 0xd4, 960}, /* 0110 1010 0 */
+	{9, 0xd5, 1024}, /* 0110 1010 1 */
+	{9, 0xd6, 1088}, /* 0110 1011 0 */
+	{9, 0xd7, 1152}, /* 0110 1011 1 */
+	{9, 0xd8, 1216}, /* 0110 1100 0 */
+	{9, 0xd9, 1280}, /* 0110 1100 1 */
+	{9, 0xda, 1344}, /* 0110 1101 0 */
+	{9, 0xdb, 1408}, /* 0110 1101 1 */
+	{11, 0x8, 1792}, /* 0000 0001 000 */
+	{11, 0xc, 1856}, /* 0000 0001 100 */
+	{11, 0xd, 1920}, /* 0000 0001 101 */
+	{12, 0x1, -1}, /* 0000 0000 0001 */
+	{12, 0x12, 1984}, /* 0000 0001 0010 */
+	{12, 0x13, 2048}, /* 0000 0001 0011 */
+	{12, 0x14, 2112}, /* 0000 0001 0100 */
+	{12, 0x15, 2176}, /* 0000 0001 0101 */
+	{12, 0x16, 2240}, /* 0000 0001 0110 */
+	{12, 0x17, 2304}, /* 0000 0001 0111 */
+	{12, 0x1c, 2368}, /* 0000 0001 1100 */
+	{12, 0x1d, 2432}, /* 0000 0001 1101 */
+	{12, 0x1e, 2496}, /* 0000 0001 1110 */
+	{12, 0x1f, 2560} /* 0000 0001 1111 */
+};
+
+static Tab faxblack[Nfaxtab] = {
+	{2, 0x2, 3}, /* 10 */
+	{2, 0x3, 2}, /* 11 */
+	{3, 0x2, 1}, /* 010 */
+	{3, 0x3, 4}, /* 011 */
+	{4, 0x2, 6}, /* 0010 */
+	{4, 0x3, 5}, /* 0011 */
+	{5, 0x3, 7}, /* 0001 1 */
+	{6, 0x4, 9}, /* 0001 00 */
+	{6, 0x5, 8}, /* 0001 01 */
+	{7, 0x4, 10}, /* 0000 100 */
+	{7, 0x5, 11}, /* 0000 101 */
+	{7, 0x7, 12}, /* 0000 111 */
+	{8, 0x4, 13}, /* 0000 0100 */
+	{8, 0x7, 14}, /* 0000 0111 */
+	{9, 0x18, 15}, /* 0000 1100 0 */
+	{10, 0x17, 16}, /* 0000 0101 11 */
+	{10, 0x18, 17}, /* 0000 0110 00 */
+	{10, 0x37, 0}, /* 0000 1101 11 */
+	{10, 0x8, 18}, /* 0000 0010 00 */
+	{10, 0xf, 64}, /* 0000 0011 11 */
+	{11, 0x17, 24}, /* 0000 0010 111 */
+	{11, 0x18, 25}, /* 0000 0011 000 */
+	{11, 0x28, 23}, /* 0000 0101 000 */
+	{11, 0x37, 22}, /* 0000 0110 111 */
+	{11, 0x67, 19}, /* 0000 1100 111 */
+	{11, 0x68, 20}, /* 0000 1101 000 */
+	{11, 0x6c, 21}, /* 0000 1101 100 */
+	{11, 0x8, 1792}, /* 0000 0001 000 */
+	{11, 0xc, 1856}, /* 0000 0001 100 */
+	{11, 0xd, 1920}, /* 0000 0001 101 */
+	{12, 0x1, -1}, /* 0000 0000 0001 */
+	{12, 0x12, 1984}, /* 0000 0001 0010 */
+	{12, 0x13, 2048}, /* 0000 0001 0011 */
+	{12, 0x14, 2112}, /* 0000 0001 0100 */
+	{12, 0x15, 2176}, /* 0000 0001 0101 */
+	{12, 0x16, 2240}, /* 0000 0001 0110 */
+	{12, 0x17, 2304}, /* 0000 0001 0111 */
+	{12, 0x1c, 2368}, /* 0000 0001 1100 */
+	{12, 0x1d, 2432}, /* 0000 0001 1101 */
+	{12, 0x1e, 2496}, /* 0000 0001 1110 */
+	{12, 0x1f, 2560}, /* 0000 0001 1111 */
+	{12, 0x24, 52}, /* 0000 0010 0100 */
+	{12, 0x27, 55}, /* 0000 0010 0111 */
+	{12, 0x28, 56}, /* 0000 0010 1000 */
+	{12, 0x2b, 59}, /* 0000 0010 1011 */
+	{12, 0x2c, 60}, /* 0000 0010 1100 */
+	{12, 0x33, 320}, /* 0000 0011 0011 */
+	{12, 0x34, 384}, /* 0000 0011 0100 */
+	{12, 0x35, 448}, /* 0000 0011 0101 */
+	{12, 0x37, 53}, /* 0000 0011 0111 */
+	{12, 0x38, 54}, /* 0000 0011 1000 */
+	{12, 0x52, 50}, /* 0000 0101 0010 */
+	{12, 0x53, 51}, /* 0000 0101 0011 */
+	{12, 0x54, 44}, /* 0000 0101 0100 */
+	{12, 0x55, 45}, /* 0000 0101 0101 */
+	{12, 0x56, 46}, /* 0000 0101 0110 */
+	{12, 0x57, 47}, /* 0000 0101 0111 */
+	{12, 0x58, 57}, /* 0000 0101 1000 */
+	{12, 0x59, 58}, /* 0000 0101 1001 */
+	{12, 0x5a, 61}, /* 0000 0101 1010 */
+	{12, 0x5b, 256}, /* 0000 0101 1011 */
+	{12, 0x64, 48}, /* 0000 0110 0100 */
+	{12, 0x65, 49}, /* 0000 0110 0101 */
+	{12, 0x66, 62}, /* 0000 0110 0110 */
+	{12, 0x67, 63}, /* 0000 0110 0111 */
+	{12, 0x68, 30}, /* 0000 0110 1000 */
+	{12, 0x69, 31}, /* 0000 0110 1001 */
+	{12, 0x6a, 32}, /* 0000 0110 1010 */
+	{12, 0x6b, 33}, /* 0000 0110 1011 */
+	{12, 0x6c, 40}, /* 0000 0110 1100 */
+	{12, 0x6d, 41}, /* 0000 0110 1101 */
+	{12, 0xc8, 128}, /* 0000 1100 1000 */
+	{12, 0xc9, 192}, /* 0000 1100 1001 */
+	{12, 0xca, 26}, /* 0000 1100 1010 */
+	{12, 0xcb, 27}, /* 0000 1100 1011 */
+	{12, 0xcc, 28}, /* 0000 1100 1100 */
+	{12, 0xcd, 29}, /* 0000 1100 1101 */
+	{12, 0xd2, 34}, /* 0000 1101 0010 */
+	{12, 0xd3, 35}, /* 0000 1101 0011 */
+	{12, 0xd4, 36}, /* 0000 1101 0100 */
+	{12, 0xd5, 37}, /* 0000 1101 0101 */
+	{12, 0xd6, 38}, /* 0000 1101 0110 */
+	{12, 0xd7, 39}, /* 0000 1101 0111 */
+	{12, 0xda, 42}, /* 0000 1101 1010 */
+	{12, 0xdb, 43}, /* 0000 1101 1011 */
+	{13, 0x4a, 640}, /* 0000 0010 0101 0 */
+	{13, 0x4b, 704}, /* 0000 0010 0101 1 */
+	{13, 0x4c, 768}, /* 0000 0010 0110 0 */
+	{13, 0x4d, 832}, /* 0000 0010 0110 1 */
+	{13, 0x52, 1280}, /* 0000 0010 1001 0 */
+	{13, 0x53, 1344}, /* 0000 0010 1001 1 */
+	{13, 0x54, 1408}, /* 0000 0010 1010 0 */
+	{13, 0x55, 1472}, /* 0000 0010 1010 1 */
+	{13, 0x5a, 1536}, /* 0000 0010 1101 0 */
+	{13, 0x5b, 1600}, /* 0000 0010 1101 1 */
+	{13, 0x64, 1664}, /* 0000 0011 0010 0 */
+	{13, 0x65, 1728}, /* 0000 0011 0010 1 */
+	{13, 0x6c, 512}, /* 0000 0011 0110 0 */
+	{13, 0x6d, 576}, /* 0000 0011 0110 1 */
+	{13, 0x72, 896}, /* 0000 0011 1001 0 */
+	{13, 0x73, 960}, /* 0000 0011 1001 1 */
+	{13, 0x74, 1024}, /* 0000 0011 1010 0 */
+	{13, 0x75, 1088}, /* 0000 0011 1010 1 */
+	{13, 0x76, 1152}, /* 0000 0011 1011 0 */
+	{13, 0x77, 1216} /* 0000 0011 1011 1 */
+};
+
+static Tab faxcodes[Nfaxcodes] = {
+	{1, 0x1, 0}, /* 1 */
+	{3, 0x1, 0}, /* 001 */
+	{3, 0x2, 0}, /* 010 */
+	{3, 0x3, 0}, /* 011 */
+	{4, 0x1, 0}, /* 0001 */
+	{6, 0x2, 0}, /* 0000 10 */
+	{6, 0x3, 0}, /* 0000 11 */
+	{7, 0x2, 0}, /* 0000 010 */
+	{7, 0x3, 0}, /* 0000 011 */
+	{12, 0x1, -1} /* 0000 0000 0001 */
+};
+
+static int typesizes[] = {0, 1, 0, 2, 4};
+static int vcodeval[] = {0, 0, 0, 1, 0, 0, 2, 3};
+
+static ulong byte2le(uchar *);
+static ulong byte4le(uchar *);
+static ulong byte2be(uchar *);
+static ulong byte4be(uchar *);
+static void readdata(Tif *, ulong);
+static void readnbytes(Tif *, ulong);
+static ulong readbyte(Tif *);
+static ulong readshort(Tif *);
+static ulong readlong(Tif *);
+static int gototif(Tif *, ulong);
+static int readheader(Tif *);
+static uchar *nocomp(Tif *);
+static int getbit1(Fax *);
+static int getbit2(Fax *);
+static Tab *findtab(Fax *, int, int, Tab *, int);
+static Tab *gettab(Fax *, int);
+static Tab *geteol(Fax *);
+static int faxfill(Fax *, uchar *, ulong, ulong *, ulong *, ulong, int);
+static void fillbits(Fax *);
+static int faxalloclines(Fax *);
+static Tab *getfax1d(Fax *, uchar *, ulong, ulong *, ulong *, ulong);
+static Tab *getfax2d(Fax *, uchar *, ulong, ulong *, ulong *, ulong);
+static int faxstrip(Tif *, Fax *, uchar *, ulong, ulong *);
+static uchar *fax(Tif *);
+static void tabinit(Lzw *);
+static Code *newcode(Lzw *, Code *);
+static void listadd(Lzw *, Code *);
+static Code *tabadd(Lzw *, Code *, Code *);
+static int getcode(Lzw *);
+static int wstr(uchar *, ulong, ulong *, Code *, long *);
+static void predict(Tif *, uchar *);
+static int lzwstrip(Lzw *, uchar *, ulong, ulong *, long);
+static uchar *lzw(Tif *);
+static uchar *packbits(Tif *);
+static int faxdecode(Tif *, Rawimage *, uchar *);
+static int greydecode(Tif *, Rawimage *, uchar *);
+static int rgbdecode(Tif *, Rawimage *, uchar *);
+static int paldecode(Tif *, Rawimage *, uchar *);
+static int parsefield(Tif *, Fld *);
+static int readfield(Tif *, Fld *);
+static int checkfields(Tif *);
+static int readstrips(Tif *);
+static Rawimage *decode(Tif *);
+static void freefields(Tif *);
+static Rawimage *readslave(Tif *);
+
+static ulong
+byte2le(uchar *buf)
+{
+	return (buf[1] << 8) | buf[0];
+}
+
+static ulong
+byte4le(uchar *buf)
+{
+	return (byte2le(buf+2) << 16) | byte2le(buf);
+}
+
+static ulong
+byte2be(uchar *buf)
+{
+	return (buf[0] << 8) | buf[1];
+}
+
+static ulong
+byte4be(uchar *buf)
+{
+	return (byte2be(buf) << 16) | byte2be(buf+2);
+}
+
+static void
+readdata(Tif *t, ulong offset)
+{
+	long n, m;
+	ulong size;
+
+	if(offset < t->nbuf)
+		offset = t->nbuf;
+	m = offset + 4096 - t->nbuf;
+	size = (m + t->nbuf) * sizeof *t->buf;
+	if(t->buf == nil) {
+		if((t->buf = malloc(size)) == nil)
+			sysfatal("malloc: %r");
+	} else {
+		if((t->buf = realloc(t->buf, size)) == nil)
+			sysfatal("realloc: %r");
+	}
+	if((n = Bread(t->fd, t->buf+t->nbuf, m)) < 0)
+		sysfatal("Bread: %r");
+	if(n != m)
+		t->eof = 1;
+	t->nbuf += n;
+}
+
+static void
+readnbytes(Tif *t, ulong n)
+{
+	if(n <= 0 || n > 4)
+		sysfatal("cannot read %lud bytes", n);
+	if(t->n+n > t->nbuf) {
+		if(t->eof)
+			sysfatal("reached end of file");
+		readdata(t, 0);
+	}
+	memmove(t->tmp, t->buf+t->n, n);
+	t->n += n;
+}
+
+static ulong
+readbyte(Tif *t)
+{
+	readnbytes(t, 1);
+	return t->tmp[0];
+}
+
+static ulong
+readshort(Tif *t)
+{
+	readnbytes(t, 2);
+	return (*t->byte2)(t->tmp);
+}
+
+static ulong
+readlong(Tif *t)
+{
+	readnbytes(t, 4);
+	return (*t->byte4)(t->tmp);
+}
+
+static int
+gototif(Tif *t, ulong n)
+{
+	if(n < 8) {
+		werrstr("offset pointing to header");
+		return -1;
+	}
+	if(n > t->nbuf)
+		readdata(t, n);
+	t->n = n;
+	return 0;
+}
+
+static int
+readheader(Tif *t)
+{
+	uint n;
+
+	t->end = readshort(t);
+	switch(t->end) {
+	case II:
+		break;
+	case MM:
+		t->byte2 = byte2be;
+		t->byte4 = byte4be;
+		break;
+	default:
+		werrstr("illegal byte order: %#.4x", t->end);
+		return -1;
+	}
+	if((n = readshort(t)) != TIF) {
+		werrstr("illegal tiff magic: %#.4x", n);
+		return -1;
+	}
+	t->off = readlong(t);
+	return gototif(t, t->off);
+}
+
+static uchar *
+nocomp(Tif *t)
+{
+	return t->data;
+}
+
+static int
+getbit1(Fax *f)
+{
+	int bit;
+
+	if(f->n >= f->next)
+		return -1;
+	bit = (f->data[f->n] >> f->m) & 0x1;
+	f->m--;
+	if(f->m < 0) {
+		f->n++;
+		f->m = 7;
+	}
+	return bit;
+}
+
+static int
+getbit2(Fax *f)
+{
+	int bit;
+
+	if(f->n >= f->next)
+		return -1;
+	bit = (f->data[f->n] >> f->m) & 0x1;
+	f->m++;
+	if(f->m >= 8) {
+		f->n++;
+		f->m = 0;
+	}
+	return bit;
+}
+
+static Tab *
+findtab(Fax *f, int code, int len, Tab *tab, int max)
+{
+	Tab *p;
+
+	while(f->ntab < max) {
+		p = &tab[f->ntab];
+		if(p->len > len)
+			break;
+		if(p->code == code) {
+			f->ntab = 0;
+			return p;
+		}
+		f->ntab++;
+	}
+	return nil;
+}
+
+static Tab *
+gettab(Fax *f, int mode)
+{
+	int i, n, maxlen, bit, code;
+	Tab *p, *tab;
+
+	code = 0;
+	if(mode) {
+		n = Nfaxcodes;
+		tab = faxcodes;
+	} else {
+		n = Nfaxtab;
+		tab = f->tab[f->st];
+	}
+	maxlen = tab[n-1].len;
+	for(i = 1; i <= maxlen; i++) {
+		if((bit = (*f->getbit)(f)) < 0) {
+			f->st = -1;
+			return nil;
+		}
+		code = (code << 1) | bit;
+		if((p = findtab(f, code, i, tab, n)) != nil)
+			return p;
+	}
+	werrstr("code not found");
+	return nil;
+}
+
+static Tab *
+geteol(Fax *f)
+{
+	int i, bit;
+	Tab *p;
+
+	if(f->eol == nil) {
+		if((p = gettab(f, 0)) == nil || p->run >= 0) {
+			werrstr("first eol");
+			return nil;
+		}
+		f->eol = p;
+		return p;
+	}
+	for(i = 0; (bit = (*f->getbit)(f)) == 0; i++)
+		;
+	if(bit < 0) {
+		f->st = -1;
+		return nil;
+	}
+	if(i < 11) {
+		werrstr("eol");
+		return nil;
+	}
+	return f->eol;
+}
+
+static int
+faxfill(Fax *f, uchar *data, ulong size, ulong *i, ulong *x, ulong dx,
+	int n)
+{
+	if((*x += n) > dx) {
+		werrstr("fax row overflow");
+		return -1;
+	}
+	if((*i += n) >= size) {
+		werrstr("fax data overflow");
+		return -1;
+	}
+	if(f->st != 0)
+		memset(data+*i-n, f->st, n);
+	return 0;
+}
+
+static void
+fillbits(Fax *f)
+{
+	if(f->getbit == getbit1) {
+		if(f->m != 7) {
+			f->n++;
+			f->m = 7;
+		}
+	} else {
+		if(f->m != 0) {
+			f->n++;
+			f->m = 0;
+		}
+	}
+}
+
+static int
+faxalloclines(Fax *f)
+{
+	f->nl *= 2;
+	f->l1 = realloc(f->l1, f->nl*sizeof *f->l1);
+	if(f->l1 == nil)
+		return -1;
+	f->l2 = realloc(f->l2, f->nl*sizeof *f->l2);
+	if(f->l2 == nil) {
+		free(f->l1);
+		return -1;
+	}
+	return 0;
+}
+
+static Tab *
+getfax1d(Fax *f, uchar *data, ulong size, ulong *i, ulong *x,
+	ulong dx)
+{
+	int j, n;
+	Tab *p;
+
+	for(j = 0; *x < dx;) {
+		if((p = gettab(f, 0)) == nil)
+			return nil;
+		if((n = p->run) < 0) {
+			f->l1[j] = dx;
+			return f->eol;
+		}
+		if(faxfill(f, data, size, i, x, dx, n) < 0)
+			return nil;
+		if(n < 64) {
+			f->l1[j++] = *x;
+			f->st ^= 1;
+		}
+		if(j >= f->nl)
+			faxalloclines(f);
+	}
+	return nil;
+}
+
+static Tab *
+getfax2d(Fax *f, uchar *data, ulong size, ulong *i, ulong *x,
+	ulong dx)
+{
+	int j, k, n, code, len, a0, a1, b1, b2, v;
+	Tab *p;
+
+	a0 = -1;
+	for(j = 0; *x < dx;) {
+		for(k = 0;; k++) {
+			b1 = f->l1[k];
+			if(b1 > a0 && f->st == k%2)
+				break;
+			if(b1 >= dx)
+				break;
+		}
+		if((b2 = b1) < dx)
+			b2 = f->l1[k+1];
+		if((p = gettab(f, 1)) == nil)
+			return nil;
+		/* early eofb */
+		if(p->run < 0) {
+			f->st = -1;
+			return nil;
+		}
+		len = p->len;
+		code = p->code;
+		if(code == 1 && len == 3) {
+			/* horizontal */
+			for(k = 0; k < 2;) {
+				if((p = gettab(f, 0)) == nil)
+					return nil;
+				n = p->run;
+				if(faxfill(f, data, size, i, x,
+					dx, n) < 0)
+					return nil;
+				if(n < 64) {
+					f->l2[j++] = *x;
+					f->st ^= 1;
+					k++;
+				}
+			}
+		} else if(code == 1 && len == 4) {
+			/* pass */
+			n = b2 - *x;
+			if(faxfill(f, data, size, i, x, dx, n) < 0)
+				return nil;
+			if(*x == dx)
+				f->l2[j++] = *x;
+		} else {
+			/* vertical */
+			switch(code) {
+			case 1:
+			case 2:
+				v = -vcodeval[len];
+				break;
+			case 3:
+				v = vcodeval[len];
+				break;
+			default:
+				werrstr("mode");
+				return nil;
+			}
+			a1 = b1 + v;
+			n = a1 - *x;
+			if(faxfill(f, data, size, i, x, dx, n) < 0)
+				return nil;
+			f->l2[j++] = *x;
+			f->st ^= 1;
+		}
+		if(j >= f->nl)
+			faxalloclines(f);
+		a0 = *x;
+	}
+	memmove(f->l1, f->l2, j*sizeof *f->l1);
+	return nil;
+}
+
+static int
+faxstrip(Tif *t, Fax *f, uchar *data, ulong size, ulong *i)
+{
+	int d1;
+	ulong x, y;
+	Tab *p;
+
+	d1 = t->comp != T6enc;
+	p = nil;
+	for(x = y = 0; x < t->dx || y < t->rows;) {
+		f->st = 0;
+		if(t->comp == T4enc) {
+			if(p == nil && geteol(f) == nil) {
+				if(f->st >= 0)
+					return -1;
+				break;
+			}
+			if(y > 0)
+				*i += t->dx - x;
+			if(t->t4 & 1) {
+				d1 = (*f->getbit)(f);
+				if(d1 < 0)
+					break;
+			}
+		}
+		x = 0;
+		y++;
+		if(d1) {
+			p = getfax1d(f, data, size, i,
+				&x, t->dx);
+		} else {
+			p = getfax2d(f, data, size, i,
+				&x, t->dx);
+		}
+		if(t->comp == Huffman)
+			fillbits(f);
+		if(p == nil && x != t->dx) {
+			if(f->st >= 0 || x > t->dx)
+				return -1;
+			break;
+		}
+	}
+	return 0;
+}
+
+/*
+* the t4 fax test images i decoded did not follow the
+* spec. in particular, they did not have rtcs.
+*/
+static uchar *
+fax(Tif *t)
+{
+	int m;
+	ulong i, j, datasz, linesz;
+	uchar *data;
+	Fax f;
+
+	datasz = t->dx * t->dy * sizeof *data;
+	data = malloc(datasz);
+	f.nl = t->dx;
+	linesz = f.nl * sizeof *f.l1;
+	f.l1 = malloc(linesz);
+	f.l2 = malloc(linesz);
+	if(data == nil || f.l1 == nil || f.l2 == nil) {
+		free(t->data);
+		if(data != nil)
+			free(data);
+		if(f.l1 != nil)
+			free(f.l1);
+		if(f.l2 != nil)
+			free(f.l2);
+		return nil;
+	}
+	memset(data, 0, datasz);
+	memset(f.l1, 0, linesz);
+	memset(f.l2, 0, linesz);
+	if(t->fill == 1) {
+		f.getbit = getbit1;
+		m = 7;
+	} else {
+		f.getbit = getbit2;
+		m = 0;
+	}
+	f.tab[0] = faxwhite;
+	f.tab[1] = faxblack;
+	f.ntab = 0;
+	f.eol = nil;
+	f.data = t->data;
+	for(i = j = 0; i < t->nstrips; i++) {
+		f.l1[0] = t->dx;
+		f.n = t->strips[i];
+		f.m = m;
+		if(i < t->nstrips-1)
+			f.next = t->strips[i+1];
+		else
+			f.next = t->ndata;
+		if(faxstrip(t, &f, data, datasz, &j) < 0)
+			break;
+	}
+	if(i < t->nstrips) {
+		free(data);
+		data = nil;
+	}
+	free(t->data);
+	free(f.l1);
+	free(f.l2);
+	return data;
+}
+
+static void
+tabinit(Lzw *l)
+{
+	l->ntab = Eoicode + 1;
+	l->len = 9;
+}
+
+static Code *
+newcode(Lzw *l, Code *p)
+{
+	Code *q;
+
+	if(p == nil)
+		return nil;
+	if(l->first != nil) {
+		q = l->first;
+		if((l->first = l->first->next) == nil)
+			l->last = nil;
+	} else if((q = malloc(sizeof *q)) == nil)
+		return nil;
+	q->val = p->val;
+	q->next = nil;
+	return q;
+}
+
+static void
+listadd(Lzw *l, Code *p)
+{
+	if(p == nil)
+		return;
+	if(l->last != nil)
+		l->last->next = p;
+	else
+		l->first = l->last = p;
+	while(l->last->next != nil)
+		l->last = l->last->next;
+}
+
+static Code *
+tabadd(Lzw *l, Code *p, Code *q)
+{
+	Code *r, *s;
+
+	if(l->ntab >= Tabsz) {
+		werrstr("lzw table full");
+		return nil;
+	}
+	r = s = &l->tab[l->ntab++];
+	switch(l->ntab) {
+	case 511:
+	case 1023:
+	case 2047:
+		l->len++;
+		break;
+	default:
+		break;
+	}
+	s->val = p->val;
+	while((p = p->next) != nil) {
+		if(s->next != nil)
+			s->next->val = p->val;
+		else if((s->next = newcode(l, p)) == nil)
+			return nil;
+		s = s->next;
+	}
+	if(s->next != nil) {
+		s->next->val = q->val;
+		s = s->next;
+		if(s->next != nil) {
+			listadd(l, s->next);
+			s->next = nil;
+		}
+	} else if((s->next = newcode(l, q)) == nil)
+		return nil;
+	return r;
+}
+
+static int
+getcode(Lzw *l)
+{
+	int i, c, code;
+
+	if(l->n >= l->next) {
+		werrstr("lzw eof");
+		return -1;
+	}
+	code = 0;
+	for(i = l->len-1; i >= 0; i--) {
+		c = (l->data[l->n] >> l->m) & 0x1;
+		code |= c << i;
+		l->m--;
+		if(l->m < 0) {
+			l->n++;
+			l->m = 7;
+		}
+	}
+	return code;
+}
+
+static int
+wstr(uchar *data, ulong size, ulong *i, Code *p, long *striplen)
+{
+	for(; p != nil; p = p->next, ++*i, --*striplen) {
+		if(*i >= size || *striplen < 0) {
+			werrstr("lzw overflow");
+			return -1;
+		}
+		data[*i] = p->val;
+	}
+	return 0;
+}
+
+static void
+predict(Tif *t, uchar *data)
+{
+	char a, b;
+	ulong y, x, i, j, k, s;
+
+	s = t->samples;
+	for(y = 0; y < t->dy; y++) {
+		for(x = 1; x < t->dx; x++) {
+			i = y*t->dx + x;
+			for(j = 0; j < s; j++) {
+				k = i*s + j;
+				a = (char)data[k];
+				b = (char)data[k-s];
+				data[k] = (uchar)(a + b);
+			}
+		}
+	}
+}
+
+static int
+lzwstrip(Lzw *l, uchar *data, ulong size, ulong *i, long striplen)
+{
+	int c, oc;
+	Code *p, *q;
+
+	if((c = getcode(l)) != Clrcode) {
+		werrstr("clear code");
+		return -1;
+	}
+	for(oc = -1; c != Eoicode;) {
+		if(c < 0)
+			return -1;
+		if(c == Clrcode) {
+			if(oc >= 0)
+				tabinit(l);
+			if((c = getcode(l)) == Eoicode)
+				break;
+			if(c < 0)
+				return -1;
+			if(wstr(data, size, i, &l->tab[c],
+				&striplen) < 0)
+				return -1;
+		} else if(c < l->ntab) {
+			p = &l->tab[c];
+			if(wstr(data, size, i, p,
+				&striplen) < 0)
+				return -1;
+			q = &l->tab[oc];
+			if(tabadd(l, q, p) == nil)
+				return -1;
+		} else {
+			q = &l->tab[oc];
+			if((p = tabadd(l, q, q)) == nil)
+				return -1;
+			if(wstr(data, size, i, p,
+				&striplen) < 0)
+				return -1;
+		}
+		if(striplen <= 0)
+			break;
+		oc = c;
+		c = getcode(l);
+	}
+	return 0;
+}
+
+static uchar *
+lzw(Tif *t)
+{
+	ulong i, j, size;
+	long striplen;
+	uchar *data;
+	Lzw l;
+	Code *p, *q;
+
+	i = t->dx * t->rows * t->depth;
+	striplen = i%8 == 0? i/8: i/8+1;
+	size = t->nstrips * striplen * sizeof *data;
+	if((data = malloc(size)) == nil) {
+		free(t->data);
+		return nil;
+	}
+	for(i = 0; i < Tabsz; i++) {
+		l.tab[i].val = i;
+		l.tab[i].next = nil;
+	}
+	l.data = t->data;
+	l.first = l.last = nil;
+	for(i = j = 0; i < t->nstrips; i++) {
+		tabinit(&l);
+		l.n = t->strips[i];
+		l.m = 7;
+		if(i < t->nstrips-1)
+			l.next = t->strips[i+1];
+		else
+			l.next = t->ndata;
+		if(lzwstrip(&l, data, size, &j, striplen) < 0)
+			break;
+	}
+	if(i < t->nstrips) {
+		free(data);
+		data = nil;
+	}
+	for(i = 0; i < Tabsz; i++) {
+		for(p = l.tab[i].next; (q = p) != nil;) {
+			p = p->next;
+			free(q);
+		}
+	}
+	for(p = l.first; (q = p) != nil;) {
+		p = p->next;
+		free(q);
+	}
+	free(t->data);
+	if(data != nil && t->predictor == 2)
+		predict(t, data);
+	return data;
+}
+
+static uchar *
+packbits(Tif *t)
+{
+	char n;
+	ulong i, j, k, size;
+	uchar *data;
+
+	size = t->dx * t->dy * t->samples * sizeof *data;
+	if((data = malloc(size)) == nil) {
+		free(t->data);
+		return nil;
+	}
+	for(i = 0, j = 0; i < t->ndata;) {
+		n = (char)t->data[i++];
+		if(n >= 0) {
+			k = n + 1;
+			if(j+k >= size || i+k >= t->ndata)
+				break;
+			memmove(data+j, t->data+i, k);
+			i += k;
+			j += k;
+		} else if(n > -128 && n < 0) {
+			k = j - n + 1;
+			if(k > size || i >= t->ndata)
+				break;
+			for(; j < k; j++)
+				data[j] = t->data[i];
+			i++;
+		}
+	}
+	if(i < t->ndata) {
+		werrstr("packbits overflow");
+		free(data);
+		data = nil;
+	}
+	free(t->data);
+	return data;
+}
+
+static int
+faxdecode(Tif *t, Rawimage *im, uchar *data)
+{
+	ulong n;
+
+	for(n = 0; n < im->chanlen; n++) {
+		if(t->photo == Whitezero)
+			data[n] ^= 1;
+		im->chans[0][n] = data[n] * 0xff;
+	}
+	return 0;
+}
+
+static int
+greydecode(Tif *t, Rawimage *im, uchar *data)
+{
+	int pix, pmask, xmask;
+	ulong i, n, x, y;
+
+	pmask = (1 << t->depth) - 1;
+	xmask = 7 >> log2[t->depth];
+	for(y = 0, n = 0; y < t->dy; y++) {
+		i = y * bytesperline(im->r, t->depth);
+		for(x = 0; x < t->dx; x++, n++) {
+			if(n >= im->chanlen) {
+				werrstr("grey overflow");
+				return -1;
+			}
+			pix = (data[i] >> t->depth*((xmask -
+				x) & xmask)) & pmask;
+			if(((x + 1) & xmask) == 0)
+				i++;
+			if(t->photo == Whitezero)
+				pix ^= pmask;
+			pix = (pix * 0xff) / pmask;
+			im->chans[0][n] = pix;
+		}
+	}
+	return 0;
+}
+
+static int
+rgbdecode(Tif *t, Rawimage *im, uchar *data)
+{
+	ulong i, n, x, y;
+
+	for(y = 0, n = 0; y < t->dy; y++) {
+		for(x = 0; x < t->dx; x++, n += 3) {
+			if(n >= im->chanlen) {
+				werrstr("rgb overflow");
+				return -1;
+			}
+			i = (y*t->dx + x) * 3;
+			im->chans[0][n] = data[i+2];
+			im->chans[0][n+1] = data[i+1];
+			im->chans[0][n+2] = data[i];
+		}
+	}
+	return 0;
+}
+
+static int
+paldecode(Tif *t, Rawimage *im, uchar *data)
+{
+	int pix, pmask, xmask;
+	ulong i, n, x, y, *r, *g, *b, max;
+
+	pmask = (1 << t->depth) - 1;
+	xmask = 7 >> log2[t->depth];
+	for(i = 0, max = 1; i < t->ncolor; i++) {
+		if(t->color[i] > max)
+			max = t->color[i];
+	}
+	for(i = 0; i < t->ncolor; i++)
+		t->color[i] = (t->color[i] * 0xff) / max;
+	r = t->color;
+	g = r + pmask + 1;
+	b = g + pmask + 1;
+	for(y = 0, n = 0; y < t->dy; y++) {
+		i = y * bytesperline(im->r, t->depth);
+		for(x = 0; x < t->dx; x++, n += 3) {
+			if(n >= im->chanlen) {
+				werrstr("palette overflow");
+				return -1;
+			}
+			pix = (data[i] >> t->depth*((xmask -
+				x) & xmask)) & pmask;
+			if(((x + 1) & xmask) == 0)
+				i++;
+			im->chans[0][n] = b[pix];
+			im->chans[0][n+1] = g[pix];
+			im->chans[0][n+2] = r[pix];
+		}
+	}
+	return 0;
+}
+
+static int
+parsefield(Tif *t, Fld *f)
+{
+	ulong v;
+
+	v = f->val[0];
+	switch(f->tag) {
+	case Width:
+		t->dx = v;
+		break;
+	case Length:
+		t->dy = v;
+		break;
+	case Bits:
+		t->depth = v;
+		if(f->cnt == 3)
+			t->depth += f->val[1] + f->val[2];
+		break;
+	case Compression:
+		t->comp = v;
+		break;
+	case Photometric:
+		t->photo = v;
+		switch(t->photo) {
+		case Whitezero:
+		case Blackzero:
+			t->decode = greydecode;
+			break;
+		case Rgb:
+			t->decode = rgbdecode;
+			break;
+		case Palette:
+			t->decode = paldecode;
+			break;
+		default:
+			break;
+		}
+		break;
+	case Strips:
+		t->strips = f->val;
+		t->nstrips = f->cnt;
+		break;
+	case Fill:
+		t->fill = v;
+		break;
+	case Orientation:
+		t->orientation = v;
+		break;
+	case Samples:
+		t->samples = v;
+		break;
+	case Rows:
+		t->rows = v;
+		break;
+	case Counts:
+		t->counts = f->val;
+		t->ncounts = f->cnt;
+		break;
+	case Planar:
+		t->planar = v;
+		break;
+	case T4opts:
+		t->t4 = v;
+		break;
+	case T6opts:
+		t->t6 = v;
+		break;
+	case Predictor:
+		t->predictor = v;
+		break;
+	case Color:
+		t->color = f->val;
+		t->ncolor = f->cnt;
+		break;
+	default:
+		werrstr("shouldn't reach");
+		return -1;
+	}
+	return 0;
+}
+
+static int
+readfield(Tif *t, Fld *f)
+{
+	int size;
+	ulong i, j, n, off;
+	ulong (*readval)(Tif *);
+
+	f->tag = readshort(t);
+	f->typ = readshort(t);
+	f->cnt = readlong(t);
+	f->val = nil;
+	switch(f->tag) {
+	case Width:
+	case Length:
+	case Compression:
+	case Photometric:
+	case Fill:
+	case Orientation:
+	case Samples:
+	case Rows:
+	case Planar:
+	case T4opts:
+	case T6opts:
+	case Predictor:
+		if(f->cnt != 1) {
+			werrstr("field count");
+			return -1;
+		}
+		break;
+	case Bits:
+		if(f->cnt != 1 && f->cnt != 3) {
+			werrstr("field count");
+			return -1;
+		}
+		break;
+	case Strips:
+	case Counts:
+	case Color:
+		break;
+	default:
+		readlong(t);
+		return 0;
+	}
+	switch(f->typ) {
+	case Byte:
+		readval = readbyte;
+		break;
+	case Short:
+		readval = readshort;
+		break;
+	case Long:
+		readval = readlong;
+		break;
+	default:
+		werrstr("unsupported type\n");
+		return -1;
+	}
+	if((f->val = malloc(f->cnt*sizeof *f->val)) == nil)
+		return -1;
+	size = typesizes[f->typ];
+	if((n = size*f->cnt) <= 4) {
+		for(i = 0; i < f->cnt; i++)
+			f->val[i] = readval(t);
+		f->off = 0x0;
+		f->nval = i;
+		for(j = n; j < 4; j += size)
+			readval(t);
+	} else {
+		f->off = readlong(t);
+		off = t->n;
+		if(gototif(t, f->off) < 0)
+			return -1;
+		for(i = 0; i < f->cnt; i++)
+			f->val[i] = readval(t);
+		f->nval = i;
+		if(gototif(t, off) < 0)
+			return -1;
+	}
+	return parsefield(t, f);
+}
+
+static int
+checkfields(Tif *t)
+{
+	double a, b;
+	ulong n, size;
+
+	if(t->dx == 0) {
+		werrstr("image width");
+		return -1;
+	}
+	if(t->dy == 0) {
+		werrstr("image length");
+		return -1;
+	}
+	switch(t->depth) {
+	case 1:
+	case 4:
+	case 8:
+	case 24:
+		break;
+	default:
+		werrstr("bits per sample");
+		return -1;
+	}
+	switch(t->comp) {
+	case Nocomp:
+		t->uncompress = nocomp;
+		break;
+	case Huffman:
+	case T4enc:
+	case T6enc:
+		t->uncompress = fax;
+		if(t->decode != nil)
+			t->decode = faxdecode;
+		if((t->comp == T4enc && t->t4 & (1<<1)) ||
+			(t->comp == T6enc &&
+			t->t6 & (1<<1))) {
+			werrstr("uncompressed mode");
+			return -1;
+		}
+		break;
+	case Lzwenc:
+		t->uncompress = lzw;
+		break;
+	case Packbits:
+		t->uncompress = packbits;
+		break;
+	default:
+		werrstr("compression");
+		return -1;
+	}
+	if(t->decode == nil) {
+		werrstr("photometric interpretation");
+		return -1;
+	}
+	if(t->depth > 1 && (t->comp == Huffman ||
+		t->comp == T4enc || t->comp == T6enc)) {
+		werrstr("compression");
+		return -1;
+	}
+	if(t->fill != 1 && t->fill != 2) {
+		werrstr("fill order");
+		return -1;
+	}
+	if(t->fill == 2 && t->depth != 1) {
+		werrstr("depth should be 1 with fill order 2");
+		return -1;
+	}
+	if(t->orientation != 1) {
+		werrstr("orientation");
+		return -1;
+	}
+	if(t->rows == 0) {
+		werrstr("rows per strip");
+		return -1;
+	}
+	a = (double)t->dy;
+	b = (double)t->rows;
+	n = (ulong)floor((a+b-1)/b);
+	if(t->strips == nil || t->nstrips != n) {
+		werrstr("strip offsets");
+		return -1;
+	}
+	if(t->samples == 1 && t->photo == Rgb) {
+		werrstr("not enough samples per pixel");
+		return -1;
+	}
+	if(t->samples == 3 && t->photo != Rgb) {
+		werrstr("too many samples per pixel");
+		return -1;
+	}
+	if(t->samples != 1 && t->samples != 3) {
+		werrstr("samples per pixel");
+		return -1;
+	}
+	/*
+	* strip byte counts should not be missing,
+	* but we can guess correctly in this case
+	*/
+	size = sizeof *t->counts;
+	if(t->counts == nil && t->comp == Nocomp &&
+		t->nstrips == 1 &&
+		(t->counts = malloc(size)) != nil) {
+		n = t->dx * t->dy * t->depth;
+		t->counts[0] = n%8 == 0? n/8: n/8+1;
+		t->ncounts = t->nstrips;
+	}
+	if(t->counts == nil || t->ncounts != t->nstrips) {
+		werrstr("strip byte counts");
+		return -1;
+	}
+	if(t->planar != 1) {
+		werrstr("planar configuration");
+		return -1;
+	}
+	if(t->photo == Palette && (t->color == nil ||
+		t->ncolor != 3*(1<<t->depth))) {
+		werrstr("color map");
+		return -1;
+	}
+	if(t->predictor == 2 && t->depth == 1) {
+		werrstr("depth too low for predictor 2");
+		return -1;
+	}
+	return 0;
+}
+
+static int
+readstrips(Tif *t)
+{
+	int i, j, n;
+	ulong off;
+
+	t->ndata = 0;
+	for(i = 0; i < t->nstrips; i++)
+		t->ndata += t->counts[i];
+	if((t->data = malloc(t->ndata*sizeof *t->data)) == nil)
+		return -1;
+	off = t->n;
+	for(i = n = 0; i < t->nstrips; i++) {
+		if(gototif(t, t->strips[i]) < 0)
+			return -1;
+		/*
+		* we store each strip's offset in t->data
+		* in order to skip the final rtc or eofb
+		* during fax decoding. t->strips is used
+		* to save on memory allocation. these
+		* offsets are also used in lzw as a
+		* preventive measure.
+		*/
+		t->strips[i] = n;
+		for(j = 0; j < t->counts[i]; j++, n++)
+			t->data[n] = readbyte(t);
+	}
+	return gototif(t, off);
+}
+
+static Rawimage *
+decode(Tif *t)
+{
+	ulong size;
+	uchar *data;
+	Rawimage *im;
+
+	if((im = malloc(sizeof *im)) == nil)
+		return nil;
+	im->r = Rect(0, 0, t->dx, t->dy);
+	im->cmap = nil;
+	im->cmaplen = 0;
+	im->chanlen = t->dx * t->dy;
+	if(t->photo == Rgb || t->photo == Palette) {
+		im->chandesc = CRGB24;
+		im->chanlen *= 3;
+	} else
+		im->chandesc = CY;
+	im->nchans = 1;
+	size = im->chanlen * sizeof *im->chans[0];
+	if((im->chans[0] = malloc(size)) == nil)
+		return nil;
+	/* unused members */
+	im->fields = 0;
+	im->gifflags = 0;
+	im->gifdelay = 0;
+	im->giftrindex = 0;
+	im->gifloopcount = 1;
+	if((data = (*t->uncompress)(t)) == nil)
+		return nil;
+	if((*t->decode)(t, im, data) < 0) {
+		free(im->chans[0]);
+		free(im);
+		im = nil;
+	}
+	free(data);
+	return im;
+}
+
+static void
+freefields(Tif *t)
+{
+	uint i;
+
+	for(i = 0; i < t->nfld; i++) {
+		if(t->fld[i].val != nil)
+			free(t->fld[i].val);
+	}
+	free(t->fld);
+}
+
+static Rawimage *
+readslave(Tif *t)
+{
+	uint i, j;
+	Rawimage *r;
+
+	if(readheader(t) < 0)
+		return nil;
+	if((t->nfld = readshort(t)) <= 0) {
+		werrstr("illegal field number: %#.4x", t->nfld);
+		return nil;
+	}
+	if((t->fld = malloc(t->nfld*sizeof *t->fld)) == nil)
+		return nil;
+	for(i = 0; i < t->nfld; i++) {
+		if(readfield(t, &t->fld[i]) < 0) {
+			if(t->fld[i].val != nil)
+				free(t->fld[i].val);
+			break;
+		}
+	}
+	if(i < t->nfld) {
+		for(j = 0; j < i; j++) {
+			if(t->fld[j].val != nil)
+				free(t->fld[j].val);
+		}
+		free(t->fld);
+		return nil;
+	}
+	readlong(t);
+	if(checkfields(t) < 0) {
+		freefields(t);
+		return nil;
+	}
+	if(readstrips(t) < 0) {
+		freefields(t);
+		if(t->data != nil)
+			free(t->data);
+		return nil;
+	}
+	free(t->buf);
+	r = decode(t);
+	freefields(t);
+	return r;
+}
+
+Rawimage **
+Breadtif(Biobuf *b)
+{
+	Rawimage **array, *r;
+	Tif *t;
+
+	if((t = malloc(sizeof *t)) == nil)
+		return nil;
+	if((array = malloc(2*sizeof *array)) == nil)
+		return nil;
+	t->fd = b;
+	t->buf = nil;
+	t->nbuf = t->eof = t->n = 0;
+	/* order doesn't matter for the first two bytes */
+	t->byte2 = byte2le;
+	t->byte4 = byte4le;
+	/* defaults */
+	t->dx = 0;
+	t->dy = 0;
+	t->depth = 1;
+	t->comp = 1;
+	t->uncompress = nil;
+	t->photo = 0;
+	t->decode = nil;
+	t->fill = 1;
+	t->orientation = 1;
+	t->strips = nil;
+	t->nstrips = 0;
+	t->samples = 1;
+	t->rows = 0xffffffff; /* entire image is one strip */
+	t->counts = nil;
+	t->ncounts = 0;
+	t->planar = 1;
+	t->t4 = 0;
+	t->t6 = 0;
+	t->predictor = 1;
+	t->color = nil;
+	t->ncolor = 0;
+	r = readslave(t);
+	free(t);
+	array[0] = r;
+	array[1] = nil;
+	return array;
+}
+
+Rawimage **
+readtif(int fd)
+{
+	Rawimage **a;
+	Biobuf b;
+
+	if(Binit(&b, fd, OREAD) < 0)
+		return nil;
+	a = Breadtif(&b);
+	Bterm(&b);
+	return a;
+}
--- /dev/null
+++ b/sys/src/cmd/jpg/tif.c
@@ -1,0 +1,251 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <event.h>
+#include "imagefile.h"
+
+int cflag = 0;
+int dflag = 0;
+int eflag = 0;
+int nineflag = 0;
+int threeflag = 0;
+int output = 0;
+Image *image;
+int defaultcolor = 1;
+
+enum {
+	Border = 2,
+	Edge = 5
+};
+
+int init(void);
+char *show(int, char *, int);
+
+void
+eresized(int new)
+{
+	Rectangle r;
+
+	if(new && getwindow(display, Refnone) < 0)
+		sysfatal("getwindow: %r");
+	if(image == nil)
+		return;
+	r = insetrect(screen->clipr, Edge+Border);
+	r.max.x = r.min.x + Dx(image->r);
+	r.max.y = r.min.y + Dy(image->r);
+	border(screen, r, -Border, nil, ZP);
+	drawop(screen, r, image, nil, image->r.min, S);
+	flushimage(display, 1);
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-39cdektv] [file.tif ...]\n", argv0);
+	exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+	int fd, i;
+	char *err;
+	ulong outchan;
+
+	outchan = CMAP8;
+	ARGBEGIN {
+	/*
+	* produce encoded, compressed, bitmap file;
+	* no display by default
+	*/
+	case 'c':
+		cflag++;
+		dflag++;
+		output++;
+		if(defaultcolor)
+			outchan = CMAP8;
+		break;
+	/* suppress display of image */
+	case 'd':
+		dflag++;
+		break;
+	/* disable floyd-steinberg error diffusion */
+	case 'e':
+		eflag++;
+		break;
+	/* force black and white */
+	case 'k':
+		defaultcolor = 0;
+		outchan = GREY8;
+		break;
+	/*
+	* produce encoded, compressed, three-color
+	* bitmap file; no display by default
+	*/
+	case '3':
+		threeflag++;
+		/* fall through */
+	/*
+	* produce encoded, compressed, true-color
+	* bitmap file; no display by default
+	*/
+	case 't':
+		cflag++;
+		dflag++;
+		output++;
+		defaultcolor = 0;
+		outchan = RGB24;
+		break;
+	/* force RGBV */
+	case 'v':
+		defaultcolor = 0;
+		outchan = CMAP8;
+		break;
+	/*
+	* produce plan 9, uncompressed, bitmap file;
+	* no display by default
+	*/
+	case '9':
+		nineflag++;
+		dflag++;
+		output++;
+		if(defaultcolor)
+			outchan = CMAP8;
+		break;
+	default:
+		usage();
+	} ARGEND
+
+	if(argc <= 0)
+		exits(show(0, "<stdin>", outchan));
+	err = nil;
+	for(i = 0; i < argc; i++) {
+		if((fd = open(argv[i], OREAD)) < 0) {
+			fprint(2, "%s: open %s: %r\n",
+				argv0, argv[i]);
+			err = "open";
+		} else {
+			err = show(fd, argv[i], outchan);
+			close(fd);
+		}
+		if((nineflag || cflag) && argc > 1 && err == nil) {
+			fprint(2, "%s: exiting after one file\n",
+				argv0);
+			break;
+		}
+	}
+	exits(err);
+}
+
+int
+init(void)
+{
+	static int inited = 0;
+
+	if(!inited) {
+		if(initdraw(0, 0, 0) < 0) {
+			fprint(2, "%s: initdraw: %r", argv0);
+			return -1;
+		}
+		einit(Ekeyboard|Emouse);
+		inited++;
+	}
+	return 0;
+}
+
+char *
+show(int fd, char *name, int outchan)
+{
+	Rawimage **array, *r, *c;
+	Image *i;
+	int j, ch;
+	Biobuf b;
+	char buf[32];
+
+	if(Binit(&b, fd, OREAD) < 0)
+		return nil;
+	array = Breadtif(&b);
+	if(array == nil || array[0] == nil) {
+		if(array != nil)
+			free(array);
+		fprint(2, "%s: decode %s failed: %r\n", argv0,
+			name);
+		return "decode";
+	}
+	Bterm(&b);
+	if(!dflag) {
+		if(init() < 0)
+			return "initdraw";
+/* fixme: ppm doesn't check for outchan==CMAP8 */
+		if(defaultcolor && screen->depth > 8 &&
+			outchan == CMAP8)
+			outchan = RGB24;
+	}
+	r = array[0];
+	if(outchan != CMAP8) {
+		switch(r->chandesc) {
+		case CY:
+			outchan = GREY8;
+			break;
+		case CRGB24:
+			outchan = RGB24;
+			break;
+		}
+		c = r;
+	} else if((c = torgbv(r, !eflag)) == nil) {
+		fprint(2, "%s: conversion of %s failed: %r\n",
+			argv0, name);
+		return "torgbv";
+	}
+	if(!dflag) {
+		i = allocimage(display, c->r, outchan, 0, 0);
+		if(i == nil) {
+			fprint(2, "%s: allocimage %s: %r\n",
+				argv0, name);
+			return "allocimage";
+		}
+		if(loadimage(i, i->r, c->chans[0],
+			c->chanlen) < 0) {
+			fprint(2, "%s: loadimage %s: %r\n",
+				argv0, name);
+			return "loadimage";
+		}
+		image = i;
+		eresized(0);
+		ch = ekbd();
+		if(ch == 'q' || ch == 0x7f || ch == 0x04)
+			exits(nil);
+		draw(screen, screen->clipr, display->white,
+			nil, ZP);
+		image = nil;
+		freeimage(i);
+	}
+	if(nineflag) {
+		chantostr(buf, outchan);
+		print("%11s %11d %11d %11d %11d ", buf,
+			c->r.min.x, c->r.min.y,
+			c->r.max.x, c->r.max.y);
+		if(write(1, c->chans[0], c->chanlen) !=
+			c->chanlen) {
+			fprint(2, "%s: %s: write error: %r\n",
+				argv0, name);
+			return "write";
+		}
+	} else if(cflag) {
+		if(writerawimage(1, c) < 0) {
+			fprint(2, "%s: %s: write error: %r\n",
+				argv0, name);
+			return "write";
+		}
+	}
+	if(c != nil && c != r) {
+		free(c->chans[0]);
+		free(c);
+	}
+	for(j = 0; j < r->nchans; j++)
+		free(r->chans[j]);
+	free(r);
+	free(array);
+	return nil;
+}
--- a/sys/src/cmd/ndb/cs.c
+++ b/sys/src/cmd/ndb/cs.c
@@ -87,6 +87,7 @@
 long	active;		/* number of active slaves */
 char	*dbfile;
 Ndb	*db, *netdb;
+char	*csuser;
 
 void	rversion(Job*);
 void	rflush(Job*);
@@ -266,6 +267,7 @@
 	netinit(0);
 
 	if(!justsetname){
+		csuser = estrdup(getuser());
 		mountinit(servefile, mntpt);
 		io();
 	}
@@ -442,8 +444,10 @@
 
 	for(;;){
 		n = read9pmsg(mfd[0], mdata, sizeof mdata);
-		if(n<=0)
+		if(n < 0)
 			error("mount read");
+		if(n == 0)
+			continue;
 		job = newjob();
 		if(convM2S(mdata, n, &job->request) != n){
 			syslog(1, logfile, "format error %ux %ux %ux %ux %ux",
@@ -779,6 +783,9 @@
 	}
 	job->request.data[cnt] = 0;
 
+	if(strcmp(mf->user, "none") == 0 || strcmp(mf->user, csuser) != 0)
+		goto query;	/* skip special commands if not owner */
+
 	/*
 	 *  toggle debugging
 	 */
@@ -825,6 +832,7 @@
 		goto send;
 	}
 
+query:
 	if(mf->ref){
 		err = "query already in progress";
 		goto send;
--- a/sys/src/cmd/ndb/dn.c
+++ b/sys/src/cmd/ndb/dn.c
@@ -597,7 +597,7 @@
 
 /*
  *  mark all local db records about my area as authoritative,
- *  time out any others
+ *  delete timed out ones
  */
 void
 dnauthdb(void)
@@ -606,7 +606,7 @@
 	ulong minttl;
 	Area *area;
 	DN *dp;
-	RR *rp;
+	RR *rp, **l;
 
 	lock(&dnlock);
 
@@ -614,8 +614,13 @@
 	for(i = 0; i < HTLEN; i++)
 		for(dp = ht[i]; dp; dp = dp->next){
 			area = inmyarea(dp->name);
-			for(rp = dp->rr; rp; rp = rp->next)
+			l = &dp->rr;
+			for(rp = *l; rp; rp = *l){
 				if(rp->db){
+					if(rp->expire == 0){
+						rrdelhead(l);
+						continue;
+					}
 					if(area){
 						minttl = area->soarr->soa->minttl;
 						if(rp->ttl < minttl)
@@ -622,11 +627,9 @@
 							rp->ttl = minttl;
 						rp->auth = 1;
 					}
-					if(rp->expire == 0){
-						rp->db = 0;
-						dp->referenced = now-Reserved-1;
-					}
 				}
+				l = &rp->next;
+			}
 		}
 
 	unlock(&dnlock);
--- a/sys/src/cmd/ndb/dns.c
+++ b/sys/src/cmd/ndb/dns.c
@@ -77,6 +77,7 @@
 
 char	*logfile = "dns";	/* or "dns.test" */
 char	*dbfile;
+char	*dnsuser;
 char	mntpt[Maxpath];
 
 int	addforwtarg(char *);
@@ -198,6 +199,7 @@
 	opendatabase();
 	now = time(nil);		/* open time files before we fork */
 	nowns = nsec();
+	dnsuser = estrdup(getuser());
 
 	snprint(servefile, sizeof servefile, "#s/dns%s", ext);
 	dir = dirstat(servefile);
@@ -428,8 +430,9 @@
 	while(!stop){
 		procsetname("%d %s/dns Twrites of %d 9p rpcs read; %d alarms",
 			stats.qrecvd9p, mntpt, stats.qrecvd9prpc, stats.alarms);
-		n = read9pmsg(mfd[0], mdata, sizeof mdata);
-		if(n<=0){
+		while((n = read9pmsg(mfd[0], mdata, sizeof mdata)) == 0)
+			;
+		if(n < 0){
 			dnslog("error reading 9P from %s: %r", mntpt);
 			sleep(2000);	/* don't thrash after read error */
 			return;
@@ -717,10 +720,14 @@
 	if(cnt > 0 && job->request.data[cnt-1] == '\n')
 		job->request.data[cnt-1] = 0;
 
+	if(strcmp(mf->user, "none") == 0 || strcmp(mf->user, dnsuser) != 0)
+		goto query;	/* skip special commands if not owner */
+
 	/*
 	 *  special commands
 	 */
-//	dnslog("rwrite got: %s", job->request.data);
+	if(debug)
+		dnslog("rwrite got: %s", job->request.data);
 	send = 1;
 	if(strcmp(job->request.data, "debug")==0)
 		debug ^= 1;
@@ -744,6 +751,7 @@
 	if (send)
 		goto send;
 
+query:
 	/*
 	 *  kill previous reply
 	 */
--- a/sys/src/cmd/nusb/ether/asix.c
+++ b/sys/src/cmd/nusb/ether/asix.c
@@ -225,7 +225,8 @@
 	hd = GET4(bin);
 	n = hd & 0xFFFF;
 	m = n+4;
-	if((n != ~(hd>>16)) || (n < 6) || (m > nbin)){
+	hd = (hd>>16) ^ 0xFFFF;
+	if((n != hd) || (n < 6) || (m > nbin)){
 		nbin = 0;
 		return 0;
 	}
@@ -242,9 +243,12 @@
 static void
 asixwrite(Dev *ep, uchar *p, int n)
 {
+	uint hd;
+
 	if(n > sizeof(bout)-8)
 		n = sizeof(bout)-8;
-	PUT4(bout, n | ~(n<<16));
+	hd = n | (n<<16)^0xFFFF0000;
+	PUT4(bout, hd);
 	memmove(bout+4, p, n);
 	n += 4;
 	if((n % ep->maxpkt) == 0){
--- a/sys/src/cmd/nusb/ether/ether.c
+++ b/sys/src/cmd/nusb/ether/ether.c
@@ -810,8 +810,9 @@
 	if(argc != 1)
 		usage();
 
-	d = getdev(atoi(*argv));
-	if(findendpoints(d, &ei, &eo)  < 0)
+	if((d = getdev(atoi(*argv))) == nil)
+		sysfatal("getdev: %r");
+	if(findendpoints(d, &ei, &eo) < 0)
 		sysfatal("no endpoints found");
 
 	werrstr("");
--- a/sys/src/cmd/page.c
+++ b/sys/src/cmd/page.c
@@ -680,6 +680,7 @@
 	"image/gif",			popenimg,	"gif",
 	"image/jpeg",			popenimg,	"jpg",
 	"image/png",			popenimg,	"png",
+	"image/tiff",			popenimg,	"tif",
 	"image/ppm",			popenimg,	"ppm",
 	"image/bmp",			popenimg,	"bmp",
 	"image/tga",			popenimg,	"tga",
@@ -1241,12 +1242,9 @@
 	if(p->ext == nil)
 		return;
 	snprint(label, sizeof(label), "%s %s", p->ext, p->label);
-	if(p->image){
-		ps = subpt(p->image->r.max, p->image->r.min);
-		ps.x += 24;
-		ps.y += 24;
-	} else
-		ps = subpt(screen->r.max, screen->r.min);
+	ps = Pt(0, 0);
+	if(p->image)
+		ps = addpt(subpt(p->image->r.max, p->image->r.min), Pt(24, 24));
 	drawlock(0);
 	if((fd = p->fd) < 0){
 		if(p->open != popenfile)
@@ -1257,7 +1255,7 @@
 		seek(fd, 0, 0);
 	}
 	if(rfork(RFPROC|RFMEM|RFFDG|RFNOTEG|RFNAMEG|RFNOWAIT) == 0){
-		snprint(buf, sizeof(buf), "-pid %d -dx %d -dy %d", getpid(), ps.x, ps.y);
+		snprint(buf, sizeof(buf), "-pid %d", getpid());
 		if(newwindow(buf) != -1){
 			dupfds(fd, 1, 2, -1);
 			if((fd = open("/dev/label", OWRITE)) >= 0){
@@ -1264,6 +1262,8 @@
 				write(fd, label, strlen(label));
 				close(fd);
 			}
+			if(ps.x && ps.y)
+				resizewin(ps);
 			argv[0] = "rc";
 			argv[1] = "-c";
 			argv[2] = p->ext;
@@ -1360,7 +1360,7 @@
 			break;
 		o = subpt(m->xy, screen->r.min);
 		if(i == Czoomin){
-			if(zoom < 0x40000000){
+			if(zoom < 0x1000){
 				zoom *= 2;
 				pos =  addpt(mulpt(subpt(pos, o), 2), o);
 			}
--- a/sys/src/cmd/paint.c
+++ b/sys/src/cmd/paint.c
@@ -15,6 +15,7 @@
 Image *pal[16];		/* palette */
 Rectangle palr;		/* palette rect on screen */
 Rectangle penr;		/* pen size rect on screen */
+
 enum {
 	NBRUSH = 10+1,
 };
@@ -42,6 +43,29 @@
 };
 
 /*
+ * get bounding rectnagle for stroke from r.min to r.max with
+ * specified brush (size).
+ */
+static Rectangle
+strokerect(Rectangle r, int brush)
+{
+	r = canonrect(r);
+	return Rect(r.min.x-brush, r.min.y-brush, r.max.x+brush+1, r.max.y+brush+1);
+}
+
+/*
+ * draw stroke from r.min to r.max to dst with color ink and
+ * brush (size).
+ */
+static void
+strokedraw(Image *dst, Rectangle r, Image *ink, int brush)
+{
+	if(!eqpt(r.min, r.max))
+		line(dst, r.min, r.max, Enddisc, Enddisc, brush, ink, ZP);
+	fillellipse(dst, r.max, brush, brush, ink, ZP);
+}
+
+/*
  * A draw operation that touches only the area contained in bot but not in top.
  * mp and sp get aligned with bot.min.
  */
@@ -259,10 +283,16 @@
 		if((tmp = undo[x]) == nil)
 			return;
 		undo[x] = nil;
-		expand(tmp->r);
-		draw(canvas, tmp->r, tmp, nil, tmp->r.min);
-		update(&tmp->r);
-		freeimage(tmp);
+		if(canvas == nil || canvas->chan != tmp->chan){
+			freeimage(canvas);
+			canvas = tmp;
+			update(nil);
+		} else {
+			expand(tmp->r);
+			draw(canvas, tmp->r, tmp, nil, tmp->r.min);
+			update(&tmp->r);
+			freeimage(tmp);
+		}
 	}
 }
 
@@ -450,19 +480,21 @@
 
 	r = penr;
 	draw(screen, r, back, nil, ZP);
-	for(i=0; i<10; i++){
+	for(i=0; i<NBRUSH; i++){
 		r.max.x = penr.min.x + (i+1)*Dx(penr) / NBRUSH;
 		rr = r;
 		if(i == brush)
 			rr.min.y += Dy(r)/3;
-		fillellipse(screen, addpt(rr.min, divpt(subpt(rr.max, rr.min), 2)), i, i, ink, ZP);
+		if(i == NBRUSH-1){
+			/* last is special brush for fill draw */
+			draw(screen, rr, ink, nil, ZP);
+		} else {
+			rr.min = addpt(rr.min, divpt(subpt(rr.max, rr.min), 2));
+			rr.max = rr.min;
+			strokedraw(screen, rr, ink, i);
+		}
 		r.min.x = r.max.x;
 	}
-	r.max.x = penr.min.x + (i+1)*Dx(penr) / NBRUSH;
-	rr = r;
-	if(i == brush)
-		rr.min.y += Dy(r)/3;
-	draw(screen, rr, ink, nil, ZP);
 
 	r = palr;
 	for(i=1; i<=nelem(pal); i++){
@@ -614,7 +646,7 @@
 				/* no break */
 			case 1:
 				p = s2c(e.mouse.xy);
-				if(brush >= 10){
+				if(brush == NBRUSH-1){
 					/* flood fill brush */
 					if(canvas == nil || !ptinrect(p, canvas->r)){
 						back = img;
@@ -632,10 +664,10 @@
 						;
 					break;
 				}
-				r = Rect(p.x-brush, p.y-brush, p.x+brush+1, p.y+brush+1);
+				r = strokerect(Rpt(p, p), brush);
 				expand(r);
 				save(r, 1);
-				fillellipse(canvas, p, brush, brush, img, ZP);
+				strokedraw(canvas, Rpt(p, p), img, brush);
 				update(&r);
 				for(;;){
 					m = e.mouse;
@@ -646,14 +678,10 @@
 					d = s2c(e.mouse.xy);
 					if(eqpt(d, p))
 						continue;
-					r = canonrect(Rpt(p, d));
-					r.min.x -= brush;
-					r.min.y -= brush;
-					r.max.x += brush+1;
-					r.max.y += brush+1;
+					r = strokerect(Rpt(p, d), brush);
 					expand(r);
 					save(r, 0);
-					line(canvas, p, d, Enddisc, Enddisc, brush, img, ZP);
+					strokedraw(canvas, Rpt(p, d), img, brush);
 					update(&r);
 					p = d;
 				}
@@ -677,10 +705,12 @@
 				center();
 				break;
 			case '+':
-				setzoom(e.mouse.xy, zoom*2);
+				if(zoom < 0x1000)
+					setzoom(e.mouse.xy, zoom*2);
 				break;
 			case '-':
-				setzoom(e.mouse.xy, zoom/2);
+				if(zoom > 1)
+					setzoom(e.mouse.xy, zoom/2);
 				break;
 			case 'c':
 				if(canvas == nil)
@@ -694,7 +724,7 @@
 				restore(16);
 				break;
 			case 'f':
-				brush = 10;
+				brush = NBRUSH-1;
 				drawpal();
 				break;
 			case '0': case '1': case '2': case '3': case '4':
--- a/sys/src/cmd/paqfs/paqfs.c
+++ b/sys/src/cmd/paqfs/paqfs.c
@@ -817,7 +817,7 @@
 		if(n < 0)
 			sysfatal("mount read");
 		if(n == 0)
-			break;
+			continue;
 		if(convM2S(mdata, n, &rhdr) == 0)
 			continue;
 
--- a/sys/src/cmd/plumb/fsys.c
+++ b/sys/src/cmd/plumb/fsys.c
@@ -237,12 +237,10 @@
 		if(buf == nil)
 			error("malloc failed: %r");
 		qlock(&readlock);
-		n = read9pmsg(srvfd, buf, messagesize);
-		if(n <= 0){
-			if(n < 0)
-				error("i/o error on server channel");
+		while((n = read9pmsg(srvfd, buf, messagesize)) == 0)
+			;
+		if(n < 0)
 			threadexitsall("unmounted");
-		}
 		if(readlock.head == nil)	/* no other processes waiting to read; start one */
 			proccreate(fsysproc, nil, Stack);
 		qunlock(&readlock);
--- a/sys/src/cmd/rio/dat.h
+++ b/sys/src/cmd/rio/dat.h
@@ -139,6 +139,7 @@
 	Channel		*mouseread;	/* chan(Mousereadmesg) */
 	Channel		*wctlread;		/* chan(Consreadmesg) */
 	Channel		*kbdread;	/* chan(Kbdreadmesg) */
+	Channel		*complete;	/* chan(Completion*) */
 	uint			nr;			/* number of runes in window */
 	uint			maxr;		/* number of runes allocated in r */
 	Rune			*r;
--- a/sys/src/cmd/rio/fsys.c
+++ b/sys/src/cmd/rio/fsys.c
@@ -194,8 +194,9 @@
 	x = nil;
 	for(;;){
 		buf = emalloc(messagesize+UTFmax);	/* UTFmax for appending partial rune in xfidwrite */
-		n = read9pmsg(fs->sfd, buf, messagesize);
-		if(n <= 0){
+		while((n = read9pmsg(fs->sfd, buf, messagesize)) == 0)
+			yield();
+		if(n < 0){
 			yield();	/* if threadexitsall'ing, will not return */
 			fprint(2, "rio: %d: read9pmsg: %d %r\n", getpid(), n);
 			errorshouldabort = 0;
--- a/sys/src/cmd/rio/wind.c
+++ b/sys/src/cmd/rio/wind.c
@@ -44,6 +44,7 @@
 	w->kbdread =  chancreate(sizeof(Kbdreadmesg), 0);
 	w->mouseread =  chancreate(sizeof(Mousereadmesg), 0);
 	w->wctlread =  chancreate(sizeof(Consreadmesg), 0);
+	w->complete = chancreate(sizeof(Completion*), 0);
 	w->scrollr = r;
 	w->scrollr.max.x = r.min.x+Scrollwid;
 	w->lastsr = ZR;
@@ -157,17 +158,19 @@
 	return 1;
 }
 
+void
+showcandidates(Window *, Completion *);
 
 void
 winctl(void *arg)
 {
 	Rune *rp, *bp, *tp, *up;
-	uint qh;
+	uint qh, q0;
 	int nr, nb, c, wid, i, npart, initial, lastb;
 	char *s, *t, part[3];
 	Window *w;
 	Mousestate *mp, m;
-	enum { WKbd, WKbdread, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT };
+	enum { WKbd, WKbdread, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, WComplete, NWALT };
 	Alt alts[NWALT+1];
 	Mousereadmesg mrm;
 	Kbdreadmesg krm;
@@ -176,6 +179,7 @@
 	Consreadmesg cwrm;
 	Stringpair pair;
 	Wctlmesg wcm;
+	Completion *cr;
 	char buf[4*12+1], *kbdq[8], *kbds;
 	int kbdqr, kbdqw;
 
@@ -215,6 +219,9 @@
 	alts[WWread].c = w->wctlread;
 	alts[WWread].v = &cwrm;
 	alts[WWread].op = CHANSND;
+	alts[WComplete].c = w->complete;
+	alts[WComplete].v = &cr;
+	alts[WComplete].op = CHANRCV;
 	alts[NWALT].op = CHANEND;
 
 	memset(kbdq, 0, sizeof(kbdq));
@@ -418,6 +425,21 @@
 			}
 			send(cwrm.c2, &pair);
 			continue;
+		case WComplete:
+			if(!w->deleted){
+				if(!cr->advance)
+					showcandidates(w, cr);
+				if(cr->advance){
+					rp = runesmprint("%s", cr->string);
+					nr = runestrlen(rp);
+					q0 = w->q0;
+					q0 = winsert(w, rp, nr, q0);
+					wshow(w, q0+nr);
+					free(rp);
+				}
+			}
+			freecompletion(cr);
+			break;
 		}
 		if(!w->deleted)
 			flushimage(display, 1);
@@ -507,17 +529,47 @@
 	free(rp);
 }
 
-Rune*
+typedef struct Completejob Completejob;
+struct Completejob
+{
+	char	*dir;
+	char	*str;
+	Window	*win;
+};
+
+void
+completeproc(void *arg)
+{
+	Completejob *job;
+	Completion *c;
+	char buf[128];
+
+	job = arg;
+	snprint(buf, sizeof(buf), "namecomplete %s", job->dir);
+	threadsetname(buf);
+
+	c = complete(job->dir, job->str);
+	if(c != nil && sendp(job->win->complete, c) <= 0)
+		freecompletion(c);
+
+	wclose(job->win);
+
+	free(job->dir);
+	free(job->str);
+	free(job);
+}
+
+void
 namecomplete(Window *w)
 {
 	int nstr, npath;
-	Rune *rp, *path, *str;
-	Completion *c;
-	char *s, *dir, *root;
+	Rune *path, *str;
+	char *dir, *root;
+	Completejob *job;
 
 	/* control-f: filename completion; works back to white space or / */
 	if(w->q0<w->nr && w->r[w->q0]>' ')	/* must be at end of word */
-		return nil;
+		return;
 	nstr = windfilewidth(w, w->q0, TRUE);
 	str = runemalloc(nstr);
 	runemove(str, w->r+(w->q0-nstr), nstr);
@@ -524,7 +576,6 @@
 	npath = windfilewidth(w, w->q0-nstr, FALSE);
 	path = runemalloc(npath);
 	runemove(path, w->r+(w->q0-nstr-npath), npath);
-	rp = nil;
 
 	/* is path rooted? if not, we need to make it relative to window path */
 	if(npath>0 && path[0]=='/'){
@@ -538,26 +589,17 @@
 		dir = malloc(strlen(root)+1+UTFmax*npath+1);
 		sprint(dir, "%s/%.*S", root, npath, path);
 	}
-	dir = cleanname(dir);
 
-	s = smprint("%.*S", nstr, str);
-	c = complete(dir, s);
-	free(s);
-	if(c == nil)
-		goto Return;
+	/* run in background, winctl will collect the result on w->complete chan */
+	job = emalloc(sizeof *job);
+	job->str = smprint("%.*S", nstr, str);
+	job->dir = cleanname(dir);
+	job->win = w;
+	incref(w);
+	proccreate(completeproc, job, STACK);
 
-	if(!c->advance)
-		showcandidates(w, c);
-
-	if(c->advance)
-		rp = runesmprint("%s", c->string);
-
-  Return:
-	freecompletion(c);
-	free(dir);
 	free(path);
 	free(str);
-	return rp;
 }
 
 void
@@ -564,8 +606,7 @@
 wkeyctl(Window *w, Rune r)
 {
 	uint q0 ,q1;
-	int n, nb, nr;
-	Rune *rp;
+	int n, nb;
 	int *notefd;
 
 	switch(r){
@@ -679,14 +720,7 @@
 		return;
 	case Kack:	/* ^F: file name completion */
 	case Kins:	/* Insert: file name completion */
-		rp = namecomplete(w);
-		if(rp == nil)
-			return;
-		nr = runestrlen(rp);
-		q0 = w->q0;
-		q0 = winsert(w, rp, nr, q0);
-		wshow(w, q0+nr);
-		free(rp);
+		namecomplete(w);
 		return;
 	case Kbs:	/* ^H: erase character */
 	case Knack:	/* ^U: erase line */
@@ -1193,6 +1227,7 @@
 		chanfree(w->mouseread);
 		chanfree(w->wctlread);
 		chanfree(w->kbdread);
+		chanfree(w->complete);
 		free(w->raw);
 		free(w->r);
 		free(w->dir);
--- a/sys/src/cmd/samterm/main.c
+++ b/sys/src/cmd/samterm/main.c
@@ -310,6 +310,9 @@
 {
 	Text *t=(Text *)l->user1;
 
+	if(t->tag == Untagged)
+		return;
+
 	switch(but){
 	case 1:
 		outTsll(Torigin, t->tag, l->origin, p0);
--- a/sys/src/cmd/scram.c
+++ b/sys/src/cmd/scram.c
@@ -25,6 +25,10 @@
 	uchar	data[];
 };
 
+enum {
+	Tblsz	= 4+4+1+1+6+8+4+4+4,
+};
+
 void*
 amlalloc(int n){
 	return mallocz(n, 1);
@@ -64,15 +68,15 @@
 	amlinit();
 	for(;;){
 		t = malloc(sizeof(*t));
-		if((n = readn(fd, t, sizeof(*t))) <= 0)
+		if((n = readn(fd, t, Tblsz)) <= 0)
 			break;
-		if(n != sizeof(*t))
+		if(n != Tblsz)
 			return -1;
 		l = get32(t->len);
-		if(l < sizeof(*t))
+		if(l < Tblsz)
 			return -1;
-		t = realloc(t, l);
-		l -= sizeof(*t);
+		l -= Tblsz;
+		t = realloc(t, sizeof(*t) + l);
 		if(readn(fd, t->data, l) != l)
 			return -1;
 		if(memcmp("DSDT", t->sig, 4) == 0)
--- a/sys/src/cmd/tcs/cyrillic.h
+++ b/sys/src/cmd/tcs/cyrillic.h
@@ -39,10 +39,10 @@
     -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
     -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
     -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+    -1,    -1,    -1,0x0451,0x0454,    -1,0x0456,0x0457,
+    -1,    -1,    -1,    -1,    -1,0x0491,0x045e,    -1,
+    -1,    -1,    -1,0x0401,0x0404,    -1,0x0406,0x0407,
+    -1,    -1,    -1,    -1,    -1,0x0490,0x040e,    -1,
 0x044e,0x0430,0x0431,0x0446,0x0434,0x0435,0x0444,0x0433,
 0x0445,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,
 0x043f,0x044f,0x0440,0x0441,0x0442,0x0443,0x0436,0x0432,
--- a/sys/src/cmd/uhtml.c
+++ b/sys/src/cmd/uhtml.c
@@ -65,7 +65,7 @@
 
 	if(*argv){
 		close(0);
-		if(open(*argv, OREAD) != 1)
+		if(open(*argv, OREAD) != 0)
 			sysfatal("open: %r");
 	}
 	nbuf = 0;
--- a/sys/src/cmd/upas/smtp/smtp.c
+++ b/sys/src/cmd/upas/smtp/smtp.c
@@ -64,7 +64,7 @@
 void
 usage(void)
 {
-	fprint(2, "usage: smtp [-aAdfips] [-b busted-mx] [-g gw] [-h host] "
+	fprint(2, "usage: smtp [-aAdfipst] [-b busted-mx] [-g gw] [-h host] "
 		"[-u user] [.domain] net!host[!service] sender rcpt-list\n");
 	exits(Giveup);
 }
--- a/sys/src/cmd/vac/vacfs.c
+++ b/sys/src/cmd/vac/vacfs.c
@@ -706,8 +706,10 @@
 
 	for(;;){
 		n = read9pmsg(mfd[0], mdata, sizeof mdata);
-		if(n <= 0)
+		if(n < 0)
 			break;
+		if(n == 0)
+			continue;
 		if(convM2Su(mdata, n, &rhdr, dotu) != n)
 			sysfatal("convM2S conversion error");
 
--- a/sys/src/cmd/vnc/exportfs.c
+++ b/sys/src/cmd/vnc/exportfs.c
@@ -149,8 +149,9 @@
 		errdepth(ed);
 		q = smalloc(sizeof(Exq));
 
-		n = read9pmsg(fs->io, q->buf, Maxrpc);
-		if(n <= 0 || convM2S(q->buf, n, &q->rpc) != n)
+		while((n = read9pmsg(fs->io, q->buf, Maxrpc)) == 0)
+			;
+		if(n < 0 || convM2S(q->buf, n, &q->rpc) != n)
 			goto bad;
 
 		if(exdebug)
--- a/sys/src/lib9p/srv.c
+++ b/sys/src/lib9p/srv.c
@@ -59,7 +59,9 @@
 	Req *r;
 
 	qlock(&s->rlock);
-	if((n = read9pmsg(s->infd, s->rbuf, s->msize)) <= 0){
+	while((n = read9pmsg(s->infd, s->rbuf, s->msize)) == 0)
+		;
+	if(n < 0){
 		qunlock(&s->rlock);
 		return nil;
 	}
--- a/sys/src/libaml/aml.c
+++ b/sys/src/libaml/aml.c
@@ -47,7 +47,8 @@
 	"Smbus",
 	"Cmos",
 	"Pcibar",
-	"Ipmi" };
+	"Ipmi",
+};
 
 /* field flags */
 enum {
@@ -85,7 +86,7 @@
 	int	space;
 	uvlong	off;
 	uvlong	len;
-	uchar	*va;
+	uchar	*va;	/* non nil when mapped */
 };
 
 struct Field {
@@ -166,8 +167,8 @@
 	Oindex, Omutex, Oevent,
 	Ocfld, Ocfld0, Ocfld1, Ocfld2, Ocfld4, Ocfld8,
 	Oif, Oelse, Owhile, Obreak, Oret, Ocall, 
-	Ostore, Oderef, Osize, Oref, Ocref,
-	Oacq, Orel, Ostall, Osleep,
+	Ostore, Oderef, Osize, Oref, Ocref, Ocat,
+	Oacq, Orel, Ostall, Osleep, Oload, Ounload,
 };
 
 static Op optab[];
@@ -175,7 +176,8 @@
 static uchar octab2[];
 
 static Name*
-rootname(Name *dot){
+rootname(Name *dot)
+{
 	while(dot != dot->up)
 		dot = dot->up;
 	return dot;
@@ -182,8 +184,14 @@
 }
 
 static void
-gcmark(void *p){
+gcmark(void *p)
+{
+	int i;
+	Env *e;
+	Field *f;
 	Heap *h;
+	Name *n, *d;
+	Package *a;
 
 	if(p == nil)
 		return;
@@ -193,35 +201,29 @@
 	h->mark = 1;
 	switch(h->tag){
 	case 'E':
-		{
-			int i;
-			Env *e = p;
-			for(i=0; i<nelem(e->loc); i++)
-				gcmark(e->loc[i]);
-			for(i=0; i<nelem(e->arg); i++)
-				gcmark(e->arg[i]);
-		}
+		e = p;
+		for(i=0; i<nelem(e->loc); i++)
+			gcmark(e->loc[i]);
+		for(i=0; i<nelem(e->arg); i++)
+			gcmark(e->arg[i]);
 		break;
-	case 'R': case 'A': case 'L':
+	case 'R':
+	case 'A':
+	case 'L':
 		gcmark(((Ref*)p)->ref);
 		break;
 	case 'N':
-		{
-			Name *d, *n = p;
-			gcmark(n->v);
-			for(d = n->down; d; d = d->next)
-				gcmark(d);
-			gcmark(n->fork);
-			gcmark(n->up);
-		}
+		n = p;
+		gcmark(n->v);
+		for(d = n->down; d; d = d->next)
+			gcmark(d);
+		gcmark(n->fork);
+		gcmark(n->up);
 		break;
 	case 'p':
-		{
-			int i;
-			Package *a = p;
-			for(i=0; i<a->n; i++)
-				gcmark(a->a[i]);
-		}
+		a = p;
+		for(i=0; i<a->n; i++)
+			gcmark(a->a[i]);
 		break;
 	case 'r':
 		gcmark(((Region*)p)->name);
@@ -231,21 +233,20 @@
 		break;
 	case 'f':
 	case 'u':
-		{
-			Field *f = p;
-			gcmark(f->reg);
-			gcmark(f->index);
-			gcmark(f->indexv);
-		}
+		f = p;
+		gcmark(f->reg);
+		gcmark(f->index);
+		gcmark(f->indexv);
 		break;
 	}
 }
 
 static int
-gc(void){
+gc(void)
+{
+	int i;
 	Heap *h, **hh;
 	Frame *f;
-	int i;
 
 	for(h = hp; h; h = h->link)
 		h->mark = 0;
@@ -268,6 +269,12 @@
 			continue;
 		}
 		*hh = h->link;
+		if(h->tag == 'r'){
+			Region *r = (void*)H2D(h);
+
+			/* TODO: unmap region */
+			r->va = nil;
+		}
 		memset(h, ~0, sizeof(Heap)+h->size);
 		amlfree(h);
 		i++;
@@ -277,7 +284,8 @@
 }
 
 static void*
-mk(int tag, int size){
+mk(int tag, int size)
+{
 	Heap *h;
 	int a;
 
@@ -291,14 +299,18 @@
 }
 
 static uvlong*
-mki(uvlong i){
-	uvlong *v = mk('i', sizeof(uvlong));
+mki(uvlong i)
+{
+	uvlong *v;
+
+	v = mk('i', sizeof(uvlong));
 	*v = i;
 	return v;
 }
 
 static char*
-mks(char *s){
+mks(char *s)
+{
 	char *r = mk('s', strlen(s)+1);
 	strcpy(r, s);
 	return r;
@@ -305,7 +317,8 @@
 }
 
 static int
-pkglen(uchar *p, uchar *e, uchar **np){
+pkglen(uchar *p, uchar *e, uchar **np)
+{
 	ulong n;
 	uchar b;
 
@@ -336,7 +349,8 @@
 }
 
 static Name*
-forkname(Name *dot){
+forkname(Name *dot)
+{
 	Name *n;
 
 	n = mk('N', sizeof(Name));
@@ -355,7 +369,8 @@
 }
 
 static Name*
-getseg(Name *dot, void *seg, int new){
+getseg(Name *dot, void *seg, int new)
+{
 	Name *n, *l;
 
 	for(n = l = nil; dot; dot = dot->fork){
@@ -425,17 +440,6 @@
 }
 
 static uvlong
-ival(void *p){
-	if(p) switch(TAG(p)){
-	case 'i':
-		return *((uvlong*)p);
-	case 's':
-		return strtoull((char*)p, 0, 0);
-	}
-	return 0;
-}
-
-static uvlong
 rwreg(void *reg, int off, int len, uvlong v, int write)
 {
 	Region *r;
@@ -447,6 +451,7 @@
 		p = reg;
 		if((off+len) > SIZE(p))
 			break;
+	RWMem:
 		if(write){
 			for(i=0; i<len; i++){
 				p[off+i] = v & 0xFF;
@@ -462,11 +467,16 @@
 		if((off+len) > r->len)
 			break;
 		if(amldebug){
-			print("rwreg: %s %-8s [%llux+%x]/%d %llux\n", 
+			print("\nrwreg: %s %-8s [%llux+%x]/%d %llux\n", 
 				write ? "W" : "R", 
 				spacename[r->space],
 				r->off, off, len, v);
 		}
+		/* TODO: map region */
+		if(r->va != nil){
+			p = r->va + off;
+			goto RWMem;
+		}
 		break;
 	}
 
@@ -473,6 +483,30 @@
 	return ~0;
 }
 
+static uvlong
+ival(void *p)
+{
+	int n;
+
+	if(p != nil){
+		switch(TAG(p)){
+		case 'i':
+			return *((uvlong*)p);
+		case 's':
+			if(*((char*)p) == 0)
+				break;
+			return strtoull((char*)p, 0, 16);
+		case 'b':
+			n = SIZE(p);
+			if(n > 0){
+				if(n > 8) n = 8;
+				return rwreg(p, 0, n, 0, 0);
+			}
+		}
+	}
+	return 0;
+}
+
 static void *deref(void *p);
 static void *store(void *s, void *d);
 
@@ -495,7 +529,8 @@
 }
 
 static void*
-rwfield(Field *f, void *v, int write){
+rwfield(Field *f, void *v, int write)
+{
 	int boff, blen, wo, ws, wl, wa, wd, i;
 	uvlong w, m;
 	void *reg;
@@ -552,6 +587,7 @@
 		return nil;
 	if(blen > 64)
 		return b;
+
 	w = 0;
 	for(i=0; i<SIZE(b); i++)
 		w |= ((uvlong)b[i]) << i*8;
@@ -559,7 +595,8 @@
 }
 
 static void*
-deref(void *p){
+deref(void *p)
+{
 	if(p) switch(TAG(p)){
 	case 'N':
 		return ((Name*)p)->v;
@@ -572,25 +609,65 @@
 }
 
 static void*
-copy(int tag, void *s){
+copy(int tag, void *s)
+{
+	static char hex[] = "0123456789ABCDEF";
+	uvlong v;
 	void *d;
-	if(s){
-		int n;
-		if(tag == 0)
-			tag = TAG(s);
+	int i, n;
+
+	if(tag == 0){
+		if(s == nil)
+			return nil;
+		tag = TAG(s);
+	}
+	if(s == nil || TAG(s) == 'i'){
+		n = 4;
+		v = ival(s);
+		if(v > 0xFFFFFFFFULL)
+			n <<= 1;
 		switch(tag){
 		case 'b':
+			d = mk(tag, n);
+			rwreg(d, 0, n, v, 1);
+			return d;
 		case 's':
-			n = SIZE(s);
-			if(tag == 's' && TAG(s) == 'b')
-				n++;
+			n <<= 1;
+			d = mk(tag, n+1);
+			((char*)d)[n] = 0;
+			while(n > 0){
+				((char*)d)[--n] = hex[v & 0xF];
+				v >>= 4;
+			}
+			return d;
+		case 'i':
+			if(v == 0ULL)
+				return nil;
+			return mki(v);
+		}
+	} else {
+		n = SIZE(s);
+		switch(tag){
+		case 's':
+			if(TAG(s) == 'b'){
+				d = mk(tag, n*3 + 1);
+				for(i=0; i<n; i++){
+					((char*)d)[i*3 + 0] = hex[((uchar*)s)[i] >> 4];
+					((char*)d)[i*3 + 1] = hex[((uchar*)s)[i] & 0xF];
+					((char*)d)[i*3 + 2] = ' ';
+				}
+				((char*)d)[n*3] = 0;
+				return d;
+			}
+			/* no break */
+		case 'b':
+			if(TAG(s) == 's'){
+				n = strlen(s);
+				/* zero length string is converted to zero length buffer */								if(n > 0) n++;
+			}
 			d = mk(tag, n);
 			memmove(d, s, n);
-			if(tag == 's')
-				((uchar*)d)[n-1] = 0;
 			return d;
-		case 'i':
-			return mki(ival(s));
 		}
 	}
 	return s;
@@ -597,7 +674,8 @@
 }
 
 static void*
-store(void *s, void *d){
+store(void *s, void *d)
+{
 	void *p, **pp;
 
 	if(d == nil)
@@ -610,24 +688,25 @@
 		/* no break */
 	case 'R': case 'L':
 		pp = ((Ref*)d)->ptr;
-		while(p = *pp){
+		while((p = *pp) != nil){
 			switch(TAG(p)){
 			case 'R': case 'A': case 'L':
 				pp = ((Ref*)p)->ptr;
-				continue;
+				break;
 			case 'N':
 				pp = &((Name*)p)->v;
-				if(*pp != p)
-					continue;
+				break;
 			}
-			break;
+			if(*pp == p)
+				break;
 		}
 		break;
 	case 'N':
 		pp = &((Name*)d)->v;
+		break;
 	}
 	p = *pp;
-	if(p && TAG(p) != 'N'){
+	if(p != nil && TAG(p) != 'N'){
 		switch(TAG(p)){
 		case 'f':
 		case 'u':
@@ -634,17 +713,21 @@
 			rwfield(p, s, 1);
 			return d;
 		}
-		*pp = copy(TAG(p), s);
-	} else
-		*pp = copy(0, s);
+		if(TAG(d) != 'A' && TAG(d) != 'L'){
+			*pp = copy(TAG(p), s);
+			return d;
+		}
+	}
+	*pp = copy(0, s);
 	return d;
 }
 
 static int
-Nfmt(Fmt *f){
+Nfmt(Fmt *f)
+{
 	char buf[5];
-	Name *n;
 	int i;
+	Name *n;
 
 	n = va_arg(f->args, Name*);
 	if(n == nil)
@@ -664,9 +747,17 @@
 }
 
 static int
-Vfmt(Fmt *f){
+Vfmt(Fmt *f)
+{
 	void *p;
-	int c;
+	int i, n, c;
+	Env *e;
+	Field *l;
+	Name *nm;
+	Method *m;
+	Package *a;
+	Region *g;
+	Ref *r;
 
 	p = va_arg(f->args, void*);
 	if(p == nil)
@@ -674,83 +765,66 @@
 	c = TAG(p);
 	switch(c){
 	case 'N':
-		{
-			Name *n = p;
-
-			if(n->v != n)
-				return fmtprint(f, "%N=%V", n, n->v);
-			return fmtprint(f, "%N=*", n);
-		}
-	case 'A': case 'L':
-		{
-			Ref *r = p;
-			Env *e = r->ref;
-			if(c == 'A')
-				return fmtprint(f, "Arg%ld=%V", r->ptr - e->arg, *r->ptr);
-			if(c == 'L')
-				return fmtprint(f, "Local%ld=%V", r->ptr - e->loc, *r->ptr);
-		}
+		nm = p;
+		if(nm->v != nm)
+			return fmtprint(f, "%N=%V", nm, nm->v);
+		return fmtprint(f, "%N=*", nm);
+	case 'A':
+	case 'L':
+		r = p;
+		e = r->ref;
+		if(c == 'A')
+			return fmtprint(f, "Arg%ld=%V", r->ptr - e->arg, *r->ptr);
+		if(c == 'L')
+			return fmtprint(f, "Local%ld=%V", r->ptr - e->loc, *r->ptr);
 	case 's':
 		return fmtprint(f, "\"%s\"", (char*)p);
 	case 'i':
-		return fmtprint(f, "0x%llux", *((uvlong*)p));
+		return fmtprint(f, "%#llux", *((uvlong*)p));
 	case 'p':
-		{
-			int i;
-			Package *a = p;
-			fmtprint(f, "Package(%d){", a->n);
-			for(i=0; i<a->n; i++){
-				if(i > 0)
-					fmtprint(f, ", ");
-				fmtprint(f, "%V", a->a[i]);
-			}
-			fmtprint(f, "}");
+		a = p;
+		fmtprint(f, "Package(%d){", a->n);
+		for(i=0; i<a->n; i++){
+			if(i > 0)
+				fmtprint(f, ", ");
+			fmtprint(f, "%V", a->a[i]);
 		}
+		fmtprint(f, "}");
 		return 0;
 	case 'b':
-		{
-			int i, n;
-			n = SIZE(p);
-			fmtprint(f, "Buffer(%d){", n);
-			for(i=0; i<n; i++){
-				if(i > 0)
-					fmtprint(f, ", ");
-				fmtprint(f, "%.2uX", ((uchar*)p)[i]);
-			}
-			fmtprint(f, "}");
+		n = SIZE(p);
+		fmtprint(f, "Buffer(%d){", n);
+		for(i=0; i<n; i++){
+			if(i > 0)
+				fmtprint(f, ", ");
+			fmtprint(f, "%.2uX", ((uchar*)p)[i]);
 		}
+		fmtprint(f, "}");
 		return 0;
 	case 'r':
-		{
-			Region *r = p;
-			return fmtprint(f, "Region(%s, 0x%llux, 0x%llux)",
-				spacename[r->space & 7], r->off, r->len);
-		}
+		g = p;
+		return fmtprint(f, "Region(%s, %#llux, %#llux)",
+			spacename[g->space & 7], g->off, g->len);
 	case 'm':
-		{
-			int i;
-			Method *m = p;
-			fmtprint(f, "%N(", m->name);
-			for(i=0; i < m->narg; i++){
-				if(i > 0)
-					fmtprint(f, ", ");
-				fmtprint(f, "Arg%d", i);
-			}
-			fmtprint(f, ")");
-			return 0;
+		m = p;
+		fmtprint(f, "%N(", m->name);
+		for(i=0; i < m->narg; i++){
+			if(i > 0)
+				fmtprint(f, ", ");
+			fmtprint(f, "Arg%d", i);
 		}
+		fmtprint(f, ")");
+		return 0;
 	case 'u':
 		fmtprint(f, "Buffer");
 		/* no break */
 	case 'f':
-		{
-			Field *l = p;
-			if(l->index)
-				return fmtprint(f, "IndexField(%x, %x, %x) @ %V[%V]",
-					l->flags, l->bitoff, l->bitlen, l->index, l->indexv);
-			return fmtprint(f, "Field(%x, %x, %x) @ %V",
-				l->flags, l->bitoff, l->bitlen, l->reg);
-		}
+		l = p;
+		if(l->index)
+			return fmtprint(f, "IndexField(%x, %x, %x) @ %V[%V]",
+				l->flags, l->bitoff, l->bitlen, l->index, l->indexv);
+		return fmtprint(f, "Field(%x, %x, %x) @ %V",
+			l->flags, l->bitoff, l->bitlen, l->reg);
 	default:
 		return fmtprint(f, "%c:%p", c, p);
 	}
@@ -757,7 +831,8 @@
 }
 
 static void
-dumpregs(void){
+dumpregs(void)
+{
 	Frame *f;
 	Env *e;
 	int i;
@@ -789,7 +864,8 @@
 }
 
 static int
-xec(uchar *pc, uchar *end, Name *dot, Env *env, void **pret){
+xec(uchar *pc, uchar *end, Name *dot, Env *env, void **pret)
+{
 	static int loop;
 	int i, c;
 	void *r;
@@ -820,12 +896,12 @@
 		default:
 			if(PC >= FP->end){
 			Overrun:
-				print("PC overrun frame end");
+				print("aml: PC overrun frame end");
 				goto Out;
 			}
 			FP++;
 			if(FP >= FT){
-				print("frame stack overflow");
+				print("aml: frame stack overflow");
 				goto Out;
 			}
 			*FP = FP[-1];
@@ -870,7 +946,10 @@
 			((uchar*)r)[c] = 0;
 			PC = end;
 			break;
-		case '1': case '2': case '4': case '8':
+		case '1':
+		case '2':
+		case '4':
+		case '8':
 			end = PC+(c-'0');
 			if(end > FP->end)
 				goto Overrun;
@@ -950,7 +1029,8 @@
 }
 
 static void*
-evalnamec(void){
+evalnamec(void)
+{
 	int s, c, new;
 	Name *x, *dot;
 
@@ -998,12 +1078,14 @@
 }
 
 static void*
-evaliarg0(){
+evaliarg0(void)
+{
 	return FP->arg[0];
 }
 
 static void*
-evalconst(void){
+evalconst(void)
+{
 	switch(FP->start[0]){
 	case 0x01:
 		return mki(1);
@@ -1014,7 +1096,8 @@
 }
 
 static void*
-evalbuf(void){
+evalbuf(void)
+{
 	int n, m;
 	uchar *p;
 
@@ -1029,12 +1112,13 @@
 }
 
 static void*
-evalpkg(void){
+evalpkg(void)
+{
 	Package *p;
 	void **x;
 	int n;
 
-	if(p = FP->ref){
+	if((p = FP->ref) != nil){
 		x = FP->aux;
 		if(x >= &p->a[0] && x < &p->a[p->n]){
 			*x++ = FP->arg[0];
@@ -1052,7 +1136,8 @@
 }
 
 static void*
-evalname(void){
+evalname(void)
+{
 	Name *n;
 
 	if(n = FP->arg[0])
@@ -1063,7 +1148,8 @@
 }
 
 static void*
-evalscope(void){
+evalscope(void)
+{
 	Name *n;
 
 	if(n = FP->arg[0])
@@ -1075,7 +1161,8 @@
 }
 
 static void*
-evalalias(void){
+evalalias(void)
+{
 	Name *n;
 
 	if(n = FP->arg[1])
@@ -1084,10 +1171,12 @@
 }
 
 static void*
-evalmet(void){
+evalmet(void)
+{
 	Name *n;
+	Method *m;
+
 	if(n = FP->arg[0]){
-		Method *m;
 		m = mk('m', sizeof(Method));
 		m->narg = ival(FP->arg[1]) & 7;
 		m->start = PC;
@@ -1100,15 +1189,18 @@
 }
 
 static void*
-evalreg(void){
+evalreg(void)
+{
 	Name *n;
-	if(n = FP->arg[0]){
-		Region *r;
+	Region *r;
+
+	if((n = FP->arg[0]) != nil){
 		r = mk('r', sizeof(Region));
 		r->space = ival(FP->arg[1]);
 		r->off = ival(FP->arg[2]);
 		r->len = ival(FP->arg[3]);
 		r->name = n;
+		r->va = nil;
 		n->v = r;
 	}
 	return nil;
@@ -1115,7 +1207,8 @@
 }
 
 static void*
-evalcfield(void){
+evalcfield(void)
+{
 	void *r;
 	Field *f;
 	Name *n;
@@ -1158,7 +1251,8 @@
 }
 
 static void*
-evalfield(void){
+evalfield(void)
+{
 	int flags, bitoff, wa, n;
 	Field *f, *df;
 	Name *d;
@@ -1226,15 +1320,17 @@
 }
 
 static void*
-evalnop(void){
+evalnop(void)
+{
 	return nil;
 }
 
 static void*
-evalbad(void){
+evalbad(void)
+{
 	int i;
 
-	print("bad opcode %p: ", PC);
+	print("aml: bad opcode %p: ", PC);
 	for(i=0; i < 8 && (FP->start+i) < FP->end; i++){
 		if(i > 0)
 			print(" ");
@@ -1248,7 +1344,8 @@
 }
 
 static void*
-evalcond(void){
+evalcond(void)
+{
 	switch(FP->op - optab){
 	case Oif:
 		if(FP <= FB)
@@ -1283,32 +1380,32 @@
 }
 
 static void*
-evalcmp(void){
+evalcmp(void)
+{
 	void *a, *b;
-	int c;
+	int tag, c;
 
-	if((a = FP->arg[0]) == nil)
-		a = mki(0);
-	if((b = FP->arg[1]) == nil)
-		b = mki(0);
-
-	switch(TAG(a)){
-	default:
-		return nil;
-	case 'i':
+	a = FP->arg[0];
+	b = FP->arg[1];
+	if(a == nil || TAG(a) == 'i'){
 		c = ival(a) - ival(b);
-		break;
-	case 's':
-		if(TAG(b) != 's')
-			b = copy('s', b);
-		c = strcmp((char*)a, (char*)b);
-		break;
-	case 'b':
-		if(TAG(b) != 'b')
-			b = copy('b', b);
-		if((c = SIZE(a) - SIZE(b)) == 0)
-			c = memcmp(a, b, SIZE(a));
-		break;
+	} else {
+		tag = TAG(a);
+		if(b == nil || TAG(b) != tag)
+			b = copy(tag, b);
+		if(TAG(b) != tag)
+			return nil;	/* botch */
+		switch(tag){
+		default:
+			return nil;	/* botch */
+		case 's':
+			c = strcmp((char*)a, (char*)b);
+			break;
+		case 'b':
+			if((c = SIZE(a) - SIZE(b)) == 0)
+				c = memcmp(a, b, SIZE(a));
+			break;
+		}
 	}
 
 	switch(FP->op - optab){
@@ -1322,12 +1419,12 @@
 		if(c < 0) return mki(1);
 		break;
 	}
-
 	return nil;
 }
 
 static void*
-evalcall(void){
+evalcall(void)
+{
 	Method *m;
 	Env *e;
 	int i;
@@ -1360,7 +1457,8 @@
 }
 
 static void*
-evalret(void){
+evalret(void)
+{
 	void *r = FP->arg[0];
 	int brk = (FP->op - optab) != Oret;
 	while(--FP >= FB){
@@ -1381,7 +1479,8 @@
 }
 
 static void*
-evalenv(void){
+evalenv(void)
+{
 	Ref *r;
 	Env *e;
 	int c;
@@ -1402,12 +1501,52 @@
 }
 
 static void*
-evalstore(void){
+evalstore(void)
+{
 	return store(FP->arg[0], FP->arg[1]);
 }
 
 static void*
-evalindex(void){
+evalcat(void)
+{
+	void *r, *a, *b;
+	int tag, n, m;
+
+	a = FP->arg[0];
+	b = FP->arg[1];
+	if(a == nil || TAG(a) == 'i')
+		a = copy('b', a);	/* Concat(Int, ???) -> Buf */
+	tag = TAG(a);
+	if(b == nil || TAG(b) != tag)
+		b = copy(tag, b);
+	if(TAG(b) != tag)
+		return nil;	/* botch */
+	switch(tag){
+	default:
+		return nil;	/* botch */
+	case 'b':
+		n = SIZE(a);
+		m = SIZE(b);
+		r = mk('b', n + m);
+		memmove(r, a, n);
+		memmove((uchar*)r + n, b, m);
+		break;
+	case 's':
+		n = strlen((char*)a);
+		m = strlen((char*)b);
+		r = mk('s', n + m + 1);
+		memmove(r, a, n);
+		memmove((char*)r + n, b, m);
+		((char*)r)[n+m] = 0;
+		break;
+	}
+	store(r, FP->arg[2]);
+	return r;
+}
+
+static void*
+evalindex(void)
+{
 	Field *f;
 	void *p;
 	Ref *r;
@@ -1441,7 +1580,8 @@
 }
 
 static void*
-evalcondref(void){
+evalcondref(void)
+{
 	void *s;
 	if((s = FP->arg[0]) == nil)
 		return nil;
@@ -1450,12 +1590,14 @@
 }
 
 static void*
-evalsize(void){
+evalsize(void)
+{
 	return mki(amllen(FP->arg[0]));
 }
 
 static void*
-evalderef(void){
+evalderef(void)
+{
 	void *p;
 
 	if(p = FP->arg[0]){
@@ -1467,8 +1609,12 @@
 }
 
 static void*
-evalarith(void){
-	void *r = nil;
+evalarith(void)
+{
+	uvlong v, d;
+	void *r;
+
+	r = nil;
 	switch(FP->op - optab){
 	case Oadd:
 		r = mki(ival(FP->arg[0]) + ival(FP->arg[1]));
@@ -1476,27 +1622,24 @@
 	case Osub:
 		r = mki(ival(FP->arg[0]) - ival(FP->arg[1]));
 		break;
-	case Omod:
-		{
-			uvlong d;
-			d = ival(FP->arg[1]);
-			r = mki(ival(FP->arg[0]) % d);
-		}
-		break;
 	case Omul:
 		r = mki(ival(FP->arg[0]) * ival(FP->arg[1]));
 		break;
+	case Omod:
 	case Odiv:
-		{
-			uvlong v, d;
-			v = ival(FP->arg[0]);
-			d = ival(FP->arg[1]);
-			r = mki(v / d);
-			if(FP->arg[2])
-				store(mki(v % d), FP->arg[2]);
-			store(r, FP->arg[3]); 
-			return r;
+		v = ival(FP->arg[0]);
+		d = ival(FP->arg[1]);
+		if(d == 0){
+			print("aml: division by zero: PC=%#p\n", PC);
+			return nil;
 		}
+		r = mki(v % d);
+		store(r, FP->arg[2]);
+		if((FP->op - optab) != Odiv)
+			return r;
+		r = mki(v / d);
+		store(r, FP->arg[3]); 
+		return r;
 	case Oshl:
 		r = mki(ival(FP->arg[0]) << ival(FP->arg[1]));
 		break;
@@ -1543,6 +1686,59 @@
 	return r;
 }
 
+static void*
+evalload(void)
+{
+	enum { LenOffset = 4, HdrLen = 36 };
+	uvlong *tid;
+	Region *r;
+	int l;
+
+	tid = nil;
+	if(FP->aux){
+		if(PC >= FP->end){
+			PC = FP->aux;	/* restore */
+			FP->aux = nil;
+			FP->end = PC;
+			tid = mki(1);	/* fake */
+		}
+	} else {
+		store(nil, FP->arg[1]);
+		if(FP->arg[0] == nil)
+			return nil;
+
+		l = rwreg(FP->arg[0], LenOffset, 32, 0, 0);
+		if(l <= HdrLen)
+			return nil;
+
+		FP->aux = PC;	/* save */
+		FP->ref = FP->arg[0];
+		switch(TAG(FP->ref)){
+		case 'b':
+			if(SIZE(FP->ref) < l)
+				return nil;
+			PC = (uchar*)FP->ref + HdrLen;
+			break;
+		case 'r':
+			r = FP->ref;
+			if(r->len < l || r->va == nil)
+				return nil;
+			/* assuming rwreg() has mapped the region */
+			PC = (uchar*)r->va + HdrLen;
+			break;
+		default:
+			return nil;
+		}
+		FP->end = PC + (l - HdrLen);
+		FP->dot = amlroot;
+		FP->env = nil;
+
+		tid = mki(1); /* fake */
+		store(tid, FP->arg[1]);
+	}
+	return tid;
+}
+
 static Op optab[] = {
 	[Obad]		"",			"",		evalbad,
 	[Onop]		"Noop",			"",		evalnop,
@@ -1623,11 +1819,14 @@
 	[Oref]		"RefOf",		"@",		evaliarg0,
 	[Ocref]		"CondRefOf",		"@@",		evalcondref,
 	[Oderef]	"DerefOf",		"@",		evalderef,
+	[Ocat]		"Concatenate",		"**@",		evalcat,
 
 	[Oacq]		"Acquire",		"@2",		evalnop,
 	[Orel]		"Release",		"@",		evalnop,
 	[Ostall]	"Stall",		"i",		evalnop,
 	[Osleep]	"Sleep",		"i",		evalnop,
+	[Oload] 	"Load", 		"*@}", 		evalload,
+	[Ounload]	"Unload",		"@",		evalnop,
 };
 
 static uchar octab1[] = {
@@ -1645,7 +1844,7 @@
 /* 58 */	Onamec,	Onamec,	Onamec,	Obad,	Onamec,	Obad,	Onamec,	Onamec,
 /* 60 */	Oenv,	Oenv,	Oenv,	Oenv,	Oenv,	Oenv,	Oenv,	Oenv,
 /* 68 */	Oenv,	Oenv,	Oenv,	Oenv,	Oenv,	Oenv,	Oenv,	Obad,
-/* 70 */	Ostore,	Oref,	Oadd,	Obad,	Osub,	Oinc,	Odec,	Omul,
+/* 70 */	Ostore,	Oref,	Oadd,	Ocat,	Osub,	Oinc,	Odec,	Omul,
 /* 78 */	Odiv,	Oshl,	Oshr,	Oand,	Onand,	Oor,	Onor,	Oxor,
 /* 80 */	Onot,	Obad,	Obad,	Oderef,	Obad,	Omod,	Obad,	Osize,
 /* 88 */	Oindex,	Obad,	Ocfld4,	Ocfld2,	Ocfld1,	Ocfld0,	Obad,	Ocfld8,
@@ -1670,8 +1869,8 @@
 /* 08 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
 /* 10 */	Obad,	Obad,	Ocref,	Ocfld,	Obad,	Obad,	Obad,	Obad,
 /* 18 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
-/* 20 */	Obad,	Ostall,	Osleep,	Oacq,	Obad,	Obad,	Obad,	Orel,
-/* 28 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
+/* 20 */	Oload,	Ostall,	Osleep,	Oacq,	Obad,	Obad,	Obad,	Orel,
+/* 28 */	Obad,	Obad,	Ounload,Obad,	Obad,	Obad,	Obad,	Obad,
 /* 30 */	Obad,	Odebug,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
 /* 38 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
 /* 40 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
@@ -1701,12 +1900,14 @@
 };
 
 int
-amltag(void *p){
+amltag(void *p)
+{
 	return p ? TAG(p) : 0;
 }
 
 void*
-amlval(void *p){
+amlval(void *p)
+{
 	p = deref(p);
 	if(p && TAG(p) == 'p')
 		p = ((Package*)p)->a;
@@ -1714,12 +1915,14 @@
 }
 
 uvlong
-amlint(void *p){
+amlint(void *p)
+{
 	return ival(p);
 }
 
 int
-amllen(void *p){
+amllen(void *p)
+{
 	while(p){
 		switch(TAG(p)){
 		case 'R':
@@ -1737,7 +1940,8 @@
 }
 
 void
-amlinit(void){
+amlinit(void)
+{
 	Name *n;
 
 	fmtinstall('V', Vfmt);
@@ -1771,7 +1975,8 @@
 }
 
 void
-amlexit(void){
+amlexit(void)
+{
 	amlroot = nil;
 	FP = FB-1;
 	gc();
@@ -1778,17 +1983,20 @@
 }
 
 int
-amlload(uchar *data, int len){
+amlload(uchar *data, int len)
+{
 	return xec(data, data+len, amlroot, nil, nil);
 }
 
 void*
-amlwalk(void *dot, char *name){
+amlwalk(void *dot, char *name)
+{
 	return getname(dot, name, 0);
 }
 
 void
-amlenum(void *dot, char *seg, int (*proc)(void *, void *), void *arg){
+amlenum(void *dot, char *seg, int (*proc)(void *, void *), void *arg)
+{
 	Name *n, *d;
 	int rec;
 
@@ -1806,7 +2014,8 @@
 }
 
 int
-amleval(void *dot, char *fmt, ...){
+amleval(void *dot, char *fmt, ...)
+{
 	va_list a;
 	Method *m;
 	void **r;
@@ -1830,13 +2039,14 @@
 	}
 	r = va_arg(a, void**);
 	va_end(a);
-	if(dot = deref(dot)) switch(TAG(dot)){
-	case 'm':
+	if(dot = deref(dot))
+	if(TAG(dot) == 'm'){
 		m = dot;
 		if(i != m->narg)
 			return -1;
 		return xec(m->start, m->end, forkname(m->name), e, r);
 	}
-	if(r) *r = dot;
+	if(r != nil)
+		*r = dot;
 	return 0;
 }
--- a/sys/src/libc/mips/lock.c
+++ b/sys/src/libc/mips/lock.c
@@ -124,8 +124,9 @@
 				return 1;
 			}
 		}
-		return 0;
+		break;
 	}
+	return 0;
 }
 
 void
@@ -164,6 +165,7 @@
 				return 0;
 			}
 		}
-		return 1;
+		break;
 	}
+	return 1;
 }
--- a/sys/src/libc/port/pool.c
+++ b/sys/src/libc/port/pool.c
@@ -141,10 +141,7 @@
 static ulong	dsize2bsize(Pool*, ulong);
 static ulong	getdsize(Alloc*);
 static Alloc*	trim(Pool*, Alloc*, ulong);
-static Free*	listadd(Free*, Free*);
-static Free*	listdelete(Free*, Free*);
 static void		logstack(Pool*);
-static Free**	ltreewalk(Free**, ulong);
 static void		memmark(void*, int, ulong);
 static Free*	pooladd(Pool*, Alloc*);
 static void*	poolallocl(Pool*, ulong);
@@ -157,9 +154,8 @@
 static void		poolfreel(Pool*, void*);
 static void		poolnewarena(Pool*, ulong);
 static void*	poolreallocl(Pool*, void*, ulong);
-static Free*	treedelete(Free*, Free*);
-static Free*	treeinsert(Free*, Free*);
 static Free*	treelookupgt(Free*, ulong);
+static Free*	treesplay(Free*, ulong);
 
 /*
  * Debugging
@@ -225,76 +221,6 @@
 	
 }
 
-/* ltreewalk: return address of pointer to node of size == size */
-static Free**
-ltreewalk(Free **t, ulong size)
-{
-	Free *f;
-
-	for(;;) {
-		f = *t;
-		if(f == nil || f->magic != FREE_MAGIC)
-			return t;
-		if(size == f->size)
-			return t;
-		if(size < f->size)
-			t = &f->left;
-		else
-			t = &f->right;
-	}
-}
-
-/* treeinsert: insert node into tree */
-static Free*
-treeinsert(Free *tree, Free *node)
-{
-	Free **loc, *repl;
-
-	assert(node != nil /* treeinsert */);
-
-	loc = ltreewalk(&tree, node->size);
-	repl = *loc;
-	if(repl == nil || repl->magic != FREE_MAGIC) {
-		node->left = nil;
-		node->right = nil;
-	} else {	/* replace existing node */
-		node->left = repl->left;
-		node->right = repl->right;
-	}
-	*loc = node;
-	return tree;
-}
-
-/* treedelete: remove node from tree */
-static Free*
-treedelete(Free *tree, Free *node)
-{
-	Free **loc, **lsucc, *succ;
-
-	assert(node != nil /* treedelete */);
-
-	loc = ltreewalk(&tree, node->size);
-	if(*loc != node || node->magic != FREE_MAGIC)
-		return tree;	/* free nodes corrupted */
-
-	if(node->left == nil)
-		*loc = node->right;
-	else if(node->right == nil)
-		*loc = node->left;
-	else {
-		/* have two children, use inorder successor as replacement */
-		for(lsucc = &node->right; (*lsucc)->left; lsucc = &(*lsucc)->left)
-			;
-		succ = *lsucc;
-		*lsucc = succ->right;
-		succ->left = node->left;
-		succ->right = node->right;
-		*loc = succ;
-	}
-	node->left = node->right = Poison;
-	return tree;
-}
-
 /* treelookupgt: find smallest node in tree with size >= size */
 static Free*
 treelookupgt(Free *t, ulong size)
@@ -303,8 +229,9 @@
 
 	lastgood = nil;
 	for(;;) {
-		if(t == nil || t->magic != FREE_MAGIC)
+		if(t == nil)
 			return lastgood;
+		assert(t->magic == FREE_MAGIC);
 		if(size == t->size)
 			return t;
 		if(size < t->size) {
@@ -315,59 +242,64 @@
 	}
 }
 
-/* 
- * List maintenance
- */
-
-/* listadd: add a node to a doubly linked list */
+/* treesplay: splay node of size size to the root and return new root */
 static Free*
-listadd(Free *list, Free *node)
+treesplay(Free *t, ulong size)
 {
-	if(list == nil) {
-		node->next = node;
-		node->prev = node;
-		return node;
-	}
+	Free N, *l, *r, *y;
 
-	node->prev = list->prev;
-	node->next = list;
+	N.left = N.right = nil;
+	l = r = &N;
 
-	node->prev->next = node;
-	node->next->prev = node;
-
-	return list;
-}
-
-/* listdelete: remove node from a doubly linked list */
-static Free*
-listdelete(Free *list, Free *node)
-{
-	if(node->next == node) {	/* singular list */
-		node->prev = node->next = Poison;
-		return nil;
+	for(;;) {
+		assert(t->magic == FREE_MAGIC);
+		if(size < t->size) {
+			y = t->left;
+			if(y != nil) {
+				assert(y->magic == FREE_MAGIC);
+				if(size < y->size) {
+					t->left = y->right;
+					y->right = t;
+					t = y;
+				}
+			}
+			if(t->left == nil)
+				break;
+			r->left = t;
+			r = t;
+			t = t->left;
+		} else if(size > t->size) {
+			y = t->right;
+			if(y != nil) {
+				assert(y->magic == FREE_MAGIC);
+				if(size > y->size) {
+					t->right = y->left;
+					y->left = t;
+					t = y;
+				}
+			}
+			if(t->right == nil)
+				break;
+			l->right = t;
+			l = t;
+			t = t->right;
+		} else
+			break;
 	}
 
-	node->next->prev = node->prev;
-	node->prev->next = node->next;
+	l->right=t->left;
+	r->left=t->right;
+	t->left=N.right;
+	t->right=N.left;
 
-	if(list == node)
-		list = node->next;
-
-	node->prev = node->next = Poison;
-	return list;
+	return t;
 }
 
-/*
- * Pool maintenance
- */
-
 /* pooladd: add anode to the free pool */
 static Free*
 pooladd(Pool *p, Alloc *anode)
 {
-	Free *lst, *olst;
-	Free *node;
-	Free **parent;
+	Free *node, *root;
 
 	antagonism {
 		memmark(_B2D(anode), 0xF7, anode->size-sizeof(Bhdr)-sizeof(Btail));
@@ -375,17 +307,33 @@
 
 	node = (Free*)anode;
 	node->magic = FREE_MAGIC;
-	parent = ltreewalk(&p->freeroot, node->size);
-	olst = *parent;
-	if(olst != nil && olst->magic != FREE_MAGIC){
-		/* corruption of free nodes */
-		poolcheckl(p);
-		olst = *parent = nil;
+	node->left = node->right = nil;
+	node->next = node->prev = node;
+
+	if(p->freeroot != nil) {
+		root = treesplay(p->freeroot, node->size);
+		if(root->size > node->size) {
+			node->left = root->left;
+			node->right = root;
+			root->left = nil;
+		} else if(root->size < node->size) {
+			node->right = root->right;
+			node->left = root;
+			root->right = nil;
+		} else {
+			node->left = root->left;
+			node->right = root->right;
+			root->left = root->right = Poison;
+
+			node->prev = root->prev;
+			node->next = root;
+			node->prev->next = node;
+			node->next->prev = node;
+		}
 	}
-	lst = listadd(olst, node);
-	if(olst != lst)	/* need to update tree */
-		*parent = treeinsert(*parent, lst);
+	p->freeroot = node;
 	p->curfree += node->size;
+
 	return node;
 }
 
@@ -393,30 +341,38 @@
 static Alloc*
 pooldel(Pool *p, Free *node)
 {
-	Free *lst, *olst;
-	Free **parent;
+	Free *root;
 
-	parent = ltreewalk(&p->freeroot, node->size);
-	olst = *parent;
-	if(olst == nil || olst->magic != FREE_MAGIC){
-		/* corruption of free nodes */
-		poolcheckl(p);
-		*parent = nil;
+	root = treesplay(p->freeroot, node->size);
+	if(node == root && node->next == node) {
+		if(node->left == nil)
+			root = node->right;
+		else {
+			root = treesplay(node->left, node->size);
+			assert(root->right == nil);
+			root->right = node->right;
+		}
 	} else {
-		lst = listdelete(olst, node);
-		if(lst == nil)
-			*parent = treedelete(*parent, olst);
-		else if(lst != olst)
-			*parent = treeinsert(*parent, lst);
+		if(node == root) {
+			root = node->next;
+			root->left = node->left;
+			root->right = node->right;
+		}
+		assert(root->magic == FREE_MAGIC && root->size == node->size);
+		node->next->prev = node->prev;
+		node->prev->next = node->next;
 	}
-	node->left = node->right = Poison;
+	p->freeroot = root;
 	p->curfree -= node->size;
 
+	node->left = node->right = node->next = node->prev = Poison;
+
 	antagonism {
 		memmark(_B2D(node), 0xF9, node->size-sizeof(Bhdr)-sizeof(Btail));
 	}
 
 	node->magic = UNALLOC_MAGIC;
+
 	return (Alloc*)node;
 }
 
--- a/sys/src/libdraw/alloc.c
+++ b/sys/src/libdraw/alloc.c
@@ -26,6 +26,10 @@
 	err = 0;
 	i = 0;
 
+	if(badrect(r)){
+		werrstr("bad rectangle");
+		return nil;
+	}
 	if(chan == 0){
 		werrstr("bad channel descriptor");
 		return nil;
--- /dev/null
+++ b/sys/src/libdraw/badrect.c
@@ -1,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+/*
+ * check for zero, negative size or insanely huge rectangle.
+ */
+int
+badrect(Rectangle r)
+{
+	int x, y;
+	uint z;
+
+	x = Dx(r);
+	y = Dy(r);
+	if(x > 0 && y > 0){
+		z = x*y;
+		if(z/x == y && z < 0x10000000)
+			return 0;
+	}
+	return 1;
+}
--- a/sys/src/libdraw/emenuhit.c
+++ b/sys/src/libdraw/emenuhit.c
@@ -230,7 +230,8 @@
 	border(screen, menur, Blackborder, bord, ZP);
 	save = allocimage(display, menurect(textr, 0), screen->chan, 0, -1);
 	r = menurect(textr, lasti);
-	emoveto(divpt(addpt(r.min, r.max), 2));
+	if(pt.x || pt.y)
+		emoveto(divpt(addpt(r.min, r.max), 2));
 	menupaint(menu, textr, off, nitemdrawn);
 	if(scrolling)
 		menuscrollpaint(scrollr, off, nitem, nitemdrawn);
--- a/sys/src/libdraw/menuhit.c
+++ b/sys/src/libdraw/menuhit.c
@@ -234,7 +234,8 @@
 	border(b, menur, Blackborder, bord, ZP);
 	save = allocimage(display, menurect(textr, 0), screen->chan, 0, -1);
 	r = menurect(textr, lasti);
-	moveto(mc, divpt(addpt(r.min, r.max), 2));
+	if(pt.x || pt.y)
+		moveto(mc, divpt(addpt(r.min, r.max), 2));
 	menupaint(b, menu, textr, off, nitemdrawn);
 	if(scrolling)
 		menuscrollpaint(b, scrollr, off, nitem, nitemdrawn);
--- a/sys/src/libdraw/mkfile
+++ b/sys/src/libdraw/mkfile
@@ -6,6 +6,7 @@
 	alloc.$O\
 	allocimagemix.$O\
 	arith.$O\
+	badrect.$O\
 	bezier.$O\
 	border.$O\
 	buildfont.$O\
--- a/sys/src/libdraw/window.c
+++ b/sys/src/libdraw/window.c
@@ -22,14 +22,11 @@
 	s = malloc(sizeof(Screen));
 	if(s == 0)
 		return 0;
-	SET(id);
 	for(try=0; try<25; try++){
 		/* loop until find a free id */
 		a = bufimage(d, 1+4+4+4+1);
-		if(a == 0){
-			free(s);
-			return 0;
-		}
+		if(a == 0)
+			break;
 		id = ++screenid;
 		a[0] = 'A';
 		BPLONG(a+1, id);
@@ -37,8 +34,12 @@
 		BPLONG(a+9, fill->id);
 		a[13] = public;
 		if(flushimage(d, 0) != -1)
-			break;
+			goto Found;
 	}
+	free(s);
+	return 0;
+
+    Found:
 	s->display = d;
 	s->id = id;
 	s->image = image;
--- a/sys/src/libmemdraw/alloc.c
+++ b/sys/src/libmemdraw/alloc.c
@@ -27,14 +27,14 @@
 	ulong l;
 	Memimage *i;
 
-	if(Dx(r) <= 0 || Dy(r) <= 0){
-		werrstr("bad rectangle %R", r);
-		return nil;
-	}
 	if((d = chantodepth(chan)) == 0) {
 		werrstr("bad channel descriptor %.8lux", chan);
 		return nil;
 	}
+	if(badrect(r)){
+		werrstr("bad rectangle %R", r);
+		return nil;
+	}
 
 	l = wordsperline(r, d);
 
@@ -76,8 +76,13 @@
 		werrstr("bad channel descriptor %.8lux", chan);
 		return nil;
 	}
+	if(badrect(r)){
+		werrstr("bad rectangle %R", r);
+		return nil;
+	}
 
 	l = wordsperline(r, d);
+
 	nw = l*Dy(r);
 	md = malloc(sizeof(Memdata));
 	if(md == nil)
--- a/sys/src/libmemdraw/cload.c
+++ b/sys/src/libmemdraw/cload.c
@@ -9,7 +9,7 @@
 	int y, bpl, c, cnt, offs;
 	uchar mem[NMEM], *memp, *omemp, *emem, *linep, *elinep, *u, *eu;
 
-	if(!rectinrect(r, i->r))
+	if(badrect(r) || !rectinrect(r, i->r))
 		return -1;
 	bpl = bytesperline(r, i->depth);
 	u = data;
--- a/sys/src/libmemdraw/draw.c
+++ b/sys/src/libmemdraw/draw.c
@@ -228,7 +228,7 @@
 	int splitcoords;
 	Rectangle omr;
 
-	if(r->min.x>=r->max.x || r->min.y>=r->max.y)
+	if(badrect(*r))
 		return 0;
 	splitcoords = (p0->x!=p1->x) || (p0->y!=p1->y);
 	/* clip to destination */
@@ -1541,8 +1541,8 @@
 static void
 writecmap(Param *p, uchar *w, Buffer src)
 {
-	uchar *cmap, *red, *grn, *blu;
-	int i, dx, delta;
+	uchar *cmap, *red, *grn, *blu, *alpha;
+	int i, dx, delta, a, m;
 
 	cmap = p->img->cmap->rgb2cmap;
 	
@@ -1552,8 +1552,20 @@
 	blu = src.blu;
 
 	dx = p->dx;
-	for(i=0; i<dx; i++, red+=delta, grn+=delta, blu+=delta)
-		*w++ = cmap[(*red>>4)*256+(*grn>>4)*16+(*blu>>4)];
+	if(p->img->flags&Falpha){
+		alpha = src.alpha;
+		m = p->img->shift[CMap]/8;
+		a = p->img->shift[CAlpha]/8;
+		for(i=0; i<dx; i++, red+=delta, grn+=delta, blu+=delta, w+=2){
+			w[a] = *alpha;
+			if(alpha != &ones)
+				alpha+=delta;
+			w[m] = cmap[(*red>>4)*256+(*grn>>4)*16+(*blu>>4)];
+		}
+	} else {
+		for(i=0; i<dx; i++, red+=delta, grn+=delta, blu+=delta)
+			*w++ = cmap[(*red>>4)*256+(*grn>>4)*16+(*blu>>4)];
+	}
 }
 
 #define DBG if(0)
@@ -1752,7 +1764,7 @@
 {
 	if(img->depth < 8)
 		return writenbit;
-	if(img->chan == CMAP8)
+	if(img->nbits[CMap] == 8)
 		return writecmap;
 	return writebyte;
 }
--- a/sys/src/libmemdraw/load.c
+++ b/sys/src/libmemdraw/load.c
@@ -10,7 +10,7 @@
 	Memdrawparam par;
 	uchar *q;
 
-	if(!rectinrect(r, i->r))
+	if(badrect(r) || !rectinrect(r, i->r))
 		return -1;
 
 	memset(&par, 0, sizeof par);
--- a/sys/src/libmemdraw/unload.c
+++ b/sys/src/libmemdraw/unload.c
@@ -9,7 +9,7 @@
 	int y, l;
 	uchar *q;
 
-	if(!rectinrect(r, i->r))
+	if(badrect(r) || !rectinrect(r, i->r))
 		return -1;
 	l = bytesperline(r, i->depth);
 	if(ndata < l*Dy(r))
--