Bläddra i källkod

Plan 9 from Bell Labs Fourth Edition 2003-12-02

David du Colombier 20 år sedan
förälder
incheckning
40ef900911
96 ändrade filer med 7770 tillägg och 127 borttagningar
  1. 108 26
      dist/replica/plan9.db
  2. 110 0
      dist/replica/plan9.log
  3. 13 4
      rc/bin/yesterday
  4. 6 0
      sys/include/ape/cursor.h
  5. 521 0
      sys/include/ape/draw.h
  6. 1 0
      sys/include/ape/errno.h
  7. 6 0
      sys/include/ape/event.h
  8. 107 0
      sys/include/ape/fmt.h
  9. 6 0
      sys/include/ape/keyboard.h
  10. 6 0
      sys/include/ape/mouse.h
  11. 36 0
      sys/include/ape/qlock.h
  12. 60 0
      sys/include/ape/utf.h
  13. 2 2
      sys/lib/plumb/basic
  14. 3 0
      sys/man/1/tar
  15. 7 1
      sys/man/1/yesterday
  16. 16 8
      sys/src/9/ip/tcp.c
  17. 7 2
      sys/src/9/port/qio.c
  18. 4 0
      sys/src/ape/lib/ap/plan9/9errstr.c
  19. 14 0
      sys/src/ape/lib/ap/plan9/9mallocz.c
  20. 21 0
      sys/src/ape/lib/ap/plan9/9readn.c
  21. 3 0
      sys/src/ape/lib/ap/plan9/mkfile
  22. 365 0
      sys/src/ape/lib/ap/plan9/qlock.c
  23. 203 0
      sys/src/ape/lib/draw/colors.c
  24. 135 0
      sys/src/ape/lib/draw/libc.h
  25. 78 0
      sys/src/ape/lib/draw/mkfile
  26. 85 0
      sys/src/ape/lib/fmt/charstod.c
  27. 558 0
      sys/src/ape/lib/fmt/dofmt.c
  28. 61 0
      sys/src/ape/lib/fmt/dorfmt.c
  29. 28 0
      sys/src/ape/lib/fmt/errfmt.c
  30. 610 0
      sys/src/ape/lib/fmt/fltfmt.c
  31. 221 0
      sys/src/ape/lib/fmt/fmt.c
  32. 121 0
      sys/src/ape/lib/fmt/fmtdef.h
  33. 46 0
      sys/src/ape/lib/fmt/fmtfd.c
  34. 33 0
      sys/src/ape/lib/fmt/fmtfdflush.c
  35. 28 0
      sys/src/ape/lib/fmt/fmtlock.c
  36. 47 0
      sys/src/ape/lib/fmt/fmtprint.c
  37. 262 0
      sys/src/ape/lib/fmt/fmtquote.c
  38. 40 0
      sys/src/ape/lib/fmt/fmtrune.c
  39. 65 0
      sys/src/ape/lib/fmt/fmtstr.c
  40. 46 0
      sys/src/ape/lib/fmt/fmtvprint.c
  41. 28 0
      sys/src/ape/lib/fmt/fprint.c
  42. 59 0
      sys/src/ape/lib/fmt/mkfile
  43. 4 0
      sys/src/ape/lib/fmt/nan.h
  44. 76 0
      sys/src/ape/lib/fmt/nan64.c
  45. 57 0
      sys/src/ape/lib/fmt/pow10.c
  46. 28 0
      sys/src/ape/lib/fmt/print.c
  47. 65 0
      sys/src/ape/lib/fmt/runefmtstr.c
  48. 30 0
      sys/src/ape/lib/fmt/runeseprint.c
  49. 30 0
      sys/src/ape/lib/fmt/runesmprint.c
  50. 31 0
      sys/src/ape/lib/fmt/runesnprint.c
  51. 30 0
      sys/src/ape/lib/fmt/runesprint.c
  52. 39 0
      sys/src/ape/lib/fmt/runevseprint.c
  53. 37 0
      sys/src/ape/lib/fmt/runevsmprint.c
  54. 38 0
      sys/src/ape/lib/fmt/runevsnprint.c
  55. 27 0
      sys/src/ape/lib/fmt/seprint.c
  56. 27 0
      sys/src/ape/lib/fmt/smprint.c
  57. 28 0
      sys/src/ape/lib/fmt/snprint.c
  58. 27 0
      sys/src/ape/lib/fmt/sprint.c
  59. 539 0
      sys/src/ape/lib/fmt/strtod.c
  60. 4 0
      sys/src/ape/lib/fmt/strtod.h
  61. 39 0
      sys/src/ape/lib/fmt/test.c
  62. 31 0
      sys/src/ape/lib/fmt/vfprint.c
  63. 37 0
      sys/src/ape/lib/fmt/vseprint.c
  64. 36 0
      sys/src/ape/lib/fmt/vsmprint.c
  65. 37 0
      sys/src/ape/lib/fmt/vsnprint.c
  66. 16 0
      sys/src/ape/lib/fmt/werrstr.c
  67. 1 1
      sys/src/ape/lib/mkfile
  68. 17 0
      sys/src/ape/lib/utf/lib9.h
  69. 37 0
      sys/src/ape/lib/utf/mkfile
  70. 177 0
      sys/src/ape/lib/utf/rune.c
  71. 25 0
      sys/src/ape/lib/utf/runestrcat.c
  72. 35 0
      sys/src/ape/lib/utf/runestrchr.c
  73. 35 0
      sys/src/ape/lib/utf/runestrcmp.c
  74. 28 0
      sys/src/ape/lib/utf/runestrcpy.c
  75. 30 0
      sys/src/ape/lib/utf/runestrdup.c
  76. 32 0
      sys/src/ape/lib/utf/runestrecpy.c
  77. 24 0
      sys/src/ape/lib/utf/runestrlen.c
  78. 32 0
      sys/src/ape/lib/utf/runestrncat.c
  79. 37 0
      sys/src/ape/lib/utf/runestrncmp.c
  80. 33 0
      sys/src/ape/lib/utf/runestrncpy.c
  81. 30 0
      sys/src/ape/lib/utf/runestrrchr.c
  82. 44 0
      sys/src/ape/lib/utf/runestrstr.c
  83. 1152 0
      sys/src/ape/lib/utf/runetype.c
  84. 14 0
      sys/src/ape/lib/utf/utfdef.h
  85. 51 0
      sys/src/ape/lib/utf/utfecpy.c
  86. 38 0
      sys/src/ape/lib/utf/utflen.c
  87. 41 0
      sys/src/ape/lib/utf/utfnlen.c
  88. 46 0
      sys/src/ape/lib/utf/utfrrune.c
  89. 45 0
      sys/src/ape/lib/utf/utfrune.c
  90. 41 0
      sys/src/ape/lib/utf/utfutf.c
  91. 1 1
      sys/src/cmd/disk/mkext.c
  92. 2 2
      sys/src/cmd/hget.c
  93. 1 1
      sys/src/cmd/ip/httpd/webls.c
  94. 2 2
      sys/src/cmd/ratfs/misc.c
  95. 198 56
      sys/src/cmd/tar.c
  96. 32 21
      sys/src/cmd/upas/common/libsys.c

+ 108 - 26
dist/replica/plan9.db

@@ -90,7 +90,7 @@
 386/bin/aux/X509gen - 775 sys sys 1048341826 129186
 386/bin/aux/accupoint - 775 sys sys 1064598059 40841
 386/bin/aux/acidleak - 775 sys sys 1064598060 67816
-386/bin/aux/antiword - 775 sys sys 1069793954 511478
+386/bin/aux/antiword - 775 sys sys 1070288096 511478
 386/bin/aux/apm - 775 sys sys 1064598061 175778
 386/bin/aux/astarld - 775 sys sys 1064598061 63276
 386/bin/aux/cddb - 775 sys sys 1064598062 70192
@@ -230,9 +230,9 @@
 386/bin/fortune - 775 sys sys 1064598140 66084
 386/bin/fossil - 20000000775 sys sys 1042005470 0
 386/bin/fossil/conf - 775 sys sys 1056364255 1497
-386/bin/fossil/flchk - 775 sys sys 1068717500 232696
-386/bin/fossil/flfmt - 775 sys sys 1068717501 230661
-386/bin/fossil/fossil - 775 sys sys 1068717502 345764
+386/bin/fossil/flchk - 775 sys sys 1070288097 232862
+386/bin/fossil/flfmt - 775 sys sys 1070288098 230827
+386/bin/fossil/fossil - 775 sys sys 1070288099 345606
 386/bin/freq - 775 sys sys 1064598145 60197
 386/bin/fs - 20000000775 sys sys 954380769 0
 386/bin/fs/32vfs - 775 sys sys 1064598146 95907
@@ -252,7 +252,7 @@
 386/bin/gunzip - 775 sys sys 1064598211 79675
 386/bin/gzip - 775 sys sys 1064598211 83770
 386/bin/hayes - 775 sys sys 1064598212 63307
-386/bin/hget - 775 sys sys 1068385800 222506
+386/bin/hget - 775 sys sys 1070286807 222506
 386/bin/history - 775 sys sys 1064598214 71890
 386/bin/hoc - 775 sys sys 1064598215 98708
 386/bin/html2ms - 775 sys sys 1064598215 64484
@@ -284,8 +284,8 @@
 386/bin/ip/ppp - 775 sys sys 1069592368 217323
 386/bin/ip/pppoe - 775 sys sys 1064598236 75233
 386/bin/ip/pptp - 775 sys sys 1064598237 123773
-386/bin/ip/pptpd - 775 sys sys 1064598238 124288
-386/bin/ip/rarpd - 775 sys sys 1069592368 107845
+386/bin/ip/pptpd - 775 sys sys 1070288105 124153
+386/bin/ip/rarpd - 775 sys sys 1070288105 107748
 386/bin/ip/rexexec - 775 sys sys 1064598240 87398
 386/bin/ip/rip - 775 sys sys 1064598241 89573
 386/bin/ip/rlogind - 775 sys sys 1064598241 66139
@@ -316,10 +316,10 @@
 386/bin/ndb - 20000000775 sys sys 985743147 0
 386/bin/ndb/cs - 775 sys sys 1069766560 144581
 386/bin/ndb/csquery - 775 sys sys 1064598259 59890
-386/bin/ndb/dns - 775 sys sys 1069592370 218704
-386/bin/ndb/dnsdebug - 775 sys sys 1069592371 191053
+386/bin/ndb/dns - 775 sys sys 1070288106 218607
+386/bin/ndb/dnsdebug - 775 sys sys 1070288107 190956
 386/bin/ndb/dnsquery - 775 sys sys 1064598263 62194
-386/bin/ndb/dnstcp - 775 sys sys 1069592372 189767
+386/bin/ndb/dnstcp - 775 sys sys 1070288108 189670
 386/bin/ndb/ipquery - 775 sys sys 1069592372 91891
 386/bin/ndb/mkdb - 775 sys sys 1064598265 62386
 386/bin/ndb/mkhash - 775 sys sys 1069592372 82479
@@ -527,7 +527,7 @@
 386/lib/libgeometry.a - 664 sys sys 1068385839 50806
 386/lib/libhtml.a - 664 sys sys 1068385840 220944
 386/lib/libhttpd.a - 664 sys sys 1068385841 98914
-386/lib/libip.a - 664 sys sys 1069766546 34142
+386/lib/libip.a - 664 sys sys 1070288108 34142
 386/lib/libl.a - 664 sys sys 1068385841 5412
 386/lib/libmach.a - 664 sys sys 1068558293 748290
 386/lib/libmemdraw.a - 664 sys sys 1068385844 292604
@@ -3098,7 +3098,7 @@ rc/bin/whois - 775 sys sys 945617210 189
 rc/bin/window - 775 sys sys 1045504108 1742
 rc/bin/wloc - 775 sys sys 969512017 191
 rc/bin/wurl2txt - 755 sys sys 1017431148 296
-rc/bin/yesterday - 775 sys sys 1044909161 2408
+rc/bin/yesterday - 775 sys sys 1070288432 2541
 rc/lib - 20000000775 sys sys 948037639 0
 rc/lib/rcmain - 664 sys sys 984696976 579
 sparc - 20000000775 sys sys 947991046 0
@@ -3319,12 +3319,17 @@ sys/include/ape/arpa/inet.h - 664 sys sys 944948760 4717
 sys/include/ape/assert.h - 664 sys sys 944948760 287
 sys/include/ape/bsd.h - 664 sys sys 944948760 1087
 sys/include/ape/ctype.h - 664 sys sys 1014927763 1654
+sys/include/ape/cursor.h - 664 sys sys 1070327358 118
 sys/include/ape/dirent.h - 664 sys sys 1035117682 717
-sys/include/ape/errno.h - 664 sys sys 944948760 1505
+sys/include/ape/draw.h - 664 sys sys 1070327359 15998
+sys/include/ape/errno.h - 664 sys sys 1070327359 1525
 sys/include/ape/error.h - 664 sys sys 944948759 356
+sys/include/ape/event.h - 664 sys sys 1070327358 117
 sys/include/ape/fcntl.h - 664 sys sys 969500385 1231
+sys/include/ape/fmt.h - 664 sys sys 1070327361 3276
 sys/include/ape/grp.h - 664 sys sys 944948759 389
 sys/include/ape/inttypes.h - 664 sys sys 1038237535 452
+sys/include/ape/keyboard.h - 664 sys sys 1070327358 120
 sys/include/ape/lib9.h - 664 sys sys 1014927764 1626
 sys/include/ape/libl.h - 664 sys sys 944948760 331
 sys/include/ape/libnet.h - 664 sys sys 944948760 487
@@ -3332,11 +3337,13 @@ sys/include/ape/libv.h - 664 sys sys 944948760 716
 sys/include/ape/limits.h - 664 sys sys 1014927764 2491
 sys/include/ape/locale.h - 664 sys sys 944948759 796
 sys/include/ape/lock.h - 664 sys sys 946054014 363
+sys/include/ape/mouse.h - 664 sys sys 1070327358 117
 sys/include/ape/netdb.h - 664 sys sys 1014927764 3907
 sys/include/ape/netinet - 20000000775 sys sys 944948759 0
 sys/include/ape/netinet/in.h - 664 sys sys 944948759 4717
 sys/include/ape/netinet/tcp.h - 664 sys sys 944948759 0
 sys/include/ape/pwd.h - 664 sys sys 944948759 423
+sys/include/ape/qlock.h - 664 sys sys 1070327358 474
 sys/include/ape/regexp.h - 664 sys sys 988225292 1559
 sys/include/ape/select.h - 664 sys sys 944948760 779
 sys/include/ape/setjmp.h - 664 sys sys 944948760 421
@@ -3365,6 +3372,7 @@ sys/include/ape/termios.h - 664 sys sys 944948760 2745
 sys/include/ape/time.h - 664 sys sys 944948760 1016
 sys/include/ape/u.h - 664 sys sys 1038237538 399
 sys/include/ape/unistd.h - 664 sys sys 944948760 3917
+sys/include/ape/utf.h - 664 sys sys 1070327359 1595
 sys/include/ape/utime.h - 664 sys sys 944948760 259
 sys/include/ar.h - 664 sys sys 1014929061 244
 sys/include/auth.h - 664 sys sys 1048614957 3469
@@ -3939,7 +3947,7 @@ sys/lib/man/trademarks - 664 sys sys 958527089 1838
 sys/lib/mimetype - 664 sys sys 1064393944 5563
 sys/lib/newuser - 775 sys sys 1018386991 1229
 sys/lib/plumb - 20000000775 sys sys 944957365 0
-sys/lib/plumb/basic - 664 sys sys 1069794119 2906
+sys/lib/plumb/basic - 664 sys sys 1070330856 2930
 sys/lib/plumb/fileaddr - 664 sys sys 944957365 88
 sys/lib/postscript - 20000000775 sys sys 950322802 0
 sys/lib/postscript/afm - 20000000775 sys sys 950322802 0
@@ -4690,7 +4698,7 @@ sys/man/1/sum - 664 sys sys 984772442 1390
 sys/man/1/syscall - 664 sys sys 1016466457 1439
 sys/man/1/tail - 664 sys sys 1017679307 1413
 sys/man/1/tapefs - 664 sys sys 944959675 1731
-sys/man/1/tar - 664 sys sys 988225296 2317
+sys/man/1/tar - 664 sys sys 1070287474 2363
 sys/man/1/tbl - 664 sys sys 944959674 4308
 sys/man/1/tcs - 664 sys sys 952627441 2575
 sys/man/1/tee - 664 sys sys 969499886 351
@@ -4713,7 +4721,7 @@ sys/man/1/wc - 664 sys sys 944959675 908
 sys/man/1/who - 664 sys sys 944959674 332
 sys/man/1/xd - 664 sys sys 944959674 1517
 sys/man/1/yacc - 664 sys sys 944959676 3273
-sys/man/1/yesterday - 664 sys sys 1044909169 2202
+sys/man/1/yesterday - 664 sys sys 1070288431 2348
 sys/man/2 - 20000000775 sys sys 1017423721 0
 sys/man/2/0intro - 664 sys sys 1044724217 11574
 sys/man/2/9p - 664 sys sys 1046886431 15221
@@ -5215,7 +5223,7 @@ sys/src/9/ip/nullmedium.c - 664 sys sys 1022588099 491
 sys/src/9/ip/pktmedium.c - 664 sys sys 1045063516 1355
 sys/src/9/ip/ptclbsum.c - 664 sys sys 1022588099 1243
 sys/src/9/ip/rudp.c - 664 sys sys 1055700790 21415
-sys/src/9/ip/tcp.c - 664 sys sys 1067722615 65141
+sys/src/9/ip/tcp.c - 664 sys sys 1070287819 65399
 sys/src/9/ip/tripmedium.c - 664 sys sys 1045063515 7136
 sys/src/9/ip/udp.c - 664 sys sys 1055700791 13069
 sys/src/9/mkfile - 664 sys sys 1063857477 205
@@ -5439,7 +5447,7 @@ sys/src/9/port/portfns.h - 664 sys sys 1068215525 11376
 sys/src/9/port/portmkfile - 664 sys sys 1067722766 2098
 sys/src/9/port/print.c - 664 sys sys 1014931178 227
 sys/src/9/port/proc.c - 664 sys sys 1068393121 24882
-sys/src/9/port/qio.c - 664 sys sys 1067953182 23406
+sys/src/9/port/qio.c - 664 sys sys 1070287837 23562
 sys/src/9/port/qlock.c - 664 sys sys 1067722765 3196
 sys/src/9/port/rdb.c - 664 sys sys 1018721202 1698
 sys/src/9/port/realtimesub.c - 664 sys sys 1055688540 9185
@@ -5984,7 +5992,10 @@ sys/src/ape/lib/ap/mips/vlop.s - 664 sys sys 1014921989 239
 sys/src/ape/lib/ap/mips/vlrt.c - 664 sys sys 1014921989 9001
 sys/src/ape/lib/ap/mkfile - 664 sys sys 1014921988 244
 sys/src/ape/lib/ap/plan9 - 20000000775 sys sys 1014921986 0
+sys/src/ape/lib/ap/plan9/9errstr.c - 664 sys sys 1070330880 31
+sys/src/ape/lib/ap/plan9/9mallocz.c - 664 sys sys 1070330880 148
 sys/src/ape/lib/ap/plan9/9read.c - 664 sys sys 1014921986 169
+sys/src/ape/lib/ap/plan9/9readn.c - 664 sys sys 1070330880 221
 sys/src/ape/lib/ap/plan9/9wait.c - 664 sys sys 1014921986 1517
 sys/src/ape/lib/ap/plan9/9write.c - 664 sys sys 1014921986 171
 sys/src/ape/lib/ap/plan9/_buf.c - 664 sys sys 1048644332 9661
@@ -6050,13 +6061,14 @@ sys/src/ape/lib/ap/plan9/link.c - 664 sys sys 1014921984 168
 sys/src/ape/lib/ap/plan9/lseek.c - 664 sys sys 1038237526 358
 sys/src/ape/lib/ap/plan9/malloc.c - 664 sys sys 1014921985 1957
 sys/src/ape/lib/ap/plan9/mkdir.c - 664 sys sys 1014921984 353
-sys/src/ape/lib/ap/plan9/mkfile - 664 sys sys 1030558746 1320
+sys/src/ape/lib/ap/plan9/mkfile - 664 sys sys 1070330880 1357
 sys/src/ape/lib/ap/plan9/nan.c - 664 sys sys 1014921985 614
 sys/src/ape/lib/ap/plan9/open.c - 664 sys sys 1048644335 1088
 sys/src/ape/lib/ap/plan9/opendir.c - 664 sys sys 1014921984 1881
 sys/src/ape/lib/ap/plan9/pause.c - 664 sys sys 1014921985 124
 sys/src/ape/lib/ap/plan9/pipe.c - 664 sys sys 1014921984 476
 sys/src/ape/lib/ap/plan9/profile.c - 664 sys sys 1014921985 2521
+sys/src/ape/lib/ap/plan9/qlock.c - 664 sys sys 1070330880 5124
 sys/src/ape/lib/ap/plan9/raise.c - 664 sys sys 1014921985 120
 sys/src/ape/lib/ap/plan9/read.c - 664 sys sys 1014921984 767
 sys/src/ape/lib/ap/plan9/rename.c - 664 sys sys 1046643006 1282
@@ -6243,6 +6255,52 @@ sys/src/ape/lib/bsd/strcasecmp.c - 664 sys sys 1051635385 343
 sys/src/ape/lib/bsd/strdup.c - 664 sys sys 1014921995 182
 sys/src/ape/lib/bsd/strncasecmp.c - 664 sys sys 1051635385 385
 sys/src/ape/lib/bsd/writev.c - 664 sys sys 1014921995 908
+sys/src/ape/lib/draw - 20000000775 sys sys 1070327097 0
+sys/src/ape/lib/draw/colors.c - 664 sys sys 1070327097 3607
+sys/src/ape/lib/draw/libc.h - 664 sys sys 1070327097 3476
+sys/src/ape/lib/draw/mkfile - 664 sys sys 1070327097 1176
+sys/src/ape/lib/fmt - 20000000775 sys sys 1070327381 0
+sys/src/ape/lib/fmt/charstod.c - 664 sys sys 1070327066 1995
+sys/src/ape/lib/fmt/dofmt.c - 664 sys sys 1070327066 10020
+sys/src/ape/lib/fmt/dorfmt.c - 664 sys sys 1070327066 1576
+sys/src/ape/lib/fmt/errfmt.c - 664 sys sys 1070327066 948
+sys/src/ape/lib/fmt/fltfmt.c - 664 sys sys 1070327066 10300
+sys/src/ape/lib/fmt/fmt.c - 664 sys sys 1070327067 4131
+sys/src/ape/lib/fmt/fmtdef.h - 664 sys sys 1070327067 3100
+sys/src/ape/lib/fmt/fmtfd.c - 664 sys sys 1070327067 1302
+sys/src/ape/lib/fmt/fmtfdflush.c - 664 sys sys 1070327067 1077
+sys/src/ape/lib/fmt/fmtlock.c - 664 sys sys 1070327067 868
+sys/src/ape/lib/fmt/fmtprint.c - 664 sys sys 1070327068 1257
+sys/src/ape/lib/fmt/fmtquote.c - 664 sys sys 1070327068 5582
+sys/src/ape/lib/fmt/fmtrune.c - 664 sys sys 1070327068 1120
+sys/src/ape/lib/fmt/fmtstr.c - 664 sys sys 1070327068 1509
+sys/src/ape/lib/fmt/fmtvprint.c - 664 sys sys 1070327068 1241
+sys/src/ape/lib/fmt/fprint.c - 664 sys sys 1070327069 946
+sys/src/ape/lib/fmt/mkfile - 664 sys sys 1070327069 794
+sys/src/ape/lib/fmt/nan.h - 664 sys sys 1070327069 114
+sys/src/ape/lib/fmt/nan64.c - 664 sys sys 1070327069 1176
+sys/src/ape/lib/fmt/pow10.c - 664 sys sys 1070327069 1987
+sys/src/ape/lib/fmt/print.c - 664 sys sys 1070327069 936
+sys/src/ape/lib/fmt/runefmtstr.c - 664 sys sys 1070327070 1544
+sys/src/ape/lib/fmt/runeseprint.c - 664 sys sys 1070327070 1016
+sys/src/ape/lib/fmt/runesmprint.c - 664 sys sys 1070327070 988
+sys/src/ape/lib/fmt/runesnprint.c - 664 sys sys 1070327070 1015
+sys/src/ape/lib/fmt/runesprint.c - 664 sys sys 1070327070 1004
+sys/src/ape/lib/fmt/runevseprint.c - 664 sys sys 1070327071 1132
+sys/src/ape/lib/fmt/runevsmprint.c - 664 sys sys 1070327071 1109
+sys/src/ape/lib/fmt/runevsnprint.c - 664 sys sys 1070327071 1142
+sys/src/ape/lib/fmt/seprint.c - 664 sys sys 1070327071 951
+sys/src/ape/lib/fmt/smprint.c - 664 sys sys 1070327071 923
+sys/src/ape/lib/fmt/snprint.c - 664 sys sys 1070327071 950
+sys/src/ape/lib/fmt/sprint.c - 664 sys sys 1070327072 991
+sys/src/ape/lib/fmt/strtod.c - 664 sys sys 1070327072 9359
+sys/src/ape/lib/fmt/strtod.h - 664 sys sys 1070327072 120
+sys/src/ape/lib/fmt/test.c - 664 sys sys 1070327072 1423
+sys/src/ape/lib/fmt/vfprint.c - 664 sys sys 1070327072 1026
+sys/src/ape/lib/fmt/vseprint.c - 664 sys sys 1070327072 1089
+sys/src/ape/lib/fmt/vsmprint.c - 664 sys sys 1070327073 1084
+sys/src/ape/lib/fmt/vsnprint.c - 664 sys sys 1070327073 1119
+sys/src/ape/lib/fmt/werrstr.c - 664 sys sys 1070330902 241
 sys/src/ape/lib/l - 20000000775 sys sys 1014921994 0
 sys/src/ape/lib/l/allprint.c - 664 sys sys 1014921994 458
 sys/src/ape/lib/l/main.c - 664 sys sys 1014921994 128
@@ -6250,7 +6308,7 @@ sys/src/ape/lib/l/mkfile - 664 sys sys 1014921994 180
 sys/src/ape/lib/l/reject.c - 664 sys sys 1014921994 985
 sys/src/ape/lib/l/yyless.c - 664 sys sys 1014921994 387
 sys/src/ape/lib/l/yywrap.c - 664 sys sys 1014921994 70
-sys/src/ape/lib/mkfile - 664 sys sys 1014921994 190
+sys/src/ape/lib/mkfile - 664 sys sys 1070330862 203
 sys/src/ape/lib/net - 20000000775 sys sys 1014921993 0
 sys/src/ape/lib/net/announce.c - 664 sys sys 1014921993 3549
 sys/src/ape/lib/net/dial.c - 664 sys sys 1014921993 2411
@@ -6265,6 +6323,30 @@ sys/src/ape/lib/regexp/regexec.c - 664 sys sys 1014921993 4339
 sys/src/ape/lib/regexp/regsub.c - 664 sys sys 1014921993 1153
 sys/src/ape/lib/regexp/rregexec.c - 664 sys sys 1014921993 4189
 sys/src/ape/lib/regexp/rregsub.c - 664 sys sys 1014921993 1188
+sys/src/ape/lib/utf - 20000000775 sys sys 1070327378 0
+sys/src/ape/lib/utf/lib9.h - 664 sys sys 1070327090 330
+sys/src/ape/lib/utf/mkfile - 664 sys sys 1070327090 529
+sys/src/ape/lib/utf/rune.c - 664 sys sys 1070327090 3101
+sys/src/ape/lib/utf/runestrcat.c - 664 sys sys 1070327090 919
+sys/src/ape/lib/utf/runestrchr.c - 664 sys sys 1070327090 1002
+sys/src/ape/lib/utf/runestrcmp.c - 664 sys sys 1070327090 1016
+sys/src/ape/lib/utf/runestrcpy.c - 664 sys sys 1070327090 933
+sys/src/ape/lib/utf/runestrdup.c - 664 sys sys 1070327091 995
+sys/src/ape/lib/utf/runestrecpy.c - 664 sys sys 1070327091 997
+sys/src/ape/lib/utf/runestrlen.c - 664 sys sys 1070327091 889
+sys/src/ape/lib/utf/runestrncat.c - 664 sys sys 1070327091 1008
+sys/src/ape/lib/utf/runestrncmp.c - 664 sys sys 1070327091 1045
+sys/src/ape/lib/utf/runestrncpy.c - 664 sys sys 1070327093 1029
+sys/src/ape/lib/utf/runestrrchr.c - 664 sys sys 1070327093 977
+sys/src/ape/lib/utf/runestrstr.c - 664 sys sys 1070327093 1192
+sys/src/ape/lib/utf/runetype.c - 664 sys sys 1070327093 30457
+sys/src/ape/lib/utf/utfdef.h - 664 sys sys 1070327093 335
+sys/src/ape/lib/utf/utfecpy.c - 664 sys sys 1070327093 1268
+sys/src/ape/lib/utf/utflen.c - 664 sys sys 1070327094 1037
+sys/src/ape/lib/utf/utfnlen.c - 664 sys sys 1070327094 1112
+sys/src/ape/lib/utf/utfrrune.c - 664 sys sys 1070327094 1209
+sys/src/ape/lib/utf/utfrune.c - 664 sys sys 1070327094 1196
+sys/src/ape/lib/utf/utfutf.c - 664 sys sys 1070327094 1184
 sys/src/ape/lib/v - 20000000775 sys sys 1014921993 0
 sys/src/ape/lib/v/error.c - 664 sys sys 1014921993 197
 sys/src/ape/lib/v/getfields.c - 664 sys sys 1014921993 3721
@@ -7300,7 +7382,7 @@ sys/src/cmd/disk/kfs/sub.c - 664 sys sys 1022008488 10804
 sys/src/cmd/disk/kfs/uid.c - 664 sys sys 1022008489 6719
 sys/src/cmd/disk/kfscmd.c - 664 sys sys 1015009135 1109
 sys/src/cmd/disk/mbr.c - 664 sys sys 1063855285 4325
-sys/src/cmd/disk/mkext.c - 664 sys sys 1069818199 5500
+sys/src/cmd/disk/mkext.c - 664 sys sys 1070287646 5506
 sys/src/cmd/disk/mkfile - 664 sys sys 1022385851 500
 sys/src/cmd/disk/mkfs.c - 664 sys sys 1032059493 14298
 sys/src/cmd/disk/prep - 20000000775 sys sys 988249968 0
@@ -9262,7 +9344,7 @@ sys/src/cmd/gzip/mkfile - 664 sys sys 984758036 125
 sys/src/cmd/gzip/unzip.c - 664 sys sys 1050689595 13603
 sys/src/cmd/gzip/zip.c - 664 sys sys 1033183074 7054
 sys/src/cmd/gzip/zip.h - 664 sys sys 954778719 1428
-sys/src/cmd/hget.c - 664 sys sys 1067722871 22715
+sys/src/cmd/hget.c - 664 sys sys 1070286807 22715
 sys/src/cmd/history.c - 664 sys sys 1044909166 5810
 sys/src/cmd/hoc - 20000000775 sys sys 954036932 0
 sys/src/cmd/hoc/code.c - 664 sys sys 944961000 10041
@@ -9335,7 +9417,7 @@ sys/src/cmd/ip/httpd/netlib_history.c - 664 sys sys 1015096252 4744
 sys/src/cmd/ip/httpd/redirect.c - 664 sys sys 1042522766 2978
 sys/src/cmd/ip/httpd/save.c - 664 sys sys 1015090172 3175
 sys/src/cmd/ip/httpd/sendfd.c - 664 sys sys 1017679317 12134
-sys/src/cmd/ip/httpd/webls.c - 664 sys sys 1065646625 7233
+sys/src/cmd/ip/httpd/webls.c - 664 sys sys 1070288192 7269
 sys/src/cmd/ip/httpd/webls.denied - 664 sys sys 1064887847 3
 sys/src/cmd/ip/httpd/wikipost.c - 664 sys sys 1019678647 5917
 sys/src/cmd/ip/imap4d - 20000000775 sys sys 988249981 0
@@ -10039,7 +10121,7 @@ sys/src/cmd/ramfs.c - 664 sys sys 1062759940 15377
 sys/src/cmd/ratfs - 20000000775 sys sys 1016943965 0
 sys/src/cmd/ratfs/ctlfiles.c - 664 sys sys 1016943964 6694
 sys/src/cmd/ratfs/main.c - 664 sys sys 1016943964 6516
-sys/src/cmd/ratfs/misc.c - 664 sys sys 1016943965 8560
+sys/src/cmd/ratfs/misc.c - 664 sys sys 1070288056 8563
 sys/src/cmd/ratfs/mkfile - 664 sys sys 1016943965 142
 sys/src/cmd/ratfs/proto.c - 664 sys sys 1016943965 8528
 sys/src/cmd/ratfs/ratfs.h - 664 sys sys 1016943965 2743
@@ -10296,7 +10378,7 @@ sys/src/cmd/tapefs/tpfs.c - 664 sys sys 953243008 1966
 sys/src/cmd/tapefs/util.c - 664 sys sys 1014926385 2653
 sys/src/cmd/tapefs/v10fs.c - 664 sys sys 1014926385 3754
 sys/src/cmd/tapefs/v6fs.c - 664 sys sys 1014926385 3971
-sys/src/cmd/tar.c - 664 sys sys 1029161018 12143
+sys/src/cmd/tar.c - 664 sys sys 1070287474 15531
 sys/src/cmd/tbl - 20000000775 sys sys 954038038 0
 sys/src/cmd/tbl/mkfile - 664 sys sys 944961243 268
 sys/src/cmd/tbl/t.h - 664 sys sys 944961244 3987
@@ -10667,7 +10749,7 @@ sys/src/cmd/upas/common/aux.c - 664 sys sys 1019498851 2300
 sys/src/cmd/upas/common/become.c - 664 sys sys 1015009623 430
 sys/src/cmd/upas/common/common.h - 664 sys sys 1015009623 1921
 sys/src/cmd/upas/common/config.c - 664 sys sys 944961316 254
-sys/src/cmd/upas/common/libsys.c - 664 sys sys 1055699577 14010
+sys/src/cmd/upas/common/libsys.c - 664 sys sys 1070287921 14107
 sys/src/cmd/upas/common/mail.c - 664 sys sys 944961315 1346
 sys/src/cmd/upas/common/makefile - 664 sys sys 944961315 366
 sys/src/cmd/upas/common/mkfile - 664 sys sys 1031707285 251

+ 110 - 0
dist/replica/plan9.log

@@ -12817,3 +12817,113 @@
 1070033454 0 c 386/bin/fmt - 775 sys sys 1070032020 63683
 1070033454 1 c sys/man/1/fmt - 664 sys sys 1070032221 1557
 1070033454 2 c sys/src/cmd/fmt.c - 664 sys sys 1070032009 4088
+1070287300 0 c 386/bin/hget - 775 sys sys 1070286807 222506
+1070287300 1 c sys/src/cmd/hget.c - 664 sys sys 1070286807 22715
+1070289103 0 c 386/bin/aux/antiword - 775 sys sys 1070288096 511478
+1070289103 1 c 386/bin/fossil/flchk - 775 sys sys 1070288097 232862
+1070289103 2 c 386/bin/fossil/flfmt - 775 sys sys 1070288098 230827
+1070289103 3 c 386/bin/fossil/fossil - 775 sys sys 1070288099 345606
+1070289103 4 c 386/bin/ip/pptpd - 775 sys sys 1070288105 124153
+1070289103 5 c 386/bin/ip/rarpd - 775 sys sys 1070288105 107748
+1070289103 6 c 386/bin/ndb/dns - 775 sys sys 1070288106 218607
+1070289103 7 c 386/bin/ndb/dnsdebug - 775 sys sys 1070288107 190956
+1070289103 8 c 386/bin/ndb/dnstcp - 775 sys sys 1070288108 189670
+1070289103 9 c 386/lib/libip.a - 664 sys sys 1070288108 34142
+1070289103 10 c rc/bin/yesterday - 775 sys sys 1070288432 2541
+1070289103 11 c sys/man/1/tar - 664 sys sys 1070287474 2363
+1070289103 12 c sys/man/1/yesterday - 664 sys sys 1070288431 2348
+1070289103 13 c sys/src/9/ip/tcp.c - 664 sys sys 1070287819 65399
+1070289103 14 c sys/src/9/port/qio.c - 664 sys sys 1070287837 23562
+1070289103 15 c sys/src/cmd/disk/mkext.c - 664 sys sys 1070287646 5506
+1070289103 16 c sys/src/cmd/ip/httpd/webls.c - 664 sys sys 1070288192 7269
+1070289103 17 c sys/src/cmd/ratfs/misc.c - 664 sys sys 1070288056 8563
+1070289103 18 c sys/src/cmd/upas/common/libsys.c - 664 sys sys 1070287921 14107
+1070289103 19 c sys/src/cmd/tar.c - 664 sys sys 1070287474 15531
+1070327406 0 a sys/include/ape/cursor.h - 664 sys sys 1070327358 118
+1070327406 1 a sys/include/ape/draw.h - 664 sys sys 1070327359 15998
+1070327406 2 c sys/include/ape/errno.h - 664 sys sys 1070327359 1525
+1070327406 3 a sys/include/ape/event.h - 664 sys sys 1070327358 117
+1070327406 4 a sys/include/ape/fmt.h - 664 sys sys 1070327361 3276
+1070327406 5 a sys/include/ape/keyboard.h - 664 sys sys 1070327358 120
+1070327406 6 a sys/include/ape/mouse.h - 664 sys sys 1070327358 117
+1070327406 7 a sys/include/ape/qlock.h - 664 sys sys 1070327358 474
+1070327406 8 a sys/include/ape/utf.h - 664 sys sys 1070327359 1595
+1070327406 9 a sys/src/ape/lib/draw - 20000000775 sys sys 1070327097 0
+1070327406 10 a sys/src/ape/lib/draw/colors.c - 664 sys sys 1070327097 3607
+1070327406 11 a sys/src/ape/lib/draw/libc.h - 664 sys sys 1070327097 3476
+1070327406 12 a sys/src/ape/lib/draw/mkfile - 664 sys sys 1070327097 1176
+1070327406 13 a sys/src/ape/lib/fmt - 20000000775 sys sys 1070327381 0
+1070327406 14 a sys/src/ape/lib/fmt/charstod.c - 664 sys sys 1070327066 1995
+1070327406 15 a sys/src/ape/lib/fmt/dofmt.c - 664 sys sys 1070327066 10020
+1070327406 16 a sys/src/ape/lib/fmt/dorfmt.c - 664 sys sys 1070327066 1576
+1070327406 17 a sys/src/ape/lib/fmt/errfmt.c - 664 sys sys 1070327066 948
+1070327406 18 a sys/src/ape/lib/fmt/fltfmt.c - 664 sys sys 1070327066 10300
+1070327406 19 a sys/src/ape/lib/fmt/fmt.c - 664 sys sys 1070327067 4131
+1070327406 20 a sys/src/ape/lib/fmt/fmtdef.h - 664 sys sys 1070327067 3100
+1070327406 21 a sys/src/ape/lib/fmt/fmtfd.c - 664 sys sys 1070327067 1302
+1070327406 22 a sys/src/ape/lib/fmt/fmtfdflush.c - 664 sys sys 1070327067 1077
+1070327406 23 a sys/src/ape/lib/fmt/fmtlock.c - 664 sys sys 1070327067 868
+1070327406 24 a sys/src/ape/lib/fmt/fmtprint.c - 664 sys sys 1070327068 1257
+1070327406 25 a sys/src/ape/lib/fmt/fmtquote.c - 664 sys sys 1070327068 5582
+1070327406 26 a sys/src/ape/lib/fmt/fmtrune.c - 664 sys sys 1070327068 1120
+1070327406 27 a sys/src/ape/lib/fmt/fmtstr.c - 664 sys sys 1070327068 1509
+1070327406 28 a sys/src/ape/lib/fmt/fmtvprint.c - 664 sys sys 1070327068 1241
+1070327406 29 a sys/src/ape/lib/fmt/fprint.c - 664 sys sys 1070327069 946
+1070327406 30 a sys/src/ape/lib/fmt/mkfile - 664 sys sys 1070327069 794
+1070327406 31 a sys/src/ape/lib/fmt/nan.h - 664 sys sys 1070327069 114
+1070327406 32 a sys/src/ape/lib/fmt/nan64.c - 664 sys sys 1070327069 1176
+1070327406 33 a sys/src/ape/lib/fmt/pow10.c - 664 sys sys 1070327069 1987
+1070327406 34 a sys/src/ape/lib/fmt/print.c - 664 sys sys 1070327069 936
+1070327406 35 a sys/src/ape/lib/fmt/runefmtstr.c - 664 sys sys 1070327070 1544
+1070327406 36 a sys/src/ape/lib/fmt/runeseprint.c - 664 sys sys 1070327070 1016
+1070327406 37 a sys/src/ape/lib/fmt/runesmprint.c - 664 sys sys 1070327070 988
+1070327406 38 a sys/src/ape/lib/fmt/runesnprint.c - 664 sys sys 1070327070 1015
+1070327406 39 a sys/src/ape/lib/fmt/runesprint.c - 664 sys sys 1070327070 1004
+1070327406 40 a sys/src/ape/lib/fmt/runevseprint.c - 664 sys sys 1070327071 1132
+1070327406 41 a sys/src/ape/lib/fmt/runevsmprint.c - 664 sys sys 1070327071 1109
+1070327406 42 a sys/src/ape/lib/fmt/runevsnprint.c - 664 sys sys 1070327071 1142
+1070327406 43 a sys/src/ape/lib/fmt/seprint.c - 664 sys sys 1070327071 951
+1070327406 44 a sys/src/ape/lib/fmt/smprint.c - 664 sys sys 1070327071 923
+1070327406 45 a sys/src/ape/lib/fmt/snprint.c - 664 sys sys 1070327071 950
+1070327406 46 a sys/src/ape/lib/fmt/sprint.c - 664 sys sys 1070327072 991
+1070327406 47 a sys/src/ape/lib/fmt/strtod.c - 664 sys sys 1070327072 9359
+1070327406 48 a sys/src/ape/lib/fmt/strtod.h - 664 sys sys 1070327072 120
+1070327406 49 a sys/src/ape/lib/fmt/test.c - 664 sys sys 1070327072 1423
+1070327406 50 a sys/src/ape/lib/fmt/vfprint.c - 664 sys sys 1070327072 1026
+1070327406 51 a sys/src/ape/lib/fmt/vseprint.c - 664 sys sys 1070327072 1089
+1070327406 52 a sys/src/ape/lib/fmt/vsmprint.c - 664 sys sys 1070327073 1084
+1070327406 53 a sys/src/ape/lib/fmt/vsnprint.c - 664 sys sys 1070327073 1119
+1070327406 54 a sys/src/ape/lib/fmt/werrstr.c - 664 sys sys 1070327073 235
+1070327406 55 a sys/src/ape/lib/utf - 20000000775 sys sys 1070327378 0
+1070327406 56 a sys/src/ape/lib/utf/lib9.h - 664 sys sys 1070327090 330
+1070327406 57 a sys/src/ape/lib/utf/mkfile - 664 sys sys 1070327090 529
+1070327406 58 a sys/src/ape/lib/utf/rune.c - 664 sys sys 1070327090 3101
+1070327406 59 a sys/src/ape/lib/utf/runestrcat.c - 664 sys sys 1070327090 919
+1070327406 60 a sys/src/ape/lib/utf/runestrchr.c - 664 sys sys 1070327090 1002
+1070327406 61 a sys/src/ape/lib/utf/runestrcmp.c - 664 sys sys 1070327090 1016
+1070327406 62 a sys/src/ape/lib/utf/runestrcpy.c - 664 sys sys 1070327090 933
+1070327406 63 a sys/src/ape/lib/utf/runestrdup.c - 664 sys sys 1070327091 995
+1070327406 64 a sys/src/ape/lib/utf/runestrecpy.c - 664 sys sys 1070327091 997
+1070327406 65 a sys/src/ape/lib/utf/runestrlen.c - 664 sys sys 1070327091 889
+1070327406 66 a sys/src/ape/lib/utf/runestrncat.c - 664 sys sys 1070327091 1008
+1070327406 67 a sys/src/ape/lib/utf/runestrncmp.c - 664 sys sys 1070327091 1045
+1070327406 68 a sys/src/ape/lib/utf/runestrncpy.c - 664 sys sys 1070327093 1029
+1070327406 69 a sys/src/ape/lib/utf/runestrrchr.c - 664 sys sys 1070327093 977
+1070327406 70 a sys/src/ape/lib/utf/runestrstr.c - 664 sys sys 1070327093 1192
+1070327406 71 a sys/src/ape/lib/utf/runetype.c - 664 sys sys 1070327093 30457
+1070327406 72 a sys/src/ape/lib/utf/utfdef.h - 664 sys sys 1070327093 335
+1070327406 73 a sys/src/ape/lib/utf/utfecpy.c - 664 sys sys 1070327093 1268
+1070327406 74 a sys/src/ape/lib/utf/utflen.c - 664 sys sys 1070327094 1037
+1070327406 75 a sys/src/ape/lib/utf/utfnlen.c - 664 sys sys 1070327094 1112
+1070327406 76 a sys/src/ape/lib/utf/utfrrune.c - 664 sys sys 1070327094 1209
+1070327406 77 a sys/src/ape/lib/utf/utfrune.c - 664 sys sys 1070327094 1196
+1070327406 78 a sys/src/ape/lib/utf/utfutf.c - 664 sys sys 1070327094 1184
+1070328706 0 c sys/lib/plumb/basic - 664 sys sys 1070327744 2922
+1070330908 0 c sys/lib/plumb/basic - 664 sys sys 1070330856 2930
+1070330908 1 a sys/src/ape/lib/ap/plan9/9errstr.c - 664 sys sys 1070330880 31
+1070330908 2 a sys/src/ape/lib/ap/plan9/9mallocz.c - 664 sys sys 1070330880 148
+1070330908 3 a sys/src/ape/lib/ap/plan9/9readn.c - 664 sys sys 1070330880 221
+1070330908 4 c sys/src/ape/lib/ap/plan9/mkfile - 664 sys sys 1070330880 1357
+1070330908 5 a sys/src/ape/lib/ap/plan9/qlock.c - 664 sys sys 1070330880 5124
+1070330908 6 c sys/src/ape/lib/fmt/werrstr.c - 664 sys sys 1070330902 241
+1070330908 7 c sys/src/ape/lib/mkfile - 664 sys sys 1070330862 203

+ 13 - 4
rc/bin/yesterday

@@ -56,6 +56,7 @@ fn Xbind {
 year=`{date|sed 's/.* //'}
 copy=Xecho
 last=()
+defdump=dump
 while(! ~ $#* 0 && ~ $1 -* && ! ~ $1 --){
 	switch($1){
 	case -a
@@ -76,6 +77,9 @@ while(! ~ $#* 0 && ~ $1 -* && ! ~ $1 --){
 	case -C
 		copy=Xcarefulcp
 		shift
+	case -s
+		defdump=snap
+		shift
 # The slashnhack is always right.
 #	case -N
 #		slashnhack=no
@@ -127,23 +131,28 @@ if(! ~ $status ''){
 
 for(i){
 	xpath=`{cleanname -d $dir -- $i}
-	xdump=dump
+	xdump=$defdump
 	dumppath=$xpath
 	if(~ $slashnhack yes && ~ $xpath /n/*/*){
-		xdump=`{echo $xpath | sed 's:/n/([^/]+)/.*:\1dump:'}
+		xdump=`{echo $xpath | sed 's:/n/([^/]+)/.*:\1'$defdump':'}
 		dumppath=`{echo $xpath | sed 's:/n/[^/]+(/.*):\1:'}
 	}
 	if(! test -e /n/$xdump/$year)
 		9fs $xdump
 
-	if(~ $#last 0)
+	if(~ $#last 0){
 		xlast=`{ls -t /n/$xdump/$year|sed 1q}
+		switch($defdump){
+		case snap
+			xlast=`{ls -t $xlast|sed 1q}
+		}
+	}
 	if not
 		xlast=/n/$xdump/$last
 
 	if(! test -e $xlast){
 		echo 'yesterday:' \
-			`{echo $xlast|sed 's/.n.dump.(....).(..)(..)/\1 \2 \3/'} \
+			`{echo $xlast|sed 's/.n.'$defdump'.(....).(..)(..)/\1 \2 \3/'} \
 			'is not a backup day for' $xdump >[1=2]
 		exit 'bad date'
 	}

+ 6 - 0
sys/include/ape/cursor.h

@@ -0,0 +1,6 @@
+#ifndef _RESEARCH_SOURCE
+  This header file is not defined in ANSI or POSIX
+#endif
+
+#include "/sys/include/cursor.h"
+

+ 521 - 0
sys/include/ape/draw.h

@@ -0,0 +1,521 @@
+#ifndef _RESEARCH_SOURCE
+  This header file is not defined in ANSI or POSIX
+#endif
+
+#pragma src "/sys/src/ape/lib/draw"
+#pragma lib "/$M/lib/ape/libdraw.a"
+
+typedef struct	Cachefont Cachefont;
+typedef struct	Cacheinfo Cacheinfo;
+typedef struct	Cachesubf Cachesubf;
+typedef struct	Display Display;
+typedef struct	Font Font;
+typedef struct	Fontchar Fontchar;
+typedef struct	Image Image;
+typedef struct	Mouse Mouse;
+typedef struct	Point Point;
+typedef struct	Rectangle Rectangle;
+typedef struct	RGB RGB;
+typedef struct	Screen Screen;
+typedef struct	Subfont Subfont;
+
+#pragma varargck	type	"R"	Rectangle
+#pragma varargck	type	"P"	Point
+extern	int	Rfmt(Fmt*);
+extern	int	Pfmt(Fmt*);
+
+enum
+{
+	DOpaque		= 0xFFFFFFFF,
+	DTransparent	= 0x00000000,		/* only useful for allocimage, memfillcolor */
+	DBlack		= 0x000000FF,
+	DWhite		= 0xFFFFFFFF,
+	DRed		= 0xFF0000FF,
+	DGreen		= 0x00FF00FF,
+	DBlue		= 0x0000FFFF,
+	DCyan		= 0x00FFFFFF,
+	DMagenta		= 0xFF00FFFF,
+	DYellow		= 0xFFFF00FF,
+	DPaleyellow	= 0xFFFFAAFF,
+	DDarkyellow	= 0xEEEE9EFF,
+	DDarkgreen	= 0x448844FF,
+	DPalegreen	= 0xAAFFAAFF,
+	DMedgreen	= 0x88CC88FF,
+	DDarkblue	= 0x000055FF,
+	DPalebluegreen= 0xAAFFFFFF,
+	DPaleblue		= 0x0000BBFF,
+	DBluegreen	= 0x008888FF,
+	DGreygreen	= 0x55AAAAFF,
+	DPalegreygreen	= 0x9EEEEEFF,
+	DYellowgreen	= 0x99994CFF,
+	DMedblue		= 0x000099FF,
+	DGreyblue	= 0x005DBBFF,
+	DPalegreyblue	= 0x4993DDFF,
+	DPurpleblue	= 0x8888CCFF,
+
+	DNotacolor	= 0xFFFFFF00,
+	DNofill		= DNotacolor,
+	
+};
+
+enum
+{
+	Displaybufsize	= 8000,
+	ICOSSCALE	= 1024,
+	Borderwidth =	4,
+};
+
+enum
+{
+	/* refresh methods */
+	Refbackup	= 0,
+	Refnone		= 1,
+	Refmesg		= 2
+};
+#define	NOREFRESH	((void*)-1)
+
+enum
+{
+	/* line ends */
+	Endsquare	= 0,
+	Enddisc		= 1,
+	Endarrow	= 2,
+	Endmask		= 0x1F
+};
+
+#define	ARROW(a, b, c)	(Endarrow|((a)<<5)|((b)<<14)|((c)<<23))
+
+typedef enum
+{
+	/* Porter-Duff compositing operators */
+	Clear	= 0,
+
+	SinD	= 8,
+	DinS	= 4,
+	SoutD	= 2,
+	DoutS	= 1,
+
+	S		= SinD|SoutD,
+	SoverD	= SinD|SoutD|DoutS,
+	SatopD	= SinD|DoutS,
+	SxorD	= SoutD|DoutS,
+
+	D		= DinS|DoutS,
+	DoverS	= DinS|DoutS|SoutD,
+	DatopS	= DinS|SoutD,
+	DxorS	= DoutS|SoutD,	/* == SxorD */
+
+	Ncomp = 12,
+} Drawop;
+
+/*
+ * image channel descriptors 
+ */
+enum {
+	CRed = 0,
+	CGreen,
+	CBlue,
+	CGrey,
+	CAlpha,
+	CMap,
+	CIgnore,
+	NChan,
+};
+
+#define __DC(type, nbits)	((((type)&15)<<4)|((nbits)&15))
+#define CHAN1(a,b)	__DC(a,b)
+#define CHAN2(a,b,c,d)	(CHAN1((a),(b))<<8|__DC((c),(d)))
+#define CHAN3(a,b,c,d,e,f)	(CHAN2((a),(b),(c),(d))<<8|__DC((e),(f)))
+#define CHAN4(a,b,c,d,e,f,g,h)	(CHAN3((a),(b),(c),(d),(e),(f))<<8|__DC((g),(h)))
+
+#define NBITS(c) ((c)&15)
+#define TYPE(c) (((c)>>4)&15)
+
+enum {
+	GREY1	= CHAN1(CGrey, 1),
+	GREY2	= CHAN1(CGrey, 2),
+	GREY4	= CHAN1(CGrey, 4),
+	GREY8	= CHAN1(CGrey, 8),
+	CMAP8	= CHAN1(CMap, 8),
+	RGB15	= CHAN4(CIgnore, 1, CRed, 5, CGreen, 5, CBlue, 5),
+	RGB16	= CHAN3(CRed, 5, CGreen, 6, CBlue, 5),
+	RGB24	= CHAN3(CRed, 8, CGreen, 8, CBlue, 8),
+	RGBA32	= CHAN4(CRed, 8, CGreen, 8, CBlue, 8, CAlpha, 8),
+	ARGB32	= CHAN4(CAlpha, 8, CRed, 8, CGreen, 8, CBlue, 8),	/* stupid VGAs */
+	XRGB32  = CHAN4(CIgnore, 8, CRed, 8, CGreen, 8, CBlue, 8),
+};
+
+extern	char*	chantostr(char*, ulong);
+extern	ulong	strtochan(char*);
+extern	int		chantodepth(ulong);
+
+struct	Point
+{
+	int	x;
+	int	y;
+};
+
+struct Rectangle
+{
+	Point	min;
+	Point	max;
+};
+
+typedef void	(*Reffn)(Image*, Rectangle, void*);
+
+struct Screen
+{
+	Display	*display;	/* display holding data */
+	int	id;		/* id of system-held Screen */
+	Image	*image;		/* unused; for reference only */
+	Image	*fill;		/* color to paint behind windows */
+};
+
+struct Display
+{
+	QLock	qlock;
+	int		locking;	/*program is using lockdisplay */
+	int		dirno;
+	int		fd;
+	int		reffd;
+	int		ctlfd;
+	int		imageid;
+	int		local;
+	void		(*error)(Display*, char*);
+	char		*devdir;
+	char		*windir;
+	char		oldlabel[64];
+	ulong		dataqid;
+	Image		*white;
+	Image		*black;
+	Image		*opaque;
+	Image		*transparent;
+	Image		*image;
+	uchar		*buf;
+	int			bufsize;
+	uchar		*bufp;
+	Font		*defaultfont;
+	Subfont		*defaultsubfont;
+	Image		*windows;
+	Image		*screenimage;
+	int			_isnewdisplay;
+};
+
+struct Image
+{
+	Display		*display;	/* display holding data */
+	int		id;		/* id of system-held Image */
+	Rectangle	r;		/* rectangle in data area, local coords */
+	Rectangle 	clipr;		/* clipping region */
+	int		depth;		/* number of bits per pixel */
+	ulong	chan;
+	int		repl;		/* flag: data replicates to tile clipr */
+	Screen		*screen;	/* 0 if not a window */
+	Image		*next;	/* next in list of windows */
+};
+
+struct RGB
+{
+	ulong	red;
+	ulong	green;
+	ulong	blue;
+};
+
+/*
+ * Subfonts
+ *
+ * given char c, Subfont *f, Fontchar *i, and Point p, one says
+ *	i = f->info+c;
+ *	draw(b, Rect(p.x+i->left, p.y+i->top,
+ *		p.x+i->left+((i+1)->x-i->x), p.y+i->bottom),
+ *		color, f->bits, Pt(i->x, i->top));
+ *	p.x += i->width;
+ * to draw characters in the specified color (itself an Image) in Image b.
+ */
+
+struct	Fontchar
+{
+	int		x;		/* left edge of bits */
+	uchar		top;		/* first non-zero scan-line */
+	uchar		bottom;		/* last non-zero scan-line + 1 */
+	char		left;		/* offset of baseline */
+	uchar		width;		/* width of baseline */
+};
+
+struct	Subfont
+{
+	char		*name;
+	short		n;		/* number of chars in font */
+	uchar		height;		/* height of image */
+	char		ascent;		/* top of image to baseline */
+	Fontchar 	*info;		/* n+1 character descriptors */
+	Image		*bits;		/* of font */
+	int		ref;
+};
+
+enum
+{
+	/* starting values */
+	LOG2NFCACHE =	6,
+	NFCACHE =	(1<<LOG2NFCACHE),	/* #chars cached */
+	NFLOOK =	5,			/* #chars to scan in cache */
+	NFSUBF =	2,			/* #subfonts to cache */
+	/* max value */
+	MAXFCACHE =	1024+NFLOOK,		/* upper limit */
+	MAXSUBF =	50,			/* generous upper limit */
+	/* deltas */
+	DSUBF = 	4,
+	/* expiry ages */
+	SUBFAGE	=	10000,
+	CACHEAGE =	10000
+};
+
+struct Cachefont
+{
+	Rune		min;	/* lowest rune value to be taken from subfont */
+	Rune		max;	/* highest rune value+1 to be taken from subfont */
+	int		offset;	/* position in subfont of character at min */
+	char		*name;			/* stored in font */
+	char		*subfontname;		/* to access subfont */
+};
+
+struct Cacheinfo
+{
+	ushort		x;		/* left edge of bits */
+	uchar		width;		/* width of baseline */
+	schar		left;		/* offset of baseline */
+	Rune		value;	/* value of character at this slot in cache */
+	ushort		age;
+};
+
+struct Cachesubf
+{
+	ulong		age;	/* for replacement */
+	Cachefont	*cf;	/* font info that owns us */
+	Subfont		*f;	/* attached subfont */
+};
+
+struct Font
+{
+	char		*name;
+	Display		*display;
+	short		height;	/* max height of image, interline spacing */
+	short		ascent;	/* top of image to baseline */
+	short		width;	/* widest so far; used in caching only */	
+	short		nsub;	/* number of subfonts */
+	ulong		age;	/* increasing counter; used for LRU */
+	int		maxdepth;	/* maximum depth of all loaded subfonts */
+	int		ncache;	/* size of cache */
+	int		nsubf;	/* size of subfont list */
+	Cacheinfo	*cache;
+	Cachesubf	*subf;
+	Cachefont	**sub;	/* as read from file */
+	Image		*cacheimage;
+};
+
+#define	Dx(r)	((r).max.x-(r).min.x)
+#define	Dy(r)	((r).max.y-(r).min.y)
+
+/*
+ * Image management
+ */
+extern Image*	_allocimage(Image*, Display*, Rectangle, ulong, int, ulong, int, int);
+extern Image*	allocimage(Display*, Rectangle, ulong, int, ulong);
+extern uchar*	bufimage(Display*, int);
+extern int	bytesperline(Rectangle, int);
+extern void	closedisplay(Display*);
+extern void	drawerror(Display*, char*);
+extern int	flushimage(Display*, int);
+extern int	freeimage(Image*);
+extern int	_freeimage1(Image*);
+extern int	geninitdraw(char*, void(*)(Display*, char*), char*, char*, char*, int);
+extern int	initdraw(void(*)(Display*, char*), char*, char*);
+extern int	newwindow(char*);
+extern Display*	initdisplay(char*, char*, void(*)(Display*, char*));
+extern int	loadimage(Image*, Rectangle, uchar*, int);
+extern int	cloadimage(Image*, Rectangle, uchar*, int);
+extern int	getwindow(Display*, int);
+extern int	gengetwindow(Display*, char*, Image**, Screen**, int);
+extern Image* readimage(Display*, int, int);
+extern Image* creadimage(Display*, int, int);
+extern int	unloadimage(Image*, Rectangle, uchar*, int);
+extern int	wordsperline(Rectangle, int);
+extern int	writeimage(int, Image*, int);
+extern Image*	namedimage(Display*, char*);
+extern int	nameimage(Image*, char*, int);
+extern Image* allocimagemix(Display*, ulong, ulong);
+
+/*
+ * Colors
+ */
+extern	void	readcolmap(Display*, RGB*);
+extern	void	writecolmap(Display*, RGB*);
+extern	ulong	setalpha(ulong, uchar);
+
+/*
+ * Windows
+ */
+extern Screen*	allocscreen(Image*, Image*, int);
+extern Image*	_allocwindow(Image*, Screen*, Rectangle, int, ulong);
+extern Image*	allocwindow(Screen*, Rectangle, int, ulong);
+extern void	bottomnwindows(Image**, int);
+extern void	bottomwindow(Image*);
+extern int	freescreen(Screen*);
+extern Screen*	publicscreen(Display*, int, ulong);
+extern void	topnwindows(Image**, int);
+extern void	topwindow(Image*);
+extern int	originwindow(Image*, Point, Point);
+
+/*
+ * Geometry
+ */
+extern Point		Pt(int, int);
+extern Rectangle	Rect(int, int, int, int);
+extern Rectangle	Rpt(Point, Point);
+extern Point		addpt(Point, Point);
+extern Point		subpt(Point, Point);
+extern Point		divpt(Point, int);
+extern Point		mulpt(Point, int);
+extern int		eqpt(Point, Point);
+extern int		eqrect(Rectangle, Rectangle);
+extern Rectangle	insetrect(Rectangle, int);
+extern Rectangle	rectaddpt(Rectangle, Point);
+extern Rectangle	rectsubpt(Rectangle, Point);
+extern Rectangle	canonrect(Rectangle);
+extern int		rectXrect(Rectangle, Rectangle);
+extern int		rectinrect(Rectangle, Rectangle);
+extern void		combinerect(Rectangle*, Rectangle);
+extern int		rectclip(Rectangle*, Rectangle);
+extern int		ptinrect(Point, Rectangle);
+extern void		replclipr(Image*, int, Rectangle);
+extern int		drawreplxy(int, int, int);	/* used to be drawsetxy */
+extern Point	drawrepl(Rectangle, Point);
+extern int		rgb2cmap(int, int, int);
+extern int		cmap2rgb(int);
+extern int		cmap2rgba(int);
+extern void		icossin(int, int*, int*);
+extern void		icossin2(int, int, int*, int*);
+
+/*
+ * Graphics
+ */
+extern void	draw(Image*, Rectangle, Image*, Image*, Point);
+extern void	drawop(Image*, Rectangle, Image*, Image*, Point, Drawop);
+extern void	gendraw(Image*, Rectangle, Image*, Point, Image*, Point);
+extern void	gendrawop(Image*, Rectangle, Image*, Point, Image*, Point, Drawop);
+extern void	line(Image*, Point, Point, int, int, int, Image*, Point);
+extern void	lineop(Image*, Point, Point, int, int, int, Image*, Point, Drawop);
+extern void	poly(Image*, Point*, int, int, int, int, Image*, Point);
+extern void	polyop(Image*, Point*, int, int, int, int, Image*, Point, Drawop);
+extern void	fillpoly(Image*, Point*, int, int, Image*, Point);
+extern void	fillpolyop(Image*, Point*, int, int, Image*, Point, Drawop);
+extern Point	string(Image*, Point, Image*, Point, Font*, char*);
+extern Point	stringop(Image*, Point, Image*, Point, Font*, char*, Drawop);
+extern Point	stringn(Image*, Point, Image*, Point, Font*, char*, int);
+extern Point	stringnop(Image*, Point, Image*, Point, Font*, char*, int, Drawop);
+extern Point	runestring(Image*, Point, Image*, Point, Font*, Rune*);
+extern Point	runestringop(Image*, Point, Image*, Point, Font*, Rune*, Drawop);
+extern Point	runestringn(Image*, Point, Image*, Point, Font*, Rune*, int);
+extern Point	runestringnop(Image*, Point, Image*, Point, Font*, Rune*, int, Drawop);
+extern Point	stringbg(Image*, Point, Image*, Point, Font*, char*, Image*, Point);
+extern Point	stringbgop(Image*, Point, Image*, Point, Font*, char*, Image*, Point, Drawop);
+extern Point	stringnbg(Image*, Point, Image*, Point, Font*, char*, int, Image*, Point);
+extern Point	stringnbgop(Image*, Point, Image*, Point, Font*, char*, int, Image*, Point, Drawop);
+extern Point	runestringbg(Image*, Point, Image*, Point, Font*, Rune*, Image*, Point);
+extern Point	runestringbgop(Image*, Point, Image*, Point, Font*, Rune*, Image*, Point, Drawop);
+extern Point	runestringnbg(Image*, Point, Image*, Point, Font*, Rune*, int, Image*, Point);
+extern Point	runestringnbgop(Image*, Point, Image*, Point, Font*, Rune*, int, Image*, Point, Drawop);
+extern Point	_string(Image*, Point, Image*, Point, Font*, char*, Rune*, int, Rectangle, Image*, Point, Drawop);
+extern Point	stringsubfont(Image*, Point, Image*, Subfont*, char*);
+extern int		bezier(Image*, Point, Point, Point, Point, int, int, int, Image*, Point);
+extern int		bezierop(Image*, Point, Point, Point, Point, int, int, int, Image*, Point, Drawop);
+extern int		bezspline(Image*, Point*, int, int, int, int, Image*, Point);
+extern int		bezsplineop(Image*, Point*, int, int, int, int, Image*, Point, Drawop);
+extern int		bezsplinepts(Point*, int, Point**);
+extern int		fillbezier(Image*, Point, Point, Point, Point, int, Image*, Point);
+extern int		fillbezierop(Image*, Point, Point, Point, Point, int, Image*, Point, Drawop);
+extern int		fillbezspline(Image*, Point*, int, int, Image*, Point);
+extern int		fillbezsplineop(Image*, Point*, int, int, Image*, Point, Drawop);
+extern void	ellipse(Image*, Point, int, int, int, Image*, Point);
+extern void	ellipseop(Image*, Point, int, int, int, Image*, Point, Drawop);
+extern void	fillellipse(Image*, Point, int, int, Image*, Point);
+extern void	fillellipseop(Image*, Point, int, int, Image*, Point, Drawop);
+extern void	arc(Image*, Point, int, int, int, Image*, Point, int, int);
+extern void	arcop(Image*, Point, int, int, int, Image*, Point, int, int, Drawop);
+extern void	fillarc(Image*, Point, int, int, Image*, Point, int, int);
+extern void	fillarcop(Image*, Point, int, int, Image*, Point, int, int, Drawop);
+extern void	border(Image*, Rectangle, int, Image*, Point);
+extern void	borderop(Image*, Rectangle, int, Image*, Point, Drawop);
+
+/*
+ * Font management
+ */
+extern Font*	openfont(Display*, char*);
+extern Font*	buildfont(Display*, char*, char*);
+extern void	freefont(Font*);
+extern Font*	mkfont(Subfont*, Rune);
+extern int	cachechars(Font*, char**, Rune**, ushort*, int, int*, char**);
+extern void	agefont(Font*);
+extern Subfont*	allocsubfont(char*, int, int, int, Fontchar*, Image*);
+extern Subfont*	lookupsubfont(Display*, char*);
+extern void	installsubfont(char*, Subfont*);
+extern void	uninstallsubfont(Subfont*);
+extern void	freesubfont(Subfont*);
+extern Subfont*	readsubfont(Display*, char*, int, int);
+extern Subfont*	readsubfonti(Display*, char*, int, Image*, int);
+extern int	writesubfont(int, Subfont*);
+extern void	_unpackinfo(Fontchar*, uchar*, int);
+extern Point	stringsize(Font*, char*);
+extern int	stringwidth(Font*, char*);
+extern int	stringnwidth(Font*, char*, int);
+extern Point	runestringsize(Font*, Rune*);
+extern int	runestringwidth(Font*, Rune*);
+extern int	runestringnwidth(Font*, Rune*, int);
+extern Point	strsubfontwidth(Subfont*, char*);
+extern int	loadchar(Font*, Rune, Cacheinfo*, int, int, char**);
+extern char*	subfontname(char*, char*, int);
+extern Subfont*	_getsubfont(Display*, char*);
+extern Subfont*	getdefont(Display*);
+extern void		lockdisplay(Display*);
+extern void	unlockdisplay(Display*);
+extern int		drawlsetrefresh(ulong, int, void*, void*);
+
+/*
+ * Predefined 
+ */
+extern	uchar	defontdata[];
+extern	int		sizeofdefont;
+extern	Point		ZP;
+extern	Rectangle	ZR;
+
+/*
+ * Set up by initdraw()
+ */
+extern	Display	*display;
+extern	Font		*font;
+extern	Image	*screen;
+extern	Screen	*_screen;
+extern	int	_cursorfd;
+extern	int	_drawdebug;	/* set to 1 to see errors from flushimage */
+extern	void	_setdrawop(Display*, Drawop);
+
+#define	BGSHORT(p)		(((p)[0]<<0) | ((p)[1]<<8))
+#define	BGLONG(p)		((BGSHORT(p)<<0) | (BGSHORT(p+2)<<16))
+#define	BPSHORT(p, v)		((p)[0]=(v), (p)[1]=((v)>>8))
+#define	BPLONG(p, v)		(BPSHORT(p, (v)), BPSHORT(p+2, (v)>>16))
+
+/*
+ * Compressed image file parameters and helper routines
+ */
+#define	NMATCH	3		/* shortest match possible */
+#define	NRUN	(NMATCH+31)	/* longest match possible */
+#define	NMEM	1024		/* window size */
+#define	NDUMP	128		/* maximum length of dump */
+#define	NCBLOCK	6000		/* size of compressed blocks */
+extern	void	_twiddlecompressed(uchar*, int);
+extern	int	_compblocksize(Rectangle, int);
+
+/* XXX backwards helps; should go */
+extern	int		log2[];
+extern	ulong	drawld2chan[];
+extern	void		drawsetdebug(int);

+ 1 - 0
sys/include/ape/errno.h

@@ -6,6 +6,7 @@ extern int errno;
 
 #define EDOM	1000
 #define ERANGE	1001
+#define EPLAN9	1002
 
 #ifdef _POSIX_SOURCE
 

+ 6 - 0
sys/include/ape/event.h

@@ -0,0 +1,6 @@
+#ifndef _RESEARCH_SOURCE
+  This header file is not defined in ANSI or POSIX
+#endif
+
+#include "/sys/include/event.h"
+

+ 107 - 0
sys/include/ape/fmt.h

@@ -0,0 +1,107 @@
+#ifndef _FMT_H_
+#define _FMT_H_ 1
+#pragma lib "/$M/lib/ape/libfmt.a"
+#pragma src "/sys/src/ape/lib/fmt"
+
+#if defined(__cplusplus)
+extern "C" { 
+#endif
+
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+
+#include <stdarg.h>
+#include <utf.h>
+
+typedef struct Fmt	Fmt;
+struct Fmt{
+	unsigned char	runes;		/* output buffer is runes or chars? */
+	void	*start;			/* of buffer */
+	void	*to;			/* current place in the buffer */
+	void	*stop;			/* end of the buffer; overwritten if flush fails */
+	int	(*flush)(Fmt *);	/* called when to == stop */
+	void	*farg;			/* to make flush a closure */
+	int	nfmt;			/* num chars formatted so far */
+	va_list	args;			/* args passed to dofmt */
+	int	r;			/* % format Rune */
+	int	width;
+	int	prec;
+	unsigned long	flags;
+};
+
+enum{
+	FmtWidth	= 1,
+	FmtLeft		= FmtWidth << 1,
+	FmtPrec		= FmtLeft << 1,
+	FmtSharp	= FmtPrec << 1,
+	FmtSpace	= FmtSharp << 1,
+	FmtSign		= FmtSpace << 1,
+	FmtZero		= FmtSign << 1,
+	FmtUnsigned	= FmtZero << 1,
+	FmtShort	= FmtUnsigned << 1,
+	FmtLong		= FmtShort << 1,
+	FmtVLong	= FmtLong << 1,
+	FmtComma	= FmtVLong << 1,
+	FmtByte		= FmtComma << 1,
+	FmtLDouble	= FmtByte << 1,
+
+	FmtFlag		= FmtLDouble << 1
+};
+
+extern	int	print(char*, ...);
+extern	char*	seprint(char*, char*, char*, ...);
+extern	char*	vseprint(char*, char*, char*, va_list);
+extern	int	snprint(char*, int, char*, ...);
+extern	int	vsnprint(char*, int, char*, va_list);
+extern	char*	smprint(char*, ...);
+extern	char*	vsmprint(char*, va_list);
+extern	int	sprint(char*, char*, ...);
+extern	int	fprint(int, char*, ...);
+extern	int	vfprint(int, char*, va_list);
+
+extern	int	runesprint(Rune*, char*, ...);
+extern	int	runesnprint(Rune*, int, char*, ...);
+extern	int	runevsnprint(Rune*, int, char*, va_list);
+extern	Rune*	runeseprint(Rune*, Rune*, char*, ...);
+extern	Rune*	runevseprint(Rune*, Rune*, char*, va_list);
+extern	Rune*	runesmprint(char*, ...);
+extern	Rune*	runevsmprint(char*, va_list);
+
+extern	int	fmtfdinit(Fmt*, int, char*, int);
+extern	int	fmtfdflush(Fmt*);
+extern	int	fmtstrinit(Fmt*);
+extern	char*	fmtstrflush(Fmt*);
+extern	int	runefmtstrinit(Fmt*);
+
+extern	int	quotestrfmt(Fmt *f);
+extern	void	quotefmtinstall(void);
+extern	int	(*fmtdoquote)(int);
+
+
+extern	int	fmtinstall(int, int (*)(Fmt*));
+extern	int	dofmt(Fmt*, char*);
+extern	int	fmtprint(Fmt*, char*, ...);
+extern	int	fmtvprint(Fmt*, char*, va_list);
+extern	int	fmtrune(Fmt*, int);
+extern	int	fmtstrcpy(Fmt*, char*);
+
+extern	double	fmtstrtod(const char *, char **);
+extern	double	fmtcharstod(int(*)(void*), void*);
+
+extern	int	werrstr(const char*, ...);
+
+#if defined(__cplusplus)
+}
+#endif
+#endif

+ 6 - 0
sys/include/ape/keyboard.h

@@ -0,0 +1,6 @@
+#ifndef _RESEARCH_SOURCE
+  This header file is not defined in ANSI or POSIX
+#endif
+
+#include "/sys/include/keyboard.h"
+

+ 6 - 0
sys/include/ape/mouse.h

@@ -0,0 +1,6 @@
+#ifndef _RESEARCH_SOURCE
+  This header file is not defined in ANSI or POSIX
+#endif
+
+#include "/sys/include/mouse.h"
+

+ 36 - 0
sys/include/ape/qlock.h

@@ -0,0 +1,36 @@
+#ifndef __QLOCK_H_
+#define __QLOCK_H
+#ifndef _QLOCK_EXTENSION
+  This header file is an extension to ANSI/POSIX
+#endif
+#pragma lib "/$M/lib/ape/lib9.a"
+
+typedef struct QLp QLp;
+struct QLp
+{
+	int	inuse;
+	QLp	*next;
+	char	state;
+};
+
+typedef
+struct QLock
+{
+	Lock	lock;
+	int	locked;
+	QLp	*head;
+	QLp 	*tail;
+} QLock;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern	void	qlock(QLock*);
+extern	void	qunlock(QLock*);
+extern	int	canqlock(QLock*);
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 60 - 0
sys/include/ape/utf.h

@@ -0,0 +1,60 @@
+#ifndef _UTF_H_
+#define _UTF_H_ 1
+#pragma lib "/$M/lib/ape/libutf.a"
+#pragma src "/sys/src/ape/lib/utf"
+
+#if defined(__cplusplus)
+extern "C" { 
+#endif
+
+typedef unsigned short Rune;	/* 16 bits */
+
+enum
+{
+	UTFmax		= 3,		/* maximum bytes per rune */
+	Runesync	= 0x80,		/* cannot represent part of a UTF sequence (<) */
+	Runeself	= 0x80,		/* rune and UTF sequences are the same (<) */
+	Runeerror	= 0x80,		/* decoding error in UTF */
+};
+
+/*
+ * rune routines
+ */
+extern	int	runetochar(char*, Rune*);
+extern	int	chartorune(Rune*, char*);
+extern	int	runelen(long);
+extern	int	runenlen(Rune*, int);
+extern	int	fullrune(char*, int);
+extern	int	utflen(char*);
+extern	int	utfnlen(char*, long);
+extern	char*	utfrune(char*, long);
+extern	char*	utfrrune(char*, long);
+extern	char*	utfutf(char*, char*);
+extern	char*	utfecpy(char*, char*, char*);
+
+extern	Rune*	runestrcat(Rune*, Rune*);
+extern	Rune*	runestrchr(Rune*, Rune);
+extern	int	runestrcmp(Rune*, Rune*);
+extern	Rune*	runestrcpy(Rune*, Rune*);
+extern	Rune*	runestrncpy(Rune*, Rune*, long);
+extern	Rune*	runestrecpy(Rune*, Rune*, Rune*);
+extern	Rune*	runestrdup(Rune*);
+extern	Rune*	runestrncat(Rune*, Rune*, long);
+extern	int	runestrncmp(Rune*, Rune*, long);
+extern	Rune*	runestrrchr(Rune*, Rune);
+extern	long	runestrlen(Rune*);
+extern	Rune*	runestrstr(Rune*, Rune*);
+
+extern	Rune	tolowerrune(Rune);
+extern	Rune	totitlerune(Rune);
+extern	Rune	toupperrune(Rune);
+extern	int	isalpharune(Rune);
+extern	int	islowerrune(Rune);
+extern	int	isspacerune(Rune);
+extern	int	istitlerune(Rune);
+extern	int	isupperrune(Rune);
+
+#if defined(__cplusplus)
+}
+#endif
+#endif

+ 2 - 2
sys/lib/plumb/basic

@@ -9,7 +9,7 @@ plumb to showmail
 
 # cheap http/gif,jpeg,ps,pdf renderer
 type is text
-data matches '(https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|prospero)://[a-zA-Z0-9_@\-]+([.:][a-zA-Z0-9_@\-]+)*/?[a-zA-Z0-9_?,%#~&/\-+=]+([:.][a-zA-Z0-9_?,%#~&/\-+=]+)*\.(jpe?g|JPE?G|gif|GIF|ps|PS|pdf|PDF)'
+data matches '(https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|prospero)://[a-zA-Z0-9_@\-]+([.:][a-zA-Z0-9_@\-]+)*/?[a-zA-Z0-9_?,%#~&/\-+=]+([:.][a-zA-Z0-9_?,%#~&/\-+=]+)*\.(jpe?g|JPE?G|gif|GIF|ps|PS|pdf|PDF|png|PNG)'
 plumb start rc -c 'hget '$0' | page -w'
 
 # rtf files go to wdoc2txt
@@ -35,7 +35,7 @@ plumb start window rc -c '''echo % mail '''$0'; mail '$0
 # image files go to page
 type is text
 data matches '[a-zA-Z¡-￿0-9_\-./]+'
-data matches '([a-zA-Z¡-￿0-9_\-./]+)\.(jpe?g|JPE?G|gif|GIF|tiff?|TIFF?|ppm|bit)'
+data matches '([a-zA-Z¡-￿0-9_\-./]+)\.(jpe?g|JPE?G|gif|GIF|tiff?|TIFF?|ppm|PPM|bit|BIT|png|PNG)'
 arg isfile	$0
 plumb to image
 plumb client page -wi

+ 3 - 0
sys/man/1/tar

@@ -77,6 +77,9 @@ a non-Plan 9 system.
 Use the next (numeric) argument as the group id for files in
 the output archive.
 .TP
+.BR p
+Create files in POSIX ustar format.
+.TP
 .B R
 When extracting, ignore leading slash on file names,
 i.e., extract all files relative to the current directory.

+ 7 - 1
sys/man/1/yesterday

@@ -4,7 +4,7 @@ yesterday \- print file names from the dump
 .SH SYNOPSIS
 .B yesterday
 [
-.B -abcCdD
+.B -abcCdDs
 ] [
 .B -n
 .I daysago
@@ -90,6 +90,12 @@ option selects the dump
 .I daysago
 prior to the current day.
 .PP
+The
+.B -s
+option selects the most recent snapshot instead of the most
+recent archived dump.  Snapshots may occur more frequently
+than dumps.
+.PP
 .I Yesterday
 does not guarantee that the string it prints represents an existing file.
 .SH EXAMPLES

+ 16 - 8
sys/src/9/ip/tcp.c

@@ -447,10 +447,11 @@ tcpstate(Conv *c, char *state, int n)
 	s = (Tcpctl*)(c->ptcl);
 
 	return snprint(state, n,
-		"%s srtt %d mdev %d cwin %lud swin %lud>>%d rwin %lud>>%d timer.start %d timer.count %d rerecv %d\n",
+		"%s srtt %d mdev %d cwin %lud swin %lud>>%d rwin %lud>>%d timer.start %d timer.count %d rerecv %d katimer.start %d katimer.count %d\n",
 		tcpstates[s->state], s->srtt, s->mdev,
 		s->cwind, s->snd.wnd, s->rcv.scale, s->rcv.wnd, s->snd.scale,
-		s->timer.start, s->timer.count, s->rerecv);
+		s->timer.start, s->timer.count, s->rerecv,
+		s->katimer.start, s->katimer.count);
 }
 
 static int
@@ -2065,8 +2066,8 @@ reset:
 	/* fix up window */
 	seg.wnd <<= tcb->rcv.scale;
 
-	if(tcb->kacounter > 0)
-		tcpsetkacounter(tcb);
+	/* every input packet in puts off the keep alive time out */
+	tcpsetkacounter(tcb);
 
 	switch(tcb->state) {
 	case Closed:
@@ -2576,8 +2577,9 @@ tcpoutput(Conv *s)
 		}
 
 		tpriv->stats[OutSegs]++;
-		if(tcb->kacounter > 0)
-			tcpgo(tpriv, &tcb->katimer);
+
+		/* put off the next keep alive */
+		tcpgo(tpriv, &tcb->katimer);
 
 		switch(version){
 		case V4:
@@ -2659,8 +2661,7 @@ tcpsendka(Conv *s)
 }
 
 /*
- *  if we've timed out, close the connection
- *  otherwise, send a keepalive and restart the timer
+ *  set connection to time out after 12 minutes
  */
 void
 tcpsetkacounter(Tcpctl *tcb)
@@ -2669,6 +2670,11 @@ tcpsetkacounter(Tcpctl *tcb)
 	if(tcb->kacounter < 3)
 		tcb->kacounter = 3;
 }
+
+/*
+ *  if we've timed out, close the connection
+ *  otherwise, send a keepalive and restart the timer
+ */
 void
 tcpkeepalive(void *v)
 {
@@ -2704,6 +2710,8 @@ tcpstartka(Conv *s, char **f, int n)
 	int x;
 
 	tcb = (Tcpctl*)s->ptcl;
+	if(tcb->state != Established)
+		return "connection must be in Establised state";
 	if(n > 1){
 		x = atoi(f[1]);
 		if(x >= MSPTICK)

+ 7 - 2
sys/src/9/port/qio.c

@@ -1300,6 +1300,9 @@ qwrite(Queue *q, void *vp, int len)
 /*
  *  used by print() to write to a queue.  Since we may be splhi or not in
  *  a process, don't qlock.
+ *
+ *  this routine merges adjacent blocks if block n+1 will fit into
+ *  the free space of block n.
  */
 int
 qiwrite(Queue *q, void *vp, int len)
@@ -1324,8 +1327,10 @@ qiwrite(Queue *q, void *vp, int len)
 
 		ilock(q);
 
-		/* don't queue over the limit, just lose the bytes */
-		if(q->len >= q->limit){
+		/* we use an artificially high limit for kernel prints since anything
+		 * over the limit gets dropped
+		 */
+		if(q->dlen >= 16*1024){
 			iunlock(q);
 			freeb(b);
 			break;

+ 4 - 0
sys/src/ape/lib/ap/plan9/9errstr.c

@@ -0,0 +1,4 @@
+#include "sys9.h"
+
+int
+_ERRSTR(

+ 14 - 0
sys/src/ape/lib/ap/plan9/9mallocz.c

@@ -0,0 +1,14 @@
+#include <stdlib.h>
+#include <string.h>
+
+void*
+_MALLOCZ(int n, int clr)
+{
+	void *v;
+
+	v = malloc(n);
+	if(v && clr)
+		memset(v, 0, n);
+	return v;
+}
+

+ 21 - 0
sys/src/ape/lib/ap/plan9/9readn.c

@@ -0,0 +1,21 @@
+#include "sys9.h"
+
+long
+_READN(int f, void *av, long n)
+{
+	char *a;
+	long m, t;
+
+	a = av;
+	t = 0;
+	while(t < n){
+		m = _READ(f, a+t, n-t);
+		if(m <= 0){
+			if(t == 0)
+				return m;
+			break;
+		}
+		t += m;
+	}
+	return t;
+}

+ 3 - 0
sys/src/ape/lib/ap/plan9/mkfile

@@ -10,7 +10,9 @@ OFILES=\
 	_fdinfo.$O\
 	_getpw.$O\
 	_nap.$O\
+	9mallocz.$O\
 	9read.$O\
+	9readn.$O\
 	9wait.$O\
 	9write.$O\
 	access.$O\
@@ -67,6 +69,7 @@ OFILES=\
 	pause.$O\
 	pipe.$O\
 	profile.$O\
+	qlock.$O\
 	raise.$O\
 	read.$O\
 	rename.$O\

+ 365 - 0
sys/src/ape/lib/ap/plan9/qlock.c

@@ -0,0 +1,365 @@
+#define _LOCK_EXTENSION
+#define _QLOCK_EXTENSION
+#define _RESEARCH_SOURCE
+#include <u.h>
+#include <lock.h>
+#include <qlock.h>
+#include <stdlib.h>
+#include "sys9.h"
+
+#define rendezvous _RENDEZVOUS
+#define _rendezvousp rendezvous
+#define _tas tas
+#define nelem(x) (sizeof(x)/sizeof((x)[0]))
+
+static struct {
+	QLp	*p;
+	QLp	x[1024];
+} ql = {
+	ql.x
+};
+
+enum
+{
+	Queuing,
+	QueuingR,
+	QueuingW,
+	Sleeping,
+};
+
+/* find a free shared memory location to queue ourselves in */
+static QLp*
+getqlp(void)
+{
+	QLp *p, *op;
+
+	op = ql.p;
+	for(p = op+1; ; p++){
+		if(p == &ql.x[nelem(ql.x)])
+			p = ql.x;
+		if(p == op)
+			abort();
+		if(_tas(&(p->inuse)) == 0){
+			ql.p = p;
+			p->next = nil;
+			break;
+		}
+	}
+	return p;
+}
+
+void
+qlock(QLock *q)
+{
+	QLp *p, *mp;
+
+	lock(&q->lock);
+	if(!q->locked){
+		q->locked = 1;
+		unlock(&q->lock);
+		return;
+	}
+
+
+	/* chain into waiting list */
+	mp = getqlp();
+	p = q->tail;
+	if(p == nil)
+		q->head = mp;
+	else
+		p->next = mp;
+	q->tail = mp;
+	mp->state = Queuing;
+	unlock(&q->lock);
+
+	/* wait */
+	while((*_rendezvousp)((ulong)mp, 1) == ~0)
+		;
+	mp->inuse = 0;
+}
+
+void
+qunlock(QLock *q)
+{
+	QLp *p;
+
+	lock(&q->lock);
+	p = q->head;
+	if(p != nil){
+		/* wakeup head waiting process */
+		q->head = p->next;
+		if(q->head == nil)
+			q->tail = nil;
+		unlock(&q->lock);
+		while((*_rendezvousp)((ulong)p, 0x12345) == ~0)
+			;
+		return;
+	}
+	q->locked = 0;
+	unlock(&q->lock);
+}
+
+int
+canqlock(QLock *q)
+{
+	if(!canlock(&q->lock))
+		return 0;
+	if(!q->locked){
+		q->locked = 1;
+		unlock(&q->lock);
+		return 1;
+	}
+	unlock(&q->lock);
+	return 0;
+}
+
+#if 0
+
+void
+rlock(RWLock *q)
+{
+	QLp *p, *mp;
+
+	lock(&q->lock);
+	if(q->writer == 0 && q->head == nil){
+		/* no writer, go for it */
+		q->readers++;
+		unlock(&q->lock);
+		return;
+	}
+
+	mp = getqlp();
+	p = q->tail;
+	if(p == 0)
+		q->head = mp;
+	else
+		p->next = mp;
+	q->tail = mp;
+	mp->next = nil;
+	mp->state = QueuingR;
+	unlock(&q->lock);
+
+	/* wait in kernel */
+	while((*_rendezvousp)((ulong)mp, 1) == ~0)
+		;
+	mp->inuse = 0;
+}
+
+int
+canrlock(RWLock *q)
+{
+	lock(&q->lock);
+	if (q->writer == 0 && q->head == nil) {
+		/* no writer; go for it */
+		q->readers++;
+		unlock(&q->lock);
+		return 1;
+	}
+	unlock(&q->lock);
+	return 0;
+}
+
+void
+runlock(RWLock *q)
+{
+	QLp *p;
+
+	lock(&q->lock);
+	if(q->readers <= 0)
+		abort();
+	p = q->head;
+	if(--(q->readers) > 0 || p == nil){
+		unlock(&q->lock);
+		return;
+	}
+
+	/* start waiting writer */
+	if(p->state != QueuingW)
+		abort();
+	q->head = p->next;
+	if(q->head == 0)
+		q->tail = 0;
+	q->writer = 1;
+	unlock(&q->lock);
+
+	/* wakeup waiter */
+	while((*_rendezvousp)((ulong)p, 0) == ~0)
+		;
+}
+
+void
+wlock(RWLock *q)
+{
+	QLp *p, *mp;
+
+	lock(&q->lock);
+	if(q->readers == 0 && q->writer == 0){
+		/* noone waiting, go for it */
+		q->writer = 1;
+		unlock(&q->lock);
+		return;
+	}
+
+	/* wait */
+	p = q->tail;
+	mp = getqlp();
+	if(p == nil)
+		q->head = mp;
+	else
+		p->next = mp;
+	q->tail = mp;
+	mp->next = nil;
+	mp->state = QueuingW;
+	unlock(&q->lock);
+
+	/* wait in kernel */
+	while((*_rendezvousp)((ulong)mp, 1) == ~0)
+		;
+	mp->inuse = 0;
+}
+
+int
+canwlock(RWLock *q)
+{
+	lock(&q->lock);
+	if (q->readers == 0 && q->writer == 0) {
+		/* no one waiting; go for it */
+		q->writer = 1;
+		unlock(&q->lock);
+		return 1;
+	}
+	unlock(&q->lock);
+	return 0;
+}
+
+void
+wunlock(RWLock *q)
+{
+	QLp *p;
+
+	lock(&q->lock);
+	if(q->writer == 0)
+		abort();
+	p = q->head;
+	if(p == nil){
+		q->writer = 0;
+		unlock(&q->lock);
+		return;
+	}
+	if(p->state == QueuingW){
+		/* start waiting writer */
+		q->head = p->next;
+		if(q->head == nil)
+			q->tail = nil;
+		unlock(&q->lock);
+		while((*_rendezvousp)((ulong)p, 0) == ~0)
+			;
+		return;
+	}
+
+	if(p->state != QueuingR)
+		abort();
+
+	/* wake waiting readers */
+	while(q->head != nil && q->head->state == QueuingR){
+		p = q->head;
+		q->head = p->next;
+		q->readers++;
+		while((*_rendezvousp)((ulong)p, 0) == ~0)
+			;
+	}
+	if(q->head == nil)
+		q->tail = nil;
+	q->writer = 0;
+	unlock(&q->lock);
+}
+
+void
+rsleep(Rendez *r)
+{
+	QLp *t, *me;
+
+	if(!r->l)
+		abort();
+	lock(&r->l->lock);
+	/* we should hold the qlock */
+	if(!r->l->locked)
+		abort();
+
+	/* add ourselves to the wait list */
+	me = getqlp();
+	me->state = Sleeping;
+	if(r->head == nil)
+		r->head = me;
+	else
+		r->tail->next = me;
+	me->next = nil;
+	r->tail = me;
+
+	/* pass the qlock to the next guy */
+	t = r->l->head;
+	if(t){
+		r->l->head = t->next;
+		if(r->l->head == nil)
+			r->l->tail = nil;
+		unlock(&r->l->lock);
+		while((*_rendezvousp)((ulong)t, 0x12345) == ~0)
+			;
+	}else{
+		r->l->locked = 0;
+		unlock(&r->l->lock);
+	}
+
+	/* wait for a wakeup */
+	while((*_rendezvousp)((ulong)me, 1) == ~0)
+		;
+	me->inuse = 0;
+}
+
+int
+rwakeup(Rendez *r)
+{
+	QLp *t;
+
+	/*
+	 * take off wait and put on front of queue
+	 * put on front so guys that have been waiting will not get starved
+	 */
+	
+	if(!r->l)
+		abort();
+	lock(&r->l->lock);
+	if(!r->l->locked)
+		abort();
+
+	t = r->head;
+	if(t == nil){
+		unlock(&r->l->lock);
+		return 0;
+	}
+
+	r->head = t->next;
+	if(r->head == nil)
+		r->tail = nil;
+
+	t->next = r->l->head;
+	r->l->head = t;
+	if(r->l->tail == nil)
+		r->l->tail = t;
+
+	t->state = Queuing;
+	unlock(&r->l->lock);
+	return 1;
+}
+
+int
+rwakeupall(Rendez *r)
+{
+	int i;
+
+	for(i=0; rwakeup(r); i++)
+		;
+	return i;
+}
+
+#endif

+ 203 - 0
sys/src/ape/lib/draw/colors.c

@@ -0,0 +1,203 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+
+char *argv0;
+
+static void
+_sysfatalimpl(char *fmt, va_list arg)
+{
+	char buf[1024];
+
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	if(argv0)
+		fprint(2, "%s: %s\n", argv0, buf);
+	else
+		fprint(2, "%s\n", buf);
+	exits(buf);
+}
+
+void (*_sysfatal)(char *fmt, va_list arg) = _sysfatalimpl;
+
+void
+sysfatal(char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	(*_sysfatal)(fmt, arg);
+	va_end(arg);
+}
+
+int nbit, npix;
+Image *pixel;
+Rectangle crect[256];
+
+Image *color[256];
+
+void
+eresized(int new)
+{
+	int x, y, i, n, nx, ny;
+	Rectangle r, b;
+
+	if(new && getwindow(display, Refnone) < 0){
+		fprint(2, "colors: can't reattach to window: %r\n");
+		exits("resized");
+	}
+	if(screen->depth > 8){
+		n = 256;
+		nx = 16;
+	}else{
+		n = 1<<screen->depth;
+		nx = 1<<(screen->depth/2);
+	}
+
+	ny = n/nx;
+	draw(screen, screen->r, display->white, nil, ZP);
+	r = insetrect(screen->r, 5);
+	r.min.y+=20;
+	b.max.y=r.min.y;
+	for(i=n-1, y=0; y!=ny; y++){
+		b.min.y=b.max.y;
+		b.max.y=r.min.y+(r.max.y-r.min.y)*(y+1)/ny;
+		b.max.x=r.min.x;
+		for(x=0; x!=nx; x++, --i){
+			b.min.x=b.max.x;
+			b.max.x=r.min.x+(r.max.x-r.min.x)*(x+1)/nx;
+			crect[i]=insetrect(b, 1);
+			draw(screen, crect[i], color[i], nil, ZP);
+		}
+	}
+	flushimage(display, 1);
+}
+
+char *buttons[] =
+{
+	"exit",
+	0
+};
+
+ulong
+grey(int i)
+{
+	if(i < 0)
+		return grey(0);
+	if(i > 255)
+		return grey(255);
+	return (i<<16)+(i<<8)+i;
+}
+
+Menu menu =
+{
+	buttons
+};
+
+int
+dither[16] =  {
+	0, 8, 2, 10,
+	12, 4, 14, 6,
+	3, 11, 1, 9,
+	15, 7, 13, 5
+};
+
+void
+main(int argc, char *argv[])
+{
+	Point p;
+	Mouse m;
+	int i, j, k, l, n, ramp, prev;
+	char buf[100];
+	char *fmt;
+	Image *dark;
+	ulong rgb;
+
+	ramp = 0;
+
+	fmt = "index %3d r %3lud g %3lud b %3lud 0x%.8luX        ";
+/*
+	ARGBEGIN{
+	default:
+		goto Usage;
+	case 'x':
+		fmt = "index %2luX r %3luX g %3luX b %3luX 0x%.8luX       ";
+		break;
+	case 'r':
+		ramp = 1;
+		break;
+	}ARGEND
+*/
+	argv0 = argv[0];
+	if(argc != 1){
+	Usage:
+		fprint(2, "Usage: %s [-rx]\n", argv0);
+		exits("usage");
+	}
+
+	if(initdraw(nil, nil, "colors") < 0)
+		sysfatal("initdraw failed: %r");
+	einit(Emouse);
+
+	for(i=0; i<256; i++){
+		if(ramp){
+			if(screen->chan == CMAP8){
+				/* dither the fine grey */
+				j = i-(i%17);
+				dark = allocimage(display, Rect(0,0,1,1), screen->chan, 1, (grey(j)<<8)+0xFF);
+				color[i] = allocimage(display, Rect(0,0,4,4), screen->chan, 1, (grey(j+17)<<8)+0xFF);
+				for(j=0; j<16; j++){
+					k = j%4;
+					l = j/4;
+					if(dither[j] > (i%17))
+						draw(color[i], Rect(k, l, k+1, l+1), dark, nil, ZP);
+				}
+				freeimage(dark);
+			}else
+				color[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, (grey(i)<<8)+0xFF);
+		}else
+			color[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, (cmap2rgb(i)<<8)+0xFF);
+		if(color[i] == nil)
+			sysfatal("can't allocate image: %r");
+	}
+	eresized(0);
+	prev = -1;
+	for(;;){
+		m = emouse();
+		switch(m.buttons){
+		case 1:
+			while(m.buttons){
+				if(screen->depth > 8)
+					n = 256;
+				else
+					n = 1<<screen->depth;
+				for(i=0; i!=n; i++)
+					if(i!=prev && ptinrect(m.xy, crect[i])){
+						if(ramp)
+							rgb = grey(i);
+						else
+							rgb = cmap2rgb(i);
+						sprint(buf, fmt,
+							i,
+							(rgb>>16)&0xFF,
+							(rgb>>8)&0xFF,
+							rgb&0xFF,
+							(rgb<<8) | 0xFF);
+						p = addpt(screen->r.min, Pt(2,2));
+						draw(screen, Rpt(p, addpt(p, stringsize(font, buf))), display->white, nil, p);
+						string(screen, p, display->black, ZP, font, buf);
+						prev=i;
+						break;
+					}
+				m = emouse();
+			}
+			break;
+
+		case 4:
+			switch(emenuhit(3, &m, &menu)){
+			case 0:
+				exits(0);
+			}
+		}
+	}
+}

+ 135 - 0
sys/src/ape/lib/draw/libc.h

@@ -0,0 +1,135 @@
+#define _LOCK_EXTENSION
+#define _QLOCK_EXTENSION
+#define _BSD_EXTENSION
+#include <sys/types.h>
+#include <lock.h>
+#include <qlock.h>
+#include <lib9.h>
+#include <stdlib.h>
+#include <string.h>
+#include <bsd.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <utf.h>
+#include <fmt.h>
+#include <signal.h>
+
+typedef
+struct Qid
+{
+	uvlong	path;
+	ulong	vers;
+	uchar	type;
+} Qid;
+
+typedef
+struct Dir {
+	/* system-modified data */
+	ushort	type;	/* server type */
+	uint	dev;	/* server subtype */
+	/* file data */
+	Qid	qid;	/* unique id from server */
+	ulong	mode;	/* permissions */
+	ulong	atime;	/* last read time */
+	ulong	mtime;	/* last write time */
+	vlong	length;	/* file length: see <u.h> */
+	char	*name;	/* last element of path */
+	char	*uid;	/* owner name */
+	char	*gid;	/* group name */
+	char	*muid;	/* last modifier name */
+} Dir;
+
+uint	_convM2D(uchar*, uint, Dir*, char*);
+uint	_convD2M(Dir*, uchar*, uint);
+Dir	*_dirstat(char*);
+int	_dirwstat(char*, Dir*);
+Dir	*_dirfstat(int);
+int	_dirfwstat(int, Dir*);
+long	_dirread(int, Dir**);
+long _dirreadall(int, Dir**);
+void _nulldir(Dir*);
+uint _sizeD2M(Dir*);
+
+typedef
+struct Waitmsg
+{
+	int pid;	/* of loved one */
+	unsigned long time[3];	/* of loved one & descendants */
+	char	*msg;
+} Waitmsg;
+
+
+extern	int	_AWAIT(char*, int);
+extern	int	_ALARM(unsigned long);
+extern	int	_BIND(const char*, const char*, int);
+extern	int	_CHDIR(const char*);
+extern	int	_CLOSE(int);
+extern	int	_CREATE(char*, int, unsigned long);
+extern	int	_DUP(int, int);
+extern	int	_ERRSTR(char*, unsigned int);
+extern	int	_EXEC(char*, char*[]);
+extern	void	_EXITS(char *);
+extern	int	_FD2PATH(int, char*, int);
+extern	int	_FAUTH(int, char*);
+extern	int	_FSESSION(int, char*, int);
+extern	int	_FSTAT(int, unsigned char*, int);
+extern	int	_FWSTAT(int, unsigned char*, int);
+extern	int	_MOUNT(int, int, const char*, int, const char*);
+extern	int	_NOTED(int);
+extern	int	_NOTIFY(int(*)(void*, char*));
+extern	int	_OPEN(const char*, int);
+extern	int	_PIPE(int*);
+extern	long	_PREAD(int, void*, long, long long);
+extern	long	_PWRITE(int, void*, long, long long);
+extern	long	_READ(int, void*, long);
+extern	int	_REMOVE(const char*);
+extern	int	_RENDEZVOUS(unsigned long, unsigned long);
+extern	int	_RFORK(int);
+extern	int	_SEGATTACH(int, char*, void*, unsigned long);
+extern	int	_SEGBRK(void*, void*);
+extern	int	_SEGDETACH(void*);
+extern	int	_SEGFLUSH(void*, unsigned long);
+extern	int	_SEGFREE(void*, unsigned long);
+extern	long long	_SEEK(int, long long, int);
+extern	int	_SLEEP(long);
+extern	int	_STAT(const char*, unsigned char*, int);
+extern	Waitmsg*	_WAIT(void);
+extern	long	_WRITE(int, const void*, long);
+extern	int	_WSTAT(const char*, unsigned char*, int);
+extern 	void *_MALLOCZ(int, int);
+extern	int	_WERRSTR(char*, ...);
+extern	long	_READN(int, void*, long);
+
+#define dirstat _dirstat
+#define dirfstat _dirfstat
+
+#define OREAD 0
+#define OWRITE 1
+#define ORDWR 2
+#define OCEXEC 32
+
+#define AREAD 4
+#define AWRITE 2
+#define AEXEC 1
+#define AEXIST 0
+
+#define open _OPEN
+#define close _CLOSE
+#define read _READ
+#define write _WRITE
+#define _exits(s) _exit(s && *(char*)s ? 1 : 0)
+#define exits(s) exit(s && *(char*)s ? 1 : 0)
+#define create _CREATE
+#define pread _PREAD
+#define readn _READN
+#define mallocz _MALLOCZ
+
+/* assume being called as in event.c */
+#define postnote(x, pid, msg) kill(pid, SIGTERM)
+#define atnotify(x, y) signal(SIGTERM, ekill)
+
+/* assume being called as in init.c */
+#define iounit(x) (32*1024)
+
+#define ERRMAX 128

+ 78 - 0
sys/src/ape/lib/draw/mkfile

@@ -0,0 +1,78 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libdraw.a
+
+OFILES=\
+	alloc.$O\
+	allocimagemix.$O\
+	arith.$O\
+	bezier.$O\
+	border.$O\
+	buildfont.$O\
+	bytesperline.$O\
+	chan.$O\
+	cloadimage.$O\
+	computil.$O\
+	creadimage.$O\
+	debug.$O\
+	defont.$O\
+	draw.$O\
+	drawrepl.$O\
+	egetrect.$O\
+	ellipse.$O\
+	emenuhit.$O\
+	event.$O\
+	fmt.$O\
+	font.$O\
+	freesubfont.$O\
+	getdefont.$O\
+	getsubfont.$O\
+	icossin.$O\
+	icossin2.$O\
+	init.$O\
+	line.$O\
+	mkfont.$O\
+	newwindow.$O\
+	openfont.$O\
+	poly.$O\
+	loadimage.$O\
+	readimage.$O\
+	readsubfont.$O\
+	rectclip.$O\
+	replclipr.$O\
+	rgb.$O\
+	string.$O\
+	stringbg.$O\
+	stringsubfont.$O\
+	stringwidth.$O\
+	subfont.$O\
+	subfontcache.$O\
+	subfontname.$O\
+	unloadimage.$O\
+	window.$O\
+	writecolmap.$O\
+	writeimage.$O\
+	writesubfont.$O\
+
+HFILES=\
+	/sys/include/ape/draw.h\
+	/sys/include/ape/event.h\
+	/sys/include/ape/mouse.h\
+	/sys/include/ape/keyboard.h\
+
+UPDATE=\
+	mkfile\
+	$HFILES\
+	${OFILES:%.$O=%.c}\
+	${LIB:/$objtype/%=/386/%}\
+
+</sys/src/cmd/mksyslib
+
+CFLAGS=-c $CFLAGS -D_POSIX_SOURCE -D_PLAN9_SOURCE -D_RESEARCH_SOURCE -I.
+
+%.$O: /sys/src/libdraw/%.c
+	$CC $CFLAGS /sys/src/libdraw/$stem.c
+
+$O.colors: colors.$O
+	$LD -o $target colors.$O

+ 85 - 0
sys/src/ape/lib/fmt/charstod.c

@@ -0,0 +1,85 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/*
+ * Reads a floating-point number by interpreting successive characters
+ * returned by (*f)(vp).  The last call it makes to f terminates the
+ * scan, so is not a character in the number.  It may therefore be
+ * necessary to back up the input stream up one byte after calling charstod.
+ */
+
+double
+fmtcharstod(int(*f)(void*), void *vp)
+{
+	double num, dem;
+	int neg, eneg, dig, exp, c;
+
+	num = 0;
+	neg = 0;
+	dig = 0;
+	exp = 0;
+	eneg = 0;
+
+	c = (*f)(vp);
+	while(c == ' ' || c == '\t')
+		c = (*f)(vp);
+	if(c == '-' || c == '+'){
+		if(c == '-')
+			neg = 1;
+		c = (*f)(vp);
+	}
+	while(c >= '0' && c <= '9'){
+		num = num*10 + c-'0';
+		c = (*f)(vp);
+	}
+	if(c == '.')
+		c = (*f)(vp);
+	while(c >= '0' && c <= '9'){
+		num = num*10 + c-'0';
+		dig++;
+		c = (*f)(vp);
+	}
+	if(c == 'e' || c == 'E'){
+		c = (*f)(vp);
+		if(c == '-' || c == '+'){
+			if(c == '-'){
+				dig = -dig;
+				eneg = 1;
+			}
+			c = (*f)(vp);
+		}
+		while(c >= '0' && c <= '9'){
+			exp = exp*10 + c-'0';
+			c = (*f)(vp);
+		}
+	}
+	exp -= dig;
+	if(exp < 0){
+		exp = -exp;
+		eneg = !eneg;
+	}
+	dem = __fmtpow10(exp);
+	if(eneg)
+		num /= dem;
+	else
+		num *= dem;
+	if(neg)
+		return -num;
+	return num;
+}

+ 558 - 0
sys/src/ape/lib/fmt/dofmt.c

@@ -0,0 +1,558 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/* format the output into f->to and return the number of characters fmted  */
+int
+dofmt(Fmt *f, char *fmt)
+{
+	Rune rune, *rt, *rs;
+	int r;
+	char *t, *s;
+	int n, nfmt;
+
+	nfmt = f->nfmt;
+	for(;;){
+		if(f->runes){
+			rt = (Rune*)f->to;
+			rs = (Rune*)f->stop;
+			while((r = *(uchar*)fmt) && r != '%'){
+				if(r < Runeself)
+					fmt++;
+				else{
+					fmt += chartorune(&rune, fmt);
+					r = rune;
+				}
+				FMTRCHAR(f, rt, rs, r);
+			}
+			fmt++;
+			f->nfmt += rt - (Rune *)f->to;
+			f->to = rt;
+			if(!r)
+				return f->nfmt - nfmt;
+			f->stop = rs;
+		}else{
+			t = (char*)f->to;
+			s = (char*)f->stop;
+			while((r = *(uchar*)fmt) && r != '%'){
+				if(r < Runeself){
+					FMTCHAR(f, t, s, r);
+					fmt++;
+				}else{
+					n = chartorune(&rune, fmt);
+					if(t + n > s){
+						t = (char*)__fmtflush(f, t, n);
+						if(t != nil)
+							s = (char*)f->stop;
+						else
+							return -1;
+					}
+					while(n--)
+						*t++ = *fmt++;
+				}
+			}
+			fmt++;
+			f->nfmt += t - (char *)f->to;
+			f->to = t;
+			if(!r)
+				return f->nfmt - nfmt;
+			f->stop = s;
+		}
+
+		fmt = (char*)__fmtdispatch(f, fmt, 0);
+		if(fmt == nil)
+			return -1;
+	}
+	return 0;	/* not reached */
+}
+
+void *
+__fmtflush(Fmt *f, void *t, int len)
+{
+	if(f->runes)
+		f->nfmt += (Rune*)t - (Rune*)f->to;
+	else
+		f->nfmt += (char*)t - (char *)f->to;
+	f->to = t;
+	if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > (char*)f->stop){
+		f->stop = f->to;
+		return nil;
+	}
+	return f->to;
+}
+
+/*
+ * put a formatted block of memory sz bytes long of n runes into the output buffer,
+ * left/right justified in a field of at least f->width charactes
+ */
+int
+__fmtpad(Fmt *f, int n)
+{
+	char *t, *s;
+	int i;
+
+	t = (char*)f->to;
+	s = (char*)f->stop;
+	for(i = 0; i < n; i++)
+		FMTCHAR(f, t, s, ' ');
+	f->nfmt += t - (char *)f->to;
+	f->to = t;
+	return 0;
+}
+
+int
+__rfmtpad(Fmt *f, int n)
+{
+	Rune *t, *s;
+	int i;
+
+	t = (Rune*)f->to;
+	s = (Rune*)f->stop;
+	for(i = 0; i < n; i++)
+		FMTRCHAR(f, t, s, ' ');
+	f->nfmt += t - (Rune *)f->to;
+	f->to = t;
+	return 0;
+}
+
+int
+__fmtcpy(Fmt *f, const void *vm, int n, int sz)
+{
+	Rune *rt, *rs, r;
+	char *t, *s, *m, *me;
+	ulong fl;
+	int nc, w;
+
+	m = (char*)vm;
+	me = m + sz;
+	w = f->width;
+	fl = f->flags;
+	if((fl & FmtPrec) && n > f->prec)
+		n = f->prec;
+	if(f->runes){
+		if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0)
+			return -1;
+		rt = (Rune*)f->to;
+		rs = (Rune*)f->stop;
+		for(nc = n; nc > 0; nc--){
+			r = *(uchar*)m;
+			if(r < Runeself)
+				m++;
+			else if((me - m) >= UTFmax || fullrune(m, me-m))
+				m += chartorune(&r, m);
+			else
+				break;
+			FMTRCHAR(f, rt, rs, r);
+		}
+		f->nfmt += rt - (Rune *)f->to;
+		f->to = rt;
+		if(m < me)
+			return -1;
+		if(fl & FmtLeft && __rfmtpad(f, w - n) < 0)
+			return -1;
+	}else{
+		if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0)
+			return -1;
+		t = (char*)f->to;
+		s = (char*)f->stop;
+		for(nc = n; nc > 0; nc--){
+			r = *(uchar*)m;
+			if(r < Runeself)
+				m++;
+			else if((me - m) >= UTFmax || fullrune(m, me-m))
+				m += chartorune(&r, m);
+			else
+				break;
+			FMTRUNE(f, t, s, r);
+		}
+		f->nfmt += t - (char *)f->to;
+		f->to = t;
+		if(fl & FmtLeft && __fmtpad(f, w - n) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+int
+__fmtrcpy(Fmt *f, const void *vm, int n)
+{
+	Rune r, *m, *me, *rt, *rs;
+	char *t, *s;
+	ulong fl;
+	int w;
+
+	m = (Rune*)vm;
+	w = f->width;
+	fl = f->flags;
+	if((fl & FmtPrec) && n > f->prec)
+		n = f->prec;
+	if(f->runes){
+		if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0)
+			return -1;
+		rt = (Rune*)f->to;
+		rs = (Rune*)f->stop;
+		for(me = m + n; m < me; m++)
+			FMTRCHAR(f, rt, rs, *m);
+		f->nfmt += rt - (Rune *)f->to;
+		f->to = rt;
+		if(fl & FmtLeft && __rfmtpad(f, w - n) < 0)
+			return -1;
+	}else{
+		if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0)
+			return -1;
+		t = (char*)f->to;
+		s = (char*)f->stop;
+		for(me = m + n; m < me; m++){
+			r = *m;
+			FMTRUNE(f, t, s, r);
+		}
+		f->nfmt += t - (char *)f->to;
+		f->to = t;
+		if(fl & FmtLeft && __fmtpad(f, w - n) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+/* fmt out one character */
+int
+__charfmt(Fmt *f)
+{
+	char x[1];
+
+	x[0] = va_arg(f->args, int);
+	f->prec = 1;
+	return __fmtcpy(f, (const char*)x, 1, 1);
+}
+
+/* fmt out one rune */
+int
+__runefmt(Fmt *f)
+{
+	Rune x[1];
+
+	x[0] = va_arg(f->args, int);
+	return __fmtrcpy(f, (const void*)x, 1);
+}
+
+/* public helper routine: fmt out a null terminated string already in hand */
+int
+fmtstrcpy(Fmt *f, char *s)
+{
+	int p, i;
+	if(!s)
+		return __fmtcpy(f, "<nil>", 5, 5);
+	/* if precision is specified, make sure we don't wander off the end */
+	if(f->flags & FmtPrec){
+		p = f->prec;
+		for(i = 0; i < p; i++)
+			if(s[i] == 0)
+				break;
+		return __fmtcpy(f, s, utfnlen(s, i), i);	/* BUG?: won't print a partial rune at end */
+	}
+
+	return __fmtcpy(f, s, utflen(s), strlen(s));
+}
+
+/* fmt out a null terminated utf string */
+int
+__strfmt(Fmt *f)
+{
+	char *s;
+
+	s = va_arg(f->args, char *);
+	return fmtstrcpy(f, s);
+}
+
+/* public helper routine: fmt out a null terminated rune string already in hand */
+int
+fmtrunestrcpy(Fmt *f, Rune *s)
+{
+	Rune *e;
+	int n, p;
+
+	if(!s)
+		return __fmtcpy(f, "<nil>", 5, 5);
+	/* if precision is specified, make sure we don't wander off the end */
+	if(f->flags & FmtPrec){
+		p = f->prec;
+		for(n = 0; n < p; n++)
+			if(s[n] == 0)
+				break;
+	}else{
+		for(e = s; *e; e++)
+			;
+		n = e - s;
+	}
+	return __fmtrcpy(f, s, n);
+}
+
+/* fmt out a null terminated rune string */
+int
+__runesfmt(Fmt *f)
+{
+	Rune *s;
+
+	s = va_arg(f->args, Rune *);
+	return fmtrunestrcpy(f, s);
+}
+
+/* fmt a % */
+int
+__percentfmt(Fmt *f)
+{
+	Rune x[1];
+
+	x[0] = f->r;
+	f->prec = 1;
+	return __fmtrcpy(f, (const void*)x, 1);
+}
+
+/* fmt an integer */
+int
+__ifmt(Fmt *f)
+{
+	char buf[70], *p, *conv;
+	uvlong vu;
+	ulong u;
+	int neg, base, i, n, fl, w, isv;
+
+	neg = 0;
+	fl = f->flags;
+	isv = 0;
+	vu = 0;
+	u = 0;
+	/*
+	 * Unsigned verbs
+	 */
+	switch(f->r){
+	case 'o':
+	case 'u':
+	case 'x':
+	case 'X':
+		fl |= FmtUnsigned;
+		break;
+	}
+	if(f->r == 'p'){
+		u = (ulong)va_arg(f->args, void*);
+		f->r = 'x';
+		fl |= FmtUnsigned;
+	}else if(fl & FmtVLong){
+		isv = 1;
+		if(fl & FmtUnsigned)
+			vu = va_arg(f->args, uvlong);
+		else
+			vu = va_arg(f->args, vlong);
+	}else if(fl & FmtLong){
+		if(fl & FmtUnsigned)
+			u = va_arg(f->args, ulong);
+		else
+			u = va_arg(f->args, long);
+	}else if(fl & FmtByte){
+		if(fl & FmtUnsigned)
+			u = (uchar)va_arg(f->args, int);
+		else
+			u = (char)va_arg(f->args, int);
+	}else if(fl & FmtShort){
+		if(fl & FmtUnsigned)
+			u = (ushort)va_arg(f->args, int);
+		else
+			u = (short)va_arg(f->args, int);
+	}else{
+		if(fl & FmtUnsigned)
+			u = va_arg(f->args, uint);
+		else
+			u = va_arg(f->args, int);
+	}
+	conv = "0123456789abcdef";
+	switch(f->r){
+	case 'd':
+	case 'i':
+		base = 10;
+		break;
+	case 'u':
+		base = 10;
+		break;
+	case 'x':
+		base = 16;
+		break;
+	case 'X':
+		base = 16;
+		conv = "0123456789ABCDEF";
+		break;
+	case 'b':
+		base = 2;
+		break;
+	case 'o':
+		base = 8;
+		break;
+	default:
+		return -1;
+	}
+	if(!(fl & FmtUnsigned)){
+		if(isv && (vlong)vu < 0){
+			vu = -(vlong)vu;
+			neg = 1;
+		}else if(!isv && (long)u < 0){
+			u = -(long)u;
+			neg = 1;
+		}
+	}else{
+		fl &= ~(FmtSign|FmtSpace);	/* no + for unsigned conversions */
+	}
+	p = buf + sizeof buf - 1;
+	n = 0;
+	if(isv){
+		while(vu){
+			i = vu % base;
+			vu /= base;
+			if((fl & FmtComma) && n % 4 == 3){
+				*p-- = ',';
+				n++;
+			}
+			*p-- = conv[i];
+			n++;
+		}
+	}else{
+		while(u){
+			i = u % base;
+			u /= base;
+			if((fl & FmtComma) && n % 4 == 3){
+				*p-- = ',';
+				n++;
+			}
+			*p-- = conv[i];
+			n++;
+		}
+	}
+	if(n == 0){
+		if(!(fl & FmtPrec) || f->prec != 0){
+			*p-- = '0';
+			n = 1;
+		}
+		fl &= ~FmtSharp;
+	}
+	for(w = f->prec; n < w && p > buf+3; n++)
+		*p-- = '0';
+	if(neg || (fl & (FmtSign|FmtSpace)))
+		n++;
+	if(fl & FmtSharp){
+		if(base == 16)
+			n += 2;
+		else if(base == 8){
+			if(p[1] == '0')
+				fl &= ~FmtSharp;
+			else
+				n++;
+		}
+	}
+	if((fl & FmtZero) && !(fl & (FmtLeft|FmtPrec))){
+		for(w = f->width; n < w && p > buf+3; n++)
+			*p-- = '0';
+		f->width = 0;
+	}
+	if(fl & FmtSharp){
+		if(base == 16)
+			*p-- = f->r;
+		if(base == 16 || base == 8)
+			*p-- = '0';
+	}
+	if(neg)
+		*p-- = '-';
+	else if(fl & FmtSign)
+		*p-- = '+';
+	else if(fl & FmtSpace)
+		*p-- = ' ';
+	f->flags &= ~FmtPrec;
+	return __fmtcpy(f, p + 1, n, n);
+}
+
+int
+__countfmt(Fmt *f)
+{
+	void *p;
+	ulong fl;
+
+	fl = f->flags;
+	p = va_arg(f->args, void*);
+	if(fl & FmtVLong){
+		*(vlong*)p = f->nfmt;
+	}else if(fl & FmtLong){
+		*(long*)p = f->nfmt;
+	}else if(fl & FmtByte){
+		*(char*)p = f->nfmt;
+	}else if(fl & FmtShort){
+		*(short*)p = f->nfmt;
+	}else{
+		*(int*)p = f->nfmt;
+	}
+	return 0;
+}
+
+int
+__flagfmt(Fmt *f)
+{
+	switch(f->r){
+	case ',':
+		f->flags |= FmtComma;
+		break;
+	case '-':
+		f->flags |= FmtLeft;
+		break;
+	case '+':
+		f->flags |= FmtSign;
+		break;
+	case '#':
+		f->flags |= FmtSharp;
+		break;
+	case ' ':
+		f->flags |= FmtSpace;
+		break;
+	case 'u':
+		f->flags |= FmtUnsigned;
+		break;
+	case 'h':
+		if(f->flags & FmtShort)
+			f->flags |= FmtByte;
+		f->flags |= FmtShort;
+		break;
+	case 'L':
+		f->flags |= FmtLDouble;
+		break;
+	case 'l':
+		if(f->flags & FmtLong)
+			f->flags |= FmtVLong;
+		f->flags |= FmtLong;
+		break;
+	}
+	return 1;
+}
+
+/* default error format */
+int
+__badfmt(Fmt *f)
+{
+	char x[3];
+
+	x[0] = '%';
+	x[1] = f->r;
+	x[2] = '%';
+	f->prec = 3;
+	__fmtcpy(f, (const void*)x, 3, 3);
+	return 0;
+}

+ 61 - 0
sys/src/ape/lib/fmt/dorfmt.c

@@ -0,0 +1,61 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/* format the output into f->to and return the number of characters fmted  */
+
+int
+dorfmt(Fmt *f, const Rune *fmt)
+{
+	Rune *rt, *rs;
+	int r;
+	char *t, *s;
+	int nfmt;
+
+	nfmt = f->nfmt;
+	for(;;){
+		if(f->runes){
+			rt = f->to;
+			rs = f->stop;
+			while((r = *fmt++) && r != '%'){
+				FMTRCHAR(f, rt, rs, r);
+			}
+			f->nfmt += rt - (Rune *)f->to;
+			f->to = rt;
+			if(!r)
+				return f->nfmt - nfmt;
+			f->stop = rs;
+		}else{
+			t = f->to;
+			s = f->stop;
+			while((r = *fmt++) && r != '%'){
+				FMTRUNE(f, t, f->stop, r);
+			}
+			f->nfmt += t - (char *)f->to;
+			f->to = t;
+			if(!r)
+				return f->nfmt - nfmt;
+			f->stop = s;
+		}
+
+		fmt = __fmtdispatch(f, fmt, 1);
+		if(fmt == nil)
+			return -1;
+	}
+	return 0;		/* not reached */
+}

+ 28 - 0
sys/src/ape/lib/fmt/errfmt.c

@@ -0,0 +1,28 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+__errfmt(Fmt *f)
+{
+	char *s;
+
+	s = strerror(errno);
+	return fmtstrcpy(f, s);
+}

+ 610 - 0
sys/src/ape/lib/fmt/fltfmt.c

@@ -0,0 +1,610 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdio.h>
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#include "fmt.h"
+#include "fmtdef.h"
+#include "nan.h"
+
+enum
+{
+	FDEFLT	= 6,
+	NSIGNIF	= 17
+};
+
+/*
+ * first few powers of 10, enough for about 1/2 of the
+ * total space for doubles.
+ */
+static double pows10[] =
+{
+	  1e0,   1e1,   1e2,   1e3,   1e4,   1e5,   1e6,   1e7,   1e8,   1e9,  
+	 1e10,  1e11,  1e12,  1e13,  1e14,  1e15,  1e16,  1e17,  1e18,  1e19,  
+	 1e20,  1e21,  1e22,  1e23,  1e24,  1e25,  1e26,  1e27,  1e28,  1e29,  
+	 1e30,  1e31,  1e32,  1e33,  1e34,  1e35,  1e36,  1e37,  1e38,  1e39,  
+	 1e40,  1e41,  1e42,  1e43,  1e44,  1e45,  1e46,  1e47,  1e48,  1e49,  
+	 1e50,  1e51,  1e52,  1e53,  1e54,  1e55,  1e56,  1e57,  1e58,  1e59,  
+	 1e60,  1e61,  1e62,  1e63,  1e64,  1e65,  1e66,  1e67,  1e68,  1e69,  
+	 1e70,  1e71,  1e72,  1e73,  1e74,  1e75,  1e76,  1e77,  1e78,  1e79,  
+	 1e80,  1e81,  1e82,  1e83,  1e84,  1e85,  1e86,  1e87,  1e88,  1e89,  
+	 1e90,  1e91,  1e92,  1e93,  1e94,  1e95,  1e96,  1e97,  1e98,  1e99,  
+	1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, 
+	1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, 
+	1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, 
+	1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, 
+	1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, 
+	1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, 
+};
+
+static double
+pow10(int n)
+{
+	double d;
+	int neg;
+
+	neg = 0;
+	if(n < 0){
+		if(n < DBL_MIN_10_EXP){
+			return 0.;
+		}
+		neg = 1;
+		n = -n;
+	}else if(n > DBL_MAX_10_EXP){
+		return HUGE_VAL;
+	}
+	if(n < (int)(sizeof(pows10)/sizeof(pows10[0])))
+		d = pows10[n];
+	else{
+		d = pows10[sizeof(pows10)/sizeof(pows10[0]) - 1];
+		for(;;){
+			n -= sizeof(pows10)/sizeof(pows10[0]) - 1;
+			if(n < (int)(sizeof(pows10)/sizeof(pows10[0]))){
+				d *= pows10[n];
+				break;
+			}
+			d *= pows10[sizeof(pows10)/sizeof(pows10[0]) - 1];
+		}
+	}
+	if(neg){
+		return 1./d;
+	}
+	return d;
+}
+
+static int
+xadd(char *a, int n, int v)
+{
+	char *b;
+	int c;
+
+	if(n < 0 || n >= NSIGNIF)
+		return 0;
+	for(b = a+n; b >= a; b--) {
+		c = *b + v;
+		if(c <= '9') {
+			*b = c;
+			return 0;
+		}
+		*b = '0';
+		v = 1;
+	}
+	*a = '1';	/* overflow adding */
+	return 1;
+}
+
+static int
+xsub(char *a, int n, int v)
+{
+	char *b;
+	int c;
+
+	for(b = a+n; b >= a; b--) {
+		c = *b - v;
+		if(c >= '0') {
+			*b = c;
+			return 0;
+		}
+		*b = '9';
+		v = 1;
+	}
+	*a = '9';	/* underflow subtracting */
+	return 1;
+}
+
+static void
+xaddexp(char *p, int e)
+{
+	char se[9];
+	int i;
+
+	*p++ = 'e';
+	if(e < 0) {
+		*p++ = '-';
+		e = -e;
+	}
+	i = 0;
+	while(e) {
+		se[i++] = e % 10 + '0';
+		e /= 10;
+	}
+	if(i == 0) {
+		*p++ = '0';
+	} else {
+		while(i > 0)
+			*p++ = se[--i];
+	}
+	*p++ = '\0';
+}
+
+static char*
+xdodtoa(char *s1, double f, int chr, int prec, int *decpt, int *rsign)
+{
+	char s2[NSIGNIF+10];
+	double g, h;
+	int e, d, i;
+	int c2, sign, oerr;
+
+	if(chr == 'F')
+		chr = 'f';
+	if(prec > NSIGNIF)
+		prec = NSIGNIF;
+	if(prec < 0)
+		prec = 0;
+	if(__isNaN(f)) {
+		*decpt = 9999;
+		*rsign = 0;
+		strcpy(s1, "nan");
+		return &s1[3];
+	}
+	sign = 0;
+	if(f < 0) {
+		f = -f;
+		sign++;
+	}
+	*rsign = sign;
+	if(__isInf(f, 1) || __isInf(f, -1)) {
+		*decpt = 9999;
+		strcpy(s1, "inf");
+		return &s1[3];
+	}
+
+	e = 0;
+	g = f;
+	if(g != 0) {
+		frexp(f, &e);
+		e = (int)(e * .301029995664);
+		if(e >= -150 && e <= +150) {
+			d = 0;
+			h = f;
+		} else {
+			d = e/2;
+			h = f * pow10(-d);
+		}
+		g = h * pow10(d-e);
+		while(g < 1) {
+			e--;
+			g = h * pow10(d-e);
+		}
+		while(g >= 10) {
+			e++;
+			g = h * pow10(d-e);
+		}
+	}
+
+	/*
+	 * convert NSIGNIF digits and convert
+	 * back to get accuracy.
+	 */
+	for(i=0; i<NSIGNIF; i++) {
+		d = (int)g;
+		s1[i] = d + '0';
+		g = (g - d) * 10;
+	}
+	s1[i] = 0;
+
+	/*
+	 * try decimal rounding to eliminate 9s
+	 */
+	c2 = prec + 1;
+	if(chr == 'f')
+		c2 += e;
+	oerr = errno;
+	if(c2 >= NSIGNIF-2) {
+		strcpy(s2, s1);
+		d = e;
+		s1[NSIGNIF-2] = '0';
+		s1[NSIGNIF-1] = '0';
+		xaddexp(s1+NSIGNIF, e-NSIGNIF+1);
+		g = fmtstrtod(s1, nil);
+		if(g == f)
+			goto found;
+		if(xadd(s1, NSIGNIF-3, 1)) {
+			e++;
+			xaddexp(s1+NSIGNIF, e-NSIGNIF+1);
+		}
+		g = fmtstrtod(s1, nil);
+		if(g == f)
+			goto found;
+		strcpy(s1, s2);
+		e = d;
+	}
+
+	/*
+	 * convert back so s1 gets exact answer
+	 */
+	for(d = 0; d < 10; d++) {
+		xaddexp(s1+NSIGNIF, e-NSIGNIF+1);
+		g = fmtstrtod(s1, nil);
+		if(f > g) {
+			if(xadd(s1, NSIGNIF-1, 1))
+				e--;
+			continue;
+		}
+		if(f < g) {
+			if(xsub(s1, NSIGNIF-1, 1))
+				e++;
+			continue;
+		}
+		break;
+	}
+
+found:
+	errno = oerr;
+
+	/*
+	 * sign
+	 */
+	d = 0;
+	i = 0;
+
+	/*
+	 * round & adjust 'f' digits
+	 */
+	c2 = prec + 1;
+	if(chr == 'f'){
+		if(xadd(s1, c2+e, 5))
+			e++;
+		c2 += e;
+		if(c2 < 0){
+			c2 = 0;
+			e = -prec - 1;
+		}
+	}else{
+		if(xadd(s1, c2, 5))
+			e++;
+	}
+	if(c2 > NSIGNIF){
+		c2 = NSIGNIF;
+	}
+
+	*decpt = e + 1;
+
+	/*
+	 * terminate the converted digits
+	 */
+	s1[c2] = '\0';
+	return &s1[c2];
+}
+
+/*
+ * this function works like the standard dtoa, if you want it.
+ */
+#if 0
+static char*
+__dtoa(double f, int mode, int ndigits, int *decpt, int *rsign, char **rve)
+{
+	static char s2[NSIGNIF + 10];
+	char *es;
+	int chr, prec;
+
+	switch(mode) {
+	/* like 'e' */
+	case 2:
+	case 4:
+	case 6:
+	case 8:
+		chr = 'e';
+		break;
+	/* like 'g' */
+	case 0:
+	case 1:
+	default:
+		chr = 'g';
+		break;
+	/* like 'f' */
+	case 3:
+	case 5:
+	case 7:
+	case 9:
+		chr = 'f';
+		break;
+	}
+
+	if(chr != 'f' && ndigits){
+		ndigits--;
+	}
+	prec = ndigits;
+	if(prec > NSIGNIF)
+		prec = NSIGNIF;
+	if(ndigits == 0)
+		prec = NSIGNIF;
+	es = xdodtoa(s2, f, chr, prec, decpt, rsign);
+
+	/*
+	 * strip trailing 0
+	 */
+	for(; es > s2 + 1; es--){
+		if(es[-1] != '0'){
+			break;
+		}
+	}
+	*es = '\0';
+	if(rve != NULL)
+		*rve = es;
+	return s2;
+}
+#endif
+
+static int
+fmtzdotpad(Fmt *f, int n, int pt)
+{
+	char *t, *s;
+	int i;
+	Rune *rt, *rs;
+
+	if(f->runes){
+		rt = (Rune*)f->to;
+		rs = (Rune*)f->stop;
+		for(i = 0; i < n; i++){
+			if(i == pt){
+				FMTRCHAR(f, rt, rs, '.');
+			}
+			FMTRCHAR(f, rt, rs, '0');
+		}
+		f->nfmt += rt - (Rune*)f->to;
+		f->to = rt;
+	}else{
+		t = (char*)f->to;
+		s = (char*)f->stop;
+		for(i = 0; i < n; i++){
+			if(i == pt){
+				FMTCHAR(f, t, s, '.');
+			}
+			FMTCHAR(f, t, s, '0');
+		}
+		f->nfmt += t - (char *)f->to;
+		f->to = t;
+	}
+	return 0;
+}
+
+int
+__efgfmt(Fmt *fmt)
+{
+	double f;
+	char s1[NSIGNIF+10];
+	int e, d, n;
+	int c1, c2, c3, c4, ucase, sign, chr, prec, fl;
+
+	f = va_arg(fmt->args, double);
+	prec = FDEFLT;
+	fl = fmt->flags;
+	fmt->flags = 0;
+	if(fl & FmtPrec)
+		prec = fmt->prec;
+	chr = fmt->r;
+	ucase = 0;
+	if(chr == 'E'){
+		chr = 'e';
+		ucase = 1;
+	}else if(chr == 'F'){
+		chr = 'f';
+		ucase = 1;
+	}else if(chr == 'G'){
+		chr = 'g';
+		ucase = 1;
+	}
+	if(prec > 0 && chr == 'g')
+		prec--;
+	if(prec < 0)
+		prec = 0;
+
+	xdodtoa(s1, f, chr, prec, &e, &sign);
+	e--;
+	if(*s1 == 'i' || *s1 == 'n'){
+		if(ucase){
+			if(*s1 == 'i'){
+				strcpy(s1, "INF");
+			}else{
+				strcpy(s1, "NAN");
+			}
+		}
+		fmt->flags = fl & (FmtWidth|FmtLeft);
+		return __fmtcpy(fmt, (const void*)s1, 3, 3);
+	}
+
+	/*
+	 * copy into final place
+	 * c1 digits of leading '0'
+	 * c2 digits from conversion
+	 * c3 digits of trailing '0'
+	 * c4 digits after '.'
+	 */
+	c1 = 0;
+	c2 = prec + 1;
+	c3 = 0;
+	c4 = prec;
+	switch(chr) {
+	default:
+		chr = 'e';
+		break;
+	case 'g':
+		/*
+		 * decide on 'e' of 'f' style convers
+		 */
+		if(e >= -4 && e <= prec) {
+			c1 = -e;
+			c4 = prec - e;
+			chr = 'h';	/* flag for 'f' style */
+		}
+		break;
+	case 'f':
+		c1 = -e;
+		if(c1 > prec)
+			c1 = prec + 1;
+		c2 += e;
+		break;
+	}
+
+	/*
+	 * clean up c1 c2 and c3
+	 */
+	if(c1 < 0)
+		c1 = 0;
+	if(c2 < 0)
+		c2 = 0;
+	if(c2 > NSIGNIF) {
+		c3 = c2-NSIGNIF;
+		c2 = NSIGNIF;
+	}
+
+	/*
+	 * trim trailing zeros for %g
+	 */
+	if(!(fl & FmtSharp)
+	&& (chr == 'g' || chr == 'h')){
+		if(c4 >= c3){
+			c4 -= c3;
+			c3 = 0;
+		}else{
+			c3 -= c4;
+			c4 = 0;
+		}
+		while(c4 && c2 > 1 && s1[c2 - 1] == '0'){
+			c4--;
+			c2--;
+		}
+	}
+
+	/*
+	 * calculate the total length
+	 */
+	n = c1 + c2 + c3;
+	if(sign || (fl & (FmtSign|FmtSpace)))
+		n++;
+	if(c4 || (fl & FmtSharp)){
+		n++;
+	}
+	if(chr == 'e' || chr == 'g'){
+		n += 4;
+		if(e >= 100)
+			n++;
+	}
+
+	/*
+	 * pad to width if right justified
+	 */
+	if((fl & (FmtWidth|FmtLeft)) == FmtWidth && n < fmt->width){
+		if(fl & FmtZero){
+			c1 += fmt->width - n;
+		}else{
+			if(__fmtpad(fmt, fmt->width - n) < 0){
+				return -1;
+			}
+		}
+	}
+
+	/*
+	 * sign
+	 */
+	d = 0;
+	if(sign)
+		d = '-';
+	else if(fl & FmtSign)
+		d = '+';
+	else if(fl & FmtSpace)
+		d = ' ';
+	if(d && fmtrune(fmt, d) < 0){
+		return -1;
+	}
+
+	/*
+	 * copy digits
+	 */
+	c4 = c1 + c2 + c3 - c4;
+	if(c1 > 0){
+		if(fmtzdotpad(fmt, c1, c4) < 0){
+			return -1;
+		}
+		c4 -= c1;
+	}
+	d = 0;
+	if(c4 >= 0 && c4 < c2){
+		if(__fmtcpy(fmt, s1, c4, c4) < 0 || fmtrune(fmt, '.') < 0)
+			return -1;
+		d = c4;
+		c2 -= c4;
+		c4 = -1;
+	}
+	if(__fmtcpy(fmt, (const void*)(s1 + d), c2, c2) < 0){
+		return -1;
+	}
+	c4 -= c2;
+	if(c3 > 0){
+		if(fmtzdotpad(fmt, c3, c4) < 0){
+			return -1;
+		}
+		c4 -= c3;
+	}
+
+	/*
+	 * strip trailing '0' on g conv
+	 */
+	if((fl & FmtSharp) && c4 == 0 && fmtrune(fmt, '.') < 0){
+		return -1;
+	}
+	if(chr == 'e' || chr == 'g') {
+		d = 0;
+		if(ucase)
+			s1[d++] = 'E';
+		else
+			s1[d++] = 'e';
+		c1 = e;
+		if(c1 < 0) {
+			s1[d++] = '-';
+			c1 = -c1;
+		} else
+			s1[d++] = '+';
+		if(c1 >= 100) {
+			s1[d++] = c1/100 + '0';
+			c1 = c1%100;
+		}
+		s1[d++] = c1/10 + '0';
+		s1[d++] = c1%10 + '0';
+		if(__fmtcpy(fmt, s1, d, d) < 0){
+			return -1;
+		}
+	}
+	if((fl & (FmtWidth|FmtLeft)) == (FmtWidth|FmtLeft) && n < fmt->width){
+		if(__fmtpad(fmt, fmt->width - n) < 0){
+			return -1;
+		}
+	}
+	return 0;
+}

+ 221 - 0
sys/src/ape/lib/fmt/fmt.c

@@ -0,0 +1,221 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+enum
+{
+	Maxfmt = 64
+};
+
+typedef struct Convfmt Convfmt;
+struct Convfmt
+{
+	int	c;
+	volatile	Fmts	fmt;	/* for spin lock in fmtfmt; avoids race due to write order */
+};
+
+struct
+{
+	/* lock by calling __fmtlock, __fmtunlock */
+	int	nfmt;
+	Convfmt	fmt[Maxfmt];
+} fmtalloc;
+
+static Convfmt knownfmt[] = {
+	' ',	__flagfmt,
+	'#',	__flagfmt,
+	'%',	__percentfmt,
+	'+',	__flagfmt,
+	',',	__flagfmt,
+	'-',	__flagfmt,
+	'C',	__runefmt,	/* Plan 9 addition */
+	'E',	__efgfmt,
+	'F',	__efgfmt,	/* ANSI only */
+	'G',	__efgfmt,
+	'L',	__flagfmt,	/* ANSI only */
+	'S',	__runesfmt,	/* Plan 9 addition */
+	'X',	__ifmt,
+	'b',	__ifmt,		/* Plan 9 addition */
+	'c',	__charfmt,
+	'd',	__ifmt,
+	'e',	__efgfmt,
+	'f',	__efgfmt,
+	'g',	__efgfmt,
+	'h',	__flagfmt,
+	'i',	__ifmt,		/* ANSI only */
+	'l',	__flagfmt,
+	'n',	__countfmt,
+	'o',	__ifmt,
+	'p',	__ifmt,
+	'r',	__errfmt,
+	's',	__strfmt,
+	'u',	__flagfmt,	/* in Unix, __ifmt */
+	'x',	__ifmt,
+	0,	nil,
+};
+
+
+int	(*fmtdoquote)(int);
+
+/*
+ * __fmtlock() must be set
+ */
+static int
+__fmtinstall(int c, Fmts f)
+{
+	Convfmt *p, *ep;
+
+	if(c<=0 || c>=65536)
+		return -1;
+	if(!f)
+		f = __badfmt;
+
+	ep = &fmtalloc.fmt[fmtalloc.nfmt];
+	for(p=fmtalloc.fmt; p<ep; p++)
+		if(p->c == c)
+			break;
+
+	if(p == &fmtalloc.fmt[Maxfmt])
+		return -1;
+
+	p->fmt = f;
+	if(p == ep){	/* installing a new format character */
+		fmtalloc.nfmt++;
+		p->c = c;
+	}
+
+	return 0;
+}
+
+int
+fmtinstall(int c, Fmts f)
+{
+	int ret;
+
+	__fmtlock();
+	ret = __fmtinstall(c, f);
+	__fmtunlock();
+	return ret;
+}
+
+static Fmts
+fmtfmt(int c)
+{
+	Convfmt *p, *ep;
+
+	ep = &fmtalloc.fmt[fmtalloc.nfmt];
+	for(p=fmtalloc.fmt; p<ep; p++)
+		if(p->c == c){
+			while(p->fmt == nil)	/* loop until value is updated */
+				;
+			return p->fmt;
+		}
+
+	/* is this a predefined format char? */
+	__fmtlock();
+	for(p=knownfmt; p->c; p++)
+		if(p->c == c){
+			__fmtinstall(p->c, p->fmt);
+			__fmtunlock();
+			return p->fmt;
+		}
+	__fmtunlock();
+
+	return __badfmt;
+}
+
+void*
+__fmtdispatch(Fmt *f, void *fmt, int isrunes)
+{
+	Rune rune, r;
+	int i, n;
+
+	f->flags = 0;
+	f->width = f->prec = 0;
+
+	for(;;){
+		if(isrunes){
+			r = *(Rune*)fmt;
+			fmt = (Rune*)fmt + 1;
+		}else{
+			fmt = (char*)fmt + chartorune(&rune, (char*)fmt);
+			r = rune;
+		}
+		f->r = r;
+		switch(r){
+		case '\0':
+			return nil;
+		case '.':
+			f->flags |= FmtWidth|FmtPrec;
+			continue;
+		case '0':
+			if(!(f->flags & FmtWidth)){
+				f->flags |= FmtZero;
+				continue;
+			}
+			/* fall through */
+		case '1': case '2': case '3': case '4':
+		case '5': case '6': case '7': case '8': case '9':
+			i = 0;
+			while(r >= '0' && r <= '9'){
+				i = i * 10 + r - '0';
+				if(isrunes){
+					r = *(Rune*)fmt;
+					fmt = (Rune*)fmt + 1;
+				}else{
+					r = *(char*)fmt;
+					fmt = (char*)fmt + 1;
+				}
+			}
+			if(isrunes)
+				fmt = (Rune*)fmt - 1;
+			else
+				fmt = (char*)fmt - 1;
+		numflag:
+			if(f->flags & FmtWidth){
+				f->flags |= FmtPrec;
+				f->prec = i;
+			}else{
+				f->flags |= FmtWidth;
+				f->width = i;
+			}
+			continue;
+		case '*':
+			i = va_arg(f->args, int);
+			if(i < 0){
+				/*
+				 * negative precision =>
+				 * ignore the precision.
+				 */
+				if(f->flags & FmtPrec){
+					f->flags &= ~FmtPrec;
+					f->prec = 0;
+					continue;
+				}
+				i = -i;
+				f->flags |= FmtLeft;
+			}
+			goto numflag;
+		}
+		n = (*fmtfmt(r))(f);
+		if(n < 0)
+			return nil;
+		if(n == 0)
+			return fmt;
+	}
+}

+ 121 - 0
sys/src/ape/lib/fmt/fmtdef.h

@@ -0,0 +1,121 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+/*
+ * dofmt -- format to a buffer
+ * the number of characters formatted is returned,
+ * or -1 if there was an error.
+ * if the buffer is ever filled, flush is called.
+ * it should reset the buffer and return whether formatting should continue.
+ */
+#define uchar _fmtuchar
+#define ushort _fmtushort
+#define uint _fmtuint
+#define ulong _fmtulong
+#define vlong _fmtvlong
+#define uvlong _fmtuvlong
+
+#define USED(x) if(x);else
+
+typedef unsigned char		uchar;
+typedef unsigned short		ushort;
+typedef unsigned int		uint;
+typedef unsigned long		ulong;
+
+#ifndef NOVLONGS
+typedef unsigned long long	uvlong;
+typedef long long		vlong;
+#endif
+
+#define nil		0	/* cannot be ((void*)0) because used for function pointers */
+
+typedef int (*Fmts)(Fmt*);
+
+typedef struct Quoteinfo Quoteinfo;
+struct Quoteinfo
+{
+	int	quoted;		/* if set, string must be quoted */
+	int	nrunesin;	/* number of input runes that can be accepted */
+	int	nbytesin;	/* number of input bytes that can be accepted */
+	int	nrunesout;	/* number of runes that will be generated */
+	int	nbytesout;	/* number of bytes that will be generated */
+};
+
+void	*__fmtflush(Fmt*, void*, int);
+void	*__fmtdispatch(Fmt*, void*, int);
+int	__floatfmt(Fmt*, double);
+int	__fmtpad(Fmt*, int);
+int	__rfmtpad(Fmt*, int);
+int	__fmtFdFlush(Fmt*);
+
+int	__efgfmt(Fmt*);
+int	__charfmt(Fmt*);
+int	__runefmt(Fmt*);
+int	__runesfmt(Fmt*);
+int	__countfmt(Fmt*);
+int	__flagfmt(Fmt*);
+int	__percentfmt(Fmt*);
+int	__ifmt(Fmt*);
+int	__strfmt(Fmt*);
+int	__badfmt(Fmt*);
+int	__fmtcpy(Fmt*, const void*, int, int);
+int	__fmtrcpy(Fmt*, const void*, int n);
+int	__errfmt(Fmt *f);
+
+double	__fmtpow10(int);
+
+void	__fmtlock(void);
+void	__fmtunlock(void);
+
+#define FMTCHAR(f, t, s, c)\
+	do{\
+	if(t + 1 > (char*)s){\
+		t = __fmtflush(f, t, 1);\
+		if(t != nil)\
+			s = f->stop;\
+		else\
+			return -1;\
+	}\
+	*t++ = c;\
+	}while(0)
+
+#define FMTRCHAR(f, t, s, c)\
+	do{\
+	if(t + 1 > (Rune*)s){\
+		t = __fmtflush(f, t, sizeof(Rune));\
+		if(t != nil)\
+			s = f->stop;\
+		else\
+			return -1;\
+	}\
+	*t++ = c;\
+	}while(0)
+
+#define FMTRUNE(f, t, s, r)\
+	do{\
+	Rune _rune;\
+	int _runelen;\
+	if(t + UTFmax > (char*)s && t + (_runelen = runelen(r)) > (char*)s){\
+		t = __fmtflush(f, t, _runelen);\
+		if(t != nil)\
+			s = f->stop;\
+		else\
+			return -1;\
+	}\
+	if(r < Runeself)\
+		*t++ = r;\
+	else{\
+		_rune = r;\
+		t += runetochar(t, &_rune);\
+	}\
+	}while(0)

+ 46 - 0
sys/src/ape/lib/fmt/fmtfd.c

@@ -0,0 +1,46 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/*
+ * public routine for final flush of a formatting buffer
+ * to a file descriptor; returns total char count.
+ */
+int
+fmtfdflush(Fmt *f)
+{
+	if(__fmtFdFlush(f) <= 0)
+		return -1;
+	return f->nfmt;
+}
+
+/*
+ * initialize an output buffer for buffered printing
+ */
+int
+fmtfdinit(Fmt *f, int fd, char *buf, int size)
+{
+	f->runes = 0;
+	f->start = buf;
+	f->to = buf;
+	f->stop = buf + size;
+	f->flush = __fmtFdFlush;
+	f->farg = (void*)fd;
+	f->nfmt = 0;
+	return 0;
+}

+ 33 - 0
sys/src/ape/lib/fmt/fmtfdflush.c

@@ -0,0 +1,33 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <unistd.h>
+#include "fmt.h"
+#include "fmtdef.h"
+
+/*
+ * generic routine for flushing a formatting buffer
+ * to a file descriptor
+ */
+int
+__fmtFdFlush(Fmt *f)
+{
+	int n;
+
+	n = (char*)f->to - (char*)f->start;
+	if(n && write((int)f->farg, f->start, n) != n)
+		return 0;
+	f->to = f->start;
+	return 1;
+}

+ 28 - 0
sys/src/ape/lib/fmt/fmtlock.c

@@ -0,0 +1,28 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include "fmt.h"
+#include "fmtdef.h"
+
+void
+__fmtlock(void)
+{
+	;
+}
+
+void
+__fmtunlock(void)
+{
+	;
+}

+ 47 - 0
sys/src/ape/lib/fmt/fmtprint.c

@@ -0,0 +1,47 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+
+/*
+ * format a string into the output buffer
+ * designed for formats which themselves call fmt,
+ * but ignore any width flags
+ */
+int
+fmtprint(Fmt *f, char *fmt, ...)
+{
+	va_list va;
+	int n;
+
+	f->flags = 0;
+	f->width = 0;
+	f->prec = 0;
+	va = f->args;
+	va_start(f->args, fmt);
+	n = dofmt(f, fmt);
+	va_end(f->args);
+	f->flags = 0;
+	f->width = 0;
+	f->prec = 0;
+	f->args = va;
+	if(n >= 0)
+		return 0;
+	return n;
+}
+

+ 262 - 0
sys/src/ape/lib/fmt/fmtquote.c

@@ -0,0 +1,262 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/*
+ * How many bytes of output UTF will be produced by quoting (if necessary) this string?
+ * How many runes? How much of the input will be consumed?
+ * The parameter q is filled in by __quotesetup.
+ * The string may be UTF or Runes (s or r).
+ * Return count does not include NUL.
+ * Terminate the scan at the first of:
+ *	NUL in input
+ *	count exceeded in input
+ *	count exceeded on output
+ * *ninp is set to number of input bytes accepted.
+ * nin may be <0 initially, to avoid checking input by count.
+ */
+void
+__quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int runesout)
+{
+	int w;
+	Rune c;
+
+	q->quoted = 0;
+	q->nbytesout = 0;
+	q->nrunesout = 0;
+	q->nbytesin = 0;
+	q->nrunesin = 0;
+	if(sharp || nin==0 || (s && *s=='\0') || (r && *r=='\0')){
+		if(nout < 2)
+			return;
+		q->quoted = 1;
+		q->nbytesout = 2;
+		q->nrunesout = 2;
+	}
+	for(; nin!=0; nin-=w){
+		if(s)
+			w = chartorune(&c, s);
+		else{
+			c = *r;
+			w = runelen(c);
+		}
+
+		if(c == '\0')
+			break;
+		if(runesout){
+			if(q->nrunesout+1 > nout)
+				break;
+		}else{
+			if(q->nbytesout+w > nout)
+				break;
+		}
+
+		if((c <= L' ') || (c == L'\'') || (fmtdoquote!=nil && fmtdoquote(c))){
+			if(!q->quoted){
+				if(runesout){
+					if(1+q->nrunesout+1+1 > nout)	/* no room for quotes */
+						break;
+				}else{
+					if(1+q->nbytesout+w+1 > nout)	/* no room for quotes */
+						break;
+				}
+				q->nrunesout += 2;	/* include quotes */
+				q->nbytesout += 2;	/* include quotes */
+				q->quoted = 1;
+			}
+			if(c == '\'')	{
+				if(runesout){
+					if(1+q->nrunesout+1 > nout)	/* no room for quotes */
+						break;
+				}else{
+					if(1+q->nbytesout+w > nout)	/* no room for quotes */
+						break;
+				}
+				q->nbytesout++;
+				q->nrunesout++;	/* quotes reproduce as two characters */
+			}
+		}
+
+		/* advance input */
+		if(s)
+			s += w;
+		else
+			r++;
+		q->nbytesin += w;
+		q->nrunesin++;
+
+		/* advance output */
+		q->nbytesout += w;
+		q->nrunesout++;
+	}
+}
+
+static int
+qstrfmt(char *sin, Rune *rin, Quoteinfo *q, Fmt *f)
+{
+	Rune r, *rm, *rme;
+	char *t, *s, *m, *me;
+	Rune *rt, *rs;
+	ulong fl;
+	int nc, w;
+
+	m = sin;
+	me = m + q->nbytesin;
+	rm = rin;
+	rme = rm + q->nrunesin;
+
+	w = f->width;
+	fl = f->flags;
+	if(f->runes){
+		if(!(fl & FmtLeft) && __rfmtpad(f, w - q->nrunesout) < 0)
+			return -1;
+	}else{
+		if(!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0)
+			return -1;
+	}
+	t = (char*)f->to;
+	s = (char*)f->stop;
+	rt = (Rune*)f->to;
+	rs = (Rune*)f->stop;
+	if(f->runes)
+		FMTRCHAR(f, rt, rs, '\'');
+	else
+		FMTRUNE(f, t, s, '\'');
+	for(nc = q->nrunesin; nc > 0; nc--){
+		if(sin){
+			r = *(uchar*)m;
+			if(r < Runeself)
+				m++;
+			else if((me - m) >= UTFmax || fullrune(m, me-m))
+				m += chartorune(&r, m);
+			else
+				break;
+		}else{
+			if(rm >= rme)
+				break;
+			r = *(uchar*)rm++;
+		}
+		if(f->runes){
+			FMTRCHAR(f, rt, rs, r);
+			if(r == '\'')
+				FMTRCHAR(f, rt, rs, r);
+		}else{
+			FMTRUNE(f, t, s, r);
+			if(r == '\'')
+				FMTRUNE(f, t, s, r);
+		}
+	}
+
+	if(f->runes){
+		FMTRCHAR(f, rt, rs, '\'');
+		USED(rs);
+		f->nfmt += rt - (Rune *)f->to;
+		f->to = rt;
+		if(fl & FmtLeft && __rfmtpad(f, w - q->nrunesout) < 0)
+			return -1;
+	}else{
+		FMTRUNE(f, t, s, '\'');
+		USED(s);
+		f->nfmt += t - (char *)f->to;
+		f->to = t;
+		if(fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+int
+__quotestrfmt(int runesin, Fmt *f)
+{
+	int outlen;
+	Rune *r;
+	char *s;
+	Quoteinfo q;
+
+	f->flags &= ~FmtPrec;	/* ignored for %q %Q, so disable for %s %S in easy case */
+	if(runesin){
+		r = va_arg(f->args, Rune *);
+		s = nil;
+	}else{
+		s = va_arg(f->args, char *);
+		r = nil;
+	}
+	if(!s && !r)
+		return __fmtcpy(f, (void*)"<nil>", 5, 5);
+
+	if(f->flush)
+		outlen = 0x7FFFFFFF;	/* if we can flush, no output limit */
+	else if(f->runes)
+		outlen = (Rune*)f->stop - (Rune*)f->to;
+	else
+		outlen = (char*)f->stop - (char*)f->to;
+
+	__quotesetup(s, r, -1, outlen, &q, f->flags&FmtSharp, f->runes);
+//print("bytes in %d bytes out %d runes in %d runesout %d\n", q.nbytesin, q.nbytesout, q.nrunesin, q.nrunesout);
+
+	if(runesin){
+		if(!q.quoted)
+			return __fmtrcpy(f, r, q.nrunesin);
+		return qstrfmt(nil, r, &q, f);
+	}
+
+	if(!q.quoted)
+		return __fmtcpy(f, s, q.nrunesin, q.nbytesin);
+	return qstrfmt(s, nil, &q, f);
+}
+
+int
+quotestrfmt(Fmt *f)
+{
+	return __quotestrfmt(0, f);
+}
+
+int
+quoterunestrfmt(Fmt *f)
+{
+	return __quotestrfmt(1, f);
+}
+
+void
+quotefmtinstall(void)
+{
+	fmtinstall('q', quotestrfmt);
+	fmtinstall('Q', quoterunestrfmt);
+}
+
+int
+__needsquotes(char *s, int *quotelenp)
+{
+	Quoteinfo q;
+
+	__quotesetup(s, nil, -1, 0x7FFFFFFF, &q, 0, 0);
+	*quotelenp = q.nbytesout;
+
+	return q.quoted;
+}
+
+int
+__runeneedsquotes(Rune *r, int *quotelenp)
+{
+	Quoteinfo q;
+
+	__quotesetup(nil, r, -1, 0x7FFFFFFF, &q, 0, 0);
+	*quotelenp = q.nrunesout;
+
+	return q.quoted;
+}

+ 40 - 0
sys/src/ape/lib/fmt/fmtrune.c

@@ -0,0 +1,40 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+fmtrune(Fmt *f, int r)
+{
+	Rune *rt;
+	char *t;
+	int n;
+
+	if(f->runes){
+		rt = (Rune*)f->to;
+		FMTRCHAR(f, rt, f->stop, r);
+		f->to = rt;
+		n = 1;
+	}else{
+		t = (char*)f->to;
+		FMTRUNE(f, t, f->stop, r);
+		n = t - (char*)f->to;
+		f->to = t;
+	}
+	f->nfmt += n;
+	return 0;
+}

+ 65 - 0
sys/src/ape/lib/fmt/fmtstr.c

@@ -0,0 +1,65 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+static int
+fmtStrFlush(Fmt *f)
+{
+	char *s;
+	int n;
+
+	n = (int)f->farg;
+	n += 256;
+	f->farg = (void*)n;
+	s = (char*)f->start;
+	f->start = realloc(s, n);
+	if(f->start == nil){
+		f->start = s;
+		return 0;
+	}
+	f->to = (char*)f->start + ((char*)f->to - s);
+	f->stop = (char*)f->start + n - 1;
+	return 1;
+}
+
+int
+fmtstrinit(Fmt *f)
+{
+	int n;
+
+	f->runes = 0;
+	n = 32;
+	f->start = malloc(n);
+	if(f->start == nil)
+		return -1;
+	f->to = f->start;
+	f->stop = (char*)f->start + n - 1;
+	f->flush = fmtStrFlush;
+	f->farg = (void*)n;
+	f->nfmt = 0;
+	return 0;
+}
+
+char*
+fmtstrflush(Fmt *f)
+{
+	*(char*)f->to = '\0';
+	f->to = f->start;
+	return (char*)f->start;
+}

+ 46 - 0
sys/src/ape/lib/fmt/fmtvprint.c

@@ -0,0 +1,46 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+
+/*
+ * format a string into the output buffer
+ * designed for formats which themselves call fmt,
+ * but ignore any width flags
+ */
+int
+fmtvprint(Fmt *f, char *fmt, va_list args)
+{
+	va_list va;
+	int n;
+
+	f->flags = 0;
+	f->width = 0;
+	f->prec = 0;
+	va = f->args;
+	f->args = args;
+	n = dofmt(f, fmt);
+	f->flags = 0;
+	f->width = 0;
+	f->prec = 0;
+	f->args = va;
+	if(n >= 0)
+		return 0;
+	return n;
+}
+

+ 28 - 0
sys/src/ape/lib/fmt/fprint.c

@@ -0,0 +1,28 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include "utf.h"
+#include "fmt.h"
+
+int
+fprint(int fd, char *fmt, ...)
+{
+	int n;
+	va_list args;
+
+	va_start(args, fmt);
+	n = vfprint(fd, fmt, args);
+	va_end(args);
+	return n;
+}

+ 59 - 0
sys/src/ape/lib/fmt/mkfile

@@ -0,0 +1,59 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libfmt.a
+
+NUM=\
+	charstod.$O\
+	pow10.$O\
+
+OFILES=\
+	dofmt.$O\
+	errfmt.$O\
+	fltfmt.$O\
+	fmt.$O\
+	fmtfd.$O\
+	fmtfdflush.$O\
+	fmtlock.$O\
+	fmtprint.$O\
+	fmtquote.$O\
+	fmtrune.$O\
+	fmtstr.$O\
+	fmtvprint.$O\
+	fprint.$O\
+	nan64.$O\
+	print.$O\
+	runefmtstr.$O\
+	runeseprint.$O\
+	runesmprint.$O\
+	runesnprint.$O\
+	runesprint.$O\
+	runevseprint.$O\
+	runevsmprint.$O\
+	runevsnprint.$O\
+	seprint.$O\
+	smprint.$O\
+	snprint.$O\
+	sprint.$O\
+	strtod.$O\
+	vfprint.$O\
+	vseprint.$O\
+	vsmprint.$O\
+	vsnprint.$O\
+	werrstr.$O\
+	$NUM\
+
+HFILES=\
+	fmtdef.h\
+	/sys/include/ape/fmt.h\
+
+</sys/src/cmd/mksyslib
+
+$NAN.$O: nan.h
+strtod.$O: nan.h
+
+test: $LIB test.$O
+	$CC -o test test.$O $LIB -L$PLAN9/lib -lutf
+
+
+CFLAGS=-c -D_POSIX_SOURCE -D_PLAN9_SOURCE -D_BSD_EXTENSION

+ 4 - 0
sys/src/ape/lib/fmt/nan.h

@@ -0,0 +1,4 @@
+extern double __NaN(void);
+extern double __Inf(int);
+extern int __isNaN(double);
+extern int __isInf(double, int);

+ 76 - 0
sys/src/ape/lib/fmt/nan64.c

@@ -0,0 +1,76 @@
+/*
+ * 64-bit IEEE not-a-number routines.
+ * This is big/little-endian portable assuming that 
+ * the 64-bit doubles and 64-bit integers have the
+ * same byte ordering.
+ */
+
+#include "nan.h"
+
+#ifdef __APPLE__
+#define _NEEDLL
+#endif
+
+typedef unsigned long long uvlong;
+typedef unsigned long ulong;
+
+#ifdef _NEEDLL
+static uvlong uvnan    = 0x7FF0000000000001LL;
+static uvlong uvinf    = 0x7FF0000000000000LL;
+static uvlong uvneginf = 0xFFF0000000000000LL;
+#else
+static uvlong uvnan    = 0x7FF0000000000001;
+static uvlong uvinf    = 0x7FF0000000000000;
+static uvlong uvneginf = 0xFFF0000000000000;
+#endif
+
+double
+__NaN(void)
+{
+	uvlong *p;
+
+	/* gcc complains about "return *(double*)&uvnan;" */
+	p = &uvnan;
+	return *(double*)p;
+}
+
+int
+__isNaN(double d)
+{
+	uvlong x;
+	double *p;
+
+	p = &d;
+	x = *(uvlong*)p;
+	return (ulong)(x>>32)==0x7FF00000 && !__isInf(d, 0);
+}
+
+double
+__Inf(int sign)
+{
+	uvlong *p;
+
+	if(sign < 0)
+		p = &uvinf;
+	else
+		p = &uvneginf;
+	return *(double*)p;
+}
+
+int
+__isInf(double d, int sign)
+{
+	uvlong x;
+	double *p;
+
+	p = &d;
+	x = *(uvlong*)p;
+	if(sign == 0)
+		return x==uvinf || x==uvneginf;
+	else if(sign > 0)
+		return x==uvinf;
+	else
+		return x==uvneginf;
+}
+
+

+ 57 - 0
sys/src/ape/lib/fmt/pow10.c

@@ -0,0 +1,57 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/*
+ * this table might overflow 127-bit exponent representations.
+ * in that case, truncate it after 1.0e38.
+ * it is important to get all one can from this
+ * routine since it is used in atof to scale numbers.
+ * the presumption is that C converts fp numbers better
+ * than multipication of lower powers of 10.
+ */
+
+static
+double	tab[] =
+{
+	1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9,
+	1.0e10,1.0e11,1.0e12,1.0e13,1.0e14,1.0e15,1.0e16,1.0e17,1.0e18,1.0e19,
+	1.0e20,1.0e21,1.0e22,1.0e23,1.0e24,1.0e25,1.0e26,1.0e27,1.0e28,1.0e29,
+	1.0e30,1.0e31,1.0e32,1.0e33,1.0e34,1.0e35,1.0e36,1.0e37,1.0e38,1.0e39,
+	1.0e40,1.0e41,1.0e42,1.0e43,1.0e44,1.0e45,1.0e46,1.0e47,1.0e48,1.0e49,
+	1.0e50,1.0e51,1.0e52,1.0e53,1.0e54,1.0e55,1.0e56,1.0e57,1.0e58,1.0e59,
+	1.0e60,1.0e61,1.0e62,1.0e63,1.0e64,1.0e65,1.0e66,1.0e67,1.0e68,1.0e69,
+};
+
+double
+__fmtpow10(int n)
+{
+	int m;
+
+	if(n < 0) {
+		n = -n;
+		if(n < (int)(sizeof(tab)/sizeof(tab[0])))
+			return 1/tab[n];
+		m = n/2;
+		return __fmtpow10(-m) * __fmtpow10(m-n);
+	}
+	if(n < (int)(sizeof(tab)/sizeof(tab[0])))
+		return tab[n];
+	m = n/2;
+	return __fmtpow10(m) * __fmtpow10(n-m);
+}

+ 28 - 0
sys/src/ape/lib/fmt/print.c

@@ -0,0 +1,28 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include "utf.h"
+#include "fmt.h"
+
+int
+print(char *fmt, ...)
+{
+	int n;
+	va_list args;
+
+	va_start(args, fmt);
+	n = vfprint(1, fmt, args);
+	va_end(args);
+	return n;
+}

+ 65 - 0
sys/src/ape/lib/fmt/runefmtstr.c

@@ -0,0 +1,65 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+static int
+runeFmtStrFlush(Fmt *f)
+{
+	Rune *s;
+	int n;
+
+	n = (int)f->farg;
+	n += 256;
+	f->farg = (void*)n;
+	s = (Rune*)f->start;
+	f->start = realloc(s, sizeof(Rune)*n);
+	if(f->start == nil){
+		f->start = s;
+		return 0;
+	}
+	f->to = (Rune*)f->start + ((Rune*)f->to - s);
+	f->stop = (Rune*)f->start + n - 1;
+	return 1;
+}
+
+int
+runefmtstrinit(Fmt *f)
+{
+	int n;
+
+	f->runes = 1;
+	n = 32;
+	f->start = malloc(sizeof(Rune)*n);
+	if(f->start == nil)
+		return -1;
+	f->to = f->start;
+	f->stop = (Rune*)f->start + n - 1;
+	f->flush = runeFmtStrFlush;
+	f->farg = (void*)n;
+	f->nfmt = 0;
+	return 0;
+}
+
+Rune*
+runefmtstrflush(Fmt *f)
+{
+	*(Rune*)f->to = '\0';
+	f->to = f->start;
+	return f->start;
+}

+ 30 - 0
sys/src/ape/lib/fmt/runeseprint.c

@@ -0,0 +1,30 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+Rune*
+runeseprint(Rune *buf, Rune *e, char *fmt, ...)
+{
+	Rune *p;
+	va_list args;
+
+	va_start(args, fmt);
+	p = runevseprint(buf, e, fmt, args);
+	va_end(args);
+	return p;
+}

+ 30 - 0
sys/src/ape/lib/fmt/runesmprint.c

@@ -0,0 +1,30 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+Rune*
+runesmprint(char *fmt, ...)
+{
+	va_list args;
+	Rune *p;
+
+	va_start(args, fmt);
+	p = runevsmprint(fmt, args);
+	va_end(args);
+	return p;
+}

+ 31 - 0
sys/src/ape/lib/fmt/runesnprint.c

@@ -0,0 +1,31 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+runesnprint(Rune *buf, int len, char *fmt, ...)
+{
+	int n;
+	va_list args;
+
+	va_start(args, fmt);
+	n = runevsnprint(buf, len, fmt, args);
+	va_end(args);
+	return n;
+}
+

+ 30 - 0
sys/src/ape/lib/fmt/runesprint.c

@@ -0,0 +1,30 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+runesprint(Rune *buf, char *fmt, ...)
+{
+	int n;
+	va_list args;
+
+	va_start(args, fmt);
+	n = runevsnprint(buf, 256, fmt, args);
+	va_end(args);
+	return n;
+}

+ 39 - 0
sys/src/ape/lib/fmt/runevseprint.c

@@ -0,0 +1,39 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+Rune*
+runevseprint(Rune *buf, Rune *e, char *fmt, va_list args)
+{
+	Fmt f;
+
+	if(e <= buf)
+		return nil;
+	f.runes = 1;
+	f.start = buf;
+	f.to = buf;
+	f.stop = e - 1;
+	f.flush = nil;
+	f.farg = nil;
+	f.nfmt = 0;
+	f.args = args;
+	dofmt(&f, fmt);
+	*(Rune*)f.to = '\0';
+	return (Rune*)f.to;
+}
+

+ 37 - 0
sys/src/ape/lib/fmt/runevsmprint.c

@@ -0,0 +1,37 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/*
+ * print into an allocated string buffer
+ */
+Rune*
+runevsmprint(char *fmt, va_list args)
+{
+	Fmt f;
+	int n;
+
+	if(runefmtstrinit(&f) < 0)
+		return nil;
+	f.args = args;
+	n = dofmt(&f, fmt);
+	if(n < 0)
+		return nil;
+	*(Rune*)f.to = '\0';
+	return (Rune*)f.start;
+}

+ 38 - 0
sys/src/ape/lib/fmt/runevsnprint.c

@@ -0,0 +1,38 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+runevsnprint(Rune *buf, int len, char *fmt, va_list args)
+{
+	Fmt f;
+
+	if(len <= 0)
+		return -1;
+	f.runes = 1;
+	f.start = buf;
+	f.to = buf;
+	f.stop = buf + len - 1;
+	f.flush = nil;
+	f.farg = nil;
+	f.nfmt = 0;
+	f.args = args;
+	dofmt(&f, fmt);
+	*(Rune*)f.to = '\0';
+	return (Rune*)f.to - buf;
+}

+ 27 - 0
sys/src/ape/lib/fmt/seprint.c

@@ -0,0 +1,27 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include "fmt.h"
+
+char*
+seprint(char *buf, char *e, char *fmt, ...)
+{
+	char *p;
+	va_list args;
+
+	va_start(args, fmt);
+	p = vseprint(buf, e, fmt, args);
+	va_end(args);
+	return p;
+}

+ 27 - 0
sys/src/ape/lib/fmt/smprint.c

@@ -0,0 +1,27 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include "fmt.h"
+
+char*
+smprint(char *fmt, ...)
+{
+	va_list args;
+	char *p;
+
+	va_start(args, fmt);
+	p = vsmprint(fmt, args);
+	va_end(args);
+	return p;
+}

+ 28 - 0
sys/src/ape/lib/fmt/snprint.c

@@ -0,0 +1,28 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include "fmt.h"
+
+int
+snprint(char *buf, int len, char *fmt, ...)
+{
+	int n;
+	va_list args;
+
+	va_start(args, fmt);
+	n = vsnprint(buf, len, fmt, args);
+	va_end(args);
+	return n;
+}
+

+ 27 - 0
sys/src/ape/lib/fmt/sprint.c

@@ -0,0 +1,27 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include "fmt.h"
+
+int
+sprint(char *buf, char *fmt, ...)
+{
+	int n;
+	va_list args;
+
+	va_start(args, fmt);
+	n = vsnprint(buf, 65536, fmt, args);	/* big number, but sprint is deprecated anyway */
+	va_end(args);
+	return n;
+}

+ 539 - 0
sys/src/ape/lib/fmt/strtod.c

@@ -0,0 +1,539 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdlib.h>
+#include <math.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "fmt.h"
+#include "nan.h"
+
+#ifndef nelem
+#define nelem(x)	(sizeof(x)/sizeof *(x))
+#endif
+#define nil ((void*)0)
+#define ulong _fmtulong
+typedef unsigned long ulong;
+
+static ulong
+umuldiv(ulong a, ulong b, ulong c)
+{
+	double d;
+
+	d = ((double)a * (double)b) / (double)c;
+	if(d >= 4294967295.)
+		d = 4294967295.;
+	return (ulong)d;
+}
+
+/*
+ * This routine will convert to arbitrary precision
+ * floating point entirely in multi-precision fixed.
+ * The answer is the closest floating point number to
+ * the given decimal number. Exactly half way are
+ * rounded ala ieee rules.
+ * Method is to scale input decimal between .500 and .999...
+ * with external power of 2, then binary search for the
+ * closest mantissa to this decimal number.
+ * Nmant is is the required precision. (53 for ieee dp)
+ * Nbits is the max number of bits/word. (must be <= 28)
+ * Prec is calculated - the number of words of fixed mantissa.
+ */
+enum
+{
+	Nbits	= 28,				/* bits safely represented in a ulong */
+	Nmant	= 53,				/* bits of precision required */
+	Prec	= (Nmant+Nbits+1)/Nbits,	/* words of Nbits each to represent mantissa */
+	Sigbit	= 1<<(Prec*Nbits-Nmant),	/* first significant bit of Prec-th word */
+	Ndig	= 1500,
+	One	= (ulong)(1<<Nbits),
+	Half	= (ulong)(One>>1),
+	Maxe	= 310,
+
+	Fsign	= 1<<0,		/* found - */
+	Fesign	= 1<<1,		/* found e- */
+	Fdpoint	= 1<<2,		/* found . */
+
+	S0	= 0,		/* _		_S0	+S1	#S2	.S3 */
+	S1,			/* _+		#S2	.S3 */
+	S2,			/* _+#		#S2	.S4	eS5 */
+	S3,			/* _+.		#S4 */
+	S4,			/* _+#.#	#S4	eS5 */
+	S5,			/* _+#.#e	+S6	#S7 */
+	S6,			/* _+#.#e+	#S7 */
+	S7,			/* _+#.#e+#	#S7 */
+};
+
+static	int	xcmp(char*, char*);
+static	int	fpcmp(char*, ulong*);
+static	void	frnorm(ulong*);
+static	void	divascii(char*, int*, int*, int*);
+static	void	mulascii(char*, int*, int*, int*);
+
+typedef	struct	Tab	Tab;
+struct	Tab
+{
+	int	bp;
+	int	siz;
+	char*	cmp;
+};
+
+double
+fmtstrtod(const char *as, char **aas)
+{
+	int na, ex, dp, bp, c, i, flag, state;
+	ulong low[Prec], hig[Prec], mid[Prec];
+	double d;
+	char *s, a[Ndig];
+
+	flag = 0;	/* Fsign, Fesign, Fdpoint */
+	na = 0;		/* number of digits of a[] */
+	dp = 0;		/* na of decimal point */
+	ex = 0;		/* exonent */
+
+	state = S0;
+	for(s=(char*)as;; s++) {
+		c = *s;
+		if(c >= '0' && c <= '9') {
+			switch(state) {
+			case S0:
+			case S1:
+			case S2:
+				state = S2;
+				break;
+			case S3:
+			case S4:
+				state = S4;
+				break;
+
+			case S5:
+			case S6:
+			case S7:
+				state = S7;
+				ex = ex*10 + (c-'0');
+				continue;
+			}
+			if(na == 0 && c == '0') {
+				dp--;
+				continue;
+			}
+			if(na < Ndig-50)
+				a[na++] = c;
+			continue;
+		}
+		switch(c) {
+		case '\t':
+		case '\n':
+		case '\v':
+		case '\f':
+		case '\r':
+		case ' ':
+			if(state == S0)
+				continue;
+			break;
+		case '-':
+			if(state == S0)
+				flag |= Fsign;
+			else
+				flag |= Fesign;
+		case '+':
+			if(state == S0)
+				state = S1;
+			else
+			if(state == S5)
+				state = S6;
+			else
+				break;	/* syntax */
+			continue;
+		case '.':
+			flag |= Fdpoint;
+			dp = na;
+			if(state == S0 || state == S1) {
+				state = S3;
+				continue;
+			}
+			if(state == S2) {
+				state = S4;
+				continue;
+			}
+			break;
+		case 'e':
+		case 'E':
+			if(state == S2 || state == S4) {
+				state = S5;
+				continue;
+			}
+			break;
+		}
+		break;
+	}
+
+	/*
+	 * clean up return char-pointer
+	 */
+	switch(state) {
+	case S0:
+		if(xcmp(s, "nan") == 0) {
+			if(aas != nil)
+				*aas = s+3;
+			goto retnan;
+		}
+	case S1:
+		if(xcmp(s, "infinity") == 0) {
+			if(aas != nil)
+				*aas = s+8;
+			goto retinf;
+		}
+		if(xcmp(s, "inf") == 0) {
+			if(aas != nil)
+				*aas = s+3;
+			goto retinf;
+		}
+	case S3:
+		if(aas != nil)
+			*aas = (char*)as;
+		goto ret0;	/* no digits found */
+	case S6:
+		s--;		/* back over +- */
+	case S5:
+		s--;		/* back over e */
+		break;
+	}
+	if(aas != nil)
+		*aas = s;
+
+	if(flag & Fdpoint)
+	while(na > 0 && a[na-1] == '0')
+		na--;
+	if(na == 0)
+		goto ret0;	/* zero */
+	a[na] = 0;
+	if(!(flag & Fdpoint))
+		dp = na;
+	if(flag & Fesign)
+		ex = -ex;
+	dp += ex;
+	if(dp < -Maxe){
+		errno = ERANGE;
+		goto ret0;	/* underflow by exp */
+	} else
+	if(dp > +Maxe)
+		goto retinf;	/* overflow by exp */
+
+	/*
+	 * normalize the decimal ascii number
+	 * to range .[5-9][0-9]* e0
+	 */
+	bp = 0;		/* binary exponent */
+	while(dp > 0)
+		divascii(a, &na, &dp, &bp);
+	while(dp < 0 || a[0] < '5')
+		mulascii(a, &na, &dp, &bp);
+
+	/* close approx by naive conversion */
+	mid[0] = 0;
+	mid[1] = 1;
+	for(i=0; c=a[i]; i++) {
+		mid[0] = mid[0]*10 + (c-'0');
+		mid[1] = mid[1]*10;
+		if(i >= 8)
+			break;
+	}
+	low[0] = umuldiv(mid[0], One, mid[1]);
+	hig[0] = umuldiv(mid[0]+1, One, mid[1]);
+	for(i=1; i<Prec; i++) {
+		low[i] = 0;
+		hig[i] = One-1;
+	}
+
+	/* binary search for closest mantissa */
+	for(;;) {
+		/* mid = (hig + low) / 2 */
+		c = 0;
+		for(i=0; i<Prec; i++) {
+			mid[i] = hig[i] + low[i];
+			if(c)
+				mid[i] += One;
+			c = mid[i] & 1;
+			mid[i] >>= 1;
+		}
+		frnorm(mid);
+
+		/* compare */
+		c = fpcmp(a, mid);
+		if(c > 0) {
+			c = 1;
+			for(i=0; i<Prec; i++)
+				if(low[i] != mid[i]) {
+					c = 0;
+					low[i] = mid[i];
+				}
+			if(c)
+				break;	/* between mid and hig */
+			continue;
+		}
+		if(c < 0) {
+			for(i=0; i<Prec; i++)
+				hig[i] = mid[i];
+			continue;
+		}
+
+		/* only hard part is if even/odd roundings wants to go up */
+		c = mid[Prec-1] & (Sigbit-1);
+		if(c == Sigbit/2 && (mid[Prec-1]&Sigbit) == 0)
+			mid[Prec-1] -= c;
+		break;	/* exactly mid */
+	}
+
+	/* normal rounding applies */
+	c = mid[Prec-1] & (Sigbit-1);
+	mid[Prec-1] -= c;
+	if(c >= Sigbit/2) {
+		mid[Prec-1] += Sigbit;
+		frnorm(mid);
+	}
+	goto out;
+
+ret0:
+	return 0;
+
+retnan:
+	return __NaN();
+
+retinf:
+	/*
+	 * Unix strtod requires these.  Plan 9 would return Inf(0) or Inf(-1). */
+	errno = ERANGE;
+	if(flag & Fsign)
+		return -HUGE_VAL;
+	return HUGE_VAL;
+
+out:
+	d = 0;
+	for(i=0; i<Prec; i++)
+		d = d*One + mid[i];
+	if(flag & Fsign)
+		d = -d;
+	d = ldexp(d, bp - Prec*Nbits);
+	if(d == 0){	/* underflow */
+		errno = ERANGE;
+	}
+	return d;
+}
+
+static void
+frnorm(ulong *f)
+{
+	int i, c;
+
+	c = 0;
+	for(i=Prec-1; i>0; i--) {
+		f[i] += c;
+		c = f[i] >> Nbits;
+		f[i] &= One-1;
+	}
+	f[0] += c;
+}
+
+static int
+fpcmp(char *a, ulong* f)
+{
+	ulong tf[Prec];
+	int i, d, c;
+
+	for(i=0; i<Prec; i++)
+		tf[i] = f[i];
+
+	for(;;) {
+		/* tf *= 10 */
+		for(i=0; i<Prec; i++)
+			tf[i] = tf[i]*10;
+		frnorm(tf);
+		d = (tf[0] >> Nbits) + '0';
+		tf[0] &= One-1;
+
+		/* compare next digit */
+		c = *a;
+		if(c == 0) {
+			if('0' < d)
+				return -1;
+			if(tf[0] != 0)
+				goto cont;
+			for(i=1; i<Prec; i++)
+				if(tf[i] != 0)
+					goto cont;
+			return 0;
+		}
+		if(c > d)
+			return +1;
+		if(c < d)
+			return -1;
+		a++;
+	cont:;
+	}
+	return 0;
+}
+
+static void
+divby(char *a, int *na, int b)
+{
+	int n, c;
+	char *p;
+
+	p = a;
+	n = 0;
+	while(n>>b == 0) {
+		c = *a++;
+		if(c == 0) {
+			while(n) {
+				c = n*10;
+				if(c>>b)
+					break;
+				n = c;
+			}
+			goto xx;
+		}
+		n = n*10 + c-'0';
+		(*na)--;
+	}
+	for(;;) {
+		c = n>>b;
+		n -= c<<b;
+		*p++ = c + '0';
+		c = *a++;
+		if(c == 0)
+			break;
+		n = n*10 + c-'0';
+	}
+	(*na)++;
+xx:
+	while(n) {
+		n = n*10;
+		c = n>>b;
+		n -= c<<b;
+		*p++ = c + '0';
+		(*na)++;
+	}
+	*p = 0;
+}
+
+static	Tab	tab1[] =
+{
+	 1,  0, "",
+	 3,  1, "7",
+	 6,  2, "63",
+	 9,  3, "511",
+	13,  4, "8191",
+	16,  5, "65535",
+	19,  6, "524287",
+	23,  7, "8388607",
+	26,  8, "67108863",
+	27,  9, "134217727",
+};
+
+static void
+divascii(char *a, int *na, int *dp, int *bp)
+{
+	int b, d;
+	Tab *t;
+
+	d = *dp;
+	if(d >= (int)(nelem(tab1)))
+		d = (int)(nelem(tab1))-1;
+	t = tab1 + d;
+	b = t->bp;
+	if(memcmp(a, t->cmp, t->siz) > 0)
+		d--;
+	*dp -= d;
+	*bp += b;
+	divby(a, na, b);
+}
+
+static void
+mulby(char *a, char *p, char *q, int b)
+{
+	int n, c;
+
+	n = 0;
+	*p = 0;
+	for(;;) {
+		q--;
+		if(q < a)
+			break;
+		c = *q - '0';
+		c = (c<<b) + n;
+		n = c/10;
+		c -= n*10;
+		p--;
+		*p = c + '0';
+	}
+	while(n) {
+		c = n;
+		n = c/10;
+		c -= n*10;
+		p--;
+		*p = c + '0';
+	}
+}
+
+static	Tab	tab2[] =
+{
+	 1,  1, "",				/* dp = 0-0 */
+	 3,  3, "125",
+	 6,  5, "15625",
+	 9,  7, "1953125",
+	13, 10, "1220703125",
+	16, 12, "152587890625",
+	19, 14, "19073486328125",
+	23, 17, "11920928955078125",
+	26, 19, "1490116119384765625",
+	27, 19, "7450580596923828125",		/* dp 8-9 */
+};
+
+static void
+mulascii(char *a, int *na, int *dp, int *bp)
+{
+	char *p;
+	int d, b;
+	Tab *t;
+
+	d = -*dp;
+	if(d >= (int)(nelem(tab2)))
+		d = (int)(nelem(tab2))-1;
+	t = tab2 + d;
+	b = t->bp;
+	if(memcmp(a, t->cmp, t->siz) < 0)
+		d--;
+	p = a + *na;
+	*bp -= b;
+	*dp += d;
+	*na += d;
+	mulby(a, p+d, p, b);
+}
+
+static int
+xcmp(char *a, char *b)
+{
+	int c1, c2;
+
+	while(c1 = *b++) {
+		c2 = *a++;
+		if(isupper(c2))
+			c2 = tolower(c2);
+		if(c1 != c2)
+			return 1;
+	}
+	return 0;
+}

+ 4 - 0
sys/src/ape/lib/fmt/strtod.h

@@ -0,0 +1,4 @@
+extern double __NaN(void);
+extern double __Inf(int);
+extern double __isNaN(double);
+extern double __isInf(double, int);

+ 39 - 0
sys/src/ape/lib/fmt/test.c

@@ -0,0 +1,39 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <utf.h>
+#include "fmt.h"
+
+int
+main(int argc, char *argv[])
+{
+	quotefmtinstall();
+	print("hello world\n");
+	print("x: %x\n", 0x87654321);
+	print("u: %u\n", 0x87654321);
+	print("d: %d\n", 0x87654321);
+	print("s: %s\n", "hi there");
+	print("q: %q\n", "hi i'm here");
+	print("c: %c\n", '!');
+	print("g: %g %g %g\n", 3.14159, 3.14159e10, 3.14159e-10);
+	print("e: %e %e %e\n", 3.14159, 3.14159e10, 3.14159e-10);
+	print("f: %f %f %f\n", 3.14159, 3.14159e10, 3.14159e-10);
+	print("smiley: %C\n", (Rune)0x263a);
+	print("%g %.18\n", 2e25, 2e25);
+	print("%2.18g\n", 1.0);
+	print("%f\n", 3.1415927/4);
+	print("%d\n", 23);
+	print("%i\n", 23);
+	return 0;
+}

+ 31 - 0
sys/src/ape/lib/fmt/vfprint.c

@@ -0,0 +1,31 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+vfprint(int fd, char *fmt, va_list args)
+{
+	Fmt f;
+	char buf[256];
+	int n;
+
+	fmtfdinit(&f, fd, buf, sizeof(buf));
+	f.args = args;
+	n = dofmt(&f, fmt);
+	if(n > 0 && __fmtFdFlush(&f) == 0)
+		return -1;
+	return n;
+}

+ 37 - 0
sys/src/ape/lib/fmt/vseprint.c

@@ -0,0 +1,37 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include "fmt.h"
+#include "fmtdef.h"
+
+char*
+vseprint(char *buf, char *e, char *fmt, va_list args)
+{
+	Fmt f;
+
+	if(e <= buf)
+		return nil;
+	f.runes = 0;
+	f.start = buf;
+	f.to = buf;
+	f.stop = e - 1;
+	f.flush = 0;
+	f.farg = nil;
+	f.nfmt = 0;
+	f.args = args;
+	dofmt(&f, fmt);
+	*(char*)f.to = '\0';
+	return (char*)f.to;
+}
+

+ 36 - 0
sys/src/ape/lib/fmt/vsmprint.c

@@ -0,0 +1,36 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdlib.h>
+#include <stdarg.h>
+#include "fmt.h"
+#include "fmtdef.h"
+
+/*
+ * print into an allocated string buffer
+ */
+char*
+vsmprint(char *fmt, va_list args)
+{
+	Fmt f;
+	int n;
+
+	if(fmtstrinit(&f) < 0)
+		return nil;
+	f.args = args;
+	n = dofmt(&f, fmt);
+	if(n < 0)
+		return nil;
+	*(char*)f.to = '\0';
+	return (char*)f.start;
+}

+ 37 - 0
sys/src/ape/lib/fmt/vsnprint.c

@@ -0,0 +1,37 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdlib.h>
+#include <stdarg.h>
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+vsnprint(char *buf, int len, char *fmt, va_list args)
+{
+	Fmt f;
+
+	if(len <= 0)
+		return -1;
+	f.runes = 0;
+	f.start = buf;
+	f.to = buf;
+	f.stop = buf + len - 1;
+	f.flush = 0;
+	f.farg = nil;
+	f.nfmt = 0;
+	f.args = args;
+	dofmt(&f, fmt);
+	*(char*)f.to = '\0';
+	return (char*)f.to - buf;
+}

+ 16 - 0
sys/src/ape/lib/fmt/werrstr.c

@@ -0,0 +1,16 @@
+#include <stdarg.h>
+#include <errno.h>
+#include "fmt.h"
+
+extern char _plan9err[128];
+
+void
+werrstr(const char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	snprint(_plan9err, sizeof _plan9err, fmt, arg);
+	va_end(arg);
+	errno = EPLAN9;
+}

+ 1 - 1
sys/src/ape/lib/mkfile

@@ -1,6 +1,6 @@
 </$objtype/mkfile
 
-DIRS=9 ap bsd l net regexp v
+DIRS=9 ap bsd draw fmt l net regexp utf v
 
 none:V:
 	echo mk all, install, installall, clean, or nuke

+ 17 - 0
sys/src/ape/lib/utf/lib9.h

@@ -0,0 +1,17 @@
+#include <string.h>
+#include "utf.h"
+
+#define nil ((void*)0)
+
+#define uchar _fmtuchar
+#define ushort _fmtushort
+#define uint _fmtuint
+#define ulong _fmtulong
+#define vlong _fmtvlong
+#define uvlong _fmtuvlong
+
+typedef unsigned char		uchar;
+typedef unsigned short		ushort;
+typedef unsigned int		uint;
+typedef unsigned long		ulong;
+

+ 37 - 0
sys/src/ape/lib/utf/mkfile

@@ -0,0 +1,37 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libutf.a
+
+OFILES=\
+	rune.$O\
+	runestrcat.$O\
+	runestrchr.$O\
+	runestrcmp.$O\
+	runestrcpy.$O\
+	runestrdup.$O\
+	runestrlen.$O\
+	runestrecpy.$O\
+	runestrncat.$O\
+	runestrncmp.$O\
+	runestrncpy.$O\
+	runestrrchr.$O\
+	runestrstr.$O\
+	runetype.$O\
+	utfecpy.$O\
+	utflen.$O\
+	utfnlen.$O\
+	utfrrune.$O\
+	utfrune.$O\
+	utfutf.$O\
+
+HFILES=\
+	/sys/include/ape/utf.h\
+
+UPDATE=\
+	mkfile\
+	${OFILES:%.$O=%.c}\
+
+</sys/src/cmd/mksyslib
+
+CFLAGS=-c -D_POSIX_SOURCE -D_PLAN9_SOURCE -D_BSD_EXTENSION

+ 177 - 0
sys/src/ape/lib/utf/rune.c

@@ -0,0 +1,177 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+enum
+{
+	Bit1	= 7,
+	Bitx	= 6,
+	Bit2	= 5,
+	Bit3	= 4,
+	Bit4	= 3,
+
+	T1	= ((1<<(Bit1+1))-1) ^ 0xFF,	/* 0000 0000 */
+	Tx	= ((1<<(Bitx+1))-1) ^ 0xFF,	/* 1000 0000 */
+	T2	= ((1<<(Bit2+1))-1) ^ 0xFF,	/* 1100 0000 */
+	T3	= ((1<<(Bit3+1))-1) ^ 0xFF,	/* 1110 0000 */
+	T4	= ((1<<(Bit4+1))-1) ^ 0xFF,	/* 1111 0000 */
+
+	Rune1	= (1<<(Bit1+0*Bitx))-1,		/* 0000 0000 0111 1111 */
+	Rune2	= (1<<(Bit2+1*Bitx))-1,		/* 0000 0111 1111 1111 */
+	Rune3	= (1<<(Bit3+2*Bitx))-1,		/* 1111 1111 1111 1111 */
+
+	Maskx	= (1<<Bitx)-1,			/* 0011 1111 */
+	Testx	= Maskx ^ 0xFF,			/* 1100 0000 */
+
+	Bad	= Runeerror,
+};
+
+int
+chartorune(Rune *rune, char *str)
+{
+	int c, c1, c2;
+	long l;
+
+	/*
+	 * one character sequence
+	 *	00000-0007F => T1
+	 */
+	c = *(uchar*)str;
+	if(c < Tx) {
+		*rune = c;
+		return 1;
+	}
+
+	/*
+	 * two character sequence
+	 *	0080-07FF => T2 Tx
+	 */
+	c1 = *(uchar*)(str+1) ^ Tx;
+	if(c1 & Testx)
+		goto bad;
+	if(c < T3) {
+		if(c < T2)
+			goto bad;
+		l = ((c << Bitx) | c1) & Rune2;
+		if(l <= Rune1)
+			goto bad;
+		*rune = l;
+		return 2;
+	}
+
+	/*
+	 * three character sequence
+	 *	0800-FFFF => T3 Tx Tx
+	 */
+	c2 = *(uchar*)(str+2) ^ Tx;
+	if(c2 & Testx)
+		goto bad;
+	if(c < T4) {
+		l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
+		if(l <= Rune2)
+			goto bad;
+		*rune = l;
+		return 3;
+	}
+
+	/*
+	 * bad decoding
+	 */
+bad:
+	*rune = Bad;
+	return 1;
+}
+
+int
+runetochar(char *str, Rune *rune)
+{
+	long c;
+
+	/*
+	 * one character sequence
+	 *	00000-0007F => 00-7F
+	 */
+	c = *rune;
+	if(c <= Rune1) {
+		str[0] = c;
+		return 1;
+	}
+
+	/*
+	 * two character sequence
+	 *	0080-07FF => T2 Tx
+	 */
+	if(c <= Rune2) {
+		str[0] = T2 | (c >> 1*Bitx);
+		str[1] = Tx | (c & Maskx);
+		return 2;
+	}
+
+	/*
+	 * three character sequence
+	 *	0800-FFFF => T3 Tx Tx
+	 */
+	str[0] = T3 |  (c >> 2*Bitx);
+	str[1] = Tx | ((c >> 1*Bitx) & Maskx);
+	str[2] = Tx |  (c & Maskx);
+	return 3;
+}
+
+int
+runelen(long c)
+{
+	Rune rune;
+	char str[10];
+
+	rune = c;
+	return runetochar(str, &rune);
+}
+
+int
+runenlen(Rune *r, int nrune)
+{
+	int nb, c;
+
+	nb = 0;
+	while(nrune--) {
+		c = *r++;
+		if(c <= Rune1)
+			nb++;
+		else
+		if(c <= Rune2)
+			nb += 2;
+		else
+			nb += 3;
+	}
+	return nb;
+}
+
+int
+fullrune(char *str, int n)
+{
+	int c;
+
+	if(n > 0) {
+		c = *(uchar*)str;
+		if(c < Tx)
+			return 1;
+		if(n > 1)
+			if(c < T3 || n > 2)
+				return 1;
+	}
+	return 0;
+}

+ 25 - 0
sys/src/ape/lib/utf/runestrcat.c

@@ -0,0 +1,25 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+Rune*
+runestrcat(Rune *s1, Rune *s2)
+{
+
+	runestrcpy(runestrchr(s1, 0), s2);
+	return s1;
+}

+ 35 - 0
sys/src/ape/lib/utf/runestrchr.c

@@ -0,0 +1,35 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+Rune*
+runestrchr(Rune *s, Rune c)
+{
+	Rune c0 = c;
+	Rune c1;
+
+	if(c == 0) {
+		while(*s++)
+			;
+		return s-1;
+	}
+
+	while(c1 = *s++)
+		if(c1 == c0)
+			return s-1;
+	return 0;
+}

+ 35 - 0
sys/src/ape/lib/utf/runestrcmp.c

@@ -0,0 +1,35 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+int
+runestrcmp(Rune *s1, Rune *s2)
+{
+	Rune c1, c2;
+
+	for(;;) {
+		c1 = *s1++;
+		c2 = *s2++;
+		if(c1 != c2) {
+			if(c1 > c2)
+				return 1;
+			return -1;
+		}
+		if(c1 == 0)
+			return 0;
+	}
+}

+ 28 - 0
sys/src/ape/lib/utf/runestrcpy.c

@@ -0,0 +1,28 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+Rune*
+runestrcpy(Rune *s1, Rune *s2)
+{
+	Rune *os1;
+
+	os1 = s1;
+	while(*s1++ = *s2++)
+		;
+	return os1;
+}

+ 30 - 0
sys/src/ape/lib/utf/runestrdup.c

@@ -0,0 +1,30 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include "utf.h"
+#include "utfdef.h"
+
+Rune*
+runestrdup(Rune *s) 
+{  
+	Rune *ns;
+
+	ns = malloc(sizeof(Rune)*(runestrlen(s) + 1));
+	if(ns == 0)
+		return 0;
+
+	return runestrcpy(ns, s);
+}

+ 32 - 0
sys/src/ape/lib/utf/runestrecpy.c

@@ -0,0 +1,32 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+Rune*
+runestrecpy(Rune *s1, Rune *es1, Rune *s2)
+{
+	if(s1 >= es1)
+		return s1;
+
+	while(*s1++ = *s2++){
+		if(s1 == es1){
+			*--s1 = '\0';
+			break;
+		}
+	}
+	return s1;
+}

+ 24 - 0
sys/src/ape/lib/utf/runestrlen.c

@@ -0,0 +1,24 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+long
+runestrlen(Rune *s)
+{
+
+	return runestrchr(s, 0) - s;
+}

+ 32 - 0
sys/src/ape/lib/utf/runestrncat.c

@@ -0,0 +1,32 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+Rune*
+runestrncat(Rune *s1, Rune *s2, long n)
+{
+	Rune *os1;
+
+	os1 = s1;
+	s1 = runestrchr(s1, 0);
+	while(*s1++ = *s2++)
+		if(--n < 0) {
+			s1[-1] = 0;
+			break;
+		}
+	return os1;
+}

+ 37 - 0
sys/src/ape/lib/utf/runestrncmp.c

@@ -0,0 +1,37 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+int
+runestrncmp(Rune *s1, Rune *s2, long n)
+{
+	Rune c1, c2;
+
+	while(n > 0) {
+		c1 = *s1++;
+		c2 = *s2++;
+		n--;
+		if(c1 != c2) {
+			if(c1 > c2)
+				return 1;
+			return -1;
+		}
+		if(c1 == 0)
+			break;
+	}
+	return 0;
+}

+ 33 - 0
sys/src/ape/lib/utf/runestrncpy.c

@@ -0,0 +1,33 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+Rune*
+runestrncpy(Rune *s1, Rune *s2, long n)
+{
+	int i;
+	Rune *os1;
+
+	os1 = s1;
+	for(i = 0; i < n; i++)
+		if((*s1++ = *s2++) == 0) {
+			while(++i < n)
+				*s1++ = 0;
+			return os1;
+		}
+	return os1;
+}

+ 30 - 0
sys/src/ape/lib/utf/runestrrchr.c

@@ -0,0 +1,30 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+Rune*
+runestrrchr(Rune *s, Rune c)
+{
+	Rune *r;
+
+	if(c == 0)
+		return runestrchr(s, 0);
+	r = 0;
+	while(s = runestrchr(s, c))
+		r = s++;
+	return r;
+}

+ 44 - 0
sys/src/ape/lib/utf/runestrstr.c

@@ -0,0 +1,44 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+/*
+ * Return pointer to first occurrence of s2 in s1,
+ * 0 if none
+ */
+Rune*
+runestrstr(Rune *s1, Rune *s2)
+{
+	Rune *p, *pa, *pb;
+	int c0, c;
+
+	c0 = *s2;
+	if(c0 == 0)
+		return s1;
+	s2++;
+	for(p=runestrchr(s1, c0); p; p=runestrchr(p+1, c0)) {
+		pa = p;
+		for(pb=s2;; pb++) {
+			c = *pb;
+			if(c == 0)
+				return p;
+			if(c != *++pa)
+				break;
+		}
+	}
+	return 0;
+}

+ 1152 - 0
sys/src/ape/lib/utf/runetype.c

@@ -0,0 +1,1152 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+/*
+ * alpha ranges -
+ *	only covers ranges not in lower||upper
+ */
+static
+Rune	__alpha2[] =
+{
+	0x00d8,	0x00f6,	/* Ø - ö */
+	0x00f8,	0x01f5,	/* ø - ǵ */
+	0x0250,	0x02a8,	/* ɐ - ʨ */
+	0x038e,	0x03a1,	/* Ύ - Ρ */
+	0x03a3,	0x03ce,	/* Σ - ώ */
+	0x03d0,	0x03d6,	/* ϐ - ϖ */
+	0x03e2,	0x03f3,	/* Ϣ - ϳ */
+	0x0490,	0x04c4,	/* Ґ - ӄ */
+	0x0561,	0x0587,	/* ա - և */
+	0x05d0,	0x05ea,	/* א - ת */
+	0x05f0,	0x05f2,	/* װ - ײ */
+	0x0621,	0x063a,	/* ء - غ */
+	0x0640,	0x064a,	/* ـ - ي */
+	0x0671,	0x06b7,	/* ٱ - ڷ */
+	0x06ba,	0x06be,	/* ں - ھ */
+	0x06c0,	0x06ce,	/* ۀ - ێ */
+	0x06d0,	0x06d3,	/* ې - ۓ */
+	0x0905,	0x0939,	/* अ - ह */
+	0x0958,	0x0961,	/* क़ - ॡ */
+	0x0985,	0x098c,	/* অ - ঌ */
+	0x098f,	0x0990,	/* এ - ঐ */
+	0x0993,	0x09a8,	/* ও - ন */
+	0x09aa,	0x09b0,	/* প - র */
+	0x09b6,	0x09b9,	/* শ - হ */
+	0x09dc,	0x09dd,	/* ড় - ঢ় */
+	0x09df,	0x09e1,	/* য় - ৡ */
+	0x09f0,	0x09f1,	/* ৰ - ৱ */
+	0x0a05,	0x0a0a,	/* ਅ - ਊ */
+	0x0a0f,	0x0a10,	/* ਏ - ਐ */
+	0x0a13,	0x0a28,	/* ਓ - ਨ */
+	0x0a2a,	0x0a30,	/* ਪ - ਰ */
+	0x0a32,	0x0a33,	/* ਲ - ਲ਼ */
+	0x0a35,	0x0a36,	/* ਵ - ਸ਼ */
+	0x0a38,	0x0a39,	/* ਸ - ਹ */
+	0x0a59,	0x0a5c,	/* ਖ਼ - ੜ */
+	0x0a85,	0x0a8b,	/* અ - ઋ */
+	0x0a8f,	0x0a91,	/* એ - ઑ */
+	0x0a93,	0x0aa8,	/* ઓ - ન */
+	0x0aaa,	0x0ab0,	/* પ - ર */
+	0x0ab2,	0x0ab3,	/* લ - ળ */
+	0x0ab5,	0x0ab9,	/* વ - હ */
+	0x0b05,	0x0b0c,	/* ଅ - ଌ */
+	0x0b0f,	0x0b10,	/* ଏ - ଐ */
+	0x0b13,	0x0b28,	/* ଓ - ନ */
+	0x0b2a,	0x0b30,	/* ପ - ର */
+	0x0b32,	0x0b33,	/* ଲ - ଳ */
+	0x0b36,	0x0b39,	/* ଶ - ହ */
+	0x0b5c,	0x0b5d,	/* ଡ଼ - ଢ଼ */
+	0x0b5f,	0x0b61,	/* ୟ - ୡ */
+	0x0b85,	0x0b8a,	/* அ - ஊ */
+	0x0b8e,	0x0b90,	/* எ - ஐ */
+	0x0b92,	0x0b95,	/* ஒ - க */
+	0x0b99,	0x0b9a,	/* ங - ச */
+	0x0b9e,	0x0b9f,	/* ஞ - ட */
+	0x0ba3,	0x0ba4,	/* ண - த */
+	0x0ba8,	0x0baa,	/* ந - ப */
+	0x0bae,	0x0bb5,	/* ம - வ */
+	0x0bb7,	0x0bb9,	/* ஷ - ஹ */
+	0x0c05,	0x0c0c,	/* అ - ఌ */
+	0x0c0e,	0x0c10,	/* ఎ - ఐ */
+	0x0c12,	0x0c28,	/* ఒ - న */
+	0x0c2a,	0x0c33,	/* ప - ళ */
+	0x0c35,	0x0c39,	/* వ - హ */
+	0x0c60,	0x0c61,	/* ౠ - ౡ */
+	0x0c85,	0x0c8c,	/* ಅ - ಌ */
+	0x0c8e,	0x0c90,	/* ಎ - ಐ */
+	0x0c92,	0x0ca8,	/* ಒ - ನ */
+	0x0caa,	0x0cb3,	/* ಪ - ಳ */
+	0x0cb5,	0x0cb9,	/* ವ - ಹ */
+	0x0ce0,	0x0ce1,	/* ೠ - ೡ */
+	0x0d05,	0x0d0c,	/* അ - ഌ */
+	0x0d0e,	0x0d10,	/* എ - ഐ */
+	0x0d12,	0x0d28,	/* ഒ - ന */
+	0x0d2a,	0x0d39,	/* പ - ഹ */
+	0x0d60,	0x0d61,	/* ൠ - ൡ */
+	0x0e01,	0x0e30,	/* ก - ะ */
+	0x0e32,	0x0e33,	/* า - ำ */
+	0x0e40,	0x0e46,	/* เ - ๆ */
+	0x0e5a,	0x0e5b,	/* ๚ - ๛ */
+	0x0e81,	0x0e82,	/* ກ - ຂ */
+	0x0e87,	0x0e88,	/* ງ - ຈ */
+	0x0e94,	0x0e97,	/* ດ - ທ */
+	0x0e99,	0x0e9f,	/* ນ - ຟ */
+	0x0ea1,	0x0ea3,	/* ມ - ຣ */
+	0x0eaa,	0x0eab,	/* ສ - ຫ */
+	0x0ead,	0x0eae,	/* ອ - ຮ */
+	0x0eb2,	0x0eb3,	/* າ - ຳ */
+	0x0ec0,	0x0ec4,	/* ເ - ໄ */
+	0x0edc,	0x0edd,	/* ໜ - ໝ */
+	0x0f18,	0x0f19,	/* ༘ - ༙ */
+	0x0f40,	0x0f47,	/* ཀ - ཇ */
+	0x0f49,	0x0f69,	/* ཉ - ཀྵ */
+	0x10d0,	0x10f6,	/* ა - ჶ */
+	0x1100,	0x1159,	/* ᄀ - ᅙ */
+	0x115f,	0x11a2,	/* ᅟ - ᆢ */
+	0x11a8,	0x11f9,	/* ᆨ - ᇹ */
+	0x1e00,	0x1e9b,	/* Ḁ - ẛ */
+	0x1f50,	0x1f57,	/* ὐ - ὗ */
+	0x1f80,	0x1fb4,	/* ᾀ - ᾴ */
+	0x1fb6,	0x1fbc,	/* ᾶ - ᾼ */
+	0x1fc2,	0x1fc4,	/* ῂ - ῄ */
+	0x1fc6,	0x1fcc,	/* ῆ - ῌ */
+	0x1fd0,	0x1fd3,	/* ῐ - ΐ */
+	0x1fd6,	0x1fdb,	/* ῖ - Ί */
+	0x1fe0,	0x1fec,	/* ῠ - Ῥ */
+	0x1ff2,	0x1ff4,	/* ῲ - ῴ */
+	0x1ff6,	0x1ffc,	/* ῶ - ῼ */
+	0x210a,	0x2113,	/* ℊ - ℓ */
+	0x2115,	0x211d,	/* ℕ - ℝ */
+	0x2120,	0x2122,	/* ℠ - ™ */
+	0x212a,	0x2131,	/* K - ℱ */
+	0x2133,	0x2138,	/* ℳ - ℸ */
+	0x3041,	0x3094,	/* ぁ - ゔ */
+	0x30a1,	0x30fa,	/* ァ - ヺ */
+	0x3105,	0x312c,	/* ㄅ - ㄬ */
+	0x3131,	0x318e,	/* ㄱ - ㆎ */
+	0x3192,	0x319f,	/* ㆒ - ㆟ */
+	0x3260,	0x327b,	/* ㉠ - ㉻ */
+	0x328a,	0x32b0,	/* ㊊ - ㊰ */
+	0x32d0,	0x32fe,	/* ㋐ - ㋾ */
+	0x3300,	0x3357,	/* ㌀ - ㍗ */
+	0x3371,	0x3376,	/* ㍱ - ㍶ */
+	0x337b,	0x3394,	/* ㍻ - ㎔ */
+	0x3399,	0x339e,	/* ㎙ - ㎞ */
+	0x33a9,	0x33ad,	/* ㎩ - ㎭ */
+	0x33b0,	0x33c1,	/* ㎰ - ㏁ */
+	0x33c3,	0x33c5,	/* ㏃ - ㏅ */
+	0x33c7,	0x33d7,	/* ㏇ - ㏗ */
+	0x33d9,	0x33dd,	/* ㏙ - ㏝ */
+	0x4e00,	0x9fff,	/* 一 - 鿿 */
+	0xac00,	0xd7a3,	/* 가 - 힣 */
+	0xf900,	0xfb06,	/* 豈 - st */
+	0xfb13,	0xfb17,	/* ﬓ - ﬗ */
+	0xfb1f,	0xfb28,	/* ײַ - ﬨ */
+	0xfb2a,	0xfb36,	/* שׁ - זּ */
+	0xfb38,	0xfb3c,	/* טּ - לּ */
+	0xfb40,	0xfb41,	/* נּ - סּ */
+	0xfb43,	0xfb44,	/* ףּ - פּ */
+	0xfb46,	0xfbb1,	/* צּ - ﮱ */
+	0xfbd3,	0xfd3d,	/* ﯓ - ﴽ */
+	0xfd50,	0xfd8f,	/* ﵐ - ﶏ */
+	0xfd92,	0xfdc7,	/* ﶒ - ﷇ */
+	0xfdf0,	0xfdf9,	/* ﷰ - ﷹ */
+	0xfe70,	0xfe72,	/* ﹰ - ﹲ */
+	0xfe76,	0xfefc,	/* ﹶ - ﻼ */
+	0xff66,	0xff6f,	/* ヲ - ッ */
+	0xff71,	0xff9d,	/* ア - ン */
+	0xffa0,	0xffbe,	/* ᅠ - ᄒ */
+	0xffc2,	0xffc7,	/* ᅡ - ᅦ */
+	0xffca,	0xffcf,	/* ᅧ - ᅬ */
+	0xffd2,	0xffd7,	/* ᅭ - ᅲ */
+	0xffda,	0xffdc,	/* ᅳ - ᅵ */
+};
+
+/*
+ * alpha singlets -
+ *	only covers ranges not in lower||upper
+ */
+static
+Rune	__alpha1[] =
+{
+	0x00aa,	/* ª */
+	0x00b5,	/* µ */
+	0x00ba,	/* º */
+	0x03da,	/* Ϛ */
+	0x03dc,	/* Ϝ */
+	0x03de,	/* Ϟ */
+	0x03e0,	/* Ϡ */
+	0x06d5,	/* ە */
+	0x09b2,	/* ল */
+	0x0a5e,	/* ਫ਼ */
+	0x0a8d,	/* ઍ */
+	0x0ae0,	/* ૠ */
+	0x0b9c,	/* ஜ */
+	0x0cde,	/* ೞ */
+	0x0e4f,	/* ๏ */
+	0x0e84,	/* ຄ */
+	0x0e8a,	/* ຊ */
+	0x0e8d,	/* ຍ */
+	0x0ea5,	/* ລ */
+	0x0ea7,	/* ວ */
+	0x0eb0,	/* ະ */
+	0x0ebd,	/* ຽ */
+	0x1fbe,	/* ι */
+	0x207f,	/* ⁿ */
+	0x20a8,	/* ₨ */
+	0x2102,	/* ℂ */
+	0x2107,	/* ℇ */
+	0x2124,	/* ℤ */
+	0x2126,	/* Ω */
+	0x2128,	/* ℨ */
+	0xfb3e,	/* מּ */
+	0xfe74,	/* ﹴ */
+};
+
+/*
+ * space ranges
+ */
+static
+Rune	__space2[] =
+{
+	0x0009,	0x000a,	/* tab and newline */
+	0x0020,	0x0020,	/* space */
+	0x00a0,	0x00a0,	/*   */
+	0x2000,	0x200b,	/*   - ​ */
+	0x2028,	0x2029,	/* 
 - 
 */
+	0x3000,	0x3000,	/*   */
+	0xfeff,	0xfeff,	/*  */
+};
+
+/*
+ * lower case ranges
+ *	3rd col is conversion excess 500
+ */
+static
+Rune	__toupper2[] =
+{
+	0x0061,	0x007a, 468,	/* a-z A-Z */
+	0x00e0,	0x00f6, 468,	/* à-ö À-Ö */
+	0x00f8,	0x00fe, 468,	/* ø-þ Ø-Þ */
+	0x0256,	0x0257, 295,	/* ɖ-ɗ Ɖ-Ɗ */
+	0x0258,	0x0259, 298,	/* ɘ-ə Ǝ-Ə */
+	0x028a,	0x028b, 283,	/* ʊ-ʋ Ʊ-Ʋ */
+	0x03ad,	0x03af, 463,	/* έ-ί Έ-Ί */
+	0x03b1,	0x03c1, 468,	/* α-ρ Α-Ρ */
+	0x03c3,	0x03cb, 468,	/* σ-ϋ Σ-Ϋ */
+	0x03cd,	0x03ce, 437,	/* ύ-ώ Ύ-Ώ */
+	0x0430,	0x044f, 468,	/* а-я А-Я */
+	0x0451,	0x045c, 420,	/* ё-ќ Ё-Ќ */
+	0x045e,	0x045f, 420,	/* ў-џ Ў-Џ */
+	0x0561,	0x0586, 452,	/* ա-ֆ Ա-Ֆ */
+	0x1f00,	0x1f07, 508,	/* ἀ-ἇ Ἀ-Ἇ */
+	0x1f10,	0x1f15, 508,	/* ἐ-ἕ Ἐ-Ἕ */
+	0x1f20,	0x1f27, 508,	/* ἠ-ἧ Ἠ-Ἧ */
+	0x1f30,	0x1f37, 508,	/* ἰ-ἷ Ἰ-Ἷ */
+	0x1f40,	0x1f45, 508,	/* ὀ-ὅ Ὀ-Ὅ */
+	0x1f60,	0x1f67, 508,	/* ὠ-ὧ Ὠ-Ὧ */
+	0x1f70,	0x1f71, 574,	/* ὰ-ά Ὰ-Ά */
+	0x1f72,	0x1f75, 586,	/* ὲ-ή Ὲ-Ή */
+	0x1f76,	0x1f77, 600,	/* ὶ-ί Ὶ-Ί */
+	0x1f78,	0x1f79, 628,	/* ὸ-ό Ὸ-Ό */
+	0x1f7a,	0x1f7b, 612,	/* ὺ-ύ Ὺ-Ύ */
+	0x1f7c,	0x1f7d, 626,	/* ὼ-ώ Ὼ-Ώ */
+	0x1f80,	0x1f87, 508,	/* ᾀ-ᾇ ᾈ-ᾏ */
+	0x1f90,	0x1f97, 508,	/* ᾐ-ᾗ ᾘ-ᾟ */
+	0x1fa0,	0x1fa7, 508,	/* ᾠ-ᾧ ᾨ-ᾯ */
+	0x1fb0,	0x1fb1, 508,	/* ᾰ-ᾱ Ᾰ-Ᾱ */
+	0x1fd0,	0x1fd1, 508,	/* ῐ-ῑ Ῐ-Ῑ */
+	0x1fe0,	0x1fe1, 508,	/* ῠ-ῡ Ῠ-Ῡ */
+	0x2170,	0x217f, 484,	/* ⅰ-ⅿ Ⅰ-Ⅿ */
+	0x24d0,	0x24e9, 474,	/* ⓐ-ⓩ Ⓐ-Ⓩ */
+	0xff41,	0xff5a, 468,	/* a-z A-Z */
+};
+
+/*
+ * lower case singlets
+ *	2nd col is conversion excess 500
+ */
+static
+Rune	__toupper1[] =
+{
+	0x00ff, 621,	/* ÿ Ÿ */
+	0x0101, 499,	/* ā Ā */
+	0x0103, 499,	/* ă Ă */
+	0x0105, 499,	/* ą Ą */
+	0x0107, 499,	/* ć Ć */
+	0x0109, 499,	/* ĉ Ĉ */
+	0x010b, 499,	/* ċ Ċ */
+	0x010d, 499,	/* č Č */
+	0x010f, 499,	/* ď Ď */
+	0x0111, 499,	/* đ Đ */
+	0x0113, 499,	/* ē Ē */
+	0x0115, 499,	/* ĕ Ĕ */
+	0x0117, 499,	/* ė Ė */
+	0x0119, 499,	/* ę Ę */
+	0x011b, 499,	/* ě Ě */
+	0x011d, 499,	/* ĝ Ĝ */
+	0x011f, 499,	/* ğ Ğ */
+	0x0121, 499,	/* ġ Ġ */
+	0x0123, 499,	/* ģ Ģ */
+	0x0125, 499,	/* ĥ Ĥ */
+	0x0127, 499,	/* ħ Ħ */
+	0x0129, 499,	/* ĩ Ĩ */
+	0x012b, 499,	/* ī Ī */
+	0x012d, 499,	/* ĭ Ĭ */
+	0x012f, 499,	/* į Į */
+	0x0131, 268,	/* ı I */
+	0x0133, 499,	/* ij IJ */
+	0x0135, 499,	/* ĵ Ĵ */
+	0x0137, 499,	/* ķ Ķ */
+	0x013a, 499,	/* ĺ Ĺ */
+	0x013c, 499,	/* ļ Ļ */
+	0x013e, 499,	/* ľ Ľ */
+	0x0140, 499,	/* ŀ Ŀ */
+	0x0142, 499,	/* ł Ł */
+	0x0144, 499,	/* ń Ń */
+	0x0146, 499,	/* ņ Ņ */
+	0x0148, 499,	/* ň Ň */
+	0x014b, 499,	/* ŋ Ŋ */
+	0x014d, 499,	/* ō Ō */
+	0x014f, 499,	/* ŏ Ŏ */
+	0x0151, 499,	/* ő Ő */
+	0x0153, 499,	/* œ Œ */
+	0x0155, 499,	/* ŕ Ŕ */
+	0x0157, 499,	/* ŗ Ŗ */
+	0x0159, 499,	/* ř Ř */
+	0x015b, 499,	/* ś Ś */
+	0x015d, 499,	/* ŝ Ŝ */
+	0x015f, 499,	/* ş Ş */
+	0x0161, 499,	/* š Š */
+	0x0163, 499,	/* ţ Ţ */
+	0x0165, 499,	/* ť Ť */
+	0x0167, 499,	/* ŧ Ŧ */
+	0x0169, 499,	/* ũ Ũ */
+	0x016b, 499,	/* ū Ū */
+	0x016d, 499,	/* ŭ Ŭ */
+	0x016f, 499,	/* ů Ů */
+	0x0171, 499,	/* ű Ű */
+	0x0173, 499,	/* ų Ų */
+	0x0175, 499,	/* ŵ Ŵ */
+	0x0177, 499,	/* ŷ Ŷ */
+	0x017a, 499,	/* ź Ź */
+	0x017c, 499,	/* ż Ż */
+	0x017e, 499,	/* ž Ž */
+	0x017f, 200,	/* ſ S */
+	0x0183, 499,	/* ƃ Ƃ */
+	0x0185, 499,	/* ƅ Ƅ */
+	0x0188, 499,	/* ƈ Ƈ */
+	0x018c, 499,	/* ƌ Ƌ */
+	0x0192, 499,	/* ƒ Ƒ */
+	0x0199, 499,	/* ƙ Ƙ */
+	0x01a1, 499,	/* ơ Ơ */
+	0x01a3, 499,	/* ƣ Ƣ */
+	0x01a5, 499,	/* ƥ Ƥ */
+	0x01a8, 499,	/* ƨ Ƨ */
+	0x01ad, 499,	/* ƭ Ƭ */
+	0x01b0, 499,	/* ư Ư */
+	0x01b4, 499,	/* ƴ Ƴ */
+	0x01b6, 499,	/* ƶ Ƶ */
+	0x01b9, 499,	/* ƹ Ƹ */
+	0x01bd, 499,	/* ƽ Ƽ */
+	0x01c5, 499,	/* Dž DŽ */
+	0x01c6, 498,	/* dž DŽ */
+	0x01c8, 499,	/* Lj LJ */
+	0x01c9, 498,	/* lj LJ */
+	0x01cb, 499,	/* Nj NJ */
+	0x01cc, 498,	/* nj NJ */
+	0x01ce, 499,	/* ǎ Ǎ */
+	0x01d0, 499,	/* ǐ Ǐ */
+	0x01d2, 499,	/* ǒ Ǒ */
+	0x01d4, 499,	/* ǔ Ǔ */
+	0x01d6, 499,	/* ǖ Ǖ */
+	0x01d8, 499,	/* ǘ Ǘ */
+	0x01da, 499,	/* ǚ Ǚ */
+	0x01dc, 499,	/* ǜ Ǜ */
+	0x01df, 499,	/* ǟ Ǟ */
+	0x01e1, 499,	/* ǡ Ǡ */
+	0x01e3, 499,	/* ǣ Ǣ */
+	0x01e5, 499,	/* ǥ Ǥ */
+	0x01e7, 499,	/* ǧ Ǧ */
+	0x01e9, 499,	/* ǩ Ǩ */
+	0x01eb, 499,	/* ǫ Ǫ */
+	0x01ed, 499,	/* ǭ Ǭ */
+	0x01ef, 499,	/* ǯ Ǯ */
+	0x01f2, 499,	/* Dz DZ */
+	0x01f3, 498,	/* dz DZ */
+	0x01f5, 499,	/* ǵ Ǵ */
+	0x01fb, 499,	/* ǻ Ǻ */
+	0x01fd, 499,	/* ǽ Ǽ */
+	0x01ff, 499,	/* ǿ Ǿ */
+	0x0201, 499,	/* ȁ Ȁ */
+	0x0203, 499,	/* ȃ Ȃ */
+	0x0205, 499,	/* ȅ Ȅ */
+	0x0207, 499,	/* ȇ Ȇ */
+	0x0209, 499,	/* ȉ Ȉ */
+	0x020b, 499,	/* ȋ Ȋ */
+	0x020d, 499,	/* ȍ Ȍ */
+	0x020f, 499,	/* ȏ Ȏ */
+	0x0211, 499,	/* ȑ Ȑ */
+	0x0213, 499,	/* ȓ Ȓ */
+	0x0215, 499,	/* ȕ Ȕ */
+	0x0217, 499,	/* ȗ Ȗ */
+	0x0253, 290,	/* ɓ Ɓ */
+	0x0254, 294,	/* ɔ Ɔ */
+	0x025b, 297,	/* ɛ Ɛ */
+	0x0260, 295,	/* ɠ Ɠ */
+	0x0263, 293,	/* ɣ Ɣ */
+	0x0268, 291,	/* ɨ Ɨ */
+	0x0269, 289,	/* ɩ Ɩ */
+	0x026f, 289,	/* ɯ Ɯ */
+	0x0272, 287,	/* ɲ Ɲ */
+	0x0283, 282,	/* ʃ Ʃ */
+	0x0288, 282,	/* ʈ Ʈ */
+	0x0292, 281,	/* ʒ Ʒ */
+	0x03ac, 462,	/* ά Ά */
+	0x03cc, 436,	/* ό Ό */
+	0x03d0, 438,	/* ϐ Β */
+	0x03d1, 443,	/* ϑ Θ */
+	0x03d5, 453,	/* ϕ Φ */
+	0x03d6, 446,	/* ϖ Π */
+	0x03e3, 499,	/* ϣ Ϣ */
+	0x03e5, 499,	/* ϥ Ϥ */
+	0x03e7, 499,	/* ϧ Ϧ */
+	0x03e9, 499,	/* ϩ Ϩ */
+	0x03eb, 499,	/* ϫ Ϫ */
+	0x03ed, 499,	/* ϭ Ϭ */
+	0x03ef, 499,	/* ϯ Ϯ */
+	0x03f0, 414,	/* ϰ Κ */
+	0x03f1, 420,	/* ϱ Ρ */
+	0x0461, 499,	/* ѡ Ѡ */
+	0x0463, 499,	/* ѣ Ѣ */
+	0x0465, 499,	/* ѥ Ѥ */
+	0x0467, 499,	/* ѧ Ѧ */
+	0x0469, 499,	/* ѩ Ѩ */
+	0x046b, 499,	/* ѫ Ѫ */
+	0x046d, 499,	/* ѭ Ѭ */
+	0x046f, 499,	/* ѯ Ѯ */
+	0x0471, 499,	/* ѱ Ѱ */
+	0x0473, 499,	/* ѳ Ѳ */
+	0x0475, 499,	/* ѵ Ѵ */
+	0x0477, 499,	/* ѷ Ѷ */
+	0x0479, 499,	/* ѹ Ѹ */
+	0x047b, 499,	/* ѻ Ѻ */
+	0x047d, 499,	/* ѽ Ѽ */
+	0x047f, 499,	/* ѿ Ѿ */
+	0x0481, 499,	/* ҁ Ҁ */
+	0x0491, 499,	/* ґ Ґ */
+	0x0493, 499,	/* ғ Ғ */
+	0x0495, 499,	/* ҕ Ҕ */
+	0x0497, 499,	/* җ Җ */
+	0x0499, 499,	/* ҙ Ҙ */
+	0x049b, 499,	/* қ Қ */
+	0x049d, 499,	/* ҝ Ҝ */
+	0x049f, 499,	/* ҟ Ҟ */
+	0x04a1, 499,	/* ҡ Ҡ */
+	0x04a3, 499,	/* ң Ң */
+	0x04a5, 499,	/* ҥ Ҥ */
+	0x04a7, 499,	/* ҧ Ҧ */
+	0x04a9, 499,	/* ҩ Ҩ */
+	0x04ab, 499,	/* ҫ Ҫ */
+	0x04ad, 499,	/* ҭ Ҭ */
+	0x04af, 499,	/* ү Ү */
+	0x04b1, 499,	/* ұ Ұ */
+	0x04b3, 499,	/* ҳ Ҳ */
+	0x04b5, 499,	/* ҵ Ҵ */
+	0x04b7, 499,	/* ҷ Ҷ */
+	0x04b9, 499,	/* ҹ Ҹ */
+	0x04bb, 499,	/* һ Һ */
+	0x04bd, 499,	/* ҽ Ҽ */
+	0x04bf, 499,	/* ҿ Ҿ */
+	0x04c2, 499,	/* ӂ Ӂ */
+	0x04c4, 499,	/* ӄ Ӄ */
+	0x04c8, 499,	/* ӈ Ӈ */
+	0x04cc, 499,	/* ӌ Ӌ */
+	0x04d1, 499,	/* ӑ Ӑ */
+	0x04d3, 499,	/* ӓ Ӓ */
+	0x04d5, 499,	/* ӕ Ӕ */
+	0x04d7, 499,	/* ӗ Ӗ */
+	0x04d9, 499,	/* ә Ә */
+	0x04db, 499,	/* ӛ Ӛ */
+	0x04dd, 499,	/* ӝ Ӝ */
+	0x04df, 499,	/* ӟ Ӟ */
+	0x04e1, 499,	/* ӡ Ӡ */
+	0x04e3, 499,	/* ӣ Ӣ */
+	0x04e5, 499,	/* ӥ Ӥ */
+	0x04e7, 499,	/* ӧ Ӧ */
+	0x04e9, 499,	/* ө Ө */
+	0x04eb, 499,	/* ӫ Ӫ */
+	0x04ef, 499,	/* ӯ Ӯ */
+	0x04f1, 499,	/* ӱ Ӱ */
+	0x04f3, 499,	/* ӳ Ӳ */
+	0x04f5, 499,	/* ӵ Ӵ */
+	0x04f9, 499,	/* ӹ Ӹ */
+	0x1e01, 499,	/* ḁ Ḁ */
+	0x1e03, 499,	/* ḃ Ḃ */
+	0x1e05, 499,	/* ḅ Ḅ */
+	0x1e07, 499,	/* ḇ Ḇ */
+	0x1e09, 499,	/* ḉ Ḉ */
+	0x1e0b, 499,	/* ḋ Ḋ */
+	0x1e0d, 499,	/* ḍ Ḍ */
+	0x1e0f, 499,	/* ḏ Ḏ */
+	0x1e11, 499,	/* ḑ Ḑ */
+	0x1e13, 499,	/* ḓ Ḓ */
+	0x1e15, 499,	/* ḕ Ḕ */
+	0x1e17, 499,	/* ḗ Ḗ */
+	0x1e19, 499,	/* ḙ Ḙ */
+	0x1e1b, 499,	/* ḛ Ḛ */
+	0x1e1d, 499,	/* ḝ Ḝ */
+	0x1e1f, 499,	/* ḟ Ḟ */
+	0x1e21, 499,	/* ḡ Ḡ */
+	0x1e23, 499,	/* ḣ Ḣ */
+	0x1e25, 499,	/* ḥ Ḥ */
+	0x1e27, 499,	/* ḧ Ḧ */
+	0x1e29, 499,	/* ḩ Ḩ */
+	0x1e2b, 499,	/* ḫ Ḫ */
+	0x1e2d, 499,	/* ḭ Ḭ */
+	0x1e2f, 499,	/* ḯ Ḯ */
+	0x1e31, 499,	/* ḱ Ḱ */
+	0x1e33, 499,	/* ḳ Ḳ */
+	0x1e35, 499,	/* ḵ Ḵ */
+	0x1e37, 499,	/* ḷ Ḷ */
+	0x1e39, 499,	/* ḹ Ḹ */
+	0x1e3b, 499,	/* ḻ Ḻ */
+	0x1e3d, 499,	/* ḽ Ḽ */
+	0x1e3f, 499,	/* ḿ Ḿ */
+	0x1e41, 499,	/* ṁ Ṁ */
+	0x1e43, 499,	/* ṃ Ṃ */
+	0x1e45, 499,	/* ṅ Ṅ */
+	0x1e47, 499,	/* ṇ Ṇ */
+	0x1e49, 499,	/* ṉ Ṉ */
+	0x1e4b, 499,	/* ṋ Ṋ */
+	0x1e4d, 499,	/* ṍ Ṍ */
+	0x1e4f, 499,	/* ṏ Ṏ */
+	0x1e51, 499,	/* ṑ Ṑ */
+	0x1e53, 499,	/* ṓ Ṓ */
+	0x1e55, 499,	/* ṕ Ṕ */
+	0x1e57, 499,	/* ṗ Ṗ */
+	0x1e59, 499,	/* ṙ Ṙ */
+	0x1e5b, 499,	/* ṛ Ṛ */
+	0x1e5d, 499,	/* ṝ Ṝ */
+	0x1e5f, 499,	/* ṟ Ṟ */
+	0x1e61, 499,	/* ṡ Ṡ */
+	0x1e63, 499,	/* ṣ Ṣ */
+	0x1e65, 499,	/* ṥ Ṥ */
+	0x1e67, 499,	/* ṧ Ṧ */
+	0x1e69, 499,	/* ṩ Ṩ */
+	0x1e6b, 499,	/* ṫ Ṫ */
+	0x1e6d, 499,	/* ṭ Ṭ */
+	0x1e6f, 499,	/* ṯ Ṯ */
+	0x1e71, 499,	/* ṱ Ṱ */
+	0x1e73, 499,	/* ṳ Ṳ */
+	0x1e75, 499,	/* ṵ Ṵ */
+	0x1e77, 499,	/* ṷ Ṷ */
+	0x1e79, 499,	/* ṹ Ṹ */
+	0x1e7b, 499,	/* ṻ Ṻ */
+	0x1e7d, 499,	/* ṽ Ṽ */
+	0x1e7f, 499,	/* ṿ Ṿ */
+	0x1e81, 499,	/* ẁ Ẁ */
+	0x1e83, 499,	/* ẃ Ẃ */
+	0x1e85, 499,	/* ẅ Ẅ */
+	0x1e87, 499,	/* ẇ Ẇ */
+	0x1e89, 499,	/* ẉ Ẉ */
+	0x1e8b, 499,	/* ẋ Ẋ */
+	0x1e8d, 499,	/* ẍ Ẍ */
+	0x1e8f, 499,	/* ẏ Ẏ */
+	0x1e91, 499,	/* ẑ Ẑ */
+	0x1e93, 499,	/* ẓ Ẓ */
+	0x1e95, 499,	/* ẕ Ẕ */
+	0x1ea1, 499,	/* ạ Ạ */
+	0x1ea3, 499,	/* ả Ả */
+	0x1ea5, 499,	/* ấ Ấ */
+	0x1ea7, 499,	/* ầ Ầ */
+	0x1ea9, 499,	/* ẩ Ẩ */
+	0x1eab, 499,	/* ẫ Ẫ */
+	0x1ead, 499,	/* ậ Ậ */
+	0x1eaf, 499,	/* ắ Ắ */
+	0x1eb1, 499,	/* ằ Ằ */
+	0x1eb3, 499,	/* ẳ Ẳ */
+	0x1eb5, 499,	/* ẵ Ẵ */
+	0x1eb7, 499,	/* ặ Ặ */
+	0x1eb9, 499,	/* ẹ Ẹ */
+	0x1ebb, 499,	/* ẻ Ẻ */
+	0x1ebd, 499,	/* ẽ Ẽ */
+	0x1ebf, 499,	/* ế Ế */
+	0x1ec1, 499,	/* ề Ề */
+	0x1ec3, 499,	/* ể Ể */
+	0x1ec5, 499,	/* ễ Ễ */
+	0x1ec7, 499,	/* ệ Ệ */
+	0x1ec9, 499,	/* ỉ Ỉ */
+	0x1ecb, 499,	/* ị Ị */
+	0x1ecd, 499,	/* ọ Ọ */
+	0x1ecf, 499,	/* ỏ Ỏ */
+	0x1ed1, 499,	/* ố Ố */
+	0x1ed3, 499,	/* ồ Ồ */
+	0x1ed5, 499,	/* ổ Ổ */
+	0x1ed7, 499,	/* ỗ Ỗ */
+	0x1ed9, 499,	/* ộ Ộ */
+	0x1edb, 499,	/* ớ Ớ */
+	0x1edd, 499,	/* ờ Ờ */
+	0x1edf, 499,	/* ở Ở */
+	0x1ee1, 499,	/* ỡ Ỡ */
+	0x1ee3, 499,	/* ợ Ợ */
+	0x1ee5, 499,	/* ụ Ụ */
+	0x1ee7, 499,	/* ủ Ủ */
+	0x1ee9, 499,	/* ứ Ứ */
+	0x1eeb, 499,	/* ừ Ừ */
+	0x1eed, 499,	/* ử Ử */
+	0x1eef, 499,	/* ữ Ữ */
+	0x1ef1, 499,	/* ự Ự */
+	0x1ef3, 499,	/* ỳ Ỳ */
+	0x1ef5, 499,	/* ỵ Ỵ */
+	0x1ef7, 499,	/* ỷ Ỷ */
+	0x1ef9, 499,	/* ỹ Ỹ */
+	0x1f51, 508,	/* ὑ Ὑ */
+	0x1f53, 508,	/* ὓ Ὓ */
+	0x1f55, 508,	/* ὕ Ὕ */
+	0x1f57, 508,	/* ὗ Ὗ */
+	0x1fb3, 509,	/* ᾳ ᾼ */
+	0x1fc3, 509,	/* ῃ ῌ */
+	0x1fe5, 507,	/* ῥ Ῥ */
+	0x1ff3, 509,	/* ῳ ῼ */
+};
+
+/*
+ * upper case ranges
+ *	3rd col is conversion excess 500
+ */
+static
+Rune	__tolower2[] =
+{
+	0x0041,	0x005a, 532,	/* A-Z a-z */
+	0x00c0,	0x00d6, 532,	/* À-Ö à-ö */
+	0x00d8,	0x00de, 532,	/* Ø-Þ ø-þ */
+	0x0189,	0x018a, 705,	/* Ɖ-Ɗ ɖ-ɗ */
+	0x018e,	0x018f, 702,	/* Ǝ-Ə ɘ-ə */
+	0x01b1,	0x01b2, 717,	/* Ʊ-Ʋ ʊ-ʋ */
+	0x0388,	0x038a, 537,	/* Έ-Ί έ-ί */
+	0x038e,	0x038f, 563,	/* Ύ-Ώ ύ-ώ */
+	0x0391,	0x03a1, 532,	/* Α-Ρ α-ρ */
+	0x03a3,	0x03ab, 532,	/* Σ-Ϋ σ-ϋ */
+	0x0401,	0x040c, 580,	/* Ё-Ќ ё-ќ */
+	0x040e,	0x040f, 580,	/* Ў-Џ ў-џ */
+	0x0410,	0x042f, 532,	/* А-Я а-я */
+	0x0531,	0x0556, 548,	/* Ա-Ֆ ա-ֆ */
+	0x10a0,	0x10c5, 548,	/* Ⴀ-Ⴥ ა-ჵ */
+	0x1f08,	0x1f0f, 492,	/* Ἀ-Ἇ ἀ-ἇ */
+	0x1f18,	0x1f1d, 492,	/* Ἐ-Ἕ ἐ-ἕ */
+	0x1f28,	0x1f2f, 492,	/* Ἠ-Ἧ ἠ-ἧ */
+	0x1f38,	0x1f3f, 492,	/* Ἰ-Ἷ ἰ-ἷ */
+	0x1f48,	0x1f4d, 492,	/* Ὀ-Ὅ ὀ-ὅ */
+	0x1f68,	0x1f6f, 492,	/* Ὠ-Ὧ ὠ-ὧ */
+	0x1f88,	0x1f8f, 492,	/* ᾈ-ᾏ ᾀ-ᾇ */
+	0x1f98,	0x1f9f, 492,	/* ᾘ-ᾟ ᾐ-ᾗ */
+	0x1fa8,	0x1faf, 492,	/* ᾨ-ᾯ ᾠ-ᾧ */
+	0x1fb8,	0x1fb9, 492,	/* Ᾰ-Ᾱ ᾰ-ᾱ */
+	0x1fba,	0x1fbb, 426,	/* Ὰ-Ά ὰ-ά */
+	0x1fc8,	0x1fcb, 414,	/* Ὲ-Ή ὲ-ή */
+	0x1fd8,	0x1fd9, 492,	/* Ῐ-Ῑ ῐ-ῑ */
+	0x1fda,	0x1fdb, 400,	/* Ὶ-Ί ὶ-ί */
+	0x1fe8,	0x1fe9, 492,	/* Ῠ-Ῡ ῠ-ῡ */
+	0x1fea,	0x1feb, 388,	/* Ὺ-Ύ ὺ-ύ */
+	0x1ff8,	0x1ff9, 372,	/* Ὸ-Ό ὸ-ό */
+	0x1ffa,	0x1ffb, 374,	/* Ὼ-Ώ ὼ-ώ */
+	0x2160,	0x216f, 516,	/* Ⅰ-Ⅿ ⅰ-ⅿ */
+	0x24b6,	0x24cf, 526,	/* Ⓐ-Ⓩ ⓐ-ⓩ */
+	0xff21,	0xff3a, 532,	/* A-Z a-z */
+};
+
+/*
+ * upper case singlets
+ *	2nd col is conversion excess 500
+ */
+static
+Rune	__tolower1[] =
+{
+	0x0100, 501,	/* Ā ā */
+	0x0102, 501,	/* Ă ă */
+	0x0104, 501,	/* Ą ą */
+	0x0106, 501,	/* Ć ć */
+	0x0108, 501,	/* Ĉ ĉ */
+	0x010a, 501,	/* Ċ ċ */
+	0x010c, 501,	/* Č č */
+	0x010e, 501,	/* Ď ď */
+	0x0110, 501,	/* Đ đ */
+	0x0112, 501,	/* Ē ē */
+	0x0114, 501,	/* Ĕ ĕ */
+	0x0116, 501,	/* Ė ė */
+	0x0118, 501,	/* Ę ę */
+	0x011a, 501,	/* Ě ě */
+	0x011c, 501,	/* Ĝ ĝ */
+	0x011e, 501,	/* Ğ ğ */
+	0x0120, 501,	/* Ġ ġ */
+	0x0122, 501,	/* Ģ ģ */
+	0x0124, 501,	/* Ĥ ĥ */
+	0x0126, 501,	/* Ħ ħ */
+	0x0128, 501,	/* Ĩ ĩ */
+	0x012a, 501,	/* Ī ī */
+	0x012c, 501,	/* Ĭ ĭ */
+	0x012e, 501,	/* Į į */
+	0x0130, 301,	/* İ i */
+	0x0132, 501,	/* IJ ij */
+	0x0134, 501,	/* Ĵ ĵ */
+	0x0136, 501,	/* Ķ ķ */
+	0x0139, 501,	/* Ĺ ĺ */
+	0x013b, 501,	/* Ļ ļ */
+	0x013d, 501,	/* Ľ ľ */
+	0x013f, 501,	/* Ŀ ŀ */
+	0x0141, 501,	/* Ł ł */
+	0x0143, 501,	/* Ń ń */
+	0x0145, 501,	/* Ņ ņ */
+	0x0147, 501,	/* Ň ň */
+	0x014a, 501,	/* Ŋ ŋ */
+	0x014c, 501,	/* Ō ō */
+	0x014e, 501,	/* Ŏ ŏ */
+	0x0150, 501,	/* Ő ő */
+	0x0152, 501,	/* Œ œ */
+	0x0154, 501,	/* Ŕ ŕ */
+	0x0156, 501,	/* Ŗ ŗ */
+	0x0158, 501,	/* Ř ř */
+	0x015a, 501,	/* Ś ś */
+	0x015c, 501,	/* Ŝ ŝ */
+	0x015e, 501,	/* Ş ş */
+	0x0160, 501,	/* Š š */
+	0x0162, 501,	/* Ţ ţ */
+	0x0164, 501,	/* Ť ť */
+	0x0166, 501,	/* Ŧ ŧ */
+	0x0168, 501,	/* Ũ ũ */
+	0x016a, 501,	/* Ū ū */
+	0x016c, 501,	/* Ŭ ŭ */
+	0x016e, 501,	/* Ů ů */
+	0x0170, 501,	/* Ű ű */
+	0x0172, 501,	/* Ų ų */
+	0x0174, 501,	/* Ŵ ŵ */
+	0x0176, 501,	/* Ŷ ŷ */
+	0x0178, 379,	/* Ÿ ÿ */
+	0x0179, 501,	/* Ź ź */
+	0x017b, 501,	/* Ż ż */
+	0x017d, 501,	/* Ž ž */
+	0x0181, 710,	/* Ɓ ɓ */
+	0x0182, 501,	/* Ƃ ƃ */
+	0x0184, 501,	/* Ƅ ƅ */
+	0x0186, 706,	/* Ɔ ɔ */
+	0x0187, 501,	/* Ƈ ƈ */
+	0x018b, 501,	/* Ƌ ƌ */
+	0x0190, 703,	/* Ɛ ɛ */
+	0x0191, 501,	/* Ƒ ƒ */
+	0x0193, 705,	/* Ɠ ɠ */
+	0x0194, 707,	/* Ɣ ɣ */
+	0x0196, 711,	/* Ɩ ɩ */
+	0x0197, 709,	/* Ɨ ɨ */
+	0x0198, 501,	/* Ƙ ƙ */
+	0x019c, 711,	/* Ɯ ɯ */
+	0x019d, 713,	/* Ɲ ɲ */
+	0x01a0, 501,	/* Ơ ơ */
+	0x01a2, 501,	/* Ƣ ƣ */
+	0x01a4, 501,	/* Ƥ ƥ */
+	0x01a7, 501,	/* Ƨ ƨ */
+	0x01a9, 718,	/* Ʃ ʃ */
+	0x01ac, 501,	/* Ƭ ƭ */
+	0x01ae, 718,	/* Ʈ ʈ */
+	0x01af, 501,	/* Ư ư */
+	0x01b3, 501,	/* Ƴ ƴ */
+	0x01b5, 501,	/* Ƶ ƶ */
+	0x01b7, 719,	/* Ʒ ʒ */
+	0x01b8, 501,	/* Ƹ ƹ */
+	0x01bc, 501,	/* Ƽ ƽ */
+	0x01c4, 502,	/* DŽ dž */
+	0x01c5, 501,	/* Dž dž */
+	0x01c7, 502,	/* LJ lj */
+	0x01c8, 501,	/* Lj lj */
+	0x01ca, 502,	/* NJ nj */
+	0x01cb, 501,	/* Nj nj */
+	0x01cd, 501,	/* Ǎ ǎ */
+	0x01cf, 501,	/* Ǐ ǐ */
+	0x01d1, 501,	/* Ǒ ǒ */
+	0x01d3, 501,	/* Ǔ ǔ */
+	0x01d5, 501,	/* Ǖ ǖ */
+	0x01d7, 501,	/* Ǘ ǘ */
+	0x01d9, 501,	/* Ǚ ǚ */
+	0x01db, 501,	/* Ǜ ǜ */
+	0x01de, 501,	/* Ǟ ǟ */
+	0x01e0, 501,	/* Ǡ ǡ */
+	0x01e2, 501,	/* Ǣ ǣ */
+	0x01e4, 501,	/* Ǥ ǥ */
+	0x01e6, 501,	/* Ǧ ǧ */
+	0x01e8, 501,	/* Ǩ ǩ */
+	0x01ea, 501,	/* Ǫ ǫ */
+	0x01ec, 501,	/* Ǭ ǭ */
+	0x01ee, 501,	/* Ǯ ǯ */
+	0x01f1, 502,	/* DZ dz */
+	0x01f2, 501,	/* Dz dz */
+	0x01f4, 501,	/* Ǵ ǵ */
+	0x01fa, 501,	/* Ǻ ǻ */
+	0x01fc, 501,	/* Ǽ ǽ */
+	0x01fe, 501,	/* Ǿ ǿ */
+	0x0200, 501,	/* Ȁ ȁ */
+	0x0202, 501,	/* Ȃ ȃ */
+	0x0204, 501,	/* Ȅ ȅ */
+	0x0206, 501,	/* Ȇ ȇ */
+	0x0208, 501,	/* Ȉ ȉ */
+	0x020a, 501,	/* Ȋ ȋ */
+	0x020c, 501,	/* Ȍ ȍ */
+	0x020e, 501,	/* Ȏ ȏ */
+	0x0210, 501,	/* Ȑ ȑ */
+	0x0212, 501,	/* Ȓ ȓ */
+	0x0214, 501,	/* Ȕ ȕ */
+	0x0216, 501,	/* Ȗ ȗ */
+	0x0386, 538,	/* Ά ά */
+	0x038c, 564,	/* Ό ό */
+	0x03e2, 501,	/* Ϣ ϣ */
+	0x03e4, 501,	/* Ϥ ϥ */
+	0x03e6, 501,	/* Ϧ ϧ */
+	0x03e8, 501,	/* Ϩ ϩ */
+	0x03ea, 501,	/* Ϫ ϫ */
+	0x03ec, 501,	/* Ϭ ϭ */
+	0x03ee, 501,	/* Ϯ ϯ */
+	0x0460, 501,	/* Ѡ ѡ */
+	0x0462, 501,	/* Ѣ ѣ */
+	0x0464, 501,	/* Ѥ ѥ */
+	0x0466, 501,	/* Ѧ ѧ */
+	0x0468, 501,	/* Ѩ ѩ */
+	0x046a, 501,	/* Ѫ ѫ */
+	0x046c, 501,	/* Ѭ ѭ */
+	0x046e, 501,	/* Ѯ ѯ */
+	0x0470, 501,	/* Ѱ ѱ */
+	0x0472, 501,	/* Ѳ ѳ */
+	0x0474, 501,	/* Ѵ ѵ */
+	0x0476, 501,	/* Ѷ ѷ */
+	0x0478, 501,	/* Ѹ ѹ */
+	0x047a, 501,	/* Ѻ ѻ */
+	0x047c, 501,	/* Ѽ ѽ */
+	0x047e, 501,	/* Ѿ ѿ */
+	0x0480, 501,	/* Ҁ ҁ */
+	0x0490, 501,	/* Ґ ґ */
+	0x0492, 501,	/* Ғ ғ */
+	0x0494, 501,	/* Ҕ ҕ */
+	0x0496, 501,	/* Җ җ */
+	0x0498, 501,	/* Ҙ ҙ */
+	0x049a, 501,	/* Қ қ */
+	0x049c, 501,	/* Ҝ ҝ */
+	0x049e, 501,	/* Ҟ ҟ */
+	0x04a0, 501,	/* Ҡ ҡ */
+	0x04a2, 501,	/* Ң ң */
+	0x04a4, 501,	/* Ҥ ҥ */
+	0x04a6, 501,	/* Ҧ ҧ */
+	0x04a8, 501,	/* Ҩ ҩ */
+	0x04aa, 501,	/* Ҫ ҫ */
+	0x04ac, 501,	/* Ҭ ҭ */
+	0x04ae, 501,	/* Ү ү */
+	0x04b0, 501,	/* Ұ ұ */
+	0x04b2, 501,	/* Ҳ ҳ */
+	0x04b4, 501,	/* Ҵ ҵ */
+	0x04b6, 501,	/* Ҷ ҷ */
+	0x04b8, 501,	/* Ҹ ҹ */
+	0x04ba, 501,	/* Һ һ */
+	0x04bc, 501,	/* Ҽ ҽ */
+	0x04be, 501,	/* Ҿ ҿ */
+	0x04c1, 501,	/* Ӂ ӂ */
+	0x04c3, 501,	/* Ӄ ӄ */
+	0x04c7, 501,	/* Ӈ ӈ */
+	0x04cb, 501,	/* Ӌ ӌ */
+	0x04d0, 501,	/* Ӑ ӑ */
+	0x04d2, 501,	/* Ӓ ӓ */
+	0x04d4, 501,	/* Ӕ ӕ */
+	0x04d6, 501,	/* Ӗ ӗ */
+	0x04d8, 501,	/* Ә ә */
+	0x04da, 501,	/* Ӛ ӛ */
+	0x04dc, 501,	/* Ӝ ӝ */
+	0x04de, 501,	/* Ӟ ӟ */
+	0x04e0, 501,	/* Ӡ ӡ */
+	0x04e2, 501,	/* Ӣ ӣ */
+	0x04e4, 501,	/* Ӥ ӥ */
+	0x04e6, 501,	/* Ӧ ӧ */
+	0x04e8, 501,	/* Ө ө */
+	0x04ea, 501,	/* Ӫ ӫ */
+	0x04ee, 501,	/* Ӯ ӯ */
+	0x04f0, 501,	/* Ӱ ӱ */
+	0x04f2, 501,	/* Ӳ ӳ */
+	0x04f4, 501,	/* Ӵ ӵ */
+	0x04f8, 501,	/* Ӹ ӹ */
+	0x1e00, 501,	/* Ḁ ḁ */
+	0x1e02, 501,	/* Ḃ ḃ */
+	0x1e04, 501,	/* Ḅ ḅ */
+	0x1e06, 501,	/* Ḇ ḇ */
+	0x1e08, 501,	/* Ḉ ḉ */
+	0x1e0a, 501,	/* Ḋ ḋ */
+	0x1e0c, 501,	/* Ḍ ḍ */
+	0x1e0e, 501,	/* Ḏ ḏ */
+	0x1e10, 501,	/* Ḑ ḑ */
+	0x1e12, 501,	/* Ḓ ḓ */
+	0x1e14, 501,	/* Ḕ ḕ */
+	0x1e16, 501,	/* Ḗ ḗ */
+	0x1e18, 501,	/* Ḙ ḙ */
+	0x1e1a, 501,	/* Ḛ ḛ */
+	0x1e1c, 501,	/* Ḝ ḝ */
+	0x1e1e, 501,	/* Ḟ ḟ */
+	0x1e20, 501,	/* Ḡ ḡ */
+	0x1e22, 501,	/* Ḣ ḣ */
+	0x1e24, 501,	/* Ḥ ḥ */
+	0x1e26, 501,	/* Ḧ ḧ */
+	0x1e28, 501,	/* Ḩ ḩ */
+	0x1e2a, 501,	/* Ḫ ḫ */
+	0x1e2c, 501,	/* Ḭ ḭ */
+	0x1e2e, 501,	/* Ḯ ḯ */
+	0x1e30, 501,	/* Ḱ ḱ */
+	0x1e32, 501,	/* Ḳ ḳ */
+	0x1e34, 501,	/* Ḵ ḵ */
+	0x1e36, 501,	/* Ḷ ḷ */
+	0x1e38, 501,	/* Ḹ ḹ */
+	0x1e3a, 501,	/* Ḻ ḻ */
+	0x1e3c, 501,	/* Ḽ ḽ */
+	0x1e3e, 501,	/* Ḿ ḿ */
+	0x1e40, 501,	/* Ṁ ṁ */
+	0x1e42, 501,	/* Ṃ ṃ */
+	0x1e44, 501,	/* Ṅ ṅ */
+	0x1e46, 501,	/* Ṇ ṇ */
+	0x1e48, 501,	/* Ṉ ṉ */
+	0x1e4a, 501,	/* Ṋ ṋ */
+	0x1e4c, 501,	/* Ṍ ṍ */
+	0x1e4e, 501,	/* Ṏ ṏ */
+	0x1e50, 501,	/* Ṑ ṑ */
+	0x1e52, 501,	/* Ṓ ṓ */
+	0x1e54, 501,	/* Ṕ ṕ */
+	0x1e56, 501,	/* Ṗ ṗ */
+	0x1e58, 501,	/* Ṙ ṙ */
+	0x1e5a, 501,	/* Ṛ ṛ */
+	0x1e5c, 501,	/* Ṝ ṝ */
+	0x1e5e, 501,	/* Ṟ ṟ */
+	0x1e60, 501,	/* Ṡ ṡ */
+	0x1e62, 501,	/* Ṣ ṣ */
+	0x1e64, 501,	/* Ṥ ṥ */
+	0x1e66, 501,	/* Ṧ ṧ */
+	0x1e68, 501,	/* Ṩ ṩ */
+	0x1e6a, 501,	/* Ṫ ṫ */
+	0x1e6c, 501,	/* Ṭ ṭ */
+	0x1e6e, 501,	/* Ṯ ṯ */
+	0x1e70, 501,	/* Ṱ ṱ */
+	0x1e72, 501,	/* Ṳ ṳ */
+	0x1e74, 501,	/* Ṵ ṵ */
+	0x1e76, 501,	/* Ṷ ṷ */
+	0x1e78, 501,	/* Ṹ ṹ */
+	0x1e7a, 501,	/* Ṻ ṻ */
+	0x1e7c, 501,	/* Ṽ ṽ */
+	0x1e7e, 501,	/* Ṿ ṿ */
+	0x1e80, 501,	/* Ẁ ẁ */
+	0x1e82, 501,	/* Ẃ ẃ */
+	0x1e84, 501,	/* Ẅ ẅ */
+	0x1e86, 501,	/* Ẇ ẇ */
+	0x1e88, 501,	/* Ẉ ẉ */
+	0x1e8a, 501,	/* Ẋ ẋ */
+	0x1e8c, 501,	/* Ẍ ẍ */
+	0x1e8e, 501,	/* Ẏ ẏ */
+	0x1e90, 501,	/* Ẑ ẑ */
+	0x1e92, 501,	/* Ẓ ẓ */
+	0x1e94, 501,	/* Ẕ ẕ */
+	0x1ea0, 501,	/* Ạ ạ */
+	0x1ea2, 501,	/* Ả ả */
+	0x1ea4, 501,	/* Ấ ấ */
+	0x1ea6, 501,	/* Ầ ầ */
+	0x1ea8, 501,	/* Ẩ ẩ */
+	0x1eaa, 501,	/* Ẫ ẫ */
+	0x1eac, 501,	/* Ậ ậ */
+	0x1eae, 501,	/* Ắ ắ */
+	0x1eb0, 501,	/* Ằ ằ */
+	0x1eb2, 501,	/* Ẳ ẳ */
+	0x1eb4, 501,	/* Ẵ ẵ */
+	0x1eb6, 501,	/* Ặ ặ */
+	0x1eb8, 501,	/* Ẹ ẹ */
+	0x1eba, 501,	/* Ẻ ẻ */
+	0x1ebc, 501,	/* Ẽ ẽ */
+	0x1ebe, 501,	/* Ế ế */
+	0x1ec0, 501,	/* Ề ề */
+	0x1ec2, 501,	/* Ể ể */
+	0x1ec4, 501,	/* Ễ ễ */
+	0x1ec6, 501,	/* Ệ ệ */
+	0x1ec8, 501,	/* Ỉ ỉ */
+	0x1eca, 501,	/* Ị ị */
+	0x1ecc, 501,	/* Ọ ọ */
+	0x1ece, 501,	/* Ỏ ỏ */
+	0x1ed0, 501,	/* Ố ố */
+	0x1ed2, 501,	/* Ồ ồ */
+	0x1ed4, 501,	/* Ổ ổ */
+	0x1ed6, 501,	/* Ỗ ỗ */
+	0x1ed8, 501,	/* Ộ ộ */
+	0x1eda, 501,	/* Ớ ớ */
+	0x1edc, 501,	/* Ờ ờ */
+	0x1ede, 501,	/* Ở ở */
+	0x1ee0, 501,	/* Ỡ ỡ */
+	0x1ee2, 501,	/* Ợ ợ */
+	0x1ee4, 501,	/* Ụ ụ */
+	0x1ee6, 501,	/* Ủ ủ */
+	0x1ee8, 501,	/* Ứ ứ */
+	0x1eea, 501,	/* Ừ ừ */
+	0x1eec, 501,	/* Ử ử */
+	0x1eee, 501,	/* Ữ ữ */
+	0x1ef0, 501,	/* Ự ự */
+	0x1ef2, 501,	/* Ỳ ỳ */
+	0x1ef4, 501,	/* Ỵ ỵ */
+	0x1ef6, 501,	/* Ỷ ỷ */
+	0x1ef8, 501,	/* Ỹ ỹ */
+	0x1f59, 492,	/* Ὑ ὑ */
+	0x1f5b, 492,	/* Ὓ ὓ */
+	0x1f5d, 492,	/* Ὕ ὕ */
+	0x1f5f, 492,	/* Ὗ ὗ */
+	0x1fbc, 491,	/* ᾼ ᾳ */
+	0x1fcc, 491,	/* ῌ ῃ */
+	0x1fec, 493,	/* Ῥ ῥ */
+	0x1ffc, 491,	/* ῼ ῳ */
+};
+
+/*
+ * title characters are those between
+ * upper and lower case. ie DZ Dz dz
+ */
+static
+Rune	__totitle1[] =
+{
+	0x01c4, 501,	/* DŽ Dž */
+	0x01c6, 499,	/* dž Dž */
+	0x01c7, 501,	/* LJ Lj */
+	0x01c9, 499,	/* lj Lj */
+	0x01ca, 501,	/* NJ Nj */
+	0x01cc, 499,	/* nj Nj */
+	0x01f1, 501,	/* DZ Dz */
+	0x01f3, 499,	/* dz Dz */
+};
+
+static
+Rune*
+bsearch(Rune c, Rune *t, int n, int ne)
+{
+	Rune *p;
+	int m;
+
+	while(n > 1) {
+		m = n/2;
+		p = t + m*ne;
+		if(c >= p[0]) {
+			t = p;
+			n = n-m;
+		} else
+			n = m;
+	}
+	if(n && c >= t[0])
+		return t;
+	return 0;
+}
+
+Rune
+tolowerrune(Rune c)
+{
+	Rune *p;
+
+	p = bsearch(c, __tolower2, nelem(__tolower2)/3, 3);
+	if(p && c >= p[0] && c <= p[1])
+		return c + p[2] - 500;
+	p = bsearch(c, __tolower1, nelem(__tolower1)/2, 2);
+	if(p && c == p[0])
+		return c + p[1] - 500;
+	return c;
+}
+
+Rune
+toupperrune(Rune c)
+{
+	Rune *p;
+
+	p = bsearch(c, __toupper2, nelem(__toupper2)/3, 3);
+	if(p && c >= p[0] && c <= p[1])
+		return c + p[2] - 500;
+	p = bsearch(c, __toupper1, nelem(__toupper1)/2, 2);
+	if(p && c == p[0])
+		return c + p[1] - 500;
+	return c;
+}
+
+Rune
+totitlerune(Rune c)
+{
+	Rune *p;
+
+	p = bsearch(c, __totitle1, nelem(__totitle1)/2, 2);
+	if(p && c == p[0])
+		return c + p[1] - 500;
+	return c;
+}
+
+int
+islowerrune(Rune c)
+{
+	Rune *p;
+
+	p = bsearch(c, __toupper2, nelem(__toupper2)/3, 3);
+	if(p && c >= p[0] && c <= p[1])
+		return 1;
+	p = bsearch(c, __toupper1, nelem(__toupper1)/2, 2);
+	if(p && c == p[0])
+		return 1;
+	return 0;
+}
+
+int
+isupperrune(Rune c)
+{
+	Rune *p;
+
+	p = bsearch(c, __tolower2, nelem(__tolower2)/3, 3);
+	if(p && c >= p[0] && c <= p[1])
+		return 1;
+	p = bsearch(c, __tolower1, nelem(__tolower1)/2, 2);
+	if(p && c == p[0])
+		return 1;
+	return 0;
+}
+
+int
+isalpharune(Rune c)
+{
+	Rune *p;
+
+	if(isupperrune(c) || islowerrune(c))
+		return 1;
+	p = bsearch(c, __alpha2, nelem(__alpha2)/2, 2);
+	if(p && c >= p[0] && c <= p[1])
+		return 1;
+	p = bsearch(c, __alpha1, nelem(__alpha1), 1);
+	if(p && c == p[0])
+		return 1;
+	return 0;
+}
+
+int
+istitlerune(Rune c)
+{
+	return isupperrune(c) && islowerrune(c);
+}
+
+int
+isspacerune(Rune c)
+{
+	Rune *p;
+
+	p = bsearch(c, __space2, nelem(__space2)/2, 2);
+	if(p && c >= p[0] && c <= p[1])
+		return 1;
+	return 0;
+}

+ 14 - 0
sys/src/ape/lib/utf/utfdef.h

@@ -0,0 +1,14 @@
+#define uchar _utfuchar
+#define ushort _utfushort
+#define uint _utfuint
+#define ulong _utfulong
+#define vlong _utfvlong
+#define uvlong _utfuvlong
+
+typedef unsigned char		uchar;
+typedef unsigned short		ushort;
+typedef unsigned int		uint;
+typedef unsigned long		ulong;
+
+#define nelem(x) (sizeof(x)/sizeof((x)[0]))
+#define nil ((void*)0)

+ 51 - 0
sys/src/ape/lib/utf/utfecpy.c

@@ -0,0 +1,51 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+static void*
+memccpy(void *a1, void *a2, int c, ulong n)
+{
+	uchar *s1, *s2;
+
+	s1 = a1;
+	s2 = a2;
+	c &= 0xFF;
+	while(n > 0) {
+		if((*s1++ = *s2++) == c)
+			return s1;
+		n--;
+	}
+	return 0;
+}
+char*
+utfecpy(char *to, char *e, char *from)
+{
+	char *end;
+
+	if(to >= e)
+		return to;
+	end = memccpy(to, from, '\0', e - to);
+	if(end == nil){
+		end = e-1;
+		while(end>to && (*--end&0xC0)==0x80)
+			;
+		*end = '\0';
+	}else{
+		end--;
+	}
+	return end;
+}

+ 38 - 0
sys/src/ape/lib/utf/utflen.c

@@ -0,0 +1,38 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+int
+utflen(char *s)
+{
+	int c;
+	long n;
+	Rune rune;
+
+	n = 0;
+	for(;;) {
+		c = *(uchar*)s;
+		if(c < Runeself) {
+			if(c == 0)
+				return n;
+			s++;
+		} else
+			s += chartorune(&rune, s);
+		n++;
+	}
+	return 0;
+}

+ 41 - 0
sys/src/ape/lib/utf/utfnlen.c

@@ -0,0 +1,41 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+int
+utfnlen(char *s, long m)
+{
+	int c;
+	long n;
+	Rune rune;
+	char *es;
+
+	es = s + m;
+	for(n = 0; s < es; n++) {
+		c = *(uchar*)s;
+		if(c < Runeself){
+			if(c == '\0')
+				break;
+			s++;
+			continue;
+		}
+		if(!fullrune(s, es-s))
+			break;
+		s += chartorune(&rune, s);
+	}
+	return n;
+}

+ 46 - 0
sys/src/ape/lib/utf/utfrrune.c

@@ -0,0 +1,46 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+char*
+utfrrune(char *s, long c)
+{
+	long c1;
+	Rune r;
+	char *s1;
+
+	if(c < Runesync)		/* not part of utf sequence */
+		return strrchr(s, c);
+
+	s1 = 0;
+	for(;;) {
+		c1 = *(uchar*)s;
+		if(c1 < Runeself) {	/* one byte rune */
+			if(c1 == 0)
+				return s1;
+			if(c1 == c)
+				s1 = s;
+			s++;
+			continue;
+		}
+		c1 = chartorune(&r, s);
+		if(r == c)
+			s1 = s;
+		s += c1;
+	}
+	return 0;
+}

+ 45 - 0
sys/src/ape/lib/utf/utfrune.c

@@ -0,0 +1,45 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+char*
+utfrune(char *s, long c)
+{
+	long c1;
+	Rune r;
+	int n;
+
+	if(c < Runesync)		/* not part of utf sequence */
+		return strchr(s, c);
+
+	for(;;) {
+		c1 = *(uchar*)s;
+		if(c1 < Runeself) {	/* one byte rune */
+			if(c1 == 0)
+				return 0;
+			if(c1 == c)
+				return s;
+			s++;
+			continue;
+		}
+		n = chartorune(&r, s);
+		if(r == c)
+			return s;
+		s += n;
+	}
+	return 0;
+}

+ 41 - 0
sys/src/ape/lib/utf/utfutf.c

@@ -0,0 +1,41 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "utf.h"
+#include "utfdef.h"
+
+
+/*
+ * Return pointer to first occurrence of s2 in s1,
+ * 0 if none
+ */
+char*
+utfutf(char *s1, char *s2)
+{
+	char *p;
+	long f, n1, n2;
+	Rune r;
+
+	n1 = chartorune(&r, s2);
+	f = r;
+	if(f <= Runesync)		/* represents self */
+		return strstr(s1, s2);
+
+	n2 = strlen(s2);
+	for(p=s1; p=utfrune(p, f); p+=n1)
+		if(strncmp(p, s2, n2) == 0)
+			return p;
+	return 0;
+}

+ 1 - 1
sys/src/cmd/disk/mkext.c

@@ -151,7 +151,7 @@ mkdir(char *name, ulong mode, ulong mtime, char *uid, char *gid)
 		rerrstr(olderr, sizeof(olderr));
 		if((d = dirstat(name)) == nil || !(d->mode & DMDIR)){
 			free(d);
-			warn("can't make directory %q, mode %luo: %s", name, olderr);
+			warn("can't make directory %q, mode %luo: %s", name, mode, olderr);
 			return;
 		}
 	}

+ 2 - 2
sys/src/cmd/hget.c

@@ -779,8 +779,8 @@ doftp(URL *u, URL *px, Range *r, int out, long mtime)
 	char conndir[NETPATHLEN];
 	char *p;
 
-	/* untestet, proxy dosn't work with ftp (I think) */
-	if(px->host != nil){
+	/* untested, proxy dosn't work with ftp (I think) */
+	if(px->host == nil){
 		ctl = dial(netmkaddr(u->host, tcpdir, u->port), 0, conndir, 0);
 	} else {
 		ctl = dial(netmkaddr(px->host, tcpdir, px->port), 0, conndir, 0);

+ 1 - 1
sys/src/cmd/ip/httpd/webls.c

@@ -108,7 +108,7 @@ allowed(char *dir)
 	int	okay;
 	Resub	match;
 
-	if (strstr(dir, ".."))
+	if (strcmp(dir, "..") == 0 || strncmp(dir, "../", 3) == 0)
 		return(0);
 	if (aio == nil)
 		return(0);

+ 2 - 2
sys/src/cmd/ratfs/misc.c

@@ -2,8 +2,8 @@
 #include <ip.h>
 
 enum {
-	Maxdoms	=	10,	/* max domains in a path */
-	Timeout =	15*60,	/* seconds until temporarily trusted addr times out */
+	Maxdoms	=	10,		/* max domains in a path */
+	Timeout =	2*60*60,	/* seconds until temporarily trusted addr times out */
 };
 
 static	int	accountmatch(char*, char**, int, char*);

+ 198 - 56
sys/src/cmd/tar.c

@@ -8,6 +8,30 @@
 #define NBLOCK	40	/* maximum blocksize */
 #define DBLOCK	20	/* default blocksize */
 #define NAMSIZ	100
+
+enum {
+	Maxpfx = 155,		/* from POSIX */
+	Maxname = NAMSIZ + 1 + Maxpfx,
+};
+
+/* POSIX link flags */
+enum {
+	LF_PLAIN1 =	'\0',
+	LF_PLAIN2 =	'0',
+	LF_LINK =	'1',
+	LF_SYMLINK1 =	'2',
+	LF_SYMLINK2 =	's',
+	LF_CHR =	'3',
+	LF_BLK =	'4',
+	LF_DIR =	'5',
+	LF_FIFO =	'6',
+	LF_CONTIG =	'7',
+	/* 'A' - 'Z' are reserved for custom implementations */
+};
+
+#define islink(lf)	(isreallink(lf) || issymlink(lf))
+#define isreallink(lf)	((lf) == LF_LINK)
+#define issymlink(lf)	((lf) == LF_SYMLINK1 || (lf) == LF_SYMLINK2)
 union	hblock
 {
 	char	dummy[TBLOCK];
@@ -22,14 +46,25 @@ union	hblock
 		char	chksum[8];
 		char	linkflag;
 		char	linkname[NAMSIZ];
+		/* rest are defined by POSIX's ustar format; see p1003.2b */
+		char	magic[6];	/* "ustar" */
+		char	version[2];
+		char	uname[32];
+		char	gname[32];
+		char	devmajor[8];
+		char	devminor[8];
+		char	prefix[155];  /* if non-null, path = prefix "/" name */
 	} dbuf;
 } dblock, tbuf[NBLOCK];
 
 Dir *stbuf;
 Biobuf bout;
+static int ustar;		/* flag: tape block just read is ustar format */
+static char *fullname;			/* if non-nil, prefix "/" name */
 
 int	rflag, xflag, vflag, tflag, mt, cflag, fflag, Tflag, Rflag;
 int	uflag, gflag;
+static int posix;		/* flag: we're writing ustar format archive */
 int	chksum, recno, first;
 int	nblock = DBLOCK;
 
@@ -54,6 +89,109 @@ void	backtar(void);
 void	flushtar(void);
 void	affix(int, char *);
 int	volprompt(void);
+
+static int
+isustar(struct header *hp)
+{
+	return strcmp(hp->magic, "ustar") == 0;
+}
+
+static void
+setustar(struct header *hp)
+{
+	strncpy(hp->magic, "ustar", sizeof hp->magic);
+	strncpy(hp->version, "00", sizeof hp->version);
+}
+
+/*
+ * s is at most n bytes long, but need not be NUL-terminated.
+ * if shorter than n bytes, all bytes after the first NUL must also
+ * be NUL.
+ */
+static int
+strnlen(char *s, int n)
+{
+	if (s[n - 1] != '\0')
+		return n;
+	else
+		return strlen(s);
+}
+
+/* set fullname from header; called from getdir() */
+static void
+getfullname(struct header *hp)
+{
+	int pfxlen, namlen;
+
+	if (fullname != nil)
+		free(fullname);
+	namlen = strnlen(hp->name, sizeof hp->name);
+	if (hp->prefix[0] == '\0' || !ustar) {
+		fullname = malloc(namlen + 1);
+		if (fullname == nil)
+			sysfatal("out of memory: %r");
+		memmove(fullname, hp->name, namlen);
+		fullname[namlen] = '\0';
+		return;
+	}
+	pfxlen = strnlen(hp->prefix, sizeof hp->prefix);
+	fullname = malloc(pfxlen + 1 + namlen + 1);
+	if (fullname == nil)
+		sysfatal("out of memory: %r");
+	memmove(fullname, hp->prefix, pfxlen);
+	fullname[pfxlen] = '/';
+	memmove(fullname + pfxlen + 1, hp->name, namlen);
+	fullname[pfxlen + 1 + namlen] = '\0';
+}
+
+/*
+ * if name is longer than NAMSIZ bytes, try to split it at a slash and fit the
+ * pieces into hp->prefix and hp->name.
+ */
+static int
+putfullname(struct header *hp, char *name)
+{
+	int namlen, pfxlen;
+	char *sl, *osl;
+
+	namlen = strlen(name);
+	if (namlen <= NAMSIZ) {
+		strncpy(hp->name, name, NAMSIZ);
+		hp->prefix[0] = '\0';		/* ustar paranoia */
+		return 0;
+	}
+	if (!posix || namlen > NAMSIZ + 1 + sizeof hp->prefix) {
+		fprint(2, "tar: name too long for tar header: %s\n", name);
+		return -1;
+	}
+	/*
+	 * try various splits until one results in pieces that fit into the
+	 * appropriate fields of the header.  look for slashes from right
+	 * to left, in the hopes of putting the largest part of the name into
+	 * hp->prefix, which is larger than hp->name.
+	 */
+	sl = strrchr(name, '/');
+	while (sl != nil) {
+		pfxlen = sl - name;
+		if (pfxlen <= sizeof hp->prefix && namlen-1 - pfxlen <= NAMSIZ)
+			break;
+		osl = sl;
+		*osl = '\0';
+		sl = strrchr(name, '/');
+		*osl = '/';
+	}
+	if (sl == nil) {
+		fprint(2, "tar: name can't be split to fit tar header: %s\n",
+			name);
+		return -1;
+	}
+	*sl = '\0';
+	strncpy(hp->prefix, name, sizeof hp->prefix);
+	*sl = '/';
+	strncpy(hp->name, sl + 1, sizeof hp->name);
+	return 0;
+}
+
 void
 main(int argc, char **argv)
 {
@@ -91,6 +229,9 @@ main(int argc, char **argv)
 			cflag++;
 			rflag++;
 			break;
+		case 'p':
+			posix++;
+			break;
 		case 'r':
 			rflag++;
 			break;
@@ -248,10 +389,8 @@ getdir(void)
 		return;
 	if(stbuf == nil){
 		stbuf = malloc(sizeof(Dir));
-		if(stbuf == nil) {
-			fprint(2, "tar: can't malloc: %r\n");
-			exits("malloc");
-		}
+		if(stbuf == nil)
+			sysfatal("out of memory: %r");
 	}
 	sp = stbuf;
 	sp->mode = strtol(dblock.dbuf.mode, 0, 8);
@@ -260,13 +399,14 @@ getdir(void)
 	sp->length = strtol(dblock.dbuf.size, 0, 8);
 	sp->mtime = strtol(dblock.dbuf.mtime, 0, 8);
 	chksum = strtol(dblock.dbuf.chksum, 0, 8);
-	if (chksum != checksum()) {
-		fprint(2, "directory checksum error\n");
-		exits("checksum error");
-	}
+	if (chksum != checksum())
+		sysfatal("header checksum error");
 	sp->qid.type = 0;
+	ustar = isustar(&dblock.dbuf);
+	getfullname(&dblock.dbuf);
 	/* the mode test is ugly but sometimes necessary */
-	if (dblock.dbuf.linkflag == '5' || (sp->mode&0170000) == 040000) {
+	if (dblock.dbuf.linkflag == LF_DIR || (sp->mode&0170000) == 040000 ||
+	    strrchr(fullname, '\0')[-1] == '/') {
 		sp->qid.type |= QTDIR;
 		sp->mode |= DMDIR;
 	}
@@ -278,8 +418,13 @@ passtar(void)
 	long blocks;
 	char buf[TBLOCK];
 
-	if (dblock.dbuf.linkflag == '1' || dblock.dbuf.linkflag == 's')
+	switch (dblock.dbuf.linkflag) {
+	case LF_LINK:
+	case LF_SYMLINK1:
+	case LF_SYMLINK2:
+	case LF_FIFO:
 		return;
+	}
 	blocks = stbuf->length;
 	blocks += TBLOCK-1;
 	blocks /= TBLOCK;
@@ -296,7 +441,7 @@ putfile(char *dir, char *longname, char *sname)
 	char buf[TBLOCK];
 	char curdir[4096];
 	char shortname[4096];
-	char *cp, *cp2;
+	char *cp;
 	Dir *db;
 	int i, n;
 
@@ -321,17 +466,17 @@ putfile(char *dir, char *longname, char *sname)
 		for (i = 0, cp = buf; *cp++ = longname[i++];);
 		*--cp = '/';
 		*++cp = 0;
-		if( (cp - buf) >= NAMSIZ) {
-			fprint(2, "tar: %s: file name too long\n", longname);
-			close(infile);
-			return;
-		}
 		stbuf->length = 0;
+
 		tomodes(stbuf);
-		strcpy(dblock.dbuf.name,buf);
-		dblock.dbuf.linkflag = '5';		/* Directory */
+		if (putfullname(&dblock.dbuf, buf) < 0) {
+			close(infile);
+			return;		/* putfullname already complained */
+		}
+		dblock.dbuf.linkflag = LF_DIR;
 		sprint(dblock.dbuf.chksum, "%6o", checksum());
 		writetar( (char *) &dblock);
+
 		if (chdir(shortname) < 0) {
 			fprint(2, "tar: can't cd to %s: %r\n", shortname);
 			snprint(curdir, sizeof(curdir), "cd %s", shortname);
@@ -342,7 +487,8 @@ putfile(char *dir, char *longname, char *sname)
 			for(i = 0; i < n; i++){
 				strncpy(cp, db[i].name, sizeof buf - (cp-buf));
 				putfile(curdir, buf, db[i].name);
-			}free(db);
+			}
+			free(db);
 		}
 		close(infile);
 		if (chdir(dir) < 0 && chdir("..") < 0) {
@@ -353,26 +499,22 @@ putfile(char *dir, char *longname, char *sname)
 		return;
 	}
 
-
+	/* plain file; write header block first */
 	tomodes(stbuf);
-
-	cp2 = longname;
-	for (cp = dblock.dbuf.name, i=0; (*cp++ = *cp2++) && i < NAMSIZ; i++);
-	if (i >= NAMSIZ) {
-		fprint(2, "%s: file name too long\n", longname);
+	if (putfullname(&dblock.dbuf, longname) < 0) {
 		close(infile);
-		return;
+		return;		/* putfullname already complained */
 	}
-
 	blocks = (stbuf->length + (TBLOCK-1)) / TBLOCK;
 	if (vflag) {
 		fprint(2, "a %s ", longname);
 		fprint(2, "%ld blocks\n", blocks);
 	}
-	dblock.dbuf.linkflag = 0;			/* Regular file */
+	dblock.dbuf.linkflag = LF_PLAIN1;
 	sprint(dblock.dbuf.chksum, "%6o", checksum());
 	writetar( (char *) &dblock);
 
+	/* then copy contents */
 	while ((i = readn(infile, buf, TBLOCK)) > 0 && blocks > 0) {
 		writetar(buf);
 		blocks--;
@@ -389,8 +531,9 @@ void
 doxtract(char **argv)
 {
 	Dir null;
+	int wrsize;
 	long blocks, bytes;
-	char buf[TBLOCK], outname[NAMSIZ+4];
+	char buf[TBLOCK], outname[Maxname+3+1];
 	char **cp;
 	int ofile;
 
@@ -403,29 +546,30 @@ doxtract(char **argv)
 			goto gotit;
 
 		for (cp = argv; *cp; cp++)
-			if (prefix(*cp, dblock.dbuf.name))
+			if (prefix(*cp, fullname))
 				goto gotit;
 		passtar();
 		continue;
 
 gotit:
-		if(checkdir(dblock.dbuf.name, stbuf->mode, &(stbuf->qid)))
+		if(checkdir(fullname, stbuf->mode, &stbuf->qid))
 			continue;
 
-		if (dblock.dbuf.linkflag == '1') {
+		if (dblock.dbuf.linkflag == LF_LINK) {
 			fprint(2, "tar: can't link %s %s\n",
-				dblock.dbuf.linkname, dblock.dbuf.name);
-			remove(dblock.dbuf.name);
+				dblock.dbuf.linkname, fullname);
+			remove(fullname);
 			continue;
 		}
-		if (dblock.dbuf.linkflag == 's') {
-			fprint(2, "tar: %s: cannot symlink\n", dblock.dbuf.name);
+		if (dblock.dbuf.linkflag == LF_SYMLINK1 ||
+		    dblock.dbuf.linkflag == LF_SYMLINK2) {
+			fprint(2, "tar: %s: cannot symlink\n", fullname);
 			continue;
 		}
-		if(dblock.dbuf.name[0] != '/' || Rflag)
-			sprint(outname, "./%s", dblock.dbuf.name);
+		if(fullname[0] != '/' || Rflag)
+			sprint(outname, "./%s", fullname);
 		else
-			strcpy(outname, dblock.dbuf.name);
+			strcpy(outname, fullname);
 		if ((ofile = create(outname, OWRITE, stbuf->mode & 0777)) < 0) {
 			fprint(2, "tar: %s - cannot create: %r\n", outname);
 			passtar();
@@ -434,20 +578,16 @@ gotit:
 
 		blocks = ((bytes = stbuf->length) + TBLOCK-1)/TBLOCK;
 		if (vflag)
-			fprint(2, "x %s, %ld bytes\n",
-				dblock.dbuf.name, bytes);
+			fprint(2, "x %s, %ld bytes\n", fullname, bytes);
 		while (blocks-- > 0) {
 			readtar(buf);
-			if (bytes > TBLOCK) {
-				if (write(ofile, buf, TBLOCK) < 0) {
-					fprint(2, "tar: %s: HELP - extract write error: %r\n", dblock.dbuf.name);
-					exits("extract write");
-				}
-			} else
-				if (write(ofile, buf, bytes) < 0) {
-					fprint(2, "tar: %s: HELP - extract write error: %r\n", dblock.dbuf.name);
-					exits("extract write");
-				}
+			wrsize = (bytes > TBLOCK? TBLOCK: bytes);
+			if (write(ofile, buf, wrsize) != wrsize) {
+				fprint(2,
+				    "tar: %s: HELP - extract write error: %r\n",
+					fullname);
+				exits("extract write");
+			}
 			bytes -= TBLOCK;
 		}
 		if(Tflag){
@@ -468,7 +608,7 @@ dotable(void)
 			break;
 		if (vflag)
 			longt(stbuf);
-		Bprint(&bout, "%s", dblock.dbuf.name);
+		Bprint(&bout, "%s", fullname);
 		if (dblock.dbuf.linkflag == '1')
 			Bprint(&bout, " linked to %s", dblock.dbuf.linkname);
 		if (dblock.dbuf.linkflag == 's')
@@ -539,15 +679,17 @@ checkdir(char *name, int mode, Qid *qid)
 void
 tomodes(Dir *sp)
 {
-	char *cp;
-
-	for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
-		*cp = '\0';
+	memset(dblock.dummy, 0, sizeof(dblock.dummy));
 	sprint(dblock.dbuf.mode, "%6lo ", sp->mode & 0777);
 	sprint(dblock.dbuf.uid, "%6o ", uflag);
 	sprint(dblock.dbuf.gid, "%6o ", gflag);
 	sprint(dblock.dbuf.size, "%11llo ", sp->length);
 	sprint(dblock.dbuf.mtime, "%11lo ", sp->mtime);
+	if (posix) {
+		setustar(&dblock.dbuf);
+		strncpy(dblock.dbuf.uname, sp->uid, sizeof dblock.dbuf.uname);
+		strncpy(dblock.dbuf.gname, sp->gid, sizeof dblock.dbuf.gname);
+	}
 }
 
 int

+ 32 - 21
sys/src/cmd/upas/common/libsys.c

@@ -884,6 +884,36 @@ remoteaddr(int fd, char *dir)
 	return "";
 }
 
+//  create a file and 
+//	1) ensure the modes we asked for
+//	2) make gid == uid
+static int
+docreate(char *file, int perm)
+{
+	int fd;
+	Dir ndir;
+	Dir *d;
+
+	//  create the mbox
+	fd = create(file, OREAD, perm);
+	if(fd < 0){
+		fprint(2, "couldn't create %s\n", file);
+		return -1;
+	}
+	d = dirfstat(fd);
+	if(d == nil){
+		fprint(2, "couldn't stat %s\n", file);
+		return -1;
+	}
+	nulldir(&ndir);
+	ndir.mode = perm;
+	ndir.gid = d->uid;
+	if(dirfwstat(fd, &ndir) < 0)
+		fprint(2, "couldn't chmod %s: %r\n", file);
+	close(fd);
+	return 0;
+}
+
 //  create a mailbox
 int
 creatembox(char *user, char *folder)
@@ -891,8 +921,6 @@ creatembox(char *user, char *folder)
 	char *p;
 	String *mailfile;
 	char buf[512];
-	int fd;
-	Dir *d;
 	Mlock *ml;
 
 	mailfile = s_new();
@@ -919,32 +947,15 @@ creatembox(char *user, char *folder)
 			break;
 		*p = 0;
 		if(access(s_to_c(mailfile), 0) != 0){
-			if((fd = create(s_to_c(mailfile), OREAD, DMDIR|0711)) < 0){
-				fprint(2, "couldn't create %s\n", s_to_c(mailfile));
+			if(docreate(s_to_c(mailfile), DMDIR|0711) < 0)
 				return -1;
-			}
-			close(fd);
 		}
 		*p = '/';
 	}
 
 	//  create the mbox
-	fd = create(s_to_c(mailfile), OREAD, 0622|DMAPPEND|DMEXCL);
-	if(fd < 0){
-		fprint(2, "couldn't create %s\n", s_to_c(mailfile));
-		return -1;
-	}
-	d = dirfstat(fd);
-	if(d == nil){
-		close(fd);
-		fprint(2, "couldn't chmod %s\n", s_to_c(mailfile));
+	if(docreate(s_to_c(mailfile), 0622|DMAPPEND|DMEXCL) < 0)
 		return -1;
-	}
-	d->mode = 0622|DMAPPEND|DMEXCL;
-	if(dirfwstat(fd, d) < 0)
-		fprint(2, "couldn't chmod %s\n", s_to_c(mailfile));
-	free(d);
-	close(fd);
 
 	/*
 	 *  create the lock file if it doesn't exist