summaryrefslogtreecommitdiff
path: root/appl/lib
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/lib')
-rw-r--r--appl/lib/NOTICE25
-rw-r--r--appl/lib/arg.b118
-rw-r--r--appl/lib/asn1.b1030
-rw-r--r--appl/lib/attrdb.b486
-rw-r--r--appl/lib/attrhash.b109
-rw-r--r--appl/lib/auth.b326
-rw-r--r--appl/lib/auth9.b351
-rw-r--r--appl/lib/bloomfilter.b72
-rw-r--r--appl/lib/bufio.b533
-rw-r--r--appl/lib/cfg.b234
-rw-r--r--appl/lib/cfgfile.b152
-rw-r--r--appl/lib/chanfill.b56
-rw-r--r--appl/lib/convcs/8bit_stob.b24
-rw-r--r--appl/lib/convcs/big5_btos.b87
-rw-r--r--appl/lib/convcs/big5_stob.b93
-rw-r--r--appl/lib/convcs/convcs.b165
-rw-r--r--appl/lib/convcs/cp932_btos.b179
-rw-r--r--appl/lib/convcs/cp_btos.b45
-rw-r--r--appl/lib/convcs/cp_stob.b49
-rw-r--r--appl/lib/convcs/euc-jp_btos.b162
-rw-r--r--appl/lib/convcs/gb2312_btos.b70
-rw-r--r--appl/lib/convcs/genbig5.b1790
-rw-r--r--appl/lib/convcs/gencp.b28
-rw-r--r--appl/lib/convcs/gencp932.b7814
-rw-r--r--appl/lib/convcs/gengb2312.b1143
-rw-r--r--appl/lib/convcs/genjisx0201kana.b117
-rw-r--r--appl/lib/convcs/genjisx0208-1997.b6958
-rw-r--r--appl/lib/convcs/genjisx0212.b6146
-rw-r--r--appl/lib/convcs/ibm437.b34
-rw-r--r--appl/lib/convcs/ibm850.b34
-rw-r--r--appl/lib/convcs/ibm866.b34
-rw-r--r--appl/lib/convcs/iso8859-1.b42
-rw-r--r--appl/lib/convcs/iso8859-10.b43
-rw-r--r--appl/lib/convcs/iso8859-2.b42
-rw-r--r--appl/lib/convcs/iso8859-3.b42
-rw-r--r--appl/lib/convcs/iso8859-4.b42
-rw-r--r--appl/lib/convcs/iso8859-5.b42
-rw-r--r--appl/lib/convcs/iso8859-6.b42
-rw-r--r--appl/lib/convcs/iso8859-7.b42
-rw-r--r--appl/lib/convcs/iso8859-8.b42
-rw-r--r--appl/lib/convcs/iso8859-9.b42
-rw-r--r--appl/lib/convcs/koi8-r.b43
-rwxr-xr-xappl/lib/convcs/mkdata39
-rw-r--r--appl/lib/convcs/mkfile27
-rw-r--r--appl/lib/convcs/utf8_btos.b35
-rw-r--r--appl/lib/convcs/utf8_stob.b17
-rw-r--r--appl/lib/convcs/windows-1250.b26
-rw-r--r--appl/lib/convcs/windows-1251.b34
-rw-r--r--appl/lib/convcs/windows-1252.b26
-rw-r--r--appl/lib/crc.b45
-rw-r--r--appl/lib/crypt/mkfile24
-rw-r--r--appl/lib/crypt/pkcs.b572
-rw-r--r--appl/lib/crypt/ssl3.b5557
-rw-r--r--appl/lib/crypt/sslsession.b176
-rw-r--r--appl/lib/crypt/x509.b4125
-rw-r--r--appl/lib/daytime.b478
-rw-r--r--appl/lib/db.b265
-rw-r--r--appl/lib/dbm.b463
-rw-r--r--appl/lib/dbsrv.b124
-rw-r--r--appl/lib/debug.b1493
-rw-r--r--appl/lib/deflate.b1367
-rw-r--r--appl/lib/devpointer.b123
-rw-r--r--appl/lib/dhcpclient.b1039
-rw-r--r--appl/lib/dialog.b190
-rw-r--r--appl/lib/dict.b57
-rw-r--r--appl/lib/dis.b609
-rw-r--r--appl/lib/diskblocks.b122
-rw-r--r--appl/lib/disks.b377
-rw-r--r--appl/lib/dividers.b242
-rw-r--r--appl/lib/ecmascript/builtin.b1480
-rw-r--r--appl/lib/ecmascript/date.b495
-rw-r--r--appl/lib/ecmascript/ecmascript.b2626
-rw-r--r--appl/lib/ecmascript/exec.b863
-rw-r--r--appl/lib/ecmascript/mkfile23
-rw-r--r--appl/lib/ecmascript/obj.b836
-rw-r--r--appl/lib/ecmascript/pprint.b378
-rw-r--r--appl/lib/ecmascript/regexp.b1286
-rw-r--r--appl/lib/ecmascript/uri.b140
-rw-r--r--appl/lib/encoding/base16.b43
-rw-r--r--appl/lib/encoding/base32.b60
-rw-r--r--appl/lib/encoding/base32a.b57
-rw-r--r--appl/lib/encoding/base64.b92
-rw-r--r--appl/lib/encoding/mkfile16
-rw-r--r--appl/lib/env.b91
-rw-r--r--appl/lib/ether.b83
-rw-r--r--appl/lib/exception.b59
-rw-r--r--appl/lib/factotum.b308
-rw-r--r--appl/lib/filepat.b169
-rw-r--r--appl/lib/format.b147
-rw-r--r--appl/lib/fsfilter.b67
-rw-r--r--appl/lib/fslib.b400
-rw-r--r--appl/lib/fsproto.b385
-rw-r--r--appl/lib/gamer.b147
-rw-r--r--appl/lib/hash.b80
-rw-r--r--appl/lib/html.b664
-rw-r--r--appl/lib/ida/NOTICE33
-rw-r--r--appl/lib/ida/ida.b228
-rw-r--r--appl/lib/ida/idatab.b65549
-rw-r--r--appl/lib/ida/idatest.b84
-rw-r--r--appl/lib/ida/mkfile18
-rw-r--r--appl/lib/ida/mktab.b32
-rw-r--r--appl/lib/imageremap.b738
-rw-r--r--appl/lib/inflate.b745
-rw-r--r--appl/lib/ip.b656
-rw-r--r--appl/lib/ipattr.b217
-rw-r--r--appl/lib/ir.b95
-rw-r--r--appl/lib/irmpath.b118
-rw-r--r--appl/lib/irsage.b99
-rw-r--r--appl/lib/irsim.b83
-rw-r--r--appl/lib/itslib.b45
-rw-r--r--appl/lib/keyset.b89
-rw-r--r--appl/lib/libc.b141
-rw-r--r--appl/lib/libc0.b241
-rw-r--r--appl/lib/lock.b26
-rw-r--r--appl/lib/login.b175
-rw-r--r--appl/lib/memfs.b46
-rw-r--r--appl/lib/mkfile223
-rw-r--r--appl/lib/mpeg.b148
-rw-r--r--appl/lib/names.b152
-rw-r--r--appl/lib/nametree.b278
-rw-r--r--appl/lib/newns.b434
-rw-r--r--appl/lib/palm.b504
-rw-r--r--appl/lib/palmdb.b576
-rw-r--r--appl/lib/palmfile.b703
-rw-r--r--appl/lib/parseman.b805
-rw-r--r--appl/lib/plumbing.b254
-rw-r--r--appl/lib/plumbing.m23
-rw-r--r--appl/lib/plumbmsg.b190
-rw-r--r--appl/lib/pop3.b298
-rw-r--r--appl/lib/popup.b124
-rw-r--r--appl/lib/powerman.b59
-rw-r--r--appl/lib/print/hp_driver.b1536
-rw-r--r--appl/lib/print/mkfile26
-rw-r--r--appl/lib/print/print.b625
-rw-r--r--appl/lib/print/scaler.b186
-rw-r--r--appl/lib/print/scaler.m30
-rw-r--r--appl/lib/profile.b1230
-rw-r--r--appl/lib/pslib.b714
-rw-r--r--appl/lib/quicktime.b205
-rw-r--r--appl/lib/rand.b29
-rw-r--r--appl/lib/random.b50
-rw-r--r--appl/lib/readdir.b123
-rw-r--r--appl/lib/readgif.b442
-rw-r--r--appl/lib/readjpg.b973
-rw-r--r--appl/lib/readpicfile.b164
-rw-r--r--appl/lib/readpng.b823
-rw-r--r--appl/lib/readxbitmap.b131
-rw-r--r--appl/lib/regex.b389
-rw-r--r--appl/lib/regexutils.b65
-rw-r--r--appl/lib/registries.b288
-rw-r--r--appl/lib/riff.b225
-rw-r--r--appl/lib/scoretable.b150
-rw-r--r--appl/lib/scsiio.b317
-rw-r--r--appl/lib/secstore.b474
-rw-r--r--appl/lib/selectfile.b624
-rw-r--r--appl/lib/sets.b329
-rw-r--r--appl/lib/sets32.b226
-rw-r--r--appl/lib/sexprs.b638
-rw-r--r--appl/lib/slip.b113
-rw-r--r--appl/lib/smtp.b241
-rw-r--r--appl/lib/sort.b36
-rw-r--r--appl/lib/spki/mkfile21
-rw-r--r--appl/lib/spki/spki.b2109
-rw-r--r--appl/lib/spki/verifier.b188
-rw-r--r--appl/lib/ssl.b90
-rw-r--r--appl/lib/string.b358
-rw-r--r--appl/lib/strinttab.b28
-rw-r--r--appl/lib/strokes/buildstrokes.b260
-rw-r--r--appl/lib/strokes/mkfile18
-rw-r--r--appl/lib/strokes/readstrokes.b205
-rw-r--r--appl/lib/strokes/strokes.b793
-rw-r--r--appl/lib/strokes/writestrokes.b68
-rw-r--r--appl/lib/styx.b934
-rw-r--r--appl/lib/styxconv/mkfile20
-rw-r--r--appl/lib/styxconv/ostyx.b773
-rw-r--r--appl/lib/styxconv/ostyx.m148
-rw-r--r--appl/lib/styxconv/osys.m34
-rw-r--r--appl/lib/styxconv/styxconv.b447
-rw-r--r--appl/lib/styxlib.b453
-rw-r--r--appl/lib/styxpersist.b898
-rw-r--r--appl/lib/styxservers.b605
-rw-r--r--appl/lib/tables.b105
-rw-r--r--appl/lib/tabs.b191
-rw-r--r--appl/lib/tcl.m19
-rw-r--r--appl/lib/tcl_calc.b909
-rw-r--r--appl/lib/tcl_core.b1397
-rw-r--r--appl/lib/tcl_inthash.b95
-rw-r--r--appl/lib/tcl_io.b350
-rw-r--r--appl/lib/tcl_list.b335
-rw-r--r--appl/lib/tcl_modhash.b114
-rw-r--r--appl/lib/tcl_stack.b142
-rw-r--r--appl/lib/tcl_strhash.b116
-rw-r--r--appl/lib/tcl_string.b246
-rw-r--r--appl/lib/tcl_symhash.b99
-rw-r--r--appl/lib/tcl_tk.b223
-rw-r--r--appl/lib/tcl_utils.b61
-rw-r--r--appl/lib/tftp.b170
-rw-r--r--appl/lib/timers.b99
-rw-r--r--appl/lib/titlebar.b111
-rw-r--r--appl/lib/tkclient.b249
-rw-r--r--appl/lib/translate.b248
-rw-r--r--appl/lib/ubfa.b623
-rw-r--r--appl/lib/url.b224
-rw-r--r--appl/lib/usb/mkfile16
-rw-r--r--appl/lib/usb/usb.b453
-rw-r--r--appl/lib/usb/usbmass.b465
-rw-r--r--appl/lib/usb/usbmct.b204
-rw-r--r--appl/lib/usb/usbmouse.b60
-rw-r--r--appl/lib/utils.m112
-rw-r--r--appl/lib/venti.b660
-rw-r--r--appl/lib/virgil.b177
-rw-r--r--appl/lib/volume.b172
-rw-r--r--appl/lib/w3c/css.b1019
-rw-r--r--appl/lib/w3c/mkfile17
-rw-r--r--appl/lib/w3c/xpointers.b858
-rw-r--r--appl/lib/wait.b55
-rw-r--r--appl/lib/watchvars.b44
-rw-r--r--appl/lib/winplace.b359
-rw-r--r--appl/lib/wmclient.b346
-rw-r--r--appl/lib/wmlib.b590
-rw-r--r--appl/lib/wmsrv.b610
-rw-r--r--appl/lib/workdir.b14
-rw-r--r--appl/lib/writegif.b362
-rw-r--r--appl/lib/xml.b712
224 files changed, 165087 insertions, 0 deletions
diff --git a/appl/lib/NOTICE b/appl/lib/NOTICE
new file mode 100644
index 00000000..e8c19e7f
--- /dev/null
+++ b/appl/lib/NOTICE
@@ -0,0 +1,25 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory. If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below. It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+Copyright © 1995-1999 Lucent Technologies Inc.
+Portions Copyright © 1997-2000 Vita Nuova Limited
+Portions Copyright © 2000-2006 Vita Nuova Holdings Limited
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License (`LGPL') as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
diff --git a/appl/lib/arg.b b/appl/lib/arg.b
new file mode 100644
index 00000000..7b45b128
--- /dev/null
+++ b/appl/lib/arg.b
@@ -0,0 +1,118 @@
+implement Arg;
+
+#
+# Copyright © 1997 Roger Peppe
+#
+
+include "sys.m";
+include "arg.m";
+
+name:= "";
+args: list of string;
+usagemsg:="";
+printusage := 1;
+
+curropt: string;
+
+init(argv: list of string)
+{
+ (curropt, args, name) = (nil, nil, nil);
+ if (argv == nil)
+ return;
+ name = hd argv;
+ args = tl argv;
+}
+
+setusage(u: string)
+{
+ usagemsg = u;
+ printusage = u != nil;
+}
+
+progname(): string
+{
+ return name;
+}
+
+# don't allow any more options after this function is invoked
+argv(): list of string
+{
+ ret := args;
+ args = nil;
+ return ret;
+}
+
+earg(): string
+{
+ if (curropt != nil) {
+ ret := curropt;
+ curropt = nil;
+ return ret;
+ }
+
+ if (args == nil)
+ usage();
+
+ ret := hd args;
+ args = tl args;
+ return ret;
+}
+
+# get next option argument
+arg(): string
+{
+ if (curropt != nil) {
+ ret := curropt;
+ curropt = nil;
+ return ret;
+ }
+
+ if (args == nil)
+ return nil;
+
+ ret := hd args;
+ args = tl args;
+ return ret;
+}
+
+# get next option letter
+# return 0 at end of options
+opt(): int
+{
+ if (curropt != nil) {
+ opt := curropt[0];
+ curropt = curropt[1:];
+ return opt;
+ }
+
+ if (args == nil)
+ return 0;
+
+ nextarg := hd args;
+ if (len nextarg < 2 || nextarg[0] != '-')
+ return 0;
+
+ if (nextarg == "--") {
+ args = tl args;
+ return 0;
+ }
+
+ opt := nextarg[1];
+ if (len nextarg > 2)
+ curropt = nextarg[2:];
+ args = tl args;
+ return opt;
+}
+
+usage()
+{
+ if(printusage){
+ if(usagemsg != nil)
+ u := "usage: "+usagemsg;
+ else
+ u = name + ": argument expected";
+ sys := load Sys Sys->PATH;
+ sys->fprint(sys->fildes(2), "%s\n", u);
+ }
+ raise "fail:usage";
+}
diff --git a/appl/lib/asn1.b b/appl/lib/asn1.b
new file mode 100644
index 00000000..0ccb7d26
--- /dev/null
+++ b/appl/lib/asn1.b
@@ -0,0 +1,1030 @@
+implement ASN1;
+
+include "sys.m";
+ sys: Sys;
+
+include "asn1.m";
+
+# Masks
+TAG_MASK : con 16r1F;
+CONSTR_MASK : con 16r20;
+CLASS_MASK : con 16rC0;
+
+# Decoding errors
+OK, ESHORT, ETOOBIG, EVALLEN, ECONSTR, EPRIM, EINVAL, EUNIMPL: con iota;
+
+debug : con 0;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+# Decode the whole array as a BER encoding of an ASN1 type.
+# If there's an error, the return string will contain the error.
+# Depending on the error, the returned elem may or may not
+# be nil.
+decode(a: array of byte) : (string, ref Elem)
+{
+ (ecode, i, elem) := ber_decode(a, 0, len a);
+ return (errstr(ecode, i, len a), elem);
+}
+
+# Like decode, but continue decoding after first element
+# of array ends.
+decode_seq(a: array of byte) : (string, list of ref Elem)
+{
+ (ecode, i, elist) := seq_decode(a, 0, len a, -1, 1);
+ return (errstr(ecode, i, len a), elist);
+}
+
+# Decode the whole array as a BER encoding of an ASN1 value,
+# (i.e., the part after the tag and length).
+# Assume the value is encoded as universal tag "kind".
+# The constr arg is 1 if the value is constructed, 0 if primitive.
+# If there's an error, the return string will contain the error.
+# Depending on the error, the returned value may or may not
+# be nil.
+decode_value(a: array of byte, kind, constr: int) : (string, ref Value)
+{
+ n := len a;
+ (ecode, i, val) := value_decode(a, 0, n, n, kind, constr);
+ return (errstr(ecode, i, len a), val);
+}
+
+# The rest of the decoding routines take the array (a), the
+# starting position (i), and the ending position +1 (n).
+# They return (err code, new i, [... varies]).
+
+# for debugging
+ber_ind := "";
+ber_ind_save := "";
+
+# Decode an ASN1 (tag, length, value).
+ber_decode(a: array of byte, i, n: int) : (int, int, ref Elem)
+{
+ if(debug) {
+ ber_ind_save = ber_ind;
+ ber_ind = ber_ind + " ";
+ sys->print("%sber_decode, byte %d\n", ber_ind, i);
+ }
+ err, length: int;
+ tag : Tag;
+ val : ref Value;
+ elem : ref Elem = nil;
+ (err, i, tag) = tag_decode(a, i, n);
+ if(err == OK) {
+ (err, i, length) = length_decode(a, i, n);
+ if(err == OK) {
+ if(debug)
+ sys->print("%sgot tag %s, length %d, now at byte %d\n",
+ ber_ind, tag.tostring(), length, i);
+ if(tag.class == Universal)
+ (err, i, val) = value_decode(a, i, n, length, tag.num, tag.constr);
+ else
+ (err, i, val) = value_decode(a, i, n, length, OCTET_STRING, 0);
+ if(val != nil)
+ elem = ref Elem(tag, val);
+ }
+ }
+ if(debug) {
+ sys->print("%send ber_decode, byte %d\n", ber_ind, i);
+ if(val != nil) {
+ sys->print("%sdecode result:\n", ber_ind);
+ print_elem(elem);
+ }
+ if(err != OK)
+ sys->print("%serror: %s\n", ber_ind, errstr(err, i, i));
+ ber_ind = ber_ind_save;
+ }
+ return (err, i, elem);
+}
+
+# Decode a tag field. As well as Tag, return an int that
+# is 1 if the type is constructed, 0 if not.
+tag_decode(a: array of byte, i, n: int) : (int, int, Tag)
+{
+ err := OK;
+ class, num, constr: int;
+ if(n-i >= 2) {
+ v := int a[i++];
+ class = v & CLASS_MASK;
+ if(v & CONSTR_MASK)
+ constr = 1;
+ else
+ constr = 0;
+ num = v & TAG_MASK;
+ if(num == TAG_MASK)
+ # long tag number
+ (err, i, num) = uint7_decode(a, i, n);
+ }
+ else
+ err = ESHORT;
+ return (err, i, Tag(class, num, constr));
+}
+
+# Decode a length field. Assume it fits in a Limbo int.
+# If "indefinite length", return -1.
+length_decode(a: array of byte, i, n: int) : (int, int, int)
+{
+ err := OK;
+ num := 0;
+ if(i < n) {
+ v := int a[i++];
+ if(v & 16r80)
+ return int_decode(a, i, n, v&16r7F, 1);
+ else if(v == 16r80)
+ num = -1;
+ else
+ num = v;
+ }
+ else
+ err = ESHORT;
+ return (err, i, num);
+}
+
+# Decode a value according to the encoding of the Universal
+# type with number "kind" and constructed/primitive according
+# to "constr", with given length (may be -1, for "indefinite").
+value_decode(a: array of byte, i, n, length, kind, constr: int) : (int, int, ref Value)
+{
+ err := OK;
+ val : ref Value;
+ va : array of byte;
+ if(length == -1) {
+ if(!constr)
+ err = EINVAL;
+ }
+ else if(i+length > n)
+ err = EVALLEN;
+ if(err != OK)
+ return (err, i, nil);
+ case kind {
+ 0 =>
+ # marker for end of indefinite constructions
+ if(length == 0)
+ val = ref Value.EOC;
+ else
+ err = EINVAL;
+ BOOLEAN =>
+ if(constr)
+ err = ECONSTR;
+ else if(length != 1)
+ err = EVALLEN;
+ else {
+ val = ref Value.Bool(int a[0]);
+ i++;
+ }
+ INTEGER or ENUMERATED =>
+ if(constr)
+ err = ECONSTR;
+ else if(length <= 4) {
+ num : int;
+ (err, i, num) = int_decode(a, i, i+length, length, 0);
+ if(err == OK)
+ val = ref Value.Int(num);
+ }
+ else {
+ va = array[length] of byte;
+ va[0:] = a[i:i+length];
+ val = ref Value.BigInt(va);
+ i += length;
+ }
+ BIT_STRING =>
+ if(constr) {
+ if(length == -1 && i+2 <= n && a[i] == byte 0 && a[i+1] == byte 0) {
+ val = ref Value.BitString(0, nil);
+ i += 2;
+ }
+ else
+ # TODO: recurse and concat results
+ err = EUNIMPL;
+ }
+ else {
+ if(length < 2) {
+ if(length == 1 && a[0] == byte 0) {
+ val = ref Value.BitString(0, nil);
+ i ++;
+ }
+ else
+ err = EINVAL;
+ }
+ else {
+ bitsunused := int a[i];
+ if(bitsunused > 7)
+ err = EINVAL;
+ else if(length > 16r0FFFFFFF)
+ err = ETOOBIG;
+ else {
+ va = array[length-1] of byte;
+ va[0:] = a[i+1:i+length];
+ val = ref Value.BitString(bitsunused, va);
+ i += length;
+ }
+ }
+ }
+ OCTET_STRING or ObjectDescriptor =>
+ (err, i, va) = octet_decode(a, i, n, length, constr);
+ if(err == OK)
+ val = ref Value.Octets(va);
+ NULL =>
+ if(constr)
+ err = ECONSTR;
+ else if(length != 0)
+ err = EVALLEN;
+ else
+ val = ref Value.Null;
+ OBJECT_ID =>
+ if(constr)
+ err = ECONSTR;
+ else if (length == 0)
+ err = EVALLEN;
+ else {
+ subids : list of int = nil;
+ iend := i+length;
+ while(i < iend) {
+ x : int;
+ (err, i, x) = uint7_decode(a, i, n);
+ if(err != OK)
+ break;
+ subids = x :: subids;
+ }
+ if(err == OK) {
+ if(i != iend)
+ err = EVALLEN;
+ else {
+ m := len subids;
+ ia := array[m+1] of int;
+ while(subids != nil) {
+ y := hd subids;
+ subids = tl subids;
+ if(m == 1) {
+ ia[1] = y % 40;
+ ia[0] = y / 40;
+ }
+ else
+ ia[m--] = y;
+ }
+ val = ref Value.ObjId(ref Oid(ia));
+ }
+ }
+ }
+ EXTERNAL or EMBEDDED_PDV =>
+ # TODO: parse this internally
+ va = array[length] of byte;
+ va[0:] = a[i:i+length];
+ val = ref Value.Other(va);
+ i += length;
+ REAL =>
+ # let the appl decode, with math module
+ if(constr)
+ err = ECONSTR;
+ else {
+ va = array[length] of byte;
+ va[0:] = a[i:i+length];
+ val = ref Value.Real(va);
+ i += length;
+ }
+ SEQUENCE or SET=>
+ vl : list of ref Elem;
+ (err, i, vl) = seq_decode(a, i, n, length, constr);
+ if(err == OK) {
+ if(kind == SEQUENCE)
+ val = ref Value.Seq(vl);
+ else
+ val = ref Value.Set(vl);
+ }
+ NumericString or PrintableString or TeletexString
+ or VideotexString or IA5String or UTCTime
+ or GeneralizedTime or GraphicString or VisibleString
+ or GeneralString or UniversalString or BMPString =>
+ (err, i, va) = octet_decode(a, i, n, length, constr);
+ if(err == OK)
+ # sometimes wrong: need to do char set conversion
+ val = ref Value.String(string va);
+
+ * =>
+ va = array[length] of byte;
+ va[0:] = a[i:i+length];
+ val = ref Value.Other(va);
+ i += length;
+ }
+ return (err, i, val);
+}
+
+# Decode an int in format where count bytes are
+# concatenated to form value.
+# Although ASN1 allows any size integer, we return
+# an error if the result doesn't fit in a Limbo int.
+# If unsigned is not set, make sure to propagate sign bit.
+int_decode(a: array of byte, i, n, count, unsigned: int) : (int, int, int)
+{
+ err := OK;
+ num := 0;
+ if(n-i >= count) {
+ if((count > 4) || (unsigned && count == 4 && (int a[i] & 16r80)))
+ err = ETOOBIG;
+ else {
+ if(!unsigned && count > 0 && count < 4 && (int a[i] & 16r80))
+ num = -1; # all bits set
+ for(j := 0; j < count; j++) {
+ v := int a[i++];
+ num = (num << 8) | v;
+ }
+ }
+ }
+ else
+ err = ESHORT;
+ return (err, i, num);
+}
+
+# Decode an unsigned int in format where each
+# byte except last has high bit set, and remaining
+# seven bits of each byte are concatenated to form value.
+# Although ASN1 allows any size integer, we return
+# an error if the result doesn't fit in a Limbo int.
+uint7_decode(a: array of byte, i, n: int) : (int, int, int)
+{
+ err := OK;
+ num := 0;
+ more := 1;
+ while(more && i < n) {
+ v := int a[i++];
+ if(num & 16r7F000000) {
+ err = ETOOBIG;
+ break;
+ }
+ num <<= 7;
+ more = v & 16r80;
+ num |= (v & 16r7F);
+ }
+ if(n == i)
+ err = ESHORT;
+ return (err, i, num);
+}
+
+# Decode an octet string, recursively if constr.
+# We've already checked that length==-1 implies constr==1,
+# and otherwise that specified length fits within a[i..n].
+octet_decode(a: array of byte, i, n, length, constr: int) : (int, int, array of byte)
+{
+ err := OK;
+ va : array of byte;
+ if(length >= 0 && !constr) {
+ va = array[length] of byte;
+ va[0:] = a[i:i+length];
+ i += length;
+ }
+ else {
+ # constructed, either definite or indefinite length
+ lva : list of array of byte = nil;
+ elem : ref Elem;
+ istart := i;
+ totbytes := 0;
+ cloop:
+ for(;;) {
+ if(length >= 0 && i >= istart+length) {
+ if(i != istart+length)
+ err = EVALLEN;
+ break cloop;
+ }
+ oldi := i;
+ (err, i, elem) = ber_decode(a, i, n);
+ if(err != OK)
+ break;
+ pick v := elem.val {
+ Octets =>
+ lva = v.bytes :: lva;
+ totbytes += len v.bytes;
+ EOC =>
+ if(length != -1) {
+ i = oldi;
+ err = EINVAL;
+ }
+ break cloop;
+ * =>
+ i = oldi;
+ err = EINVAL;
+ break cloop;
+ }
+ }
+ if(err == OK) {
+ va = array[totbytes] of byte;
+ j := totbytes;
+ while(lva != nil) {
+ x := hd lva;
+ lva = tl lva;
+ m := len x;
+ va[j-m:] = x[0:];
+ j -= m;
+ }
+ }
+ }
+ return (err, i, va);
+}
+
+# Decode a sequence or set.
+# We've already checked that length==-1 implies constr==1,
+# and otherwise that specified length fits within a[i..n].
+seq_decode(a : array of byte, i, n, length, constr: int) : (int, int, list of ref Elem)
+{
+ err := OK;
+ ans : list of ref Elem = nil;
+ if(!constr)
+ err = EPRIM;
+ else {
+ # constructed, either definite or indefinite length
+ lve : list of ref Elem = nil;
+ elem : ref Elem;
+ istart := i;
+ cloop:
+ for(;;) {
+ if(length >= 0 && i >= istart+length) {
+ if(i != istart+length)
+ err = EVALLEN;
+ break cloop;
+ }
+ oldi := i;
+ (err, i, elem) = ber_decode(a, i, n);
+ if(err != OK)
+ break;
+ pick v := elem.val {
+ EOC =>
+ if(length != -1) {
+ i = oldi;
+ err = EINVAL;
+ }
+ break cloop;
+ * =>
+ lve = elem :: lve;
+ }
+ }
+ if(err == OK) {
+ # reverse back to original order
+ while(lve != nil) {
+ e := hd lve;
+ lve = tl lve;
+ ans = e :: ans;
+ }
+ }
+ }
+ return (err, i, ans);
+}
+
+# Encode e by BER rules
+encode(e: ref Elem) : (string, array of byte)
+{
+ (err, n) := enc(nil, e, 0, 1);
+ if(err != "")
+ return (err, nil);
+ b := array[n] of byte;
+ enc(b, e, 0, 0);
+ return ("", b);
+}
+
+# Encode e into array b, only putting in bytes if !lenonly.
+# Start at loc i, return index after.
+enc(b: array of byte, e: ref Elem, i, lenonly: int) : (string, int)
+{
+ (err, vlen, constr) := val_enc(b, e, 0, 1);
+ if(err != "")
+ return (err, i);
+ tag := e.tag;
+ v := tag.class | constr;
+ if(tag.num < 31) {
+ if(!lenonly)
+ b[i] = byte (v | tag.num);
+ i++;
+ }
+ else {
+ if(!lenonly)
+ b[i] = byte (v | 31);
+ if(tag.num < 0)
+ return ("negative tag number", i);
+ i = uint7_enc(b, tag.num, i+1, lenonly);
+ }
+ if(vlen < 16r80) {
+ if(!lenonly)
+ b[i] = byte vlen;
+ i++;
+ }
+ else {
+ ilen := int_enc(b, vlen, 1, 0, 1);
+ if(!lenonly) {
+ b[i] = byte (16r80 | ilen);
+ i = int_enc(b, vlen, 1, i+1, 0);
+ }
+ else
+ i += 1+ilen;
+ }
+ if(!lenonly)
+ val_enc(b, e, i, 0);
+ i += vlen;
+ return ("", i);
+}
+
+# Encode e.val into array b, only putting in bytes if !lenonly.
+# Start at loc i, return (err, index after, constructed or primitive)
+val_enc(b: array of byte, e: ref Elem, i, lenonly: int) : (string, int, int)
+{
+ kind := e.tag.num;
+ cl := e.tag.class;
+ ok := 1;
+ v : int;
+ bb : array of byte;
+ constr := 0;
+ if(cl != Universal) {
+ pick vv := e.val {
+ Bool =>
+ kind = BOOLEAN;
+ Int =>
+ kind = INTEGER;
+ BigInt =>
+ kind = INTEGER;
+ Octets =>
+ kind = OCTET_STRING;
+ Real =>
+ kind = REAL;
+ Other =>
+ kind = OCTET_STRING;
+ BitString =>
+ kind = BIT_STRING;
+ Null =>
+ kind = NULL;
+ ObjId =>
+ kind = OBJECT_ID;
+ String =>
+ kind = UniversalString;
+ Seq =>
+ kind = SEQUENCE;
+ Set =>
+ kind = SET;
+ }
+ }
+ case kind {
+ BOOLEAN =>
+ (ok, v) = e.is_int();
+ if(ok) {
+ if(v != 0)
+ v = 255;
+ i = int_enc(b, v, 1, i, lenonly);
+ }
+ INTEGER or ENUMERATED =>
+ (ok, v) = e.is_int();
+ if(ok)
+ i = int_enc(b, v, 0, i, lenonly);
+ else {
+ (ok, bb) = e.is_bigint();
+ if(ok) {
+ if(!lenonly)
+ b[i:] = bb;
+ i += len bb;
+ }
+ }
+ BIT_STRING =>
+ (ok, v, bb) = e.is_bitstring();
+ if(ok) {
+ if(bb == nil) {
+ if(!lenonly)
+ b[i] = byte 0;
+ i++;
+ }
+ else {
+ if(v < 0 || v > 7)
+ ok = 0;
+ else {
+ if(!lenonly) {
+ b[i] = byte v;
+ b[i+1:] = bb;
+ }
+ i += 1 + len bb;
+ }
+ }
+ }
+ OCTET_STRING or ObjectDescriptor or EXTERNAL or REAL
+ or EMBEDDED_PDV =>
+ pick vv := e.val {
+ Octets or Real or Other =>
+ if(!lenonly && vv.bytes != nil)
+ b[i:] = vv.bytes;
+ i += len vv.bytes;
+ * =>
+ ok = 0;
+ }
+ NULL =>
+ ;
+ OBJECT_ID =>
+ oid : ref Oid;
+ (ok, oid) = e.is_oid();
+ if(ok) {
+ n := len oid.nums;
+ for(k := 0; k < n; k++) {
+ v = oid.nums[k];
+ if(k == 0) {
+ v *= 40;
+ if(n > 1)
+ v += oid.nums[++k];
+ }
+ i = uint7_enc(b, v, i, lenonly);
+ }
+ }
+ SEQUENCE or SET =>
+ pick vv := e.val {
+ Seq or Set =>
+ constr = CONSTR_MASK;
+ for(l := vv.l; l != nil; l = tl l) {
+ err : string;
+ (err, i) = enc(b, hd l, i, lenonly);
+ if(err != "")
+ return (err, i, 0);
+ }
+ }
+ NumericString or PrintableString or TeletexString
+ or VideotexString or IA5String or UTCTime
+ or GeneralizedTime or GraphicString or VisibleString
+ or GeneralString or UniversalString or BMPString =>
+ pick vv := e.val {
+ String =>
+ bb = array of byte vv.s;
+ if(!lenonly && bb != nil)
+ b[i:] = bb;
+ i += len bb;
+ * =>
+ ok = 0;
+ }
+ * =>
+ ok = 0;
+ }
+ if(!ok)
+ return ("bad value for encoding kind", i, constr);
+ return ("", i, constr);
+}
+
+# Encode num as unsigned 7 bit values with top bit 1 on all bytes
+# except last, into array b, only putting in bytes if !lenonly.
+# Start at loc i, return index after.
+uint7_enc(b: array of byte, num, i, lenonly: int) : int
+{
+ n := 1;
+ v := num>>7;
+ while(v > 0) {
+ v >>= 7;
+ n++;
+ }
+ if(lenonly)
+ i += n;
+ else {
+ for(k := (n-1)*7; k > 0; k -= 7)
+ b[i++] = byte ((num>>k) | 16r80);
+ b[i++] = byte (num & 16r7F);
+ }
+ return i;
+}
+
+# Encode num as unsigned or signed integer into array b,
+# only putting in bytes if !lenonly.
+# Encoding is length followed by bytes to concatenate.
+# Start at loc i, return index after.
+int_enc(b: array of byte, num, unsigned, i, lenonly: int) : int
+{
+ v := num;
+ if(v < 0)
+ v = -(v+1);
+ n := 1;
+ prevv := v;
+ v >>= 8;
+ while(v > 0) {
+ prevv = v;
+ v >>= 8;
+ n++;
+ }
+ if(!unsigned && (prevv & 16r80))
+ n++;
+ if(lenonly)
+ i += n;
+ else {
+ for(k := (n-1)*8; k >= 0; k -= 8)
+ b[i++] = byte (num>>k);
+ }
+ return i;
+}
+
+# Compare two arrays of integers; return true if they match
+intarr_eq(a: array of int, b: array of int) : int
+{
+ alen := len a;
+ if(alen != len b)
+ return 0;
+ for(i := 0; i < alen; i++)
+ if(a[i] != b[i])
+ return 0;
+ return 1;
+}
+
+# Look for o in tab; if found, return index, else return -1.
+oid_lookup(o: ref Oid, tab: array of Oid) : int
+{
+ for(i := 0; i < len tab; i++)
+ if(intarr_eq(o.nums, tab[i].nums))
+ return i;
+ return -1;
+}
+
+# If e is a SEQUENCE, return (1, e's element list)
+# else return (error, nil).
+Elem.is_seq(e: self ref Elem) : (int, list of ref Elem)
+{
+ if(e.tag.class == Universal && e.tag.num == SEQUENCE) {
+ pick v := e.val {
+ Seq =>
+ return (1, v.l);
+ }
+ }
+ return (0, nil);
+}
+
+# If e is a SET, return (1, e's element list)
+# else return (error, nil).
+Elem.is_set(e: self ref Elem) : (int, list of ref Elem)
+{
+ if(e.tag.class == Universal && e.tag.num == SET) {
+ pick v := e.val {
+ Set =>
+ return (1, v.l);
+ }
+ }
+ return (0, nil);
+}
+
+# If e is an INTEGER that fits in a limbo int, return (1, val)
+# else return (0, 0l).
+Elem.is_int(e: self ref Elem) : (int, int)
+{
+ if(e.tag.class == Universal && (e.tag.num == INTEGER || e.tag.num == BOOLEAN)) {
+ pick v := e.val {
+ Bool or
+ Int =>
+ return (1, v.v);
+ }
+ }
+ return (0, 0);
+}
+
+# If e is an INTEGER that doesn't fit in a limbo int, return (1, bytes),
+# or even if it does fit, return it as an array of bytes.
+# else return (0, nil).
+Elem.is_bigint(e: self ref Elem) : (int, array of byte)
+{
+ if(e.tag.class == Universal && e.tag.num == INTEGER) {
+ pick v := e.val {
+ BigInt =>
+ return (1, v.bytes);
+ Int =>
+ x := v.v;
+ a := array[4] of byte;
+ for(i := 0; i < 4; i++)
+ a[i] = byte ((x >> (8*(3-i))) & 16rFF);
+ for(j := 0; j < 3; j++)
+ if(a[j] != byte 0)
+ break;
+ return (1, a[j:]);
+ }
+ }
+ return (0, nil);
+}
+
+# If e is a bitstring, return (1, unused bits, bytes containing bit string),
+# else return (0, nil)
+Elem.is_bitstring(e: self ref Elem) : (int, int, array of byte)
+{
+ if(e.tag.class == Universal && e.tag.num == BIT_STRING) {
+ pick v := e.val {
+ BitString =>
+ return (1, v.unusedbits, v.bits);
+ }
+ }
+ return (0, 0, nil);
+}
+
+# If e is an octetstring, return (1, bytes),
+# else return (0, nil)
+Elem.is_octetstring(e: self ref Elem) : (int, array of byte)
+{
+ if(e.tag.class == Universal && e.tag.num == OCTET_STRING) {
+ pick v := e.val {
+ Octets =>
+ return (1, v.bytes);
+ }
+ }
+ return (0, nil);
+}
+
+# If e is an object id, return (1, ref Oid),
+# else return (0, nil)
+Elem.is_oid(e: self ref Elem) : (int, ref Oid)
+{
+ if(e.tag.class == Universal && e.tag.num == OBJECT_ID) {
+ pick v := e.val {
+ ObjId =>
+ return (1, v.id);
+ }
+ }
+ return (0, nil);
+}
+
+# If e is some kind of string (excluding times), return (1, string),
+# else return (0, "")
+Elem.is_string(e: self ref Elem) : (int, string)
+{
+ if(e.tag.class == Universal) {
+ case e.tag.num {
+ NumericString or PrintableString or TeletexString
+ or VideotexString or IA5String or GraphicString
+ or VisibleString or GeneralString or UniversalString
+ or BMPString =>
+ pick v := e.val {
+ String =>
+ return (1, v.s);
+ }
+ }
+ }
+ return (0, nil);
+}
+
+# If e is some kind of time, return (1, string),
+# else return (0, "")
+Elem.is_time(e: self ref Elem) : (int, string)
+{
+ if(e.tag.class == Universal
+ && (e.tag.num == UTCTime || e.tag.num == GeneralizedTime)) {
+ pick v := e.val {
+ String =>
+ return (1, v.s);
+ }
+ }
+ return (0, nil);
+}
+
+# Return printable error string for code ecode.
+# i is position where error is first noted.
+# n is the end of the passed data: if i!=n then
+# we didn't use all the data and an error should
+# be returned about that.
+errstr(ecode, i, n: int) : string
+{
+ if(ecode == OK && i == n)
+ return "";
+ err := "BER decode: ";
+ case ecode {
+ OK =>
+ err += "OK";
+ ESHORT =>
+ err += "need more data";
+ ETOOBIG =>
+ err += "value exceeds implementation limit";
+ EVALLEN =>
+ err += "value has wrong length";
+ ECONSTR =>
+ err += "value is constructed, should be primitive";
+ EPRIM =>
+ err += "value is primitive";
+ EINVAL =>
+ err += "value encoding invalid";
+ * =>
+ err += "unknown error " + string ecode;
+ }
+ if(err == "" && i != n)
+ err += "extra data";
+ err += " at byte " + string i;
+ return err;
+}
+
+# Printing functions, for debugging
+
+Tag.tostring(t: self Tag) : string
+{
+ ans := "";
+ snum := string t.num;
+ if(t.class == Universal) {
+ case t.num {
+ BOOLEAN => ans = "BOOLEAN";
+ INTEGER => ans = "INTEGER";
+ BIT_STRING => ans = "BIT STRING";
+ OCTET_STRING => ans = "OCTET STRING";
+ NULL => ans = "NULL";
+ OBJECT_ID => ans = "OBJECT IDENTIFER";
+ ObjectDescriptor => ans = "OBJECT_DES";
+ EXTERNAL => ans = "EXTERNAL";
+ REAL => ans = "REAL";
+ ENUMERATED => ans = "ENUMERATED";
+ EMBEDDED_PDV => ans = "EMBEDDED PDV";
+ SEQUENCE => ans = "SEQUENCE";
+ SET => ans = "SET";
+ NumericString => ans = "NumericString";
+ PrintableString => ans = "PrintableString";
+ TeletexString => ans = "TeletexString";
+ VideotexString => ans = "VideotexString";
+ IA5String => ans = "IA5String";
+ UTCTime => ans = "UTCTime";
+ GeneralizedTime => ans = "GeneralizedTime";
+ GraphicString => ans = "GraphicString";
+ VisibleString => ans = "VisibleString";
+ GeneralString => ans = "GeneralString";
+ UniversalString => ans = "UniversalString";
+ BMPString => ans = "BMPString";
+ * => ans = "UNIVERSAL " + snum;
+ }
+ }
+ else {
+ case t.class {
+ Application =>
+ ans = "APPLICATION " + snum;
+ Context =>
+ ans = "CONTEXT "+ snum;
+ Private =>
+ ans = "PRIVATE " + snum;
+ }
+ }
+ return ans;
+}
+
+Elem.tostring(e: self ref Elem) : string
+{
+ return estring(e, "");
+}
+
+Value.tostring(v: self ref Value) : string
+{
+ return vstring(v, "");
+}
+
+estring(e: ref Elem, indent: string) : string
+{
+ return indent + e.tag.tostring() + " " + vstring(e.val, indent);
+}
+
+vstring(val: ref Value, indent: string) : string
+{
+ ans := "";
+ pick v := val {
+ Bool or Int =>
+ ans += string v.v;
+ Octets or BigInt or Real or Other =>
+ ans += bastring(v.bytes, indent + "\t");
+ BitString =>
+ ans += " bits (unused " +string v.unusedbits + ")" + bastring(v.bits, indent + "\t");
+ Null or EOC =>
+ ;
+ ObjId =>
+ ans += v.id.tostring();
+ String =>
+ ans += "\"" + v.s + "\"";
+ Seq or Set =>
+ ans += "{\n";
+ newindent := indent + "\t";
+ l := v.l;
+ while(l != nil) {
+ if(ans[len ans-1] != '\n')
+ ans[len ans] = '\n';
+ ans += estring(hd l, newindent);
+ l = tl l;
+ }
+ if(ans[len ans-1] != '\n')
+ ans[len ans] = '\n';
+ ans += indent + "}";
+ }
+ return ans;
+}
+
+bastring(a: array of byte, indent: string) : string
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ ans := indent;
+ nlindent := "\n" + indent;
+ for(i := 0; i < len a; i++) {
+ if(i < len a - 1 && i%10 == 0)
+ ans += nlindent ;
+ ans += sys->sprint("%2x ", int a[i]);
+ }
+ return ans;
+}
+
+Oid.tostring(o: self ref Oid) : string
+{
+ ans := "";
+ for(i := 0; i < len o.nums; i++) {
+ ans += string o.nums[i];
+ if(i < len o.nums - 1)
+ ans[len ans] = '.';
+ }
+ return ans;
+}
+
+print_elem(e: ref Elem)
+{
+ s := e.tostring();
+ a := array of byte s;
+ sys->write(sys->fildes(1), a, len a);
+ sys->print("\n");
+}
diff --git a/appl/lib/attrdb.b b/appl/lib/attrdb.b
new file mode 100644
index 00000000..de9fa1b1
--- /dev/null
+++ b/appl/lib/attrdb.b
@@ -0,0 +1,486 @@
+implement Attrdb;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "attrdb.m";
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ if(bufio == nil)
+ return sys->sprint("can't load Bufio: %r");
+ return nil;
+}
+
+parseentry(s: string, lno: int): (ref Dbentry, int, string)
+{
+ (nil, flds) := sys->tokenize(s, "\n");
+ lines: list of ref Tuples;
+ errs: string;
+ for(; flds != nil; flds = tl flds){
+ (ts, err) := parseline(hd flds, lno);
+ if(ts != nil)
+ lines = ts :: lines;
+ else if(err != nil && errs == nil)
+ errs = err;
+ lno++;
+ }
+ return (ref Dbentry(0, lines), lno, errs);
+}
+
+parseline(s: string, lno: int): (ref Tuples, string)
+{
+ attrs: list of ref Attr;
+ quote := 0;
+ word := "";
+ lastword := "";
+ name := "";
+
+Line:
+ for(i := 0; i < len s; i++) {
+ if(quote) {
+ if(s[i] == quote) {
+ if(i+1 >= len s || s[i+1] != quote){
+ quote = 0;
+ continue;
+ }
+ i++;
+ }
+ word[len word] = s[i];
+ continue;
+ }
+ case s[i] {
+ '\'' or '\"' =>
+ quote = s[i];
+ '#' =>
+ break Line;
+ ' ' or '\t' or '\n' =>
+ if(word == nil)
+ continue;
+ if(lastword != nil) {
+ # lastword space word space
+ attrs = ref Attr(lastword, nil, 0) :: attrs;
+ }
+ lastword = word;
+ word = nil;
+
+ if(name != nil) {
+ # name = lastword space
+ attrs = ref Attr(name, lastword, 0) :: attrs;
+ name = lastword = nil;
+ }
+ '=' =>
+ if(lastword == nil) {
+ # word=
+ lastword = word;
+ word = nil;
+ }
+ if(word != nil) {
+ # lastword word=
+ attrs = ref Attr(lastword, nil, 0) :: attrs;
+ lastword = word;
+ word = nil;
+ }
+ if(lastword == nil)
+ return (nil, "empty name");
+ name = lastword;
+ lastword = nil;
+ * =>
+ word[len word] = s[i];
+ }
+ }
+ if(quote)
+ return (nil, "missing quote");
+
+ if(lastword == nil) {
+ lastword = word;
+ word = nil;
+ }
+
+ if(name == nil) {
+ name = lastword;
+ lastword = nil;
+ }
+
+ if(name != nil)
+ attrs = ref Attr(name, lastword, 0) :: attrs;
+
+ if(attrs == nil)
+ return (nil, nil);
+
+ return (ref Tuples(lno, rev(attrs)), nil);
+
+}
+
+Tuples.hasattr(ts: self ref Tuples, attr: string): int
+{
+ for(pl := ts.pairs; pl != nil; pl = tl pl){
+ a := hd pl;
+ if(a.attr == attr)
+ return 1;
+ }
+ return 0;
+}
+
+Tuples.haspair(ts: self ref Tuples, attr: string, value: string): int
+{
+ for(pl := ts.pairs; pl != nil; pl = tl pl){
+ a := hd pl;
+ if(a.attr == attr && a.val == value)
+ return 1;
+ }
+ return 0;
+}
+
+Tuples.find(ts: self ref Tuples, attr: string): list of ref Attr
+{
+ ra: list of ref Attr;
+ for(pl := ts.pairs; pl != nil; pl = tl pl){
+ a := hd pl;
+ if(a.attr == attr)
+ ra = a :: ra;
+ }
+ return rev(ra);
+}
+
+Tuples.findbyattr(ts: self ref Tuples, attr: string, value: string, rattr: string): list of ref Attr
+{
+ if(ts.haspair(attr, value))
+ return ts.find(rattr);
+ return nil;
+}
+
+Dbentry.find(e: self ref Dbentry, attr: string): list of (ref Tuples, list of ref Attr)
+{
+ rrt: list of (ref Tuples, list of ref Attr);
+ for(lines := e.lines; lines != nil; lines = tl lines){
+ l := hd lines;
+ if((ra := l.find(attr)) != nil)
+ rrt = (l, rev(ra)) :: rrt;
+ }
+ rt: list of (ref Tuples, list of ref Attr);
+ for(; rrt != nil; rrt = tl rrt)
+ rt = hd rrt :: rt;
+ return rt;
+}
+
+Dbentry.findfirst(e: self ref Dbentry, attr: string): string
+{
+ for(lines := e.lines; lines != nil; lines = tl lines){
+ l := hd lines;
+ for(pl := l.pairs; pl != nil; pl = tl pl)
+ if((hd pl).attr == attr)
+ return (hd pl).val;
+ }
+ return nil;
+}
+
+Dbentry.findpair(e: self ref Dbentry, attr: string, value: string): list of ref Tuples
+{
+ rts: list of ref Tuples;
+ for(lines := e.lines; lines != nil; lines = tl lines){
+ l := hd lines;
+ if(l.haspair(attr, value))
+ rts = l :: rts;
+ }
+ for(; rts != nil; rts = tl rts)
+ lines = hd rts :: lines;
+ return lines;
+}
+
+Dbentry.findbyattr(e: self ref Dbentry, attr: string, value: string, rattr: string): list of (ref Tuples, list of ref Attr)
+{
+ rm: list of (ref Tuples, list of ref Attr); # lines with attr=value and rattr
+ rnm: list of (ref Tuples, list of ref Attr); # lines with rattr alone
+ for(lines := e.lines; lines != nil; lines = tl lines){
+ l := hd lines;
+ ra: list of ref Attr = nil;
+ match := 0;
+ for(pl := l.pairs; pl != nil; pl = tl pl){
+ a := hd pl;
+ if(a.attr == attr && a.val == value)
+ match = 1;
+ if(a.attr == rattr)
+ ra = a :: ra;
+ }
+ if(ra != nil){
+ if(match)
+ rm = (l, rev(ra)) :: rm;
+ else
+ rnm = (l, rev(ra)) :: rnm;
+ }
+ }
+ rt: list of (ref Tuples, list of ref Attr);
+ for(; rnm != nil; rnm = tl rnm)
+ rt = hd rnm :: rt;
+ for(; rm != nil; rm = tl rm)
+ rt = hd rm :: rt;
+ return rt;
+}
+
+Dbf.open(path: string): ref Dbf
+{
+ df := ref Dbf;
+ df.lockc = chan[1] of int;
+ df.fd = bufio->open(path, Bufio->OREAD);
+ if(df.fd == nil)
+ return nil;
+ df.name = path;
+ (ok, d) := sys->fstat(df.fd.fd);
+ if(ok >= 0)
+ df.dir = ref d;
+ # TO DO: indices
+ return df;
+}
+
+Dbf.sopen(data: string): ref Dbf
+{
+ df := ref Dbf;
+ df.lockc = chan[1] of int;
+ df.fd = bufio->sopen(data);
+ if(df.fd == nil)
+ return nil;
+ df.name = nil;
+ df.dir = nil;
+ return df;
+}
+
+Dbf.reopen(df: self ref Dbf): int
+{
+ lock(df);
+ if(df.name == nil){
+ unlock(df);
+ return 0;
+ }
+ fd := bufio->open(df.name, Bufio->OREAD);
+ if(fd == nil){
+ unlock(df);
+ return -1;
+ }
+ df.fd = fd;
+ df.dir = nil;
+ (ok, d) := sys->fstat(fd.fd);
+ if(ok >= 0)
+ df.dir = ref d;
+ # TO DO: cache, hash tables
+ unlock(df);
+ return 0;
+}
+
+Dbf.changed(df: self ref Dbf): int
+{
+ r: int;
+
+ lock(df);
+ if(df.name == nil){
+ unlock(df);
+ return 0;
+ }
+ (ok, d) := sys->stat(df.name);
+ if(ok < 0)
+ r = df.fd != nil || df.dir == nil;
+ else
+ r = df.dir == nil || !samefile(*df.dir, d);
+ unlock(df);
+ return r;
+}
+
+samefile(d1, d2: Sys->Dir): int
+{
+ # ``it was black ... it was white! it was dark ... it was light! ah yes, i remember it well...''
+ return d1.dev==d2.dev && d1.dtype==d2.dtype &&
+ d1.qid.path==d2.qid.path && d1.qid.vers==d2.qid.vers &&
+ d1.mtime == d2.mtime;
+}
+
+flatten(ts: list of (ref Tuples, list of ref Attr), attr: string): list of ref Attr
+{
+ l: list of ref Attr;
+ for(; ts != nil; ts = tl ts){
+ (line, a) := hd ts;
+ t := line.find(attr);
+ for(; t != nil; t = tl t)
+ l = hd t :: l;
+ }
+ return rev(l);
+}
+
+Db.open(path: string): ref Db
+{
+ df := Dbf.open(path);
+ if(df == nil)
+ return nil;
+ db := ref Db(df :: nil);
+ (e, nil) := db.findpair(nil, "database", "");
+ if(e != nil){
+ files := flatten(e.find("file"), "file");
+ if(files != nil){
+ dbs: list of ref Dbf;
+ for(; files != nil; files = tl files){
+ name := (hd files).val;
+ if(name == path && df != nil){
+ dbs = df :: dbs;
+ df = nil;
+ }else if((tf := Dbf.open(name)) != nil)
+ dbs = tf :: dbs;
+ }
+ db.dbs = rev(dbs);
+ if(df != nil)
+ db.dbs = df :: db.dbs;
+ }
+ }
+ return db;
+}
+
+Db.sopen(data: string): ref Db
+{
+ df := Dbf.sopen(data);
+ if(df == nil)
+ return nil;
+ return ref Db(df :: nil);
+}
+
+Db.append(db1: self ref Db, db2: ref Db): ref Db
+{
+ if(db1 == nil)
+ return db2;
+ if(db2 == nil)
+ return db1;
+ dbs := db2.dbs;
+ for(rl := rev(db1.dbs); rl != nil; rl = tl rl)
+ dbs = hd rl :: dbs;
+ return ref Db(dbs);
+}
+
+Db.reopen(db: self ref Db): int
+{
+ f := 0;
+ for(dbs := db.dbs; dbs != nil; dbs = tl dbs)
+ if((hd dbs).reopen() < 0)
+ f = -1;
+ return f;
+}
+
+Db.changed(db: self ref Db): int
+{
+ f := 0;
+ for(dbs := db.dbs; dbs != nil; dbs = tl dbs)
+ f |= (hd dbs).changed();
+ return f;
+}
+
+isentry(l: string): int
+{
+ return l!=nil && l[0]!='\t' && l[0]!='\n' && l[0]!=' ' && l[0]!='#';
+}
+
+Dbf.readentry(dbf: self ref Dbf, offset: int, attr: string, value: string, useval: int): (ref Dbentry, int, int)
+{
+ lock(dbf);
+ fd := dbf.fd;
+ fd.seek(big offset, 0);
+ lines: list of ref Tuples;
+ match := attr == nil;
+ while((l := fd.gets('\n')) != nil){
+ while(isentry(l)){
+ lines = nil;
+ do{
+ offset = int fd.offset();
+ (t, nil) := parseline(l, 0);
+ if(t != nil){
+ lines = t :: lines;
+ if(!match){
+ if(useval)
+ match = t.haspair(attr, value);
+ else
+ match = t.hasattr(attr);
+ }
+ }
+ l = fd.gets('\n');
+ }while(l != nil && !isentry(l));
+ if(match && lines != nil){
+ rl := lines;
+ for(lines = nil; rl != nil; rl = tl rl)
+ lines = hd rl :: lines;
+ unlock(dbf);
+ return (ref Dbentry(0, lines), 1, offset);
+ }
+ }
+ }
+ unlock(dbf);
+ return (nil, 0, int fd.offset());
+}
+
+nextentry(db: ref Db, ptr: ref Dbptr, attr: string, value: string, useval: int): (ref Dbentry, ref Dbptr)
+{
+ if(ptr == nil){
+ ptr = ref Dbptr.Direct(db.dbs, nil, 0);
+ # TO DO: index
+ }
+ while(ptr.dbs != nil){
+ offset: int;
+ dbf := hd ptr.dbs;
+ pick p := ptr {
+ Direct =>
+ offset = p.offset;
+ Hash =>
+ raise "not done yet";
+ }
+ (e, match, next) := dbf.readentry(offset, attr, value, useval);
+ if(match)
+ return (e, ref Dbptr.Direct(ptr.dbs, nil, next));
+ if(e == nil)
+ ptr = ref Dbptr.Direct(tl ptr.dbs, nil, 0);
+ else
+ ptr = ref Dbptr.Direct(ptr.dbs, nil, next);
+ }
+ return (nil, ptr);
+}
+
+Db.find(db: self ref Db, ptr: ref Dbptr, attr: string): (ref Dbentry, ref Dbptr)
+{
+ return nextentry(db, ptr, attr, nil, 0);
+}
+
+Db.findpair(db: self ref Db, ptr: ref Dbptr, attr: string, value: string): (ref Dbentry, ref Dbptr)
+{
+ return nextentry(db, ptr, attr, value, 1);
+}
+
+Db.findbyattr(db: self ref Db, ptr: ref Dbptr, attr: string, value: string, rattr: string): (ref Dbentry, ref Dbptr)
+{
+ for(;;){
+ e: ref Dbentry;
+ (e, ptr) = nextentry(db, ptr, attr, value, 1);
+ if(e == nil || e.find(rattr) != nil)
+ return (e, ptr);
+ }
+}
+
+rev[T](l: list of T): list of T
+{
+ rl: list of T;
+ for(; l != nil; l = tl l)
+ rl = hd l :: rl;
+ return rl;
+}
+
+lock(dbf: ref Dbf)
+{
+ dbf.lockc <-= 1;
+}
+
+unlock(dbf: ref Dbf)
+{
+ <-dbf.lockc;
+}
diff --git a/appl/lib/attrhash.b b/appl/lib/attrhash.b
new file mode 100644
index 00000000..b48e0477
--- /dev/null
+++ b/appl/lib/attrhash.b
@@ -0,0 +1,109 @@
+implement Attrhash, Attrindex;
+
+include "sys.m";
+ sys: Sys;
+
+include "bufio.m";
+ bufio: Bufio;
+
+include "attrdb.m";
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ if(bufio == nil)
+ return sys->sprint("can't load %s: %r", Bufio->PATH);
+ return nil;
+}
+
+attrindex(): Attrindex
+{
+ return load Attrindex "$self";
+}
+
+Index.open(dbf: Attrdb->Dbf, attr: string, fd: ref Sys->FD): ref Index
+{
+ (ok, d) := sys->fstat(fd);
+ if(ok < 0 || dbf.dir == nil || dbf.dir.mtime > d.mtime)
+ return nil;
+ length := int d.length;
+ if(length < NDBHLEN)
+ return nil;
+ buf := array[length] of byte;
+ if(sys->read(fd, buf, len buf) != len buf)
+ return nil;
+ mtime := get4(buf);
+ if(mtime != dbf.dir.mtime)
+ return nil;
+ size := get3(buf[4:]);
+ return ref Index(fd, attr, d.mtime, size, buf[8:]);
+}
+
+#Index.firstoff(ind: self ref Index, val: string): ref Attrdb->Dbptr
+#{
+# o := hash(val, ind.size)*NDBPLEN;
+# p := get3(tab[o:]);
+# if(p == NDBNAP)
+# return nil;
+# if((p & NDBCHAIN) == 0)
+# return ref Attrdb.Direct(p);
+# p &= ~NDBCHAIN;
+# return ref Attrdb.Hash(get3(tab[p:]), get3(tab[p+NDBPLEN:]));
+#}
+
+#Index.nextoff(ind: self ref Index, val: string, ptr: ref Attrdb->Dbptr): (int, ref Attrdb->Dbptr)
+#{
+# pick p := ptr {
+# Hash =>
+# o := get3(tab[p.current:]);
+# if((o & NDBCHAIN) == 0)
+# return (o, ref Attrdb.Direct(p.next));
+# o &= ~NDBCHAIN;
+# o1 := get3(tab[o:]);
+# o2 := get3(tab[o+NDBPLEN:]);
+#
+
+# o := hash(val, ind.size)*NDBPLEN;
+# p := get3(tab[o:]);
+# if(p == NDBNAP)
+# return nil;
+# for(; (p := get3(tab[o:])) != NDBNAP; o = p & ~NDBCHAIN)
+# if((p & NDBCHAIN) == 0){
+# put3(tab[o:], chain | NDBCHAIN);
+# put3(tab[chain:], p);
+# put3(tab[chain+NDBPLEN:], offset);
+# return chain+2*NDBPLEN;
+# }
+# return nil;
+#}
+
+#
+# this must be the same hash function used by Plan 9's ndb
+#
+hash(s: string, hlen: int): int
+{
+ h := 0;
+ for(i := 0; i < len s; i++)
+ if(s[i] >= 16r80){
+ # could optimise by calculating utf ourselves
+ a := array of byte s;
+ for(i=0; i<len a; i++)
+ h = (h*13) + int a[i] - 'a';
+ break;
+ }else
+ h = (h*13) + s[i]-'a';
+ if(h < 0)
+ return int((big h & big 16rFFFFFFFF)%big hlen);
+ return h%hlen;
+}
+
+get3(a: array of byte): int
+{
+ return (int a[2]<<16) | (int a[1]<<8) | int a[0];
+}
+
+get4(a: array of byte): int
+{
+ return (int a[3]<<24) | (int a[2]<<16) | (int a[1]<<8) | int a[0];
+}
diff --git a/appl/lib/auth.b b/appl/lib/auth.b
new file mode 100644
index 00000000..35dee2e0
--- /dev/null
+++ b/appl/lib/auth.b
@@ -0,0 +1,326 @@
+# Inferno authentication protocol
+implement Auth;
+
+include "sys.m";
+ sys: Sys;
+
+include "keyring.m";
+
+include "security.m";
+ ssl: SSL;
+
+init(): string
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ return nil;
+}
+
+server(algs: list of string, ai: ref Keyring->Authinfo, fd: ref Sys->FD, setid: int): (ref Sys->FD, string)
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ kr := load Keyring Keyring->PATH;
+ if(kr == nil)
+ return (nil, sys->sprint("%r"));
+
+ # mutual authentication
+ (id_or_err, secret) := kr->auth(fd, ai, setid);
+
+ if(secret == nil){
+ if(ai == nil && id_or_err == "no authentication information")
+ id_or_err = "no server certificate";
+ return (nil, id_or_err);
+ }
+ if(0)
+ sys->fprint(sys->fildes(2), "secret is %s\n", dump(secret));
+
+ # have got a secret, get algorithm from client
+ # check if the client algorithm is in the server algorithm list
+ # client algorithm ::= ident (' ' ident)*
+ # where ident is defined by ssl(3)
+ algbuf := string kr->getmsg(fd);
+ if(algbuf == nil)
+ return (nil, sys->sprint("can't read client ssl algorithm: %r"));
+ alg := "";
+ (nil, calgs) := sys->tokenize(algbuf, " /");
+ for(; calgs != nil; calgs = tl calgs){
+ calg := hd calgs;
+ if(algs != nil){ # otherwise we suck it and see
+ for(sl := algs; sl != nil; sl = tl sl)
+ if(hd sl == calg)
+ break;
+ if(sl == nil)
+ return (nil, "unsupported client algorithm: " + calg);
+ }
+ alg += calg + " ";
+ }
+ if(alg != nil)
+ alg = alg[0:len alg - 1];
+
+ # don't push ssl if server supports nossl
+ if(alg == nil || alg == "none")
+ return (fd, id_or_err);
+
+ # push ssl and turn on algorithms
+ ssl = load SSL SSL->PATH;
+ if(ssl == nil)
+ return (nil, sys->sprint("can't load ssl: %r"));
+ (c, err) := pushssl(fd, secret, secret, alg);
+ if(c == nil)
+ return (nil, "push ssl: " + err);
+ return (c, id_or_err);
+}
+
+client(alg: string, ai: ref Keyring->Authinfo, fd: ref Sys->FD): (ref Sys->FD, string)
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ kr := load Keyring Keyring->PATH;
+ if(kr == nil)
+ return (nil, sys->sprint("%r"));
+
+ if(alg == nil)
+ alg = "none";
+
+ # mutual authentication
+ (id_or_err, secret) := kr->auth(fd, ai, 0);
+ if(secret == nil)
+ return (nil, id_or_err);
+
+ # send algorithm
+ buf := array of byte alg;
+ if(kr->sendmsg(fd, buf, len buf) < 0)
+ return (nil, sys->sprint("can't send ssl algorithm: %r"));
+
+ # don't push ssl if server supports no ssl connection
+ if(alg == "none")
+ return (fd, id_or_err);
+
+ # push ssl and turn on algorithm
+ ssl = load SSL SSL->PATH;
+ if(ssl == nil)
+ return (nil, sys->sprint("can't load ssl: %r"));
+ (c, err) := pushssl(fd, secret, secret, alg);
+ if(c == nil)
+ return (nil, "push ssl: " + err);
+ return (c, id_or_err);
+}
+
+auth(ai: ref Keyring->Authinfo, keyspec: string, alg: string, fd: ref Sys->FD): (ref Sys->FD, ref Keyring->Authinfo, string)
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ kr := load Keyring Keyring->PATH;
+ if(kr == nil)
+ return (nil, nil, sys->sprint("can't load %s: %r", Keyring->PATH));
+ if(alg == nil)
+ alg = "none";
+ if(ai == nil && keyspec != nil){
+ ai = key(keyspec);
+ if(ai == nil)
+ return (nil, nil, sys->sprint("can't obtain key: %r"));
+ }
+
+ # mutual authentication
+ (id_or_err, secret) := kr->auth(fd, ai, 0);
+ if(secret == nil)
+ return (nil, nil, id_or_err);
+
+ # send algorithm
+ buf := array of byte alg;
+ if(kr->sendmsg(fd, buf, len buf) < 0)
+ return (nil, nil, sys->sprint("can't send ssl algorithm: %r"));
+
+ if(0){ # TO DO
+ hisalg := string kr->getmsg(fd);
+ if(hisalg == nil)
+ return (nil, nil, sys->sprint("can't get remote algorithm: %r"));
+ # TO DO: compare the two, sort it out if they aren't equal
+ }
+
+ # don't push ssl if server supports no ssl connection
+ if(alg == "none")
+ return (fd, nil, id_or_err);
+
+ # push ssl and turn on algorithm
+ ssl = load SSL SSL->PATH;
+ if(ssl == nil)
+ return (nil, nil, sys->sprint("can't load ssl: %r"));
+ (c, err) := pushssl(fd, secret, secret, alg);
+ if(c == nil)
+ return (nil, nil, "push ssl: " + err);
+ return (c, nil, id_or_err);
+}
+
+dump(b: array of byte): string
+{
+ s := "";
+ for(i := 0; i < len b; i++)
+ s += sys->sprint("%.2ux", int b[i]);
+ return s;
+}
+
+# push an SSLv2 Record Layer onto the fd
+pushssl(fd: ref Sys->FD, secretin, secretout: array of byte, alg: string): (ref Sys->FD, string)
+{
+ (err, c) := ssl->connect(fd);
+ if(err != nil)
+ return (nil, "can't connect ssl: " + err);
+
+ err = ssl->secret(c, secretin, secretout);
+ if(err != nil)
+ return (nil, "can't write secret: " + err);
+
+ if(sys->fprint(c.cfd, "alg %s", alg) < 0)
+ return (nil, sys->sprint("can't push algorithm %s: %r", alg));
+
+ return (c.dfd, nil);
+}
+
+key(keyspec: string): ref Keyring->Authinfo
+{
+ f := keyfile(keyspec);
+ if(f == nil)
+ return nil;
+ kr := load Keyring Keyring->PATH;
+ if(kr == nil){
+ sys->werrstr(sys->sprint("can't load %s: %r", Keyring->PATH));
+ return nil;
+ }
+ return kr->readauthinfo(f);
+}
+
+#
+# look for key in old style keyring directory;
+# closest match to [net!]addr[!svc]
+#
+
+keyfile(keyspec: string): string
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ al := parseattr(keyspec);
+ keyname := get(al, "key");
+ if(keyname != nil){
+ # explicit keyname overrides rest of spec
+ if(keyname[0] == '/' ||
+ len keyname > 2 && keyname[0:2]=="./" ||
+ len keyname > 3 && keyname[0:3]=="../")
+ return keyname; # don't add directory
+ return keydir()+keyname;
+ }
+ net := "net";
+ svc := get(al, "service");
+ addr := get(al, "server");
+ (nf, flds) := sys->tokenize(addr, "!"); # compatibility
+ if(nf > 1){
+ net = hd flds;
+ addr = hd tl flds;
+ }
+ if(addr != nil)
+ keyname = addr;
+ else
+ keyname = "default";
+ kd := keydir();
+ dom := get(al, "dom");
+ if(dom != nil){
+ if((cert := exists(kd+dom)) != nil)
+ return cert;
+ }
+ if(keyname == "default")
+ return kd+"default";
+ if(net == "net")
+ l := "net!" :: "tcp!" :: nil;
+ else
+ l = net+"!" :: nil;
+ if(svc != nil){
+ for(nl := l; nl != nil; nl = tl nl){
+ cert := exists(kd+(hd nl)+keyname+"!"+svc); # most specific
+ if(cert != nil)
+ return cert;
+ }
+ }
+ for(nl := l; nl != nil; nl = tl nl){
+ cert := exists(kd+(hd nl)+keyname);
+ if(cert != nil)
+ return cert;
+ }
+ cert := exists(kd+keyname); # unadorned
+ if(cert != nil)
+ return cert;
+ if(keyname != "default"){
+ cert = exists(kd+"default");
+ if(cert != nil)
+ return cert;
+ }
+ return kd+keyname;
+}
+
+keydir(): string
+{
+ fd := sys->open("/dev/user", Sys->OREAD);
+ if(fd == nil)
+ return nil;
+ b := array[Sys->NAMEMAX] of byte;
+ nr := sys->read(fd, b, len b);
+ if(nr <= 0){
+ sys->werrstr("can't read /dev/user");
+ return nil;
+ }
+ user := string b[0:nr];
+ return "/usr/" + user + "/keyring/";
+}
+
+exists(f: string): string
+{
+ (ok, nil) := sys->stat(f);
+ if(0)sys->fprint(sys->fildes(2), "exists: %q %d\n", f, ok>=0);
+ if(ok >= 0)
+ return f;
+ return nil;
+}
+
+Aattr, Aval, Aquery: con iota;
+
+Attr: adt {
+ tag: int;
+ name: string;
+ val: string;
+};
+
+parseattr(s: string): list of ref Attr
+{
+ (nil, fld) := sys->tokenize(s, " \t\n"); # should do quoting; later
+ rfld := fld;
+ for(fld = nil; rfld != nil; rfld = tl rfld)
+ fld = (hd rfld) :: fld;
+ attrs: list of ref Attr;
+ for(; fld != nil; fld = tl fld){
+ n := hd fld;
+ a := "";
+ tag := Aattr;
+ for(i:=0; i<len n; i++)
+ if(n[i] == '='){
+ a = n[i+1:];
+ n = n[0:i];
+ tag = Aval;
+ }
+ if(len n == 0)
+ continue;
+ if(tag == Aattr && len n > 1 && n[len n-1] == '?'){
+ tag = Aquery;
+ n = n[0:len n-1];
+ }
+ attrs = ref Attr(tag, n, a) :: attrs;
+ }
+ return attrs;
+}
+
+get(al: list of ref Attr, n: string): string
+{
+ for(; al != nil; al = tl al)
+ if((a := hd al).name == n && a.tag == Aval)
+ return a.val;
+ return nil;
+}
diff --git a/appl/lib/auth9.b b/appl/lib/auth9.b
new file mode 100644
index 00000000..06accb53
--- /dev/null
+++ b/appl/lib/auth9.b
@@ -0,0 +1,351 @@
+implement Auth9;
+
+#
+# elements of Plan 9 authentication
+#
+# this is a near transliteration of Plan 9 source, subject to the Lucent Public License 1.02
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "keyring.m";
+
+include "auth9.m";
+
+debug := 0;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+setdebug(i: int)
+{
+ debug = i;
+}
+
+put2(a: array of byte, v: int)
+{
+ a[0] = byte v;
+ a[1] = byte (v>>8);
+}
+
+get2(a: array of byte): int
+{
+ return (int a[1]<<8) | int a[0];
+}
+
+put4(a: array of byte, v: int)
+{
+ a[0] = byte v;
+ a[1] = byte (v>>8);
+ a[2] = byte (v>>16);
+ a[3] = byte (v>>24);
+}
+
+get4(a: array of byte): int
+{
+ return (int a[3]<<24) | (int a[2]<<16) | (int a[1]<<8) | int a[0];
+}
+
+puts(a: array of byte, s: string, n: int)
+{
+ b := array of byte s;
+ l := len b;
+ if(l > n)
+ b = b[0:n];
+ a[0:] = b;
+ for(; l < n; l++)
+ a[l] = byte 0;
+}
+
+gets(a: array of byte, n: int): string
+{
+ for(i:=0; i<n; i++)
+ if(a[i] == byte 0)
+ break;
+ return string a[0:i];
+}
+
+geta(a: array of byte, n: int): array of byte
+{
+ b := array[n] of byte;
+ b[0:] = a[0:n];
+ return b;
+}
+
+Authenticator.pack(f: self ref Authenticator, key: array of byte): array of byte
+{
+ p := array[AUTHENTLEN] of {* => byte 0};
+ p[0] = byte f.num;
+ p[1:] = f.chal;
+ put4(p[1+CHALLEN:], f.id);
+ if(key != nil)
+ encrypt(key, p, len p);
+ return p;
+}
+
+Authenticator.unpack(a: array of byte, key: array of byte): (int, ref Authenticator)
+{
+ if(key != nil)
+ decrypt(key, a, AUTHENTLEN);
+ f := ref Authenticator;
+ f.num = int a[0];
+ f.chal = geta(a[1:], CHALLEN);
+ f.id = get4(a[1+CHALLEN:]);
+ return (AUTHENTLEN, f);
+}
+
+Passwordreq.pack(f: self ref Passwordreq, key: array of byte): array of byte
+{
+ a := array[PASSREQLEN] of {* => byte 0};
+ a[0] = byte f.num;
+ a[1:] = f.old;
+ a[1+ANAMELEN:] = f.new;
+ a[1+2*ANAMELEN] = byte f.changesecret;
+ a[1+2*ANAMELEN+1:] = f.secret;
+ if(key != nil)
+ encrypt(key, a, len a);
+ return a;
+}
+
+Passwordreq.unpack(a: array of byte, key: array of byte): (int, ref Passwordreq)
+{
+ if(key != nil)
+ decrypt(key, a, PASSREQLEN);
+ f := ref Passwordreq;
+ f.num = int a[0];
+ f.old = geta(a[1:], ANAMELEN);
+ f.old[ANAMELEN-1] = byte 0;
+ f.new = geta(a[1+ANAMELEN:], ANAMELEN);
+ f.new[ANAMELEN-1] = byte 0;
+ f.changesecret = int a[1+2*ANAMELEN];
+ f.secret = geta(a[1+2*ANAMELEN+1:], SECRETLEN);
+ f.secret[SECRETLEN-1] = byte 0;
+ return (PASSREQLEN, f);
+}
+
+Ticket.pack(f: self ref Ticket, key: array of byte): array of byte
+{
+ a := array[TICKETLEN] of {* => byte 0};
+ a[0] = byte f.num;
+ a[1:] = f.chal;
+ puts(a[1+CHALLEN:], f.cuid, ANAMELEN);
+ puts(a[1+CHALLEN+ANAMELEN:], f.suid, ANAMELEN);
+ a[1+CHALLEN+2*ANAMELEN:] = f.key;
+ if(key != nil)
+ encrypt(key, a, len a);
+ return a;
+}
+
+Ticket.unpack(a: array of byte, key: array of byte): (int, ref Ticket)
+{
+ if(key != nil)
+ decrypt(key, a, TICKETLEN);
+ f := ref Ticket;
+ f.num = int a[0];
+ f.chal = geta(a[1:], CHALLEN);
+ f.cuid = gets(a[1+CHALLEN:], ANAMELEN);
+ f.suid = gets(a[1+CHALLEN+ANAMELEN:], ANAMELEN);
+ f.key = geta(a[1+CHALLEN+2*ANAMELEN:], DESKEYLEN);
+ return (TICKETLEN, f);
+}
+
+Ticketreq.unpack(a: array of byte): (int, ref Ticketreq)
+{
+ f := ref Ticketreq;
+ f.rtype = int a[0];
+ f.authid = gets(a[1:], ANAMELEN);
+ f.authdom = gets(a[1+ANAMELEN:], DOMLEN);
+ f.chal = geta(a[1+ANAMELEN+DOMLEN:], CHALLEN);
+ f.hostid = gets(a[1+ANAMELEN+DOMLEN+CHALLEN:], ANAMELEN);
+ f.uid = gets(a[1+ANAMELEN+DOMLEN+CHALLEN+ANAMELEN:], ANAMELEN);
+ return (TICKREQLEN, f);
+}
+
+Ticketreq.pack(f: self ref Ticketreq): array of byte
+{
+ a := array[TICKREQLEN] of {* => byte 0};
+ a[0] = byte f.rtype;
+ puts(a[1:], f.authid, ANAMELEN);
+ puts(a[1+ANAMELEN:], f.authdom, DOMLEN);
+ a[1+ANAMELEN+DOMLEN:] = f.chal;
+ puts(a[1+ANAMELEN+DOMLEN+CHALLEN:], f.hostid, ANAMELEN);
+ puts(a[1+ANAMELEN+DOMLEN+CHALLEN+ANAMELEN:], f.uid, ANAMELEN);
+ return a;
+}
+
+netcrypt(key: array of byte, chal: string): string
+{
+ buf := array[8] of {* => byte 0};
+ a := array of byte chal;
+ if(len a > 7)
+ a = a[0:7];
+ buf[0:] = a;
+ encrypt(key, buf, len buf);
+ return sys->sprint("%.2ux%.2ux%.2ux%.2ux", int buf[0], int buf[1], int buf[2], int buf[3]);
+}
+
+passtokey(p: string): array of byte
+{
+ a := array of byte p;
+ n := len a;
+ if(n >= ANAMELEN)
+ n = ANAMELEN-1;
+ buf := array[ANAMELEN] of {* => byte ' '};
+ buf[0:] = a[0:n];
+ buf[n] = byte 0;
+ key := array[DESKEYLEN] of {* => byte 0};
+ t := 0;
+ for(;;){
+ for(i := 0; i < DESKEYLEN; i++)
+ key[i] = byte ((int buf[t+i] >> i) + (int buf[t+i+1] << (8 - (i+1))));
+ if(n <= 8)
+ return key;
+ n -= 8;
+ t += 8;
+ if(n < 8){
+ t -= 8 - n;
+ n = 8;
+ }
+ encrypt(key, buf[t:], 8);
+ }
+}
+
+parity := array[] of {
+ byte 16r01, byte 16r02, byte 16r04, byte 16r07, byte 16r08, byte 16r0b, byte 16r0d, byte 16r0e,
+ byte 16r10, byte 16r13, byte 16r15, byte 16r16, byte 16r19, byte 16r1a, byte 16r1c, byte 16r1f,
+ byte 16r20, byte 16r23, byte 16r25, byte 16r26, byte 16r29, byte 16r2a, byte 16r2c, byte 16r2f,
+ byte 16r31, byte 16r32, byte 16r34, byte 16r37, byte 16r38, byte 16r3b, byte 16r3d, byte 16r3e,
+ byte 16r40, byte 16r43, byte 16r45, byte 16r46, byte 16r49, byte 16r4a, byte 16r4c, byte 16r4f,
+ byte 16r51, byte 16r52, byte 16r54, byte 16r57, byte 16r58, byte 16r5b, byte 16r5d, byte 16r5e,
+ byte 16r61, byte 16r62, byte 16r64, byte 16r67, byte 16r68, byte 16r6b, byte 16r6d, byte 16r6e,
+ byte 16r70, byte 16r73, byte 16r75, byte 16r76, byte 16r79, byte 16r7a, byte 16r7c, byte 16r7f,
+ byte 16r80, byte 16r83, byte 16r85, byte 16r86, byte 16r89, byte 16r8a, byte 16r8c, byte 16r8f,
+ byte 16r91, byte 16r92, byte 16r94, byte 16r97, byte 16r98, byte 16r9b, byte 16r9d, byte 16r9e,
+ byte 16ra1, byte 16ra2, byte 16ra4, byte 16ra7, byte 16ra8, byte 16rab, byte 16rad, byte 16rae,
+ byte 16rb0, byte 16rb3, byte 16rb5, byte 16rb6, byte 16rb9, byte 16rba, byte 16rbc, byte 16rbf,
+ byte 16rc1, byte 16rc2, byte 16rc4, byte 16rc7, byte 16rc8, byte 16rcb, byte 16rcd, byte 16rce,
+ byte 16rd0, byte 16rd3, byte 16rd5, byte 16rd6, byte 16rd9, byte 16rda, byte 16rdc, byte 16rdf,
+ byte 16re0, byte 16re3, byte 16re5, byte 16re6, byte 16re9, byte 16rea, byte 16rec, byte 16ref,
+ byte 16rf1, byte 16rf2, byte 16rf4, byte 16rf7, byte 16rf8, byte 16rfb, byte 16rfd, byte 16rfe,
+};
+
+des56to64(k56: array of byte): array of byte
+{
+ k64 := array[8] of byte;
+ hi := (int k56[0]<<24)|(int k56[1]<<16)|(int k56[2]<<8)|int k56[3];
+ lo := (int k56[4]<<24)|(int k56[5]<<16)|(int k56[6]<<8);
+
+ k64[0] = parity[(hi>>25)&16r7f];
+ k64[1] = parity[(hi>>18)&16r7f];
+ k64[2] = parity[(hi>>11)&16r7f];
+ k64[3] = parity[(hi>>4)&16r7f];
+ k64[4] = parity[((hi<<3)|int ((big lo & big 16rFFFFFFFF)>>29))&16r7f]; # watch the sign extension
+ k64[5] = parity[(lo>>22)&16r7f];
+ k64[6] = parity[(lo>>15)&16r7f];
+ k64[7] = parity[(lo>>8)&16r7f];
+ return k64;
+}
+
+encrypt(key: array of byte, data: array of byte, n: int)
+{
+ if(n < 8)
+ return;
+ kr := load Keyring Keyring->PATH;
+ ds := kr->dessetup(des56to64(key), nil);
+ n--;
+ r := n % 7;
+ n /= 7;
+ j := 0;
+ for(i := 0; i < n; i++){
+ kr->desecb(ds, data[j:], 8, Keyring->Encrypt);
+ j += 7;
+ }
+ if(r)
+ kr->desecb(ds, data[j-7+r:], 8, Keyring->Encrypt);
+}
+
+decrypt(key: array of byte, data: array of byte, n: int)
+{
+ if(n < 8)
+ return;
+ kr := load Keyring Keyring->PATH;
+ ds := kr->dessetup(des56to64(key), nil);
+ n--;
+ r := n % 7;
+ n /= 7;
+ j := n*7;
+ if(r)
+ kr->desecb(ds, data[j-7+r:], 8, Keyring->Decrypt);
+ for(i := 0; i < n; i++){
+ j -= 7;
+ kr->desecb(ds, data[j:], 8, Keyring->Decrypt);
+ }
+}
+
+readn(fd: ref Sys->FD, nb: int): array of byte
+{
+ buf:= array[nb] of byte;
+ for(n:=0; n<nb;){
+ m := sys->read(fd, buf[n:], nb-n);
+ if(m <= 0)
+ return nil;
+ n += m;
+ }
+ return buf;
+}
+
+pbmsg: con "AS protocol botch";
+
+_asgetticket(fd: ref Sys->FD, tr: ref Ticketreq, key: array of byte): (ref Ticket, array of byte)
+{
+ a := tr.pack();
+ if(sys->write(fd, a, len a) < 0){
+ sys->werrstr(pbmsg);
+ return (nil, nil);
+ }
+ a = _asrdresp(fd, 2*TICKETLEN);
+ if(a == nil)
+ return (nil, nil);
+ (nil, t) := Ticket.unpack(a, key);
+ return (t, a[TICKETLEN:]); # can't unpack both since the second uses server key
+}
+
+_asrdresp(fd: ref Sys->FD, n: int): array of byte
+{
+ b := array[1] of byte;
+ if(sys->read(fd, b, 1) != 1){
+ sys->werrstr(pbmsg);
+ return nil;
+ }
+
+ buf: array of byte;
+ case int b[0] {
+ AuthOK =>
+ buf = readn(fd, n);
+ AuthOKvar =>
+ b = readn(fd, 5);
+ if(b == nil)
+ break;
+ n = int string b;
+ if(n<= 0 || n > 4096)
+ break;
+ buf = readn(fd, n);
+ AuthErr =>
+ b = readn(fd, 64);
+ if(b == nil)
+ break;
+ for(i:=0; i<len b && b[i] != byte 0; i++)
+ ;
+ sys->werrstr(sys->sprint("remote: %s", string b[0:i]));
+ return nil;
+ * =>
+ sys->werrstr(sys->sprint("%s: resp %d", pbmsg, int b[0]));
+ return nil;
+ }
+ if(buf == nil)
+ sys->werrstr(pbmsg);
+ return buf;
+}
diff --git a/appl/lib/bloomfilter.b b/appl/lib/bloomfilter.b
new file mode 100644
index 00000000..8860c8b0
--- /dev/null
+++ b/appl/lib/bloomfilter.b
@@ -0,0 +1,72 @@
+implement Bloomfilter;
+
+include "sys.m";
+ sys: Sys;
+include "sets.m";
+ sets: Sets;
+ Set: import sets;
+include "keyring.m";
+ keyring: Keyring;
+include "bloomfilter.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ sets = load Sets Sets->PATH;
+ sets->init();
+ keyring = load Keyring Keyring->PATH;
+}
+
+Bits: adt {
+ d: array of byte;
+ n: int; # bits used
+ get: fn(bits: self ref Bits, n: int): int;
+};
+
+filter(d: array of byte, logm, k: int): Set
+{
+ if(logm < 3 || logm > 30)
+ raise "invalid bloom filter size";
+ nb := 1 << logm;
+ f := array[nb / 8 + 1] of {* => byte 0}; # one extra zero to make sure set's not inverted.
+ bits := hashbits(d, logm * k);
+ while(k--){
+ v := bits.get(logm);
+ f[v >> 3] |= byte 1 << (v & 7);
+ }
+ return sets->bytes2set(f);
+}
+
+hashbits(data: array of byte, n: int): ref Bits
+{
+ d := array[((n + 7) / 8)] of byte;
+ digest := array[Keyring->SHA1dlen] of byte;
+ state := keyring->sha1(data, len data, nil, nil);
+ extra := array[2] of byte;
+ e := 0;
+ for(i := 0; i < len d; i += Keyring->SHA1dlen){
+ extra[0] = byte e;
+ extra[1] = byte (e>>8);
+ e++;
+ state = keyring->sha1(extra, len extra, digest, state);
+ if(i + Keyring->SHA1dlen > len d)
+ digest = digest[0:len d - i];
+ d[i:] = digest;
+ }
+ return ref Bits(d, 0);
+}
+
+# XXX could be more efficient.
+Bits.get(bits: self ref Bits, n: int): int
+{
+ d := bits.d;
+ v := 0;
+ nb := bits.n;
+ for(i := 0; i < n; i++){
+ j := nb + i;
+ if(int d[j >> 3] & (1 << (j & 7)))
+ v |= (1 << i);
+ }
+ bits.n += n;
+ return v;
+}
diff --git a/appl/lib/bufio.b b/appl/lib/bufio.b
new file mode 100644
index 00000000..5960e310
--- /dev/null
+++ b/appl/lib/bufio.b
@@ -0,0 +1,533 @@
+implement Bufio;
+
+include "sys.m";
+ sys: Sys;
+
+include "bufio.m";
+
+UTFself: con 16r80; # ascii and UTF sequences are the same (<)
+Maxrune: con 8; # could probably be Sys->UTFmax
+Bufsize: con Sys->ATOMICIO;
+
+Filler: adt
+{
+ iobuf: ref Iobuf;
+ fill: BufioFill;
+ next: cyclic ref Filler;
+};
+
+fillers: ref Filler;
+
+create(filename: string, mode, perm: int): ref Iobuf
+{
+ if (sys == nil)
+ sys = load Sys Sys->PATH;
+ if ((fd := sys->create(filename, mode, perm)) == nil)
+ return nil;
+ return ref Iobuf(fd, array[Bufsize+Maxrune] of byte, 0, 0, 0, big 0, big 0, mode, mode);
+}
+
+open(filename: string, mode: int): ref Iobuf
+{
+ if (sys == nil)
+ sys = load Sys Sys->PATH;
+ if ((fd := sys->open(filename, mode)) == nil)
+ return nil;
+ return ref Iobuf(fd, array[Bufsize+Maxrune] of byte, 0, 0, 0, big 0, big 0, mode, mode);
+}
+
+fopen(fd: ref Sys->FD, mode: int): ref Iobuf
+{
+ if (sys == nil)
+ sys = load Sys Sys->PATH;
+ if ((filpos := sys->seek(fd, big 0, 1)) < big 0)
+ filpos = big 0;
+ return ref Iobuf(fd, array[Bufsize+Maxrune] of byte, 0, 0, 0, filpos, filpos, mode, mode);
+}
+
+sopen(input: string): ref Iobuf
+{
+ return aopen(array of byte input);
+}
+
+aopen(b: array of byte): ref Iobuf
+{
+ if (sys == nil)
+ sys = load Sys Sys->PATH;
+ return ref Iobuf(nil, b, 0, len b, 0, big 0, big 0, OREAD, OREAD);
+}
+
+readchunk(b: ref Iobuf): int
+{
+ if (b.fd == nil){
+ if ((f := filler(b)) != nil){
+ if ((n := f.fill->fill(b)) == EOF)
+ nofill(b);
+ return n;
+ }
+ return EOF;
+ }
+ if (b.filpos != b.bufpos + big b.size) {
+ s := b.bufpos + big b.size;
+ if (sys->seek(b.fd, s, 0) != s)
+ return ERROR;
+ b.filpos = s;
+ }
+ i := len b.buffer - b.size - 1;
+ if(i > Bufsize)
+ i = Bufsize;
+ if ((i = sys->read(b.fd, b.buffer[b.size:], i)) <= 0) {
+ if(i < 0)
+ return ERROR;
+ return EOF;
+ }
+ b.size += i;
+ b.filpos += big i;
+ return i;
+}
+
+writechunk(b: ref Iobuf): int
+{
+ if (b.fd == nil)
+ return ERROR;
+ if (b.filpos != b.bufpos) {
+ if (sys->seek(b.fd, b.bufpos, 0) != b.bufpos)
+ return ERROR;
+ b.filpos = b.bufpos;
+ }
+ if ((size := b.size) > Bufsize)
+ size = Bufsize;
+ if (sys->write(b.fd, b.buffer, size) != size)
+ return ERROR;
+ b.filpos += big size;
+ b.size -= size;
+ if (b.size) {
+ b.dirty = 1;
+ b.buffer[0:] = b.buffer[Bufsize:Bufsize+b.size];
+ } else
+ b.dirty = 0;
+ b.bufpos += big size;
+ b.index -= size;
+ return size;
+}
+
+Iobuf.close(b: self ref Iobuf)
+{
+ if (b.fd == nil) {
+ nofill(b);
+ return;
+ }
+ if (b.dirty)
+ b.flush();
+ b.fd = nil;
+ b.buffer = nil;
+}
+
+Iobuf.flush(b: self ref Iobuf): int
+{
+ if (b.fd == nil)
+ return ERROR;
+ if (b.lastop == OREAD){
+ b.bufpos = b.filpos;
+ b.size = 0;
+ return 0;
+ }
+ while (b.dirty) {
+ if (writechunk(b) < 0)
+ return ERROR;
+ if (b.index < 0) {
+ b.bufpos += big b.index;
+ b.index = 0;
+ }
+ }
+ return 0;
+}
+
+Iobuf.seek(b: self ref Iobuf, off: big, start: int): big
+{
+ npos: big;
+
+ if (b.fd == nil){
+ if(filler(b) != nil)
+ return big ERROR;
+ }
+ case (start) {
+ 0 => # absolute address
+ npos = off;
+ 1 => # offset from current location
+ npos = b.bufpos + big b.index + off;
+ off = npos;
+ start = Sys->SEEKSTART;
+ 2 => # offset from EOF
+ npos = big -1;
+ * => return big ERROR;
+ }
+ if (b.bufpos <= npos && npos < b.bufpos + big b.size) {
+ b.index = int(npos - b.bufpos);
+ return npos;
+ }
+ if (b.fd == nil || b.dirty && b.flush() < 0)
+ return big ERROR;
+ b.size = 0;
+ b.index = 0;
+ if ((s := sys->seek(b.fd, off, start)) < big 0) {
+ b.filpos = b.bufpos = big 0;
+ return big ERROR;
+ }
+ b.bufpos = b.filpos = s;
+ return b.bufpos = b.filpos = s;
+}
+
+Iobuf.offset(b: self ref Iobuf): big
+{
+ return b.bufpos + big b.index;
+}
+
+write2read(b: ref Iobuf): int
+{
+ while (b.dirty)
+ if (b.flush() < 0)
+ return ERROR;
+ b.bufpos = b.filpos;
+ b.size = 0;
+ b.lastop = OREAD;
+ if ((r := readchunk(b)) < 0)
+ return r;
+ if (b.index > b.size)
+ return EOF;
+ return 0;
+}
+
+Iobuf.read(b: self ref Iobuf, buf: array of byte, n: int): int
+{
+ if (b.mode == OWRITE)
+ return ERROR;
+ if (b.lastop != OREAD){
+ if ((r := write2read(b)) < 0)
+ return r;
+ }
+ k := n;
+ while (b.size - b.index < k) {
+ buf[0:] = b.buffer[b.index:b.size];
+ buf = buf[b.size - b.index:];
+ k -= b.size - b.index;
+
+ b.bufpos += big b.size;
+ b.index = 0;
+ b.size = 0;
+ if ((r := readchunk(b)) < 0) {
+ if(r == EOF || n != k)
+ return n-k;
+ return ERROR;
+ }
+ }
+ buf[0:] = b.buffer[b.index:b.index+k];
+ b.index += k;
+ return n;
+}
+
+Iobuf.getb(b: self ref Iobuf): int
+{
+ if(b.lastop != OREAD){
+ if(b.mode == OWRITE)
+ return ERROR;
+ if((r := write2read(b)) < 0)
+ return r;
+ }
+ if (b.index == b.size) {
+ b.bufpos += big b.index;
+ b.index = 0;
+ b.size = 0;
+ if ((r := readchunk(b)) < 0)
+ return r;
+ }
+ return int b.buffer[b.index++];
+}
+
+Iobuf.ungetb(b: self ref Iobuf): int
+{
+ if(b.mode == OWRITE || b.lastop != OREAD)
+ return ERROR;
+ b.index--;
+ return 1;
+}
+
+Iobuf.getc(b: self ref Iobuf): int
+{
+ r, i, s: int;
+
+ if(b.lastop != OREAD){
+ if(b.mode == OWRITE)
+ return ERROR;
+ if((r = write2read(b)) < 0)
+ return r;
+ }
+ for(;;) {
+ if(b.index < b.size) {
+ r = int b.buffer[b.index];
+ if(r < UTFself){
+ b.index++;
+ return r;
+ }
+ (r, i, s) = sys->byte2char(b.buffer[0:b.size], b.index);
+ if (i != 0) {
+ b.index += i;
+ return r;
+ }
+ b.buffer[0:] = b.buffer[b.index:b.size];
+ }
+ b.bufpos += big b.index;
+ b.size -= b.index;
+ b.index = 0;
+ if ((r = readchunk(b)) < 0)
+ return r;
+ }
+ # Not reached:
+ return -1;
+}
+
+Iobuf.ungetc(b: self ref Iobuf): int
+{
+ if(b.index == 0 || b.mode == OWRITE || b.lastop != OREAD)
+ return ERROR;
+ stop := b.index - Sys->UTFmax;
+ if(stop < 0)
+ stop = 0;
+ buf := b.buffer[0:b.size];
+ for(i := b.index-1; i >= stop; i--){
+ (r, n, s) := sys->byte2char(buf, i);
+ if(s && i + n == b.index){
+ b.index = i;
+ return 1;
+ }
+ }
+ b.index--;
+
+ return 1;
+}
+
+# optimised when term < UTFself (common case)
+tgets(b: ref Iobuf, t: int): string
+{
+ str: string;
+ term := byte t;
+ for(;;){
+ start := b.index;
+ end := start + sys->utfbytes(b.buffer[start:], b.size-start);
+ buf := b.buffer;
+ # XXX could speed up by adding extra byte to end of buffer and
+ # placing a sentinel there (eliminate one test, perhaps 35% speedup).
+ # (but not when we've been given the buffer externally)
+ for(i := start; i < end; i++){
+ if(buf[i] == term){
+ i++;
+ str += string buf[start:i];
+ b.index = i;
+ return str;
+ }
+ }
+ str += string buf[start:i];
+ if(i < b.size)
+ b.buffer[0:] = buf[i:b.size];
+ b.size -= i;
+ b.bufpos += big i;
+ b.index = 0;
+ if(readchunk(b) < 0)
+ break;
+ }
+ return str;
+}
+
+Iobuf.gets(b: self ref Iobuf, term: int): string
+{
+ i: int;
+
+ if(b.mode == OWRITE)
+ return nil;
+ if(b.lastop != OREAD && write2read(b) < 0)
+ return nil;
+# if(term < UTFself)
+# return tgets(b, term);
+ str: string;
+ ch := -1;
+ for (;;) {
+ start := b.index;
+ n := 0;
+ while(b.index < b.size){
+ (ch, i, nil) = sys->byte2char(b.buffer[0:b.size], b.index);
+ if(i == 0) # too few bytes for full Rune
+ break;
+ n += i;
+ b.index += i;
+ if(ch == term)
+ break;
+ }
+ if(n > 0)
+ str += string b.buffer[start:start+n];
+ if(ch == term)
+ return str;
+ b.buffer[0:] = b.buffer[b.index:b.size];
+ b.bufpos += big b.index;
+ b.size -= b.index;
+ b.index = 0;
+ if (readchunk(b) < 0)
+ break;
+ }
+ return str; # nil at EOF
+}
+
+Iobuf.gett(b: self ref Iobuf, s: string): string
+{
+ r := "";
+ if (b.mode == OWRITE || (ch := b.getc()) < 0)
+ return nil;
+ do {
+ r[len r] = ch;
+ for (i:=0; i<len(s); i++)
+ if (ch == s[i])
+ return r;
+ } while ((ch = b.getc()) >= 0);
+ return r;
+}
+
+read2write(b: ref Iobuf)
+{
+ # last operation was a read
+ b.bufpos += big b.index;
+ b.size = 0;
+ b.index = 0;
+ b.lastop = OWRITE;
+}
+
+Iobuf.write(b: self ref Iobuf, buf: array of byte, n: int): int
+{
+ if(b.lastop != OWRITE) {
+ if(b.mode == OREAD)
+ return ERROR;
+ read2write(b);
+ }
+ start := 0;
+ k := n;
+ while(k > 0){
+ nw := Bufsize - b.index;
+ if(nw > k)
+ nw = k;
+ end := start + nw;
+ b.buffer[b.index:] = buf[start:end];
+ start = end;
+ b.index += nw;
+ k -= nw;
+ if(b.index > b.size)
+ b.size = b.index;
+ b.dirty = 1;
+ if(b.size == Bufsize && writechunk(b) < 0)
+ return ERROR;
+ }
+ return n;
+}
+
+Iobuf.putb(b: self ref Iobuf, c: byte): int
+{
+ if(b.lastop != OWRITE) {
+ if(b.mode == OREAD)
+ return ERROR;
+ read2write(b);
+ }
+ b.buffer[b.index++] = c;
+ if(b.index > b.size)
+ b.size = b.index;
+ b.dirty = 1;
+ if(b.size >= Bufsize) {
+ if (b.fd == nil)
+ return ERROR;
+ if (writechunk(b) < 0)
+ return ERROR;
+ }
+ return 0;
+}
+
+Iobuf.putc(b: self ref Iobuf, c: int): int
+{
+ if(b.lastop != OWRITE) {
+ if (b.mode == OREAD)
+ return ERROR;
+ read2write(b);
+ }
+ if(c < UTFself)
+ b.buffer[b.index++] = byte c;
+ else
+ b.index += sys->char2byte(c, b.buffer, b.index);
+ if (b.index > b.size)
+ b.size = b.index;
+ b.dirty = 1;
+ if (b.size >= Bufsize) {
+ if (writechunk(b) < 0)
+ return ERROR;
+ }
+ return 0;
+}
+
+Iobuf.puts(b: self ref Iobuf, s: string): int
+{
+ if(b.lastop != OWRITE) {
+ if (b.mode == OREAD)
+ return ERROR;
+ read2write(b);
+ }
+ n := len s;
+ if (n == 0)
+ return 0;
+ ind := b.index;
+ buf := b.buffer;
+ for(i := 0; i < n; i++){
+ c := s[i];
+ if(c < UTFself)
+ buf[ind++] = byte c;
+ else
+ ind += sys->char2byte(c, buf, ind);
+ if(ind >= Bufsize){
+ b.index = ind;
+ if(ind > b.size)
+ b.size = ind;
+ b.dirty = 1;
+ if(writechunk(b) < 0)
+ return ERROR;
+ ind = b.index;
+ }
+ }
+ b.dirty = b.index != ind;
+ b.index = ind;
+ if (ind > b.size)
+ b.size = ind;
+ return n;
+}
+
+filler(b: ref Iobuf): ref Filler
+{
+ for (f := fillers; f != nil; f = f.next)
+ if(f.iobuf == b)
+ return f;
+ return nil;
+}
+
+Iobuf.setfill(b: self ref Iobuf, fill: BufioFill)
+{
+ if ((f := filler(b)) != nil)
+ f.fill = fill;
+ else
+ fillers = ref Filler(b, fill, fillers);
+}
+
+nofill(b: ref Iobuf)
+{
+ prev: ref Filler;
+ for(f := fillers; f != nil; f = f.next) {
+ if(f.iobuf == b) {
+ if (prev == nil)
+ fillers = f.next;
+ else
+ prev.next = f.next;
+ }
+ prev = f;
+ }
+}
diff --git a/appl/lib/cfg.b b/appl/lib/cfg.b
new file mode 100644
index 00000000..49e60262
--- /dev/null
+++ b/appl/lib/cfg.b
@@ -0,0 +1,234 @@
+implement Cfg;
+
+include "sys.m";
+include "bufio.m";
+include "cfg.m";
+
+sys : Sys;
+bufio : Bufio;
+
+Iobuf : import bufio;
+ENOMOD : con "cannot load module";
+EBADPATH : con "bad path";
+
+
+init(path : string) : string
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ if (bufio == nil)
+ return sys->sprint("%s: %r", ENOMOD);
+
+ iob := bufio->open(path, Sys->OREAD);
+ if (iob == nil)
+ return sys->sprint("%s: %r", EBADPATH);
+
+ # parse the config file
+ r : list of ref Tuple;
+ lnum := 0;
+ rlist : list of list of ref Tuple;
+
+ while ((line := iob.gets('\n')) != nil) {
+ lnum++;
+ (tuple, err) := parseline(line, lnum);
+ if (err != nil)
+ return sys->sprint("%s:%d: %s", path, lnum, err);
+
+ if (tuple == nil)
+ continue;
+ if (line[0] != ' ' && line[0] != '\t') {
+ # start of a new record
+ if (r != nil)
+ rlist = r :: rlist;
+ r = nil;
+ }
+ r = tuple :: r;
+ }
+ if (r != nil)
+ rlist = r :: rlist;
+ for (; rlist != nil; rlist = tl rlist)
+ insert(hd rlist);
+ return nil;
+}
+
+parseline(s : string, lnum : int) : (ref Tuple, string)
+{
+ attrs : list of Attr;
+ quote := 0;
+ word := "";
+ lastword := "";
+ name := "";
+
+loop:
+ for (i := 0 ; i < len s; i++) {
+ if (quote) {
+ if (s[i] == quote) {
+ if (i + 1 < len s && s[i+1] == quote) {
+ word[len word] = quote;
+ i++;
+ } else {
+ quote = 0;
+ continue;
+ }
+ } else
+ word[len word] = s[i];
+ continue;
+ }
+ case s[i] {
+ '\'' or '\"' =>
+ quote = s[i];
+ continue;
+ '#' =>
+ break loop;
+ ' ' or '\t' or '\n' =>
+ if (word == nil)
+ continue;
+ if (lastword != nil) {
+ # lastword space word space
+ attrs = Attr(lastword, nil) :: attrs;
+ }
+ lastword = word;
+ word = nil;
+
+ if (name != nil) {
+ # name = lastword space
+ attrs = Attr(name, lastword) :: attrs;
+ name = lastword = nil;
+ }
+ '=' =>
+ if (lastword == nil) {
+ # word=
+ lastword = word;
+ word = nil;
+ }
+ if (word != nil) {
+ # lastword word=
+ attrs = Attr(lastword, nil) :: attrs;
+ lastword = word;
+ word = nil;
+ }
+ if (lastword == nil)
+ return (nil, "empty name");
+ name = lastword;
+ lastword = nil;
+ * =>
+ word[len word] = s[i];
+ }
+ }
+ if (quote)
+ return (nil, "missing quote");
+
+ if (lastword == nil) {
+ lastword = word;
+ word = nil;
+ }
+
+ if (name == nil) {
+ name = lastword;
+ lastword = nil;
+ }
+
+ if (name != nil)
+ attrs = Attr(name, lastword) :: attrs;
+
+ if (attrs == nil)
+ return (nil, nil);
+
+ fattrs : list of Attr;
+ for (; attrs != nil; attrs = tl attrs)
+ fattrs = hd attrs :: fattrs;
+ return (ref Tuple(lnum, fattrs), nil);
+}
+
+lookup(name : string) : list of (string, ref Record)
+{
+ l := buckets[hash(name)];
+ for (; l != nil; l = tl l) {
+ hr := hd l;
+ if (hr.name != name)
+ continue;
+ return hr.vrecs;
+ }
+ return nil;
+}
+
+Record.lookup(r : self ref Record, name : string) : (string, ref Tuple)
+{
+ for (ts := r.tuples; ts != nil; ts = tl ts) {
+ t := hd ts;
+ for (as := t.attrs; as != nil; as = tl as) {
+ a := hd as;
+ if (a.name == name)
+ return (a.value, t);
+ }
+ }
+ return (nil, nil);
+}
+
+Tuple.lookup(t : self ref Tuple, name : string) : string
+{
+ for (as := t.attrs; as != nil; as = tl as) {
+ a := hd as;
+ if (a.name == name)
+ return a.value;
+ }
+ return nil;
+}
+
+reset()
+{
+ keynames = nil;
+ buckets = array[HSIZE+1] of list of ref HRecord;
+}
+
+# Record hash table
+HRecord : adt {
+ name : string;
+ vrecs : list of (string, ref Record);
+};
+
+keynames : list of string;
+
+HSIZE : con 16rff; # must be (2^n)-1 due to hash() defn
+buckets := array [HSIZE+1] of list of ref HRecord;
+
+hash(name : string) : int
+{
+ # maybe use hashPJW?
+ h := 0;
+ for (i := 0; i < len name; i++)
+ h = (h + name[i]) & HSIZE;
+ return h;
+}
+
+insert(rtups : list of ref Tuple) {
+ # tuple list is currently in reverse order
+ ftups : list of ref Tuple;
+ for (; rtups != nil; rtups = tl rtups)
+ ftups = hd rtups :: ftups;
+
+ maintuple := hd ftups;
+ mainattr := hd maintuple.attrs;
+ name := mainattr.name;
+ value := mainattr.value;
+
+ # does name already exist?
+ hr : ref HRecord;
+ h := hash(name);
+ l := buckets[h];
+ for (; l != nil; l = tl l) {
+ hr = hd l;
+ if (hr.name == name)
+ break;
+ }
+ if (l == nil) {
+ keynames = name :: keynames;
+ buckets[h] = ref HRecord(name, (value, ref Record(ftups))::nil) :: buckets[h];
+ } else
+ hr.vrecs = (value, ref Record(ftups)) :: hr.vrecs;
+}
+
+getkeys() : list of string
+{
+ return keynames;
+}
diff --git a/appl/lib/cfgfile.b b/appl/lib/cfgfile.b
new file mode 100644
index 00000000..f7dcf4b6
--- /dev/null
+++ b/appl/lib/cfgfile.b
@@ -0,0 +1,152 @@
+implement CfgFile;
+
+#
+# Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved.
+# Revisions Copyright © 2000 Vita Nuova Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+ Dir: import sys;
+
+include "draw.m";
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "cfgfile.m";
+
+# Detect/Copy/Create
+verify(default: string, path: string): ref Sys->FD
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+
+ fd := sys->open(path, Sys->ORDWR);
+ if(fd == nil) {
+ fd = sys->create(path, Sys->ORDWR, 8r666);
+ if(fd == nil)
+ return nil;
+ # copy default configuration file (if present)
+ ifd := sys->open(default, Sys->OREAD);
+ if(ifd != nil){
+ buf := array[Sys->ATOMICIO] of byte;
+ while((n := sys->read(ifd, buf, len buf)) > 0)
+ if(sys->write(fd, buf, n) != n)
+ return nil;
+ ifd = nil;
+ }
+ }
+ return fd;
+}
+
+init(file: string): ref ConfigFile
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+
+ if(sys == nil || bufio == nil)
+ return nil;
+
+ me := ref ConfigFile;
+ me.file = file;
+ me.readonly = 0;
+ f := bufio->open(file, Sys->ORDWR);
+ if(f == nil) {
+ f = bufio->open(file, Sys->OREAD);
+ if (f == nil)
+ return nil;
+ me.readonly = 1;
+ }
+
+ while((l := f.gett("\r\n")) != nil) {
+ if(l[(len l)-1] == '\n')
+ l = l[0:(len l)-1];
+ me.lines = l :: me.lines;
+ }
+
+ return me;
+}
+
+ConfigFile.flush(me: self ref ConfigFile): string
+{
+ if(me.readonly)
+ return "file is read only";
+ if((fd := sys->create(me.file,Sys->OWRITE,0644)) == nil)
+ return sys->sprint("%r");
+ if((f := bufio->fopen(fd,Sys->OWRITE)) == nil)
+ return sys->sprint("%r");
+
+ l := me.lines;
+ while(l != nil) {
+ f.puts(hd l+"\n");
+ l = tl l;
+ }
+ if(f.flush() == -1)
+ return sys->sprint("%r");
+
+ return nil;
+}
+
+ConfigFile.getcfg(me:self ref ConfigFile, field:string): list of string
+{
+ l := me.lines;
+
+ while(l != nil) {
+ (n,fields) := sys->tokenize(hd l," \t");
+ if(n >= 1 && field == hd fields)
+ return tl fields;
+ l = tl l;
+ }
+ return nil;
+}
+
+ConfigFile.setcfg(me:self ref ConfigFile, field:string, val:string)
+{
+ l := me.lines;
+ newlist: list of string;
+
+ if(me.readonly)
+ return; # should return "file is read only";
+
+ matched := 0;
+
+ while(l != nil) {
+ (n,fields) := sys->tokenize(hd l," \t");
+ if(!matched && n >= 1 && field == hd fields) {
+ newlist = field+"\t"+val::newlist;
+ matched = 1;
+ }
+ else
+ newlist = hd l::newlist;
+ l = tl l;
+ }
+ if(!matched)
+ newlist = field+"\t"+val::newlist;
+
+ me.lines = newlist;
+}
+
+ConfigFile.delete(me:self ref ConfigFile, field:string)
+{
+ l := me.lines;
+ newlist: list of string;
+
+ if(me.readonly)
+ return; # should return "file is read only";
+
+ matched := 0;
+
+ while(l != nil) {
+ (n,fields) := sys->tokenize(hd l," \t");
+ if(!matched && n >= 1 && field == hd fields) {
+ matched = 1;
+ }
+ else
+ newlist = hd l::newlist;
+ l = tl l;
+ }
+
+ me.lines = newlist;
+}
diff --git a/appl/lib/chanfill.b b/appl/lib/chanfill.b
new file mode 100644
index 00000000..7e7d2685
--- /dev/null
+++ b/appl/lib/chanfill.b
@@ -0,0 +1,56 @@
+implement ChanFill;
+
+#
+# Iobuf fill routine to serve a file2chan.
+#
+
+include "sys.m";
+include "bufio.m";
+
+myfill: BufioFill;
+bufio: Bufio;
+fid: int;
+wc: Sys->Rwrite;
+fio: ref Sys->FileIO;
+
+Iobuf: import bufio;
+
+init(data: array of byte, f: int, c: Sys->Rwrite, r: ref Sys->FileIO, b: Bufio): ref Iobuf
+{
+ if (myfill == nil)
+ myfill = load BufioFill SELF;
+ bufio = b;
+ i := bufio->sopen(string data);
+ fid = f;
+ wc = c;
+ fio = r;
+ i.setfill(myfill);
+ wc <-= (len data, nil);
+ return i;
+}
+
+fill(b: ref Iobuf): int
+{
+ for (;;) {
+ alt {
+ (nil, data, f, c) := <-fio.write =>
+ if (f != fid) {
+ if (c != nil)
+ c <-= (0, "file busy");
+ continue;
+ }
+ if (c == nil)
+ return Bufio->EOF;
+ c <-= (len data, nil);
+ i := len data;
+ if (i == 0)
+ continue;
+ b.buffer[b.size:] = data;
+ b.size += i;
+ b.filpos += big i;
+ return i;
+ * =>
+ return Bufio->EOF;
+ }
+ }
+}
diff --git a/appl/lib/convcs/8bit_stob.b b/appl/lib/convcs/8bit_stob.b
new file mode 100644
index 00000000..6f7647a4
--- /dev/null
+++ b/appl/lib/convcs/8bit_stob.b
@@ -0,0 +1,24 @@
+implement Stob;
+
+include "sys.m";
+include "convcs.m";
+
+sys : Sys;
+
+init(nil : string) : string
+{
+ sys = load Sys Sys->PATH;
+ return nil;
+}
+
+stob(nil : Convcs->State, str : string) : (Convcs->State, array of byte)
+{
+ b := array [len str] of byte;
+ for (i := 0; i < len str; i++) {
+ ch := str[i];
+ if (ch > 255)
+ ch = '?';
+ b[i] = byte ch;
+ }
+ return (nil, b);
+}
diff --git a/appl/lib/convcs/big5_btos.b b/appl/lib/convcs/big5_btos.b
new file mode 100644
index 00000000..f92fe471
--- /dev/null
+++ b/appl/lib/convcs/big5_btos.b
@@ -0,0 +1,87 @@
+implement Btos;
+
+include "sys.m";
+include "convcs.m";
+
+# Big5 consists of 89 fonts of 157 chars each
+BIG5MAX : con 13973;
+BIG5FONT : con 157;
+
+BIG5DATA : con "/lib/convcs/big5";
+
+MAXINT : con 16r7fffffff;
+BADCHAR : con 16rFFFD;
+
+big5map := "";
+
+init(nil : string) : string
+{
+ sys := load Sys Sys->PATH;
+ fd := sys->open(BIG5DATA, Sys->OREAD);
+ if (fd == nil)
+ return sys->sprint("%s: %r", BIG5DATA);
+
+ buf := array[BIG5MAX * Sys->UTFmax] of byte;
+ nread := 0;
+ for (;nread < len buf;) {
+ n := sys->read(fd, buf[nread:], Sys->ATOMICIO);
+ if (n <= 0)
+ break;
+ nread += n;
+ }
+ big5map = string buf[:nread];
+ if (len big5map != BIG5MAX) {
+ big5map = nil;
+ return sys->sprint("%s: corrupt data", BIG5DATA);
+ }
+ return nil;
+}
+
+btos(nil : Convcs->State, b : array of byte, n : int) : (Convcs->State, string, int)
+{
+ nbytes := 0;
+ str := "";
+
+ if (n == -1)
+ n = MAXINT;
+
+ font := -1;
+ for (i := 0; i < len b && len str < n; i++) {
+ c := int b[i];
+ if (font == -1) {
+ # idle state
+ if(c >= 16rA1){
+ font = c;
+ continue;
+ }
+ if(c == 26)
+ c = '\n';
+ str[len str] = c;
+ nbytes = i + 1;
+ continue;
+ } else {
+ # seen a font spec
+ f := font;
+ font = -1;
+ ch := Sys->UTFerror;
+ if(c >= 64 && c <= 126)
+ c -= 64;
+ else if(c >= 161 && c <= 254)
+ c = c-161 + 63;
+ else
+ # bad big5 char
+ f = 255;
+ if(f <= 254) {
+ f -= 161;
+ ix := f*BIG5FONT + c;
+ if(ix < len big5map)
+ ch = big5map[ix];
+ if (ch == -1)
+ ch = BADCHAR;
+ }
+ str[len str] = ch;
+ nbytes = i + 1;
+ }
+ }
+ return (nil, str, nbytes);
+}
diff --git a/appl/lib/convcs/big5_stob.b b/appl/lib/convcs/big5_stob.b
new file mode 100644
index 00000000..e2936971
--- /dev/null
+++ b/appl/lib/convcs/big5_stob.b
@@ -0,0 +1,93 @@
+implement Stob;
+
+include "sys.m";
+include "convcs.m";
+
+
+# Big5 consists of 89 fonts of 157 chars each
+BIG5MAX : con 13973;
+BIG5FONT : con 157;
+NFONTS : con 89;
+
+BIG5DATA : con "/lib/convcs/big5";
+
+big5map := "";
+r2fontchar : array of byte;
+
+# NOTE: could be more memory friendly during init()
+# by building the r2fontchar mapping table on the fly
+# instead of building it from the complete big5map string
+
+init(nil : string) : string
+{
+ sys := load Sys Sys->PATH;
+ fd := sys->open(BIG5DATA, Sys->OREAD);
+ if (fd == nil)
+ return sys->sprint("%s: %r", BIG5DATA);
+
+ buf := array[BIG5MAX * Sys->UTFmax] of byte;
+ nread := 0;
+ for (;nread < len buf;) {
+ n := sys->read(fd, buf[nread:], Sys->ATOMICIO);
+ if (n <= 0)
+ break;
+ nread += n;
+ }
+ big5map = string buf[:nread];
+ buf = nil;
+ if (len big5map != BIG5MAX) {
+ big5map = nil;
+ return sys->sprint("%s: corrupt data", BIG5DATA);
+ }
+ r2fontchar = array [2 * 16r10000] of { * => byte 16rff};
+ for (i := 0; i < len big5map; i++) {
+ f := i / BIG5FONT;
+ c := i % BIG5FONT;
+ ix := 2*big5map[i];
+ r2fontchar[ix] = byte f;
+ r2fontchar[ix+1] = byte c;
+ }
+ return nil;
+}
+
+stob(nil : Convcs->State, str : string) : (Convcs->State, array of byte)
+{
+ buf := array [1024] of byte;
+ nb := 0;
+ cbuf := array [2] of byte;
+ nc := 0;
+ for (i := 0; i < len str; i++) {
+ c := str[i];
+ nc = 0;
+ if (c < 128) {
+# if (c == '\n') # not sure abou this
+# c = 26;
+ cbuf[nc++] = byte c;
+ } else {
+ ix := 2*c;
+ f := int r2fontchar[ix];
+ c = int r2fontchar[ix+1];
+ if (f >= NFONTS) {
+ # no mapping of unicode character to big5
+ cbuf[nc++] = byte '?';
+ } else {
+ f += 16rA1;
+ cbuf[nc++] = byte f;
+ if (c <= 62)
+ c += 64;
+ else
+ c += 16rA1 - 63;
+ cbuf[nc++] = byte c;
+ }
+ }
+ if (nc + nb > len buf)
+ buf = ((array [len buf * 2] of byte)[:] = buf);
+ buf[nb:] = cbuf[:nc];
+ nb += nc;
+ }
+ if (nb == 0)
+ return (nil, nil);
+ r := array [nb] of byte;
+ r[:] = buf[:nb];
+ return (nil, r);
+}
diff --git a/appl/lib/convcs/convcs.b b/appl/lib/convcs/convcs.b
new file mode 100644
index 00000000..23208224
--- /dev/null
+++ b/appl/lib/convcs/convcs.b
@@ -0,0 +1,165 @@
+implement Convcs;
+
+include "sys.m";
+include "cfg.m";
+include "convcs.m";
+
+DEFCSFILE : con "/lib/convcs/charsets";
+
+sys : Sys;
+cfg : Cfg;
+
+Record, Tuple : import cfg;
+
+init(csfile : string) : string
+{
+ sys = load Sys Sys->PATH;
+ cfg = load Cfg Cfg->PATH;
+ if (cfg == nil)
+ return sys->sprint("cannot load module %s: %r", Cfg->PATH);
+ if (csfile == nil)
+ csfile = DEFCSFILE;
+ err := cfg->init(csfile);
+ if (err != nil) {
+ cfg = nil;
+ return err;
+ }
+ return nil;
+}
+
+getbtos(cs : string) : (Btos, string)
+{
+ cs = normalize(cs);
+ (rec, err) := csalias(cs);
+ if (err != nil)
+ return (nil, err);
+
+ (path, btostup) := rec.lookup("btos");
+ if (path == nil)
+ return (nil, sys->sprint("no converter for %s", cs));
+ arg := btostup.lookup("arg");
+
+ btos := load Btos path;
+ if (btos == nil)
+ return (nil, sys->sprint("cannot load converter: %r"));
+ err = btos->init(arg);
+ if (err != nil)
+ return (nil, err);
+ return (btos, nil);
+}
+
+getstob(cs : string) : (Stob, string)
+{
+ cs = normalize(cs);
+ (rec, err) := csalias(cs);
+ if (err != nil)
+ return (nil, err);
+
+ (path, stobtup) := rec.lookup("stob");
+ if (path == nil)
+ return (nil, sys->sprint("no converter for %s", cs));
+ arg := stobtup.lookup("arg");
+
+ stob := load Stob path;
+ if (stob == nil)
+ return (nil, sys->sprint("cannot load converter: %r"));
+ err = stob->init(arg);
+ if (err != nil)
+ return (nil, err);
+ return (stob, nil);
+}
+
+csalias(cs : string) : (ref Cfg->Record, string)
+{
+ # search out charset record - allow for one level of renaming
+ for (i := 0; i < 2; i++) {
+ recs := cfg->lookup(cs);
+ if (recs == nil)
+ return (nil, sys->sprint("unknown charset %s", cs));
+ (val, rec) := hd recs;
+ if (val != nil) {
+ cs = val;
+ continue;
+ }
+ return (rec, nil);
+ }
+ return (nil, sys->sprint("too man aliases for %s\n", cs));
+}
+
+enumcs() : list of (string, string, int)
+{
+ d : list of (string, string, int);
+ for (csl := cfg->getkeys(); csl != nil; csl = tl csl) {
+ cs := hd csl;
+ recs := cfg->lookup(cs);
+ if (recs == nil)
+ continue; # shouldn't happen!
+ (val, rec) := hd recs;
+ if (val != nil)
+ # an alias - ignore
+ continue;
+
+ (btos, nil) := rec.lookup("btos");
+ (stob, nil) := rec.lookup("stob");
+
+ if (btos == nil && stob == nil)
+ continue;
+ mode := 0;
+ if (btos != nil)
+ mode = BTOS;
+ if (stob != nil)
+ mode |= STOB;
+
+ (desc, nil) := rec.lookup("desc");
+ if (desc == nil)
+ desc = cs;
+
+ d = (cs, desc, mode) :: d;
+ }
+ # d is in reverse order to that in the csfile file
+ l : list of (string, string, int);
+ for (; d != nil; d = tl d)
+ l = hd d :: l;
+ return l;
+}
+
+aliases(cs : string) : (string, list of string)
+{
+ cs = normalize(cs);
+ (mainrec, err) := csalias(cs);
+ if (err != nil)
+ return (err, nil);
+
+ cs = (hd (hd mainrec.tuples).attrs).name;
+
+ (desc, nil) := mainrec.lookup("desc");
+ if (desc == nil)
+ desc = cs;
+
+ al := cs :: nil;
+ for (csl := cfg->getkeys(); csl != nil; csl = tl csl) {
+ name := hd csl;
+ recs := cfg->lookup(name);
+ if (recs == nil)
+ continue; # shouldn't happen!
+ (val, rec) := hd recs;
+ if (val != cs)
+ continue;
+ al = name :: al;
+ }
+
+ r : list of string;
+ for (; al != nil; al = tl al)
+ r = hd al :: r;
+ return (desc, r);
+}
+
+normalize(s : string) : string
+{
+ for (i := 0; i < len s; i++) {
+ r := s[i];
+ if (r >= 'A' && r <= 'Z')
+ s[i] = r + ('a' - 'A');
+ }
+ return s;
+}
diff --git a/appl/lib/convcs/cp932_btos.b b/appl/lib/convcs/cp932_btos.b
new file mode 100644
index 00000000..a99d87f5
--- /dev/null
+++ b/appl/lib/convcs/cp932_btos.b
@@ -0,0 +1,179 @@
+implement Btos;
+
+# encoding details
+# (Traditional) Shift-JIS
+#
+# 00..1f control characters
+# 20 space
+# 21..7f JIS X 0201:1976/1997 roman (see notes)
+# 80 undefined
+# 81..9f lead byte of JIS X 0208-1983 or JIS X 0202:1990/1997
+# a0 undefined
+# a1..df JIS X 0201:1976/1997 katakana
+# e0..ea lead byte of JIS X 0208-1983 or JIS X 0202:1990/1997
+# eb..ff undefined
+#
+# CP932 (windows-31J)
+#
+# this encoding scheme extends Shift-JIS in the following way
+#
+# eb..ec undefined (marked as lead bytes - see notes below)
+# ed..ee lead byte of NEC-selected IBM extended characters
+# ef undefined (marked as lead byte - see notes below)
+# f0..f9 lead byte of User defined GAIJI (see note below)
+# fa..fc lead byte of IBM extended characters
+# fd..ff undefined
+#
+#
+# Notes
+#
+# JISX 0201:1976/1997 roman
+# this is the same as ASCII but with 0x5c (ASCII code for '\')
+# representing the Yen currency symbol '¥' (U+00a5)
+# This mapping is contentious, some conversion packages implent it
+# others do not.
+# The mapping files from The Unicode Consortium show cp932 mapping
+# plain ascii in the range 00..7f whereas shift-jis maps 16r5c ('\') to the yen
+# symbol (¥) and 16r7e ('~') to overline (¯)
+#
+# CP932 double-byte character codes:
+#
+# eb-ec, ef, f0-f9:
+# Marked as DBCS LEAD BYTEs in the unicode mapping data
+# obtained from:
+# https://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP932.TXT
+#
+# but there are no defined mappings for codes in this range.
+# It is not clear whether or not an implementation should
+# consume one or two bytes before emitting an error char.
+#
+
+include "sys.m";
+include "convcs.m";
+
+sys : Sys;
+
+MAXINT : con 16r7fffffff;
+BADCHAR : con 16rFFFD;
+
+KANAPAGES : con 1;
+KANAPAGESZ : con 63;
+KANACHAR0 : con 16ra1;
+
+CP932PAGES : con 45; # 81..84, 87..9f, e0..ea, ed..ee, fa..fc
+CP932PAGESZ : con 189; # 40..fc (including 7f)
+CP932CHAR0 : con 16r40;
+
+
+shiftjis := 0;
+page0 := array [256] of { * => BADCHAR };
+cp932 : string;
+dbcsoff := array [256] of { * => -1 };
+
+init(arg : string) : string
+{
+ sys = load Sys Sys->PATH;
+ shiftjis = arg == "shiftjis";
+
+ (error, kana) := getmap("/lib/convcs/jisx0201kana", KANAPAGESZ, KANAPAGES);
+ if (error != nil)
+ return error;
+
+ (error, cp932) = getmap("/lib/convcs/cp932", CP932PAGESZ, CP932PAGES);
+ if (error != nil)
+ return error;
+
+ # jisx0201kana is mapped into 16rA1..16rDF
+ for (i := 0; i < KANAPAGESZ; i++)
+ page0[i + KANACHAR0] = kana[i];
+
+ # 00..7f same as ascii in cp932
+ for (i = 0; i <= 16r7f; i++)
+ page0[i] = i;
+ if (shiftjis) {
+ # shift-jis uses JIS X 0201 for the ASCII range
+ # this is the same as ASCII apart from
+ # 16r5c ('\') maps to yen symbol (¥) and 16r7e ('~') maps to overline (¯)
+ page0['\\'] = '¥';
+ page0['~'] = '¯';
+ }
+
+ # pre-calculate DBCS page numbers to mapping file page numbers
+ # and mark codes in page0 that are DBCS lead bytes
+ pnum := 0;
+ for (i = 16r81; i <= 16r84; i++){
+ page0[i] = -1;
+ dbcsoff[i] = pnum++;
+ }
+ for (i = 16r87; i <= 16r9f; i++){
+ page0[i] = -1;
+ dbcsoff[i] = pnum++;
+ }
+ for (i = 16re0; i <= 16rea; i++) {
+ page0[i] = -1;
+ dbcsoff[i] = pnum++;
+ }
+ if (!shiftjis) {
+ # add in cp932 extensions
+ for (i = 16red; i <= 16ree; i++) {
+ page0[i] = -1;
+ dbcsoff[i] = pnum++;
+ }
+ for (i = 16rfa; i <= 16rfc; i++) {
+ page0[i] = -1;
+ dbcsoff[i] = pnum++;
+ }
+ }
+ return nil;
+}
+
+btos(nil : Convcs->State, b : array of byte, n : int) : (Convcs->State, string, int)
+{
+ nbytes := 0;
+ str := "";
+
+ if (n == -1)
+ n = MAXINT;
+
+ for (i := 0; i < len b && len str < n; i++) {
+ b1 := int b[i];
+ ch := page0[b1];
+ if (ch != -1) {
+ str[len str] = ch;
+ nbytes++;
+ continue;
+ }
+ # DBCS
+ i++;
+ if (i >= len b)
+ break;
+ pnum := dbcsoff[b1];
+ ix := (int b[i]) - CP932CHAR0;
+ if (pnum == -1 || ix < 0 || ix >= CP932PAGESZ)
+ str[len str] = BADCHAR;
+ else
+ str[len str] = cp932[(pnum * CP932PAGESZ)+ix];
+ nbytes += 2;
+ }
+ return (nil, str, nbytes);
+}
+
+getmap(path : string, pgsz, npgs : int) : (string, string)
+{
+ fd := sys->open(path, Sys->OREAD);
+ if (fd == nil)
+ return (sys->sprint("%s: %r", path), nil);
+
+ buf := array[(pgsz * npgs) * Sys->UTFmax] of byte;
+ nread := 0;
+ for (;nread < len buf;) {
+ n := sys->read(fd, buf[nread:], Sys->ATOMICIO);
+ if (n <= 0)
+ break;
+ nread += n;
+ }
+ map := string buf[:nread];
+ if (len map != (pgsz * npgs))
+ return (sys->sprint("%s: bad data", path), nil);
+ return (nil, map);
+}
diff --git a/appl/lib/convcs/cp_btos.b b/appl/lib/convcs/cp_btos.b
new file mode 100644
index 00000000..2d904e21
--- /dev/null
+++ b/appl/lib/convcs/cp_btos.b
@@ -0,0 +1,45 @@
+implement Btos;
+
+include "sys.m";
+include "convcs.m";
+
+sys : Sys;
+codepage : string;
+
+init(arg : string) : string
+{
+ sys = load Sys Sys->PATH;
+ if (arg == nil)
+ return "codepage path required";
+ fd := sys->open(arg, Sys->OREAD);
+ if (fd == nil)
+ return sys->sprint("%s: %r", arg);
+
+ buf := array[Sys->UTFmax * 256] of byte;
+ nread := 0;
+ for (;nread < len buf;) {
+ toread := len buf - nread;
+ n := sys->read(fd, buf[nread:], toread);
+ if (n <= 0)
+ break;
+ nread += n;
+ }
+ codepage = string buf[0:nread];
+ if (len codepage != 256) {
+ codepage = nil;
+ return sys->sprint("%s: bad codepage", arg);
+ }
+ return nil;
+}
+
+btos(nil : Convcs->State, b : array of byte, n : int) : (Convcs->State, string, int)
+{
+ s := "";
+ if (n == -1 || n > len b)
+ # consume all available characters
+ n = len b;
+
+ for (i := 0; i < n; i++)
+ s[len s] = codepage[int b[i]];
+ return (nil, s, n);
+} \ No newline at end of file
diff --git a/appl/lib/convcs/cp_stob.b b/appl/lib/convcs/cp_stob.b
new file mode 100644
index 00000000..40c70792
--- /dev/null
+++ b/appl/lib/convcs/cp_stob.b
@@ -0,0 +1,49 @@
+implement Stob;
+
+include "sys.m";
+include "convcs.m";
+
+sys : Sys;
+map : array of byte;
+
+ERRCHAR : con 16rFFFD;
+
+init(arg : string) : string
+{
+ sys = load Sys Sys->PATH;
+ if (arg == nil)
+ return "codepage path required";
+ fd := sys->open(arg, Sys->OREAD);
+ if (fd == nil)
+ return sys->sprint("%s: %r", arg);
+
+ buf := array[Sys->UTFmax * 256] of byte;
+ nread := 0;
+ for (;nread < len buf;) {
+ toread := len buf - nread;
+ n := sys->read(fd, buf[nread:], toread);
+ if (n <= 0)
+ break;
+ nread += n;
+ }
+ codepage := string buf[0:nread];
+ if (len codepage != 256) {
+ codepage = nil;
+ return sys->sprint("%s: bad codepage", arg);
+ }
+ buf = nil;
+ map = array[16r10000] of { * => byte '?' };
+ for (i := 0; i < 256; i++)
+ map[codepage[i]] = byte i;
+ return nil;
+}
+
+stob(nil : Convcs->State, str : string) : (Convcs->State, array of byte)
+{
+ b := array [len str] of byte;
+ n := len str;
+
+ for (i := 0; i < n; i++)
+ b[i] = map[str[i]];
+ return (nil, b);
+} \ No newline at end of file
diff --git a/appl/lib/convcs/euc-jp_btos.b b/appl/lib/convcs/euc-jp_btos.b
new file mode 100644
index 00000000..5a5aa9ec
--- /dev/null
+++ b/appl/lib/convcs/euc-jp_btos.b
@@ -0,0 +1,162 @@
+implement Btos;
+
+# EUC-JP is based on ISO2022 but only uses the 8 bit stateless encoding.
+# Thus, only the following ISO2022 shift functions are used:
+# SINGLE-SHIFT TWO
+# SINGLE-SHIFT THREE
+#
+# The initial state is G0 mapped into GL and G1 mapped into GR
+# SINGLE-SHIFT TWO maps G2 into GR for one code-point encoding
+# SINGLE-SHIFT THREE maps G3 into GR for one code-point encoding
+#
+# EUC-JP has pre-assigned code elements (G0..G3) that are never re-assigned
+# by means on ISO2022 code-identification functions (escape sequences)
+#
+# G0 = ASCII
+# G1 = JIS X 0208
+# G2 = JIS X 0201 Kana
+# G3 = JIS X 0212
+
+include "sys.m";
+include "convcs.m";
+
+sys : Sys;
+
+SS2 : con 16r8E; # ISO2022 SINGLE-SHIFT TWO
+SS3 : con 16r8F; # ISO2022 SINGLE-SHIFT THREE
+
+MAXINT : con 16r7fffffff;
+BADCHAR : con 16rFFFD;
+
+G1PATH : con "/lib/convcs/jisx0208-1997";
+G2PATH : con "/lib/convcs/jisx0201kana";
+G3PATH : con "/lib/convcs/jisx0212";
+
+g1map : string;
+g2map : string;
+g3map : string;
+
+G1PAGESZ : con 94;
+G1NPAGES : con 84;
+G1PAGE0 : con 16rA1;
+G1CHAR0 : con 16rA1;
+
+G2PAGESZ : con 63;
+G2NPAGES : con 1;
+G2CHAR0 : con 16rA1;
+
+G3PAGESZ : con 94;
+G3NPAGES : con 77;
+G3PAGE0 : con 16rA1;
+G3CHAR0 : con 16rA1;
+
+init(nil : string) : string
+{
+ sys = load Sys Sys->PATH;
+
+ error := "";
+ (error, g1map) = getmap(G1PATH, G1PAGESZ, G1NPAGES);
+ if (error != nil)
+ return error;
+ (error, g2map) = getmap(G2PATH, G2PAGESZ, G2NPAGES);
+ if (error != nil)
+ return error;
+ (error, g3map) = getmap(G3PATH, G3PAGESZ, G3NPAGES);
+ return error;
+}
+
+getmap(path : string, pgsz, npgs : int) : (string, string)
+{
+ fd := sys->open(path, Sys->OREAD);
+ if (fd == nil)
+ return (sys->sprint("%s: %r", path), nil);
+
+ buf := array[(pgsz * npgs) * Sys->UTFmax] of byte;
+ nread := 0;
+ for (;nread < len buf;) {
+ n := sys->read(fd, buf[nread:], Sys->ATOMICIO);
+ if (n <= 0)
+ break;
+ nread += n;
+ }
+ map := string buf[:nread];
+ if (len map != (pgsz * npgs))
+ return (sys->sprint("%s: bad data", path), nil);
+ return (nil, map);
+}
+
+btos(nil : Convcs->State, b : array of byte, n : int) : (Convcs->State, string, int)
+{
+ nbytes := 0;
+ str := "";
+
+ if (n == -1)
+ n = MAXINT;
+
+ codelen := 1;
+ codeix := 0;
+ G0, G1, G2, G3 : con iota;
+ state := G0;
+ bytes := array [3] of int;
+
+ while (len str < n) {
+ for (i := nbytes + codeix; i < len b && codeix < codelen; i++)
+ bytes[codeix++]= int b[i];
+
+ if (codeix != codelen)
+ break;
+
+ case state {
+ G0 =>
+ case bytes[0] {
+ 0 to 16r7f =>
+ str[len str] = bytes[0];
+ G1PAGE0 to G1PAGE0+G1NPAGES =>
+ state = G1;
+ codelen = 2;
+ continue;
+ SS2 =>
+ state = G2;
+ codelen = 2;
+ continue;
+ SS3 =>
+ state = G3;
+ codelen = 3;
+ continue;
+ * =>
+ str[len str] = BADCHAR;
+ }
+ G1 =>
+ # double byte encoding
+ page := bytes[0] - G1PAGE0;
+ char := bytes[1] - G1CHAR0;
+ str[len str] = g1map[(page * G1PAGESZ) + char];
+ G2 =>
+ # single byte encoding (byte 0 == SS2)
+ char := bytes[1] - G2CHAR0;
+ if (char < 0 || char >= len g2map)
+ char = BADCHAR;
+ else
+ char = g2map[char];
+ str[len str] = char;
+ G3 =>
+ # double byte encoding (byte 0 == SS3)
+ page := bytes[1] - G3PAGE0;
+ char := bytes[2] - G3CHAR0;
+ if (page < 0 || page >= G3NPAGES) {
+ # first byte is wrong - backup
+ i--;
+ str[len str] = BADCHAR;
+ } else if (char >= G3PAGESZ)
+ str[len str] = BADCHAR;
+ else
+ str[len str] = g3map[(page * G3PAGESZ)+char];
+ }
+
+ state = G0;
+ nbytes = i;
+ codelen = 1;
+ codeix = 0;
+ }
+ return (nil, str, nbytes);
+} \ No newline at end of file
diff --git a/appl/lib/convcs/gb2312_btos.b b/appl/lib/convcs/gb2312_btos.b
new file mode 100644
index 00000000..8a06a75f
--- /dev/null
+++ b/appl/lib/convcs/gb2312_btos.b
@@ -0,0 +1,70 @@
+implement Btos;
+
+include "sys.m";
+include "convcs.m";
+
+GBMAX : con 8795;
+
+GBDATA : con "/lib/convcs/gb2312";
+
+MAXINT : con 16r7fffffff;
+BADCHAR : con 16rFFFD;
+
+gbmap := "";
+
+init(nil : string): string
+{
+ sys := load Sys Sys->PATH;
+ fd := sys->open(GBDATA, Sys->OREAD);
+ if (fd == nil)
+ return sys->sprint("%s: %r", GBDATA);
+
+ buf := array[GBMAX * Sys->UTFmax] of byte;
+ nread := 0;
+ for (;nread < len buf;) {
+ n := sys->read(fd, buf[nread:], Sys->ATOMICIO);
+ if (n <= 0)
+ break;
+ nread += n;
+ }
+ gbmap = string buf[:nread];
+ if (len gbmap != GBMAX) {
+ gbmap = nil;
+ return sys->sprint("%s: corrupt data", GBDATA);
+ }
+ return nil;
+}
+
+btos(nil : Convcs->State, b : array of byte, n : int) : (Convcs->State, string, int)
+{
+ nbytes := 0;
+ str := "";
+
+ if (n == -1)
+ n = MAXINT;
+
+ font := -1;
+ for (i := 0; i < len b && len str < n; i++) {
+ c := int b[i];
+ ch := Sys->UTFerror;
+ if (font == -1) {
+ # idle state
+ if (c >= 16rA1) {
+ font = c;
+ continue;
+ }
+ ch = c;
+ } else {
+ # seen a font spec
+ if (c >= 16rA1) {
+ ix := (font - 16rA0)*100 + (c-16rA0);
+ ch = gbmap[ix];
+ }
+ font = -1;
+ }
+ str[len str] = ch;
+ nbytes = i + 1;
+ }
+ return (nil, str, nbytes);
+}
+
diff --git a/appl/lib/convcs/genbig5.b b/appl/lib/convcs/genbig5.b
new file mode 100644
index 00000000..0b67ea63
--- /dev/null
+++ b/appl/lib/convcs/genbig5.b
@@ -0,0 +1,1790 @@
+# generate the Big5 character set converter data file
+implement GenBIG5;
+
+include "sys.m";
+include "draw.m";
+
+BIG5DATA: con "/lib/convcs/big5";
+
+GenBIG5 : module {
+ init : fn (ctxt : ref Draw->Context, args : list of string);
+};
+
+init(nil : ref Draw->Context, nil : list of string)
+{
+ sys := load Sys Sys->PATH;
+ fd := sys->create(BIG5DATA, Sys->OWRITE, 8r644);
+ if (fd == nil) {
+ sys->print("cannot create %s: %r\n", BIG5DATA);
+ return;
+ }
+ s := "";
+ for (i := 0; i < len tabbig5; i++)
+ s[len s] = tabbig5[i];
+
+ buf := array of byte s;
+ for (i = 0; i < len buf;) {
+ towrite := Sys->ATOMICIO;
+ if (len buf - i < Sys->ATOMICIO)
+ towrite = len buf -i;
+ n := sys->write(fd, buf[i:], towrite);
+ if (n <= 0) {
+ sys->print("error writing %s: %r", BIG5DATA);
+ return;
+ }
+ i += n;
+ }
+}
+
+
+ERRchar : con 16rFFFD;
+
+tabbig5 := array[] of {
+16r3000,16rff0c,16r3001,16r3002,16rff0e,16r30fb,16rff1b,16rff1a,
+16rff1f,16rff01,16rfe30,16r2026,16r2025,16rfe50,16rfe51,16rfe52,
+16r00b7,16rfe54,16rfe55,16rfe56,16rfe57,16rfe32,16r2013,16rfe31,
+16r2014,16rfe33,ERRchar,16rfe34,16rfe4f,16rff08,16rff09,16rfe35,
+16rfe36,16rff5b,16rff5d,16rfe37,16rfe38,16r3014,16r3015,16rfe39,
+16rfe3a,16r3010,16r3011,16rfe3b,16rfe3c,16r300a,16r300b,16rfe3d,
+16rfe3e,16r3008,16r3009,16rfe3f,16rfe40,16r300c,16r300d,16rfe41,
+16rfe42,16r300e,16r300f,16rfe43,16rfe44,16rfe59,16rfe5a,16rfe5b,
+16rfe5c,16rfe5d,16rfe5e,16r2018,16r2019,16r201c,16r201d,16r301d,
+16r301e,16r2035,16r2032,16rff03,16rff06,16rff0a,16r203b,16r00a7,
+16r3003,16r25cb,16r25cf,16r25b3,16r25b2,16r25ce,16r2606,16r2605,
+16r25c7,16r25c6,16r25a1,16r25a0,16r25bd,16r25bc,16r32a3,16r2105,
+16r203e,ERRchar,16rff3f,ERRchar,16rfe49,16rfe4a,16rfe4d,16rfe4e,
+16rfe4b,16rfe4c,16r0023,16r0026,16r002a,16rff0b,16rff0d,16r00d7,
+16r00f7,16r00b1,16r221a,16rff1c,16rff1e,16rff1d,16r2264,16r2265,
+16r2260,16r221e,16r2252,16r2261,16rfe62,16rfe63,16rfe64,16rfe65,
+16rfe66,16r223c,16r2229,16r222a,16r22a5,16r2220,16r221f,16r22bf,
+16r33d2,16r33d1,16r222b,16r222e,16r2235,16r2234,16r2640,16r2642,
+16r2641,16r2609,16r2191,16r2193,16r2190,16r2192,16r2196,16r2197,
+16r2199,16r2198,16r2225,16r2223,ERRchar,ERRchar,16rff0f,16rff3c,
+16rff04,16r00a5,16r3012,16r00a2,16r00a3,16rff05,16rff20,16r2103,
+16r2109,16r0024,16r0025,16r0040,16r33d5,16r339c,16r339d,16r339e,
+16r33ce,16r33a1,16r338e,16r338f,16r33c4,16r00b0,16r5159,16r515b,
+16r515e,16r515d,16r5161,16r5163,16r55e7,16r74e9,16r7cce,16r2581,
+16r2582,16r2583,16r2584,16r2585,16r2586,16r2587,16r2588,16r258f,
+16r258e,16r258d,16r258c,16r258b,16r258a,16r2589,16r253c,16r2534,
+16r252c,16r2524,16r251c,16r2594,16r2500,16r2502,16r2595,16r250c,
+16r2510,16r2514,16r2518,16r256d,16r256e,16r2570,16r256f,16r2550,
+16r255e,16r256a,16r2561,16r25e2,16r25e3,16r25e5,16r25e4,16r2571,
+16r2572,16r2573,16rff10,16rff11,16rff12,16rff13,16rff14,16rff15,
+16rff16,16rff17,16rff18,16rff19,16r2160,16r2161,16r2162,16r2163,
+16r2164,16r2165,16r2166,16r2167,16r2168,16r2169,16r3021,16r3022,
+16r3023,16r3024,16r3025,16r3026,16r3027,16r3028,16r3029,ERRchar,
+16r5344,ERRchar,16rff21,16rff22,16rff23,16rff24,16rff25,16rff26,
+16rff27,16rff28,16rff29,16rff2a,16rff2b,16rff2c,16rff2d,16rff2e,
+16rff2f,16rff30,16rff31,16rff32,16rff33,16rff34,16rff35,16rff36,
+16rff37,16rff38,16rff39,16rff3a,16rff41,16rff42,16rff43,16rff44,
+16rff45,16rff46,16rff47,16rff48,16rff49,16rff4a,16rff4b,16rff4c,
+16rff4d,16rff4e,16rff4f,16rff50,16rff51,16rff52,16rff53,16rff54,
+16rff55,16rff56,16rff57,16rff58,16rff59,16rff5a,16r0391,16r0392,
+16r0393,16r0394,16r0395,16r0396,16r0397,16r0398,16r0399,16r039a,
+16r039b,16r039c,16r039d,16r039e,16r039f,16r03a0,16r03a1,16r03a3,
+16r03a4,16r03a5,16r03a6,16r03a7,16r03a8,16r03a9,16r03b1,16r03b2,
+16r03b3,16r03b4,16r03b5,16r03b6,16r03b7,16r03b8,16r03b9,16r03ba,
+16r03bb,16r03bc,16r03bd,16r03be,16r03bf,16r03c0,16r03c1,16r03c3,
+16r03c4,16r03c5,16r03c6,16r03c7,16r03c8,16r03c9,16r3105,16r3106,
+16r3107,16r3108,16r3109,16r310a,16r310b,16r310c,16r310d,16r310e,
+16r310f,16r3110,16r3111,16r3112,16r3113,16r3114,16r3115,16r3116,
+16r3117,16r3118,16r3119,16r311a,16r311b,16r311c,16r311d,16r311e,
+16r311f,16r3120,16r3121,16r3122,16r3123,16r3124,16r3125,16r3126,
+16r3127,16r3128,16r3129,16r02d9,16r02c9,16r02ca,16r02c7,16r02cb,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r4e00,
+16r4e59,16r4e01,16r4e03,16r4e43,16r4e5d,16r4e86,16r4e8c,16r4eba,
+16r513f,16r5165,16r516b,16r51e0,16r5200,16r5201,16r529b,16r5315,
+16r5341,16r535c,16r53c8,16r4e09,16r4e0b,16r4e08,16r4e0a,16r4e2b,
+16r4e38,16r51e1,16r4e45,16r4e48,16r4e5f,16r4e5e,16r4e8e,16r4ea1,
+16r5140,16r5203,16r52fa,16r5343,16r53c9,16r53e3,16r571f,16r58eb,
+16r5915,16r5927,16r5973,16r5b50,16r5b51,16r5b53,16r5bf8,16r5c0f,
+16r5c22,16r5c38,16r5c71,16r5ddd,16r5de5,16r5df1,16r5df2,16r5df3,
+16r5dfe,16r5e72,16r5efe,16r5f0b,16r5f13,16r624d,16r4e11,16r4e10,
+16r4e0d,16r4e2d,16r4e30,16r4e39,16r4e4b,16r5c39,16r4e88,16r4e91,
+16r4e95,16r4e92,16r4e94,16r4ea2,16r4ec1,16r4ec0,16r4ec3,16r4ec6,
+16r4ec7,16r4ecd,16r4eca,16r4ecb,16r4ec4,16r5143,16r5141,16r5167,
+16r516d,16r516e,16r516c,16r5197,16r51f6,16r5206,16r5207,16r5208,
+16r52fb,16r52fe,16r52ff,16r5316,16r5339,16r5348,16r5347,16r5345,
+16r535e,16r5384,16r53cb,16r53ca,16r53cd,16r58ec,16r5929,16r592b,
+16r592a,16r592d,16r5b54,16r5c11,16r5c24,16r5c3a,16r5c6f,16r5df4,
+16r5e7b,16r5eff,16r5f14,16r5f15,16r5fc3,16r6208,16r6236,16r624b,
+16r624e,16r652f,16r6587,16r6597,16r65a4,16r65b9,16r65e5,16r66f0,
+16r6708,16r6728,16r6b20,16r6b62,16r6b79,16r6bcb,16r6bd4,16r6bdb,
+16r6c0f,16r6c34,16r706b,16r722a,16r7236,16r723b,16r7247,16r7259,
+16r725b,16r72ac,16r738b,16r4e19,16r4e16,16r4e15,16r4e14,16r4e18,
+16r4e3b,16r4e4d,16r4e4f,16r4e4e,16r4ee5,16r4ed8,16r4ed4,16r4ed5,
+16r4ed6,16r4ed7,16r4ee3,16r4ee4,16r4ed9,16r4ede,16r5145,16r5144,
+16r5189,16r518a,16r51ac,16r51f9,16r51fa,16r51f8,16r520a,16r52a0,
+16r529f,16r5305,16r5306,16r5317,16r531d,16r4edf,16r534a,16r5349,
+16r5361,16r5360,16r536f,16r536e,16r53bb,16r53ef,16r53e4,16r53f3,
+16r53ec,16r53ee,16r53e9,16r53e8,16r53fc,16r53f8,16r53f5,16r53eb,
+16r53e6,16r53ea,16r53f2,16r53f1,16r53f0,16r53e5,16r53ed,16r53fb,
+16r56db,16r56da,16r5916,16r592e,16r5931,16r5974,16r5976,16r5b55,
+16r5b83,16r5c3c,16r5de8,16r5de7,16r5de6,16r5e02,16r5e03,16r5e73,
+16r5e7c,16r5f01,16r5f18,16r5f17,16r5fc5,16r620a,16r6253,16r6254,
+16r6252,16r6251,16r65a5,16r65e6,16r672e,16r672c,16r672a,16r672b,
+16r672d,16r6b63,16r6bcd,16r6c11,16r6c10,16r6c38,16r6c41,16r6c40,
+16r6c3e,16r72af,16r7384,16r7389,16r74dc,16r74e6,16r7518,16r751f,
+16r7528,16r7529,16r7530,16r7531,16r7532,16r7533,16r758b,16r767d,
+16r76ae,16r76bf,16r76ee,16r77db,16r77e2,16r77f3,16r793a,16r79be,
+16r7a74,16r7acb,16r4e1e,16r4e1f,16r4e52,16r4e53,16r4e69,16r4e99,
+16r4ea4,16r4ea6,16r4ea5,16r4eff,16r4f09,16r4f19,16r4f0a,16r4f15,
+16r4f0d,16r4f10,16r4f11,16r4f0f,16r4ef2,16r4ef6,16r4efb,16r4ef0,
+16r4ef3,16r4efd,16r4f01,16r4f0b,16r5149,16r5147,16r5146,16r5148,
+16r5168,16r5171,16r518d,16r51b0,16r5217,16r5211,16r5212,16r520e,
+16r5216,16r52a3,16r5308,16r5321,16r5320,16r5370,16r5371,16r5409,
+16r540f,16r540c,16r540a,16r5410,16r5401,16r540b,16r5404,16r5411,
+16r540d,16r5408,16r5403,16r540e,16r5406,16r5412,16r56e0,16r56de,
+16r56dd,16r5733,16r5730,16r5728,16r572d,16r572c,16r572f,16r5729,
+16r5919,16r591a,16r5937,16r5938,16r5984,16r5978,16r5983,16r597d,
+16r5979,16r5982,16r5981,16r5b57,16r5b58,16r5b87,16r5b88,16r5b85,
+16r5b89,16r5bfa,16r5c16,16r5c79,16r5dde,16r5e06,16r5e76,16r5e74,
+16r5f0f,16r5f1b,16r5fd9,16r5fd6,16r620e,16r620c,16r620d,16r6210,
+16r6263,16r625b,16r6258,16r6536,16r65e9,16r65e8,16r65ec,16r65ed,
+16r66f2,16r66f3,16r6709,16r673d,16r6734,16r6731,16r6735,16r6b21,
+16r6b64,16r6b7b,16r6c16,16r6c5d,16r6c57,16r6c59,16r6c5f,16r6c60,
+16r6c50,16r6c55,16r6c61,16r6c5b,16r6c4d,16r6c4e,16r7070,16r725f,
+16r725d,16r767e,16r7af9,16r7c73,16r7cf8,16r7f36,16r7f8a,16r7fbd,
+16r8001,16r8003,16r800c,16r8012,16r8033,16r807f,16r8089,16r808b,
+16r808c,16r81e3,16r81ea,16r81f3,16r81fc,16r820c,16r821b,16r821f,
+16r826e,16r8272,16r827e,16r866b,16r8840,16r884c,16r8863,16r897f,
+16r9621,16r4e32,16r4ea8,16r4f4d,16r4f4f,16r4f47,16r4f57,16r4f5e,
+16r4f34,16r4f5b,16r4f55,16r4f30,16r4f50,16r4f51,16r4f3d,16r4f3a,
+16r4f38,16r4f43,16r4f54,16r4f3c,16r4f46,16r4f63,16r4f5c,16r4f60,
+16r4f2f,16r4f4e,16r4f36,16r4f59,16r4f5d,16r4f48,16r4f5a,16r514c,
+16r514b,16r514d,16r5175,16r51b6,16r51b7,16r5225,16r5224,16r5229,
+16r522a,16r5228,16r52ab,16r52a9,16r52aa,16r52ac,16r5323,16r5373,
+16r5375,16r541d,16r542d,16r541e,16r543e,16r5426,16r544e,16r5427,
+16r5446,16r5443,16r5433,16r5448,16r5442,16r541b,16r5429,16r544a,
+16r5439,16r543b,16r5438,16r542e,16r5435,16r5436,16r5420,16r543c,
+16r5440,16r5431,16r542b,16r541f,16r542c,16r56ea,16r56f0,16r56e4,
+16r56eb,16r574a,16r5751,16r5740,16r574d,16r5747,16r574e,16r573e,
+16r5750,16r574f,16r573b,16r58ef,16r593e,16r599d,16r5992,16r59a8,
+16r599e,16r59a3,16r5999,16r5996,16r598d,16r59a4,16r5993,16r598a,
+16r59a5,16r5b5d,16r5b5c,16r5b5a,16r5b5b,16r5b8c,16r5b8b,16r5b8f,
+16r5c2c,16r5c40,16r5c41,16r5c3f,16r5c3e,16r5c90,16r5c91,16r5c94,
+16r5c8c,16r5deb,16r5e0c,16r5e8f,16r5e87,16r5e8a,16r5ef7,16r5f04,
+16r5f1f,16r5f64,16r5f62,16r5f77,16r5f79,16r5fd8,16r5fcc,16r5fd7,
+16r5fcd,16r5ff1,16r5feb,16r5ff8,16r5fea,16r6212,16r6211,16r6284,
+16r6297,16r6296,16r6280,16r6276,16r6289,16r626d,16r628a,16r627c,
+16r627e,16r6279,16r6273,16r6292,16r626f,16r6298,16r626e,16r6295,
+16r6293,16r6291,16r6286,16r6539,16r653b,16r6538,16r65f1,16r66f4,
+16r675f,16r674e,16r674f,16r6750,16r6751,16r675c,16r6756,16r675e,
+16r6749,16r6746,16r6760,16r6753,16r6757,16r6b65,16r6bcf,16r6c42,
+16r6c5e,16r6c99,16r6c81,16r6c88,16r6c89,16r6c85,16r6c9b,16r6c6a,
+16r6c7a,16r6c90,16r6c70,16r6c8c,16r6c68,16r6c96,16r6c92,16r6c7d,
+16r6c83,16r6c72,16r6c7e,16r6c74,16r6c86,16r6c76,16r6c8d,16r6c94,
+16r6c98,16r6c82,16r7076,16r707c,16r707d,16r7078,16r7262,16r7261,
+16r7260,16r72c4,16r72c2,16r7396,16r752c,16r752b,16r7537,16r7538,
+16r7682,16r76ef,16r77e3,16r79c1,16r79c0,16r79bf,16r7a76,16r7cfb,
+16r7f55,16r8096,16r8093,16r809d,16r8098,16r809b,16r809a,16r80b2,
+16r826f,16r8292,16r828b,16r828d,16r898b,16r89d2,16r8a00,16r8c37,
+16r8c46,16r8c55,16r8c9d,16r8d64,16r8d70,16r8db3,16r8eab,16r8eca,
+16r8f9b,16r8fb0,16r8fc2,16r8fc6,16r8fc5,16r8fc4,16r5de1,16r9091,
+16r90a2,16r90aa,16r90a6,16r90a3,16r9149,16r91c6,16r91cc,16r9632,
+16r962e,16r9631,16r962a,16r962c,16r4e26,16r4e56,16r4e73,16r4e8b,
+16r4e9b,16r4e9e,16r4eab,16r4eac,16r4f6f,16r4f9d,16r4f8d,16r4f73,
+16r4f7f,16r4f6c,16r4f9b,16r4f8b,16r4f86,16r4f83,16r4f70,16r4f75,
+16r4f88,16r4f69,16r4f7b,16r4f96,16r4f7e,16r4f8f,16r4f91,16r4f7a,
+16r5154,16r5152,16r5155,16r5169,16r5177,16r5176,16r5178,16r51bd,
+16r51fd,16r523b,16r5238,16r5237,16r523a,16r5230,16r522e,16r5236,
+16r5241,16r52be,16r52bb,16r5352,16r5354,16r5353,16r5351,16r5366,
+16r5377,16r5378,16r5379,16r53d6,16r53d4,16r53d7,16r5473,16r5475,
+16r5496,16r5478,16r5495,16r5480,16r547b,16r5477,16r5484,16r5492,
+16r5486,16r547c,16r5490,16r5471,16r5476,16r548c,16r549a,16r5462,
+16r5468,16r548b,16r547d,16r548e,16r56fa,16r5783,16r5777,16r576a,
+16r5769,16r5761,16r5766,16r5764,16r577c,16r591c,16r5949,16r5947,
+16r5948,16r5944,16r5954,16r59be,16r59bb,16r59d4,16r59b9,16r59ae,
+16r59d1,16r59c6,16r59d0,16r59cd,16r59cb,16r59d3,16r59ca,16r59af,
+16r59b3,16r59d2,16r59c5,16r5b5f,16r5b64,16r5b63,16r5b97,16r5b9a,
+16r5b98,16r5b9c,16r5b99,16r5b9b,16r5c1a,16r5c48,16r5c45,16r5c46,
+16r5cb7,16r5ca1,16r5cb8,16r5ca9,16r5cab,16r5cb1,16r5cb3,16r5e18,
+16r5e1a,16r5e16,16r5e15,16r5e1b,16r5e11,16r5e78,16r5e9a,16r5e97,
+16r5e9c,16r5e95,16r5e96,16r5ef6,16r5f26,16r5f27,16r5f29,16r5f80,
+16r5f81,16r5f7f,16r5f7c,16r5fdd,16r5fe0,16r5ffd,16r5ff5,16r5fff,
+16r600f,16r6014,16r602f,16r6035,16r6016,16r602a,16r6015,16r6021,
+16r6027,16r6029,16r602b,16r601b,16r6216,16r6215,16r623f,16r623e,
+16r6240,16r627f,16r62c9,16r62cc,16r62c4,16r62bf,16r62c2,16r62b9,
+16r62d2,16r62db,16r62ab,16r62d3,16r62d4,16r62cb,16r62c8,16r62a8,
+16r62bd,16r62bc,16r62d0,16r62d9,16r62c7,16r62cd,16r62b5,16r62da,
+16r62b1,16r62d8,16r62d6,16r62d7,16r62c6,16r62ac,16r62ce,16r653e,
+16r65a7,16r65bc,16r65fa,16r6614,16r6613,16r660c,16r6606,16r6602,
+16r660e,16r6600,16r660f,16r6615,16r660a,16r6607,16r670d,16r670b,
+16r676d,16r678b,16r6795,16r6771,16r679c,16r6773,16r6777,16r6787,
+16r679d,16r6797,16r676f,16r6770,16r677f,16r6789,16r677e,16r6790,
+16r6775,16r679a,16r6793,16r677c,16r676a,16r6772,16r6b23,16r6b66,
+16r6b67,16r6b7f,16r6c13,16r6c1b,16r6ce3,16r6ce8,16r6cf3,16r6cb1,
+16r6ccc,16r6ce5,16r6cb3,16r6cbd,16r6cbe,16r6cbc,16r6ce2,16r6cab,
+16r6cd5,16r6cd3,16r6cb8,16r6cc4,16r6cb9,16r6cc1,16r6cae,16r6cd7,
+16r6cc5,16r6cf1,16r6cbf,16r6cbb,16r6ce1,16r6cdb,16r6cca,16r6cac,
+16r6cef,16r6cdc,16r6cd6,16r6ce0,16r7095,16r708e,16r7092,16r708a,
+16r7099,16r722c,16r722d,16r7238,16r7248,16r7267,16r7269,16r72c0,
+16r72ce,16r72d9,16r72d7,16r72d0,16r73a9,16r73a8,16r739f,16r73ab,
+16r73a5,16r753d,16r759d,16r7599,16r759a,16r7684,16r76c2,16r76f2,
+16r76f4,16r77e5,16r77fd,16r793e,16r7940,16r7941,16r79c9,16r79c8,
+16r7a7a,16r7a79,16r7afa,16r7cfe,16r7f54,16r7f8c,16r7f8b,16r8005,
+16r80ba,16r80a5,16r80a2,16r80b1,16r80a1,16r80ab,16r80a9,16r80b4,
+16r80aa,16r80af,16r81e5,16r81fe,16r820d,16r82b3,16r829d,16r8299,
+16r82ad,16r82bd,16r829f,16r82b9,16r82b1,16r82ac,16r82a5,16r82af,
+16r82b8,16r82a3,16r82b0,16r82be,16r82b7,16r864e,16r8671,16r521d,
+16r8868,16r8ecb,16r8fce,16r8fd4,16r8fd1,16r90b5,16r90b8,16r90b1,
+16r90b6,16r91c7,16r91d1,16r9577,16r9580,16r961c,16r9640,16r963f,
+16r963b,16r9644,16r9642,16r96b9,16r96e8,16r9752,16r975e,16r4e9f,
+16r4ead,16r4eae,16r4fe1,16r4fb5,16r4faf,16r4fbf,16r4fe0,16r4fd1,
+16r4fcf,16r4fdd,16r4fc3,16r4fb6,16r4fd8,16r4fdf,16r4fca,16r4fd7,
+16r4fae,16r4fd0,16r4fc4,16r4fc2,16r4fda,16r4fce,16r4fde,16r4fb7,
+16r5157,16r5192,16r5191,16r51a0,16r524e,16r5243,16r524a,16r524d,
+16r524c,16r524b,16r5247,16r52c7,16r52c9,16r52c3,16r52c1,16r530d,
+16r5357,16r537b,16r539a,16r53db,16r54ac,16r54c0,16r54a8,16r54ce,
+16r54c9,16r54b8,16r54a6,16r54b3,16r54c7,16r54c2,16r54bd,16r54aa,
+16r54c1,16r54c4,16r54c8,16r54af,16r54ab,16r54b1,16r54bb,16r54a9,
+16r54a7,16r54bf,16r56ff,16r5782,16r578b,16r57a0,16r57a3,16r57a2,
+16r57ce,16r57ae,16r5793,16r5955,16r5951,16r594f,16r594e,16r5950,
+16r59dc,16r59d8,16r59ff,16r59e3,16r59e8,16r5a03,16r59e5,16r59ea,
+16r59da,16r59e6,16r5a01,16r59fb,16r5b69,16r5ba3,16r5ba6,16r5ba4,
+16r5ba2,16r5ba5,16r5c01,16r5c4e,16r5c4f,16r5c4d,16r5c4b,16r5cd9,
+16r5cd2,16r5df7,16r5e1d,16r5e25,16r5e1f,16r5e7d,16r5ea0,16r5ea6,
+16r5efa,16r5f08,16r5f2d,16r5f65,16r5f88,16r5f85,16r5f8a,16r5f8b,
+16r5f87,16r5f8c,16r5f89,16r6012,16r601d,16r6020,16r6025,16r600e,
+16r6028,16r604d,16r6070,16r6068,16r6062,16r6046,16r6043,16r606c,
+16r606b,16r606a,16r6064,16r6241,16r62dc,16r6316,16r6309,16r62fc,
+16r62ed,16r6301,16r62ee,16r62fd,16r6307,16r62f1,16r62f7,16r62ef,
+16r62ec,16r62fe,16r62f4,16r6311,16r6302,16r653f,16r6545,16r65ab,
+16r65bd,16r65e2,16r6625,16r662d,16r6620,16r6627,16r662f,16r661f,
+16r6628,16r6631,16r6624,16r66f7,16r67ff,16r67d3,16r67f1,16r67d4,
+16r67d0,16r67ec,16r67b6,16r67af,16r67f5,16r67e9,16r67ef,16r67c4,
+16r67d1,16r67b4,16r67da,16r67e5,16r67b8,16r67cf,16r67de,16r67f3,
+16r67b0,16r67d9,16r67e2,16r67dd,16r67d2,16r6b6a,16r6b83,16r6b86,
+16r6bb5,16r6bd2,16r6bd7,16r6c1f,16r6cc9,16r6d0b,16r6d32,16r6d2a,
+16r6d41,16r6d25,16r6d0c,16r6d31,16r6d1e,16r6d17,16r6d3b,16r6d3d,
+16r6d3e,16r6d36,16r6d1b,16r6cf5,16r6d39,16r6d27,16r6d38,16r6d29,
+16r6d2e,16r6d35,16r6d0e,16r6d2b,16r70ab,16r70ba,16r70b3,16r70ac,
+16r70af,16r70ad,16r70b8,16r70ae,16r70a4,16r7230,16r7272,16r726f,
+16r7274,16r72e9,16r72e0,16r72e1,16r73b7,16r73ca,16r73bb,16r73b2,
+16r73cd,16r73c0,16r73b3,16r751a,16r752d,16r754f,16r754c,16r754e,
+16r754b,16r75ab,16r75a4,16r75a5,16r75a2,16r75a3,16r7678,16r7686,
+16r7687,16r7688,16r76c8,16r76c6,16r76c3,16r76c5,16r7701,16r76f9,
+16r76f8,16r7709,16r770b,16r76fe,16r76fc,16r7707,16r77dc,16r7802,
+16r7814,16r780c,16r780d,16r7946,16r7949,16r7948,16r7947,16r79b9,
+16r79ba,16r79d1,16r79d2,16r79cb,16r7a7f,16r7a81,16r7aff,16r7afd,
+16r7c7d,16r7d02,16r7d05,16r7d00,16r7d09,16r7d07,16r7d04,16r7d06,
+16r7f38,16r7f8e,16r7fbf,16r8004,16r8010,16r800d,16r8011,16r8036,
+16r80d6,16r80e5,16r80da,16r80c3,16r80c4,16r80cc,16r80e1,16r80db,
+16r80ce,16r80de,16r80e4,16r80dd,16r81f4,16r8222,16r82e7,16r8303,
+16r8305,16r82e3,16r82db,16r82e6,16r8304,16r82e5,16r8302,16r8309,
+16r82d2,16r82d7,16r82f1,16r8301,16r82dc,16r82d4,16r82d1,16r82de,
+16r82d3,16r82df,16r82ef,16r8306,16r8650,16r8679,16r867b,16r867a,
+16r884d,16r886b,16r8981,16r89d4,16r8a08,16r8a02,16r8a03,16r8c9e,
+16r8ca0,16r8d74,16r8d73,16r8db4,16r8ecd,16r8ecc,16r8ff0,16r8fe6,
+16r8fe2,16r8fea,16r8fe5,16r8fed,16r8feb,16r8fe4,16r8fe8,16r90ca,
+16r90ce,16r90c1,16r90c3,16r914b,16r914a,16r91cd,16r9582,16r9650,
+16r964b,16r964c,16r964d,16r9762,16r9769,16r97cb,16r97ed,16r97f3,
+16r9801,16r98a8,16r98db,16r98df,16r9996,16r9999,16r4e58,16r4eb3,
+16r500c,16r500d,16r5023,16r4fef,16r5026,16r5025,16r4ff8,16r5029,
+16r5016,16r5006,16r503c,16r501f,16r501a,16r5012,16r5011,16r4ffa,
+16r5000,16r5014,16r5028,16r4ff1,16r5021,16r500b,16r5019,16r5018,
+16r4ff3,16r4fee,16r502d,16r502a,16r4ffe,16r502b,16r5009,16r517c,
+16r51a4,16r51a5,16r51a2,16r51cd,16r51cc,16r51c6,16r51cb,16r5256,
+16r525c,16r5254,16r525b,16r525d,16r532a,16r537f,16r539f,16r539d,
+16r53df,16r54e8,16r5510,16r5501,16r5537,16r54fc,16r54e5,16r54f2,
+16r5506,16r54fa,16r5514,16r54e9,16r54ed,16r54e1,16r5509,16r54ee,
+16r54ea,16r54e6,16r5527,16r5507,16r54fd,16r550f,16r5703,16r5704,
+16r57c2,16r57d4,16r57cb,16r57c3,16r5809,16r590f,16r5957,16r5958,
+16r595a,16r5a11,16r5a18,16r5a1c,16r5a1f,16r5a1b,16r5a13,16r59ec,
+16r5a20,16r5a23,16r5a29,16r5a25,16r5a0c,16r5a09,16r5b6b,16r5c58,
+16r5bb0,16r5bb3,16r5bb6,16r5bb4,16r5bae,16r5bb5,16r5bb9,16r5bb8,
+16r5c04,16r5c51,16r5c55,16r5c50,16r5ced,16r5cfd,16r5cfb,16r5cea,
+16r5ce8,16r5cf0,16r5cf6,16r5d01,16r5cf4,16r5dee,16r5e2d,16r5e2b,
+16r5eab,16r5ead,16r5ea7,16r5f31,16r5f92,16r5f91,16r5f90,16r6059,
+16r6063,16r6065,16r6050,16r6055,16r606d,16r6069,16r606f,16r6084,
+16r609f,16r609a,16r608d,16r6094,16r608c,16r6085,16r6096,16r6247,
+16r62f3,16r6308,16r62ff,16r634e,16r633e,16r632f,16r6355,16r6342,
+16r6346,16r634f,16r6349,16r633a,16r6350,16r633d,16r632a,16r632b,
+16r6328,16r634d,16r634c,16r6548,16r6549,16r6599,16r65c1,16r65c5,
+16r6642,16r6649,16r664f,16r6643,16r6652,16r664c,16r6645,16r6641,
+16r66f8,16r6714,16r6715,16r6717,16r6821,16r6838,16r6848,16r6846,
+16r6853,16r6839,16r6842,16r6854,16r6829,16r68b3,16r6817,16r684c,
+16r6851,16r683d,16r67f4,16r6850,16r6840,16r683c,16r6843,16r682a,
+16r6845,16r6813,16r6818,16r6841,16r6b8a,16r6b89,16r6bb7,16r6c23,
+16r6c27,16r6c28,16r6c26,16r6c24,16r6cf0,16r6d6a,16r6d95,16r6d88,
+16r6d87,16r6d66,16r6d78,16r6d77,16r6d59,16r6d93,16r6d6c,16r6d89,
+16r6d6e,16r6d5a,16r6d74,16r6d69,16r6d8c,16r6d8a,16r6d79,16r6d85,
+16r6d65,16r6d94,16r70ca,16r70d8,16r70e4,16r70d9,16r70c8,16r70cf,
+16r7239,16r7279,16r72fc,16r72f9,16r72fd,16r72f8,16r72f7,16r7386,
+16r73ed,16r7409,16r73ee,16r73e0,16r73ea,16r73de,16r7554,16r755d,
+16r755c,16r755a,16r7559,16r75be,16r75c5,16r75c7,16r75b2,16r75b3,
+16r75bd,16r75bc,16r75b9,16r75c2,16r75b8,16r768b,16r76b0,16r76ca,
+16r76cd,16r76ce,16r7729,16r771f,16r7720,16r7728,16r77e9,16r7830,
+16r7827,16r7838,16r781d,16r7834,16r7837,16r7825,16r782d,16r7820,
+16r781f,16r7832,16r7955,16r7950,16r7960,16r795f,16r7956,16r795e,
+16r795d,16r7957,16r795a,16r79e4,16r79e3,16r79e7,16r79df,16r79e6,
+16r79e9,16r79d8,16r7a84,16r7a88,16r7ad9,16r7b06,16r7b11,16r7c89,
+16r7d21,16r7d17,16r7d0b,16r7d0a,16r7d20,16r7d22,16r7d14,16r7d10,
+16r7d15,16r7d1a,16r7d1c,16r7d0d,16r7d19,16r7d1b,16r7f3a,16r7f5f,
+16r7f94,16r7fc5,16r7fc1,16r8006,16r8018,16r8015,16r8019,16r8017,
+16r803d,16r803f,16r80f1,16r8102,16r80f0,16r8105,16r80ed,16r80f4,
+16r8106,16r80f8,16r80f3,16r8108,16r80fd,16r810a,16r80fc,16r80ef,
+16r81ed,16r81ec,16r8200,16r8210,16r822a,16r822b,16r8228,16r822c,
+16r82bb,16r832b,16r8352,16r8354,16r834a,16r8338,16r8350,16r8349,
+16r8335,16r8334,16r834f,16r8332,16r8339,16r8336,16r8317,16r8340,
+16r8331,16r8328,16r8343,16r8654,16r868a,16r86aa,16r8693,16r86a4,
+16r86a9,16r868c,16r86a3,16r869c,16r8870,16r8877,16r8881,16r8882,
+16r887d,16r8879,16r8a18,16r8a10,16r8a0e,16r8a0c,16r8a15,16r8a0a,
+16r8a17,16r8a13,16r8a16,16r8a0f,16r8a11,16r8c48,16r8c7a,16r8c79,
+16r8ca1,16r8ca2,16r8d77,16r8eac,16r8ed2,16r8ed4,16r8ecf,16r8fb1,
+16r9001,16r9006,16r8ff7,16r9000,16r8ffa,16r8ff4,16r9003,16r8ffd,
+16r9005,16r8ff8,16r9095,16r90e1,16r90dd,16r90e2,16r9152,16r914d,
+16r914c,16r91d8,16r91dd,16r91d7,16r91dc,16r91d9,16r9583,16r9662,
+16r9663,16r9661,16r965b,16r965d,16r9664,16r9658,16r965e,16r96bb,
+16r98e2,16r99ac,16r9aa8,16r9ad8,16r9b25,16r9b32,16r9b3c,16r4e7e,
+16r507a,16r507d,16r505c,16r5047,16r5043,16r504c,16r505a,16r5049,
+16r5065,16r5076,16r504e,16r5055,16r5075,16r5074,16r5077,16r504f,
+16r500f,16r506f,16r506d,16r515c,16r5195,16r51f0,16r526a,16r526f,
+16r52d2,16r52d9,16r52d8,16r52d5,16r5310,16r530f,16r5319,16r533f,
+16r5340,16r533e,16r53c3,16r66fc,16r5546,16r556a,16r5566,16r5544,
+16r555e,16r5561,16r5543,16r554a,16r5531,16r5556,16r554f,16r5555,
+16r552f,16r5564,16r5538,16r552e,16r555c,16r552c,16r5563,16r5533,
+16r5541,16r5557,16r5708,16r570b,16r5709,16r57df,16r5805,16r580a,
+16r5806,16r57e0,16r57e4,16r57fa,16r5802,16r5835,16r57f7,16r57f9,
+16r5920,16r5962,16r5a36,16r5a41,16r5a49,16r5a66,16r5a6a,16r5a40,
+16r5a3c,16r5a62,16r5a5a,16r5a46,16r5a4a,16r5b70,16r5bc7,16r5bc5,
+16r5bc4,16r5bc2,16r5bbf,16r5bc6,16r5c09,16r5c08,16r5c07,16r5c60,
+16r5c5c,16r5c5d,16r5d07,16r5d06,16r5d0e,16r5d1b,16r5d16,16r5d22,
+16r5d11,16r5d29,16r5d14,16r5d19,16r5d24,16r5d27,16r5d17,16r5de2,
+16r5e38,16r5e36,16r5e33,16r5e37,16r5eb7,16r5eb8,16r5eb6,16r5eb5,
+16r5ebe,16r5f35,16r5f37,16r5f57,16r5f6c,16r5f69,16r5f6b,16r5f97,
+16r5f99,16r5f9e,16r5f98,16r5fa1,16r5fa0,16r5f9c,16r607f,16r60a3,
+16r6089,16r60a0,16r60a8,16r60cb,16r60b4,16r60e6,16r60bd,16r60c5,
+16r60bb,16r60b5,16r60dc,16r60bc,16r60d8,16r60d5,16r60c6,16r60df,
+16r60b8,16r60da,16r60c7,16r621a,16r621b,16r6248,16r63a0,16r63a7,
+16r6372,16r6396,16r63a2,16r63a5,16r6377,16r6367,16r6398,16r63aa,
+16r6371,16r63a9,16r6389,16r6383,16r639b,16r636b,16r63a8,16r6384,
+16r6388,16r6399,16r63a1,16r63ac,16r6392,16r638f,16r6380,16r637b,
+16r6369,16r6368,16r637a,16r655d,16r6556,16r6551,16r6559,16r6557,
+16r555f,16r654f,16r6558,16r6555,16r6554,16r659c,16r659b,16r65ac,
+16r65cf,16r65cb,16r65cc,16r65ce,16r665d,16r665a,16r6664,16r6668,
+16r6666,16r665e,16r66f9,16r52d7,16r671b,16r6881,16r68af,16r68a2,
+16r6893,16r68b5,16r687f,16r6876,16r68b1,16r68a7,16r6897,16r68b0,
+16r6883,16r68c4,16r68ad,16r6886,16r6885,16r6894,16r689d,16r68a8,
+16r689f,16r68a1,16r6882,16r6b32,16r6bba,16r6beb,16r6bec,16r6c2b,
+16r6d8e,16r6dbc,16r6df3,16r6dd9,16r6db2,16r6de1,16r6dcc,16r6de4,
+16r6dfb,16r6dfa,16r6e05,16r6dc7,16r6dcb,16r6daf,16r6dd1,16r6dae,
+16r6dde,16r6df9,16r6db8,16r6df7,16r6df5,16r6dc5,16r6dd2,16r6e1a,
+16r6db5,16r6dda,16r6deb,16r6dd8,16r6dea,16r6df1,16r6dee,16r6de8,
+16r6dc6,16r6dc4,16r6daa,16r6dec,16r6dbf,16r6de6,16r70f9,16r7109,
+16r710a,16r70fd,16r70ef,16r723d,16r727d,16r7281,16r731c,16r731b,
+16r7316,16r7313,16r7319,16r7387,16r7405,16r740a,16r7403,16r7406,
+16r73fe,16r740d,16r74e0,16r74f6,16r74f7,16r751c,16r7522,16r7565,
+16r7566,16r7562,16r7570,16r758f,16r75d4,16r75d5,16r75b5,16r75ca,
+16r75cd,16r768e,16r76d4,16r76d2,16r76db,16r7737,16r773e,16r773c,
+16r7736,16r7738,16r773a,16r786b,16r7843,16r784e,16r7965,16r7968,
+16r796d,16r79fb,16r7a92,16r7a95,16r7b20,16r7b28,16r7b1b,16r7b2c,
+16r7b26,16r7b19,16r7b1e,16r7b2e,16r7c92,16r7c97,16r7c95,16r7d46,
+16r7d43,16r7d71,16r7d2e,16r7d39,16r7d3c,16r7d40,16r7d30,16r7d33,
+16r7d44,16r7d2f,16r7d42,16r7d32,16r7d31,16r7f3d,16r7f9e,16r7f9a,
+16r7fcc,16r7fce,16r7fd2,16r801c,16r804a,16r8046,16r812f,16r8116,
+16r8123,16r812b,16r8129,16r8130,16r8124,16r8202,16r8235,16r8237,
+16r8236,16r8239,16r838e,16r839e,16r8398,16r8378,16r83a2,16r8396,
+16r83bd,16r83ab,16r8392,16r838a,16r8393,16r8389,16r83a0,16r8377,
+16r837b,16r837c,16r8386,16r83a7,16r8655,16r5f6a,16r86c7,16r86c0,
+16r86b6,16r86c4,16r86b5,16r86c6,16r86cb,16r86b1,16r86af,16r86c9,
+16r8853,16r889e,16r8888,16r88ab,16r8892,16r8896,16r888d,16r888b,
+16r8993,16r898f,16r8a2a,16r8a1d,16r8a23,16r8a25,16r8a31,16r8a2d,
+16r8a1f,16r8a1b,16r8a22,16r8c49,16r8c5a,16r8ca9,16r8cac,16r8cab,
+16r8ca8,16r8caa,16r8ca7,16r8d67,16r8d66,16r8dbe,16r8dba,16r8edb,
+16r8edf,16r9019,16r900d,16r901a,16r9017,16r9023,16r901f,16r901d,
+16r9010,16r9015,16r901e,16r9020,16r900f,16r9022,16r9016,16r901b,
+16r9014,16r90e8,16r90ed,16r90fd,16r9157,16r91ce,16r91f5,16r91e6,
+16r91e3,16r91e7,16r91ed,16r91e9,16r9589,16r966a,16r9675,16r9673,
+16r9678,16r9670,16r9674,16r9676,16r9677,16r966c,16r96c0,16r96ea,
+16r96e9,16r7ae0,16r7adf,16r9802,16r9803,16r9b5a,16r9ce5,16r9e75,
+16r9e7f,16r9ea5,16r9ebb,16r50a2,16r508d,16r5085,16r5099,16r5091,
+16r5080,16r5096,16r5098,16r509a,16r6700,16r51f1,16r5272,16r5274,
+16r5275,16r5269,16r52de,16r52dd,16r52db,16r535a,16r53a5,16r557b,
+16r5580,16r55a7,16r557c,16r558a,16r559d,16r5598,16r5582,16r559c,
+16r55aa,16r5594,16r5587,16r558b,16r5583,16r55b3,16r55ae,16r559f,
+16r553e,16r55b2,16r559a,16r55bb,16r55ac,16r55b1,16r557e,16r5589,
+16r55ab,16r5599,16r570d,16r582f,16r582a,16r5834,16r5824,16r5830,
+16r5831,16r5821,16r581d,16r5820,16r58f9,16r58fa,16r5960,16r5a77,
+16r5a9a,16r5a7f,16r5a92,16r5a9b,16r5aa7,16r5b73,16r5b71,16r5bd2,
+16r5bcc,16r5bd3,16r5bd0,16r5c0a,16r5c0b,16r5c31,16r5d4c,16r5d50,
+16r5d34,16r5d47,16r5dfd,16r5e45,16r5e3d,16r5e40,16r5e43,16r5e7e,
+16r5eca,16r5ec1,16r5ec2,16r5ec4,16r5f3c,16r5f6d,16r5fa9,16r5faa,
+16r5fa8,16r60d1,16r60e1,16r60b2,16r60b6,16r60e0,16r611c,16r6123,
+16r60fa,16r6115,16r60f0,16r60fb,16r60f4,16r6168,16r60f1,16r610e,
+16r60f6,16r6109,16r6100,16r6112,16r621f,16r6249,16r63a3,16r638c,
+16r63cf,16r63c0,16r63e9,16r63c9,16r63c6,16r63cd,16r63d2,16r63e3,
+16r63d0,16r63e1,16r63d6,16r63ed,16r63ee,16r6376,16r63f4,16r63ea,
+16r63db,16r6452,16r63da,16r63f9,16r655e,16r6566,16r6562,16r6563,
+16r6591,16r6590,16r65af,16r666e,16r6670,16r6674,16r6676,16r666f,
+16r6691,16r667a,16r667e,16r6677,16r66fe,16r66ff,16r671f,16r671d,
+16r68fa,16r68d5,16r68e0,16r68d8,16r68d7,16r6905,16r68df,16r68f5,
+16r68ee,16r68e7,16r68f9,16r68d2,16r68f2,16r68e3,16r68cb,16r68cd,
+16r690d,16r6912,16r690e,16r68c9,16r68da,16r696e,16r68fb,16r6b3e,
+16r6b3a,16r6b3d,16r6b98,16r6b96,16r6bbc,16r6bef,16r6c2e,16r6c2f,
+16r6c2c,16r6e2f,16r6e38,16r6e54,16r6e21,16r6e32,16r6e67,16r6e4a,
+16r6e20,16r6e25,16r6e23,16r6e1b,16r6e5b,16r6e58,16r6e24,16r6e56,
+16r6e6e,16r6e2d,16r6e26,16r6e6f,16r6e34,16r6e4d,16r6e3a,16r6e2c,
+16r6e43,16r6e1d,16r6e3e,16r6ecb,16r6e89,16r6e19,16r6e4e,16r6e63,
+16r6e44,16r6e72,16r6e69,16r6e5f,16r7119,16r711a,16r7126,16r7130,
+16r7121,16r7136,16r716e,16r711c,16r724c,16r7284,16r7280,16r7336,
+16r7325,16r7334,16r7329,16r743a,16r742a,16r7433,16r7422,16r7425,
+16r7435,16r7436,16r7434,16r742f,16r741b,16r7426,16r7428,16r7525,
+16r7526,16r756b,16r756a,16r75e2,16r75db,16r75e3,16r75d9,16r75d8,
+16r75de,16r75e0,16r767b,16r767c,16r7696,16r7693,16r76b4,16r76dc,
+16r774f,16r77ed,16r785d,16r786c,16r786f,16r7a0d,16r7a08,16r7a0b,
+16r7a05,16r7a00,16r7a98,16r7a97,16r7a96,16r7ae5,16r7ae3,16r7b49,
+16r7b56,16r7b46,16r7b50,16r7b52,16r7b54,16r7b4d,16r7b4b,16r7b4f,
+16r7b51,16r7c9f,16r7ca5,16r7d5e,16r7d50,16r7d68,16r7d55,16r7d2b,
+16r7d6e,16r7d72,16r7d61,16r7d66,16r7d62,16r7d70,16r7d73,16r5584,
+16r7fd4,16r7fd5,16r800b,16r8052,16r8085,16r8155,16r8154,16r814b,
+16r8151,16r814e,16r8139,16r8146,16r813e,16r814c,16r8153,16r8174,
+16r8212,16r821c,16r83e9,16r8403,16r83f8,16r840d,16r83e0,16r83c5,
+16r840b,16r83c1,16r83ef,16r83f1,16r83f4,16r8457,16r840a,16r83f0,
+16r840c,16r83cc,16r83fd,16r83f2,16r83ca,16r8438,16r840e,16r8404,
+16r83dc,16r8407,16r83d4,16r83df,16r865b,16r86df,16r86d9,16r86ed,
+16r86d4,16r86db,16r86e4,16r86d0,16r86de,16r8857,16r88c1,16r88c2,
+16r88b1,16r8983,16r8996,16r8a3b,16r8a60,16r8a55,16r8a5e,16r8a3c,
+16r8a41,16r8a54,16r8a5b,16r8a50,16r8a46,16r8a34,16r8a3a,16r8a36,
+16r8a56,16r8c61,16r8c82,16r8caf,16r8cbc,16r8cb3,16r8cbd,16r8cc1,
+16r8cbb,16r8cc0,16r8cb4,16r8cb7,16r8cb6,16r8cbf,16r8cb8,16r8d8a,
+16r8d85,16r8d81,16r8dce,16r8ddd,16r8dcb,16r8dda,16r8dd1,16r8dcc,
+16r8ddb,16r8dc6,16r8efb,16r8ef8,16r8efc,16r8f9c,16r902e,16r9035,
+16r9031,16r9038,16r9032,16r9036,16r9102,16r90f5,16r9109,16r90fe,
+16r9163,16r9165,16r91cf,16r9214,16r9215,16r9223,16r9209,16r921e,
+16r920d,16r9210,16r9207,16r9211,16r9594,16r958f,16r958b,16r9591,
+16r9593,16r9592,16r958e,16r968a,16r968e,16r968b,16r967d,16r9685,
+16r9686,16r968d,16r9672,16r9684,16r96c1,16r96c5,16r96c4,16r96c6,
+16r96c7,16r96ef,16r96f2,16r97cc,16r9805,16r9806,16r9808,16r98e7,
+16r98ea,16r98ef,16r98e9,16r98f2,16r98ed,16r99ae,16r99ad,16r9ec3,
+16r9ecd,16r9ed1,16r4e82,16r50ad,16r50b5,16r50b2,16r50b3,16r50c5,
+16r50be,16r50ac,16r50b7,16r50bb,16r50af,16r50c7,16r527f,16r5277,
+16r527d,16r52df,16r52e6,16r52e4,16r52e2,16r52e3,16r532f,16r55df,
+16r55e8,16r55d3,16r55e6,16r55ce,16r55dc,16r55c7,16r55d1,16r55e3,
+16r55e4,16r55ef,16r55da,16r55e1,16r55c5,16r55c6,16r55e5,16r55c9,
+16r5712,16r5713,16r585e,16r5851,16r5858,16r5857,16r585a,16r5854,
+16r586b,16r584c,16r586d,16r584a,16r5862,16r5852,16r584b,16r5967,
+16r5ac1,16r5ac9,16r5acc,16r5abe,16r5abd,16r5abc,16r5ab3,16r5ac2,
+16r5ab2,16r5d69,16r5d6f,16r5e4c,16r5e79,16r5ec9,16r5ec8,16r5f12,
+16r5f59,16r5fac,16r5fae,16r611a,16r610f,16r6148,16r611f,16r60f3,
+16r611b,16r60f9,16r6101,16r6108,16r614e,16r614c,16r6144,16r614d,
+16r613e,16r6134,16r6127,16r610d,16r6106,16r6137,16r6221,16r6222,
+16r6413,16r643e,16r641e,16r642a,16r642d,16r643d,16r642c,16r640f,
+16r641c,16r6414,16r640d,16r6436,16r6416,16r6417,16r6406,16r656c,
+16r659f,16r65b0,16r6697,16r6689,16r6687,16r6688,16r6696,16r6684,
+16r6698,16r668d,16r6703,16r6994,16r696d,16r695a,16r6977,16r6960,
+16r6954,16r6975,16r6930,16r6982,16r694a,16r6968,16r696b,16r695e,
+16r6953,16r6979,16r6986,16r695d,16r6963,16r695b,16r6b47,16r6b72,
+16r6bc0,16r6bbf,16r6bd3,16r6bfd,16r6ea2,16r6eaf,16r6ed3,16r6eb6,
+16r6ec2,16r6e90,16r6e9d,16r6ec7,16r6ec5,16r6ea5,16r6e98,16r6ebc,
+16r6eba,16r6eab,16r6ed1,16r6e96,16r6e9c,16r6ec4,16r6ed4,16r6eaa,
+16r6ea7,16r6eb4,16r714e,16r7159,16r7169,16r7164,16r7149,16r7167,
+16r715c,16r716c,16r7166,16r714c,16r7165,16r715e,16r7146,16r7168,
+16r7156,16r723a,16r7252,16r7337,16r7345,16r733f,16r733e,16r746f,
+16r745a,16r7455,16r745f,16r745e,16r7441,16r743f,16r7459,16r745b,
+16r745c,16r7576,16r7578,16r7600,16r75f0,16r7601,16r75f2,16r75f1,
+16r75fa,16r75ff,16r75f4,16r75f3,16r76de,16r76df,16r775b,16r776b,
+16r7766,16r775e,16r7763,16r7779,16r776a,16r776c,16r775c,16r7765,
+16r7768,16r7762,16r77ee,16r788e,16r78b0,16r7897,16r7898,16r788c,
+16r7889,16r787c,16r7891,16r7893,16r787f,16r797a,16r797f,16r7981,
+16r842c,16r79bd,16r7a1c,16r7a1a,16r7a20,16r7a14,16r7a1f,16r7a1e,
+16r7a9f,16r7aa0,16r7b77,16r7bc0,16r7b60,16r7b6e,16r7b67,16r7cb1,
+16r7cb3,16r7cb5,16r7d93,16r7d79,16r7d91,16r7d81,16r7d8f,16r7d5b,
+16r7f6e,16r7f69,16r7f6a,16r7f72,16r7fa9,16r7fa8,16r7fa4,16r8056,
+16r8058,16r8086,16r8084,16r8171,16r8170,16r8178,16r8165,16r816e,
+16r8173,16r816b,16r8179,16r817a,16r8166,16r8205,16r8247,16r8482,
+16r8477,16r843d,16r8431,16r8475,16r8466,16r846b,16r8449,16r846c,
+16r845b,16r843c,16r8435,16r8461,16r8463,16r8469,16r846d,16r8446,
+16r865e,16r865c,16r865f,16r86f9,16r8713,16r8708,16r8707,16r8700,
+16r86fe,16r86fb,16r8702,16r8703,16r8706,16r870a,16r8859,16r88df,
+16r88d4,16r88d9,16r88dc,16r88d8,16r88dd,16r88e1,16r88ca,16r88d5,
+16r88d2,16r899c,16r89e3,16r8a6b,16r8a72,16r8a73,16r8a66,16r8a69,
+16r8a70,16r8a87,16r8a7c,16r8a63,16r8aa0,16r8a71,16r8a85,16r8a6d,
+16r8a62,16r8a6e,16r8a6c,16r8a79,16r8a7b,16r8a3e,16r8a68,16r8c62,
+16r8c8a,16r8c89,16r8cca,16r8cc7,16r8cc8,16r8cc4,16r8cb2,16r8cc3,
+16r8cc2,16r8cc5,16r8de1,16r8ddf,16r8de8,16r8def,16r8df3,16r8dfa,
+16r8dea,16r8de4,16r8de6,16r8eb2,16r8f03,16r8f09,16r8efe,16r8f0a,
+16r8f9f,16r8fb2,16r904b,16r904a,16r9053,16r9042,16r9054,16r903c,
+16r9055,16r9050,16r9047,16r904f,16r904e,16r904d,16r9051,16r903e,
+16r9041,16r9112,16r9117,16r916c,16r916a,16r9169,16r91c9,16r9237,
+16r9257,16r9238,16r923d,16r9240,16r923e,16r925b,16r924b,16r9264,
+16r9251,16r9234,16r9249,16r924d,16r9245,16r9239,16r923f,16r925a,
+16r9598,16r9698,16r9694,16r9695,16r96cd,16r96cb,16r96c9,16r96ca,
+16r96f7,16r96fb,16r96f9,16r96f6,16r9756,16r9774,16r9776,16r9810,
+16r9811,16r9813,16r980a,16r9812,16r980c,16r98fc,16r98f4,16r98fd,
+16r98fe,16r99b3,16r99b1,16r99b4,16r9ae1,16r9ce9,16r9e82,16r9f0e,
+16r9f13,16r9f20,16r50e7,16r50ee,16r50e5,16r50d6,16r50ed,16r50da,
+16r50d5,16r50cf,16r50d1,16r50f1,16r50ce,16r50e9,16r5162,16r51f3,
+16r5283,16r5282,16r5331,16r53ad,16r55fe,16r5600,16r561b,16r5617,
+16r55fd,16r5614,16r5606,16r5609,16r560d,16r560e,16r55f7,16r5616,
+16r561f,16r5608,16r5610,16r55f6,16r5718,16r5716,16r5875,16r587e,
+16r5883,16r5893,16r588a,16r5879,16r5885,16r587d,16r58fd,16r5925,
+16r5922,16r5924,16r596a,16r5969,16r5ae1,16r5ae6,16r5ae9,16r5ad7,
+16r5ad6,16r5ad8,16r5ae3,16r5b75,16r5bde,16r5be7,16r5be1,16r5be5,
+16r5be6,16r5be8,16r5be2,16r5be4,16r5bdf,16r5c0d,16r5c62,16r5d84,
+16r5d87,16r5e5b,16r5e63,16r5e55,16r5e57,16r5e54,16r5ed3,16r5ed6,
+16r5f0a,16r5f46,16r5f70,16r5fb9,16r6147,16r613f,16r614b,16r6177,
+16r6162,16r6163,16r615f,16r615a,16r6158,16r6175,16r622a,16r6487,
+16r6458,16r6454,16r64a4,16r6478,16r645f,16r647a,16r6451,16r6467,
+16r6434,16r646d,16r647b,16r6572,16r65a1,16r65d7,16r65d6,16r66a2,
+16r66a8,16r669d,16r699c,16r69a8,16r6995,16r69c1,16r69ae,16r69d3,
+16r69cb,16r699b,16r69b7,16r69bb,16r69ab,16r69b4,16r69d0,16r69cd,
+16r69ad,16r69cc,16r69a6,16r69c3,16r69a3,16r6b49,16r6b4c,16r6c33,
+16r6f33,16r6f14,16r6efe,16r6f13,16r6ef4,16r6f29,16r6f3e,16r6f20,
+16r6f2c,16r6f0f,16r6f02,16r6f22,16r6eff,16r6eef,16r6f06,16r6f31,
+16r6f38,16r6f32,16r6f23,16r6f15,16r6f2b,16r6f2f,16r6f88,16r6f2a,
+16r6eec,16r6f01,16r6ef2,16r6ecc,16r6ef7,16r7194,16r7199,16r717d,
+16r718a,16r7184,16r7192,16r723e,16r7292,16r7296,16r7344,16r7350,
+16r7464,16r7463,16r746a,16r7470,16r746d,16r7504,16r7591,16r7627,
+16r760d,16r760b,16r7609,16r7613,16r76e1,16r76e3,16r7784,16r777d,
+16r777f,16r7761,16r78c1,16r789f,16r78a7,16r78b3,16r78a9,16r78a3,
+16r798e,16r798f,16r798d,16r7a2e,16r7a31,16r7aaa,16r7aa9,16r7aed,
+16r7aef,16r7ba1,16r7b95,16r7b8b,16r7b75,16r7b97,16r7b9d,16r7b94,
+16r7b8f,16r7bb8,16r7b87,16r7b84,16r7cb9,16r7cbd,16r7cbe,16r7dbb,
+16r7db0,16r7d9c,16r7dbd,16r7dbe,16r7da0,16r7dca,16r7db4,16r7db2,
+16r7db1,16r7dba,16r7da2,16r7dbf,16r7db5,16r7db8,16r7dad,16r7dd2,
+16r7dc7,16r7dac,16r7f70,16r7fe0,16r7fe1,16r7fdf,16r805e,16r805a,
+16r8087,16r8150,16r8180,16r818f,16r8188,16r818a,16r817f,16r8182,
+16r81e7,16r81fa,16r8207,16r8214,16r821e,16r824b,16r84c9,16r84bf,
+16r84c6,16r84c4,16r8499,16r849e,16r84b2,16r849c,16r84cb,16r84b8,
+16r84c0,16r84d3,16r8490,16r84bc,16r84d1,16r84ca,16r873f,16r871c,
+16r873b,16r8722,16r8725,16r8734,16r8718,16r8755,16r8737,16r8729,
+16r88f3,16r8902,16r88f4,16r88f9,16r88f8,16r88fd,16r88e8,16r891a,
+16r88ef,16r8aa6,16r8a8c,16r8a9e,16r8aa3,16r8a8d,16r8aa1,16r8a93,
+16r8aa4,16r8aaa,16r8aa5,16r8aa8,16r8a98,16r8a91,16r8a9a,16r8aa7,
+16r8c6a,16r8c8d,16r8c8c,16r8cd3,16r8cd1,16r8cd2,16r8d6b,16r8d99,
+16r8d95,16r8dfc,16r8f14,16r8f12,16r8f15,16r8f13,16r8fa3,16r9060,
+16r9058,16r905c,16r9063,16r9059,16r905e,16r9062,16r905d,16r905b,
+16r9119,16r9118,16r911e,16r9175,16r9178,16r9177,16r9174,16r9278,
+16r9280,16r9285,16r9298,16r9296,16r927b,16r9293,16r929c,16r92a8,
+16r927c,16r9291,16r95a1,16r95a8,16r95a9,16r95a3,16r95a5,16r95a4,
+16r9699,16r969c,16r969b,16r96cc,16r96d2,16r9700,16r977c,16r9785,
+16r97f6,16r9817,16r9818,16r98af,16r98b1,16r9903,16r9905,16r990c,
+16r9909,16r99c1,16r9aaf,16r9ab0,16r9ae6,16r9b41,16r9b42,16r9cf4,
+16r9cf6,16r9cf3,16r9ebc,16r9f3b,16r9f4a,16r5104,16r5100,16r50fb,
+16r50f5,16r50f9,16r5102,16r5108,16r5109,16r5105,16r51dc,16r5287,
+16r5288,16r5289,16r528d,16r528a,16r52f0,16r53b2,16r562e,16r563b,
+16r5639,16r5632,16r563f,16r5634,16r5629,16r5653,16r564e,16r5657,
+16r5674,16r5636,16r562f,16r5630,16r5880,16r589f,16r589e,16r58b3,
+16r589c,16r58ae,16r58a9,16r58a6,16r596d,16r5b09,16r5afb,16r5b0b,
+16r5af5,16r5b0c,16r5b08,16r5bee,16r5bec,16r5be9,16r5beb,16r5c64,
+16r5c65,16r5d9d,16r5d94,16r5e62,16r5e5f,16r5e61,16r5ee2,16r5eda,
+16r5edf,16r5edd,16r5ee3,16r5ee0,16r5f48,16r5f71,16r5fb7,16r5fb5,
+16r6176,16r6167,16r616e,16r615d,16r6155,16r6182,16r617c,16r6170,
+16r616b,16r617e,16r61a7,16r6190,16r61ab,16r618e,16r61ac,16r619a,
+16r61a4,16r6194,16r61ae,16r622e,16r6469,16r646f,16r6479,16r649e,
+16r64b2,16r6488,16r6490,16r64b0,16r64a5,16r6493,16r6495,16r64a9,
+16r6492,16r64ae,16r64ad,16r64ab,16r649a,16r64ac,16r6499,16r64a2,
+16r64b3,16r6575,16r6577,16r6578,16r66ae,16r66ab,16r66b4,16r66b1,
+16r6a23,16r6a1f,16r69e8,16r6a01,16r6a1e,16r6a19,16r69fd,16r6a21,
+16r6a13,16r6a0a,16r69f3,16r6a02,16r6a05,16r69ed,16r6a11,16r6b50,
+16r6b4e,16r6ba4,16r6bc5,16r6bc6,16r6f3f,16r6f7c,16r6f84,16r6f51,
+16r6f66,16r6f54,16r6f86,16r6f6d,16r6f5b,16r6f78,16r6f6e,16r6f8e,
+16r6f7a,16r6f70,16r6f64,16r6f97,16r6f58,16r6ed5,16r6f6f,16r6f60,
+16r6f5f,16r719f,16r71ac,16r71b1,16r71a8,16r7256,16r729b,16r734e,
+16r7357,16r7469,16r748b,16r7483,16r747e,16r7480,16r757f,16r7620,
+16r7629,16r761f,16r7624,16r7626,16r7621,16r7622,16r769a,16r76ba,
+16r76e4,16r778e,16r7787,16r778c,16r7791,16r778b,16r78cb,16r78c5,
+16r78ba,16r78ca,16r78be,16r78d5,16r78bc,16r78d0,16r7a3f,16r7a3c,
+16r7a40,16r7a3d,16r7a37,16r7a3b,16r7aaf,16r7aae,16r7bad,16r7bb1,
+16r7bc4,16r7bb4,16r7bc6,16r7bc7,16r7bc1,16r7ba0,16r7bcc,16r7cca,
+16r7de0,16r7df4,16r7def,16r7dfb,16r7dd8,16r7dec,16r7ddd,16r7de8,
+16r7de3,16r7dda,16r7dde,16r7de9,16r7d9e,16r7dd9,16r7df2,16r7df9,
+16r7f75,16r7f77,16r7faf,16r7fe9,16r8026,16r819b,16r819c,16r819d,
+16r81a0,16r819a,16r8198,16r8517,16r853d,16r851a,16r84ee,16r852c,
+16r852d,16r8513,16r8511,16r8523,16r8521,16r8514,16r84ec,16r8525,
+16r84ff,16r8506,16r8782,16r8774,16r8776,16r8760,16r8766,16r8778,
+16r8768,16r8759,16r8757,16r874c,16r8753,16r885b,16r885d,16r8910,
+16r8907,16r8912,16r8913,16r8915,16r890a,16r8abc,16r8ad2,16r8ac7,
+16r8ac4,16r8a95,16r8acb,16r8af8,16r8ab2,16r8ac9,16r8ac2,16r8abf,
+16r8ab0,16r8ad6,16r8acd,16r8ab6,16r8ab9,16r8adb,16r8c4c,16r8c4e,
+16r8c6c,16r8ce0,16r8cde,16r8ce6,16r8ce4,16r8cec,16r8ced,16r8ce2,
+16r8ce3,16r8cdc,16r8cea,16r8ce1,16r8d6d,16r8d9f,16r8da3,16r8e2b,
+16r8e10,16r8e1d,16r8e22,16r8e0f,16r8e29,16r8e1f,16r8e21,16r8e1e,
+16r8eba,16r8f1d,16r8f1b,16r8f1f,16r8f29,16r8f26,16r8f2a,16r8f1c,
+16r8f1e,16r8f25,16r9069,16r906e,16r9068,16r906d,16r9077,16r9130,
+16r912d,16r9127,16r9131,16r9187,16r9189,16r918b,16r9183,16r92c5,
+16r92bb,16r92b7,16r92ea,16r92ac,16r92e4,16r92c1,16r92b3,16r92bc,
+16r92d2,16r92c7,16r92f0,16r92b2,16r95ad,16r95b1,16r9704,16r9706,
+16r9707,16r9709,16r9760,16r978d,16r978b,16r978f,16r9821,16r982b,
+16r981c,16r98b3,16r990a,16r9913,16r9912,16r9918,16r99dd,16r99d0,
+16r99df,16r99db,16r99d1,16r99d5,16r99d2,16r99d9,16r9ab7,16r9aee,
+16r9aef,16r9b27,16r9b45,16r9b44,16r9b77,16r9b6f,16r9d06,16r9d09,
+16r9d03,16r9ea9,16r9ebe,16r9ece,16r58a8,16r9f52,16r5112,16r5118,
+16r5114,16r5110,16r5115,16r5180,16r51aa,16r51dd,16r5291,16r5293,
+16r52f3,16r5659,16r566b,16r5679,16r5669,16r5664,16r5678,16r566a,
+16r5668,16r5665,16r5671,16r566f,16r566c,16r5662,16r5676,16r58c1,
+16r58be,16r58c7,16r58c5,16r596e,16r5b1d,16r5b34,16r5b78,16r5bf0,
+16r5c0e,16r5f4a,16r61b2,16r6191,16r61a9,16r618a,16r61cd,16r61b6,
+16r61be,16r61ca,16r61c8,16r6230,16r64c5,16r64c1,16r64cb,16r64bb,
+16r64bc,16r64da,16r64c4,16r64c7,16r64c2,16r64cd,16r64bf,16r64d2,
+16r64d4,16r64be,16r6574,16r66c6,16r66c9,16r66b9,16r66c4,16r66c7,
+16r66b8,16r6a3d,16r6a38,16r6a3a,16r6a59,16r6a6b,16r6a58,16r6a39,
+16r6a44,16r6a62,16r6a61,16r6a4b,16r6a47,16r6a35,16r6a5f,16r6a48,
+16r6b59,16r6b77,16r6c05,16r6fc2,16r6fb1,16r6fa1,16r6fc3,16r6fa4,
+16r6fc1,16r6fa7,16r6fb3,16r6fc0,16r6fb9,16r6fb6,16r6fa6,16r6fa0,
+16r6fb4,16r71be,16r71c9,16r71d0,16r71d2,16r71c8,16r71d5,16r71b9,
+16r71ce,16r71d9,16r71dc,16r71c3,16r71c4,16r7368,16r749c,16r74a3,
+16r7498,16r749f,16r749e,16r74e2,16r750c,16r750d,16r7634,16r7638,
+16r763a,16r76e7,16r76e5,16r77a0,16r779e,16r779f,16r77a5,16r78e8,
+16r78da,16r78ec,16r78e7,16r79a6,16r7a4d,16r7a4e,16r7a46,16r7a4c,
+16r7a4b,16r7aba,16r7bd9,16r7c11,16r7bc9,16r7be4,16r7bdb,16r7be1,
+16r7be9,16r7be6,16r7cd5,16r7cd6,16r7e0a,16r7e11,16r7e08,16r7e1b,
+16r7e23,16r7e1e,16r7e1d,16r7e09,16r7e10,16r7f79,16r7fb2,16r7ff0,
+16r7ff1,16r7fee,16r8028,16r81b3,16r81a9,16r81a8,16r81fb,16r8208,
+16r8258,16r8259,16r854a,16r8559,16r8548,16r8568,16r8569,16r8543,
+16r8549,16r856d,16r856a,16r855e,16r8783,16r879f,16r879e,16r87a2,
+16r878d,16r8861,16r892a,16r8932,16r8925,16r892b,16r8921,16r89aa,
+16r89a6,16r8ae6,16r8afa,16r8aeb,16r8af1,16r8b00,16r8adc,16r8ae7,
+16r8aee,16r8afe,16r8b01,16r8b02,16r8af7,16r8aed,16r8af3,16r8af6,
+16r8afc,16r8c6b,16r8c6d,16r8c93,16r8cf4,16r8e44,16r8e31,16r8e34,
+16r8e42,16r8e39,16r8e35,16r8f3b,16r8f2f,16r8f38,16r8f33,16r8fa8,
+16r8fa6,16r9075,16r9074,16r9078,16r9072,16r907c,16r907a,16r9134,
+16r9192,16r9320,16r9336,16r92f8,16r9333,16r932f,16r9322,16r92fc,
+16r932b,16r9304,16r931a,16r9310,16r9326,16r9321,16r9315,16r932e,
+16r9319,16r95bb,16r96a7,16r96a8,16r96aa,16r96d5,16r970e,16r9711,
+16r9716,16r970d,16r9713,16r970f,16r975b,16r975c,16r9766,16r9798,
+16r9830,16r9838,16r983b,16r9837,16r982d,16r9839,16r9824,16r9910,
+16r9928,16r991e,16r991b,16r9921,16r991a,16r99ed,16r99e2,16r99f1,
+16r9ab8,16r9abc,16r9afb,16r9aed,16r9b28,16r9b91,16r9d15,16r9d23,
+16r9d26,16r9d28,16r9d12,16r9d1b,16r9ed8,16r9ed4,16r9f8d,16r9f9c,
+16r512a,16r511f,16r5121,16r5132,16r52f5,16r568e,16r5680,16r5690,
+16r5685,16r5687,16r568f,16r58d5,16r58d3,16r58d1,16r58ce,16r5b30,
+16r5b2a,16r5b24,16r5b7a,16r5c37,16r5c68,16r5dbc,16r5dba,16r5dbd,
+16r5db8,16r5e6b,16r5f4c,16r5fbd,16r61c9,16r61c2,16r61c7,16r61e6,
+16r61cb,16r6232,16r6234,16r64ce,16r64ca,16r64d8,16r64e0,16r64f0,
+16r64e6,16r64ec,16r64f1,16r64e2,16r64ed,16r6582,16r6583,16r66d9,
+16r66d6,16r6a80,16r6a94,16r6a84,16r6aa2,16r6a9c,16r6adb,16r6aa3,
+16r6a7e,16r6a97,16r6a90,16r6aa0,16r6b5c,16r6bae,16r6bda,16r6c08,
+16r6fd8,16r6ff1,16r6fdf,16r6fe0,16r6fdb,16r6fe4,16r6feb,16r6fef,
+16r6f80,16r6fec,16r6fe1,16r6fe9,16r6fd5,16r6fee,16r6ff0,16r71e7,
+16r71df,16r71ee,16r71e6,16r71e5,16r71ed,16r71ec,16r71f4,16r71e0,
+16r7235,16r7246,16r7370,16r7372,16r74a9,16r74b0,16r74a6,16r74a8,
+16r7646,16r7642,16r764c,16r76ea,16r77b3,16r77aa,16r77b0,16r77ac,
+16r77a7,16r77ad,16r77ef,16r78f7,16r78fa,16r78f4,16r78ef,16r7901,
+16r79a7,16r79aa,16r7a57,16r7abf,16r7c07,16r7c0d,16r7bfe,16r7bf7,
+16r7c0c,16r7be0,16r7ce0,16r7cdc,16r7cde,16r7ce2,16r7cdf,16r7cd9,
+16r7cdd,16r7e2e,16r7e3e,16r7e46,16r7e37,16r7e32,16r7e43,16r7e2b,
+16r7e3d,16r7e31,16r7e45,16r7e41,16r7e34,16r7e39,16r7e48,16r7e35,
+16r7e3f,16r7e2f,16r7f44,16r7ff3,16r7ffc,16r8071,16r8072,16r8070,
+16r806f,16r8073,16r81c6,16r81c3,16r81ba,16r81c2,16r81c0,16r81bf,
+16r81bd,16r81c9,16r81be,16r81e8,16r8209,16r8271,16r85aa,16r8584,
+16r857e,16r859c,16r8591,16r8594,16r85af,16r859b,16r8587,16r85a8,
+16r858a,16r8667,16r87c0,16r87d1,16r87b3,16r87d2,16r87c6,16r87ab,
+16r87bb,16r87ba,16r87c8,16r87cb,16r893b,16r8936,16r8944,16r8938,
+16r893d,16r89ac,16r8b0e,16r8b17,16r8b19,16r8b1b,16r8b0a,16r8b20,
+16r8b1d,16r8b04,16r8b10,16r8c41,16r8c3f,16r8c73,16r8cfa,16r8cfd,
+16r8cfc,16r8cf8,16r8cfb,16r8da8,16r8e49,16r8e4b,16r8e48,16r8e4a,
+16r8f44,16r8f3e,16r8f42,16r8f45,16r8f3f,16r907f,16r907d,16r9084,
+16r9081,16r9082,16r9080,16r9139,16r91a3,16r919e,16r919c,16r934d,
+16r9382,16r9328,16r9375,16r934a,16r9365,16r934b,16r9318,16r937e,
+16r936c,16r935b,16r9370,16r935a,16r9354,16r95ca,16r95cb,16r95cc,
+16r95c8,16r95c6,16r96b1,16r96b8,16r96d6,16r971c,16r971e,16r97a0,
+16r97d3,16r9846,16r98b6,16r9935,16r9a01,16r99ff,16r9bae,16r9bab,
+16r9baa,16r9bad,16r9d3b,16r9d3f,16r9e8b,16r9ecf,16r9ede,16r9edc,
+16r9edd,16r9edb,16r9f3e,16r9f4b,16r53e2,16r5695,16r56ae,16r58d9,
+16r58d8,16r5b38,16r5f5d,16r61e3,16r6233,16r64f4,16r64f2,16r64fe,
+16r6506,16r64fa,16r64fb,16r64f7,16r65b7,16r66dc,16r6726,16r6ab3,
+16r6aac,16r6ac3,16r6abb,16r6ab8,16r6ac2,16r6aae,16r6aaf,16r6b5f,
+16r6b78,16r6baf,16r7009,16r700b,16r6ffe,16r7006,16r6ffa,16r7011,
+16r700f,16r71fb,16r71fc,16r71fe,16r71f8,16r7377,16r7375,16r74a7,
+16r74bf,16r7515,16r7656,16r7658,16r7652,16r77bd,16r77bf,16r77bb,
+16r77bc,16r790e,16r79ae,16r7a61,16r7a62,16r7a60,16r7ac4,16r7ac5,
+16r7c2b,16r7c27,16r7c2a,16r7c1e,16r7c23,16r7c21,16r7ce7,16r7e54,
+16r7e55,16r7e5e,16r7e5a,16r7e61,16r7e52,16r7e59,16r7f48,16r7ff9,
+16r7ffb,16r8077,16r8076,16r81cd,16r81cf,16r820a,16r85cf,16r85a9,
+16r85cd,16r85d0,16r85c9,16r85b0,16r85ba,16r85b9,16r85a6,16r87ef,
+16r87ec,16r87f2,16r87e0,16r8986,16r89b2,16r89f4,16r8b28,16r8b39,
+16r8b2c,16r8b2b,16r8c50,16r8d05,16r8e59,16r8e63,16r8e66,16r8e64,
+16r8e5f,16r8e55,16r8ec0,16r8f49,16r8f4d,16r9087,16r9083,16r9088,
+16r91ab,16r91ac,16r91d0,16r9394,16r938a,16r9396,16r93a2,16r93b3,
+16r93ae,16r93ac,16r93b0,16r9398,16r939a,16r9397,16r95d4,16r95d6,
+16r95d0,16r95d5,16r96e2,16r96dc,16r96d9,16r96db,16r96de,16r9724,
+16r97a3,16r97a6,16r97ad,16r97f9,16r984d,16r984f,16r984c,16r984e,
+16r9853,16r98ba,16r993e,16r993f,16r993d,16r992e,16r99a5,16r9a0e,
+16r9ac1,16r9b03,16r9b06,16r9b4f,16r9b4e,16r9b4d,16r9bca,16r9bc9,
+16r9bfd,16r9bc8,16r9bc0,16r9d51,16r9d5d,16r9d60,16r9ee0,16r9f15,
+16r9f2c,16r5133,16r56a5,16r58de,16r58df,16r58e2,16r5bf5,16r9f90,
+16r5eec,16r61f2,16r61f7,16r61f6,16r61f5,16r6500,16r650f,16r66e0,
+16r66dd,16r6ae5,16r6add,16r6ada,16r6ad3,16r701b,16r701f,16r7028,
+16r701a,16r701d,16r7015,16r7018,16r7206,16r720d,16r7258,16r72a2,
+16r7378,16r737a,16r74bd,16r74ca,16r74e3,16r7587,16r7586,16r765f,
+16r7661,16r77c7,16r7919,16r79b1,16r7a6b,16r7a69,16r7c3e,16r7c3f,
+16r7c38,16r7c3d,16r7c37,16r7c40,16r7e6b,16r7e6d,16r7e79,16r7e69,
+16r7e6a,16r7f85,16r7e73,16r7fb6,16r7fb9,16r7fb8,16r81d8,16r85e9,
+16r85dd,16r85ea,16r85d5,16r85e4,16r85e5,16r85f7,16r87fb,16r8805,
+16r880d,16r87f9,16r87fe,16r8960,16r895f,16r8956,16r895e,16r8b41,
+16r8b5c,16r8b58,16r8b49,16r8b5a,16r8b4e,16r8b4f,16r8b46,16r8b59,
+16r8d08,16r8d0a,16r8e7c,16r8e72,16r8e87,16r8e76,16r8e6c,16r8e7a,
+16r8e74,16r8f54,16r8f4e,16r8fad,16r908a,16r908b,16r91b1,16r91ae,
+16r93e1,16r93d1,16r93df,16r93c3,16r93c8,16r93dc,16r93dd,16r93d6,
+16r93e2,16r93cd,16r93d8,16r93e4,16r93d7,16r93e8,16r95dc,16r96b4,
+16r96e3,16r972a,16r9727,16r9761,16r97dc,16r97fb,16r985e,16r9858,
+16r985b,16r98bc,16r9945,16r9949,16r9a16,16r9a19,16r9b0d,16r9be8,
+16r9be7,16r9bd6,16r9bdb,16r9d89,16r9d61,16r9d72,16r9d6a,16r9d6c,
+16r9e92,16r9e97,16r9e93,16r9eb4,16r52f8,16r56a8,16r56b7,16r56b6,
+16r56b4,16r56bc,16r58e4,16r5b40,16r5b43,16r5b7d,16r5bf6,16r5dc9,
+16r61f8,16r61fa,16r6518,16r6514,16r6519,16r66e6,16r6727,16r6aec,
+16r703e,16r7030,16r7032,16r7210,16r737b,16r74cf,16r7662,16r7665,
+16r7926,16r792a,16r792c,16r792b,16r7ac7,16r7af6,16r7c4c,16r7c43,
+16r7c4d,16r7cef,16r7cf0,16r8fae,16r7e7d,16r7e7c,16r7e82,16r7f4c,
+16r8000,16r81da,16r8266,16r85fb,16r85f9,16r8611,16r85fa,16r8606,
+16r860b,16r8607,16r860a,16r8814,16r8815,16r8964,16r89ba,16r89f8,
+16r8b70,16r8b6c,16r8b66,16r8b6f,16r8b5f,16r8b6b,16r8d0f,16r8d0d,
+16r8e89,16r8e81,16r8e85,16r8e82,16r91b4,16r91cb,16r9418,16r9403,
+16r93fd,16r95e1,16r9730,16r98c4,16r9952,16r9951,16r99a8,16r9a2b,
+16r9a30,16r9a37,16r9a35,16r9c13,16r9c0d,16r9e79,16r9eb5,16r9ee8,
+16r9f2f,16r9f5f,16r9f63,16r9f61,16r5137,16r5138,16r56c1,16r56c0,
+16r56c2,16r5914,16r5c6c,16r5dcd,16r61fc,16r61fe,16r651d,16r651c,
+16r6595,16r66e9,16r6afb,16r6b04,16r6afa,16r6bb2,16r704c,16r721b,
+16r72a7,16r74d6,16r74d4,16r7669,16r77d3,16r7c50,16r7e8f,16r7e8c,
+16r7fbc,16r8617,16r862d,16r861a,16r8823,16r8822,16r8821,16r881f,
+16r896a,16r896c,16r89bd,16r8b74,16r8b77,16r8b7d,16r8d13,16r8e8a,
+16r8e8d,16r8e8b,16r8f5f,16r8faf,16r91ba,16r942e,16r9433,16r9435,
+16r943a,16r9438,16r9432,16r942b,16r95e2,16r9738,16r9739,16r9732,
+16r97ff,16r9867,16r9865,16r9957,16r9a45,16r9a43,16r9a40,16r9a3e,
+16r9acf,16r9b54,16r9b51,16r9c2d,16r9c25,16r9daf,16r9db4,16r9dc2,
+16r9db8,16r9e9d,16r9eef,16r9f19,16r9f5c,16r9f66,16r9f67,16r513c,
+16r513b,16r56c8,16r56ca,16r56c9,16r5b7f,16r5dd4,16r5dd2,16r5f4e,
+16r61ff,16r6524,16r6b0a,16r6b61,16r7051,16r7058,16r7380,16r74e4,
+16r758a,16r766e,16r766c,16r79b3,16r7c60,16r7c5f,16r807e,16r807d,
+16r81df,16r8972,16r896f,16r89fc,16r8b80,16r8d16,16r8d17,16r8e91,
+16r8e93,16r8f61,16r9148,16r9444,16r9451,16r9452,16r973d,16r973e,
+16r97c3,16r97c1,16r986b,16r9955,16r9a55,16r9a4d,16r9ad2,16r9b1a,
+16r9c49,16r9c31,16r9c3e,16r9c3b,16r9dd3,16r9dd7,16r9f34,16r9f6c,
+16r9f6a,16r9f94,16r56cc,16r5dd6,16r6200,16r6523,16r652b,16r652a,
+16r66ec,16r6b10,16r74da,16r7aca,16r7c64,16r7c63,16r7c65,16r7e93,
+16r7e96,16r7e94,16r81e2,16r8638,16r863f,16r8831,16r8b8a,16r9090,
+16r908f,16r9463,16r9460,16r9464,16r9768,16r986f,16r995c,16r9a5a,
+16r9a5b,16r9a57,16r9ad3,16r9ad4,16r9ad1,16r9c54,16r9c57,16r9c56,
+16r9de5,16r9e9f,16r9ef4,16r56d1,16r58e9,16r652c,16r705e,16r7671,
+16r7672,16r77d7,16r7f50,16r7f88,16r8836,16r8839,16r8862,16r8b93,
+16r8b92,16r8b96,16r8277,16r8d1b,16r91c0,16r946a,16r9742,16r9748,
+16r9744,16r97c6,16r9870,16r9a5f,16r9b22,16r9b58,16r9c5f,16r9df9,
+16r9dfa,16r9e7c,16r9e7d,16r9f07,16r9f77,16r9f72,16r5ef3,16r6b16,
+16r7063,16r7c6c,16r7c6e,16r883b,16r89c0,16r8ea1,16r91c1,16r9472,
+16r9470,16r9871,16r995e,16r9ad6,16r9b23,16r9ecc,16r7064,16r77da,
+16r8b9a,16r9477,16r97c9,16r9a62,16r9a65,16r7e9c,16r8b9c,16r8eaa,
+16r91c5,16r947d,16r947e,16r947c,16r9c77,16r9c78,16r9ef7,16r8c54,
+16r947f,16r9e1a,16r7228,16r9a6a,16r9b31,16r9e1b,16r9e1e,16r7c72,
+16r30fe,16r309d,16r309e,16r3005,16r3041,16r3042,16r3043,16r3044,
+16r3045,16r3046,16r3047,16r3048,16r3049,16r304a,16r304b,16r304c,
+16r304d,16r304e,16r304f,16r3050,16r3051,16r3052,16r3053,16r3054,
+16r3055,16r3056,16r3057,16r3058,16r3059,16r305a,16r305b,16r305c,
+16r305d,16r305e,16r305f,16r3060,16r3061,16r3062,16r3063,16r3064,
+16r3065,16r3066,16r3067,16r3068,16r3069,16r306a,16r306b,16r306c,
+16r306d,16r306e,16r306f,16r3070,16r3071,16r3072,16r3073,16r3074,
+16r3075,16r3076,16r3077,16r3078,16r3079,16r307a,16r307b,16r307c,
+16r307d,16r307e,16r307f,16r3080,16r3081,16r3082,16r3083,16r3084,
+16r3085,16r3086,16r3087,16r3088,16r3089,16r308a,16r308b,16r308c,
+16r308d,16r308e,16r308f,16r3090,16r3091,16r3092,16r3093,16r30a1,
+16r30a2,16r30a3,16r30a4,16r30a5,16r30a6,16r30a7,16r30a8,16r30a9,
+16r30aa,16r30ab,16r30ac,16r30ad,16r30ae,16r30af,16r30b0,16r30b1,
+16r30b2,16r30b3,16r30b4,16r30b5,16r30b6,16r30b7,16r30b8,16r30b9,
+16r30ba,16r30bb,16r30bc,16r30bd,16r30be,16r30bf,16r30c0,16r30c1,
+16r30c2,16r30c3,16r30c4,16r30c5,16r30c6,16r30c7,16r30c8,16r30c9,
+16r30ca,16r30cb,16r30cc,16r30cd,16r30ce,16r30cf,16r30d0,16r30d1,
+16r30d2,16r30d3,16r30d4,16r30d5,16r30d6,16r30d7,16r30d8,16r30d9,
+16r30da,16r30db,16r30dc,16r30dd,16r30de,16r30df,16r30e0,16r30e1,
+16r30e2,16r30e3,16r30e4,16r30e5,16r30e6,16r30e7,16r30e8,16r30e9,
+16r30ea,16r30eb,16r30ec,16r30ed,16r30ee,16r30ef,16r30f0,16r30f1,
+16r30f2,16r30f3,16r30f4,16r30f5,16r30f6,16r0414,16r0415,16r0401,
+16r0416,16r0417,16r0418,16r0419,16r041a,16r041b,16r041c,16r0423,
+16r0424,16r0425,16r0426,16r0427,16r0428,16r0429,16r042a,16r042b,
+16r042c,16r042d,16r042e,16r042f,16r0430,16r0431,16r0432,16r0433,
+16r0434,16r0435,16r0451,16r0436,16r0437,16r0438,16r0439,16r043a,
+16r043b,16r043c,16r043d,16r043e,16r043f,16r0440,16r0441,16r0442,
+16r0443,16r0444,16r0445,16r0446,16r0447,16r0448,16r0449,16r044a,
+16r044b,16r044c,16r044d,16r044e,16r044f,16r2460,16r2461,16r2462,
+16r2463,16r2464,16r2465,16r2466,16r2467,16r2468,16r2469,16r2474,
+16r2475,16r2476,16r2477,16r2478,16r2479,16r247a,16r247b,16r247c,
+16r247d,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+16r4e42,16r4e5c,16r51f5,16r531a,16r5382,16r4e07,16r4e0c,16r4e47,
+16r4e8d,16r56d7,16rfa0c,16r5c6e,16r5f73,16r4e0f,16r5187,16r4e0e,
+16r4e2e,16r4e93,16r4ec2,16r4ec9,16r4ec8,16r5198,16r52fc,16r536c,
+16r53b9,16r5720,16r5903,16r592c,16r5c10,16r5dff,16r65e1,16r6bb3,
+16r6bcc,16r6c14,16r723f,16r4e31,16r4e3c,16r4ee8,16r4edc,16r4ee9,
+16r4ee1,16r4edd,16r4eda,16r520c,16r531c,16r534c,16r5722,16r5723,
+16r5917,16r592f,16r5b81,16r5b84,16r5c12,16r5c3b,16r5c74,16r5c73,
+16r5e04,16r5e80,16r5e82,16r5fc9,16r6209,16r6250,16r6c15,16r6c36,
+16r6c43,16r6c3f,16r6c3b,16r72ae,16r72b0,16r738a,16r79b8,16r808a,
+16r961e,16r4f0e,16r4f18,16r4f2c,16r4ef5,16r4f14,16r4ef1,16r4f00,
+16r4ef7,16r4f08,16r4f1d,16r4f02,16r4f05,16r4f22,16r4f13,16r4f04,
+16r4ef4,16r4f12,16r51b1,16r5213,16r5209,16r5210,16r52a6,16r5322,
+16r531f,16r534d,16r538a,16r5407,16r56e1,16r56df,16r572e,16r572a,
+16r5734,16r593c,16r5980,16r597c,16r5985,16r597b,16r597e,16r5977,
+16r597f,16r5b56,16r5c15,16r5c25,16r5c7c,16r5c7a,16r5c7b,16r5c7e,
+16r5ddf,16r5e75,16r5e84,16r5f02,16r5f1a,16r5f74,16r5fd5,16r5fd4,
+16r5fcf,16r625c,16r625e,16r6264,16r6261,16r6266,16r6262,16r6259,
+16r6260,16r625a,16r6265,16r65ef,16r65ee,16r673e,16r6739,16r6738,
+16r673b,16r673a,16r673f,16r673c,16r6733,16r6c18,16r6c46,16r6c52,
+16r6c5c,16r6c4f,16r6c4a,16r6c54,16r6c4b,16r6c4c,16r7071,16r725e,
+16r72b4,16r72b5,16r738e,16r752a,16r767f,16r7a75,16r7f51,16r8278,
+16r827c,16r8280,16r827d,16r827f,16r864d,16r897e,16r9099,16r9097,
+16r9098,16r909b,16r9094,16r9622,16r9624,16r9620,16r9623,16r4f56,
+16r4f3b,16r4f62,16r4f49,16r4f53,16r4f64,16r4f3e,16r4f67,16r4f52,
+16r4f5f,16r4f41,16r4f58,16r4f2d,16r4f33,16r4f3f,16r4f61,16r518f,
+16r51b9,16r521c,16r521e,16r5221,16r52ad,16r52ae,16r5309,16r5363,
+16r5372,16r538e,16r538f,16r5430,16r5437,16r542a,16r5454,16r5445,
+16r5419,16r541c,16r5425,16r5418,16r543d,16r544f,16r5441,16r5428,
+16r5424,16r5447,16r56ee,16r56e7,16r56e5,16r5741,16r5745,16r574c,
+16r5749,16r574b,16r5752,16r5906,16r5940,16r59a6,16r5998,16r59a0,
+16r5997,16r598e,16r59a2,16r5990,16r598f,16r59a7,16r59a1,16r5b8e,
+16r5b92,16r5c28,16r5c2a,16r5c8d,16r5c8f,16r5c88,16r5c8b,16r5c89,
+16r5c92,16r5c8a,16r5c86,16r5c93,16r5c95,16r5de0,16r5e0a,16r5e0e,
+16r5e8b,16r5e89,16r5e8c,16r5e88,16r5e8d,16r5f05,16r5f1d,16r5f78,
+16r5f76,16r5fd2,16r5fd1,16r5fd0,16r5fed,16r5fe8,16r5fee,16r5ff3,
+16r5fe1,16r5fe4,16r5fe3,16r5ffa,16r5fef,16r5ff7,16r5ffb,16r6000,
+16r5ff4,16r623a,16r6283,16r628c,16r628e,16r628f,16r6294,16r6287,
+16r6271,16r627b,16r627a,16r6270,16r6281,16r6288,16r6277,16r627d,
+16r6272,16r6274,16r6537,16r65f0,16r65f4,16r65f3,16r65f2,16r65f5,
+16r6745,16r6747,16r6759,16r6755,16r674c,16r6748,16r675d,16r674d,
+16r675a,16r674b,16r6bd0,16r6c19,16r6c1a,16r6c78,16r6c67,16r6c6b,
+16r6c84,16r6c8b,16r6c8f,16r6c71,16r6c6f,16r6c69,16r6c9a,16r6c6d,
+16r6c87,16r6c95,16r6c9c,16r6c66,16r6c73,16r6c65,16r6c7b,16r6c8e,
+16r7074,16r707a,16r7263,16r72bf,16r72bd,16r72c3,16r72c6,16r72c1,
+16r72ba,16r72c5,16r7395,16r7397,16r7393,16r7394,16r7392,16r753a,
+16r7539,16r7594,16r7595,16r7681,16r793d,16r8034,16r8095,16r8099,
+16r8090,16r8092,16r809c,16r8290,16r828f,16r8285,16r828e,16r8291,
+16r8293,16r828a,16r8283,16r8284,16r8c78,16r8fc9,16r8fbf,16r909f,
+16r90a1,16r90a5,16r909e,16r90a7,16r90a0,16r9630,16r9628,16r962f,
+16r962d,16r4e33,16r4f98,16r4f7c,16r4f85,16r4f7d,16r4f80,16r4f87,
+16r4f76,16r4f74,16r4f89,16r4f84,16r4f77,16r4f4c,16r4f97,16r4f6a,
+16r4f9a,16r4f79,16r4f81,16r4f78,16r4f90,16r4f9c,16r4f94,16r4f9e,
+16r4f92,16r4f82,16r4f95,16r4f6b,16r4f6e,16r519e,16r51bc,16r51be,
+16r5235,16r5232,16r5233,16r5246,16r5231,16r52bc,16r530a,16r530b,
+16r533c,16r5392,16r5394,16r5487,16r547f,16r5481,16r5491,16r5482,
+16r5488,16r546b,16r547a,16r547e,16r5465,16r546c,16r5474,16r5466,
+16r548d,16r546f,16r5461,16r5460,16r5498,16r5463,16r5467,16r5464,
+16r56f7,16r56f9,16r576f,16r5772,16r576d,16r576b,16r5771,16r5770,
+16r5776,16r5780,16r5775,16r577b,16r5773,16r5774,16r5762,16r5768,
+16r577d,16r590c,16r5945,16r59b5,16r59ba,16r59cf,16r59ce,16r59b2,
+16r59cc,16r59c1,16r59b6,16r59bc,16r59c3,16r59d6,16r59b1,16r59bd,
+16r59c0,16r59c8,16r59b4,16r59c7,16r5b62,16r5b65,16r5b93,16r5b95,
+16r5c44,16r5c47,16r5cae,16r5ca4,16r5ca0,16r5cb5,16r5caf,16r5ca8,
+16r5cac,16r5c9f,16r5ca3,16r5cad,16r5ca2,16r5caa,16r5ca7,16r5c9d,
+16r5ca5,16r5cb6,16r5cb0,16r5ca6,16r5e17,16r5e14,16r5e19,16r5f28,
+16r5f22,16r5f23,16r5f24,16r5f54,16r5f82,16r5f7e,16r5f7d,16r5fde,
+16r5fe5,16r602d,16r6026,16r6019,16r6032,16r600b,16r6034,16r600a,
+16r6017,16r6033,16r601a,16r601e,16r602c,16r6022,16r600d,16r6010,
+16r602e,16r6013,16r6011,16r600c,16r6009,16r601c,16r6214,16r623d,
+16r62ad,16r62b4,16r62d1,16r62be,16r62aa,16r62b6,16r62ca,16r62ae,
+16r62b3,16r62af,16r62bb,16r62a9,16r62b0,16r62b8,16r653d,16r65a8,
+16r65bb,16r6609,16r65fc,16r6604,16r6612,16r6608,16r65fb,16r6603,
+16r660b,16r660d,16r6605,16r65fd,16r6611,16r6610,16r66f6,16r670a,
+16r6785,16r676c,16r678e,16r6792,16r6776,16r677b,16r6798,16r6786,
+16r6784,16r6774,16r678d,16r678c,16r677a,16r679f,16r6791,16r6799,
+16r6783,16r677d,16r6781,16r6778,16r6779,16r6794,16r6b25,16r6b80,
+16r6b7e,16r6bde,16r6c1d,16r6c93,16r6cec,16r6ceb,16r6cee,16r6cd9,
+16r6cb6,16r6cd4,16r6cad,16r6ce7,16r6cb7,16r6cd0,16r6cc2,16r6cba,
+16r6cc3,16r6cc6,16r6ced,16r6cf2,16r6cd2,16r6cdd,16r6cb4,16r6c8a,
+16r6c9d,16r6c80,16r6cde,16r6cc0,16r6d30,16r6ccd,16r6cc7,16r6cb0,
+16r6cf9,16r6ccf,16r6ce9,16r6cd1,16r7094,16r7098,16r7085,16r7093,
+16r7086,16r7084,16r7091,16r7096,16r7082,16r709a,16r7083,16r726a,
+16r72d6,16r72cb,16r72d8,16r72c9,16r72dc,16r72d2,16r72d4,16r72da,
+16r72cc,16r72d1,16r73a4,16r73a1,16r73ad,16r73a6,16r73a2,16r73a0,
+16r73ac,16r739d,16r74dd,16r74e8,16r753f,16r7540,16r753e,16r758c,
+16r7598,16r76af,16r76f3,16r76f1,16r76f0,16r76f5,16r77f8,16r77fc,
+16r77f9,16r77fb,16r77fa,16r77f7,16r7942,16r793f,16r79c5,16r7a78,
+16r7a7b,16r7afb,16r7c75,16r7cfd,16r8035,16r808f,16r80ae,16r80a3,
+16r80b8,16r80b5,16r80ad,16r8220,16r82a0,16r82c0,16r82ab,16r829a,
+16r8298,16r829b,16r82b5,16r82a7,16r82ae,16r82bc,16r829e,16r82ba,
+16r82b4,16r82a8,16r82a1,16r82a9,16r82c2,16r82a4,16r82c3,16r82b6,
+16r82a2,16r8670,16r866f,16r866d,16r866e,16r8c56,16r8fd2,16r8fcb,
+16r8fd3,16r8fcd,16r8fd6,16r8fd5,16r8fd7,16r90b2,16r90b4,16r90af,
+16r90b3,16r90b0,16r9639,16r963d,16r963c,16r963a,16r9643,16r4fcd,
+16r4fc5,16r4fd3,16r4fb2,16r4fc9,16r4fcb,16r4fc1,16r4fd4,16r4fdc,
+16r4fd9,16r4fbb,16r4fb3,16r4fdb,16r4fc7,16r4fd6,16r4fba,16r4fc0,
+16r4fb9,16r4fec,16r5244,16r5249,16r52c0,16r52c2,16r533d,16r537c,
+16r5397,16r5396,16r5399,16r5398,16r54ba,16r54a1,16r54ad,16r54a5,
+16r54cf,16r54c3,16r830d,16r54b7,16r54ae,16r54d6,16r54b6,16r54c5,
+16r54c6,16r54a0,16r5470,16r54bc,16r54a2,16r54be,16r5472,16r54de,
+16r54b0,16r57b5,16r579e,16r579f,16r57a4,16r578c,16r5797,16r579d,
+16r579b,16r5794,16r5798,16r578f,16r5799,16r57a5,16r579a,16r5795,
+16r58f4,16r590d,16r5953,16r59e1,16r59de,16r59ee,16r5a00,16r59f1,
+16r59dd,16r59fa,16r59fd,16r59fc,16r59f6,16r59e4,16r59f2,16r59f7,
+16r59db,16r59e9,16r59f3,16r59f5,16r59e0,16r59fe,16r59f4,16r59ed,
+16r5ba8,16r5c4c,16r5cd0,16r5cd8,16r5ccc,16r5cd7,16r5ccb,16r5cdb,
+16r5cde,16r5cda,16r5cc9,16r5cc7,16r5cca,16r5cd6,16r5cd3,16r5cd4,
+16r5ccf,16r5cc8,16r5cc6,16r5cce,16r5cdf,16r5cf8,16r5df9,16r5e21,
+16r5e22,16r5e23,16r5e20,16r5e24,16r5eb0,16r5ea4,16r5ea2,16r5e9b,
+16r5ea3,16r5ea5,16r5f07,16r5f2e,16r5f56,16r5f86,16r6037,16r6039,
+16r6054,16r6072,16r605e,16r6045,16r6053,16r6047,16r6049,16r605b,
+16r604c,16r6040,16r6042,16r605f,16r6024,16r6044,16r6058,16r6066,
+16r606e,16r6242,16r6243,16r62cf,16r630d,16r630b,16r62f5,16r630e,
+16r6303,16r62eb,16r62f9,16r630f,16r630c,16r62f8,16r62f6,16r6300,
+16r6313,16r6314,16r62fa,16r6315,16r62fb,16r62f0,16r6541,16r6543,
+16r65aa,16r65bf,16r6636,16r6621,16r6632,16r6635,16r661c,16r6626,
+16r6622,16r6633,16r662b,16r663a,16r661d,16r6634,16r6639,16r662e,
+16r670f,16r6710,16r67c1,16r67f2,16r67c8,16r67ba,16r67dc,16r67bb,
+16r67f8,16r67d8,16r67c0,16r67b7,16r67c5,16r67eb,16r67e4,16r67df,
+16r67b5,16r67cd,16r67b3,16r67f7,16r67f6,16r67ee,16r67e3,16r67c2,
+16r67b9,16r67ce,16r67e7,16r67f0,16r67b2,16r67fc,16r67c6,16r67ed,
+16r67cc,16r67ae,16r67e6,16r67db,16r67fa,16r67c9,16r67ca,16r67c3,
+16r67ea,16r67cb,16r6b28,16r6b82,16r6b84,16r6bb6,16r6bd6,16r6bd8,
+16r6be0,16r6c20,16r6c21,16r6d28,16r6d34,16r6d2d,16r6d1f,16r6d3c,
+16r6d3f,16r6d12,16r6d0a,16r6cda,16r6d33,16r6d04,16r6d19,16r6d3a,
+16r6d1a,16r6d11,16r6d00,16r6d1d,16r6d42,16r6d01,16r6d18,16r6d37,
+16r6d03,16r6d0f,16r6d40,16r6d07,16r6d20,16r6d2c,16r6d08,16r6d22,
+16r6d09,16r6d10,16r70b7,16r709f,16r70be,16r70b1,16r70b0,16r70a1,
+16r70b4,16r70b5,16r70a9,16r7241,16r7249,16r724a,16r726c,16r7270,
+16r7273,16r726e,16r72ca,16r72e4,16r72e8,16r72eb,16r72df,16r72ea,
+16r72e6,16r72e3,16r7385,16r73cc,16r73c2,16r73c8,16r73c5,16r73b9,
+16r73b6,16r73b5,16r73b4,16r73eb,16r73bf,16r73c7,16r73be,16r73c3,
+16r73c6,16r73b8,16r73cb,16r74ec,16r74ee,16r752e,16r7547,16r7548,
+16r75a7,16r75aa,16r7679,16r76c4,16r7708,16r7703,16r7704,16r7705,
+16r770a,16r76f7,16r76fb,16r76fa,16r77e7,16r77e8,16r7806,16r7811,
+16r7812,16r7805,16r7810,16r780f,16r780e,16r7809,16r7803,16r7813,
+16r794a,16r794c,16r794b,16r7945,16r7944,16r79d5,16r79cd,16r79cf,
+16r79d6,16r79ce,16r7a80,16r7a7e,16r7ad1,16r7b00,16r7b01,16r7c7a,
+16r7c78,16r7c79,16r7c7f,16r7c80,16r7c81,16r7d03,16r7d08,16r7d01,
+16r7f58,16r7f91,16r7f8d,16r7fbe,16r8007,16r800e,16r800f,16r8014,
+16r8037,16r80d8,16r80c7,16r80e0,16r80d1,16r80c8,16r80c2,16r80d0,
+16r80c5,16r80e3,16r80d9,16r80dc,16r80ca,16r80d5,16r80c9,16r80cf,
+16r80d7,16r80e6,16r80cd,16r81ff,16r8221,16r8294,16r82d9,16r82fe,
+16r82f9,16r8307,16r82e8,16r8300,16r82d5,16r833a,16r82eb,16r82d6,
+16r82f4,16r82ec,16r82e1,16r82f2,16r82f5,16r830c,16r82fb,16r82f6,
+16r82f0,16r82ea,16r82e4,16r82e0,16r82fa,16r82f3,16r82ed,16r8677,
+16r8674,16r867c,16r8673,16r8841,16r884e,16r8867,16r886a,16r8869,
+16r89d3,16r8a04,16r8a07,16r8d72,16r8fe3,16r8fe1,16r8fee,16r8fe0,
+16r90f1,16r90bd,16r90bf,16r90d5,16r90c5,16r90be,16r90c7,16r90cb,
+16r90c8,16r91d4,16r91d3,16r9654,16r964f,16r9651,16r9653,16r964a,
+16r964e,16r501e,16r5005,16r5007,16r5013,16r5022,16r5030,16r501b,
+16r4ff5,16r4ff4,16r5033,16r5037,16r502c,16r4ff6,16r4ff7,16r5017,
+16r501c,16r5020,16r5027,16r5035,16r502f,16r5031,16r500e,16r515a,
+16r5194,16r5193,16r51ca,16r51c4,16r51c5,16r51c8,16r51ce,16r5261,
+16r525a,16r5252,16r525e,16r525f,16r5255,16r5262,16r52cd,16r530e,
+16r539e,16r5526,16r54e2,16r5517,16r5512,16r54e7,16r54f3,16r54e4,
+16r551a,16r54ff,16r5504,16r5508,16r54eb,16r5511,16r5505,16r54f1,
+16r550a,16r54fb,16r54f7,16r54f8,16r54e0,16r550e,16r5503,16r550b,
+16r5701,16r5702,16r57cc,16r5832,16r57d5,16r57d2,16r57ba,16r57c6,
+16r57bd,16r57bc,16r57b8,16r57b6,16r57bf,16r57c7,16r57d0,16r57b9,
+16r57c1,16r590e,16r594a,16r5a19,16r5a16,16r5a2d,16r5a2e,16r5a15,
+16r5a0f,16r5a17,16r5a0a,16r5a1e,16r5a33,16r5b6c,16r5ba7,16r5bad,
+16r5bac,16r5c03,16r5c56,16r5c54,16r5cec,16r5cff,16r5cee,16r5cf1,
+16r5cf7,16r5d00,16r5cf9,16r5e29,16r5e28,16r5ea8,16r5eae,16r5eaa,
+16r5eac,16r5f33,16r5f30,16r5f67,16r605d,16r605a,16r6067,16r6041,
+16r60a2,16r6088,16r6080,16r6092,16r6081,16r609d,16r6083,16r6095,
+16r609b,16r6097,16r6087,16r609c,16r608e,16r6219,16r6246,16r62f2,
+16r6310,16r6356,16r632c,16r6344,16r6345,16r6336,16r6343,16r63e4,
+16r6339,16r634b,16r634a,16r633c,16r6329,16r6341,16r6334,16r6358,
+16r6354,16r6359,16r632d,16r6347,16r6333,16r635a,16r6351,16r6338,
+16r6357,16r6340,16r6348,16r654a,16r6546,16r65c6,16r65c3,16r65c4,
+16r65c2,16r664a,16r665f,16r6647,16r6651,16r6712,16r6713,16r681f,
+16r681a,16r6849,16r6832,16r6833,16r683b,16r684b,16r684f,16r6816,
+16r6831,16r681c,16r6835,16r682b,16r682d,16r682f,16r684e,16r6844,
+16r6834,16r681d,16r6812,16r6814,16r6826,16r6828,16r682e,16r684d,
+16r683a,16r6825,16r6820,16r6b2c,16r6b2f,16r6b2d,16r6b31,16r6b34,
+16r6b6d,16r8082,16r6b88,16r6be6,16r6be4,16r6be8,16r6be3,16r6be2,
+16r6be7,16r6c25,16r6d7a,16r6d63,16r6d64,16r6d76,16r6d0d,16r6d61,
+16r6d92,16r6d58,16r6d62,16r6d6d,16r6d6f,16r6d91,16r6d8d,16r6def,
+16r6d7f,16r6d86,16r6d5e,16r6d67,16r6d60,16r6d97,16r6d70,16r6d7c,
+16r6d5f,16r6d82,16r6d98,16r6d2f,16r6d68,16r6d8b,16r6d7e,16r6d80,
+16r6d84,16r6d16,16r6d83,16r6d7b,16r6d7d,16r6d75,16r6d90,16r70dc,
+16r70d3,16r70d1,16r70dd,16r70cb,16r7f39,16r70e2,16r70d7,16r70d2,
+16r70de,16r70e0,16r70d4,16r70cd,16r70c5,16r70c6,16r70c7,16r70da,
+16r70ce,16r70e1,16r7242,16r7278,16r7277,16r7276,16r7300,16r72fa,
+16r72f4,16r72fe,16r72f6,16r72f3,16r72fb,16r7301,16r73d3,16r73d9,
+16r73e5,16r73d6,16r73bc,16r73e7,16r73e3,16r73e9,16r73dc,16r73d2,
+16r73db,16r73d4,16r73dd,16r73da,16r73d7,16r73d8,16r73e8,16r74de,
+16r74df,16r74f4,16r74f5,16r7521,16r755b,16r755f,16r75b0,16r75c1,
+16r75bb,16r75c4,16r75c0,16r75bf,16r75b6,16r75ba,16r768a,16r76c9,
+16r771d,16r771b,16r7710,16r7713,16r7712,16r7723,16r7711,16r7715,
+16r7719,16r771a,16r7722,16r7727,16r7823,16r782c,16r7822,16r7835,
+16r782f,16r7828,16r782e,16r782b,16r7821,16r7829,16r7833,16r782a,
+16r7831,16r7954,16r795b,16r794f,16r795c,16r7953,16r7952,16r7951,
+16r79eb,16r79ec,16r79e0,16r79ee,16r79ed,16r79ea,16r79dc,16r79de,
+16r79dd,16r7a86,16r7a89,16r7a85,16r7a8b,16r7a8c,16r7a8a,16r7a87,
+16r7ad8,16r7b10,16r7b04,16r7b13,16r7b05,16r7b0f,16r7b08,16r7b0a,
+16r7b0e,16r7b09,16r7b12,16r7c84,16r7c91,16r7c8a,16r7c8c,16r7c88,
+16r7c8d,16r7c85,16r7d1e,16r7d1d,16r7d11,16r7d0e,16r7d18,16r7d16,
+16r7d13,16r7d1f,16r7d12,16r7d0f,16r7d0c,16r7f5c,16r7f61,16r7f5e,
+16r7f60,16r7f5d,16r7f5b,16r7f96,16r7f92,16r7fc3,16r7fc2,16r7fc0,
+16r8016,16r803e,16r8039,16r80fa,16r80f2,16r80f9,16r80f5,16r8101,
+16r80fb,16r8100,16r8201,16r822f,16r8225,16r8333,16r832d,16r8344,
+16r8319,16r8351,16r8325,16r8356,16r833f,16r8341,16r8326,16r831c,
+16r8322,16r8342,16r834e,16r831b,16r832a,16r8308,16r833c,16r834d,
+16r8316,16r8324,16r8320,16r8337,16r832f,16r8329,16r8347,16r8345,
+16r834c,16r8353,16r831e,16r832c,16r834b,16r8327,16r8348,16r8653,
+16r8652,16r86a2,16r86a8,16r8696,16r868d,16r8691,16r869e,16r8687,
+16r8697,16r8686,16r868b,16r869a,16r8685,16r86a5,16r8699,16r86a1,
+16r86a7,16r8695,16r8698,16r868e,16r869d,16r8690,16r8694,16r8843,
+16r8844,16r886d,16r8875,16r8876,16r8872,16r8880,16r8871,16r887f,
+16r886f,16r8883,16r887e,16r8874,16r887c,16r8a12,16r8c47,16r8c57,
+16r8c7b,16r8ca4,16r8ca3,16r8d76,16r8d78,16r8db5,16r8db7,16r8db6,
+16r8ed1,16r8ed3,16r8ffe,16r8ff5,16r9002,16r8fff,16r8ffb,16r9004,
+16r8ffc,16r8ff6,16r90d6,16r90e0,16r90d9,16r90da,16r90e3,16r90df,
+16r90e5,16r90d8,16r90db,16r90d7,16r90dc,16r90e4,16r9150,16r914e,
+16r914f,16r91d5,16r91e2,16r91da,16r965c,16r965f,16r96bc,16r98e3,
+16r9adf,16r9b2f,16r4e7f,16r5070,16r506a,16r5061,16r505e,16r5060,
+16r5053,16r504b,16r505d,16r5072,16r5048,16r504d,16r5041,16r505b,
+16r504a,16r5062,16r5015,16r5045,16r505f,16r5069,16r506b,16r5063,
+16r5064,16r5046,16r5040,16r506e,16r5073,16r5057,16r5051,16r51d0,
+16r526b,16r526d,16r526c,16r526e,16r52d6,16r52d3,16r532d,16r539c,
+16r5575,16r5576,16r553c,16r554d,16r5550,16r5534,16r552a,16r5551,
+16r5562,16r5536,16r5535,16r5530,16r5552,16r5545,16r550c,16r5532,
+16r5565,16r554e,16r5539,16r5548,16r552d,16r553b,16r5540,16r554b,
+16r570a,16r5707,16r57fb,16r5814,16r57e2,16r57f6,16r57dc,16r57f4,
+16r5800,16r57ed,16r57fd,16r5808,16r57f8,16r580b,16r57f3,16r57cf,
+16r5807,16r57ee,16r57e3,16r57f2,16r57e5,16r57ec,16r57e1,16r580e,
+16r57fc,16r5810,16r57e7,16r5801,16r580c,16r57f1,16r57e9,16r57f0,
+16r580d,16r5804,16r595c,16r5a60,16r5a58,16r5a55,16r5a67,16r5a5e,
+16r5a38,16r5a35,16r5a6d,16r5a50,16r5a5f,16r5a65,16r5a6c,16r5a53,
+16r5a64,16r5a57,16r5a43,16r5a5d,16r5a52,16r5a44,16r5a5b,16r5a48,
+16r5a8e,16r5a3e,16r5a4d,16r5a39,16r5a4c,16r5a70,16r5a69,16r5a47,
+16r5a51,16r5a56,16r5a42,16r5a5c,16r5b72,16r5b6e,16r5bc1,16r5bc0,
+16r5c59,16r5d1e,16r5d0b,16r5d1d,16r5d1a,16r5d20,16r5d0c,16r5d28,
+16r5d0d,16r5d26,16r5d25,16r5d0f,16r5d30,16r5d12,16r5d23,16r5d1f,
+16r5d2e,16r5e3e,16r5e34,16r5eb1,16r5eb4,16r5eb9,16r5eb2,16r5eb3,
+16r5f36,16r5f38,16r5f9b,16r5f96,16r5f9f,16r608a,16r6090,16r6086,
+16r60be,16r60b0,16r60ba,16r60d3,16r60d4,16r60cf,16r60e4,16r60d9,
+16r60dd,16r60c8,16r60b1,16r60db,16r60b7,16r60ca,16r60bf,16r60c3,
+16r60cd,16r60c0,16r6332,16r6365,16r638a,16r6382,16r637d,16r63bd,
+16r639e,16r63ad,16r639d,16r6397,16r63ab,16r638e,16r636f,16r6387,
+16r6390,16r636e,16r63af,16r6375,16r639c,16r636d,16r63ae,16r637c,
+16r63a4,16r633b,16r639f,16r6378,16r6385,16r6381,16r6391,16r638d,
+16r6370,16r6553,16r65cd,16r6665,16r6661,16r665b,16r6659,16r665c,
+16r6662,16r6718,16r6879,16r6887,16r6890,16r689c,16r686d,16r686e,
+16r68ae,16r68ab,16r6956,16r686f,16r68a3,16r68ac,16r68a9,16r6875,
+16r6874,16r68b2,16r688f,16r6877,16r6892,16r687c,16r686b,16r6872,
+16r68aa,16r6880,16r6871,16r687e,16r689b,16r6896,16r688b,16r68a0,
+16r6889,16r68a4,16r6878,16r687b,16r6891,16r688c,16r688a,16r687d,
+16r6b36,16r6b33,16r6b37,16r6b38,16r6b91,16r6b8f,16r6b8d,16r6b8e,
+16r6b8c,16r6c2a,16r6dc0,16r6dab,16r6db4,16r6db3,16r6e74,16r6dac,
+16r6de9,16r6de2,16r6db7,16r6df6,16r6dd4,16r6e00,16r6dc8,16r6de0,
+16r6ddf,16r6dd6,16r6dbe,16r6de5,16r6ddc,16r6ddd,16r6ddb,16r6df4,
+16r6dca,16r6dbd,16r6ded,16r6df0,16r6dba,16r6dd5,16r6dc2,16r6dcf,
+16r6dc9,16r6dd0,16r6df2,16r6dd3,16r6dfd,16r6dd7,16r6dcd,16r6de3,
+16r6dbb,16r70fa,16r710d,16r70f7,16r7117,16r70f4,16r710c,16r70f0,
+16r7104,16r70f3,16r7110,16r70fc,16r70ff,16r7106,16r7113,16r7100,
+16r70f8,16r70f6,16r710b,16r7102,16r710e,16r727e,16r727b,16r727c,
+16r727f,16r731d,16r7317,16r7307,16r7311,16r7318,16r730a,16r7308,
+16r72ff,16r730f,16r731e,16r7388,16r73f6,16r73f8,16r73f5,16r7404,
+16r7401,16r73fd,16r7407,16r7400,16r73fa,16r73fc,16r73ff,16r740c,
+16r740b,16r73f4,16r7408,16r7564,16r7563,16r75ce,16r75d2,16r75cf,
+16r75cb,16r75cc,16r75d1,16r75d0,16r768f,16r7689,16r76d3,16r7739,
+16r772f,16r772d,16r7731,16r7732,16r7734,16r7733,16r773d,16r7725,
+16r773b,16r7735,16r7848,16r7852,16r7849,16r784d,16r784a,16r784c,
+16r7826,16r7845,16r7850,16r7964,16r7967,16r7969,16r796a,16r7963,
+16r796b,16r7961,16r79bb,16r79fa,16r79f8,16r79f6,16r79f7,16r7a8f,
+16r7a94,16r7a90,16r7b35,16r7b47,16r7b34,16r7b25,16r7b30,16r7b22,
+16r7b24,16r7b33,16r7b18,16r7b2a,16r7b1d,16r7b31,16r7b2b,16r7b2d,
+16r7b2f,16r7b32,16r7b38,16r7b1a,16r7b23,16r7c94,16r7c98,16r7c96,
+16r7ca3,16r7d35,16r7d3d,16r7d38,16r7d36,16r7d3a,16r7d45,16r7d2c,
+16r7d29,16r7d41,16r7d47,16r7d3e,16r7d3f,16r7d4a,16r7d3b,16r7d28,
+16r7f63,16r7f95,16r7f9c,16r7f9d,16r7f9b,16r7fca,16r7fcb,16r7fcd,
+16r7fd0,16r7fd1,16r7fc7,16r7fcf,16r7fc9,16r801f,16r801e,16r801b,
+16r8047,16r8043,16r8048,16r8118,16r8125,16r8119,16r811b,16r812d,
+16r811f,16r812c,16r811e,16r8121,16r8115,16r8127,16r811d,16r8122,
+16r8211,16r8238,16r8233,16r823a,16r8234,16r8232,16r8274,16r8390,
+16r83a3,16r83a8,16r838d,16r837a,16r8373,16r83a4,16r8374,16r838f,
+16r8381,16r8395,16r8399,16r8375,16r8394,16r83a9,16r837d,16r8383,
+16r838c,16r839d,16r839b,16r83aa,16r838b,16r837e,16r83a5,16r83af,
+16r8388,16r8397,16r83b0,16r837f,16r83a6,16r8387,16r83ae,16r8376,
+16r839a,16r8659,16r8656,16r86bf,16r86b7,16r86c2,16r86c1,16r86c5,
+16r86ba,16r86b0,16r86c8,16r86b9,16r86b3,16r86b8,16r86cc,16r86b4,
+16r86bb,16r86bc,16r86c3,16r86bd,16r86be,16r8852,16r8889,16r8895,
+16r88a8,16r88a2,16r88aa,16r889a,16r8891,16r88a1,16r889f,16r8898,
+16r88a7,16r8899,16r889b,16r8897,16r88a4,16r88ac,16r888c,16r8893,
+16r888e,16r8982,16r89d6,16r89d9,16r89d5,16r8a30,16r8a27,16r8a2c,
+16r8a1e,16r8c39,16r8c3b,16r8c5c,16r8c5d,16r8c7d,16r8ca5,16r8d7d,
+16r8d7b,16r8d79,16r8dbc,16r8dc2,16r8db9,16r8dbf,16r8dc1,16r8ed8,
+16r8ede,16r8edd,16r8edc,16r8ed7,16r8ee0,16r8ee1,16r9024,16r900b,
+16r9011,16r901c,16r900c,16r9021,16r90ef,16r90ea,16r90f0,16r90f4,
+16r90f2,16r90f3,16r90d4,16r90eb,16r90ec,16r90e9,16r9156,16r9158,
+16r915a,16r9153,16r9155,16r91ec,16r91f4,16r91f1,16r91f3,16r91f8,
+16r91e4,16r91f9,16r91ea,16r91eb,16r91f7,16r91e8,16r91ee,16r957a,
+16r9586,16r9588,16r967c,16r966d,16r966b,16r9671,16r966f,16r96bf,
+16r976a,16r9804,16r98e5,16r9997,16r509b,16r5095,16r5094,16r509e,
+16r508b,16r50a3,16r5083,16r508c,16r508e,16r509d,16r5068,16r509c,
+16r5092,16r5082,16r5087,16r515f,16r51d4,16r5312,16r5311,16r53a4,
+16r53a7,16r5591,16r55a8,16r55a5,16r55ad,16r5577,16r5645,16r55a2,
+16r5593,16r5588,16r558f,16r55b5,16r5581,16r55a3,16r5592,16r55a4,
+16r557d,16r558c,16r55a6,16r557f,16r5595,16r55a1,16r558e,16r570c,
+16r5829,16r5837,16r5819,16r581e,16r5827,16r5823,16r5828,16r57f5,
+16r5848,16r5825,16r581c,16r581b,16r5833,16r583f,16r5836,16r582e,
+16r5839,16r5838,16r582d,16r582c,16r583b,16r5961,16r5aaf,16r5a94,
+16r5a9f,16r5a7a,16r5aa2,16r5a9e,16r5a78,16r5aa6,16r5a7c,16r5aa5,
+16r5aac,16r5a95,16r5aae,16r5a37,16r5a84,16r5a8a,16r5a97,16r5a83,
+16r5a8b,16r5aa9,16r5a7b,16r5a7d,16r5a8c,16r5a9c,16r5a8f,16r5a93,
+16r5a9d,16r5bea,16r5bcd,16r5bcb,16r5bd4,16r5bd1,16r5bca,16r5bce,
+16r5c0c,16r5c30,16r5d37,16r5d43,16r5d6b,16r5d41,16r5d4b,16r5d3f,
+16r5d35,16r5d51,16r5d4e,16r5d55,16r5d33,16r5d3a,16r5d52,16r5d3d,
+16r5d31,16r5d59,16r5d42,16r5d39,16r5d49,16r5d38,16r5d3c,16r5d32,
+16r5d36,16r5d40,16r5d45,16r5e44,16r5e41,16r5f58,16r5fa6,16r5fa5,
+16r5fab,16r60c9,16r60b9,16r60cc,16r60e2,16r60ce,16r60c4,16r6114,
+16r60f2,16r610a,16r6116,16r6105,16r60f5,16r6113,16r60f8,16r60fc,
+16r60fe,16r60c1,16r6103,16r6118,16r611d,16r6110,16r60ff,16r6104,
+16r610b,16r624a,16r6394,16r63b1,16r63b0,16r63ce,16r63e5,16r63e8,
+16r63ef,16r63c3,16r649d,16r63f3,16r63ca,16r63e0,16r63f6,16r63d5,
+16r63f2,16r63f5,16r6461,16r63df,16r63be,16r63dd,16r63dc,16r63c4,
+16r63d8,16r63d3,16r63c2,16r63c7,16r63cc,16r63cb,16r63c8,16r63f0,
+16r63d7,16r63d9,16r6532,16r6567,16r656a,16r6564,16r655c,16r6568,
+16r6565,16r658c,16r659d,16r659e,16r65ae,16r65d0,16r65d2,16r667c,
+16r666c,16r667b,16r6680,16r6671,16r6679,16r666a,16r6672,16r6701,
+16r690c,16r68d3,16r6904,16r68dc,16r692a,16r68ec,16r68ea,16r68f1,
+16r690f,16r68d6,16r68f7,16r68eb,16r68e4,16r68f6,16r6913,16r6910,
+16r68f3,16r68e1,16r6907,16r68cc,16r6908,16r6970,16r68b4,16r6911,
+16r68ef,16r68c6,16r6914,16r68f8,16r68d0,16r68fd,16r68fc,16r68e8,
+16r690b,16r690a,16r6917,16r68ce,16r68c8,16r68dd,16r68de,16r68e6,
+16r68f4,16r68d1,16r6906,16r68d4,16r68e9,16r6915,16r6925,16r68c7,
+16r6b39,16r6b3b,16r6b3f,16r6b3c,16r6b94,16r6b97,16r6b99,16r6b95,
+16r6bbd,16r6bf0,16r6bf2,16r6bf3,16r6c30,16r6dfc,16r6e46,16r6e47,
+16r6e1f,16r6e49,16r6e88,16r6e3c,16r6e3d,16r6e45,16r6e62,16r6e2b,
+16r6e3f,16r6e41,16r6e5d,16r6e73,16r6e1c,16r6e33,16r6e4b,16r6e40,
+16r6e51,16r6e3b,16r6e03,16r6e2e,16r6e5e,16r6e68,16r6e5c,16r6e61,
+16r6e31,16r6e28,16r6e60,16r6e71,16r6e6b,16r6e39,16r6e22,16r6e30,
+16r6e53,16r6e65,16r6e27,16r6e78,16r6e64,16r6e77,16r6e55,16r6e79,
+16r6e52,16r6e66,16r6e35,16r6e36,16r6e5a,16r7120,16r711e,16r712f,
+16r70fb,16r712e,16r7131,16r7123,16r7125,16r7122,16r7132,16r711f,
+16r7128,16r713a,16r711b,16r724b,16r725a,16r7288,16r7289,16r7286,
+16r7285,16r728b,16r7312,16r730b,16r7330,16r7322,16r7331,16r7333,
+16r7327,16r7332,16r732d,16r7326,16r7323,16r7335,16r730c,16r742e,
+16r742c,16r7430,16r742b,16r7416,16r741a,16r7421,16r742d,16r7431,
+16r7424,16r7423,16r741d,16r7429,16r7420,16r7432,16r74fb,16r752f,
+16r756f,16r756c,16r75e7,16r75da,16r75e1,16r75e6,16r75dd,16r75df,
+16r75e4,16r75d7,16r7695,16r7692,16r76da,16r7746,16r7747,16r7744,
+16r774d,16r7745,16r774a,16r774e,16r774b,16r774c,16r77de,16r77ec,
+16r7860,16r7864,16r7865,16r785c,16r786d,16r7871,16r786a,16r786e,
+16r7870,16r7869,16r7868,16r785e,16r7862,16r7974,16r7973,16r7972,
+16r7970,16r7a02,16r7a0a,16r7a03,16r7a0c,16r7a04,16r7a99,16r7ae6,
+16r7ae4,16r7b4a,16r7b3b,16r7b44,16r7b48,16r7b4c,16r7b4e,16r7b40,
+16r7b58,16r7b45,16r7ca2,16r7c9e,16r7ca8,16r7ca1,16r7d58,16r7d6f,
+16r7d63,16r7d53,16r7d56,16r7d67,16r7d6a,16r7d4f,16r7d6d,16r7d5c,
+16r7d6b,16r7d52,16r7d54,16r7d69,16r7d51,16r7d5f,16r7d4e,16r7f3e,
+16r7f3f,16r7f65,16r7f66,16r7fa2,16r7fa0,16r7fa1,16r7fd7,16r8051,
+16r804f,16r8050,16r80fe,16r80d4,16r8143,16r814a,16r8152,16r814f,
+16r8147,16r813d,16r814d,16r813a,16r81e6,16r81ee,16r81f7,16r81f8,
+16r81f9,16r8204,16r823c,16r823d,16r823f,16r8275,16r833b,16r83cf,
+16r83f9,16r8423,16r83c0,16r83e8,16r8412,16r83e7,16r83e4,16r83fc,
+16r83f6,16r8410,16r83c6,16r83c8,16r83eb,16r83e3,16r83bf,16r8401,
+16r83dd,16r83e5,16r83d8,16r83ff,16r83e1,16r83cb,16r83ce,16r83d6,
+16r83f5,16r83c9,16r8409,16r840f,16r83de,16r8411,16r8406,16r83c2,
+16r83f3,16r83d5,16r83fa,16r83c7,16r83d1,16r83ea,16r8413,16r83c3,
+16r83ec,16r83ee,16r83c4,16r83fb,16r83d7,16r83e2,16r841b,16r83db,
+16r83fe,16r86d8,16r86e2,16r86e6,16r86d3,16r86e3,16r86da,16r86ea,
+16r86dd,16r86eb,16r86dc,16r86ec,16r86e9,16r86d7,16r86e8,16r86d1,
+16r8848,16r8856,16r8855,16r88ba,16r88d7,16r88b9,16r88b8,16r88c0,
+16r88be,16r88b6,16r88bc,16r88b7,16r88bd,16r88b2,16r8901,16r88c9,
+16r8995,16r8998,16r8997,16r89dd,16r89da,16r89db,16r8a4e,16r8a4d,
+16r8a39,16r8a59,16r8a40,16r8a57,16r8a58,16r8a44,16r8a45,16r8a52,
+16r8a48,16r8a51,16r8a4a,16r8a4c,16r8a4f,16r8c5f,16r8c81,16r8c80,
+16r8cba,16r8cbe,16r8cb0,16r8cb9,16r8cb5,16r8d84,16r8d80,16r8d89,
+16r8dd8,16r8dd3,16r8dcd,16r8dc7,16r8dd6,16r8ddc,16r8dcf,16r8dd5,
+16r8dd9,16r8dc8,16r8dd7,16r8dc5,16r8eef,16r8ef7,16r8efa,16r8ef9,
+16r8ee6,16r8eee,16r8ee5,16r8ef5,16r8ee7,16r8ee8,16r8ef6,16r8eeb,
+16r8ef1,16r8eec,16r8ef4,16r8ee9,16r902d,16r9034,16r902f,16r9106,
+16r912c,16r9104,16r90ff,16r90fc,16r9108,16r90f9,16r90fb,16r9101,
+16r9100,16r9107,16r9105,16r9103,16r9161,16r9164,16r915f,16r9162,
+16r9160,16r9201,16r920a,16r9225,16r9203,16r921a,16r9226,16r920f,
+16r920c,16r9200,16r9212,16r91ff,16r91fd,16r9206,16r9204,16r9227,
+16r9202,16r921c,16r9224,16r9219,16r9217,16r9205,16r9216,16r957b,
+16r958d,16r958c,16r9590,16r9687,16r967e,16r9688,16r9689,16r9683,
+16r9680,16r96c2,16r96c8,16r96c3,16r96f1,16r96f0,16r976c,16r9770,
+16r976e,16r9807,16r98a9,16r98eb,16r9ce6,16r9ef9,16r4e83,16r4e84,
+16r4eb6,16r50bd,16r50bf,16r50c6,16r50ae,16r50c4,16r50ca,16r50b4,
+16r50c8,16r50c2,16r50b0,16r50c1,16r50ba,16r50b1,16r50cb,16r50c9,
+16r50b6,16r50b8,16r51d7,16r527a,16r5278,16r527b,16r527c,16r55c3,
+16r55db,16r55cc,16r55d0,16r55cb,16r55ca,16r55dd,16r55c0,16r55d4,
+16r55c4,16r55e9,16r55bf,16r55d2,16r558d,16r55cf,16r55d5,16r55e2,
+16r55d6,16r55c8,16r55f2,16r55cd,16r55d9,16r55c2,16r5714,16r5853,
+16r5868,16r5864,16r584f,16r584d,16r5849,16r586f,16r5855,16r584e,
+16r585d,16r5859,16r5865,16r585b,16r583d,16r5863,16r5871,16r58fc,
+16r5ac7,16r5ac4,16r5acb,16r5aba,16r5ab8,16r5ab1,16r5ab5,16r5ab0,
+16r5abf,16r5ac8,16r5abb,16r5ac6,16r5ab7,16r5ac0,16r5aca,16r5ab4,
+16r5ab6,16r5acd,16r5ab9,16r5a90,16r5bd6,16r5bd8,16r5bd9,16r5c1f,
+16r5c33,16r5d71,16r5d63,16r5d4a,16r5d65,16r5d72,16r5d6c,16r5d5e,
+16r5d68,16r5d67,16r5d62,16r5df0,16r5e4f,16r5e4e,16r5e4a,16r5e4d,
+16r5e4b,16r5ec5,16r5ecc,16r5ec6,16r5ecb,16r5ec7,16r5f40,16r5faf,
+16r5fad,16r60f7,16r6149,16r614a,16r612b,16r6145,16r6136,16r6132,
+16r612e,16r6146,16r612f,16r614f,16r6129,16r6140,16r6220,16r9168,
+16r6223,16r6225,16r6224,16r63c5,16r63f1,16r63eb,16r6410,16r6412,
+16r6409,16r6420,16r6424,16r6433,16r6443,16r641f,16r6415,16r6418,
+16r6439,16r6437,16r6422,16r6423,16r640c,16r6426,16r6430,16r6428,
+16r6441,16r6435,16r642f,16r640a,16r641a,16r6440,16r6425,16r6427,
+16r640b,16r63e7,16r641b,16r642e,16r6421,16r640e,16r656f,16r6592,
+16r65d3,16r6686,16r668c,16r6695,16r6690,16r668b,16r668a,16r6699,
+16r6694,16r6678,16r6720,16r6966,16r695f,16r6938,16r694e,16r6962,
+16r6971,16r693f,16r6945,16r696a,16r6939,16r6942,16r6957,16r6959,
+16r697a,16r6948,16r6949,16r6935,16r696c,16r6933,16r693d,16r6965,
+16r68f0,16r6978,16r6934,16r6969,16r6940,16r696f,16r6944,16r6976,
+16r6958,16r6941,16r6974,16r694c,16r693b,16r694b,16r6937,16r695c,
+16r694f,16r6951,16r6932,16r6952,16r692f,16r697b,16r693c,16r6b46,
+16r6b45,16r6b43,16r6b42,16r6b48,16r6b41,16r6b9b,16rfa0d,16r6bfb,
+16r6bfc,16r6bf9,16r6bf7,16r6bf8,16r6e9b,16r6ed6,16r6ec8,16r6e8f,
+16r6ec0,16r6e9f,16r6e93,16r6e94,16r6ea0,16r6eb1,16r6eb9,16r6ec6,
+16r6ed2,16r6ebd,16r6ec1,16r6e9e,16r6ec9,16r6eb7,16r6eb0,16r6ecd,
+16r6ea6,16r6ecf,16r6eb2,16r6ebe,16r6ec3,16r6edc,16r6ed8,16r6e99,
+16r6e92,16r6e8e,16r6e8d,16r6ea4,16r6ea1,16r6ebf,16r6eb3,16r6ed0,
+16r6eca,16r6e97,16r6eae,16r6ea3,16r7147,16r7154,16r7152,16r7163,
+16r7160,16r7141,16r715d,16r7162,16r7172,16r7178,16r716a,16r7161,
+16r7142,16r7158,16r7143,16r714b,16r7170,16r715f,16r7150,16r7153,
+16r7144,16r714d,16r715a,16r724f,16r728d,16r728c,16r7291,16r7290,
+16r728e,16r733c,16r7342,16r733b,16r733a,16r7340,16r734a,16r7349,
+16r7444,16r744a,16r744b,16r7452,16r7451,16r7457,16r7440,16r744f,
+16r7450,16r744e,16r7442,16r7446,16r744d,16r7454,16r74e1,16r74ff,
+16r74fe,16r74fd,16r751d,16r7579,16r7577,16r6983,16r75ef,16r760f,
+16r7603,16r75f7,16r75fe,16r75fc,16r75f9,16r75f8,16r7610,16r75fb,
+16r75f6,16r75ed,16r75f5,16r75fd,16r7699,16r76b5,16r76dd,16r7755,
+16r775f,16r7760,16r7752,16r7756,16r775a,16r7769,16r7767,16r7754,
+16r7759,16r776d,16r77e0,16r7887,16r789a,16r7894,16r788f,16r7884,
+16r7895,16r7885,16r7886,16r78a1,16r7883,16r7879,16r7899,16r7880,
+16r7896,16r787b,16r797c,16r7982,16r797d,16r7979,16r7a11,16r7a18,
+16r7a19,16r7a12,16r7a17,16r7a15,16r7a22,16r7a13,16r7a1b,16r7a10,
+16r7aa3,16r7aa2,16r7a9e,16r7aeb,16r7b66,16r7b64,16r7b6d,16r7b74,
+16r7b69,16r7b72,16r7b65,16r7b73,16r7b71,16r7b70,16r7b61,16r7b78,
+16r7b76,16r7b63,16r7cb2,16r7cb4,16r7caf,16r7d88,16r7d86,16r7d80,
+16r7d8d,16r7d7f,16r7d85,16r7d7a,16r7d8e,16r7d7b,16r7d83,16r7d7c,
+16r7d8c,16r7d94,16r7d84,16r7d7d,16r7d92,16r7f6d,16r7f6b,16r7f67,
+16r7f68,16r7f6c,16r7fa6,16r7fa5,16r7fa7,16r7fdb,16r7fdc,16r8021,
+16r8164,16r8160,16r8177,16r815c,16r8169,16r815b,16r8162,16r8172,
+16r6721,16r815e,16r8176,16r8167,16r816f,16r8144,16r8161,16r821d,
+16r8249,16r8244,16r8240,16r8242,16r8245,16r84f1,16r843f,16r8456,
+16r8476,16r8479,16r848f,16r848d,16r8465,16r8451,16r8440,16r8486,
+16r8467,16r8430,16r844d,16r847d,16r845a,16r8459,16r8474,16r8473,
+16r845d,16r8507,16r845e,16r8437,16r843a,16r8434,16r847a,16r8443,
+16r8478,16r8432,16r8445,16r8429,16r83d9,16r844b,16r842f,16r8442,
+16r842d,16r845f,16r8470,16r8439,16r844e,16r844c,16r8452,16r846f,
+16r84c5,16r848e,16r843b,16r8447,16r8436,16r8433,16r8468,16r847e,
+16r8444,16r842b,16r8460,16r8454,16r846e,16r8450,16r870b,16r8704,
+16r86f7,16r870c,16r86fa,16r86d6,16r86f5,16r874d,16r86f8,16r870e,
+16r8709,16r8701,16r86f6,16r870d,16r8705,16r88d6,16r88cb,16r88cd,
+16r88ce,16r88de,16r88db,16r88da,16r88cc,16r88d0,16r8985,16r899b,
+16r89df,16r89e5,16r89e4,16r89e1,16r89e0,16r89e2,16r89dc,16r89e6,
+16r8a76,16r8a86,16r8a7f,16r8a61,16r8a3f,16r8a77,16r8a82,16r8a84,
+16r8a75,16r8a83,16r8a81,16r8a74,16r8a7a,16r8c3c,16r8c4b,16r8c4a,
+16r8c65,16r8c64,16r8c66,16r8c86,16r8c84,16r8c85,16r8ccc,16r8d68,
+16r8d69,16r8d91,16r8d8c,16r8d8e,16r8d8f,16r8d8d,16r8d93,16r8d94,
+16r8d90,16r8d92,16r8df0,16r8de0,16r8dec,16r8df1,16r8dee,16r8dd0,
+16r8de9,16r8de3,16r8de2,16r8de7,16r8df2,16r8deb,16r8df4,16r8f06,
+16r8eff,16r8f01,16r8f00,16r8f05,16r8f07,16r8f08,16r8f02,16r8f0b,
+16r9052,16r903f,16r9044,16r9049,16r903d,16r9110,16r910d,16r910f,
+16r9111,16r9116,16r9114,16r910b,16r910e,16r916e,16r916f,16r9248,
+16r9252,16r9230,16r923a,16r9266,16r9233,16r9265,16r925e,16r9283,
+16r922e,16r924a,16r9246,16r926d,16r926c,16r924f,16r9260,16r9267,
+16r926f,16r9236,16r9261,16r9270,16r9231,16r9254,16r9263,16r9250,
+16r9272,16r924e,16r9253,16r924c,16r9256,16r9232,16r959f,16r959c,
+16r959e,16r959b,16r9692,16r9693,16r9691,16r9697,16r96ce,16r96fa,
+16r96fd,16r96f8,16r96f5,16r9773,16r9777,16r9778,16r9772,16r980f,
+16r980d,16r980e,16r98ac,16r98f6,16r98f9,16r99af,16r99b2,16r99b0,
+16r99b5,16r9aad,16r9aab,16r9b5b,16r9cea,16r9ced,16r9ce7,16r9e80,
+16r9efd,16r50e6,16r50d4,16r50d7,16r50e8,16r50f3,16r50db,16r50ea,
+16r50dd,16r50e4,16r50d3,16r50ec,16r50f0,16r50ef,16r50e3,16r50e0,
+16r51d8,16r5280,16r5281,16r52e9,16r52eb,16r5330,16r53ac,16r5627,
+16r5615,16r560c,16r5612,16r55fc,16r560f,16r561c,16r5601,16r5613,
+16r5602,16r55fa,16r561d,16r5604,16r55ff,16r55f9,16r5889,16r587c,
+16r5890,16r5898,16r5886,16r5881,16r587f,16r5874,16r588b,16r587a,
+16r5887,16r5891,16r588e,16r5876,16r5882,16r5888,16r587b,16r5894,
+16r588f,16r58fe,16r596b,16r5adc,16r5aee,16r5ae5,16r5ad5,16r5aea,
+16r5ada,16r5aed,16r5aeb,16r5af3,16r5ae2,16r5ae0,16r5adb,16r5aec,
+16r5ade,16r5add,16r5ad9,16r5ae8,16r5adf,16r5b77,16r5be0,16r5be3,
+16r5c63,16r5d82,16r5d80,16r5d7d,16r5d86,16r5d7a,16r5d81,16r5d77,
+16r5d8a,16r5d89,16r5d88,16r5d7e,16r5d7c,16r5d8d,16r5d79,16r5d7f,
+16r5e58,16r5e59,16r5e53,16r5ed8,16r5ed1,16r5ed7,16r5ece,16r5edc,
+16r5ed5,16r5ed9,16r5ed2,16r5ed4,16r5f44,16r5f43,16r5f6f,16r5fb6,
+16r612c,16r6128,16r6141,16r615e,16r6171,16r6173,16r6152,16r6153,
+16r6172,16r616c,16r6180,16r6174,16r6154,16r617a,16r615b,16r6165,
+16r613b,16r616a,16r6161,16r6156,16r6229,16r6227,16r622b,16r642b,
+16r644d,16r645b,16r645d,16r6474,16r6476,16r6472,16r6473,16r647d,
+16r6475,16r6466,16r64a6,16r644e,16r6482,16r645e,16r645c,16r644b,
+16r6453,16r6460,16r6450,16r647f,16r643f,16r646c,16r646b,16r6459,
+16r6465,16r6477,16r6573,16r65a0,16r66a1,16r66a0,16r669f,16r6705,
+16r6704,16r6722,16r69b1,16r69b6,16r69c9,16r69a0,16r69ce,16r6996,
+16r69b0,16r69ac,16r69bc,16r6991,16r6999,16r698e,16r69a7,16r698d,
+16r69a9,16r69be,16r69af,16r69bf,16r69c4,16r69bd,16r69a4,16r69d4,
+16r69b9,16r69ca,16r699a,16r69cf,16r69b3,16r6993,16r69aa,16r69a1,
+16r699e,16r69d9,16r6997,16r6990,16r69c2,16r69b5,16r69a5,16r69c6,
+16r6b4a,16r6b4d,16r6b4b,16r6b9e,16r6b9f,16r6ba0,16r6bc3,16r6bc4,
+16r6bfe,16r6ece,16r6ef5,16r6ef1,16r6f03,16r6f25,16r6ef8,16r6f37,
+16r6efb,16r6f2e,16r6f09,16r6f4e,16r6f19,16r6f1a,16r6f27,16r6f18,
+16r6f3b,16r6f12,16r6eed,16r6f0a,16r6f36,16r6f73,16r6ef9,16r6eee,
+16r6f2d,16r6f40,16r6f30,16r6f3c,16r6f35,16r6eeb,16r6f07,16r6f0e,
+16r6f43,16r6f05,16r6efd,16r6ef6,16r6f39,16r6f1c,16r6efc,16r6f3a,
+16r6f1f,16r6f0d,16r6f1e,16r6f08,16r6f21,16r7187,16r7190,16r7189,
+16r7180,16r7185,16r7182,16r718f,16r717b,16r7186,16r7181,16r7197,
+16r7244,16r7253,16r7297,16r7295,16r7293,16r7343,16r734d,16r7351,
+16r734c,16r7462,16r7473,16r7471,16r7475,16r7472,16r7467,16r746e,
+16r7500,16r7502,16r7503,16r757d,16r7590,16r7616,16r7608,16r760c,
+16r7615,16r7611,16r760a,16r7614,16r76b8,16r7781,16r777c,16r7785,
+16r7782,16r776e,16r7780,16r776f,16r777e,16r7783,16r78b2,16r78aa,
+16r78b4,16r78ad,16r78a8,16r787e,16r78ab,16r789e,16r78a5,16r78a0,
+16r78ac,16r78a2,16r78a4,16r7998,16r798a,16r798b,16r7996,16r7995,
+16r7994,16r7993,16r7997,16r7988,16r7992,16r7990,16r7a2b,16r7a4a,
+16r7a30,16r7a2f,16r7a28,16r7a26,16r7aa8,16r7aab,16r7aac,16r7aee,
+16r7b88,16r7b9c,16r7b8a,16r7b91,16r7b90,16r7b96,16r7b8d,16r7b8c,
+16r7b9b,16r7b8e,16r7b85,16r7b98,16r5284,16r7b99,16r7ba4,16r7b82,
+16r7cbb,16r7cbf,16r7cbc,16r7cba,16r7da7,16r7db7,16r7dc2,16r7da3,
+16r7daa,16r7dc1,16r7dc0,16r7dc5,16r7d9d,16r7dce,16r7dc4,16r7dc6,
+16r7dcb,16r7dcc,16r7daf,16r7db9,16r7d96,16r7dbc,16r7d9f,16r7da6,
+16r7dae,16r7da9,16r7da1,16r7dc9,16r7f73,16r7fe2,16r7fe3,16r7fe5,
+16r7fde,16r8024,16r805d,16r805c,16r8189,16r8186,16r8183,16r8187,
+16r818d,16r818c,16r818b,16r8215,16r8497,16r84a4,16r84a1,16r849f,
+16r84ba,16r84ce,16r84c2,16r84ac,16r84ae,16r84ab,16r84b9,16r84b4,
+16r84c1,16r84cd,16r84aa,16r849a,16r84b1,16r84d0,16r849d,16r84a7,
+16r84bb,16r84a2,16r8494,16r84c7,16r84cc,16r849b,16r84a9,16r84af,
+16r84a8,16r84d6,16r8498,16r84b6,16r84cf,16r84a0,16r84d7,16r84d4,
+16r84d2,16r84db,16r84b0,16r8491,16r8661,16r8733,16r8723,16r8728,
+16r876b,16r8740,16r872e,16r871e,16r8721,16r8719,16r871b,16r8743,
+16r872c,16r8741,16r873e,16r8746,16r8720,16r8732,16r872a,16r872d,
+16r873c,16r8712,16r873a,16r8731,16r8735,16r8742,16r8726,16r8727,
+16r8738,16r8724,16r871a,16r8730,16r8711,16r88f7,16r88e7,16r88f1,
+16r88f2,16r88fa,16r88fe,16r88ee,16r88fc,16r88f6,16r88fb,16r88f0,
+16r88ec,16r88eb,16r899d,16r89a1,16r899f,16r899e,16r89e9,16r89eb,
+16r89e8,16r8aab,16r8a99,16r8a8b,16r8a92,16r8a8f,16r8a96,16r8c3d,
+16r8c68,16r8c69,16r8cd5,16r8ccf,16r8cd7,16r8d96,16r8e09,16r8e02,
+16r8dff,16r8e0d,16r8dfd,16r8e0a,16r8e03,16r8e07,16r8e06,16r8e05,
+16r8dfe,16r8e00,16r8e04,16r8f10,16r8f11,16r8f0e,16r8f0d,16r9123,
+16r911c,16r9120,16r9122,16r911f,16r911d,16r911a,16r9124,16r9121,
+16r911b,16r917a,16r9172,16r9179,16r9173,16r92a5,16r92a4,16r9276,
+16r929b,16r927a,16r92a0,16r9294,16r92aa,16r928d,16r92a6,16r929a,
+16r92ab,16r9279,16r9297,16r927f,16r92a3,16r92ee,16r928e,16r9282,
+16r9295,16r92a2,16r927d,16r9288,16r92a1,16r928a,16r9286,16r928c,
+16r9299,16r92a7,16r927e,16r9287,16r92a9,16r929d,16r928b,16r922d,
+16r969e,16r96a1,16r96ff,16r9758,16r977d,16r977a,16r977e,16r9783,
+16r9780,16r9782,16r977b,16r9784,16r9781,16r977f,16r97ce,16r97cd,
+16r9816,16r98ad,16r98ae,16r9902,16r9900,16r9907,16r999d,16r999c,
+16r99c3,16r99b9,16r99bb,16r99ba,16r99c2,16r99bd,16r99c7,16r9ab1,
+16r9ae3,16r9ae7,16r9b3e,16r9b3f,16r9b60,16r9b61,16r9b5f,16r9cf1,
+16r9cf2,16r9cf5,16r9ea7,16r50ff,16r5103,16r5130,16r50f8,16r5106,
+16r5107,16r50f6,16r50fe,16r510b,16r510c,16r50fd,16r510a,16r528b,
+16r528c,16r52f1,16r52ef,16r5648,16r5642,16r564c,16r5635,16r5641,
+16r564a,16r5649,16r5646,16r5658,16r565a,16r5640,16r5633,16r563d,
+16r562c,16r563e,16r5638,16r562a,16r563a,16r571a,16r58ab,16r589d,
+16r58b1,16r58a0,16r58a3,16r58af,16r58ac,16r58a5,16r58a1,16r58ff,
+16r5aff,16r5af4,16r5afd,16r5af7,16r5af6,16r5b03,16r5af8,16r5b02,
+16r5af9,16r5b01,16r5b07,16r5b05,16r5b0f,16r5c67,16r5d99,16r5d97,
+16r5d9f,16r5d92,16r5da2,16r5d93,16r5d95,16r5da0,16r5d9c,16r5da1,
+16r5d9a,16r5d9e,16r5e69,16r5e5d,16r5e60,16r5e5c,16r7df3,16r5edb,
+16r5ede,16r5ee1,16r5f49,16r5fb2,16r618b,16r6183,16r6179,16r61b1,
+16r61b0,16r61a2,16r6189,16r619b,16r6193,16r61af,16r61ad,16r619f,
+16r6192,16r61aa,16r61a1,16r618d,16r6166,16r61b3,16r622d,16r646e,
+16r6470,16r6496,16r64a0,16r6485,16r6497,16r649c,16r648f,16r648b,
+16r648a,16r648c,16r64a3,16r649f,16r6468,16r64b1,16r6498,16r6576,
+16r657a,16r6579,16r657b,16r65b2,16r65b3,16r66b5,16r66b0,16r66a9,
+16r66b2,16r66b7,16r66aa,16r66af,16r6a00,16r6a06,16r6a17,16r69e5,
+16r69f8,16r6a15,16r69f1,16r69e4,16r6a20,16r69ff,16r69ec,16r69e2,
+16r6a1b,16r6a1d,16r69fe,16r6a27,16r69f2,16r69ee,16r6a14,16r69f7,
+16r69e7,16r6a40,16r6a08,16r69e6,16r69fb,16r6a0d,16r69fc,16r69eb,
+16r6a09,16r6a04,16r6a18,16r6a25,16r6a0f,16r69f6,16r6a26,16r6a07,
+16r69f4,16r6a16,16r6b51,16r6ba5,16r6ba3,16r6ba2,16r6ba6,16r6c01,
+16r6c00,16r6bff,16r6c02,16r6f41,16r6f26,16r6f7e,16r6f87,16r6fc6,
+16r6f92,16r6f8d,16r6f89,16r6f8c,16r6f62,16r6f4f,16r6f85,16r6f5a,
+16r6f96,16r6f76,16r6f6c,16r6f82,16r6f55,16r6f72,16r6f52,16r6f50,
+16r6f57,16r6f94,16r6f93,16r6f5d,16r6f00,16r6f61,16r6f6b,16r6f7d,
+16r6f67,16r6f90,16r6f53,16r6f8b,16r6f69,16r6f7f,16r6f95,16r6f63,
+16r6f77,16r6f6a,16r6f7b,16r71b2,16r71af,16r719b,16r71b0,16r71a0,
+16r719a,16r71a9,16r71b5,16r719d,16r71a5,16r719e,16r71a4,16r71a1,
+16r71aa,16r719c,16r71a7,16r71b3,16r7298,16r729a,16r7358,16r7352,
+16r735e,16r735f,16r7360,16r735d,16r735b,16r7361,16r735a,16r7359,
+16r7362,16r7487,16r7489,16r748a,16r7486,16r7481,16r747d,16r7485,
+16r7488,16r747c,16r7479,16r7508,16r7507,16r757e,16r7625,16r761e,
+16r7619,16r761d,16r761c,16r7623,16r761a,16r7628,16r761b,16r769c,
+16r769d,16r769e,16r769b,16r778d,16r778f,16r7789,16r7788,16r78cd,
+16r78bb,16r78cf,16r78cc,16r78d1,16r78ce,16r78d4,16r78c8,16r78c3,
+16r78c4,16r78c9,16r799a,16r79a1,16r79a0,16r799c,16r79a2,16r799b,
+16r6b76,16r7a39,16r7ab2,16r7ab4,16r7ab3,16r7bb7,16r7bcb,16r7bbe,
+16r7bac,16r7bce,16r7baf,16r7bb9,16r7bca,16r7bb5,16r7cc5,16r7cc8,
+16r7ccc,16r7ccb,16r7df7,16r7ddb,16r7dea,16r7de7,16r7dd7,16r7de1,
+16r7e03,16r7dfa,16r7de6,16r7df6,16r7df1,16r7df0,16r7dee,16r7ddf,
+16r7f76,16r7fac,16r7fb0,16r7fad,16r7fed,16r7feb,16r7fea,16r7fec,
+16r7fe6,16r7fe8,16r8064,16r8067,16r81a3,16r819f,16r819e,16r8195,
+16r81a2,16r8199,16r8197,16r8216,16r824f,16r8253,16r8252,16r8250,
+16r824e,16r8251,16r8524,16r853b,16r850f,16r8500,16r8529,16r850e,
+16r8509,16r850d,16r851f,16r850a,16r8527,16r851c,16r84fb,16r852b,
+16r84fa,16r8508,16r850c,16r84f4,16r852a,16r84f2,16r8515,16r84f7,
+16r84eb,16r84f3,16r84fc,16r8512,16r84ea,16r84e9,16r8516,16r84fe,
+16r8528,16r851d,16r852e,16r8502,16r84fd,16r851e,16r84f6,16r8531,
+16r8526,16r84e7,16r84e8,16r84f0,16r84ef,16r84f9,16r8518,16r8520,
+16r8530,16r850b,16r8519,16r852f,16r8662,16r8756,16r8763,16r8764,
+16r8777,16r87e1,16r8773,16r8758,16r8754,16r875b,16r8752,16r8761,
+16r875a,16r8751,16r875e,16r876d,16r876a,16r8750,16r874e,16r875f,
+16r875d,16r876f,16r876c,16r877a,16r876e,16r875c,16r8765,16r874f,
+16r877b,16r8775,16r8762,16r8767,16r8769,16r885a,16r8905,16r890c,
+16r8914,16r890b,16r8917,16r8918,16r8919,16r8906,16r8916,16r8911,
+16r890e,16r8909,16r89a2,16r89a4,16r89a3,16r89ed,16r89f0,16r89ec,
+16r8acf,16r8ac6,16r8ab8,16r8ad3,16r8ad1,16r8ad4,16r8ad5,16r8abb,
+16r8ad7,16r8abe,16r8ac0,16r8ac5,16r8ad8,16r8ac3,16r8aba,16r8abd,
+16r8ad9,16r8c3e,16r8c4d,16r8c8f,16r8ce5,16r8cdf,16r8cd9,16r8ce8,
+16r8cda,16r8cdd,16r8ce7,16r8da0,16r8d9c,16r8da1,16r8d9b,16r8e20,
+16r8e23,16r8e25,16r8e24,16r8e2e,16r8e15,16r8e1b,16r8e16,16r8e11,
+16r8e19,16r8e26,16r8e27,16r8e14,16r8e12,16r8e18,16r8e13,16r8e1c,
+16r8e17,16r8e1a,16r8f2c,16r8f24,16r8f18,16r8f1a,16r8f20,16r8f23,
+16r8f16,16r8f17,16r9073,16r9070,16r906f,16r9067,16r906b,16r912f,
+16r912b,16r9129,16r912a,16r9132,16r9126,16r912e,16r9185,16r9186,
+16r918a,16r9181,16r9182,16r9184,16r9180,16r92d0,16r92c3,16r92c4,
+16r92c0,16r92d9,16r92b6,16r92cf,16r92f1,16r92df,16r92d8,16r92e9,
+16r92d7,16r92dd,16r92cc,16r92ef,16r92c2,16r92e8,16r92ca,16r92c8,
+16r92ce,16r92e6,16r92cd,16r92d5,16r92c9,16r92e0,16r92de,16r92e7,
+16r92d1,16r92d3,16r92b5,16r92e1,16r92c6,16r92b4,16r957c,16r95ac,
+16r95ab,16r95ae,16r95b0,16r96a4,16r96a2,16r96d3,16r9705,16r9708,
+16r9702,16r975a,16r978a,16r978e,16r9788,16r97d0,16r97cf,16r981e,
+16r981d,16r9826,16r9829,16r9828,16r9820,16r981b,16r9827,16r98b2,
+16r9908,16r98fa,16r9911,16r9914,16r9916,16r9917,16r9915,16r99dc,
+16r99cd,16r99cf,16r99d3,16r99d4,16r99ce,16r99c9,16r99d6,16r99d8,
+16r99cb,16r99d7,16r99cc,16r9ab3,16r9aec,16r9aeb,16r9af3,16r9af2,
+16r9af1,16r9b46,16r9b43,16r9b67,16r9b74,16r9b71,16r9b66,16r9b76,
+16r9b75,16r9b70,16r9b68,16r9b64,16r9b6c,16r9cfc,16r9cfa,16r9cfd,
+16r9cff,16r9cf7,16r9d07,16r9d00,16r9cf9,16r9cfb,16r9d08,16r9d05,
+16r9d04,16r9e83,16r9ed3,16r9f0f,16r9f10,16r511c,16r5113,16r5117,
+16r511a,16r5111,16r51de,16r5334,16r53e1,16r5670,16r5660,16r566e,
+16r5673,16r5666,16r5663,16r566d,16r5672,16r565e,16r5677,16r571c,
+16r571b,16r58c8,16r58bd,16r58c9,16r58bf,16r58ba,16r58c2,16r58bc,
+16r58c6,16r5b17,16r5b19,16r5b1b,16r5b21,16r5b14,16r5b13,16r5b10,
+16r5b16,16r5b28,16r5b1a,16r5b20,16r5b1e,16r5bef,16r5dac,16r5db1,
+16r5da9,16r5da7,16r5db5,16r5db0,16r5dae,16r5daa,16r5da8,16r5db2,
+16r5dad,16r5daf,16r5db4,16r5e67,16r5e68,16r5e66,16r5e6f,16r5ee9,
+16r5ee7,16r5ee6,16r5ee8,16r5ee5,16r5f4b,16r5fbc,16r619d,16r61a8,
+16r6196,16r61c5,16r61b4,16r61c6,16r61c1,16r61cc,16r61ba,16r61bf,
+16r61b8,16r618c,16r64d7,16r64d6,16r64d0,16r64cf,16r64c9,16r64bd,
+16r6489,16r64c3,16r64db,16r64f3,16r64d9,16r6533,16r657f,16r657c,
+16r65a2,16r66c8,16r66be,16r66c0,16r66ca,16r66cb,16r66cf,16r66bd,
+16r66bb,16r66ba,16r66cc,16r6723,16r6a34,16r6a66,16r6a49,16r6a67,
+16r6a32,16r6a68,16r6a3e,16r6a5d,16r6a6d,16r6a76,16r6a5b,16r6a51,
+16r6a28,16r6a5a,16r6a3b,16r6a3f,16r6a41,16r6a6a,16r6a64,16r6a50,
+16r6a4f,16r6a54,16r6a6f,16r6a69,16r6a60,16r6a3c,16r6a5e,16r6a56,
+16r6a55,16r6a4d,16r6a4e,16r6a46,16r6b55,16r6b54,16r6b56,16r6ba7,
+16r6baa,16r6bab,16r6bc8,16r6bc7,16r6c04,16r6c03,16r6c06,16r6fad,
+16r6fcb,16r6fa3,16r6fc7,16r6fbc,16r6fce,16r6fc8,16r6f5e,16r6fc4,
+16r6fbd,16r6f9e,16r6fca,16r6fa8,16r7004,16r6fa5,16r6fae,16r6fba,
+16r6fac,16r6faa,16r6fcf,16r6fbf,16r6fb8,16r6fa2,16r6fc9,16r6fab,
+16r6fcd,16r6faf,16r6fb2,16r6fb0,16r71c5,16r71c2,16r71bf,16r71b8,
+16r71d6,16r71c0,16r71c1,16r71cb,16r71d4,16r71ca,16r71c7,16r71cf,
+16r71bd,16r71d8,16r71bc,16r71c6,16r71da,16r71db,16r729d,16r729e,
+16r7369,16r7366,16r7367,16r736c,16r7365,16r736b,16r736a,16r747f,
+16r749a,16r74a0,16r7494,16r7492,16r7495,16r74a1,16r750b,16r7580,
+16r762f,16r762d,16r7631,16r763d,16r7633,16r763c,16r7635,16r7632,
+16r7630,16r76bb,16r76e6,16r779a,16r779d,16r77a1,16r779c,16r779b,
+16r77a2,16r77a3,16r7795,16r7799,16r7797,16r78dd,16r78e9,16r78e5,
+16r78ea,16r78de,16r78e3,16r78db,16r78e1,16r78e2,16r78ed,16r78df,
+16r78e0,16r79a4,16r7a44,16r7a48,16r7a47,16r7ab6,16r7ab8,16r7ab5,
+16r7ab1,16r7ab7,16r7bde,16r7be3,16r7be7,16r7bdd,16r7bd5,16r7be5,
+16r7bda,16r7be8,16r7bf9,16r7bd4,16r7bea,16r7be2,16r7bdc,16r7beb,
+16r7bd8,16r7bdf,16r7cd2,16r7cd4,16r7cd7,16r7cd0,16r7cd1,16r7e12,
+16r7e21,16r7e17,16r7e0c,16r7e1f,16r7e20,16r7e13,16r7e0e,16r7e1c,
+16r7e15,16r7e1a,16r7e22,16r7e0b,16r7e0f,16r7e16,16r7e0d,16r7e14,
+16r7e25,16r7e24,16r7f43,16r7f7b,16r7f7c,16r7f7a,16r7fb1,16r7fef,
+16r802a,16r8029,16r806c,16r81b1,16r81a6,16r81ae,16r81b9,16r81b5,
+16r81ab,16r81b0,16r81ac,16r81b4,16r81b2,16r81b7,16r81a7,16r81f2,
+16r8255,16r8256,16r8257,16r8556,16r8545,16r856b,16r854d,16r8553,
+16r8561,16r8558,16r8540,16r8546,16r8564,16r8541,16r8562,16r8544,
+16r8551,16r8547,16r8563,16r853e,16r855b,16r8571,16r854e,16r856e,
+16r8575,16r8555,16r8567,16r8560,16r858c,16r8566,16r855d,16r8554,
+16r8565,16r856c,16r8663,16r8665,16r8664,16r879b,16r878f,16r8797,
+16r8793,16r8792,16r8788,16r8781,16r8796,16r8798,16r8779,16r8787,
+16r87a3,16r8785,16r8790,16r8791,16r879d,16r8784,16r8794,16r879c,
+16r879a,16r8789,16r891e,16r8926,16r8930,16r892d,16r892e,16r8927,
+16r8931,16r8922,16r8929,16r8923,16r892f,16r892c,16r891f,16r89f1,
+16r8ae0,16r8ae2,16r8af2,16r8af4,16r8af5,16r8add,16r8b14,16r8ae4,
+16r8adf,16r8af0,16r8ac8,16r8ade,16r8ae1,16r8ae8,16r8aff,16r8aef,
+16r8afb,16r8c91,16r8c92,16r8c90,16r8cf5,16r8cee,16r8cf1,16r8cf0,
+16r8cf3,16r8d6c,16r8d6e,16r8da5,16r8da7,16r8e33,16r8e3e,16r8e38,
+16r8e40,16r8e45,16r8e36,16r8e3c,16r8e3d,16r8e41,16r8e30,16r8e3f,
+16r8ebd,16r8f36,16r8f2e,16r8f35,16r8f32,16r8f39,16r8f37,16r8f34,
+16r9076,16r9079,16r907b,16r9086,16r90fa,16r9133,16r9135,16r9136,
+16r9193,16r9190,16r9191,16r918d,16r918f,16r9327,16r931e,16r9308,
+16r931f,16r9306,16r930f,16r937a,16r9338,16r933c,16r931b,16r9323,
+16r9312,16r9301,16r9346,16r932d,16r930e,16r930d,16r92cb,16r931d,
+16r92fa,16r9325,16r9313,16r92f9,16r92f7,16r9334,16r9302,16r9324,
+16r92ff,16r9329,16r9339,16r9335,16r932a,16r9314,16r930c,16r930b,
+16r92fe,16r9309,16r9300,16r92fb,16r9316,16r95bc,16r95cd,16r95be,
+16r95b9,16r95ba,16r95b6,16r95bf,16r95b5,16r95bd,16r96a9,16r96d4,
+16r970b,16r9712,16r9710,16r9799,16r9797,16r9794,16r97f0,16r97f8,
+16r9835,16r982f,16r9832,16r9924,16r991f,16r9927,16r9929,16r999e,
+16r99ee,16r99ec,16r99e5,16r99e4,16r99f0,16r99e3,16r99ea,16r99e9,
+16r99e7,16r9ab9,16r9abf,16r9ab4,16r9abb,16r9af6,16r9afa,16r9af9,
+16r9af7,16r9b33,16r9b80,16r9b85,16r9b87,16r9b7c,16r9b7e,16r9b7b,
+16r9b82,16r9b93,16r9b92,16r9b90,16r9b7a,16r9b95,16r9b7d,16r9b88,
+16r9d25,16r9d17,16r9d20,16r9d1e,16r9d14,16r9d29,16r9d1d,16r9d18,
+16r9d22,16r9d10,16r9d19,16r9d1f,16r9e88,16r9e86,16r9e87,16r9eae,
+16r9ead,16r9ed5,16r9ed6,16r9efa,16r9f12,16r9f3d,16r5126,16r5125,
+16r5122,16r5124,16r5120,16r5129,16r52f4,16r5693,16r568c,16r568d,
+16r5686,16r5684,16r5683,16r567e,16r5682,16r567f,16r5681,16r58d6,
+16r58d4,16r58cf,16r58d2,16r5b2d,16r5b25,16r5b32,16r5b23,16r5b2c,
+16r5b27,16r5b26,16r5b2f,16r5b2e,16r5b7b,16r5bf1,16r5bf2,16r5db7,
+16r5e6c,16r5e6a,16r5fbe,16r5fbb,16r61c3,16r61b5,16r61bc,16r61e7,
+16r61e0,16r61e5,16r61e4,16r61e8,16r61de,16r64ef,16r64e9,16r64e3,
+16r64eb,16r64e4,16r64e8,16r6581,16r6580,16r65b6,16r65da,16r66d2,
+16r6a8d,16r6a96,16r6a81,16r6aa5,16r6a89,16r6a9f,16r6a9b,16r6aa1,
+16r6a9e,16r6a87,16r6a93,16r6a8e,16r6a95,16r6a83,16r6aa8,16r6aa4,
+16r6a91,16r6a7f,16r6aa6,16r6a9a,16r6a85,16r6a8c,16r6a92,16r6b5b,
+16r6bad,16r6c09,16r6fcc,16r6fa9,16r6ff4,16r6fd4,16r6fe3,16r6fdc,
+16r6fed,16r6fe7,16r6fe6,16r6fde,16r6ff2,16r6fdd,16r6fe2,16r6fe8,
+16r71e1,16r71f1,16r71e8,16r71f2,16r71e4,16r71f0,16r71e2,16r7373,
+16r736e,16r736f,16r7497,16r74b2,16r74ab,16r7490,16r74aa,16r74ad,
+16r74b1,16r74a5,16r74af,16r7510,16r7511,16r7512,16r750f,16r7584,
+16r7643,16r7648,16r7649,16r7647,16r76a4,16r76e9,16r77b5,16r77ab,
+16r77b2,16r77b7,16r77b6,16r77b4,16r77b1,16r77a8,16r77f0,16r78f3,
+16r78fd,16r7902,16r78fb,16r78fc,16r78f2,16r7905,16r78f9,16r78fe,
+16r7904,16r79ab,16r79a8,16r7a5c,16r7a5b,16r7a56,16r7a58,16r7a54,
+16r7a5a,16r7abe,16r7ac0,16r7ac1,16r7c05,16r7c0f,16r7bf2,16r7c00,
+16r7bff,16r7bfb,16r7c0e,16r7bf4,16r7c0b,16r7bf3,16r7c02,16r7c09,
+16r7c03,16r7c01,16r7bf8,16r7bfd,16r7c06,16r7bf0,16r7bf1,16r7c10,
+16r7c0a,16r7ce8,16r7e2d,16r7e3c,16r7e42,16r7e33,16r9848,16r7e38,
+16r7e2a,16r7e49,16r7e40,16r7e47,16r7e29,16r7e4c,16r7e30,16r7e3b,
+16r7e36,16r7e44,16r7e3a,16r7f45,16r7f7f,16r7f7e,16r7f7d,16r7ff4,
+16r7ff2,16r802c,16r81bb,16r81c4,16r81cc,16r81ca,16r81c5,16r81c7,
+16r81bc,16r81e9,16r825b,16r825a,16r825c,16r8583,16r8580,16r858f,
+16r85a7,16r8595,16r85a0,16r858b,16r85a3,16r857b,16r85a4,16r859a,
+16r859e,16r8577,16r857c,16r8589,16r85a1,16r857a,16r8578,16r8557,
+16r858e,16r8596,16r8586,16r858d,16r8599,16r859d,16r8581,16r85a2,
+16r8582,16r8588,16r8585,16r8579,16r8576,16r8598,16r8590,16r859f,
+16r8668,16r87be,16r87aa,16r87ad,16r87c5,16r87b0,16r87ac,16r87b9,
+16r87b5,16r87bc,16r87ae,16r87c9,16r87c3,16r87c2,16r87cc,16r87b7,
+16r87af,16r87c4,16r87ca,16r87b4,16r87b6,16r87bf,16r87b8,16r87bd,
+16r87de,16r87b2,16r8935,16r8933,16r893c,16r893e,16r8941,16r8952,
+16r8937,16r8942,16r89ad,16r89af,16r89ae,16r89f2,16r89f3,16r8b1e,
+16r8b18,16r8b16,16r8b11,16r8b05,16r8b0b,16r8b22,16r8b0f,16r8b12,
+16r8b15,16r8b07,16r8b0d,16r8b08,16r8b06,16r8b1c,16r8b13,16r8b1a,
+16r8c4f,16r8c70,16r8c72,16r8c71,16r8c6f,16r8c95,16r8c94,16r8cf9,
+16r8d6f,16r8e4e,16r8e4d,16r8e53,16r8e50,16r8e4c,16r8e47,16r8f43,
+16r8f40,16r9085,16r907e,16r9138,16r919a,16r91a2,16r919b,16r9199,
+16r919f,16r91a1,16r919d,16r91a0,16r93a1,16r9383,16r93af,16r9364,
+16r9356,16r9347,16r937c,16r9358,16r935c,16r9376,16r9349,16r9350,
+16r9351,16r9360,16r936d,16r938f,16r934c,16r936a,16r9379,16r9357,
+16r9355,16r9352,16r934f,16r9371,16r9377,16r937b,16r9361,16r935e,
+16r9363,16r9367,16r9380,16r934e,16r9359,16r95c7,16r95c0,16r95c9,
+16r95c3,16r95c5,16r95b7,16r96ae,16r96b0,16r96ac,16r9720,16r971f,
+16r9718,16r971d,16r9719,16r979a,16r97a1,16r979c,16r979e,16r979d,
+16r97d5,16r97d4,16r97f1,16r9841,16r9844,16r984a,16r9849,16r9845,
+16r9843,16r9925,16r992b,16r992c,16r992a,16r9933,16r9932,16r992f,
+16r992d,16r9931,16r9930,16r9998,16r99a3,16r99a1,16r9a02,16r99fa,
+16r99f4,16r99f7,16r99f9,16r99f8,16r99f6,16r99fb,16r99fd,16r99fe,
+16r99fc,16r9a03,16r9abe,16r9afe,16r9afd,16r9b01,16r9afc,16r9b48,
+16r9b9a,16r9ba8,16r9b9e,16r9b9b,16r9ba6,16r9ba1,16r9ba5,16r9ba4,
+16r9b86,16r9ba2,16r9ba0,16r9baf,16r9d33,16r9d41,16r9d67,16r9d36,
+16r9d2e,16r9d2f,16r9d31,16r9d38,16r9d30,16r9d45,16r9d42,16r9d43,
+16r9d3e,16r9d37,16r9d40,16r9d3d,16r7ff5,16r9d2d,16r9e8a,16r9e89,
+16r9e8d,16r9eb0,16r9ec8,16r9eda,16r9efb,16r9eff,16r9f24,16r9f23,
+16r9f22,16r9f54,16r9fa0,16r5131,16r512d,16r512e,16r5698,16r569c,
+16r5697,16r569a,16r569d,16r5699,16r5970,16r5b3c,16r5c69,16r5c6a,
+16r5dc0,16r5e6d,16r5e6e,16r61d8,16r61df,16r61ed,16r61ee,16r61f1,
+16r61ea,16r61f0,16r61eb,16r61d6,16r61e9,16r64ff,16r6504,16r64fd,
+16r64f8,16r6501,16r6503,16r64fc,16r6594,16r65db,16r66da,16r66db,
+16r66d8,16r6ac5,16r6ab9,16r6abd,16r6ae1,16r6ac6,16r6aba,16r6ab6,
+16r6ab7,16r6ac7,16r6ab4,16r6aad,16r6b5e,16r6bc9,16r6c0b,16r7007,
+16r700c,16r700d,16r7001,16r7005,16r7014,16r700e,16r6fff,16r7000,
+16r6ffb,16r7026,16r6ffc,16r6ff7,16r700a,16r7201,16r71ff,16r71f9,
+16r7203,16r71fd,16r7376,16r74b8,16r74c0,16r74b5,16r74c1,16r74be,
+16r74b6,16r74bb,16r74c2,16r7514,16r7513,16r765c,16r7664,16r7659,
+16r7650,16r7653,16r7657,16r765a,16r76a6,16r76bd,16r76ec,16r77c2,
+16r77ba,16r78ff,16r790c,16r7913,16r7914,16r7909,16r7910,16r7912,
+16r7911,16r79ad,16r79ac,16r7a5f,16r7c1c,16r7c29,16r7c19,16r7c20,
+16r7c1f,16r7c2d,16r7c1d,16r7c26,16r7c28,16r7c22,16r7c25,16r7c30,
+16r7e5c,16r7e50,16r7e56,16r7e63,16r7e58,16r7e62,16r7e5f,16r7e51,
+16r7e60,16r7e57,16r7e53,16r7fb5,16r7fb3,16r7ff7,16r7ff8,16r8075,
+16r81d1,16r81d2,16r81d0,16r825f,16r825e,16r85b4,16r85c6,16r85c0,
+16r85c3,16r85c2,16r85b3,16r85b5,16r85bd,16r85c7,16r85c4,16r85bf,
+16r85cb,16r85ce,16r85c8,16r85c5,16r85b1,16r85b6,16r85d2,16r8624,
+16r85b8,16r85b7,16r85be,16r8669,16r87e7,16r87e6,16r87e2,16r87db,
+16r87eb,16r87ea,16r87e5,16r87df,16r87f3,16r87e4,16r87d4,16r87dc,
+16r87d3,16r87ed,16r87d8,16r87e3,16r87a4,16r87d7,16r87d9,16r8801,
+16r87f4,16r87e8,16r87dd,16r8953,16r894b,16r894f,16r894c,16r8946,
+16r8950,16r8951,16r8949,16r8b2a,16r8b27,16r8b23,16r8b33,16r8b30,
+16r8b35,16r8b47,16r8b2f,16r8b3c,16r8b3e,16r8b31,16r8b25,16r8b37,
+16r8b26,16r8b36,16r8b2e,16r8b24,16r8b3b,16r8b3d,16r8b3a,16r8c42,
+16r8c75,16r8c99,16r8c98,16r8c97,16r8cfe,16r8d04,16r8d02,16r8d00,
+16r8e5c,16r8e62,16r8e60,16r8e57,16r8e56,16r8e5e,16r8e65,16r8e67,
+16r8e5b,16r8e5a,16r8e61,16r8e5d,16r8e69,16r8e54,16r8f46,16r8f47,
+16r8f48,16r8f4b,16r9128,16r913a,16r913b,16r913e,16r91a8,16r91a5,
+16r91a7,16r91af,16r91aa,16r93b5,16r938c,16r9392,16r93b7,16r939b,
+16r939d,16r9389,16r93a7,16r938e,16r93aa,16r939e,16r93a6,16r9395,
+16r9388,16r9399,16r939f,16r938d,16r93b1,16r9391,16r93b2,16r93a4,
+16r93a8,16r93b4,16r93a3,16r93a5,16r95d2,16r95d3,16r95d1,16r96b3,
+16r96d7,16r96da,16r5dc2,16r96df,16r96d8,16r96dd,16r9723,16r9722,
+16r9725,16r97ac,16r97ae,16r97a8,16r97ab,16r97a4,16r97aa,16r97a2,
+16r97a5,16r97d7,16r97d9,16r97d6,16r97d8,16r97fa,16r9850,16r9851,
+16r9852,16r98b8,16r9941,16r993c,16r993a,16r9a0f,16r9a0b,16r9a09,
+16r9a0d,16r9a04,16r9a11,16r9a0a,16r9a05,16r9a07,16r9a06,16r9ac0,
+16r9adc,16r9b08,16r9b04,16r9b05,16r9b29,16r9b35,16r9b4a,16r9b4c,
+16r9b4b,16r9bc7,16r9bc6,16r9bc3,16r9bbf,16r9bc1,16r9bb5,16r9bb8,
+16r9bd3,16r9bb6,16r9bc4,16r9bb9,16r9bbd,16r9d5c,16r9d53,16r9d4f,
+16r9d4a,16r9d5b,16r9d4b,16r9d59,16r9d56,16r9d4c,16r9d57,16r9d52,
+16r9d54,16r9d5f,16r9d58,16r9d5a,16r9e8e,16r9e8c,16r9edf,16r9f01,
+16r9f00,16r9f16,16r9f25,16r9f2b,16r9f2a,16r9f29,16r9f28,16r9f4c,
+16r9f55,16r5134,16r5135,16r5296,16r52f7,16r53b4,16r56ab,16r56ad,
+16r56a6,16r56a7,16r56aa,16r56ac,16r58da,16r58dd,16r58db,16r5912,
+16r5b3d,16r5b3e,16r5b3f,16r5dc3,16r5e70,16r5fbf,16r61fb,16r6507,
+16r6510,16r650d,16r6509,16r650c,16r650e,16r6584,16r65de,16r65dd,
+16r66de,16r6ae7,16r6ae0,16r6acc,16r6ad1,16r6ad9,16r6acb,16r6adf,
+16r6adc,16r6ad0,16r6aeb,16r6acf,16r6acd,16r6ade,16r6b60,16r6bb0,
+16r6c0c,16r7019,16r7027,16r7020,16r7016,16r702b,16r7021,16r7022,
+16r7023,16r7029,16r7017,16r7024,16r701c,16r702a,16r720c,16r720a,
+16r7207,16r7202,16r7205,16r72a5,16r72a6,16r72a4,16r72a3,16r72a1,
+16r74cb,16r74c5,16r74b7,16r74c3,16r7516,16r7660,16r77c9,16r77ca,
+16r77c4,16r77f1,16r791d,16r791b,16r7921,16r791c,16r7917,16r791e,
+16r79b0,16r7a67,16r7a68,16r7c33,16r7c3c,16r7c39,16r7c2c,16r7c3b,
+16r7cec,16r7cea,16r7e76,16r7e75,16r7e78,16r7e70,16r7e77,16r7e6f,
+16r7e7a,16r7e72,16r7e74,16r7e68,16r7f4b,16r7f4a,16r7f83,16r7f86,
+16r7fb7,16r7ffd,16r7ffe,16r8078,16r81d7,16r81d5,16r8264,16r8261,
+16r8263,16r85eb,16r85f1,16r85ed,16r85d9,16r85e1,16r85e8,16r85da,
+16r85d7,16r85ec,16r85f2,16r85f8,16r85d8,16r85df,16r85e3,16r85dc,
+16r85d1,16r85f0,16r85e6,16r85ef,16r85de,16r85e2,16r8800,16r87fa,
+16r8803,16r87f6,16r87f7,16r8809,16r880c,16r880b,16r8806,16r87fc,
+16r8808,16r87ff,16r880a,16r8802,16r8962,16r895a,16r895b,16r8957,
+16r8961,16r895c,16r8958,16r895d,16r8959,16r8988,16r89b7,16r89b6,
+16r89f6,16r8b50,16r8b48,16r8b4a,16r8b40,16r8b53,16r8b56,16r8b54,
+16r8b4b,16r8b55,16r8b51,16r8b42,16r8b52,16r8b57,16r8c43,16r8c77,
+16r8c76,16r8c9a,16r8d06,16r8d07,16r8d09,16r8dac,16r8daa,16r8dad,
+16r8dab,16r8e6d,16r8e78,16r8e73,16r8e6a,16r8e6f,16r8e7b,16r8ec2,
+16r8f52,16r8f51,16r8f4f,16r8f50,16r8f53,16r8fb4,16r9140,16r913f,
+16r91b0,16r91ad,16r93de,16r93c7,16r93cf,16r93c2,16r93da,16r93d0,
+16r93f9,16r93ec,16r93cc,16r93d9,16r93a9,16r93e6,16r93ca,16r93d4,
+16r93ee,16r93e3,16r93d5,16r93c4,16r93ce,16r93c0,16r93d2,16r93e7,
+16r957d,16r95da,16r95db,16r96e1,16r9729,16r972b,16r972c,16r9728,
+16r9726,16r97b3,16r97b7,16r97b6,16r97dd,16r97de,16r97df,16r985c,
+16r9859,16r985d,16r9857,16r98bf,16r98bd,16r98bb,16r98be,16r9948,
+16r9947,16r9943,16r99a6,16r99a7,16r9a1a,16r9a15,16r9a25,16r9a1d,
+16r9a24,16r9a1b,16r9a22,16r9a20,16r9a27,16r9a23,16r9a1e,16r9a1c,
+16r9a14,16r9ac2,16r9b0b,16r9b0a,16r9b0e,16r9b0c,16r9b37,16r9bea,
+16r9beb,16r9be0,16r9bde,16r9be4,16r9be6,16r9be2,16r9bf0,16r9bd4,
+16r9bd7,16r9bec,16r9bdc,16r9bd9,16r9be5,16r9bd5,16r9be1,16r9bda,
+16r9d77,16r9d81,16r9d8a,16r9d84,16r9d88,16r9d71,16r9d80,16r9d78,
+16r9d86,16r9d8b,16r9d8c,16r9d7d,16r9d6b,16r9d74,16r9d75,16r9d70,
+16r9d69,16r9d85,16r9d73,16r9d7b,16r9d82,16r9d6f,16r9d79,16r9d7f,
+16r9d87,16r9d68,16r9e94,16r9e91,16r9ec0,16r9efc,16r9f2d,16r9f40,
+16r9f41,16r9f4d,16r9f56,16r9f57,16r9f58,16r5337,16r56b2,16r56b5,
+16r56b3,16r58e3,16r5b45,16r5dc6,16r5dc7,16r5eee,16r5eef,16r5fc0,
+16r5fc1,16r61f9,16r6517,16r6516,16r6515,16r6513,16r65df,16r66e8,
+16r66e3,16r66e4,16r6af3,16r6af0,16r6aea,16r6ae8,16r6af9,16r6af1,
+16r6aee,16r6aef,16r703c,16r7035,16r702f,16r7037,16r7034,16r7031,
+16r7042,16r7038,16r703f,16r703a,16r7039,16r7040,16r703b,16r7033,
+16r7041,16r7213,16r7214,16r72a8,16r737d,16r737c,16r74ba,16r76ab,
+16r76aa,16r76be,16r76ed,16r77cc,16r77ce,16r77cf,16r77cd,16r77f2,
+16r7925,16r7923,16r7927,16r7928,16r7924,16r7929,16r79b2,16r7a6e,
+16r7a6c,16r7a6d,16r7af7,16r7c49,16r7c48,16r7c4a,16r7c47,16r7c45,
+16r7cee,16r7e7b,16r7e7e,16r7e81,16r7e80,16r7fba,16r7fff,16r8079,
+16r81db,16r81d9,16r820b,16r8268,16r8269,16r8622,16r85ff,16r8601,
+16r85fe,16r861b,16r8600,16r85f6,16r8604,16r8609,16r8605,16r860c,
+16r85fd,16r8819,16r8810,16r8811,16r8817,16r8813,16r8816,16r8963,
+16r8966,16r89b9,16r89f7,16r8b60,16r8b6a,16r8b5d,16r8b68,16r8b63,
+16r8b65,16r8b67,16r8b6d,16r8dae,16r8e86,16r8e88,16r8e84,16r8f59,
+16r8f56,16r8f57,16r8f55,16r8f58,16r8f5a,16r908d,16r9143,16r9141,
+16r91b7,16r91b5,16r91b2,16r91b3,16r940b,16r9413,16r93fb,16r9420,
+16r940f,16r9414,16r93fe,16r9415,16r9410,16r9428,16r9419,16r940d,
+16r93f5,16r9400,16r93f7,16r9407,16r940e,16r9416,16r9412,16r93fa,
+16r9409,16r93f8,16r940a,16r93ff,16r93fc,16r940c,16r93f6,16r9411,
+16r9406,16r95de,16r95e0,16r95df,16r972e,16r972f,16r97b9,16r97bb,
+16r97fd,16r97fe,16r9860,16r9862,16r9863,16r985f,16r98c1,16r98c2,
+16r9950,16r994e,16r9959,16r994c,16r994b,16r9953,16r9a32,16r9a34,
+16r9a31,16r9a2c,16r9a2a,16r9a36,16r9a29,16r9a2e,16r9a38,16r9a2d,
+16r9ac7,16r9aca,16r9ac6,16r9b10,16r9b12,16r9b11,16r9c0b,16r9c08,
+16r9bf7,16r9c05,16r9c12,16r9bf8,16r9c40,16r9c07,16r9c0e,16r9c06,
+16r9c17,16r9c14,16r9c09,16r9d9f,16r9d99,16r9da4,16r9d9d,16r9d92,
+16r9d98,16r9d90,16r9d9b,16r9da0,16r9d94,16r9d9c,16r9daa,16r9d97,
+16r9da1,16r9d9a,16r9da2,16r9da8,16r9d9e,16r9da3,16r9dbf,16r9da9,
+16r9d96,16r9da6,16r9da7,16r9e99,16r9e9b,16r9e9a,16r9ee5,16r9ee4,
+16r9ee7,16r9ee6,16r9f30,16r9f2e,16r9f5b,16r9f60,16r9f5e,16r9f5d,
+16r9f59,16r9f91,16r513a,16r5139,16r5298,16r5297,16r56c3,16r56bd,
+16r56be,16r5b48,16r5b47,16r5dcb,16r5dcf,16r5ef1,16r61fd,16r651b,
+16r6b02,16r6afc,16r6b03,16r6af8,16r6b00,16r7043,16r7044,16r704a,
+16r7048,16r7049,16r7045,16r7046,16r721d,16r721a,16r7219,16r737e,
+16r7517,16r766a,16r77d0,16r792d,16r7931,16r792f,16r7c54,16r7c53,
+16r7cf2,16r7e8a,16r7e87,16r7e88,16r7e8b,16r7e86,16r7e8d,16r7f4d,
+16r7fbb,16r8030,16r81dd,16r8618,16r862a,16r8626,16r861f,16r8623,
+16r861c,16r8619,16r8627,16r862e,16r8621,16r8620,16r8629,16r861e,
+16r8625,16r8829,16r881d,16r881b,16r8820,16r8824,16r881c,16r882b,
+16r884a,16r896d,16r8969,16r896e,16r896b,16r89fa,16r8b79,16r8b78,
+16r8b45,16r8b7a,16r8b7b,16r8d10,16r8d14,16r8daf,16r8e8e,16r8e8c,
+16r8f5e,16r8f5b,16r8f5d,16r9146,16r9144,16r9145,16r91b9,16r943f,
+16r943b,16r9436,16r9429,16r943d,16r943c,16r9430,16r9439,16r942a,
+16r9437,16r942c,16r9440,16r9431,16r95e5,16r95e4,16r95e3,16r9735,
+16r973a,16r97bf,16r97e1,16r9864,16r98c9,16r98c6,16r98c0,16r9958,
+16r9956,16r9a39,16r9a3d,16r9a46,16r9a44,16r9a42,16r9a41,16r9a3a,
+16r9a3f,16r9acd,16r9b15,16r9b17,16r9b18,16r9b16,16r9b3a,16r9b52,
+16r9c2b,16r9c1d,16r9c1c,16r9c2c,16r9c23,16r9c28,16r9c29,16r9c24,
+16r9c21,16r9db7,16r9db6,16r9dbc,16r9dc1,16r9dc7,16r9dca,16r9dcf,
+16r9dbe,16r9dc5,16r9dc3,16r9dbb,16r9db5,16r9dce,16r9db9,16r9dba,
+16r9dac,16r9dc8,16r9db1,16r9dad,16r9dcc,16r9db3,16r9dcd,16r9db2,
+16r9e7a,16r9e9c,16r9eeb,16r9eee,16r9eed,16r9f1b,16r9f18,16r9f1a,
+16r9f31,16r9f4e,16r9f65,16r9f64,16r9f92,16r4eb9,16r56c6,16r56c5,
+16r56cb,16r5971,16r5b4b,16r5b4c,16r5dd5,16r5dd1,16r5ef2,16r6521,
+16r6520,16r6526,16r6522,16r6b0b,16r6b08,16r6b09,16r6c0d,16r7055,
+16r7056,16r7057,16r7052,16r721e,16r721f,16r72a9,16r737f,16r74d8,
+16r74d5,16r74d9,16r74d7,16r766d,16r76ad,16r7935,16r79b4,16r7a70,
+16r7a71,16r7c57,16r7c5c,16r7c59,16r7c5b,16r7c5a,16r7cf4,16r7cf1,
+16r7e91,16r7f4f,16r7f87,16r81de,16r826b,16r8634,16r8635,16r8633,
+16r862c,16r8632,16r8636,16r882c,16r8828,16r8826,16r882a,16r8825,
+16r8971,16r89bf,16r89be,16r89fb,16r8b7e,16r8b84,16r8b82,16r8b86,
+16r8b85,16r8b7f,16r8d15,16r8e95,16r8e94,16r8e9a,16r8e92,16r8e90,
+16r8e96,16r8e97,16r8f60,16r8f62,16r9147,16r944c,16r9450,16r944a,
+16r944b,16r944f,16r9447,16r9445,16r9448,16r9449,16r9446,16r973f,
+16r97e3,16r986a,16r9869,16r98cb,16r9954,16r995b,16r9a4e,16r9a53,
+16r9a54,16r9a4c,16r9a4f,16r9a48,16r9a4a,16r9a49,16r9a52,16r9a50,
+16r9ad0,16r9b19,16r9b2b,16r9b3b,16r9b56,16r9b55,16r9c46,16r9c48,
+16r9c3f,16r9c44,16r9c39,16r9c33,16r9c41,16r9c3c,16r9c37,16r9c34,
+16r9c32,16r9c3d,16r9c36,16r9ddb,16r9dd2,16r9dde,16r9dda,16r9dcb,
+16r9dd0,16r9ddc,16r9dd1,16r9ddf,16r9de9,16r9dd9,16r9dd8,16r9dd6,
+16r9df5,16r9dd5,16r9ddd,16r9eb6,16r9ef0,16r9f35,16r9f33,16r9f32,
+16r9f42,16r9f6b,16r9f95,16r9fa2,16r513d,16r5299,16r58e8,16r58e7,
+16r5972,16r5b4d,16r5dd8,16r882f,16r5f4f,16r6201,16r6203,16r6204,
+16r6529,16r6525,16r6596,16r66eb,16r6b11,16r6b12,16r6b0f,16r6bca,
+16r705b,16r705a,16r7222,16r7382,16r7381,16r7383,16r7670,16r77d4,
+16r7c67,16r7c66,16r7e95,16r826c,16r863a,16r8640,16r8639,16r863c,
+16r8631,16r863b,16r863e,16r8830,16r8832,16r882e,16r8833,16r8976,
+16r8974,16r8973,16r89fe,16r8b8c,16r8b8e,16r8b8b,16r8b88,16r8c45,
+16r8d19,16r8e98,16r8f64,16r8f63,16r91bc,16r9462,16r9455,16r945d,
+16r9457,16r945e,16r97c4,16r97c5,16r9800,16r9a56,16r9a59,16r9b1e,
+16r9b1f,16r9b20,16r9c52,16r9c58,16r9c50,16r9c4a,16r9c4d,16r9c4b,
+16r9c55,16r9c59,16r9c4c,16r9c4e,16r9dfb,16r9df7,16r9def,16r9de3,
+16r9deb,16r9df8,16r9de4,16r9df6,16r9de1,16r9dee,16r9de6,16r9df2,
+16r9df0,16r9de2,16r9dec,16r9df4,16r9df3,16r9de8,16r9ded,16r9ec2,
+16r9ed0,16r9ef2,16r9ef3,16r9f06,16r9f1c,16r9f38,16r9f37,16r9f36,
+16r9f43,16r9f4f,16r9f71,16r9f70,16r9f6e,16r9f6f,16r56d3,16r56cd,
+16r5b4e,16r5c6d,16r652d,16r66ed,16r66ee,16r6b13,16r705f,16r7061,
+16r705d,16r7060,16r7223,16r74db,16r74e5,16r77d5,16r7938,16r79b7,
+16r79b6,16r7c6a,16r7e97,16r7f89,16r826d,16r8643,16r8838,16r8837,
+16r8835,16r884b,16r8b94,16r8b95,16r8e9e,16r8e9f,16r8ea0,16r8e9d,
+16r91be,16r91bd,16r91c2,16r946b,16r9468,16r9469,16r96e5,16r9746,
+16r9743,16r9747,16r97c7,16r97e5,16r9a5e,16r9ad5,16r9b59,16r9c63,
+16r9c67,16r9c66,16r9c62,16r9c5e,16r9c60,16r9e02,16r9dfe,16r9e07,
+16r9e03,16r9e06,16r9e05,16r9e00,16r9e01,16r9e09,16r9dff,16r9dfd,
+16r9e04,16r9ea0,16r9f1e,16r9f46,16r9f74,16r9f75,16r9f76,16r56d4,
+16r652e,16r65b8,16r6b18,16r6b19,16r6b17,16r6b1a,16r7062,16r7226,
+16r72aa,16r77d8,16r77d9,16r7939,16r7c69,16r7c6b,16r7cf6,16r7e9a,
+16r7e98,16r7e9b,16r7e99,16r81e0,16r81e1,16r8646,16r8647,16r8648,
+16r8979,16r897a,16r897c,16r897b,16r89ff,16r8b98,16r8b99,16r8ea5,
+16r8ea4,16r8ea3,16r946e,16r946d,16r946f,16r9471,16r9473,16r9749,
+16r9872,16r995f,16r9c68,16r9c6e,16r9c6d,16r9e0b,16r9e0d,16r9e10,
+16r9e0f,16r9e12,16r9e11,16r9ea1,16r9ef5,16r9f09,16r9f47,16r9f78,
+16r9f7b,16r9f7a,16r9f79,16r571e,16r7066,16r7c6f,16r883c,16r8db2,
+16r8ea6,16r91c3,16r9474,16r9478,16r9476,16r9475,16r9a60,16r9c74,
+16r9c73,16r9c71,16r9c75,16r9e14,16r9e13,16r9ef6,16r9f0a,16r9fa4,
+16r7068,16r7065,16r7cf7,16r866a,16r883e,16r883d,16r883f,16r8b9e,
+16r8c9c,16r8ea9,16r8ec9,16r974b,16r9873,16r9874,16r98cc,16r9961,
+16r99ab,16r9a64,16r9a66,16r9a67,16r9b24,16r9e15,16r9e17,16r9f48,
+16r6207,16r6b1e,16r7227,16r864c,16r8ea8,16r9482,16r9480,16r9481,
+16r9a69,16r9a68,16r9b2e,16r9e19,16r7229,16r864b,16r8b9f,16r9483,
+16r9c79,16r9eb7,16r7675,16r9a6b,16r9c7a,16r9e1d,16r7069,16r706a,
+16r9ea4,16r9f7e,16r9f49,16r9f98,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+};
diff --git a/appl/lib/convcs/gencp.b b/appl/lib/convcs/gencp.b
new file mode 100644
index 00000000..cbc17af7
--- /dev/null
+++ b/appl/lib/convcs/gencp.b
@@ -0,0 +1,28 @@
+NCHARS : con 256;
+ERRCHAR : con 16rFFFD;
+
+sys : Sys;
+
+GenCP : module {
+ init : fn (ctxt : ref Draw->Context, args : list of string);
+};
+
+init(nil : ref Draw->Context, nil : list of string)
+{
+ sys = load Sys Sys->PATH;
+ path := sys->sprint("/lib/convcs/%s.cp", CHARSET);
+ fd := sys->create(path, Sys->OWRITE, 8r644);
+ if (fd == nil) {
+ sys->print("cannot create %s: %r\n", path);
+ return;
+ }
+ s := "";
+ for (i := 0; i < NCHARS; i++) {
+ if (cstab[i] == -1)
+ cstab[i] = ERRCHAR;
+ s[i] = cstab[i];
+ }
+ buf := array of byte s;
+ sys->write(fd, buf, len buf);
+}
+
diff --git a/appl/lib/convcs/gencp932.b b/appl/lib/convcs/gencp932.b
new file mode 100644
index 00000000..6fcfc131
--- /dev/null
+++ b/appl/lib/convcs/gencp932.b
@@ -0,0 +1,7814 @@
+implement gencp932;
+
+include "sys.m";
+include "draw.m";
+
+gencp932 : module {
+ init: fn(ctxt: ref Draw->Context, args: list of string);
+};
+
+DATAFILE : con "/lib/convcs/cp932";
+
+CP932PAGES : con 45; # 81..84, 87..9f, e0..ea, ed..ee, fa..fc
+CP932PAGESZ : con 189; # 40..fc (including 7f)
+CP932CHAR0 : con 16r40;
+
+BADCHAR : con 16rFFFD;
+
+init(nil: ref Draw->Context, nil: list of string)
+{
+ sys := load Sys Sys->PATH;
+ fd := sys->create(DATAFILE, Sys->OWRITE, 8r644);
+ if (fd == nil) {
+ sys->print("cannot create %s: %r\n", DATAFILE);
+ return;
+ }
+
+ # pre-calculate DBCS page numbers to mapping file page numbers
+ dbcsoff := array [256] of { * => -1 };
+ p := 0;
+ for (i := 16r81; i <= 16r84; i++)
+ dbcsoff[i] = p++;
+ for (i = 16r87; i <= 16r9f; i++)
+ dbcsoff[i] = p++;
+ for (i = 16re0; i <= 16rea; i++)
+ dbcsoff[i] = p++;
+ for (i = 16red; i <= 16ree; i++)
+ dbcsoff[i] = p++;
+ for (i = 16rfa; i <= 16rfc; i++)
+ dbcsoff[i] = p++;
+
+ pages := array [CP932PAGES * CP932PAGESZ] of { * => BADCHAR };
+
+ for (i = 0; i < len cp932; i++) {
+ (nil, toks) := sys->tokenize(cp932[i], "\t");
+ (bytes, ucode) := (hd toks, hd tl toks);
+ u := hex2int(ucode);
+ (nil, blist) := sys->tokenize(bytes, " ");
+ b1 := hex2int(hd blist);
+ b2 := hex2int(hd tl blist);
+
+ page := dbcsoff[b1];
+ if (page == -1) {
+ sys->print("conversion error\n");
+ raise "fail:bad data";
+ }
+ char := b2 - CP932CHAR0;
+ pages[(page * CP932PAGESZ) + char] = u;
+ }
+
+ s := "";
+ for (i = 0; i < len pages; i++)
+ s[i] = pages[i];
+ pages = nil;
+ buf := array of byte s;
+ sys->write(fd, buf, len buf);
+}
+
+hex2int(s: string): int
+{
+ n := 0;
+ for (i := 0; i < len s; i++) {
+ case s[i] {
+ '0' to '9' =>
+ n = 16*n + s[i] - '0';
+ 'A' to 'F' =>
+ n = 16*n + s[i] + 10 - 'A';
+ 'a' to 'f' =>
+ n = 16*n + s[i] + 10 - 'a';
+ * =>
+ return n;
+ }
+ }
+ return n;
+}
+
+# This data derived directly from the unicode mapping file shiftjis.txt
+# available from http://www.unicode.org/Public//MAPPINGS
+
+cp932 := array [] of {
+ "81 40 3000", #IDEOGRAPHIC SPACE
+ "81 41 3001", #IDEOGRAPHIC COMMA
+ "81 42 3002", #IDEOGRAPHIC FULL STOP
+ "81 43 FF0C", #FULLWIDTH COMMA
+ "81 44 FF0E", #FULLWIDTH FULL STOP
+ "81 45 30FB", #KATAKANA MIDDLE DOT
+ "81 46 FF1A", #FULLWIDTH COLON
+ "81 47 FF1B", #FULLWIDTH SEMICOLON
+ "81 48 FF1F", #FULLWIDTH QUESTION MARK
+ "81 49 FF01", #FULLWIDTH EXCLAMATION MARK
+ "81 4A 309B", #KATAKANA-HIRAGANA VOICED SOUND MARK
+ "81 4B 309C", #KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+ "81 4C 00B4", #ACUTE ACCENT
+ "81 4D FF40", #FULLWIDTH GRAVE ACCENT
+ "81 4E 00A8", #DIAERESIS
+ "81 4F FF3E", #FULLWIDTH CIRCUMFLEX ACCENT
+ "81 50 FFE3", #FULLWIDTH MACRON
+ "81 51 FF3F", #FULLWIDTH LOW LINE
+ "81 52 30FD", #KATAKANA ITERATION MARK
+ "81 53 30FE", #KATAKANA VOICED ITERATION MARK
+ "81 54 309D", #HIRAGANA ITERATION MARK
+ "81 55 309E", #HIRAGANA VOICED ITERATION MARK
+ "81 56 3003", #DITTO MARK
+ "81 57 4EDD", #CJK UNIFIED IDEOGRAPH
+ "81 58 3005", #IDEOGRAPHIC ITERATION MARK
+ "81 59 3006", #IDEOGRAPHIC CLOSING MARK
+ "81 5A 3007", #IDEOGRAPHIC NUMBER ZERO
+ "81 5B 30FC", #KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ "81 5C 2015", #HORIZONTAL BAR
+ "81 5D 2010", #HYPHEN
+ "81 5E FF0F", #FULLWIDTH SOLIDUS
+ "81 5F FF3C", #FULLWIDTH REVERSE SOLIDUS
+ "81 60 FF5E", #FULLWIDTH TILDE
+ "81 61 2225", #PARALLEL TO
+ "81 62 FF5C", #FULLWIDTH VERTICAL LINE
+ "81 63 2026", #HORIZONTAL ELLIPSIS
+ "81 64 2025", #TWO DOT LEADER
+ "81 65 2018", #LEFT SINGLE QUOTATION MARK
+ "81 66 2019", #RIGHT SINGLE QUOTATION MARK
+ "81 67 201C", #LEFT DOUBLE QUOTATION MARK
+ "81 68 201D", #RIGHT DOUBLE QUOTATION MARK
+ "81 69 FF08", #FULLWIDTH LEFT PARENTHESIS
+ "81 6A FF09", #FULLWIDTH RIGHT PARENTHESIS
+ "81 6B 3014", #LEFT TORTOISE SHELL BRACKET
+ "81 6C 3015", #RIGHT TORTOISE SHELL BRACKET
+ "81 6D FF3B", #FULLWIDTH LEFT SQUARE BRACKET
+ "81 6E FF3D", #FULLWIDTH RIGHT SQUARE BRACKET
+ "81 6F FF5B", #FULLWIDTH LEFT CURLY BRACKET
+ "81 70 FF5D", #FULLWIDTH RIGHT CURLY BRACKET
+ "81 71 3008", #LEFT ANGLE BRACKET
+ "81 72 3009", #RIGHT ANGLE BRACKET
+ "81 73 300A", #LEFT DOUBLE ANGLE BRACKET
+ "81 74 300B", #RIGHT DOUBLE ANGLE BRACKET
+ "81 75 300C", #LEFT CORNER BRACKET
+ "81 76 300D", #RIGHT CORNER BRACKET
+ "81 77 300E", #LEFT WHITE CORNER BRACKET
+ "81 78 300F", #RIGHT WHITE CORNER BRACKET
+ "81 79 3010", #LEFT BLACK LENTICULAR BRACKET
+ "81 7A 3011", #RIGHT BLACK LENTICULAR BRACKET
+ "81 7B FF0B", #FULLWIDTH PLUS SIGN
+ "81 7C FF0D", #FULLWIDTH HYPHEN-MINUS
+ "81 7D 00B1", #PLUS-MINUS SIGN
+ "81 7E 00D7", #MULTIPLICATION SIGN
+ "81 80 00F7", #DIVISION SIGN
+ "81 81 FF1D", #FULLWIDTH EQUALS SIGN
+ "81 82 2260", #NOT EQUAL TO
+ "81 83 FF1C", #FULLWIDTH LESS-THAN SIGN
+ "81 84 FF1E", #FULLWIDTH GREATER-THAN SIGN
+ "81 85 2266", #LESS-THAN OVER EQUAL TO
+ "81 86 2267", #GREATER-THAN OVER EQUAL TO
+ "81 87 221E", #INFINITY
+ "81 88 2234", #THEREFORE
+ "81 89 2642", #MALE SIGN
+ "81 8A 2640", #FEMALE SIGN
+ "81 8B 00B0", #DEGREE SIGN
+ "81 8C 2032", #PRIME
+ "81 8D 2033", #DOUBLE PRIME
+ "81 8E 2103", #DEGREE CELSIUS
+ "81 8F FFE5", #FULLWIDTH YEN SIGN
+ "81 90 FF04", #FULLWIDTH DOLLAR SIGN
+ "81 91 FFE0", #FULLWIDTH CENT SIGN
+ "81 92 FFE1", #FULLWIDTH POUND SIGN
+ "81 93 FF05", #FULLWIDTH PERCENT SIGN
+ "81 94 FF03", #FULLWIDTH NUMBER SIGN
+ "81 95 FF06", #FULLWIDTH AMPERSAND
+ "81 96 FF0A", #FULLWIDTH ASTERISK
+ "81 97 FF20", #FULLWIDTH COMMERCIAL AT
+ "81 98 00A7", #SECTION SIGN
+ "81 99 2606", #WHITE STAR
+ "81 9A 2605", #BLACK STAR
+ "81 9B 25CB", #WHITE CIRCLE
+ "81 9C 25CF", #BLACK CIRCLE
+ "81 9D 25CE", #BULLSEYE
+ "81 9E 25C7", #WHITE DIAMOND
+ "81 9F 25C6", #BLACK DIAMOND
+ "81 A0 25A1", #WHITE SQUARE
+ "81 A1 25A0", #BLACK SQUARE
+ "81 A2 25B3", #WHITE UP-POINTING TRIANGLE
+ "81 A3 25B2", #BLACK UP-POINTING TRIANGLE
+ "81 A4 25BD", #WHITE DOWN-POINTING TRIANGLE
+ "81 A5 25BC", #BLACK DOWN-POINTING TRIANGLE
+ "81 A6 203B", #REFERENCE MARK
+ "81 A7 3012", #POSTAL MARK
+ "81 A8 2192", #RIGHTWARDS ARROW
+ "81 A9 2190", #LEFTWARDS ARROW
+ "81 AA 2191", #UPWARDS ARROW
+ "81 AB 2193", #DOWNWARDS ARROW
+ "81 AC 3013", #GETA MARK
+ "81 B8 2208", #ELEMENT OF
+ "81 B9 220B", #CONTAINS AS MEMBER
+ "81 BA 2286", #SUBSET OF OR EQUAL TO
+ "81 BB 2287", #SUPERSET OF OR EQUAL TO
+ "81 BC 2282", #SUBSET OF
+ "81 BD 2283", #SUPERSET OF
+ "81 BE 222A", #UNION
+ "81 BF 2229", #INTERSECTION
+ "81 C8 2227", #LOGICAL AND
+ "81 C9 2228", #LOGICAL OR
+ "81 CA FFE2", #FULLWIDTH NOT SIGN
+ "81 CB 21D2", #RIGHTWARDS DOUBLE ARROW
+ "81 CC 21D4", #LEFT RIGHT DOUBLE ARROW
+ "81 CD 2200", #FOR ALL
+ "81 CE 2203", #THERE EXISTS
+ "81 DA 2220", #ANGLE
+ "81 DB 22A5", #UP TACK
+ "81 DC 2312", #ARC
+ "81 DD 2202", #PARTIAL DIFFERENTIAL
+ "81 DE 2207", #NABLA
+ "81 DF 2261", #IDENTICAL TO
+ "81 E0 2252", #APPROXIMATELY EQUAL TO OR THE IMAGE OF
+ "81 E1 226A", #MUCH LESS-THAN
+ "81 E2 226B", #MUCH GREATER-THAN
+ "81 E3 221A", #SQUARE ROOT
+ "81 E4 223D", #REVERSED TILDE
+ "81 E5 221D", #PROPORTIONAL TO
+ "81 E6 2235", #BECAUSE
+ "81 E7 222B", #INTEGRAL
+ "81 E8 222C", #DOUBLE INTEGRAL
+ "81 F0 212B", #ANGSTROM SIGN
+ "81 F1 2030", #PER MILLE SIGN
+ "81 F2 266F", #MUSIC SHARP SIGN
+ "81 F3 266D", #MUSIC FLAT SIGN
+ "81 F4 266A", #EIGHTH NOTE
+ "81 F5 2020", #DAGGER
+ "81 F6 2021", #DOUBLE DAGGER
+ "81 F7 00B6", #PILCROW SIGN
+ "81 FC 25EF", #LARGE CIRCLE
+ "82 4F FF10", #FULLWIDTH DIGIT ZERO
+ "82 50 FF11", #FULLWIDTH DIGIT ONE
+ "82 51 FF12", #FULLWIDTH DIGIT TWO
+ "82 52 FF13", #FULLWIDTH DIGIT THREE
+ "82 53 FF14", #FULLWIDTH DIGIT FOUR
+ "82 54 FF15", #FULLWIDTH DIGIT FIVE
+ "82 55 FF16", #FULLWIDTH DIGIT SIX
+ "82 56 FF17", #FULLWIDTH DIGIT SEVEN
+ "82 57 FF18", #FULLWIDTH DIGIT EIGHT
+ "82 58 FF19", #FULLWIDTH DIGIT NINE
+ "82 60 FF21", #FULLWIDTH LATIN CAPITAL LETTER A
+ "82 61 FF22", #FULLWIDTH LATIN CAPITAL LETTER B
+ "82 62 FF23", #FULLWIDTH LATIN CAPITAL LETTER C
+ "82 63 FF24", #FULLWIDTH LATIN CAPITAL LETTER D
+ "82 64 FF25", #FULLWIDTH LATIN CAPITAL LETTER E
+ "82 65 FF26", #FULLWIDTH LATIN CAPITAL LETTER F
+ "82 66 FF27", #FULLWIDTH LATIN CAPITAL LETTER G
+ "82 67 FF28", #FULLWIDTH LATIN CAPITAL LETTER H
+ "82 68 FF29", #FULLWIDTH LATIN CAPITAL LETTER I
+ "82 69 FF2A", #FULLWIDTH LATIN CAPITAL LETTER J
+ "82 6A FF2B", #FULLWIDTH LATIN CAPITAL LETTER K
+ "82 6B FF2C", #FULLWIDTH LATIN CAPITAL LETTER L
+ "82 6C FF2D", #FULLWIDTH LATIN CAPITAL LETTER M
+ "82 6D FF2E", #FULLWIDTH LATIN CAPITAL LETTER N
+ "82 6E FF2F", #FULLWIDTH LATIN CAPITAL LETTER O
+ "82 6F FF30", #FULLWIDTH LATIN CAPITAL LETTER P
+ "82 70 FF31", #FULLWIDTH LATIN CAPITAL LETTER Q
+ "82 71 FF32", #FULLWIDTH LATIN CAPITAL LETTER R
+ "82 72 FF33", #FULLWIDTH LATIN CAPITAL LETTER S
+ "82 73 FF34", #FULLWIDTH LATIN CAPITAL LETTER T
+ "82 74 FF35", #FULLWIDTH LATIN CAPITAL LETTER U
+ "82 75 FF36", #FULLWIDTH LATIN CAPITAL LETTER V
+ "82 76 FF37", #FULLWIDTH LATIN CAPITAL LETTER W
+ "82 77 FF38", #FULLWIDTH LATIN CAPITAL LETTER X
+ "82 78 FF39", #FULLWIDTH LATIN CAPITAL LETTER Y
+ "82 79 FF3A", #FULLWIDTH LATIN CAPITAL LETTER Z
+ "82 81 FF41", #FULLWIDTH LATIN SMALL LETTER A
+ "82 82 FF42", #FULLWIDTH LATIN SMALL LETTER B
+ "82 83 FF43", #FULLWIDTH LATIN SMALL LETTER C
+ "82 84 FF44", #FULLWIDTH LATIN SMALL LETTER D
+ "82 85 FF45", #FULLWIDTH LATIN SMALL LETTER E
+ "82 86 FF46", #FULLWIDTH LATIN SMALL LETTER F
+ "82 87 FF47", #FULLWIDTH LATIN SMALL LETTER G
+ "82 88 FF48", #FULLWIDTH LATIN SMALL LETTER H
+ "82 89 FF49", #FULLWIDTH LATIN SMALL LETTER I
+ "82 8A FF4A", #FULLWIDTH LATIN SMALL LETTER J
+ "82 8B FF4B", #FULLWIDTH LATIN SMALL LETTER K
+ "82 8C FF4C", #FULLWIDTH LATIN SMALL LETTER L
+ "82 8D FF4D", #FULLWIDTH LATIN SMALL LETTER M
+ "82 8E FF4E", #FULLWIDTH LATIN SMALL LETTER N
+ "82 8F FF4F", #FULLWIDTH LATIN SMALL LETTER O
+ "82 90 FF50", #FULLWIDTH LATIN SMALL LETTER P
+ "82 91 FF51", #FULLWIDTH LATIN SMALL LETTER Q
+ "82 92 FF52", #FULLWIDTH LATIN SMALL LETTER R
+ "82 93 FF53", #FULLWIDTH LATIN SMALL LETTER S
+ "82 94 FF54", #FULLWIDTH LATIN SMALL LETTER T
+ "82 95 FF55", #FULLWIDTH LATIN SMALL LETTER U
+ "82 96 FF56", #FULLWIDTH LATIN SMALL LETTER V
+ "82 97 FF57", #FULLWIDTH LATIN SMALL LETTER W
+ "82 98 FF58", #FULLWIDTH LATIN SMALL LETTER X
+ "82 99 FF59", #FULLWIDTH LATIN SMALL LETTER Y
+ "82 9A FF5A", #FULLWIDTH LATIN SMALL LETTER Z
+ "82 9F 3041", #HIRAGANA LETTER SMALL A
+ "82 A0 3042", #HIRAGANA LETTER A
+ "82 A1 3043", #HIRAGANA LETTER SMALL I
+ "82 A2 3044", #HIRAGANA LETTER I
+ "82 A3 3045", #HIRAGANA LETTER SMALL U
+ "82 A4 3046", #HIRAGANA LETTER U
+ "82 A5 3047", #HIRAGANA LETTER SMALL E
+ "82 A6 3048", #HIRAGANA LETTER E
+ "82 A7 3049", #HIRAGANA LETTER SMALL O
+ "82 A8 304A", #HIRAGANA LETTER O
+ "82 A9 304B", #HIRAGANA LETTER KA
+ "82 AA 304C", #HIRAGANA LETTER GA
+ "82 AB 304D", #HIRAGANA LETTER KI
+ "82 AC 304E", #HIRAGANA LETTER GI
+ "82 AD 304F", #HIRAGANA LETTER KU
+ "82 AE 3050", #HIRAGANA LETTER GU
+ "82 AF 3051", #HIRAGANA LETTER KE
+ "82 B0 3052", #HIRAGANA LETTER GE
+ "82 B1 3053", #HIRAGANA LETTER KO
+ "82 B2 3054", #HIRAGANA LETTER GO
+ "82 B3 3055", #HIRAGANA LETTER SA
+ "82 B4 3056", #HIRAGANA LETTER ZA
+ "82 B5 3057", #HIRAGANA LETTER SI
+ "82 B6 3058", #HIRAGANA LETTER ZI
+ "82 B7 3059", #HIRAGANA LETTER SU
+ "82 B8 305A", #HIRAGANA LETTER ZU
+ "82 B9 305B", #HIRAGANA LETTER SE
+ "82 BA 305C", #HIRAGANA LETTER ZE
+ "82 BB 305D", #HIRAGANA LETTER SO
+ "82 BC 305E", #HIRAGANA LETTER ZO
+ "82 BD 305F", #HIRAGANA LETTER TA
+ "82 BE 3060", #HIRAGANA LETTER DA
+ "82 BF 3061", #HIRAGANA LETTER TI
+ "82 C0 3062", #HIRAGANA LETTER DI
+ "82 C1 3063", #HIRAGANA LETTER SMALL TU
+ "82 C2 3064", #HIRAGANA LETTER TU
+ "82 C3 3065", #HIRAGANA LETTER DU
+ "82 C4 3066", #HIRAGANA LETTER TE
+ "82 C5 3067", #HIRAGANA LETTER DE
+ "82 C6 3068", #HIRAGANA LETTER TO
+ "82 C7 3069", #HIRAGANA LETTER DO
+ "82 C8 306A", #HIRAGANA LETTER NA
+ "82 C9 306B", #HIRAGANA LETTER NI
+ "82 CA 306C", #HIRAGANA LETTER NU
+ "82 CB 306D", #HIRAGANA LETTER NE
+ "82 CC 306E", #HIRAGANA LETTER NO
+ "82 CD 306F", #HIRAGANA LETTER HA
+ "82 CE 3070", #HIRAGANA LETTER BA
+ "82 CF 3071", #HIRAGANA LETTER PA
+ "82 D0 3072", #HIRAGANA LETTER HI
+ "82 D1 3073", #HIRAGANA LETTER BI
+ "82 D2 3074", #HIRAGANA LETTER PI
+ "82 D3 3075", #HIRAGANA LETTER HU
+ "82 D4 3076", #HIRAGANA LETTER BU
+ "82 D5 3077", #HIRAGANA LETTER PU
+ "82 D6 3078", #HIRAGANA LETTER HE
+ "82 D7 3079", #HIRAGANA LETTER BE
+ "82 D8 307A", #HIRAGANA LETTER PE
+ "82 D9 307B", #HIRAGANA LETTER HO
+ "82 DA 307C", #HIRAGANA LETTER BO
+ "82 DB 307D", #HIRAGANA LETTER PO
+ "82 DC 307E", #HIRAGANA LETTER MA
+ "82 DD 307F", #HIRAGANA LETTER MI
+ "82 DE 3080", #HIRAGANA LETTER MU
+ "82 DF 3081", #HIRAGANA LETTER ME
+ "82 E0 3082", #HIRAGANA LETTER MO
+ "82 E1 3083", #HIRAGANA LETTER SMALL YA
+ "82 E2 3084", #HIRAGANA LETTER YA
+ "82 E3 3085", #HIRAGANA LETTER SMALL YU
+ "82 E4 3086", #HIRAGANA LETTER YU
+ "82 E5 3087", #HIRAGANA LETTER SMALL YO
+ "82 E6 3088", #HIRAGANA LETTER YO
+ "82 E7 3089", #HIRAGANA LETTER RA
+ "82 E8 308A", #HIRAGANA LETTER RI
+ "82 E9 308B", #HIRAGANA LETTER RU
+ "82 EA 308C", #HIRAGANA LETTER RE
+ "82 EB 308D", #HIRAGANA LETTER RO
+ "82 EC 308E", #HIRAGANA LETTER SMALL WA
+ "82 ED 308F", #HIRAGANA LETTER WA
+ "82 EE 3090", #HIRAGANA LETTER WI
+ "82 EF 3091", #HIRAGANA LETTER WE
+ "82 F0 3092", #HIRAGANA LETTER WO
+ "82 F1 3093", #HIRAGANA LETTER N
+ "83 40 30A1", #KATAKANA LETTER SMALL A
+ "83 41 30A2", #KATAKANA LETTER A
+ "83 42 30A3", #KATAKANA LETTER SMALL I
+ "83 43 30A4", #KATAKANA LETTER I
+ "83 44 30A5", #KATAKANA LETTER SMALL U
+ "83 45 30A6", #KATAKANA LETTER U
+ "83 46 30A7", #KATAKANA LETTER SMALL E
+ "83 47 30A8", #KATAKANA LETTER E
+ "83 48 30A9", #KATAKANA LETTER SMALL O
+ "83 49 30AA", #KATAKANA LETTER O
+ "83 4A 30AB", #KATAKANA LETTER KA
+ "83 4B 30AC", #KATAKANA LETTER GA
+ "83 4C 30AD", #KATAKANA LETTER KI
+ "83 4D 30AE", #KATAKANA LETTER GI
+ "83 4E 30AF", #KATAKANA LETTER KU
+ "83 4F 30B0", #KATAKANA LETTER GU
+ "83 50 30B1", #KATAKANA LETTER KE
+ "83 51 30B2", #KATAKANA LETTER GE
+ "83 52 30B3", #KATAKANA LETTER KO
+ "83 53 30B4", #KATAKANA LETTER GO
+ "83 54 30B5", #KATAKANA LETTER SA
+ "83 55 30B6", #KATAKANA LETTER ZA
+ "83 56 30B7", #KATAKANA LETTER SI
+ "83 57 30B8", #KATAKANA LETTER ZI
+ "83 58 30B9", #KATAKANA LETTER SU
+ "83 59 30BA", #KATAKANA LETTER ZU
+ "83 5A 30BB", #KATAKANA LETTER SE
+ "83 5B 30BC", #KATAKANA LETTER ZE
+ "83 5C 30BD", #KATAKANA LETTER SO
+ "83 5D 30BE", #KATAKANA LETTER ZO
+ "83 5E 30BF", #KATAKANA LETTER TA
+ "83 5F 30C0", #KATAKANA LETTER DA
+ "83 60 30C1", #KATAKANA LETTER TI
+ "83 61 30C2", #KATAKANA LETTER DI
+ "83 62 30C3", #KATAKANA LETTER SMALL TU
+ "83 63 30C4", #KATAKANA LETTER TU
+ "83 64 30C5", #KATAKANA LETTER DU
+ "83 65 30C6", #KATAKANA LETTER TE
+ "83 66 30C7", #KATAKANA LETTER DE
+ "83 67 30C8", #KATAKANA LETTER TO
+ "83 68 30C9", #KATAKANA LETTER DO
+ "83 69 30CA", #KATAKANA LETTER NA
+ "83 6A 30CB", #KATAKANA LETTER NI
+ "83 6B 30CC", #KATAKANA LETTER NU
+ "83 6C 30CD", #KATAKANA LETTER NE
+ "83 6D 30CE", #KATAKANA LETTER NO
+ "83 6E 30CF", #KATAKANA LETTER HA
+ "83 6F 30D0", #KATAKANA LETTER BA
+ "83 70 30D1", #KATAKANA LETTER PA
+ "83 71 30D2", #KATAKANA LETTER HI
+ "83 72 30D3", #KATAKANA LETTER BI
+ "83 73 30D4", #KATAKANA LETTER PI
+ "83 74 30D5", #KATAKANA LETTER HU
+ "83 75 30D6", #KATAKANA LETTER BU
+ "83 76 30D7", #KATAKANA LETTER PU
+ "83 77 30D8", #KATAKANA LETTER HE
+ "83 78 30D9", #KATAKANA LETTER BE
+ "83 79 30DA", #KATAKANA LETTER PE
+ "83 7A 30DB", #KATAKANA LETTER HO
+ "83 7B 30DC", #KATAKANA LETTER BO
+ "83 7C 30DD", #KATAKANA LETTER PO
+ "83 7D 30DE", #KATAKANA LETTER MA
+ "83 7E 30DF", #KATAKANA LETTER MI
+ "83 80 30E0", #KATAKANA LETTER MU
+ "83 81 30E1", #KATAKANA LETTER ME
+ "83 82 30E2", #KATAKANA LETTER MO
+ "83 83 30E3", #KATAKANA LETTER SMALL YA
+ "83 84 30E4", #KATAKANA LETTER YA
+ "83 85 30E5", #KATAKANA LETTER SMALL YU
+ "83 86 30E6", #KATAKANA LETTER YU
+ "83 87 30E7", #KATAKANA LETTER SMALL YO
+ "83 88 30E8", #KATAKANA LETTER YO
+ "83 89 30E9", #KATAKANA LETTER RA
+ "83 8A 30EA", #KATAKANA LETTER RI
+ "83 8B 30EB", #KATAKANA LETTER RU
+ "83 8C 30EC", #KATAKANA LETTER RE
+ "83 8D 30ED", #KATAKANA LETTER RO
+ "83 8E 30EE", #KATAKANA LETTER SMALL WA
+ "83 8F 30EF", #KATAKANA LETTER WA
+ "83 90 30F0", #KATAKANA LETTER WI
+ "83 91 30F1", #KATAKANA LETTER WE
+ "83 92 30F2", #KATAKANA LETTER WO
+ "83 93 30F3", #KATAKANA LETTER N
+ "83 94 30F4", #KATAKANA LETTER VU
+ "83 95 30F5", #KATAKANA LETTER SMALL KA
+ "83 96 30F6", #KATAKANA LETTER SMALL KE
+ "83 9F 0391", #GREEK CAPITAL LETTER ALPHA
+ "83 A0 0392", #GREEK CAPITAL LETTER BETA
+ "83 A1 0393", #GREEK CAPITAL LETTER GAMMA
+ "83 A2 0394", #GREEK CAPITAL LETTER DELTA
+ "83 A3 0395", #GREEK CAPITAL LETTER EPSILON
+ "83 A4 0396", #GREEK CAPITAL LETTER ZETA
+ "83 A5 0397", #GREEK CAPITAL LETTER ETA
+ "83 A6 0398", #GREEK CAPITAL LETTER THETA
+ "83 A7 0399", #GREEK CAPITAL LETTER IOTA
+ "83 A8 039A", #GREEK CAPITAL LETTER KAPPA
+ "83 A9 039B", #GREEK CAPITAL LETTER LAMDA
+ "83 AA 039C", #GREEK CAPITAL LETTER MU
+ "83 AB 039D", #GREEK CAPITAL LETTER NU
+ "83 AC 039E", #GREEK CAPITAL LETTER XI
+ "83 AD 039F", #GREEK CAPITAL LETTER OMICRON
+ "83 AE 03A0", #GREEK CAPITAL LETTER PI
+ "83 AF 03A1", #GREEK CAPITAL LETTER RHO
+ "83 B0 03A3", #GREEK CAPITAL LETTER SIGMA
+ "83 B1 03A4", #GREEK CAPITAL LETTER TAU
+ "83 B2 03A5", #GREEK CAPITAL LETTER UPSILON
+ "83 B3 03A6", #GREEK CAPITAL LETTER PHI
+ "83 B4 03A7", #GREEK CAPITAL LETTER CHI
+ "83 B5 03A8", #GREEK CAPITAL LETTER PSI
+ "83 B6 03A9", #GREEK CAPITAL LETTER OMEGA
+ "83 BF 03B1", #GREEK SMALL LETTER ALPHA
+ "83 C0 03B2", #GREEK SMALL LETTER BETA
+ "83 C1 03B3", #GREEK SMALL LETTER GAMMA
+ "83 C2 03B4", #GREEK SMALL LETTER DELTA
+ "83 C3 03B5", #GREEK SMALL LETTER EPSILON
+ "83 C4 03B6", #GREEK SMALL LETTER ZETA
+ "83 C5 03B7", #GREEK SMALL LETTER ETA
+ "83 C6 03B8", #GREEK SMALL LETTER THETA
+ "83 C7 03B9", #GREEK SMALL LETTER IOTA
+ "83 C8 03BA", #GREEK SMALL LETTER KAPPA
+ "83 C9 03BB", #GREEK SMALL LETTER LAMDA
+ "83 CA 03BC", #GREEK SMALL LETTER MU
+ "83 CB 03BD", #GREEK SMALL LETTER NU
+ "83 CC 03BE", #GREEK SMALL LETTER XI
+ "83 CD 03BF", #GREEK SMALL LETTER OMICRON
+ "83 CE 03C0", #GREEK SMALL LETTER PI
+ "83 CF 03C1", #GREEK SMALL LETTER RHO
+ "83 D0 03C3", #GREEK SMALL LETTER SIGMA
+ "83 D1 03C4", #GREEK SMALL LETTER TAU
+ "83 D2 03C5", #GREEK SMALL LETTER UPSILON
+ "83 D3 03C6", #GREEK SMALL LETTER PHI
+ "83 D4 03C7", #GREEK SMALL LETTER CHI
+ "83 D5 03C8", #GREEK SMALL LETTER PSI
+ "83 D6 03C9", #GREEK SMALL LETTER OMEGA
+ "84 40 0410", #CYRILLIC CAPITAL LETTER A
+ "84 41 0411", #CYRILLIC CAPITAL LETTER BE
+ "84 42 0412", #CYRILLIC CAPITAL LETTER VE
+ "84 43 0413", #CYRILLIC CAPITAL LETTER GHE
+ "84 44 0414", #CYRILLIC CAPITAL LETTER DE
+ "84 45 0415", #CYRILLIC CAPITAL LETTER IE
+ "84 46 0401", #CYRILLIC CAPITAL LETTER IO
+ "84 47 0416", #CYRILLIC CAPITAL LETTER ZHE
+ "84 48 0417", #CYRILLIC CAPITAL LETTER ZE
+ "84 49 0418", #CYRILLIC CAPITAL LETTER I
+ "84 4A 0419", #CYRILLIC CAPITAL LETTER SHORT I
+ "84 4B 041A", #CYRILLIC CAPITAL LETTER KA
+ "84 4C 041B", #CYRILLIC CAPITAL LETTER EL
+ "84 4D 041C", #CYRILLIC CAPITAL LETTER EM
+ "84 4E 041D", #CYRILLIC CAPITAL LETTER EN
+ "84 4F 041E", #CYRILLIC CAPITAL LETTER O
+ "84 50 041F", #CYRILLIC CAPITAL LETTER PE
+ "84 51 0420", #CYRILLIC CAPITAL LETTER ER
+ "84 52 0421", #CYRILLIC CAPITAL LETTER ES
+ "84 53 0422", #CYRILLIC CAPITAL LETTER TE
+ "84 54 0423", #CYRILLIC CAPITAL LETTER U
+ "84 55 0424", #CYRILLIC CAPITAL LETTER EF
+ "84 56 0425", #CYRILLIC CAPITAL LETTER HA
+ "84 57 0426", #CYRILLIC CAPITAL LETTER TSE
+ "84 58 0427", #CYRILLIC CAPITAL LETTER CHE
+ "84 59 0428", #CYRILLIC CAPITAL LETTER SHA
+ "84 5A 0429", #CYRILLIC CAPITAL LETTER SHCHA
+ "84 5B 042A", #CYRILLIC CAPITAL LETTER HARD SIGN
+ "84 5C 042B", #CYRILLIC CAPITAL LETTER YERU
+ "84 5D 042C", #CYRILLIC CAPITAL LETTER SOFT SIGN
+ "84 5E 042D", #CYRILLIC CAPITAL LETTER E
+ "84 5F 042E", #CYRILLIC CAPITAL LETTER YU
+ "84 60 042F", #CYRILLIC CAPITAL LETTER YA
+ "84 70 0430", #CYRILLIC SMALL LETTER A
+ "84 71 0431", #CYRILLIC SMALL LETTER BE
+ "84 72 0432", #CYRILLIC SMALL LETTER VE
+ "84 73 0433", #CYRILLIC SMALL LETTER GHE
+ "84 74 0434", #CYRILLIC SMALL LETTER DE
+ "84 75 0435", #CYRILLIC SMALL LETTER IE
+ "84 76 0451", #CYRILLIC SMALL LETTER IO
+ "84 77 0436", #CYRILLIC SMALL LETTER ZHE
+ "84 78 0437", #CYRILLIC SMALL LETTER ZE
+ "84 79 0438", #CYRILLIC SMALL LETTER I
+ "84 7A 0439", #CYRILLIC SMALL LETTER SHORT I
+ "84 7B 043A", #CYRILLIC SMALL LETTER KA
+ "84 7C 043B", #CYRILLIC SMALL LETTER EL
+ "84 7D 043C", #CYRILLIC SMALL LETTER EM
+ "84 7E 043D", #CYRILLIC SMALL LETTER EN
+ "84 80 043E", #CYRILLIC SMALL LETTER O
+ "84 81 043F", #CYRILLIC SMALL LETTER PE
+ "84 82 0440", #CYRILLIC SMALL LETTER ER
+ "84 83 0441", #CYRILLIC SMALL LETTER ES
+ "84 84 0442", #CYRILLIC SMALL LETTER TE
+ "84 85 0443", #CYRILLIC SMALL LETTER U
+ "84 86 0444", #CYRILLIC SMALL LETTER EF
+ "84 87 0445", #CYRILLIC SMALL LETTER HA
+ "84 88 0446", #CYRILLIC SMALL LETTER TSE
+ "84 89 0447", #CYRILLIC SMALL LETTER CHE
+ "84 8A 0448", #CYRILLIC SMALL LETTER SHA
+ "84 8B 0449", #CYRILLIC SMALL LETTER SHCHA
+ "84 8C 044A", #CYRILLIC SMALL LETTER HARD SIGN
+ "84 8D 044B", #CYRILLIC SMALL LETTER YERU
+ "84 8E 044C", #CYRILLIC SMALL LETTER SOFT SIGN
+ "84 8F 044D", #CYRILLIC SMALL LETTER E
+ "84 90 044E", #CYRILLIC SMALL LETTER YU
+ "84 91 044F", #CYRILLIC SMALL LETTER YA
+ "84 9F 2500", #BOX DRAWINGS LIGHT HORIZONTAL
+ "84 A0 2502", #BOX DRAWINGS LIGHT VERTICAL
+ "84 A1 250C", #BOX DRAWINGS LIGHT DOWN AND RIGHT
+ "84 A2 2510", #BOX DRAWINGS LIGHT DOWN AND LEFT
+ "84 A3 2518", #BOX DRAWINGS LIGHT UP AND LEFT
+ "84 A4 2514", #BOX DRAWINGS LIGHT UP AND RIGHT
+ "84 A5 251C", #BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+ "84 A6 252C", #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+ "84 A7 2524", #BOX DRAWINGS LIGHT VERTICAL AND LEFT
+ "84 A8 2534", #BOX DRAWINGS LIGHT UP AND HORIZONTAL
+ "84 A9 253C", #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+ "84 AA 2501", #BOX DRAWINGS HEAVY HORIZONTAL
+ "84 AB 2503", #BOX DRAWINGS HEAVY VERTICAL
+ "84 AC 250F", #BOX DRAWINGS HEAVY DOWN AND RIGHT
+ "84 AD 2513", #BOX DRAWINGS HEAVY DOWN AND LEFT
+ "84 AE 251B", #BOX DRAWINGS HEAVY UP AND LEFT
+ "84 AF 2517", #BOX DRAWINGS HEAVY UP AND RIGHT
+ "84 B0 2523", #BOX DRAWINGS HEAVY VERTICAL AND RIGHT
+ "84 B1 2533", #BOX DRAWINGS HEAVY DOWN AND HORIZONTAL
+ "84 B2 252B", #BOX DRAWINGS HEAVY VERTICAL AND LEFT
+ "84 B3 253B", #BOX DRAWINGS HEAVY UP AND HORIZONTAL
+ "84 B4 254B", #BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL
+ "84 B5 2520", #BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT
+ "84 B6 252F", #BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY
+ "84 B7 2528", #BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT
+ "84 B8 2537", #BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY
+ "84 B9 253F", #BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY
+ "84 BA 251D", #BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY
+ "84 BB 2530", #BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT
+ "84 BC 2525", #BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY
+ "84 BD 2538", #BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT
+ "84 BE 2542", #BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT
+ "87 40 2460", #CIRCLED DIGIT ONE
+ "87 41 2461", #CIRCLED DIGIT TWO
+ "87 42 2462", #CIRCLED DIGIT THREE
+ "87 43 2463", #CIRCLED DIGIT FOUR
+ "87 44 2464", #CIRCLED DIGIT FIVE
+ "87 45 2465", #CIRCLED DIGIT SIX
+ "87 46 2466", #CIRCLED DIGIT SEVEN
+ "87 47 2467", #CIRCLED DIGIT EIGHT
+ "87 48 2468", #CIRCLED DIGIT NINE
+ "87 49 2469", #CIRCLED NUMBER TEN
+ "87 4A 246A", #CIRCLED NUMBER ELEVEN
+ "87 4B 246B", #CIRCLED NUMBER TWELVE
+ "87 4C 246C", #CIRCLED NUMBER THIRTEEN
+ "87 4D 246D", #CIRCLED NUMBER FOURTEEN
+ "87 4E 246E", #CIRCLED NUMBER FIFTEEN
+ "87 4F 246F", #CIRCLED NUMBER SIXTEEN
+ "87 50 2470", #CIRCLED NUMBER SEVENTEEN
+ "87 51 2471", #CIRCLED NUMBER EIGHTEEN
+ "87 52 2472", #CIRCLED NUMBER NINETEEN
+ "87 53 2473", #CIRCLED NUMBER TWENTY
+ "87 54 2160", #ROMAN NUMERAL ONE
+ "87 55 2161", #ROMAN NUMERAL TWO
+ "87 56 2162", #ROMAN NUMERAL THREE
+ "87 57 2163", #ROMAN NUMERAL FOUR
+ "87 58 2164", #ROMAN NUMERAL FIVE
+ "87 59 2165", #ROMAN NUMERAL SIX
+ "87 5A 2166", #ROMAN NUMERAL SEVEN
+ "87 5B 2167", #ROMAN NUMERAL EIGHT
+ "87 5C 2168", #ROMAN NUMERAL NINE
+ "87 5D 2169", #ROMAN NUMERAL TEN
+ "87 5F 3349", #SQUARE MIRI
+ "87 60 3314", #SQUARE KIRO
+ "87 61 3322", #SQUARE SENTI
+ "87 62 334D", #SQUARE MEETORU
+ "87 63 3318", #SQUARE GURAMU
+ "87 64 3327", #SQUARE TON
+ "87 65 3303", #SQUARE AARU
+ "87 66 3336", #SQUARE HEKUTAARU
+ "87 67 3351", #SQUARE RITTORU
+ "87 68 3357", #SQUARE WATTO
+ "87 69 330D", #SQUARE KARORII
+ "87 6A 3326", #SQUARE DORU
+ "87 6B 3323", #SQUARE SENTO
+ "87 6C 332B", #SQUARE PAASENTO
+ "87 6D 334A", #SQUARE MIRIBAARU
+ "87 6E 333B", #SQUARE PEEZI
+ "87 6F 339C", #SQUARE MM
+ "87 70 339D", #SQUARE CM
+ "87 71 339E", #SQUARE KM
+ "87 72 338E", #SQUARE MG
+ "87 73 338F", #SQUARE KG
+ "87 74 33C4", #SQUARE CC
+ "87 75 33A1", #SQUARE M SQUARED
+ "87 7E 337B", #SQUARE ERA NAME HEISEI
+ "87 80 301D", #REVERSED DOUBLE PRIME QUOTATION MARK
+ "87 81 301F", #LOW DOUBLE PRIME QUOTATION MARK
+ "87 82 2116", #NUMERO SIGN
+ "87 83 33CD", #SQUARE KK
+ "87 84 2121", #TELEPHONE SIGN
+ "87 85 32A4", #CIRCLED IDEOGRAPH HIGH
+ "87 86 32A5", #CIRCLED IDEOGRAPH CENTRE
+ "87 87 32A6", #CIRCLED IDEOGRAPH LOW
+ "87 88 32A7", #CIRCLED IDEOGRAPH LEFT
+ "87 89 32A8", #CIRCLED IDEOGRAPH RIGHT
+ "87 8A 3231", #PARENTHESIZED IDEOGRAPH STOCK
+ "87 8B 3232", #PARENTHESIZED IDEOGRAPH HAVE
+ "87 8C 3239", #PARENTHESIZED IDEOGRAPH REPRESENT
+ "87 8D 337E", #SQUARE ERA NAME MEIZI
+ "87 8E 337D", #SQUARE ERA NAME TAISYOU
+ "87 8F 337C", #SQUARE ERA NAME SYOUWA
+ "87 90 2252", #APPROXIMATELY EQUAL TO OR THE IMAGE OF
+ "87 91 2261", #IDENTICAL TO
+ "87 92 222B", #INTEGRAL
+ "87 93 222E", #CONTOUR INTEGRAL
+ "87 94 2211", #N-ARY SUMMATION
+ "87 95 221A", #SQUARE ROOT
+ "87 96 22A5", #UP TACK
+ "87 97 2220", #ANGLE
+ "87 98 221F", #RIGHT ANGLE
+ "87 99 22BF", #RIGHT TRIANGLE
+ "87 9A 2235", #BECAUSE
+ "87 9B 2229", #INTERSECTION
+ "87 9C 222A", #UNION
+ "88 9F 4E9C", #CJK UNIFIED IDEOGRAPH
+ "88 A0 5516", #CJK UNIFIED IDEOGRAPH
+ "88 A1 5A03", #CJK UNIFIED IDEOGRAPH
+ "88 A2 963F", #CJK UNIFIED IDEOGRAPH
+ "88 A3 54C0", #CJK UNIFIED IDEOGRAPH
+ "88 A4 611B", #CJK UNIFIED IDEOGRAPH
+ "88 A5 6328", #CJK UNIFIED IDEOGRAPH
+ "88 A6 59F6", #CJK UNIFIED IDEOGRAPH
+ "88 A7 9022", #CJK UNIFIED IDEOGRAPH
+ "88 A8 8475", #CJK UNIFIED IDEOGRAPH
+ "88 A9 831C", #CJK UNIFIED IDEOGRAPH
+ "88 AA 7A50", #CJK UNIFIED IDEOGRAPH
+ "88 AB 60AA", #CJK UNIFIED IDEOGRAPH
+ "88 AC 63E1", #CJK UNIFIED IDEOGRAPH
+ "88 AD 6E25", #CJK UNIFIED IDEOGRAPH
+ "88 AE 65ED", #CJK UNIFIED IDEOGRAPH
+ "88 AF 8466", #CJK UNIFIED IDEOGRAPH
+ "88 B0 82A6", #CJK UNIFIED IDEOGRAPH
+ "88 B1 9BF5", #CJK UNIFIED IDEOGRAPH
+ "88 B2 6893", #CJK UNIFIED IDEOGRAPH
+ "88 B3 5727", #CJK UNIFIED IDEOGRAPH
+ "88 B4 65A1", #CJK UNIFIED IDEOGRAPH
+ "88 B5 6271", #CJK UNIFIED IDEOGRAPH
+ "88 B6 5B9B", #CJK UNIFIED IDEOGRAPH
+ "88 B7 59D0", #CJK UNIFIED IDEOGRAPH
+ "88 B8 867B", #CJK UNIFIED IDEOGRAPH
+ "88 B9 98F4", #CJK UNIFIED IDEOGRAPH
+ "88 BA 7D62", #CJK UNIFIED IDEOGRAPH
+ "88 BB 7DBE", #CJK UNIFIED IDEOGRAPH
+ "88 BC 9B8E", #CJK UNIFIED IDEOGRAPH
+ "88 BD 6216", #CJK UNIFIED IDEOGRAPH
+ "88 BE 7C9F", #CJK UNIFIED IDEOGRAPH
+ "88 BF 88B7", #CJK UNIFIED IDEOGRAPH
+ "88 C0 5B89", #CJK UNIFIED IDEOGRAPH
+ "88 C1 5EB5", #CJK UNIFIED IDEOGRAPH
+ "88 C2 6309", #CJK UNIFIED IDEOGRAPH
+ "88 C3 6697", #CJK UNIFIED IDEOGRAPH
+ "88 C4 6848", #CJK UNIFIED IDEOGRAPH
+ "88 C5 95C7", #CJK UNIFIED IDEOGRAPH
+ "88 C6 978D", #CJK UNIFIED IDEOGRAPH
+ "88 C7 674F", #CJK UNIFIED IDEOGRAPH
+ "88 C8 4EE5", #CJK UNIFIED IDEOGRAPH
+ "88 C9 4F0A", #CJK UNIFIED IDEOGRAPH
+ "88 CA 4F4D", #CJK UNIFIED IDEOGRAPH
+ "88 CB 4F9D", #CJK UNIFIED IDEOGRAPH
+ "88 CC 5049", #CJK UNIFIED IDEOGRAPH
+ "88 CD 56F2", #CJK UNIFIED IDEOGRAPH
+ "88 CE 5937", #CJK UNIFIED IDEOGRAPH
+ "88 CF 59D4", #CJK UNIFIED IDEOGRAPH
+ "88 D0 5A01", #CJK UNIFIED IDEOGRAPH
+ "88 D1 5C09", #CJK UNIFIED IDEOGRAPH
+ "88 D2 60DF", #CJK UNIFIED IDEOGRAPH
+ "88 D3 610F", #CJK UNIFIED IDEOGRAPH
+ "88 D4 6170", #CJK UNIFIED IDEOGRAPH
+ "88 D5 6613", #CJK UNIFIED IDEOGRAPH
+ "88 D6 6905", #CJK UNIFIED IDEOGRAPH
+ "88 D7 70BA", #CJK UNIFIED IDEOGRAPH
+ "88 D8 754F", #CJK UNIFIED IDEOGRAPH
+ "88 D9 7570", #CJK UNIFIED IDEOGRAPH
+ "88 DA 79FB", #CJK UNIFIED IDEOGRAPH
+ "88 DB 7DAD", #CJK UNIFIED IDEOGRAPH
+ "88 DC 7DEF", #CJK UNIFIED IDEOGRAPH
+ "88 DD 80C3", #CJK UNIFIED IDEOGRAPH
+ "88 DE 840E", #CJK UNIFIED IDEOGRAPH
+ "88 DF 8863", #CJK UNIFIED IDEOGRAPH
+ "88 E0 8B02", #CJK UNIFIED IDEOGRAPH
+ "88 E1 9055", #CJK UNIFIED IDEOGRAPH
+ "88 E2 907A", #CJK UNIFIED IDEOGRAPH
+ "88 E3 533B", #CJK UNIFIED IDEOGRAPH
+ "88 E4 4E95", #CJK UNIFIED IDEOGRAPH
+ "88 E5 4EA5", #CJK UNIFIED IDEOGRAPH
+ "88 E6 57DF", #CJK UNIFIED IDEOGRAPH
+ "88 E7 80B2", #CJK UNIFIED IDEOGRAPH
+ "88 E8 90C1", #CJK UNIFIED IDEOGRAPH
+ "88 E9 78EF", #CJK UNIFIED IDEOGRAPH
+ "88 EA 4E00", #CJK UNIFIED IDEOGRAPH
+ "88 EB 58F1", #CJK UNIFIED IDEOGRAPH
+ "88 EC 6EA2", #CJK UNIFIED IDEOGRAPH
+ "88 ED 9038", #CJK UNIFIED IDEOGRAPH
+ "88 EE 7A32", #CJK UNIFIED IDEOGRAPH
+ "88 EF 8328", #CJK UNIFIED IDEOGRAPH
+ "88 F0 828B", #CJK UNIFIED IDEOGRAPH
+ "88 F1 9C2F", #CJK UNIFIED IDEOGRAPH
+ "88 F2 5141", #CJK UNIFIED IDEOGRAPH
+ "88 F3 5370", #CJK UNIFIED IDEOGRAPH
+ "88 F4 54BD", #CJK UNIFIED IDEOGRAPH
+ "88 F5 54E1", #CJK UNIFIED IDEOGRAPH
+ "88 F6 56E0", #CJK UNIFIED IDEOGRAPH
+ "88 F7 59FB", #CJK UNIFIED IDEOGRAPH
+ "88 F8 5F15", #CJK UNIFIED IDEOGRAPH
+ "88 F9 98F2", #CJK UNIFIED IDEOGRAPH
+ "88 FA 6DEB", #CJK UNIFIED IDEOGRAPH
+ "88 FB 80E4", #CJK UNIFIED IDEOGRAPH
+ "88 FC 852D", #CJK UNIFIED IDEOGRAPH
+ "89 40 9662", #CJK UNIFIED IDEOGRAPH
+ "89 41 9670", #CJK UNIFIED IDEOGRAPH
+ "89 42 96A0", #CJK UNIFIED IDEOGRAPH
+ "89 43 97FB", #CJK UNIFIED IDEOGRAPH
+ "89 44 540B", #CJK UNIFIED IDEOGRAPH
+ "89 45 53F3", #CJK UNIFIED IDEOGRAPH
+ "89 46 5B87", #CJK UNIFIED IDEOGRAPH
+ "89 47 70CF", #CJK UNIFIED IDEOGRAPH
+ "89 48 7FBD", #CJK UNIFIED IDEOGRAPH
+ "89 49 8FC2", #CJK UNIFIED IDEOGRAPH
+ "89 4A 96E8", #CJK UNIFIED IDEOGRAPH
+ "89 4B 536F", #CJK UNIFIED IDEOGRAPH
+ "89 4C 9D5C", #CJK UNIFIED IDEOGRAPH
+ "89 4D 7ABA", #CJK UNIFIED IDEOGRAPH
+ "89 4E 4E11", #CJK UNIFIED IDEOGRAPH
+ "89 4F 7893", #CJK UNIFIED IDEOGRAPH
+ "89 50 81FC", #CJK UNIFIED IDEOGRAPH
+ "89 51 6E26", #CJK UNIFIED IDEOGRAPH
+ "89 52 5618", #CJK UNIFIED IDEOGRAPH
+ "89 53 5504", #CJK UNIFIED IDEOGRAPH
+ "89 54 6B1D", #CJK UNIFIED IDEOGRAPH
+ "89 55 851A", #CJK UNIFIED IDEOGRAPH
+ "89 56 9C3B", #CJK UNIFIED IDEOGRAPH
+ "89 57 59E5", #CJK UNIFIED IDEOGRAPH
+ "89 58 53A9", #CJK UNIFIED IDEOGRAPH
+ "89 59 6D66", #CJK UNIFIED IDEOGRAPH
+ "89 5A 74DC", #CJK UNIFIED IDEOGRAPH
+ "89 5B 958F", #CJK UNIFIED IDEOGRAPH
+ "89 5C 5642", #CJK UNIFIED IDEOGRAPH
+ "89 5D 4E91", #CJK UNIFIED IDEOGRAPH
+ "89 5E 904B", #CJK UNIFIED IDEOGRAPH
+ "89 5F 96F2", #CJK UNIFIED IDEOGRAPH
+ "89 60 834F", #CJK UNIFIED IDEOGRAPH
+ "89 61 990C", #CJK UNIFIED IDEOGRAPH
+ "89 62 53E1", #CJK UNIFIED IDEOGRAPH
+ "89 63 55B6", #CJK UNIFIED IDEOGRAPH
+ "89 64 5B30", #CJK UNIFIED IDEOGRAPH
+ "89 65 5F71", #CJK UNIFIED IDEOGRAPH
+ "89 66 6620", #CJK UNIFIED IDEOGRAPH
+ "89 67 66F3", #CJK UNIFIED IDEOGRAPH
+ "89 68 6804", #CJK UNIFIED IDEOGRAPH
+ "89 69 6C38", #CJK UNIFIED IDEOGRAPH
+ "89 6A 6CF3", #CJK UNIFIED IDEOGRAPH
+ "89 6B 6D29", #CJK UNIFIED IDEOGRAPH
+ "89 6C 745B", #CJK UNIFIED IDEOGRAPH
+ "89 6D 76C8", #CJK UNIFIED IDEOGRAPH
+ "89 6E 7A4E", #CJK UNIFIED IDEOGRAPH
+ "89 6F 9834", #CJK UNIFIED IDEOGRAPH
+ "89 70 82F1", #CJK UNIFIED IDEOGRAPH
+ "89 71 885B", #CJK UNIFIED IDEOGRAPH
+ "89 72 8A60", #CJK UNIFIED IDEOGRAPH
+ "89 73 92ED", #CJK UNIFIED IDEOGRAPH
+ "89 74 6DB2", #CJK UNIFIED IDEOGRAPH
+ "89 75 75AB", #CJK UNIFIED IDEOGRAPH
+ "89 76 76CA", #CJK UNIFIED IDEOGRAPH
+ "89 77 99C5", #CJK UNIFIED IDEOGRAPH
+ "89 78 60A6", #CJK UNIFIED IDEOGRAPH
+ "89 79 8B01", #CJK UNIFIED IDEOGRAPH
+ "89 7A 8D8A", #CJK UNIFIED IDEOGRAPH
+ "89 7B 95B2", #CJK UNIFIED IDEOGRAPH
+ "89 7C 698E", #CJK UNIFIED IDEOGRAPH
+ "89 7D 53AD", #CJK UNIFIED IDEOGRAPH
+ "89 7E 5186", #CJK UNIFIED IDEOGRAPH
+ "89 80 5712", #CJK UNIFIED IDEOGRAPH
+ "89 81 5830", #CJK UNIFIED IDEOGRAPH
+ "89 82 5944", #CJK UNIFIED IDEOGRAPH
+ "89 83 5BB4", #CJK UNIFIED IDEOGRAPH
+ "89 84 5EF6", #CJK UNIFIED IDEOGRAPH
+ "89 85 6028", #CJK UNIFIED IDEOGRAPH
+ "89 86 63A9", #CJK UNIFIED IDEOGRAPH
+ "89 87 63F4", #CJK UNIFIED IDEOGRAPH
+ "89 88 6CBF", #CJK UNIFIED IDEOGRAPH
+ "89 89 6F14", #CJK UNIFIED IDEOGRAPH
+ "89 8A 708E", #CJK UNIFIED IDEOGRAPH
+ "89 8B 7114", #CJK UNIFIED IDEOGRAPH
+ "89 8C 7159", #CJK UNIFIED IDEOGRAPH
+ "89 8D 71D5", #CJK UNIFIED IDEOGRAPH
+ "89 8E 733F", #CJK UNIFIED IDEOGRAPH
+ "89 8F 7E01", #CJK UNIFIED IDEOGRAPH
+ "89 90 8276", #CJK UNIFIED IDEOGRAPH
+ "89 91 82D1", #CJK UNIFIED IDEOGRAPH
+ "89 92 8597", #CJK UNIFIED IDEOGRAPH
+ "89 93 9060", #CJK UNIFIED IDEOGRAPH
+ "89 94 925B", #CJK UNIFIED IDEOGRAPH
+ "89 95 9D1B", #CJK UNIFIED IDEOGRAPH
+ "89 96 5869", #CJK UNIFIED IDEOGRAPH
+ "89 97 65BC", #CJK UNIFIED IDEOGRAPH
+ "89 98 6C5A", #CJK UNIFIED IDEOGRAPH
+ "89 99 7525", #CJK UNIFIED IDEOGRAPH
+ "89 9A 51F9", #CJK UNIFIED IDEOGRAPH
+ "89 9B 592E", #CJK UNIFIED IDEOGRAPH
+ "89 9C 5965", #CJK UNIFIED IDEOGRAPH
+ "89 9D 5F80", #CJK UNIFIED IDEOGRAPH
+ "89 9E 5FDC", #CJK UNIFIED IDEOGRAPH
+ "89 9F 62BC", #CJK UNIFIED IDEOGRAPH
+ "89 A0 65FA", #CJK UNIFIED IDEOGRAPH
+ "89 A1 6A2A", #CJK UNIFIED IDEOGRAPH
+ "89 A2 6B27", #CJK UNIFIED IDEOGRAPH
+ "89 A3 6BB4", #CJK UNIFIED IDEOGRAPH
+ "89 A4 738B", #CJK UNIFIED IDEOGRAPH
+ "89 A5 7FC1", #CJK UNIFIED IDEOGRAPH
+ "89 A6 8956", #CJK UNIFIED IDEOGRAPH
+ "89 A7 9D2C", #CJK UNIFIED IDEOGRAPH
+ "89 A8 9D0E", #CJK UNIFIED IDEOGRAPH
+ "89 A9 9EC4", #CJK UNIFIED IDEOGRAPH
+ "89 AA 5CA1", #CJK UNIFIED IDEOGRAPH
+ "89 AB 6C96", #CJK UNIFIED IDEOGRAPH
+ "89 AC 837B", #CJK UNIFIED IDEOGRAPH
+ "89 AD 5104", #CJK UNIFIED IDEOGRAPH
+ "89 AE 5C4B", #CJK UNIFIED IDEOGRAPH
+ "89 AF 61B6", #CJK UNIFIED IDEOGRAPH
+ "89 B0 81C6", #CJK UNIFIED IDEOGRAPH
+ "89 B1 6876", #CJK UNIFIED IDEOGRAPH
+ "89 B2 7261", #CJK UNIFIED IDEOGRAPH
+ "89 B3 4E59", #CJK UNIFIED IDEOGRAPH
+ "89 B4 4FFA", #CJK UNIFIED IDEOGRAPH
+ "89 B5 5378", #CJK UNIFIED IDEOGRAPH
+ "89 B6 6069", #CJK UNIFIED IDEOGRAPH
+ "89 B7 6E29", #CJK UNIFIED IDEOGRAPH
+ "89 B8 7A4F", #CJK UNIFIED IDEOGRAPH
+ "89 B9 97F3", #CJK UNIFIED IDEOGRAPH
+ "89 BA 4E0B", #CJK UNIFIED IDEOGRAPH
+ "89 BB 5316", #CJK UNIFIED IDEOGRAPH
+ "89 BC 4EEE", #CJK UNIFIED IDEOGRAPH
+ "89 BD 4F55", #CJK UNIFIED IDEOGRAPH
+ "89 BE 4F3D", #CJK UNIFIED IDEOGRAPH
+ "89 BF 4FA1", #CJK UNIFIED IDEOGRAPH
+ "89 C0 4F73", #CJK UNIFIED IDEOGRAPH
+ "89 C1 52A0", #CJK UNIFIED IDEOGRAPH
+ "89 C2 53EF", #CJK UNIFIED IDEOGRAPH
+ "89 C3 5609", #CJK UNIFIED IDEOGRAPH
+ "89 C4 590F", #CJK UNIFIED IDEOGRAPH
+ "89 C5 5AC1", #CJK UNIFIED IDEOGRAPH
+ "89 C6 5BB6", #CJK UNIFIED IDEOGRAPH
+ "89 C7 5BE1", #CJK UNIFIED IDEOGRAPH
+ "89 C8 79D1", #CJK UNIFIED IDEOGRAPH
+ "89 C9 6687", #CJK UNIFIED IDEOGRAPH
+ "89 CA 679C", #CJK UNIFIED IDEOGRAPH
+ "89 CB 67B6", #CJK UNIFIED IDEOGRAPH
+ "89 CC 6B4C", #CJK UNIFIED IDEOGRAPH
+ "89 CD 6CB3", #CJK UNIFIED IDEOGRAPH
+ "89 CE 706B", #CJK UNIFIED IDEOGRAPH
+ "89 CF 73C2", #CJK UNIFIED IDEOGRAPH
+ "89 D0 798D", #CJK UNIFIED IDEOGRAPH
+ "89 D1 79BE", #CJK UNIFIED IDEOGRAPH
+ "89 D2 7A3C", #CJK UNIFIED IDEOGRAPH
+ "89 D3 7B87", #CJK UNIFIED IDEOGRAPH
+ "89 D4 82B1", #CJK UNIFIED IDEOGRAPH
+ "89 D5 82DB", #CJK UNIFIED IDEOGRAPH
+ "89 D6 8304", #CJK UNIFIED IDEOGRAPH
+ "89 D7 8377", #CJK UNIFIED IDEOGRAPH
+ "89 D8 83EF", #CJK UNIFIED IDEOGRAPH
+ "89 D9 83D3", #CJK UNIFIED IDEOGRAPH
+ "89 DA 8766", #CJK UNIFIED IDEOGRAPH
+ "89 DB 8AB2", #CJK UNIFIED IDEOGRAPH
+ "89 DC 5629", #CJK UNIFIED IDEOGRAPH
+ "89 DD 8CA8", #CJK UNIFIED IDEOGRAPH
+ "89 DE 8FE6", #CJK UNIFIED IDEOGRAPH
+ "89 DF 904E", #CJK UNIFIED IDEOGRAPH
+ "89 E0 971E", #CJK UNIFIED IDEOGRAPH
+ "89 E1 868A", #CJK UNIFIED IDEOGRAPH
+ "89 E2 4FC4", #CJK UNIFIED IDEOGRAPH
+ "89 E3 5CE8", #CJK UNIFIED IDEOGRAPH
+ "89 E4 6211", #CJK UNIFIED IDEOGRAPH
+ "89 E5 7259", #CJK UNIFIED IDEOGRAPH
+ "89 E6 753B", #CJK UNIFIED IDEOGRAPH
+ "89 E7 81E5", #CJK UNIFIED IDEOGRAPH
+ "89 E8 82BD", #CJK UNIFIED IDEOGRAPH
+ "89 E9 86FE", #CJK UNIFIED IDEOGRAPH
+ "89 EA 8CC0", #CJK UNIFIED IDEOGRAPH
+ "89 EB 96C5", #CJK UNIFIED IDEOGRAPH
+ "89 EC 9913", #CJK UNIFIED IDEOGRAPH
+ "89 ED 99D5", #CJK UNIFIED IDEOGRAPH
+ "89 EE 4ECB", #CJK UNIFIED IDEOGRAPH
+ "89 EF 4F1A", #CJK UNIFIED IDEOGRAPH
+ "89 F0 89E3", #CJK UNIFIED IDEOGRAPH
+ "89 F1 56DE", #CJK UNIFIED IDEOGRAPH
+ "89 F2 584A", #CJK UNIFIED IDEOGRAPH
+ "89 F3 58CA", #CJK UNIFIED IDEOGRAPH
+ "89 F4 5EFB", #CJK UNIFIED IDEOGRAPH
+ "89 F5 5FEB", #CJK UNIFIED IDEOGRAPH
+ "89 F6 602A", #CJK UNIFIED IDEOGRAPH
+ "89 F7 6094", #CJK UNIFIED IDEOGRAPH
+ "89 F8 6062", #CJK UNIFIED IDEOGRAPH
+ "89 F9 61D0", #CJK UNIFIED IDEOGRAPH
+ "89 FA 6212", #CJK UNIFIED IDEOGRAPH
+ "89 FB 62D0", #CJK UNIFIED IDEOGRAPH
+ "89 FC 6539", #CJK UNIFIED IDEOGRAPH
+ "8A 40 9B41", #CJK UNIFIED IDEOGRAPH
+ "8A 41 6666", #CJK UNIFIED IDEOGRAPH
+ "8A 42 68B0", #CJK UNIFIED IDEOGRAPH
+ "8A 43 6D77", #CJK UNIFIED IDEOGRAPH
+ "8A 44 7070", #CJK UNIFIED IDEOGRAPH
+ "8A 45 754C", #CJK UNIFIED IDEOGRAPH
+ "8A 46 7686", #CJK UNIFIED IDEOGRAPH
+ "8A 47 7D75", #CJK UNIFIED IDEOGRAPH
+ "8A 48 82A5", #CJK UNIFIED IDEOGRAPH
+ "8A 49 87F9", #CJK UNIFIED IDEOGRAPH
+ "8A 4A 958B", #CJK UNIFIED IDEOGRAPH
+ "8A 4B 968E", #CJK UNIFIED IDEOGRAPH
+ "8A 4C 8C9D", #CJK UNIFIED IDEOGRAPH
+ "8A 4D 51F1", #CJK UNIFIED IDEOGRAPH
+ "8A 4E 52BE", #CJK UNIFIED IDEOGRAPH
+ "8A 4F 5916", #CJK UNIFIED IDEOGRAPH
+ "8A 50 54B3", #CJK UNIFIED IDEOGRAPH
+ "8A 51 5BB3", #CJK UNIFIED IDEOGRAPH
+ "8A 52 5D16", #CJK UNIFIED IDEOGRAPH
+ "8A 53 6168", #CJK UNIFIED IDEOGRAPH
+ "8A 54 6982", #CJK UNIFIED IDEOGRAPH
+ "8A 55 6DAF", #CJK UNIFIED IDEOGRAPH
+ "8A 56 788D", #CJK UNIFIED IDEOGRAPH
+ "8A 57 84CB", #CJK UNIFIED IDEOGRAPH
+ "8A 58 8857", #CJK UNIFIED IDEOGRAPH
+ "8A 59 8A72", #CJK UNIFIED IDEOGRAPH
+ "8A 5A 93A7", #CJK UNIFIED IDEOGRAPH
+ "8A 5B 9AB8", #CJK UNIFIED IDEOGRAPH
+ "8A 5C 6D6C", #CJK UNIFIED IDEOGRAPH
+ "8A 5D 99A8", #CJK UNIFIED IDEOGRAPH
+ "8A 5E 86D9", #CJK UNIFIED IDEOGRAPH
+ "8A 5F 57A3", #CJK UNIFIED IDEOGRAPH
+ "8A 60 67FF", #CJK UNIFIED IDEOGRAPH
+ "8A 61 86CE", #CJK UNIFIED IDEOGRAPH
+ "8A 62 920E", #CJK UNIFIED IDEOGRAPH
+ "8A 63 5283", #CJK UNIFIED IDEOGRAPH
+ "8A 64 5687", #CJK UNIFIED IDEOGRAPH
+ "8A 65 5404", #CJK UNIFIED IDEOGRAPH
+ "8A 66 5ED3", #CJK UNIFIED IDEOGRAPH
+ "8A 67 62E1", #CJK UNIFIED IDEOGRAPH
+ "8A 68 64B9", #CJK UNIFIED IDEOGRAPH
+ "8A 69 683C", #CJK UNIFIED IDEOGRAPH
+ "8A 6A 6838", #CJK UNIFIED IDEOGRAPH
+ "8A 6B 6BBB", #CJK UNIFIED IDEOGRAPH
+ "8A 6C 7372", #CJK UNIFIED IDEOGRAPH
+ "8A 6D 78BA", #CJK UNIFIED IDEOGRAPH
+ "8A 6E 7A6B", #CJK UNIFIED IDEOGRAPH
+ "8A 6F 899A", #CJK UNIFIED IDEOGRAPH
+ "8A 70 89D2", #CJK UNIFIED IDEOGRAPH
+ "8A 71 8D6B", #CJK UNIFIED IDEOGRAPH
+ "8A 72 8F03", #CJK UNIFIED IDEOGRAPH
+ "8A 73 90ED", #CJK UNIFIED IDEOGRAPH
+ "8A 74 95A3", #CJK UNIFIED IDEOGRAPH
+ "8A 75 9694", #CJK UNIFIED IDEOGRAPH
+ "8A 76 9769", #CJK UNIFIED IDEOGRAPH
+ "8A 77 5B66", #CJK UNIFIED IDEOGRAPH
+ "8A 78 5CB3", #CJK UNIFIED IDEOGRAPH
+ "8A 79 697D", #CJK UNIFIED IDEOGRAPH
+ "8A 7A 984D", #CJK UNIFIED IDEOGRAPH
+ "8A 7B 984E", #CJK UNIFIED IDEOGRAPH
+ "8A 7C 639B", #CJK UNIFIED IDEOGRAPH
+ "8A 7D 7B20", #CJK UNIFIED IDEOGRAPH
+ "8A 7E 6A2B", #CJK UNIFIED IDEOGRAPH
+ "8A 80 6A7F", #CJK UNIFIED IDEOGRAPH
+ "8A 81 68B6", #CJK UNIFIED IDEOGRAPH
+ "8A 82 9C0D", #CJK UNIFIED IDEOGRAPH
+ "8A 83 6F5F", #CJK UNIFIED IDEOGRAPH
+ "8A 84 5272", #CJK UNIFIED IDEOGRAPH
+ "8A 85 559D", #CJK UNIFIED IDEOGRAPH
+ "8A 86 6070", #CJK UNIFIED IDEOGRAPH
+ "8A 87 62EC", #CJK UNIFIED IDEOGRAPH
+ "8A 88 6D3B", #CJK UNIFIED IDEOGRAPH
+ "8A 89 6E07", #CJK UNIFIED IDEOGRAPH
+ "8A 8A 6ED1", #CJK UNIFIED IDEOGRAPH
+ "8A 8B 845B", #CJK UNIFIED IDEOGRAPH
+ "8A 8C 8910", #CJK UNIFIED IDEOGRAPH
+ "8A 8D 8F44", #CJK UNIFIED IDEOGRAPH
+ "8A 8E 4E14", #CJK UNIFIED IDEOGRAPH
+ "8A 8F 9C39", #CJK UNIFIED IDEOGRAPH
+ "8A 90 53F6", #CJK UNIFIED IDEOGRAPH
+ "8A 91 691B", #CJK UNIFIED IDEOGRAPH
+ "8A 92 6A3A", #CJK UNIFIED IDEOGRAPH
+ "8A 93 9784", #CJK UNIFIED IDEOGRAPH
+ "8A 94 682A", #CJK UNIFIED IDEOGRAPH
+ "8A 95 515C", #CJK UNIFIED IDEOGRAPH
+ "8A 96 7AC3", #CJK UNIFIED IDEOGRAPH
+ "8A 97 84B2", #CJK UNIFIED IDEOGRAPH
+ "8A 98 91DC", #CJK UNIFIED IDEOGRAPH
+ "8A 99 938C", #CJK UNIFIED IDEOGRAPH
+ "8A 9A 565B", #CJK UNIFIED IDEOGRAPH
+ "8A 9B 9D28", #CJK UNIFIED IDEOGRAPH
+ "8A 9C 6822", #CJK UNIFIED IDEOGRAPH
+ "8A 9D 8305", #CJK UNIFIED IDEOGRAPH
+ "8A 9E 8431", #CJK UNIFIED IDEOGRAPH
+ "8A 9F 7CA5", #CJK UNIFIED IDEOGRAPH
+ "8A A0 5208", #CJK UNIFIED IDEOGRAPH
+ "8A A1 82C5", #CJK UNIFIED IDEOGRAPH
+ "8A A2 74E6", #CJK UNIFIED IDEOGRAPH
+ "8A A3 4E7E", #CJK UNIFIED IDEOGRAPH
+ "8A A4 4F83", #CJK UNIFIED IDEOGRAPH
+ "8A A5 51A0", #CJK UNIFIED IDEOGRAPH
+ "8A A6 5BD2", #CJK UNIFIED IDEOGRAPH
+ "8A A7 520A", #CJK UNIFIED IDEOGRAPH
+ "8A A8 52D8", #CJK UNIFIED IDEOGRAPH
+ "8A A9 52E7", #CJK UNIFIED IDEOGRAPH
+ "8A AA 5DFB", #CJK UNIFIED IDEOGRAPH
+ "8A AB 559A", #CJK UNIFIED IDEOGRAPH
+ "8A AC 582A", #CJK UNIFIED IDEOGRAPH
+ "8A AD 59E6", #CJK UNIFIED IDEOGRAPH
+ "8A AE 5B8C", #CJK UNIFIED IDEOGRAPH
+ "8A AF 5B98", #CJK UNIFIED IDEOGRAPH
+ "8A B0 5BDB", #CJK UNIFIED IDEOGRAPH
+ "8A B1 5E72", #CJK UNIFIED IDEOGRAPH
+ "8A B2 5E79", #CJK UNIFIED IDEOGRAPH
+ "8A B3 60A3", #CJK UNIFIED IDEOGRAPH
+ "8A B4 611F", #CJK UNIFIED IDEOGRAPH
+ "8A B5 6163", #CJK UNIFIED IDEOGRAPH
+ "8A B6 61BE", #CJK UNIFIED IDEOGRAPH
+ "8A B7 63DB", #CJK UNIFIED IDEOGRAPH
+ "8A B8 6562", #CJK UNIFIED IDEOGRAPH
+ "8A B9 67D1", #CJK UNIFIED IDEOGRAPH
+ "8A BA 6853", #CJK UNIFIED IDEOGRAPH
+ "8A BB 68FA", #CJK UNIFIED IDEOGRAPH
+ "8A BC 6B3E", #CJK UNIFIED IDEOGRAPH
+ "8A BD 6B53", #CJK UNIFIED IDEOGRAPH
+ "8A BE 6C57", #CJK UNIFIED IDEOGRAPH
+ "8A BF 6F22", #CJK UNIFIED IDEOGRAPH
+ "8A C0 6F97", #CJK UNIFIED IDEOGRAPH
+ "8A C1 6F45", #CJK UNIFIED IDEOGRAPH
+ "8A C2 74B0", #CJK UNIFIED IDEOGRAPH
+ "8A C3 7518", #CJK UNIFIED IDEOGRAPH
+ "8A C4 76E3", #CJK UNIFIED IDEOGRAPH
+ "8A C5 770B", #CJK UNIFIED IDEOGRAPH
+ "8A C6 7AFF", #CJK UNIFIED IDEOGRAPH
+ "8A C7 7BA1", #CJK UNIFIED IDEOGRAPH
+ "8A C8 7C21", #CJK UNIFIED IDEOGRAPH
+ "8A C9 7DE9", #CJK UNIFIED IDEOGRAPH
+ "8A CA 7F36", #CJK UNIFIED IDEOGRAPH
+ "8A CB 7FF0", #CJK UNIFIED IDEOGRAPH
+ "8A CC 809D", #CJK UNIFIED IDEOGRAPH
+ "8A CD 8266", #CJK UNIFIED IDEOGRAPH
+ "8A CE 839E", #CJK UNIFIED IDEOGRAPH
+ "8A CF 89B3", #CJK UNIFIED IDEOGRAPH
+ "8A D0 8ACC", #CJK UNIFIED IDEOGRAPH
+ "8A D1 8CAB", #CJK UNIFIED IDEOGRAPH
+ "8A D2 9084", #CJK UNIFIED IDEOGRAPH
+ "8A D3 9451", #CJK UNIFIED IDEOGRAPH
+ "8A D4 9593", #CJK UNIFIED IDEOGRAPH
+ "8A D5 9591", #CJK UNIFIED IDEOGRAPH
+ "8A D6 95A2", #CJK UNIFIED IDEOGRAPH
+ "8A D7 9665", #CJK UNIFIED IDEOGRAPH
+ "8A D8 97D3", #CJK UNIFIED IDEOGRAPH
+ "8A D9 9928", #CJK UNIFIED IDEOGRAPH
+ "8A DA 8218", #CJK UNIFIED IDEOGRAPH
+ "8A DB 4E38", #CJK UNIFIED IDEOGRAPH
+ "8A DC 542B", #CJK UNIFIED IDEOGRAPH
+ "8A DD 5CB8", #CJK UNIFIED IDEOGRAPH
+ "8A DE 5DCC", #CJK UNIFIED IDEOGRAPH
+ "8A DF 73A9", #CJK UNIFIED IDEOGRAPH
+ "8A E0 764C", #CJK UNIFIED IDEOGRAPH
+ "8A E1 773C", #CJK UNIFIED IDEOGRAPH
+ "8A E2 5CA9", #CJK UNIFIED IDEOGRAPH
+ "8A E3 7FEB", #CJK UNIFIED IDEOGRAPH
+ "8A E4 8D0B", #CJK UNIFIED IDEOGRAPH
+ "8A E5 96C1", #CJK UNIFIED IDEOGRAPH
+ "8A E6 9811", #CJK UNIFIED IDEOGRAPH
+ "8A E7 9854", #CJK UNIFIED IDEOGRAPH
+ "8A E8 9858", #CJK UNIFIED IDEOGRAPH
+ "8A E9 4F01", #CJK UNIFIED IDEOGRAPH
+ "8A EA 4F0E", #CJK UNIFIED IDEOGRAPH
+ "8A EB 5371", #CJK UNIFIED IDEOGRAPH
+ "8A EC 559C", #CJK UNIFIED IDEOGRAPH
+ "8A ED 5668", #CJK UNIFIED IDEOGRAPH
+ "8A EE 57FA", #CJK UNIFIED IDEOGRAPH
+ "8A EF 5947", #CJK UNIFIED IDEOGRAPH
+ "8A F0 5B09", #CJK UNIFIED IDEOGRAPH
+ "8A F1 5BC4", #CJK UNIFIED IDEOGRAPH
+ "8A F2 5C90", #CJK UNIFIED IDEOGRAPH
+ "8A F3 5E0C", #CJK UNIFIED IDEOGRAPH
+ "8A F4 5E7E", #CJK UNIFIED IDEOGRAPH
+ "8A F5 5FCC", #CJK UNIFIED IDEOGRAPH
+ "8A F6 63EE", #CJK UNIFIED IDEOGRAPH
+ "8A F7 673A", #CJK UNIFIED IDEOGRAPH
+ "8A F8 65D7", #CJK UNIFIED IDEOGRAPH
+ "8A F9 65E2", #CJK UNIFIED IDEOGRAPH
+ "8A FA 671F", #CJK UNIFIED IDEOGRAPH
+ "8A FB 68CB", #CJK UNIFIED IDEOGRAPH
+ "8A FC 68C4", #CJK UNIFIED IDEOGRAPH
+ "8B 40 6A5F", #CJK UNIFIED IDEOGRAPH
+ "8B 41 5E30", #CJK UNIFIED IDEOGRAPH
+ "8B 42 6BC5", #CJK UNIFIED IDEOGRAPH
+ "8B 43 6C17", #CJK UNIFIED IDEOGRAPH
+ "8B 44 6C7D", #CJK UNIFIED IDEOGRAPH
+ "8B 45 757F", #CJK UNIFIED IDEOGRAPH
+ "8B 46 7948", #CJK UNIFIED IDEOGRAPH
+ "8B 47 5B63", #CJK UNIFIED IDEOGRAPH
+ "8B 48 7A00", #CJK UNIFIED IDEOGRAPH
+ "8B 49 7D00", #CJK UNIFIED IDEOGRAPH
+ "8B 4A 5FBD", #CJK UNIFIED IDEOGRAPH
+ "8B 4B 898F", #CJK UNIFIED IDEOGRAPH
+ "8B 4C 8A18", #CJK UNIFIED IDEOGRAPH
+ "8B 4D 8CB4", #CJK UNIFIED IDEOGRAPH
+ "8B 4E 8D77", #CJK UNIFIED IDEOGRAPH
+ "8B 4F 8ECC", #CJK UNIFIED IDEOGRAPH
+ "8B 50 8F1D", #CJK UNIFIED IDEOGRAPH
+ "8B 51 98E2", #CJK UNIFIED IDEOGRAPH
+ "8B 52 9A0E", #CJK UNIFIED IDEOGRAPH
+ "8B 53 9B3C", #CJK UNIFIED IDEOGRAPH
+ "8B 54 4E80", #CJK UNIFIED IDEOGRAPH
+ "8B 55 507D", #CJK UNIFIED IDEOGRAPH
+ "8B 56 5100", #CJK UNIFIED IDEOGRAPH
+ "8B 57 5993", #CJK UNIFIED IDEOGRAPH
+ "8B 58 5B9C", #CJK UNIFIED IDEOGRAPH
+ "8B 59 622F", #CJK UNIFIED IDEOGRAPH
+ "8B 5A 6280", #CJK UNIFIED IDEOGRAPH
+ "8B 5B 64EC", #CJK UNIFIED IDEOGRAPH
+ "8B 5C 6B3A", #CJK UNIFIED IDEOGRAPH
+ "8B 5D 72A0", #CJK UNIFIED IDEOGRAPH
+ "8B 5E 7591", #CJK UNIFIED IDEOGRAPH
+ "8B 5F 7947", #CJK UNIFIED IDEOGRAPH
+ "8B 60 7FA9", #CJK UNIFIED IDEOGRAPH
+ "8B 61 87FB", #CJK UNIFIED IDEOGRAPH
+ "8B 62 8ABC", #CJK UNIFIED IDEOGRAPH
+ "8B 63 8B70", #CJK UNIFIED IDEOGRAPH
+ "8B 64 63AC", #CJK UNIFIED IDEOGRAPH
+ "8B 65 83CA", #CJK UNIFIED IDEOGRAPH
+ "8B 66 97A0", #CJK UNIFIED IDEOGRAPH
+ "8B 67 5409", #CJK UNIFIED IDEOGRAPH
+ "8B 68 5403", #CJK UNIFIED IDEOGRAPH
+ "8B 69 55AB", #CJK UNIFIED IDEOGRAPH
+ "8B 6A 6854", #CJK UNIFIED IDEOGRAPH
+ "8B 6B 6A58", #CJK UNIFIED IDEOGRAPH
+ "8B 6C 8A70", #CJK UNIFIED IDEOGRAPH
+ "8B 6D 7827", #CJK UNIFIED IDEOGRAPH
+ "8B 6E 6775", #CJK UNIFIED IDEOGRAPH
+ "8B 6F 9ECD", #CJK UNIFIED IDEOGRAPH
+ "8B 70 5374", #CJK UNIFIED IDEOGRAPH
+ "8B 71 5BA2", #CJK UNIFIED IDEOGRAPH
+ "8B 72 811A", #CJK UNIFIED IDEOGRAPH
+ "8B 73 8650", #CJK UNIFIED IDEOGRAPH
+ "8B 74 9006", #CJK UNIFIED IDEOGRAPH
+ "8B 75 4E18", #CJK UNIFIED IDEOGRAPH
+ "8B 76 4E45", #CJK UNIFIED IDEOGRAPH
+ "8B 77 4EC7", #CJK UNIFIED IDEOGRAPH
+ "8B 78 4F11", #CJK UNIFIED IDEOGRAPH
+ "8B 79 53CA", #CJK UNIFIED IDEOGRAPH
+ "8B 7A 5438", #CJK UNIFIED IDEOGRAPH
+ "8B 7B 5BAE", #CJK UNIFIED IDEOGRAPH
+ "8B 7C 5F13", #CJK UNIFIED IDEOGRAPH
+ "8B 7D 6025", #CJK UNIFIED IDEOGRAPH
+ "8B 7E 6551", #CJK UNIFIED IDEOGRAPH
+ "8B 80 673D", #CJK UNIFIED IDEOGRAPH
+ "8B 81 6C42", #CJK UNIFIED IDEOGRAPH
+ "8B 82 6C72", #CJK UNIFIED IDEOGRAPH
+ "8B 83 6CE3", #CJK UNIFIED IDEOGRAPH
+ "8B 84 7078", #CJK UNIFIED IDEOGRAPH
+ "8B 85 7403", #CJK UNIFIED IDEOGRAPH
+ "8B 86 7A76", #CJK UNIFIED IDEOGRAPH
+ "8B 87 7AAE", #CJK UNIFIED IDEOGRAPH
+ "8B 88 7B08", #CJK UNIFIED IDEOGRAPH
+ "8B 89 7D1A", #CJK UNIFIED IDEOGRAPH
+ "8B 8A 7CFE", #CJK UNIFIED IDEOGRAPH
+ "8B 8B 7D66", #CJK UNIFIED IDEOGRAPH
+ "8B 8C 65E7", #CJK UNIFIED IDEOGRAPH
+ "8B 8D 725B", #CJK UNIFIED IDEOGRAPH
+ "8B 8E 53BB", #CJK UNIFIED IDEOGRAPH
+ "8B 8F 5C45", #CJK UNIFIED IDEOGRAPH
+ "8B 90 5DE8", #CJK UNIFIED IDEOGRAPH
+ "8B 91 62D2", #CJK UNIFIED IDEOGRAPH
+ "8B 92 62E0", #CJK UNIFIED IDEOGRAPH
+ "8B 93 6319", #CJK UNIFIED IDEOGRAPH
+ "8B 94 6E20", #CJK UNIFIED IDEOGRAPH
+ "8B 95 865A", #CJK UNIFIED IDEOGRAPH
+ "8B 96 8A31", #CJK UNIFIED IDEOGRAPH
+ "8B 97 8DDD", #CJK UNIFIED IDEOGRAPH
+ "8B 98 92F8", #CJK UNIFIED IDEOGRAPH
+ "8B 99 6F01", #CJK UNIFIED IDEOGRAPH
+ "8B 9A 79A6", #CJK UNIFIED IDEOGRAPH
+ "8B 9B 9B5A", #CJK UNIFIED IDEOGRAPH
+ "8B 9C 4EA8", #CJK UNIFIED IDEOGRAPH
+ "8B 9D 4EAB", #CJK UNIFIED IDEOGRAPH
+ "8B 9E 4EAC", #CJK UNIFIED IDEOGRAPH
+ "8B 9F 4F9B", #CJK UNIFIED IDEOGRAPH
+ "8B A0 4FA0", #CJK UNIFIED IDEOGRAPH
+ "8B A1 50D1", #CJK UNIFIED IDEOGRAPH
+ "8B A2 5147", #CJK UNIFIED IDEOGRAPH
+ "8B A3 7AF6", #CJK UNIFIED IDEOGRAPH
+ "8B A4 5171", #CJK UNIFIED IDEOGRAPH
+ "8B A5 51F6", #CJK UNIFIED IDEOGRAPH
+ "8B A6 5354", #CJK UNIFIED IDEOGRAPH
+ "8B A7 5321", #CJK UNIFIED IDEOGRAPH
+ "8B A8 537F", #CJK UNIFIED IDEOGRAPH
+ "8B A9 53EB", #CJK UNIFIED IDEOGRAPH
+ "8B AA 55AC", #CJK UNIFIED IDEOGRAPH
+ "8B AB 5883", #CJK UNIFIED IDEOGRAPH
+ "8B AC 5CE1", #CJK UNIFIED IDEOGRAPH
+ "8B AD 5F37", #CJK UNIFIED IDEOGRAPH
+ "8B AE 5F4A", #CJK UNIFIED IDEOGRAPH
+ "8B AF 602F", #CJK UNIFIED IDEOGRAPH
+ "8B B0 6050", #CJK UNIFIED IDEOGRAPH
+ "8B B1 606D", #CJK UNIFIED IDEOGRAPH
+ "8B B2 631F", #CJK UNIFIED IDEOGRAPH
+ "8B B3 6559", #CJK UNIFIED IDEOGRAPH
+ "8B B4 6A4B", #CJK UNIFIED IDEOGRAPH
+ "8B B5 6CC1", #CJK UNIFIED IDEOGRAPH
+ "8B B6 72C2", #CJK UNIFIED IDEOGRAPH
+ "8B B7 72ED", #CJK UNIFIED IDEOGRAPH
+ "8B B8 77EF", #CJK UNIFIED IDEOGRAPH
+ "8B B9 80F8", #CJK UNIFIED IDEOGRAPH
+ "8B BA 8105", #CJK UNIFIED IDEOGRAPH
+ "8B BB 8208", #CJK UNIFIED IDEOGRAPH
+ "8B BC 854E", #CJK UNIFIED IDEOGRAPH
+ "8B BD 90F7", #CJK UNIFIED IDEOGRAPH
+ "8B BE 93E1", #CJK UNIFIED IDEOGRAPH
+ "8B BF 97FF", #CJK UNIFIED IDEOGRAPH
+ "8B C0 9957", #CJK UNIFIED IDEOGRAPH
+ "8B C1 9A5A", #CJK UNIFIED IDEOGRAPH
+ "8B C2 4EF0", #CJK UNIFIED IDEOGRAPH
+ "8B C3 51DD", #CJK UNIFIED IDEOGRAPH
+ "8B C4 5C2D", #CJK UNIFIED IDEOGRAPH
+ "8B C5 6681", #CJK UNIFIED IDEOGRAPH
+ "8B C6 696D", #CJK UNIFIED IDEOGRAPH
+ "8B C7 5C40", #CJK UNIFIED IDEOGRAPH
+ "8B C8 66F2", #CJK UNIFIED IDEOGRAPH
+ "8B C9 6975", #CJK UNIFIED IDEOGRAPH
+ "8B CA 7389", #CJK UNIFIED IDEOGRAPH
+ "8B CB 6850", #CJK UNIFIED IDEOGRAPH
+ "8B CC 7C81", #CJK UNIFIED IDEOGRAPH
+ "8B CD 50C5", #CJK UNIFIED IDEOGRAPH
+ "8B CE 52E4", #CJK UNIFIED IDEOGRAPH
+ "8B CF 5747", #CJK UNIFIED IDEOGRAPH
+ "8B D0 5DFE", #CJK UNIFIED IDEOGRAPH
+ "8B D1 9326", #CJK UNIFIED IDEOGRAPH
+ "8B D2 65A4", #CJK UNIFIED IDEOGRAPH
+ "8B D3 6B23", #CJK UNIFIED IDEOGRAPH
+ "8B D4 6B3D", #CJK UNIFIED IDEOGRAPH
+ "8B D5 7434", #CJK UNIFIED IDEOGRAPH
+ "8B D6 7981", #CJK UNIFIED IDEOGRAPH
+ "8B D7 79BD", #CJK UNIFIED IDEOGRAPH
+ "8B D8 7B4B", #CJK UNIFIED IDEOGRAPH
+ "8B D9 7DCA", #CJK UNIFIED IDEOGRAPH
+ "8B DA 82B9", #CJK UNIFIED IDEOGRAPH
+ "8B DB 83CC", #CJK UNIFIED IDEOGRAPH
+ "8B DC 887F", #CJK UNIFIED IDEOGRAPH
+ "8B DD 895F", #CJK UNIFIED IDEOGRAPH
+ "8B DE 8B39", #CJK UNIFIED IDEOGRAPH
+ "8B DF 8FD1", #CJK UNIFIED IDEOGRAPH
+ "8B E0 91D1", #CJK UNIFIED IDEOGRAPH
+ "8B E1 541F", #CJK UNIFIED IDEOGRAPH
+ "8B E2 9280", #CJK UNIFIED IDEOGRAPH
+ "8B E3 4E5D", #CJK UNIFIED IDEOGRAPH
+ "8B E4 5036", #CJK UNIFIED IDEOGRAPH
+ "8B E5 53E5", #CJK UNIFIED IDEOGRAPH
+ "8B E6 533A", #CJK UNIFIED IDEOGRAPH
+ "8B E7 72D7", #CJK UNIFIED IDEOGRAPH
+ "8B E8 7396", #CJK UNIFIED IDEOGRAPH
+ "8B E9 77E9", #CJK UNIFIED IDEOGRAPH
+ "8B EA 82E6", #CJK UNIFIED IDEOGRAPH
+ "8B EB 8EAF", #CJK UNIFIED IDEOGRAPH
+ "8B EC 99C6", #CJK UNIFIED IDEOGRAPH
+ "8B ED 99C8", #CJK UNIFIED IDEOGRAPH
+ "8B EE 99D2", #CJK UNIFIED IDEOGRAPH
+ "8B EF 5177", #CJK UNIFIED IDEOGRAPH
+ "8B F0 611A", #CJK UNIFIED IDEOGRAPH
+ "8B F1 865E", #CJK UNIFIED IDEOGRAPH
+ "8B F2 55B0", #CJK UNIFIED IDEOGRAPH
+ "8B F3 7A7A", #CJK UNIFIED IDEOGRAPH
+ "8B F4 5076", #CJK UNIFIED IDEOGRAPH
+ "8B F5 5BD3", #CJK UNIFIED IDEOGRAPH
+ "8B F6 9047", #CJK UNIFIED IDEOGRAPH
+ "8B F7 9685", #CJK UNIFIED IDEOGRAPH
+ "8B F8 4E32", #CJK UNIFIED IDEOGRAPH
+ "8B F9 6ADB", #CJK UNIFIED IDEOGRAPH
+ "8B FA 91E7", #CJK UNIFIED IDEOGRAPH
+ "8B FB 5C51", #CJK UNIFIED IDEOGRAPH
+ "8B FC 5C48", #CJK UNIFIED IDEOGRAPH
+ "8C 40 6398", #CJK UNIFIED IDEOGRAPH
+ "8C 41 7A9F", #CJK UNIFIED IDEOGRAPH
+ "8C 42 6C93", #CJK UNIFIED IDEOGRAPH
+ "8C 43 9774", #CJK UNIFIED IDEOGRAPH
+ "8C 44 8F61", #CJK UNIFIED IDEOGRAPH
+ "8C 45 7AAA", #CJK UNIFIED IDEOGRAPH
+ "8C 46 718A", #CJK UNIFIED IDEOGRAPH
+ "8C 47 9688", #CJK UNIFIED IDEOGRAPH
+ "8C 48 7C82", #CJK UNIFIED IDEOGRAPH
+ "8C 49 6817", #CJK UNIFIED IDEOGRAPH
+ "8C 4A 7E70", #CJK UNIFIED IDEOGRAPH
+ "8C 4B 6851", #CJK UNIFIED IDEOGRAPH
+ "8C 4C 936C", #CJK UNIFIED IDEOGRAPH
+ "8C 4D 52F2", #CJK UNIFIED IDEOGRAPH
+ "8C 4E 541B", #CJK UNIFIED IDEOGRAPH
+ "8C 4F 85AB", #CJK UNIFIED IDEOGRAPH
+ "8C 50 8A13", #CJK UNIFIED IDEOGRAPH
+ "8C 51 7FA4", #CJK UNIFIED IDEOGRAPH
+ "8C 52 8ECD", #CJK UNIFIED IDEOGRAPH
+ "8C 53 90E1", #CJK UNIFIED IDEOGRAPH
+ "8C 54 5366", #CJK UNIFIED IDEOGRAPH
+ "8C 55 8888", #CJK UNIFIED IDEOGRAPH
+ "8C 56 7941", #CJK UNIFIED IDEOGRAPH
+ "8C 57 4FC2", #CJK UNIFIED IDEOGRAPH
+ "8C 58 50BE", #CJK UNIFIED IDEOGRAPH
+ "8C 59 5211", #CJK UNIFIED IDEOGRAPH
+ "8C 5A 5144", #CJK UNIFIED IDEOGRAPH
+ "8C 5B 5553", #CJK UNIFIED IDEOGRAPH
+ "8C 5C 572D", #CJK UNIFIED IDEOGRAPH
+ "8C 5D 73EA", #CJK UNIFIED IDEOGRAPH
+ "8C 5E 578B", #CJK UNIFIED IDEOGRAPH
+ "8C 5F 5951", #CJK UNIFIED IDEOGRAPH
+ "8C 60 5F62", #CJK UNIFIED IDEOGRAPH
+ "8C 61 5F84", #CJK UNIFIED IDEOGRAPH
+ "8C 62 6075", #CJK UNIFIED IDEOGRAPH
+ "8C 63 6176", #CJK UNIFIED IDEOGRAPH
+ "8C 64 6167", #CJK UNIFIED IDEOGRAPH
+ "8C 65 61A9", #CJK UNIFIED IDEOGRAPH
+ "8C 66 63B2", #CJK UNIFIED IDEOGRAPH
+ "8C 67 643A", #CJK UNIFIED IDEOGRAPH
+ "8C 68 656C", #CJK UNIFIED IDEOGRAPH
+ "8C 69 666F", #CJK UNIFIED IDEOGRAPH
+ "8C 6A 6842", #CJK UNIFIED IDEOGRAPH
+ "8C 6B 6E13", #CJK UNIFIED IDEOGRAPH
+ "8C 6C 7566", #CJK UNIFIED IDEOGRAPH
+ "8C 6D 7A3D", #CJK UNIFIED IDEOGRAPH
+ "8C 6E 7CFB", #CJK UNIFIED IDEOGRAPH
+ "8C 6F 7D4C", #CJK UNIFIED IDEOGRAPH
+ "8C 70 7D99", #CJK UNIFIED IDEOGRAPH
+ "8C 71 7E4B", #CJK UNIFIED IDEOGRAPH
+ "8C 72 7F6B", #CJK UNIFIED IDEOGRAPH
+ "8C 73 830E", #CJK UNIFIED IDEOGRAPH
+ "8C 74 834A", #CJK UNIFIED IDEOGRAPH
+ "8C 75 86CD", #CJK UNIFIED IDEOGRAPH
+ "8C 76 8A08", #CJK UNIFIED IDEOGRAPH
+ "8C 77 8A63", #CJK UNIFIED IDEOGRAPH
+ "8C 78 8B66", #CJK UNIFIED IDEOGRAPH
+ "8C 79 8EFD", #CJK UNIFIED IDEOGRAPH
+ "8C 7A 981A", #CJK UNIFIED IDEOGRAPH
+ "8C 7B 9D8F", #CJK UNIFIED IDEOGRAPH
+ "8C 7C 82B8", #CJK UNIFIED IDEOGRAPH
+ "8C 7D 8FCE", #CJK UNIFIED IDEOGRAPH
+ "8C 7E 9BE8", #CJK UNIFIED IDEOGRAPH
+ "8C 80 5287", #CJK UNIFIED IDEOGRAPH
+ "8C 81 621F", #CJK UNIFIED IDEOGRAPH
+ "8C 82 6483", #CJK UNIFIED IDEOGRAPH
+ "8C 83 6FC0", #CJK UNIFIED IDEOGRAPH
+ "8C 84 9699", #CJK UNIFIED IDEOGRAPH
+ "8C 85 6841", #CJK UNIFIED IDEOGRAPH
+ "8C 86 5091", #CJK UNIFIED IDEOGRAPH
+ "8C 87 6B20", #CJK UNIFIED IDEOGRAPH
+ "8C 88 6C7A", #CJK UNIFIED IDEOGRAPH
+ "8C 89 6F54", #CJK UNIFIED IDEOGRAPH
+ "8C 8A 7A74", #CJK UNIFIED IDEOGRAPH
+ "8C 8B 7D50", #CJK UNIFIED IDEOGRAPH
+ "8C 8C 8840", #CJK UNIFIED IDEOGRAPH
+ "8C 8D 8A23", #CJK UNIFIED IDEOGRAPH
+ "8C 8E 6708", #CJK UNIFIED IDEOGRAPH
+ "8C 8F 4EF6", #CJK UNIFIED IDEOGRAPH
+ "8C 90 5039", #CJK UNIFIED IDEOGRAPH
+ "8C 91 5026", #CJK UNIFIED IDEOGRAPH
+ "8C 92 5065", #CJK UNIFIED IDEOGRAPH
+ "8C 93 517C", #CJK UNIFIED IDEOGRAPH
+ "8C 94 5238", #CJK UNIFIED IDEOGRAPH
+ "8C 95 5263", #CJK UNIFIED IDEOGRAPH
+ "8C 96 55A7", #CJK UNIFIED IDEOGRAPH
+ "8C 97 570F", #CJK UNIFIED IDEOGRAPH
+ "8C 98 5805", #CJK UNIFIED IDEOGRAPH
+ "8C 99 5ACC", #CJK UNIFIED IDEOGRAPH
+ "8C 9A 5EFA", #CJK UNIFIED IDEOGRAPH
+ "8C 9B 61B2", #CJK UNIFIED IDEOGRAPH
+ "8C 9C 61F8", #CJK UNIFIED IDEOGRAPH
+ "8C 9D 62F3", #CJK UNIFIED IDEOGRAPH
+ "8C 9E 6372", #CJK UNIFIED IDEOGRAPH
+ "8C 9F 691C", #CJK UNIFIED IDEOGRAPH
+ "8C A0 6A29", #CJK UNIFIED IDEOGRAPH
+ "8C A1 727D", #CJK UNIFIED IDEOGRAPH
+ "8C A2 72AC", #CJK UNIFIED IDEOGRAPH
+ "8C A3 732E", #CJK UNIFIED IDEOGRAPH
+ "8C A4 7814", #CJK UNIFIED IDEOGRAPH
+ "8C A5 786F", #CJK UNIFIED IDEOGRAPH
+ "8C A6 7D79", #CJK UNIFIED IDEOGRAPH
+ "8C A7 770C", #CJK UNIFIED IDEOGRAPH
+ "8C A8 80A9", #CJK UNIFIED IDEOGRAPH
+ "8C A9 898B", #CJK UNIFIED IDEOGRAPH
+ "8C AA 8B19", #CJK UNIFIED IDEOGRAPH
+ "8C AB 8CE2", #CJK UNIFIED IDEOGRAPH
+ "8C AC 8ED2", #CJK UNIFIED IDEOGRAPH
+ "8C AD 9063", #CJK UNIFIED IDEOGRAPH
+ "8C AE 9375", #CJK UNIFIED IDEOGRAPH
+ "8C AF 967A", #CJK UNIFIED IDEOGRAPH
+ "8C B0 9855", #CJK UNIFIED IDEOGRAPH
+ "8C B1 9A13", #CJK UNIFIED IDEOGRAPH
+ "8C B2 9E78", #CJK UNIFIED IDEOGRAPH
+ "8C B3 5143", #CJK UNIFIED IDEOGRAPH
+ "8C B4 539F", #CJK UNIFIED IDEOGRAPH
+ "8C B5 53B3", #CJK UNIFIED IDEOGRAPH
+ "8C B6 5E7B", #CJK UNIFIED IDEOGRAPH
+ "8C B7 5F26", #CJK UNIFIED IDEOGRAPH
+ "8C B8 6E1B", #CJK UNIFIED IDEOGRAPH
+ "8C B9 6E90", #CJK UNIFIED IDEOGRAPH
+ "8C BA 7384", #CJK UNIFIED IDEOGRAPH
+ "8C BB 73FE", #CJK UNIFIED IDEOGRAPH
+ "8C BC 7D43", #CJK UNIFIED IDEOGRAPH
+ "8C BD 8237", #CJK UNIFIED IDEOGRAPH
+ "8C BE 8A00", #CJK UNIFIED IDEOGRAPH
+ "8C BF 8AFA", #CJK UNIFIED IDEOGRAPH
+ "8C C0 9650", #CJK UNIFIED IDEOGRAPH
+ "8C C1 4E4E", #CJK UNIFIED IDEOGRAPH
+ "8C C2 500B", #CJK UNIFIED IDEOGRAPH
+ "8C C3 53E4", #CJK UNIFIED IDEOGRAPH
+ "8C C4 547C", #CJK UNIFIED IDEOGRAPH
+ "8C C5 56FA", #CJK UNIFIED IDEOGRAPH
+ "8C C6 59D1", #CJK UNIFIED IDEOGRAPH
+ "8C C7 5B64", #CJK UNIFIED IDEOGRAPH
+ "8C C8 5DF1", #CJK UNIFIED IDEOGRAPH
+ "8C C9 5EAB", #CJK UNIFIED IDEOGRAPH
+ "8C CA 5F27", #CJK UNIFIED IDEOGRAPH
+ "8C CB 6238", #CJK UNIFIED IDEOGRAPH
+ "8C CC 6545", #CJK UNIFIED IDEOGRAPH
+ "8C CD 67AF", #CJK UNIFIED IDEOGRAPH
+ "8C CE 6E56", #CJK UNIFIED IDEOGRAPH
+ "8C CF 72D0", #CJK UNIFIED IDEOGRAPH
+ "8C D0 7CCA", #CJK UNIFIED IDEOGRAPH
+ "8C D1 88B4", #CJK UNIFIED IDEOGRAPH
+ "8C D2 80A1", #CJK UNIFIED IDEOGRAPH
+ "8C D3 80E1", #CJK UNIFIED IDEOGRAPH
+ "8C D4 83F0", #CJK UNIFIED IDEOGRAPH
+ "8C D5 864E", #CJK UNIFIED IDEOGRAPH
+ "8C D6 8A87", #CJK UNIFIED IDEOGRAPH
+ "8C D7 8DE8", #CJK UNIFIED IDEOGRAPH
+ "8C D8 9237", #CJK UNIFIED IDEOGRAPH
+ "8C D9 96C7", #CJK UNIFIED IDEOGRAPH
+ "8C DA 9867", #CJK UNIFIED IDEOGRAPH
+ "8C DB 9F13", #CJK UNIFIED IDEOGRAPH
+ "8C DC 4E94", #CJK UNIFIED IDEOGRAPH
+ "8C DD 4E92", #CJK UNIFIED IDEOGRAPH
+ "8C DE 4F0D", #CJK UNIFIED IDEOGRAPH
+ "8C DF 5348", #CJK UNIFIED IDEOGRAPH
+ "8C E0 5449", #CJK UNIFIED IDEOGRAPH
+ "8C E1 543E", #CJK UNIFIED IDEOGRAPH
+ "8C E2 5A2F", #CJK UNIFIED IDEOGRAPH
+ "8C E3 5F8C", #CJK UNIFIED IDEOGRAPH
+ "8C E4 5FA1", #CJK UNIFIED IDEOGRAPH
+ "8C E5 609F", #CJK UNIFIED IDEOGRAPH
+ "8C E6 68A7", #CJK UNIFIED IDEOGRAPH
+ "8C E7 6A8E", #CJK UNIFIED IDEOGRAPH
+ "8C E8 745A", #CJK UNIFIED IDEOGRAPH
+ "8C E9 7881", #CJK UNIFIED IDEOGRAPH
+ "8C EA 8A9E", #CJK UNIFIED IDEOGRAPH
+ "8C EB 8AA4", #CJK UNIFIED IDEOGRAPH
+ "8C EC 8B77", #CJK UNIFIED IDEOGRAPH
+ "8C ED 9190", #CJK UNIFIED IDEOGRAPH
+ "8C EE 4E5E", #CJK UNIFIED IDEOGRAPH
+ "8C EF 9BC9", #CJK UNIFIED IDEOGRAPH
+ "8C F0 4EA4", #CJK UNIFIED IDEOGRAPH
+ "8C F1 4F7C", #CJK UNIFIED IDEOGRAPH
+ "8C F2 4FAF", #CJK UNIFIED IDEOGRAPH
+ "8C F3 5019", #CJK UNIFIED IDEOGRAPH
+ "8C F4 5016", #CJK UNIFIED IDEOGRAPH
+ "8C F5 5149", #CJK UNIFIED IDEOGRAPH
+ "8C F6 516C", #CJK UNIFIED IDEOGRAPH
+ "8C F7 529F", #CJK UNIFIED IDEOGRAPH
+ "8C F8 52B9", #CJK UNIFIED IDEOGRAPH
+ "8C F9 52FE", #CJK UNIFIED IDEOGRAPH
+ "8C FA 539A", #CJK UNIFIED IDEOGRAPH
+ "8C FB 53E3", #CJK UNIFIED IDEOGRAPH
+ "8C FC 5411", #CJK UNIFIED IDEOGRAPH
+ "8D 40 540E", #CJK UNIFIED IDEOGRAPH
+ "8D 41 5589", #CJK UNIFIED IDEOGRAPH
+ "8D 42 5751", #CJK UNIFIED IDEOGRAPH
+ "8D 43 57A2", #CJK UNIFIED IDEOGRAPH
+ "8D 44 597D", #CJK UNIFIED IDEOGRAPH
+ "8D 45 5B54", #CJK UNIFIED IDEOGRAPH
+ "8D 46 5B5D", #CJK UNIFIED IDEOGRAPH
+ "8D 47 5B8F", #CJK UNIFIED IDEOGRAPH
+ "8D 48 5DE5", #CJK UNIFIED IDEOGRAPH
+ "8D 49 5DE7", #CJK UNIFIED IDEOGRAPH
+ "8D 4A 5DF7", #CJK UNIFIED IDEOGRAPH
+ "8D 4B 5E78", #CJK UNIFIED IDEOGRAPH
+ "8D 4C 5E83", #CJK UNIFIED IDEOGRAPH
+ "8D 4D 5E9A", #CJK UNIFIED IDEOGRAPH
+ "8D 4E 5EB7", #CJK UNIFIED IDEOGRAPH
+ "8D 4F 5F18", #CJK UNIFIED IDEOGRAPH
+ "8D 50 6052", #CJK UNIFIED IDEOGRAPH
+ "8D 51 614C", #CJK UNIFIED IDEOGRAPH
+ "8D 52 6297", #CJK UNIFIED IDEOGRAPH
+ "8D 53 62D8", #CJK UNIFIED IDEOGRAPH
+ "8D 54 63A7", #CJK UNIFIED IDEOGRAPH
+ "8D 55 653B", #CJK UNIFIED IDEOGRAPH
+ "8D 56 6602", #CJK UNIFIED IDEOGRAPH
+ "8D 57 6643", #CJK UNIFIED IDEOGRAPH
+ "8D 58 66F4", #CJK UNIFIED IDEOGRAPH
+ "8D 59 676D", #CJK UNIFIED IDEOGRAPH
+ "8D 5A 6821", #CJK UNIFIED IDEOGRAPH
+ "8D 5B 6897", #CJK UNIFIED IDEOGRAPH
+ "8D 5C 69CB", #CJK UNIFIED IDEOGRAPH
+ "8D 5D 6C5F", #CJK UNIFIED IDEOGRAPH
+ "8D 5E 6D2A", #CJK UNIFIED IDEOGRAPH
+ "8D 5F 6D69", #CJK UNIFIED IDEOGRAPH
+ "8D 60 6E2F", #CJK UNIFIED IDEOGRAPH
+ "8D 61 6E9D", #CJK UNIFIED IDEOGRAPH
+ "8D 62 7532", #CJK UNIFIED IDEOGRAPH
+ "8D 63 7687", #CJK UNIFIED IDEOGRAPH
+ "8D 64 786C", #CJK UNIFIED IDEOGRAPH
+ "8D 65 7A3F", #CJK UNIFIED IDEOGRAPH
+ "8D 66 7CE0", #CJK UNIFIED IDEOGRAPH
+ "8D 67 7D05", #CJK UNIFIED IDEOGRAPH
+ "8D 68 7D18", #CJK UNIFIED IDEOGRAPH
+ "8D 69 7D5E", #CJK UNIFIED IDEOGRAPH
+ "8D 6A 7DB1", #CJK UNIFIED IDEOGRAPH
+ "8D 6B 8015", #CJK UNIFIED IDEOGRAPH
+ "8D 6C 8003", #CJK UNIFIED IDEOGRAPH
+ "8D 6D 80AF", #CJK UNIFIED IDEOGRAPH
+ "8D 6E 80B1", #CJK UNIFIED IDEOGRAPH
+ "8D 6F 8154", #CJK UNIFIED IDEOGRAPH
+ "8D 70 818F", #CJK UNIFIED IDEOGRAPH
+ "8D 71 822A", #CJK UNIFIED IDEOGRAPH
+ "8D 72 8352", #CJK UNIFIED IDEOGRAPH
+ "8D 73 884C", #CJK UNIFIED IDEOGRAPH
+ "8D 74 8861", #CJK UNIFIED IDEOGRAPH
+ "8D 75 8B1B", #CJK UNIFIED IDEOGRAPH
+ "8D 76 8CA2", #CJK UNIFIED IDEOGRAPH
+ "8D 77 8CFC", #CJK UNIFIED IDEOGRAPH
+ "8D 78 90CA", #CJK UNIFIED IDEOGRAPH
+ "8D 79 9175", #CJK UNIFIED IDEOGRAPH
+ "8D 7A 9271", #CJK UNIFIED IDEOGRAPH
+ "8D 7B 783F", #CJK UNIFIED IDEOGRAPH
+ "8D 7C 92FC", #CJK UNIFIED IDEOGRAPH
+ "8D 7D 95A4", #CJK UNIFIED IDEOGRAPH
+ "8D 7E 964D", #CJK UNIFIED IDEOGRAPH
+ "8D 80 9805", #CJK UNIFIED IDEOGRAPH
+ "8D 81 9999", #CJK UNIFIED IDEOGRAPH
+ "8D 82 9AD8", #CJK UNIFIED IDEOGRAPH
+ "8D 83 9D3B", #CJK UNIFIED IDEOGRAPH
+ "8D 84 525B", #CJK UNIFIED IDEOGRAPH
+ "8D 85 52AB", #CJK UNIFIED IDEOGRAPH
+ "8D 86 53F7", #CJK UNIFIED IDEOGRAPH
+ "8D 87 5408", #CJK UNIFIED IDEOGRAPH
+ "8D 88 58D5", #CJK UNIFIED IDEOGRAPH
+ "8D 89 62F7", #CJK UNIFIED IDEOGRAPH
+ "8D 8A 6FE0", #CJK UNIFIED IDEOGRAPH
+ "8D 8B 8C6A", #CJK UNIFIED IDEOGRAPH
+ "8D 8C 8F5F", #CJK UNIFIED IDEOGRAPH
+ "8D 8D 9EB9", #CJK UNIFIED IDEOGRAPH
+ "8D 8E 514B", #CJK UNIFIED IDEOGRAPH
+ "8D 8F 523B", #CJK UNIFIED IDEOGRAPH
+ "8D 90 544A", #CJK UNIFIED IDEOGRAPH
+ "8D 91 56FD", #CJK UNIFIED IDEOGRAPH
+ "8D 92 7A40", #CJK UNIFIED IDEOGRAPH
+ "8D 93 9177", #CJK UNIFIED IDEOGRAPH
+ "8D 94 9D60", #CJK UNIFIED IDEOGRAPH
+ "8D 95 9ED2", #CJK UNIFIED IDEOGRAPH
+ "8D 96 7344", #CJK UNIFIED IDEOGRAPH
+ "8D 97 6F09", #CJK UNIFIED IDEOGRAPH
+ "8D 98 8170", #CJK UNIFIED IDEOGRAPH
+ "8D 99 7511", #CJK UNIFIED IDEOGRAPH
+ "8D 9A 5FFD", #CJK UNIFIED IDEOGRAPH
+ "8D 9B 60DA", #CJK UNIFIED IDEOGRAPH
+ "8D 9C 9AA8", #CJK UNIFIED IDEOGRAPH
+ "8D 9D 72DB", #CJK UNIFIED IDEOGRAPH
+ "8D 9E 8FBC", #CJK UNIFIED IDEOGRAPH
+ "8D 9F 6B64", #CJK UNIFIED IDEOGRAPH
+ "8D A0 9803", #CJK UNIFIED IDEOGRAPH
+ "8D A1 4ECA", #CJK UNIFIED IDEOGRAPH
+ "8D A2 56F0", #CJK UNIFIED IDEOGRAPH
+ "8D A3 5764", #CJK UNIFIED IDEOGRAPH
+ "8D A4 58BE", #CJK UNIFIED IDEOGRAPH
+ "8D A5 5A5A", #CJK UNIFIED IDEOGRAPH
+ "8D A6 6068", #CJK UNIFIED IDEOGRAPH
+ "8D A7 61C7", #CJK UNIFIED IDEOGRAPH
+ "8D A8 660F", #CJK UNIFIED IDEOGRAPH
+ "8D A9 6606", #CJK UNIFIED IDEOGRAPH
+ "8D AA 6839", #CJK UNIFIED IDEOGRAPH
+ "8D AB 68B1", #CJK UNIFIED IDEOGRAPH
+ "8D AC 6DF7", #CJK UNIFIED IDEOGRAPH
+ "8D AD 75D5", #CJK UNIFIED IDEOGRAPH
+ "8D AE 7D3A", #CJK UNIFIED IDEOGRAPH
+ "8D AF 826E", #CJK UNIFIED IDEOGRAPH
+ "8D B0 9B42", #CJK UNIFIED IDEOGRAPH
+ "8D B1 4E9B", #CJK UNIFIED IDEOGRAPH
+ "8D B2 4F50", #CJK UNIFIED IDEOGRAPH
+ "8D B3 53C9", #CJK UNIFIED IDEOGRAPH
+ "8D B4 5506", #CJK UNIFIED IDEOGRAPH
+ "8D B5 5D6F", #CJK UNIFIED IDEOGRAPH
+ "8D B6 5DE6", #CJK UNIFIED IDEOGRAPH
+ "8D B7 5DEE", #CJK UNIFIED IDEOGRAPH
+ "8D B8 67FB", #CJK UNIFIED IDEOGRAPH
+ "8D B9 6C99", #CJK UNIFIED IDEOGRAPH
+ "8D BA 7473", #CJK UNIFIED IDEOGRAPH
+ "8D BB 7802", #CJK UNIFIED IDEOGRAPH
+ "8D BC 8A50", #CJK UNIFIED IDEOGRAPH
+ "8D BD 9396", #CJK UNIFIED IDEOGRAPH
+ "8D BE 88DF", #CJK UNIFIED IDEOGRAPH
+ "8D BF 5750", #CJK UNIFIED IDEOGRAPH
+ "8D C0 5EA7", #CJK UNIFIED IDEOGRAPH
+ "8D C1 632B", #CJK UNIFIED IDEOGRAPH
+ "8D C2 50B5", #CJK UNIFIED IDEOGRAPH
+ "8D C3 50AC", #CJK UNIFIED IDEOGRAPH
+ "8D C4 518D", #CJK UNIFIED IDEOGRAPH
+ "8D C5 6700", #CJK UNIFIED IDEOGRAPH
+ "8D C6 54C9", #CJK UNIFIED IDEOGRAPH
+ "8D C7 585E", #CJK UNIFIED IDEOGRAPH
+ "8D C8 59BB", #CJK UNIFIED IDEOGRAPH
+ "8D C9 5BB0", #CJK UNIFIED IDEOGRAPH
+ "8D CA 5F69", #CJK UNIFIED IDEOGRAPH
+ "8D CB 624D", #CJK UNIFIED IDEOGRAPH
+ "8D CC 63A1", #CJK UNIFIED IDEOGRAPH
+ "8D CD 683D", #CJK UNIFIED IDEOGRAPH
+ "8D CE 6B73", #CJK UNIFIED IDEOGRAPH
+ "8D CF 6E08", #CJK UNIFIED IDEOGRAPH
+ "8D D0 707D", #CJK UNIFIED IDEOGRAPH
+ "8D D1 91C7", #CJK UNIFIED IDEOGRAPH
+ "8D D2 7280", #CJK UNIFIED IDEOGRAPH
+ "8D D3 7815", #CJK UNIFIED IDEOGRAPH
+ "8D D4 7826", #CJK UNIFIED IDEOGRAPH
+ "8D D5 796D", #CJK UNIFIED IDEOGRAPH
+ "8D D6 658E", #CJK UNIFIED IDEOGRAPH
+ "8D D7 7D30", #CJK UNIFIED IDEOGRAPH
+ "8D D8 83DC", #CJK UNIFIED IDEOGRAPH
+ "8D D9 88C1", #CJK UNIFIED IDEOGRAPH
+ "8D DA 8F09", #CJK UNIFIED IDEOGRAPH
+ "8D DB 969B", #CJK UNIFIED IDEOGRAPH
+ "8D DC 5264", #CJK UNIFIED IDEOGRAPH
+ "8D DD 5728", #CJK UNIFIED IDEOGRAPH
+ "8D DE 6750", #CJK UNIFIED IDEOGRAPH
+ "8D DF 7F6A", #CJK UNIFIED IDEOGRAPH
+ "8D E0 8CA1", #CJK UNIFIED IDEOGRAPH
+ "8D E1 51B4", #CJK UNIFIED IDEOGRAPH
+ "8D E2 5742", #CJK UNIFIED IDEOGRAPH
+ "8D E3 962A", #CJK UNIFIED IDEOGRAPH
+ "8D E4 583A", #CJK UNIFIED IDEOGRAPH
+ "8D E5 698A", #CJK UNIFIED IDEOGRAPH
+ "8D E6 80B4", #CJK UNIFIED IDEOGRAPH
+ "8D E7 54B2", #CJK UNIFIED IDEOGRAPH
+ "8D E8 5D0E", #CJK UNIFIED IDEOGRAPH
+ "8D E9 57FC", #CJK UNIFIED IDEOGRAPH
+ "8D EA 7895", #CJK UNIFIED IDEOGRAPH
+ "8D EB 9DFA", #CJK UNIFIED IDEOGRAPH
+ "8D EC 4F5C", #CJK UNIFIED IDEOGRAPH
+ "8D ED 524A", #CJK UNIFIED IDEOGRAPH
+ "8D EE 548B", #CJK UNIFIED IDEOGRAPH
+ "8D EF 643E", #CJK UNIFIED IDEOGRAPH
+ "8D F0 6628", #CJK UNIFIED IDEOGRAPH
+ "8D F1 6714", #CJK UNIFIED IDEOGRAPH
+ "8D F2 67F5", #CJK UNIFIED IDEOGRAPH
+ "8D F3 7A84", #CJK UNIFIED IDEOGRAPH
+ "8D F4 7B56", #CJK UNIFIED IDEOGRAPH
+ "8D F5 7D22", #CJK UNIFIED IDEOGRAPH
+ "8D F6 932F", #CJK UNIFIED IDEOGRAPH
+ "8D F7 685C", #CJK UNIFIED IDEOGRAPH
+ "8D F8 9BAD", #CJK UNIFIED IDEOGRAPH
+ "8D F9 7B39", #CJK UNIFIED IDEOGRAPH
+ "8D FA 5319", #CJK UNIFIED IDEOGRAPH
+ "8D FB 518A", #CJK UNIFIED IDEOGRAPH
+ "8D FC 5237", #CJK UNIFIED IDEOGRAPH
+ "8E 40 5BDF", #CJK UNIFIED IDEOGRAPH
+ "8E 41 62F6", #CJK UNIFIED IDEOGRAPH
+ "8E 42 64AE", #CJK UNIFIED IDEOGRAPH
+ "8E 43 64E6", #CJK UNIFIED IDEOGRAPH
+ "8E 44 672D", #CJK UNIFIED IDEOGRAPH
+ "8E 45 6BBA", #CJK UNIFIED IDEOGRAPH
+ "8E 46 85A9", #CJK UNIFIED IDEOGRAPH
+ "8E 47 96D1", #CJK UNIFIED IDEOGRAPH
+ "8E 48 7690", #CJK UNIFIED IDEOGRAPH
+ "8E 49 9BD6", #CJK UNIFIED IDEOGRAPH
+ "8E 4A 634C", #CJK UNIFIED IDEOGRAPH
+ "8E 4B 9306", #CJK UNIFIED IDEOGRAPH
+ "8E 4C 9BAB", #CJK UNIFIED IDEOGRAPH
+ "8E 4D 76BF", #CJK UNIFIED IDEOGRAPH
+ "8E 4E 6652", #CJK UNIFIED IDEOGRAPH
+ "8E 4F 4E09", #CJK UNIFIED IDEOGRAPH
+ "8E 50 5098", #CJK UNIFIED IDEOGRAPH
+ "8E 51 53C2", #CJK UNIFIED IDEOGRAPH
+ "8E 52 5C71", #CJK UNIFIED IDEOGRAPH
+ "8E 53 60E8", #CJK UNIFIED IDEOGRAPH
+ "8E 54 6492", #CJK UNIFIED IDEOGRAPH
+ "8E 55 6563", #CJK UNIFIED IDEOGRAPH
+ "8E 56 685F", #CJK UNIFIED IDEOGRAPH
+ "8E 57 71E6", #CJK UNIFIED IDEOGRAPH
+ "8E 58 73CA", #CJK UNIFIED IDEOGRAPH
+ "8E 59 7523", #CJK UNIFIED IDEOGRAPH
+ "8E 5A 7B97", #CJK UNIFIED IDEOGRAPH
+ "8E 5B 7E82", #CJK UNIFIED IDEOGRAPH
+ "8E 5C 8695", #CJK UNIFIED IDEOGRAPH
+ "8E 5D 8B83", #CJK UNIFIED IDEOGRAPH
+ "8E 5E 8CDB", #CJK UNIFIED IDEOGRAPH
+ "8E 5F 9178", #CJK UNIFIED IDEOGRAPH
+ "8E 60 9910", #CJK UNIFIED IDEOGRAPH
+ "8E 61 65AC", #CJK UNIFIED IDEOGRAPH
+ "8E 62 66AB", #CJK UNIFIED IDEOGRAPH
+ "8E 63 6B8B", #CJK UNIFIED IDEOGRAPH
+ "8E 64 4ED5", #CJK UNIFIED IDEOGRAPH
+ "8E 65 4ED4", #CJK UNIFIED IDEOGRAPH
+ "8E 66 4F3A", #CJK UNIFIED IDEOGRAPH
+ "8E 67 4F7F", #CJK UNIFIED IDEOGRAPH
+ "8E 68 523A", #CJK UNIFIED IDEOGRAPH
+ "8E 69 53F8", #CJK UNIFIED IDEOGRAPH
+ "8E 6A 53F2", #CJK UNIFIED IDEOGRAPH
+ "8E 6B 55E3", #CJK UNIFIED IDEOGRAPH
+ "8E 6C 56DB", #CJK UNIFIED IDEOGRAPH
+ "8E 6D 58EB", #CJK UNIFIED IDEOGRAPH
+ "8E 6E 59CB", #CJK UNIFIED IDEOGRAPH
+ "8E 6F 59C9", #CJK UNIFIED IDEOGRAPH
+ "8E 70 59FF", #CJK UNIFIED IDEOGRAPH
+ "8E 71 5B50", #CJK UNIFIED IDEOGRAPH
+ "8E 72 5C4D", #CJK UNIFIED IDEOGRAPH
+ "8E 73 5E02", #CJK UNIFIED IDEOGRAPH
+ "8E 74 5E2B", #CJK UNIFIED IDEOGRAPH
+ "8E 75 5FD7", #CJK UNIFIED IDEOGRAPH
+ "8E 76 601D", #CJK UNIFIED IDEOGRAPH
+ "8E 77 6307", #CJK UNIFIED IDEOGRAPH
+ "8E 78 652F", #CJK UNIFIED IDEOGRAPH
+ "8E 79 5B5C", #CJK UNIFIED IDEOGRAPH
+ "8E 7A 65AF", #CJK UNIFIED IDEOGRAPH
+ "8E 7B 65BD", #CJK UNIFIED IDEOGRAPH
+ "8E 7C 65E8", #CJK UNIFIED IDEOGRAPH
+ "8E 7D 679D", #CJK UNIFIED IDEOGRAPH
+ "8E 7E 6B62", #CJK UNIFIED IDEOGRAPH
+ "8E 80 6B7B", #CJK UNIFIED IDEOGRAPH
+ "8E 81 6C0F", #CJK UNIFIED IDEOGRAPH
+ "8E 82 7345", #CJK UNIFIED IDEOGRAPH
+ "8E 83 7949", #CJK UNIFIED IDEOGRAPH
+ "8E 84 79C1", #CJK UNIFIED IDEOGRAPH
+ "8E 85 7CF8", #CJK UNIFIED IDEOGRAPH
+ "8E 86 7D19", #CJK UNIFIED IDEOGRAPH
+ "8E 87 7D2B", #CJK UNIFIED IDEOGRAPH
+ "8E 88 80A2", #CJK UNIFIED IDEOGRAPH
+ "8E 89 8102", #CJK UNIFIED IDEOGRAPH
+ "8E 8A 81F3", #CJK UNIFIED IDEOGRAPH
+ "8E 8B 8996", #CJK UNIFIED IDEOGRAPH
+ "8E 8C 8A5E", #CJK UNIFIED IDEOGRAPH
+ "8E 8D 8A69", #CJK UNIFIED IDEOGRAPH
+ "8E 8E 8A66", #CJK UNIFIED IDEOGRAPH
+ "8E 8F 8A8C", #CJK UNIFIED IDEOGRAPH
+ "8E 90 8AEE", #CJK UNIFIED IDEOGRAPH
+ "8E 91 8CC7", #CJK UNIFIED IDEOGRAPH
+ "8E 92 8CDC", #CJK UNIFIED IDEOGRAPH
+ "8E 93 96CC", #CJK UNIFIED IDEOGRAPH
+ "8E 94 98FC", #CJK UNIFIED IDEOGRAPH
+ "8E 95 6B6F", #CJK UNIFIED IDEOGRAPH
+ "8E 96 4E8B", #CJK UNIFIED IDEOGRAPH
+ "8E 97 4F3C", #CJK UNIFIED IDEOGRAPH
+ "8E 98 4F8D", #CJK UNIFIED IDEOGRAPH
+ "8E 99 5150", #CJK UNIFIED IDEOGRAPH
+ "8E 9A 5B57", #CJK UNIFIED IDEOGRAPH
+ "8E 9B 5BFA", #CJK UNIFIED IDEOGRAPH
+ "8E 9C 6148", #CJK UNIFIED IDEOGRAPH
+ "8E 9D 6301", #CJK UNIFIED IDEOGRAPH
+ "8E 9E 6642", #CJK UNIFIED IDEOGRAPH
+ "8E 9F 6B21", #CJK UNIFIED IDEOGRAPH
+ "8E A0 6ECB", #CJK UNIFIED IDEOGRAPH
+ "8E A1 6CBB", #CJK UNIFIED IDEOGRAPH
+ "8E A2 723E", #CJK UNIFIED IDEOGRAPH
+ "8E A3 74BD", #CJK UNIFIED IDEOGRAPH
+ "8E A4 75D4", #CJK UNIFIED IDEOGRAPH
+ "8E A5 78C1", #CJK UNIFIED IDEOGRAPH
+ "8E A6 793A", #CJK UNIFIED IDEOGRAPH
+ "8E A7 800C", #CJK UNIFIED IDEOGRAPH
+ "8E A8 8033", #CJK UNIFIED IDEOGRAPH
+ "8E A9 81EA", #CJK UNIFIED IDEOGRAPH
+ "8E AA 8494", #CJK UNIFIED IDEOGRAPH
+ "8E AB 8F9E", #CJK UNIFIED IDEOGRAPH
+ "8E AC 6C50", #CJK UNIFIED IDEOGRAPH
+ "8E AD 9E7F", #CJK UNIFIED IDEOGRAPH
+ "8E AE 5F0F", #CJK UNIFIED IDEOGRAPH
+ "8E AF 8B58", #CJK UNIFIED IDEOGRAPH
+ "8E B0 9D2B", #CJK UNIFIED IDEOGRAPH
+ "8E B1 7AFA", #CJK UNIFIED IDEOGRAPH
+ "8E B2 8EF8", #CJK UNIFIED IDEOGRAPH
+ "8E B3 5B8D", #CJK UNIFIED IDEOGRAPH
+ "8E B4 96EB", #CJK UNIFIED IDEOGRAPH
+ "8E B5 4E03", #CJK UNIFIED IDEOGRAPH
+ "8E B6 53F1", #CJK UNIFIED IDEOGRAPH
+ "8E B7 57F7", #CJK UNIFIED IDEOGRAPH
+ "8E B8 5931", #CJK UNIFIED IDEOGRAPH
+ "8E B9 5AC9", #CJK UNIFIED IDEOGRAPH
+ "8E BA 5BA4", #CJK UNIFIED IDEOGRAPH
+ "8E BB 6089", #CJK UNIFIED IDEOGRAPH
+ "8E BC 6E7F", #CJK UNIFIED IDEOGRAPH
+ "8E BD 6F06", #CJK UNIFIED IDEOGRAPH
+ "8E BE 75BE", #CJK UNIFIED IDEOGRAPH
+ "8E BF 8CEA", #CJK UNIFIED IDEOGRAPH
+ "8E C0 5B9F", #CJK UNIFIED IDEOGRAPH
+ "8E C1 8500", #CJK UNIFIED IDEOGRAPH
+ "8E C2 7BE0", #CJK UNIFIED IDEOGRAPH
+ "8E C3 5072", #CJK UNIFIED IDEOGRAPH
+ "8E C4 67F4", #CJK UNIFIED IDEOGRAPH
+ "8E C5 829D", #CJK UNIFIED IDEOGRAPH
+ "8E C6 5C61", #CJK UNIFIED IDEOGRAPH
+ "8E C7 854A", #CJK UNIFIED IDEOGRAPH
+ "8E C8 7E1E", #CJK UNIFIED IDEOGRAPH
+ "8E C9 820E", #CJK UNIFIED IDEOGRAPH
+ "8E CA 5199", #CJK UNIFIED IDEOGRAPH
+ "8E CB 5C04", #CJK UNIFIED IDEOGRAPH
+ "8E CC 6368", #CJK UNIFIED IDEOGRAPH
+ "8E CD 8D66", #CJK UNIFIED IDEOGRAPH
+ "8E CE 659C", #CJK UNIFIED IDEOGRAPH
+ "8E CF 716E", #CJK UNIFIED IDEOGRAPH
+ "8E D0 793E", #CJK UNIFIED IDEOGRAPH
+ "8E D1 7D17", #CJK UNIFIED IDEOGRAPH
+ "8E D2 8005", #CJK UNIFIED IDEOGRAPH
+ "8E D3 8B1D", #CJK UNIFIED IDEOGRAPH
+ "8E D4 8ECA", #CJK UNIFIED IDEOGRAPH
+ "8E D5 906E", #CJK UNIFIED IDEOGRAPH
+ "8E D6 86C7", #CJK UNIFIED IDEOGRAPH
+ "8E D7 90AA", #CJK UNIFIED IDEOGRAPH
+ "8E D8 501F", #CJK UNIFIED IDEOGRAPH
+ "8E D9 52FA", #CJK UNIFIED IDEOGRAPH
+ "8E DA 5C3A", #CJK UNIFIED IDEOGRAPH
+ "8E DB 6753", #CJK UNIFIED IDEOGRAPH
+ "8E DC 707C", #CJK UNIFIED IDEOGRAPH
+ "8E DD 7235", #CJK UNIFIED IDEOGRAPH
+ "8E DE 914C", #CJK UNIFIED IDEOGRAPH
+ "8E DF 91C8", #CJK UNIFIED IDEOGRAPH
+ "8E E0 932B", #CJK UNIFIED IDEOGRAPH
+ "8E E1 82E5", #CJK UNIFIED IDEOGRAPH
+ "8E E2 5BC2", #CJK UNIFIED IDEOGRAPH
+ "8E E3 5F31", #CJK UNIFIED IDEOGRAPH
+ "8E E4 60F9", #CJK UNIFIED IDEOGRAPH
+ "8E E5 4E3B", #CJK UNIFIED IDEOGRAPH
+ "8E E6 53D6", #CJK UNIFIED IDEOGRAPH
+ "8E E7 5B88", #CJK UNIFIED IDEOGRAPH
+ "8E E8 624B", #CJK UNIFIED IDEOGRAPH
+ "8E E9 6731", #CJK UNIFIED IDEOGRAPH
+ "8E EA 6B8A", #CJK UNIFIED IDEOGRAPH
+ "8E EB 72E9", #CJK UNIFIED IDEOGRAPH
+ "8E EC 73E0", #CJK UNIFIED IDEOGRAPH
+ "8E ED 7A2E", #CJK UNIFIED IDEOGRAPH
+ "8E EE 816B", #CJK UNIFIED IDEOGRAPH
+ "8E EF 8DA3", #CJK UNIFIED IDEOGRAPH
+ "8E F0 9152", #CJK UNIFIED IDEOGRAPH
+ "8E F1 9996", #CJK UNIFIED IDEOGRAPH
+ "8E F2 5112", #CJK UNIFIED IDEOGRAPH
+ "8E F3 53D7", #CJK UNIFIED IDEOGRAPH
+ "8E F4 546A", #CJK UNIFIED IDEOGRAPH
+ "8E F5 5BFF", #CJK UNIFIED IDEOGRAPH
+ "8E F6 6388", #CJK UNIFIED IDEOGRAPH
+ "8E F7 6A39", #CJK UNIFIED IDEOGRAPH
+ "8E F8 7DAC", #CJK UNIFIED IDEOGRAPH
+ "8E F9 9700", #CJK UNIFIED IDEOGRAPH
+ "8E FA 56DA", #CJK UNIFIED IDEOGRAPH
+ "8E FB 53CE", #CJK UNIFIED IDEOGRAPH
+ "8E FC 5468", #CJK UNIFIED IDEOGRAPH
+ "8F 40 5B97", #CJK UNIFIED IDEOGRAPH
+ "8F 41 5C31", #CJK UNIFIED IDEOGRAPH
+ "8F 42 5DDE", #CJK UNIFIED IDEOGRAPH
+ "8F 43 4FEE", #CJK UNIFIED IDEOGRAPH
+ "8F 44 6101", #CJK UNIFIED IDEOGRAPH
+ "8F 45 62FE", #CJK UNIFIED IDEOGRAPH
+ "8F 46 6D32", #CJK UNIFIED IDEOGRAPH
+ "8F 47 79C0", #CJK UNIFIED IDEOGRAPH
+ "8F 48 79CB", #CJK UNIFIED IDEOGRAPH
+ "8F 49 7D42", #CJK UNIFIED IDEOGRAPH
+ "8F 4A 7E4D", #CJK UNIFIED IDEOGRAPH
+ "8F 4B 7FD2", #CJK UNIFIED IDEOGRAPH
+ "8F 4C 81ED", #CJK UNIFIED IDEOGRAPH
+ "8F 4D 821F", #CJK UNIFIED IDEOGRAPH
+ "8F 4E 8490", #CJK UNIFIED IDEOGRAPH
+ "8F 4F 8846", #CJK UNIFIED IDEOGRAPH
+ "8F 50 8972", #CJK UNIFIED IDEOGRAPH
+ "8F 51 8B90", #CJK UNIFIED IDEOGRAPH
+ "8F 52 8E74", #CJK UNIFIED IDEOGRAPH
+ "8F 53 8F2F", #CJK UNIFIED IDEOGRAPH
+ "8F 54 9031", #CJK UNIFIED IDEOGRAPH
+ "8F 55 914B", #CJK UNIFIED IDEOGRAPH
+ "8F 56 916C", #CJK UNIFIED IDEOGRAPH
+ "8F 57 96C6", #CJK UNIFIED IDEOGRAPH
+ "8F 58 919C", #CJK UNIFIED IDEOGRAPH
+ "8F 59 4EC0", #CJK UNIFIED IDEOGRAPH
+ "8F 5A 4F4F", #CJK UNIFIED IDEOGRAPH
+ "8F 5B 5145", #CJK UNIFIED IDEOGRAPH
+ "8F 5C 5341", #CJK UNIFIED IDEOGRAPH
+ "8F 5D 5F93", #CJK UNIFIED IDEOGRAPH
+ "8F 5E 620E", #CJK UNIFIED IDEOGRAPH
+ "8F 5F 67D4", #CJK UNIFIED IDEOGRAPH
+ "8F 60 6C41", #CJK UNIFIED IDEOGRAPH
+ "8F 61 6E0B", #CJK UNIFIED IDEOGRAPH
+ "8F 62 7363", #CJK UNIFIED IDEOGRAPH
+ "8F 63 7E26", #CJK UNIFIED IDEOGRAPH
+ "8F 64 91CD", #CJK UNIFIED IDEOGRAPH
+ "8F 65 9283", #CJK UNIFIED IDEOGRAPH
+ "8F 66 53D4", #CJK UNIFIED IDEOGRAPH
+ "8F 67 5919", #CJK UNIFIED IDEOGRAPH
+ "8F 68 5BBF", #CJK UNIFIED IDEOGRAPH
+ "8F 69 6DD1", #CJK UNIFIED IDEOGRAPH
+ "8F 6A 795D", #CJK UNIFIED IDEOGRAPH
+ "8F 6B 7E2E", #CJK UNIFIED IDEOGRAPH
+ "8F 6C 7C9B", #CJK UNIFIED IDEOGRAPH
+ "8F 6D 587E", #CJK UNIFIED IDEOGRAPH
+ "8F 6E 719F", #CJK UNIFIED IDEOGRAPH
+ "8F 6F 51FA", #CJK UNIFIED IDEOGRAPH
+ "8F 70 8853", #CJK UNIFIED IDEOGRAPH
+ "8F 71 8FF0", #CJK UNIFIED IDEOGRAPH
+ "8F 72 4FCA", #CJK UNIFIED IDEOGRAPH
+ "8F 73 5CFB", #CJK UNIFIED IDEOGRAPH
+ "8F 74 6625", #CJK UNIFIED IDEOGRAPH
+ "8F 75 77AC", #CJK UNIFIED IDEOGRAPH
+ "8F 76 7AE3", #CJK UNIFIED IDEOGRAPH
+ "8F 77 821C", #CJK UNIFIED IDEOGRAPH
+ "8F 78 99FF", #CJK UNIFIED IDEOGRAPH
+ "8F 79 51C6", #CJK UNIFIED IDEOGRAPH
+ "8F 7A 5FAA", #CJK UNIFIED IDEOGRAPH
+ "8F 7B 65EC", #CJK UNIFIED IDEOGRAPH
+ "8F 7C 696F", #CJK UNIFIED IDEOGRAPH
+ "8F 7D 6B89", #CJK UNIFIED IDEOGRAPH
+ "8F 7E 6DF3", #CJK UNIFIED IDEOGRAPH
+ "8F 80 6E96", #CJK UNIFIED IDEOGRAPH
+ "8F 81 6F64", #CJK UNIFIED IDEOGRAPH
+ "8F 82 76FE", #CJK UNIFIED IDEOGRAPH
+ "8F 83 7D14", #CJK UNIFIED IDEOGRAPH
+ "8F 84 5DE1", #CJK UNIFIED IDEOGRAPH
+ "8F 85 9075", #CJK UNIFIED IDEOGRAPH
+ "8F 86 9187", #CJK UNIFIED IDEOGRAPH
+ "8F 87 9806", #CJK UNIFIED IDEOGRAPH
+ "8F 88 51E6", #CJK UNIFIED IDEOGRAPH
+ "8F 89 521D", #CJK UNIFIED IDEOGRAPH
+ "8F 8A 6240", #CJK UNIFIED IDEOGRAPH
+ "8F 8B 6691", #CJK UNIFIED IDEOGRAPH
+ "8F 8C 66D9", #CJK UNIFIED IDEOGRAPH
+ "8F 8D 6E1A", #CJK UNIFIED IDEOGRAPH
+ "8F 8E 5EB6", #CJK UNIFIED IDEOGRAPH
+ "8F 8F 7DD2", #CJK UNIFIED IDEOGRAPH
+ "8F 90 7F72", #CJK UNIFIED IDEOGRAPH
+ "8F 91 66F8", #CJK UNIFIED IDEOGRAPH
+ "8F 92 85AF", #CJK UNIFIED IDEOGRAPH
+ "8F 93 85F7", #CJK UNIFIED IDEOGRAPH
+ "8F 94 8AF8", #CJK UNIFIED IDEOGRAPH
+ "8F 95 52A9", #CJK UNIFIED IDEOGRAPH
+ "8F 96 53D9", #CJK UNIFIED IDEOGRAPH
+ "8F 97 5973", #CJK UNIFIED IDEOGRAPH
+ "8F 98 5E8F", #CJK UNIFIED IDEOGRAPH
+ "8F 99 5F90", #CJK UNIFIED IDEOGRAPH
+ "8F 9A 6055", #CJK UNIFIED IDEOGRAPH
+ "8F 9B 92E4", #CJK UNIFIED IDEOGRAPH
+ "8F 9C 9664", #CJK UNIFIED IDEOGRAPH
+ "8F 9D 50B7", #CJK UNIFIED IDEOGRAPH
+ "8F 9E 511F", #CJK UNIFIED IDEOGRAPH
+ "8F 9F 52DD", #CJK UNIFIED IDEOGRAPH
+ "8F A0 5320", #CJK UNIFIED IDEOGRAPH
+ "8F A1 5347", #CJK UNIFIED IDEOGRAPH
+ "8F A2 53EC", #CJK UNIFIED IDEOGRAPH
+ "8F A3 54E8", #CJK UNIFIED IDEOGRAPH
+ "8F A4 5546", #CJK UNIFIED IDEOGRAPH
+ "8F A5 5531", #CJK UNIFIED IDEOGRAPH
+ "8F A6 5617", #CJK UNIFIED IDEOGRAPH
+ "8F A7 5968", #CJK UNIFIED IDEOGRAPH
+ "8F A8 59BE", #CJK UNIFIED IDEOGRAPH
+ "8F A9 5A3C", #CJK UNIFIED IDEOGRAPH
+ "8F AA 5BB5", #CJK UNIFIED IDEOGRAPH
+ "8F AB 5C06", #CJK UNIFIED IDEOGRAPH
+ "8F AC 5C0F", #CJK UNIFIED IDEOGRAPH
+ "8F AD 5C11", #CJK UNIFIED IDEOGRAPH
+ "8F AE 5C1A", #CJK UNIFIED IDEOGRAPH
+ "8F AF 5E84", #CJK UNIFIED IDEOGRAPH
+ "8F B0 5E8A", #CJK UNIFIED IDEOGRAPH
+ "8F B1 5EE0", #CJK UNIFIED IDEOGRAPH
+ "8F B2 5F70", #CJK UNIFIED IDEOGRAPH
+ "8F B3 627F", #CJK UNIFIED IDEOGRAPH
+ "8F B4 6284", #CJK UNIFIED IDEOGRAPH
+ "8F B5 62DB", #CJK UNIFIED IDEOGRAPH
+ "8F B6 638C", #CJK UNIFIED IDEOGRAPH
+ "8F B7 6377", #CJK UNIFIED IDEOGRAPH
+ "8F B8 6607", #CJK UNIFIED IDEOGRAPH
+ "8F B9 660C", #CJK UNIFIED IDEOGRAPH
+ "8F BA 662D", #CJK UNIFIED IDEOGRAPH
+ "8F BB 6676", #CJK UNIFIED IDEOGRAPH
+ "8F BC 677E", #CJK UNIFIED IDEOGRAPH
+ "8F BD 68A2", #CJK UNIFIED IDEOGRAPH
+ "8F BE 6A1F", #CJK UNIFIED IDEOGRAPH
+ "8F BF 6A35", #CJK UNIFIED IDEOGRAPH
+ "8F C0 6CBC", #CJK UNIFIED IDEOGRAPH
+ "8F C1 6D88", #CJK UNIFIED IDEOGRAPH
+ "8F C2 6E09", #CJK UNIFIED IDEOGRAPH
+ "8F C3 6E58", #CJK UNIFIED IDEOGRAPH
+ "8F C4 713C", #CJK UNIFIED IDEOGRAPH
+ "8F C5 7126", #CJK UNIFIED IDEOGRAPH
+ "8F C6 7167", #CJK UNIFIED IDEOGRAPH
+ "8F C7 75C7", #CJK UNIFIED IDEOGRAPH
+ "8F C8 7701", #CJK UNIFIED IDEOGRAPH
+ "8F C9 785D", #CJK UNIFIED IDEOGRAPH
+ "8F CA 7901", #CJK UNIFIED IDEOGRAPH
+ "8F CB 7965", #CJK UNIFIED IDEOGRAPH
+ "8F CC 79F0", #CJK UNIFIED IDEOGRAPH
+ "8F CD 7AE0", #CJK UNIFIED IDEOGRAPH
+ "8F CE 7B11", #CJK UNIFIED IDEOGRAPH
+ "8F CF 7CA7", #CJK UNIFIED IDEOGRAPH
+ "8F D0 7D39", #CJK UNIFIED IDEOGRAPH
+ "8F D1 8096", #CJK UNIFIED IDEOGRAPH
+ "8F D2 83D6", #CJK UNIFIED IDEOGRAPH
+ "8F D3 848B", #CJK UNIFIED IDEOGRAPH
+ "8F D4 8549", #CJK UNIFIED IDEOGRAPH
+ "8F D5 885D", #CJK UNIFIED IDEOGRAPH
+ "8F D6 88F3", #CJK UNIFIED IDEOGRAPH
+ "8F D7 8A1F", #CJK UNIFIED IDEOGRAPH
+ "8F D8 8A3C", #CJK UNIFIED IDEOGRAPH
+ "8F D9 8A54", #CJK UNIFIED IDEOGRAPH
+ "8F DA 8A73", #CJK UNIFIED IDEOGRAPH
+ "8F DB 8C61", #CJK UNIFIED IDEOGRAPH
+ "8F DC 8CDE", #CJK UNIFIED IDEOGRAPH
+ "8F DD 91A4", #CJK UNIFIED IDEOGRAPH
+ "8F DE 9266", #CJK UNIFIED IDEOGRAPH
+ "8F DF 937E", #CJK UNIFIED IDEOGRAPH
+ "8F E0 9418", #CJK UNIFIED IDEOGRAPH
+ "8F E1 969C", #CJK UNIFIED IDEOGRAPH
+ "8F E2 9798", #CJK UNIFIED IDEOGRAPH
+ "8F E3 4E0A", #CJK UNIFIED IDEOGRAPH
+ "8F E4 4E08", #CJK UNIFIED IDEOGRAPH
+ "8F E5 4E1E", #CJK UNIFIED IDEOGRAPH
+ "8F E6 4E57", #CJK UNIFIED IDEOGRAPH
+ "8F E7 5197", #CJK UNIFIED IDEOGRAPH
+ "8F E8 5270", #CJK UNIFIED IDEOGRAPH
+ "8F E9 57CE", #CJK UNIFIED IDEOGRAPH
+ "8F EA 5834", #CJK UNIFIED IDEOGRAPH
+ "8F EB 58CC", #CJK UNIFIED IDEOGRAPH
+ "8F EC 5B22", #CJK UNIFIED IDEOGRAPH
+ "8F ED 5E38", #CJK UNIFIED IDEOGRAPH
+ "8F EE 60C5", #CJK UNIFIED IDEOGRAPH
+ "8F EF 64FE", #CJK UNIFIED IDEOGRAPH
+ "8F F0 6761", #CJK UNIFIED IDEOGRAPH
+ "8F F1 6756", #CJK UNIFIED IDEOGRAPH
+ "8F F2 6D44", #CJK UNIFIED IDEOGRAPH
+ "8F F3 72B6", #CJK UNIFIED IDEOGRAPH
+ "8F F4 7573", #CJK UNIFIED IDEOGRAPH
+ "8F F5 7A63", #CJK UNIFIED IDEOGRAPH
+ "8F F6 84B8", #CJK UNIFIED IDEOGRAPH
+ "8F F7 8B72", #CJK UNIFIED IDEOGRAPH
+ "8F F8 91B8", #CJK UNIFIED IDEOGRAPH
+ "8F F9 9320", #CJK UNIFIED IDEOGRAPH
+ "8F FA 5631", #CJK UNIFIED IDEOGRAPH
+ "8F FB 57F4", #CJK UNIFIED IDEOGRAPH
+ "8F FC 98FE", #CJK UNIFIED IDEOGRAPH
+ "90 40 62ED", #CJK UNIFIED IDEOGRAPH
+ "90 41 690D", #CJK UNIFIED IDEOGRAPH
+ "90 42 6B96", #CJK UNIFIED IDEOGRAPH
+ "90 43 71ED", #CJK UNIFIED IDEOGRAPH
+ "90 44 7E54", #CJK UNIFIED IDEOGRAPH
+ "90 45 8077", #CJK UNIFIED IDEOGRAPH
+ "90 46 8272", #CJK UNIFIED IDEOGRAPH
+ "90 47 89E6", #CJK UNIFIED IDEOGRAPH
+ "90 48 98DF", #CJK UNIFIED IDEOGRAPH
+ "90 49 8755", #CJK UNIFIED IDEOGRAPH
+ "90 4A 8FB1", #CJK UNIFIED IDEOGRAPH
+ "90 4B 5C3B", #CJK UNIFIED IDEOGRAPH
+ "90 4C 4F38", #CJK UNIFIED IDEOGRAPH
+ "90 4D 4FE1", #CJK UNIFIED IDEOGRAPH
+ "90 4E 4FB5", #CJK UNIFIED IDEOGRAPH
+ "90 4F 5507", #CJK UNIFIED IDEOGRAPH
+ "90 50 5A20", #CJK UNIFIED IDEOGRAPH
+ "90 51 5BDD", #CJK UNIFIED IDEOGRAPH
+ "90 52 5BE9", #CJK UNIFIED IDEOGRAPH
+ "90 53 5FC3", #CJK UNIFIED IDEOGRAPH
+ "90 54 614E", #CJK UNIFIED IDEOGRAPH
+ "90 55 632F", #CJK UNIFIED IDEOGRAPH
+ "90 56 65B0", #CJK UNIFIED IDEOGRAPH
+ "90 57 664B", #CJK UNIFIED IDEOGRAPH
+ "90 58 68EE", #CJK UNIFIED IDEOGRAPH
+ "90 59 699B", #CJK UNIFIED IDEOGRAPH
+ "90 5A 6D78", #CJK UNIFIED IDEOGRAPH
+ "90 5B 6DF1", #CJK UNIFIED IDEOGRAPH
+ "90 5C 7533", #CJK UNIFIED IDEOGRAPH
+ "90 5D 75B9", #CJK UNIFIED IDEOGRAPH
+ "90 5E 771F", #CJK UNIFIED IDEOGRAPH
+ "90 5F 795E", #CJK UNIFIED IDEOGRAPH
+ "90 60 79E6", #CJK UNIFIED IDEOGRAPH
+ "90 61 7D33", #CJK UNIFIED IDEOGRAPH
+ "90 62 81E3", #CJK UNIFIED IDEOGRAPH
+ "90 63 82AF", #CJK UNIFIED IDEOGRAPH
+ "90 64 85AA", #CJK UNIFIED IDEOGRAPH
+ "90 65 89AA", #CJK UNIFIED IDEOGRAPH
+ "90 66 8A3A", #CJK UNIFIED IDEOGRAPH
+ "90 67 8EAB", #CJK UNIFIED IDEOGRAPH
+ "90 68 8F9B", #CJK UNIFIED IDEOGRAPH
+ "90 69 9032", #CJK UNIFIED IDEOGRAPH
+ "90 6A 91DD", #CJK UNIFIED IDEOGRAPH
+ "90 6B 9707", #CJK UNIFIED IDEOGRAPH
+ "90 6C 4EBA", #CJK UNIFIED IDEOGRAPH
+ "90 6D 4EC1", #CJK UNIFIED IDEOGRAPH
+ "90 6E 5203", #CJK UNIFIED IDEOGRAPH
+ "90 6F 5875", #CJK UNIFIED IDEOGRAPH
+ "90 70 58EC", #CJK UNIFIED IDEOGRAPH
+ "90 71 5C0B", #CJK UNIFIED IDEOGRAPH
+ "90 72 751A", #CJK UNIFIED IDEOGRAPH
+ "90 73 5C3D", #CJK UNIFIED IDEOGRAPH
+ "90 74 814E", #CJK UNIFIED IDEOGRAPH
+ "90 75 8A0A", #CJK UNIFIED IDEOGRAPH
+ "90 76 8FC5", #CJK UNIFIED IDEOGRAPH
+ "90 77 9663", #CJK UNIFIED IDEOGRAPH
+ "90 78 976D", #CJK UNIFIED IDEOGRAPH
+ "90 79 7B25", #CJK UNIFIED IDEOGRAPH
+ "90 7A 8ACF", #CJK UNIFIED IDEOGRAPH
+ "90 7B 9808", #CJK UNIFIED IDEOGRAPH
+ "90 7C 9162", #CJK UNIFIED IDEOGRAPH
+ "90 7D 56F3", #CJK UNIFIED IDEOGRAPH
+ "90 7E 53A8", #CJK UNIFIED IDEOGRAPH
+ "90 80 9017", #CJK UNIFIED IDEOGRAPH
+ "90 81 5439", #CJK UNIFIED IDEOGRAPH
+ "90 82 5782", #CJK UNIFIED IDEOGRAPH
+ "90 83 5E25", #CJK UNIFIED IDEOGRAPH
+ "90 84 63A8", #CJK UNIFIED IDEOGRAPH
+ "90 85 6C34", #CJK UNIFIED IDEOGRAPH
+ "90 86 708A", #CJK UNIFIED IDEOGRAPH
+ "90 87 7761", #CJK UNIFIED IDEOGRAPH
+ "90 88 7C8B", #CJK UNIFIED IDEOGRAPH
+ "90 89 7FE0", #CJK UNIFIED IDEOGRAPH
+ "90 8A 8870", #CJK UNIFIED IDEOGRAPH
+ "90 8B 9042", #CJK UNIFIED IDEOGRAPH
+ "90 8C 9154", #CJK UNIFIED IDEOGRAPH
+ "90 8D 9310", #CJK UNIFIED IDEOGRAPH
+ "90 8E 9318", #CJK UNIFIED IDEOGRAPH
+ "90 8F 968F", #CJK UNIFIED IDEOGRAPH
+ "90 90 745E", #CJK UNIFIED IDEOGRAPH
+ "90 91 9AC4", #CJK UNIFIED IDEOGRAPH
+ "90 92 5D07", #CJK UNIFIED IDEOGRAPH
+ "90 93 5D69", #CJK UNIFIED IDEOGRAPH
+ "90 94 6570", #CJK UNIFIED IDEOGRAPH
+ "90 95 67A2", #CJK UNIFIED IDEOGRAPH
+ "90 96 8DA8", #CJK UNIFIED IDEOGRAPH
+ "90 97 96DB", #CJK UNIFIED IDEOGRAPH
+ "90 98 636E", #CJK UNIFIED IDEOGRAPH
+ "90 99 6749", #CJK UNIFIED IDEOGRAPH
+ "90 9A 6919", #CJK UNIFIED IDEOGRAPH
+ "90 9B 83C5", #CJK UNIFIED IDEOGRAPH
+ "90 9C 9817", #CJK UNIFIED IDEOGRAPH
+ "90 9D 96C0", #CJK UNIFIED IDEOGRAPH
+ "90 9E 88FE", #CJK UNIFIED IDEOGRAPH
+ "90 9F 6F84", #CJK UNIFIED IDEOGRAPH
+ "90 A0 647A", #CJK UNIFIED IDEOGRAPH
+ "90 A1 5BF8", #CJK UNIFIED IDEOGRAPH
+ "90 A2 4E16", #CJK UNIFIED IDEOGRAPH
+ "90 A3 702C", #CJK UNIFIED IDEOGRAPH
+ "90 A4 755D", #CJK UNIFIED IDEOGRAPH
+ "90 A5 662F", #CJK UNIFIED IDEOGRAPH
+ "90 A6 51C4", #CJK UNIFIED IDEOGRAPH
+ "90 A7 5236", #CJK UNIFIED IDEOGRAPH
+ "90 A8 52E2", #CJK UNIFIED IDEOGRAPH
+ "90 A9 59D3", #CJK UNIFIED IDEOGRAPH
+ "90 AA 5F81", #CJK UNIFIED IDEOGRAPH
+ "90 AB 6027", #CJK UNIFIED IDEOGRAPH
+ "90 AC 6210", #CJK UNIFIED IDEOGRAPH
+ "90 AD 653F", #CJK UNIFIED IDEOGRAPH
+ "90 AE 6574", #CJK UNIFIED IDEOGRAPH
+ "90 AF 661F", #CJK UNIFIED IDEOGRAPH
+ "90 B0 6674", #CJK UNIFIED IDEOGRAPH
+ "90 B1 68F2", #CJK UNIFIED IDEOGRAPH
+ "90 B2 6816", #CJK UNIFIED IDEOGRAPH
+ "90 B3 6B63", #CJK UNIFIED IDEOGRAPH
+ "90 B4 6E05", #CJK UNIFIED IDEOGRAPH
+ "90 B5 7272", #CJK UNIFIED IDEOGRAPH
+ "90 B6 751F", #CJK UNIFIED IDEOGRAPH
+ "90 B7 76DB", #CJK UNIFIED IDEOGRAPH
+ "90 B8 7CBE", #CJK UNIFIED IDEOGRAPH
+ "90 B9 8056", #CJK UNIFIED IDEOGRAPH
+ "90 BA 58F0", #CJK UNIFIED IDEOGRAPH
+ "90 BB 88FD", #CJK UNIFIED IDEOGRAPH
+ "90 BC 897F", #CJK UNIFIED IDEOGRAPH
+ "90 BD 8AA0", #CJK UNIFIED IDEOGRAPH
+ "90 BE 8A93", #CJK UNIFIED IDEOGRAPH
+ "90 BF 8ACB", #CJK UNIFIED IDEOGRAPH
+ "90 C0 901D", #CJK UNIFIED IDEOGRAPH
+ "90 C1 9192", #CJK UNIFIED IDEOGRAPH
+ "90 C2 9752", #CJK UNIFIED IDEOGRAPH
+ "90 C3 9759", #CJK UNIFIED IDEOGRAPH
+ "90 C4 6589", #CJK UNIFIED IDEOGRAPH
+ "90 C5 7A0E", #CJK UNIFIED IDEOGRAPH
+ "90 C6 8106", #CJK UNIFIED IDEOGRAPH
+ "90 C7 96BB", #CJK UNIFIED IDEOGRAPH
+ "90 C8 5E2D", #CJK UNIFIED IDEOGRAPH
+ "90 C9 60DC", #CJK UNIFIED IDEOGRAPH
+ "90 CA 621A", #CJK UNIFIED IDEOGRAPH
+ "90 CB 65A5", #CJK UNIFIED IDEOGRAPH
+ "90 CC 6614", #CJK UNIFIED IDEOGRAPH
+ "90 CD 6790", #CJK UNIFIED IDEOGRAPH
+ "90 CE 77F3", #CJK UNIFIED IDEOGRAPH
+ "90 CF 7A4D", #CJK UNIFIED IDEOGRAPH
+ "90 D0 7C4D", #CJK UNIFIED IDEOGRAPH
+ "90 D1 7E3E", #CJK UNIFIED IDEOGRAPH
+ "90 D2 810A", #CJK UNIFIED IDEOGRAPH
+ "90 D3 8CAC", #CJK UNIFIED IDEOGRAPH
+ "90 D4 8D64", #CJK UNIFIED IDEOGRAPH
+ "90 D5 8DE1", #CJK UNIFIED IDEOGRAPH
+ "90 D6 8E5F", #CJK UNIFIED IDEOGRAPH
+ "90 D7 78A9", #CJK UNIFIED IDEOGRAPH
+ "90 D8 5207", #CJK UNIFIED IDEOGRAPH
+ "90 D9 62D9", #CJK UNIFIED IDEOGRAPH
+ "90 DA 63A5", #CJK UNIFIED IDEOGRAPH
+ "90 DB 6442", #CJK UNIFIED IDEOGRAPH
+ "90 DC 6298", #CJK UNIFIED IDEOGRAPH
+ "90 DD 8A2D", #CJK UNIFIED IDEOGRAPH
+ "90 DE 7A83", #CJK UNIFIED IDEOGRAPH
+ "90 DF 7BC0", #CJK UNIFIED IDEOGRAPH
+ "90 E0 8AAC", #CJK UNIFIED IDEOGRAPH
+ "90 E1 96EA", #CJK UNIFIED IDEOGRAPH
+ "90 E2 7D76", #CJK UNIFIED IDEOGRAPH
+ "90 E3 820C", #CJK UNIFIED IDEOGRAPH
+ "90 E4 8749", #CJK UNIFIED IDEOGRAPH
+ "90 E5 4ED9", #CJK UNIFIED IDEOGRAPH
+ "90 E6 5148", #CJK UNIFIED IDEOGRAPH
+ "90 E7 5343", #CJK UNIFIED IDEOGRAPH
+ "90 E8 5360", #CJK UNIFIED IDEOGRAPH
+ "90 E9 5BA3", #CJK UNIFIED IDEOGRAPH
+ "90 EA 5C02", #CJK UNIFIED IDEOGRAPH
+ "90 EB 5C16", #CJK UNIFIED IDEOGRAPH
+ "90 EC 5DDD", #CJK UNIFIED IDEOGRAPH
+ "90 ED 6226", #CJK UNIFIED IDEOGRAPH
+ "90 EE 6247", #CJK UNIFIED IDEOGRAPH
+ "90 EF 64B0", #CJK UNIFIED IDEOGRAPH
+ "90 F0 6813", #CJK UNIFIED IDEOGRAPH
+ "90 F1 6834", #CJK UNIFIED IDEOGRAPH
+ "90 F2 6CC9", #CJK UNIFIED IDEOGRAPH
+ "90 F3 6D45", #CJK UNIFIED IDEOGRAPH
+ "90 F4 6D17", #CJK UNIFIED IDEOGRAPH
+ "90 F5 67D3", #CJK UNIFIED IDEOGRAPH
+ "90 F6 6F5C", #CJK UNIFIED IDEOGRAPH
+ "90 F7 714E", #CJK UNIFIED IDEOGRAPH
+ "90 F8 717D", #CJK UNIFIED IDEOGRAPH
+ "90 F9 65CB", #CJK UNIFIED IDEOGRAPH
+ "90 FA 7A7F", #CJK UNIFIED IDEOGRAPH
+ "90 FB 7BAD", #CJK UNIFIED IDEOGRAPH
+ "90 FC 7DDA", #CJK UNIFIED IDEOGRAPH
+ "91 40 7E4A", #CJK UNIFIED IDEOGRAPH
+ "91 41 7FA8", #CJK UNIFIED IDEOGRAPH
+ "91 42 817A", #CJK UNIFIED IDEOGRAPH
+ "91 43 821B", #CJK UNIFIED IDEOGRAPH
+ "91 44 8239", #CJK UNIFIED IDEOGRAPH
+ "91 45 85A6", #CJK UNIFIED IDEOGRAPH
+ "91 46 8A6E", #CJK UNIFIED IDEOGRAPH
+ "91 47 8CCE", #CJK UNIFIED IDEOGRAPH
+ "91 48 8DF5", #CJK UNIFIED IDEOGRAPH
+ "91 49 9078", #CJK UNIFIED IDEOGRAPH
+ "91 4A 9077", #CJK UNIFIED IDEOGRAPH
+ "91 4B 92AD", #CJK UNIFIED IDEOGRAPH
+ "91 4C 9291", #CJK UNIFIED IDEOGRAPH
+ "91 4D 9583", #CJK UNIFIED IDEOGRAPH
+ "91 4E 9BAE", #CJK UNIFIED IDEOGRAPH
+ "91 4F 524D", #CJK UNIFIED IDEOGRAPH
+ "91 50 5584", #CJK UNIFIED IDEOGRAPH
+ "91 51 6F38", #CJK UNIFIED IDEOGRAPH
+ "91 52 7136", #CJK UNIFIED IDEOGRAPH
+ "91 53 5168", #CJK UNIFIED IDEOGRAPH
+ "91 54 7985", #CJK UNIFIED IDEOGRAPH
+ "91 55 7E55", #CJK UNIFIED IDEOGRAPH
+ "91 56 81B3", #CJK UNIFIED IDEOGRAPH
+ "91 57 7CCE", #CJK UNIFIED IDEOGRAPH
+ "91 58 564C", #CJK UNIFIED IDEOGRAPH
+ "91 59 5851", #CJK UNIFIED IDEOGRAPH
+ "91 5A 5CA8", #CJK UNIFIED IDEOGRAPH
+ "91 5B 63AA", #CJK UNIFIED IDEOGRAPH
+ "91 5C 66FE", #CJK UNIFIED IDEOGRAPH
+ "91 5D 66FD", #CJK UNIFIED IDEOGRAPH
+ "91 5E 695A", #CJK UNIFIED IDEOGRAPH
+ "91 5F 72D9", #CJK UNIFIED IDEOGRAPH
+ "91 60 758F", #CJK UNIFIED IDEOGRAPH
+ "91 61 758E", #CJK UNIFIED IDEOGRAPH
+ "91 62 790E", #CJK UNIFIED IDEOGRAPH
+ "91 63 7956", #CJK UNIFIED IDEOGRAPH
+ "91 64 79DF", #CJK UNIFIED IDEOGRAPH
+ "91 65 7C97", #CJK UNIFIED IDEOGRAPH
+ "91 66 7D20", #CJK UNIFIED IDEOGRAPH
+ "91 67 7D44", #CJK UNIFIED IDEOGRAPH
+ "91 68 8607", #CJK UNIFIED IDEOGRAPH
+ "91 69 8A34", #CJK UNIFIED IDEOGRAPH
+ "91 6A 963B", #CJK UNIFIED IDEOGRAPH
+ "91 6B 9061", #CJK UNIFIED IDEOGRAPH
+ "91 6C 9F20", #CJK UNIFIED IDEOGRAPH
+ "91 6D 50E7", #CJK UNIFIED IDEOGRAPH
+ "91 6E 5275", #CJK UNIFIED IDEOGRAPH
+ "91 6F 53CC", #CJK UNIFIED IDEOGRAPH
+ "91 70 53E2", #CJK UNIFIED IDEOGRAPH
+ "91 71 5009", #CJK UNIFIED IDEOGRAPH
+ "91 72 55AA", #CJK UNIFIED IDEOGRAPH
+ "91 73 58EE", #CJK UNIFIED IDEOGRAPH
+ "91 74 594F", #CJK UNIFIED IDEOGRAPH
+ "91 75 723D", #CJK UNIFIED IDEOGRAPH
+ "91 76 5B8B", #CJK UNIFIED IDEOGRAPH
+ "91 77 5C64", #CJK UNIFIED IDEOGRAPH
+ "91 78 531D", #CJK UNIFIED IDEOGRAPH
+ "91 79 60E3", #CJK UNIFIED IDEOGRAPH
+ "91 7A 60F3", #CJK UNIFIED IDEOGRAPH
+ "91 7B 635C", #CJK UNIFIED IDEOGRAPH
+ "91 7C 6383", #CJK UNIFIED IDEOGRAPH
+ "91 7D 633F", #CJK UNIFIED IDEOGRAPH
+ "91 7E 63BB", #CJK UNIFIED IDEOGRAPH
+ "91 80 64CD", #CJK UNIFIED IDEOGRAPH
+ "91 81 65E9", #CJK UNIFIED IDEOGRAPH
+ "91 82 66F9", #CJK UNIFIED IDEOGRAPH
+ "91 83 5DE3", #CJK UNIFIED IDEOGRAPH
+ "91 84 69CD", #CJK UNIFIED IDEOGRAPH
+ "91 85 69FD", #CJK UNIFIED IDEOGRAPH
+ "91 86 6F15", #CJK UNIFIED IDEOGRAPH
+ "91 87 71E5", #CJK UNIFIED IDEOGRAPH
+ "91 88 4E89", #CJK UNIFIED IDEOGRAPH
+ "91 89 75E9", #CJK UNIFIED IDEOGRAPH
+ "91 8A 76F8", #CJK UNIFIED IDEOGRAPH
+ "91 8B 7A93", #CJK UNIFIED IDEOGRAPH
+ "91 8C 7CDF", #CJK UNIFIED IDEOGRAPH
+ "91 8D 7DCF", #CJK UNIFIED IDEOGRAPH
+ "91 8E 7D9C", #CJK UNIFIED IDEOGRAPH
+ "91 8F 8061", #CJK UNIFIED IDEOGRAPH
+ "91 90 8349", #CJK UNIFIED IDEOGRAPH
+ "91 91 8358", #CJK UNIFIED IDEOGRAPH
+ "91 92 846C", #CJK UNIFIED IDEOGRAPH
+ "91 93 84BC", #CJK UNIFIED IDEOGRAPH
+ "91 94 85FB", #CJK UNIFIED IDEOGRAPH
+ "91 95 88C5", #CJK UNIFIED IDEOGRAPH
+ "91 96 8D70", #CJK UNIFIED IDEOGRAPH
+ "91 97 9001", #CJK UNIFIED IDEOGRAPH
+ "91 98 906D", #CJK UNIFIED IDEOGRAPH
+ "91 99 9397", #CJK UNIFIED IDEOGRAPH
+ "91 9A 971C", #CJK UNIFIED IDEOGRAPH
+ "91 9B 9A12", #CJK UNIFIED IDEOGRAPH
+ "91 9C 50CF", #CJK UNIFIED IDEOGRAPH
+ "91 9D 5897", #CJK UNIFIED IDEOGRAPH
+ "91 9E 618E", #CJK UNIFIED IDEOGRAPH
+ "91 9F 81D3", #CJK UNIFIED IDEOGRAPH
+ "91 A0 8535", #CJK UNIFIED IDEOGRAPH
+ "91 A1 8D08", #CJK UNIFIED IDEOGRAPH
+ "91 A2 9020", #CJK UNIFIED IDEOGRAPH
+ "91 A3 4FC3", #CJK UNIFIED IDEOGRAPH
+ "91 A4 5074", #CJK UNIFIED IDEOGRAPH
+ "91 A5 5247", #CJK UNIFIED IDEOGRAPH
+ "91 A6 5373", #CJK UNIFIED IDEOGRAPH
+ "91 A7 606F", #CJK UNIFIED IDEOGRAPH
+ "91 A8 6349", #CJK UNIFIED IDEOGRAPH
+ "91 A9 675F", #CJK UNIFIED IDEOGRAPH
+ "91 AA 6E2C", #CJK UNIFIED IDEOGRAPH
+ "91 AB 8DB3", #CJK UNIFIED IDEOGRAPH
+ "91 AC 901F", #CJK UNIFIED IDEOGRAPH
+ "91 AD 4FD7", #CJK UNIFIED IDEOGRAPH
+ "91 AE 5C5E", #CJK UNIFIED IDEOGRAPH
+ "91 AF 8CCA", #CJK UNIFIED IDEOGRAPH
+ "91 B0 65CF", #CJK UNIFIED IDEOGRAPH
+ "91 B1 7D9A", #CJK UNIFIED IDEOGRAPH
+ "91 B2 5352", #CJK UNIFIED IDEOGRAPH
+ "91 B3 8896", #CJK UNIFIED IDEOGRAPH
+ "91 B4 5176", #CJK UNIFIED IDEOGRAPH
+ "91 B5 63C3", #CJK UNIFIED IDEOGRAPH
+ "91 B6 5B58", #CJK UNIFIED IDEOGRAPH
+ "91 B7 5B6B", #CJK UNIFIED IDEOGRAPH
+ "91 B8 5C0A", #CJK UNIFIED IDEOGRAPH
+ "91 B9 640D", #CJK UNIFIED IDEOGRAPH
+ "91 BA 6751", #CJK UNIFIED IDEOGRAPH
+ "91 BB 905C", #CJK UNIFIED IDEOGRAPH
+ "91 BC 4ED6", #CJK UNIFIED IDEOGRAPH
+ "91 BD 591A", #CJK UNIFIED IDEOGRAPH
+ "91 BE 592A", #CJK UNIFIED IDEOGRAPH
+ "91 BF 6C70", #CJK UNIFIED IDEOGRAPH
+ "91 C0 8A51", #CJK UNIFIED IDEOGRAPH
+ "91 C1 553E", #CJK UNIFIED IDEOGRAPH
+ "91 C2 5815", #CJK UNIFIED IDEOGRAPH
+ "91 C3 59A5", #CJK UNIFIED IDEOGRAPH
+ "91 C4 60F0", #CJK UNIFIED IDEOGRAPH
+ "91 C5 6253", #CJK UNIFIED IDEOGRAPH
+ "91 C6 67C1", #CJK UNIFIED IDEOGRAPH
+ "91 C7 8235", #CJK UNIFIED IDEOGRAPH
+ "91 C8 6955", #CJK UNIFIED IDEOGRAPH
+ "91 C9 9640", #CJK UNIFIED IDEOGRAPH
+ "91 CA 99C4", #CJK UNIFIED IDEOGRAPH
+ "91 CB 9A28", #CJK UNIFIED IDEOGRAPH
+ "91 CC 4F53", #CJK UNIFIED IDEOGRAPH
+ "91 CD 5806", #CJK UNIFIED IDEOGRAPH
+ "91 CE 5BFE", #CJK UNIFIED IDEOGRAPH
+ "91 CF 8010", #CJK UNIFIED IDEOGRAPH
+ "91 D0 5CB1", #CJK UNIFIED IDEOGRAPH
+ "91 D1 5E2F", #CJK UNIFIED IDEOGRAPH
+ "91 D2 5F85", #CJK UNIFIED IDEOGRAPH
+ "91 D3 6020", #CJK UNIFIED IDEOGRAPH
+ "91 D4 614B", #CJK UNIFIED IDEOGRAPH
+ "91 D5 6234", #CJK UNIFIED IDEOGRAPH
+ "91 D6 66FF", #CJK UNIFIED IDEOGRAPH
+ "91 D7 6CF0", #CJK UNIFIED IDEOGRAPH
+ "91 D8 6EDE", #CJK UNIFIED IDEOGRAPH
+ "91 D9 80CE", #CJK UNIFIED IDEOGRAPH
+ "91 DA 817F", #CJK UNIFIED IDEOGRAPH
+ "91 DB 82D4", #CJK UNIFIED IDEOGRAPH
+ "91 DC 888B", #CJK UNIFIED IDEOGRAPH
+ "91 DD 8CB8", #CJK UNIFIED IDEOGRAPH
+ "91 DE 9000", #CJK UNIFIED IDEOGRAPH
+ "91 DF 902E", #CJK UNIFIED IDEOGRAPH
+ "91 E0 968A", #CJK UNIFIED IDEOGRAPH
+ "91 E1 9EDB", #CJK UNIFIED IDEOGRAPH
+ "91 E2 9BDB", #CJK UNIFIED IDEOGRAPH
+ "91 E3 4EE3", #CJK UNIFIED IDEOGRAPH
+ "91 E4 53F0", #CJK UNIFIED IDEOGRAPH
+ "91 E5 5927", #CJK UNIFIED IDEOGRAPH
+ "91 E6 7B2C", #CJK UNIFIED IDEOGRAPH
+ "91 E7 918D", #CJK UNIFIED IDEOGRAPH
+ "91 E8 984C", #CJK UNIFIED IDEOGRAPH
+ "91 E9 9DF9", #CJK UNIFIED IDEOGRAPH
+ "91 EA 6EDD", #CJK UNIFIED IDEOGRAPH
+ "91 EB 7027", #CJK UNIFIED IDEOGRAPH
+ "91 EC 5353", #CJK UNIFIED IDEOGRAPH
+ "91 ED 5544", #CJK UNIFIED IDEOGRAPH
+ "91 EE 5B85", #CJK UNIFIED IDEOGRAPH
+ "91 EF 6258", #CJK UNIFIED IDEOGRAPH
+ "91 F0 629E", #CJK UNIFIED IDEOGRAPH
+ "91 F1 62D3", #CJK UNIFIED IDEOGRAPH
+ "91 F2 6CA2", #CJK UNIFIED IDEOGRAPH
+ "91 F3 6FEF", #CJK UNIFIED IDEOGRAPH
+ "91 F4 7422", #CJK UNIFIED IDEOGRAPH
+ "91 F5 8A17", #CJK UNIFIED IDEOGRAPH
+ "91 F6 9438", #CJK UNIFIED IDEOGRAPH
+ "91 F7 6FC1", #CJK UNIFIED IDEOGRAPH
+ "91 F8 8AFE", #CJK UNIFIED IDEOGRAPH
+ "91 F9 8338", #CJK UNIFIED IDEOGRAPH
+ "91 FA 51E7", #CJK UNIFIED IDEOGRAPH
+ "91 FB 86F8", #CJK UNIFIED IDEOGRAPH
+ "91 FC 53EA", #CJK UNIFIED IDEOGRAPH
+ "92 40 53E9", #CJK UNIFIED IDEOGRAPH
+ "92 41 4F46", #CJK UNIFIED IDEOGRAPH
+ "92 42 9054", #CJK UNIFIED IDEOGRAPH
+ "92 43 8FB0", #CJK UNIFIED IDEOGRAPH
+ "92 44 596A", #CJK UNIFIED IDEOGRAPH
+ "92 45 8131", #CJK UNIFIED IDEOGRAPH
+ "92 46 5DFD", #CJK UNIFIED IDEOGRAPH
+ "92 47 7AEA", #CJK UNIFIED IDEOGRAPH
+ "92 48 8FBF", #CJK UNIFIED IDEOGRAPH
+ "92 49 68DA", #CJK UNIFIED IDEOGRAPH
+ "92 4A 8C37", #CJK UNIFIED IDEOGRAPH
+ "92 4B 72F8", #CJK UNIFIED IDEOGRAPH
+ "92 4C 9C48", #CJK UNIFIED IDEOGRAPH
+ "92 4D 6A3D", #CJK UNIFIED IDEOGRAPH
+ "92 4E 8AB0", #CJK UNIFIED IDEOGRAPH
+ "92 4F 4E39", #CJK UNIFIED IDEOGRAPH
+ "92 50 5358", #CJK UNIFIED IDEOGRAPH
+ "92 51 5606", #CJK UNIFIED IDEOGRAPH
+ "92 52 5766", #CJK UNIFIED IDEOGRAPH
+ "92 53 62C5", #CJK UNIFIED IDEOGRAPH
+ "92 54 63A2", #CJK UNIFIED IDEOGRAPH
+ "92 55 65E6", #CJK UNIFIED IDEOGRAPH
+ "92 56 6B4E", #CJK UNIFIED IDEOGRAPH
+ "92 57 6DE1", #CJK UNIFIED IDEOGRAPH
+ "92 58 6E5B", #CJK UNIFIED IDEOGRAPH
+ "92 59 70AD", #CJK UNIFIED IDEOGRAPH
+ "92 5A 77ED", #CJK UNIFIED IDEOGRAPH
+ "92 5B 7AEF", #CJK UNIFIED IDEOGRAPH
+ "92 5C 7BAA", #CJK UNIFIED IDEOGRAPH
+ "92 5D 7DBB", #CJK UNIFIED IDEOGRAPH
+ "92 5E 803D", #CJK UNIFIED IDEOGRAPH
+ "92 5F 80C6", #CJK UNIFIED IDEOGRAPH
+ "92 60 86CB", #CJK UNIFIED IDEOGRAPH
+ "92 61 8A95", #CJK UNIFIED IDEOGRAPH
+ "92 62 935B", #CJK UNIFIED IDEOGRAPH
+ "92 63 56E3", #CJK UNIFIED IDEOGRAPH
+ "92 64 58C7", #CJK UNIFIED IDEOGRAPH
+ "92 65 5F3E", #CJK UNIFIED IDEOGRAPH
+ "92 66 65AD", #CJK UNIFIED IDEOGRAPH
+ "92 67 6696", #CJK UNIFIED IDEOGRAPH
+ "92 68 6A80", #CJK UNIFIED IDEOGRAPH
+ "92 69 6BB5", #CJK UNIFIED IDEOGRAPH
+ "92 6A 7537", #CJK UNIFIED IDEOGRAPH
+ "92 6B 8AC7", #CJK UNIFIED IDEOGRAPH
+ "92 6C 5024", #CJK UNIFIED IDEOGRAPH
+ "92 6D 77E5", #CJK UNIFIED IDEOGRAPH
+ "92 6E 5730", #CJK UNIFIED IDEOGRAPH
+ "92 6F 5F1B", #CJK UNIFIED IDEOGRAPH
+ "92 70 6065", #CJK UNIFIED IDEOGRAPH
+ "92 71 667A", #CJK UNIFIED IDEOGRAPH
+ "92 72 6C60", #CJK UNIFIED IDEOGRAPH
+ "92 73 75F4", #CJK UNIFIED IDEOGRAPH
+ "92 74 7A1A", #CJK UNIFIED IDEOGRAPH
+ "92 75 7F6E", #CJK UNIFIED IDEOGRAPH
+ "92 76 81F4", #CJK UNIFIED IDEOGRAPH
+ "92 77 8718", #CJK UNIFIED IDEOGRAPH
+ "92 78 9045", #CJK UNIFIED IDEOGRAPH
+ "92 79 99B3", #CJK UNIFIED IDEOGRAPH
+ "92 7A 7BC9", #CJK UNIFIED IDEOGRAPH
+ "92 7B 755C", #CJK UNIFIED IDEOGRAPH
+ "92 7C 7AF9", #CJK UNIFIED IDEOGRAPH
+ "92 7D 7B51", #CJK UNIFIED IDEOGRAPH
+ "92 7E 84C4", #CJK UNIFIED IDEOGRAPH
+ "92 80 9010", #CJK UNIFIED IDEOGRAPH
+ "92 81 79E9", #CJK UNIFIED IDEOGRAPH
+ "92 82 7A92", #CJK UNIFIED IDEOGRAPH
+ "92 83 8336", #CJK UNIFIED IDEOGRAPH
+ "92 84 5AE1", #CJK UNIFIED IDEOGRAPH
+ "92 85 7740", #CJK UNIFIED IDEOGRAPH
+ "92 86 4E2D", #CJK UNIFIED IDEOGRAPH
+ "92 87 4EF2", #CJK UNIFIED IDEOGRAPH
+ "92 88 5B99", #CJK UNIFIED IDEOGRAPH
+ "92 89 5FE0", #CJK UNIFIED IDEOGRAPH
+ "92 8A 62BD", #CJK UNIFIED IDEOGRAPH
+ "92 8B 663C", #CJK UNIFIED IDEOGRAPH
+ "92 8C 67F1", #CJK UNIFIED IDEOGRAPH
+ "92 8D 6CE8", #CJK UNIFIED IDEOGRAPH
+ "92 8E 866B", #CJK UNIFIED IDEOGRAPH
+ "92 8F 8877", #CJK UNIFIED IDEOGRAPH
+ "92 90 8A3B", #CJK UNIFIED IDEOGRAPH
+ "92 91 914E", #CJK UNIFIED IDEOGRAPH
+ "92 92 92F3", #CJK UNIFIED IDEOGRAPH
+ "92 93 99D0", #CJK UNIFIED IDEOGRAPH
+ "92 94 6A17", #CJK UNIFIED IDEOGRAPH
+ "92 95 7026", #CJK UNIFIED IDEOGRAPH
+ "92 96 732A", #CJK UNIFIED IDEOGRAPH
+ "92 97 82E7", #CJK UNIFIED IDEOGRAPH
+ "92 98 8457", #CJK UNIFIED IDEOGRAPH
+ "92 99 8CAF", #CJK UNIFIED IDEOGRAPH
+ "92 9A 4E01", #CJK UNIFIED IDEOGRAPH
+ "92 9B 5146", #CJK UNIFIED IDEOGRAPH
+ "92 9C 51CB", #CJK UNIFIED IDEOGRAPH
+ "92 9D 558B", #CJK UNIFIED IDEOGRAPH
+ "92 9E 5BF5", #CJK UNIFIED IDEOGRAPH
+ "92 9F 5E16", #CJK UNIFIED IDEOGRAPH
+ "92 A0 5E33", #CJK UNIFIED IDEOGRAPH
+ "92 A1 5E81", #CJK UNIFIED IDEOGRAPH
+ "92 A2 5F14", #CJK UNIFIED IDEOGRAPH
+ "92 A3 5F35", #CJK UNIFIED IDEOGRAPH
+ "92 A4 5F6B", #CJK UNIFIED IDEOGRAPH
+ "92 A5 5FB4", #CJK UNIFIED IDEOGRAPH
+ "92 A6 61F2", #CJK UNIFIED IDEOGRAPH
+ "92 A7 6311", #CJK UNIFIED IDEOGRAPH
+ "92 A8 66A2", #CJK UNIFIED IDEOGRAPH
+ "92 A9 671D", #CJK UNIFIED IDEOGRAPH
+ "92 AA 6F6E", #CJK UNIFIED IDEOGRAPH
+ "92 AB 7252", #CJK UNIFIED IDEOGRAPH
+ "92 AC 753A", #CJK UNIFIED IDEOGRAPH
+ "92 AD 773A", #CJK UNIFIED IDEOGRAPH
+ "92 AE 8074", #CJK UNIFIED IDEOGRAPH
+ "92 AF 8139", #CJK UNIFIED IDEOGRAPH
+ "92 B0 8178", #CJK UNIFIED IDEOGRAPH
+ "92 B1 8776", #CJK UNIFIED IDEOGRAPH
+ "92 B2 8ABF", #CJK UNIFIED IDEOGRAPH
+ "92 B3 8ADC", #CJK UNIFIED IDEOGRAPH
+ "92 B4 8D85", #CJK UNIFIED IDEOGRAPH
+ "92 B5 8DF3", #CJK UNIFIED IDEOGRAPH
+ "92 B6 929A", #CJK UNIFIED IDEOGRAPH
+ "92 B7 9577", #CJK UNIFIED IDEOGRAPH
+ "92 B8 9802", #CJK UNIFIED IDEOGRAPH
+ "92 B9 9CE5", #CJK UNIFIED IDEOGRAPH
+ "92 BA 52C5", #CJK UNIFIED IDEOGRAPH
+ "92 BB 6357", #CJK UNIFIED IDEOGRAPH
+ "92 BC 76F4", #CJK UNIFIED IDEOGRAPH
+ "92 BD 6715", #CJK UNIFIED IDEOGRAPH
+ "92 BE 6C88", #CJK UNIFIED IDEOGRAPH
+ "92 BF 73CD", #CJK UNIFIED IDEOGRAPH
+ "92 C0 8CC3", #CJK UNIFIED IDEOGRAPH
+ "92 C1 93AE", #CJK UNIFIED IDEOGRAPH
+ "92 C2 9673", #CJK UNIFIED IDEOGRAPH
+ "92 C3 6D25", #CJK UNIFIED IDEOGRAPH
+ "92 C4 589C", #CJK UNIFIED IDEOGRAPH
+ "92 C5 690E", #CJK UNIFIED IDEOGRAPH
+ "92 C6 69CC", #CJK UNIFIED IDEOGRAPH
+ "92 C7 8FFD", #CJK UNIFIED IDEOGRAPH
+ "92 C8 939A", #CJK UNIFIED IDEOGRAPH
+ "92 C9 75DB", #CJK UNIFIED IDEOGRAPH
+ "92 CA 901A", #CJK UNIFIED IDEOGRAPH
+ "92 CB 585A", #CJK UNIFIED IDEOGRAPH
+ "92 CC 6802", #CJK UNIFIED IDEOGRAPH
+ "92 CD 63B4", #CJK UNIFIED IDEOGRAPH
+ "92 CE 69FB", #CJK UNIFIED IDEOGRAPH
+ "92 CF 4F43", #CJK UNIFIED IDEOGRAPH
+ "92 D0 6F2C", #CJK UNIFIED IDEOGRAPH
+ "92 D1 67D8", #CJK UNIFIED IDEOGRAPH
+ "92 D2 8FBB", #CJK UNIFIED IDEOGRAPH
+ "92 D3 8526", #CJK UNIFIED IDEOGRAPH
+ "92 D4 7DB4", #CJK UNIFIED IDEOGRAPH
+ "92 D5 9354", #CJK UNIFIED IDEOGRAPH
+ "92 D6 693F", #CJK UNIFIED IDEOGRAPH
+ "92 D7 6F70", #CJK UNIFIED IDEOGRAPH
+ "92 D8 576A", #CJK UNIFIED IDEOGRAPH
+ "92 D9 58F7", #CJK UNIFIED IDEOGRAPH
+ "92 DA 5B2C", #CJK UNIFIED IDEOGRAPH
+ "92 DB 7D2C", #CJK UNIFIED IDEOGRAPH
+ "92 DC 722A", #CJK UNIFIED IDEOGRAPH
+ "92 DD 540A", #CJK UNIFIED IDEOGRAPH
+ "92 DE 91E3", #CJK UNIFIED IDEOGRAPH
+ "92 DF 9DB4", #CJK UNIFIED IDEOGRAPH
+ "92 E0 4EAD", #CJK UNIFIED IDEOGRAPH
+ "92 E1 4F4E", #CJK UNIFIED IDEOGRAPH
+ "92 E2 505C", #CJK UNIFIED IDEOGRAPH
+ "92 E3 5075", #CJK UNIFIED IDEOGRAPH
+ "92 E4 5243", #CJK UNIFIED IDEOGRAPH
+ "92 E5 8C9E", #CJK UNIFIED IDEOGRAPH
+ "92 E6 5448", #CJK UNIFIED IDEOGRAPH
+ "92 E7 5824", #CJK UNIFIED IDEOGRAPH
+ "92 E8 5B9A", #CJK UNIFIED IDEOGRAPH
+ "92 E9 5E1D", #CJK UNIFIED IDEOGRAPH
+ "92 EA 5E95", #CJK UNIFIED IDEOGRAPH
+ "92 EB 5EAD", #CJK UNIFIED IDEOGRAPH
+ "92 EC 5EF7", #CJK UNIFIED IDEOGRAPH
+ "92 ED 5F1F", #CJK UNIFIED IDEOGRAPH
+ "92 EE 608C", #CJK UNIFIED IDEOGRAPH
+ "92 EF 62B5", #CJK UNIFIED IDEOGRAPH
+ "92 F0 633A", #CJK UNIFIED IDEOGRAPH
+ "92 F1 63D0", #CJK UNIFIED IDEOGRAPH
+ "92 F2 68AF", #CJK UNIFIED IDEOGRAPH
+ "92 F3 6C40", #CJK UNIFIED IDEOGRAPH
+ "92 F4 7887", #CJK UNIFIED IDEOGRAPH
+ "92 F5 798E", #CJK UNIFIED IDEOGRAPH
+ "92 F6 7A0B", #CJK UNIFIED IDEOGRAPH
+ "92 F7 7DE0", #CJK UNIFIED IDEOGRAPH
+ "92 F8 8247", #CJK UNIFIED IDEOGRAPH
+ "92 F9 8A02", #CJK UNIFIED IDEOGRAPH
+ "92 FA 8AE6", #CJK UNIFIED IDEOGRAPH
+ "92 FB 8E44", #CJK UNIFIED IDEOGRAPH
+ "92 FC 9013", #CJK UNIFIED IDEOGRAPH
+ "93 40 90B8", #CJK UNIFIED IDEOGRAPH
+ "93 41 912D", #CJK UNIFIED IDEOGRAPH
+ "93 42 91D8", #CJK UNIFIED IDEOGRAPH
+ "93 43 9F0E", #CJK UNIFIED IDEOGRAPH
+ "93 44 6CE5", #CJK UNIFIED IDEOGRAPH
+ "93 45 6458", #CJK UNIFIED IDEOGRAPH
+ "93 46 64E2", #CJK UNIFIED IDEOGRAPH
+ "93 47 6575", #CJK UNIFIED IDEOGRAPH
+ "93 48 6EF4", #CJK UNIFIED IDEOGRAPH
+ "93 49 7684", #CJK UNIFIED IDEOGRAPH
+ "93 4A 7B1B", #CJK UNIFIED IDEOGRAPH
+ "93 4B 9069", #CJK UNIFIED IDEOGRAPH
+ "93 4C 93D1", #CJK UNIFIED IDEOGRAPH
+ "93 4D 6EBA", #CJK UNIFIED IDEOGRAPH
+ "93 4E 54F2", #CJK UNIFIED IDEOGRAPH
+ "93 4F 5FB9", #CJK UNIFIED IDEOGRAPH
+ "93 50 64A4", #CJK UNIFIED IDEOGRAPH
+ "93 51 8F4D", #CJK UNIFIED IDEOGRAPH
+ "93 52 8FED", #CJK UNIFIED IDEOGRAPH
+ "93 53 9244", #CJK UNIFIED IDEOGRAPH
+ "93 54 5178", #CJK UNIFIED IDEOGRAPH
+ "93 55 586B", #CJK UNIFIED IDEOGRAPH
+ "93 56 5929", #CJK UNIFIED IDEOGRAPH
+ "93 57 5C55", #CJK UNIFIED IDEOGRAPH
+ "93 58 5E97", #CJK UNIFIED IDEOGRAPH
+ "93 59 6DFB", #CJK UNIFIED IDEOGRAPH
+ "93 5A 7E8F", #CJK UNIFIED IDEOGRAPH
+ "93 5B 751C", #CJK UNIFIED IDEOGRAPH
+ "93 5C 8CBC", #CJK UNIFIED IDEOGRAPH
+ "93 5D 8EE2", #CJK UNIFIED IDEOGRAPH
+ "93 5E 985B", #CJK UNIFIED IDEOGRAPH
+ "93 5F 70B9", #CJK UNIFIED IDEOGRAPH
+ "93 60 4F1D", #CJK UNIFIED IDEOGRAPH
+ "93 61 6BBF", #CJK UNIFIED IDEOGRAPH
+ "93 62 6FB1", #CJK UNIFIED IDEOGRAPH
+ "93 63 7530", #CJK UNIFIED IDEOGRAPH
+ "93 64 96FB", #CJK UNIFIED IDEOGRAPH
+ "93 65 514E", #CJK UNIFIED IDEOGRAPH
+ "93 66 5410", #CJK UNIFIED IDEOGRAPH
+ "93 67 5835", #CJK UNIFIED IDEOGRAPH
+ "93 68 5857", #CJK UNIFIED IDEOGRAPH
+ "93 69 59AC", #CJK UNIFIED IDEOGRAPH
+ "93 6A 5C60", #CJK UNIFIED IDEOGRAPH
+ "93 6B 5F92", #CJK UNIFIED IDEOGRAPH
+ "93 6C 6597", #CJK UNIFIED IDEOGRAPH
+ "93 6D 675C", #CJK UNIFIED IDEOGRAPH
+ "93 6E 6E21", #CJK UNIFIED IDEOGRAPH
+ "93 6F 767B", #CJK UNIFIED IDEOGRAPH
+ "93 70 83DF", #CJK UNIFIED IDEOGRAPH
+ "93 71 8CED", #CJK UNIFIED IDEOGRAPH
+ "93 72 9014", #CJK UNIFIED IDEOGRAPH
+ "93 73 90FD", #CJK UNIFIED IDEOGRAPH
+ "93 74 934D", #CJK UNIFIED IDEOGRAPH
+ "93 75 7825", #CJK UNIFIED IDEOGRAPH
+ "93 76 783A", #CJK UNIFIED IDEOGRAPH
+ "93 77 52AA", #CJK UNIFIED IDEOGRAPH
+ "93 78 5EA6", #CJK UNIFIED IDEOGRAPH
+ "93 79 571F", #CJK UNIFIED IDEOGRAPH
+ "93 7A 5974", #CJK UNIFIED IDEOGRAPH
+ "93 7B 6012", #CJK UNIFIED IDEOGRAPH
+ "93 7C 5012", #CJK UNIFIED IDEOGRAPH
+ "93 7D 515A", #CJK UNIFIED IDEOGRAPH
+ "93 7E 51AC", #CJK UNIFIED IDEOGRAPH
+ "93 80 51CD", #CJK UNIFIED IDEOGRAPH
+ "93 81 5200", #CJK UNIFIED IDEOGRAPH
+ "93 82 5510", #CJK UNIFIED IDEOGRAPH
+ "93 83 5854", #CJK UNIFIED IDEOGRAPH
+ "93 84 5858", #CJK UNIFIED IDEOGRAPH
+ "93 85 5957", #CJK UNIFIED IDEOGRAPH
+ "93 86 5B95", #CJK UNIFIED IDEOGRAPH
+ "93 87 5CF6", #CJK UNIFIED IDEOGRAPH
+ "93 88 5D8B", #CJK UNIFIED IDEOGRAPH
+ "93 89 60BC", #CJK UNIFIED IDEOGRAPH
+ "93 8A 6295", #CJK UNIFIED IDEOGRAPH
+ "93 8B 642D", #CJK UNIFIED IDEOGRAPH
+ "93 8C 6771", #CJK UNIFIED IDEOGRAPH
+ "93 8D 6843", #CJK UNIFIED IDEOGRAPH
+ "93 8E 68BC", #CJK UNIFIED IDEOGRAPH
+ "93 8F 68DF", #CJK UNIFIED IDEOGRAPH
+ "93 90 76D7", #CJK UNIFIED IDEOGRAPH
+ "93 91 6DD8", #CJK UNIFIED IDEOGRAPH
+ "93 92 6E6F", #CJK UNIFIED IDEOGRAPH
+ "93 93 6D9B", #CJK UNIFIED IDEOGRAPH
+ "93 94 706F", #CJK UNIFIED IDEOGRAPH
+ "93 95 71C8", #CJK UNIFIED IDEOGRAPH
+ "93 96 5F53", #CJK UNIFIED IDEOGRAPH
+ "93 97 75D8", #CJK UNIFIED IDEOGRAPH
+ "93 98 7977", #CJK UNIFIED IDEOGRAPH
+ "93 99 7B49", #CJK UNIFIED IDEOGRAPH
+ "93 9A 7B54", #CJK UNIFIED IDEOGRAPH
+ "93 9B 7B52", #CJK UNIFIED IDEOGRAPH
+ "93 9C 7CD6", #CJK UNIFIED IDEOGRAPH
+ "93 9D 7D71", #CJK UNIFIED IDEOGRAPH
+ "93 9E 5230", #CJK UNIFIED IDEOGRAPH
+ "93 9F 8463", #CJK UNIFIED IDEOGRAPH
+ "93 A0 8569", #CJK UNIFIED IDEOGRAPH
+ "93 A1 85E4", #CJK UNIFIED IDEOGRAPH
+ "93 A2 8A0E", #CJK UNIFIED IDEOGRAPH
+ "93 A3 8B04", #CJK UNIFIED IDEOGRAPH
+ "93 A4 8C46", #CJK UNIFIED IDEOGRAPH
+ "93 A5 8E0F", #CJK UNIFIED IDEOGRAPH
+ "93 A6 9003", #CJK UNIFIED IDEOGRAPH
+ "93 A7 900F", #CJK UNIFIED IDEOGRAPH
+ "93 A8 9419", #CJK UNIFIED IDEOGRAPH
+ "93 A9 9676", #CJK UNIFIED IDEOGRAPH
+ "93 AA 982D", #CJK UNIFIED IDEOGRAPH
+ "93 AB 9A30", #CJK UNIFIED IDEOGRAPH
+ "93 AC 95D8", #CJK UNIFIED IDEOGRAPH
+ "93 AD 50CD", #CJK UNIFIED IDEOGRAPH
+ "93 AE 52D5", #CJK UNIFIED IDEOGRAPH
+ "93 AF 540C", #CJK UNIFIED IDEOGRAPH
+ "93 B0 5802", #CJK UNIFIED IDEOGRAPH
+ "93 B1 5C0E", #CJK UNIFIED IDEOGRAPH
+ "93 B2 61A7", #CJK UNIFIED IDEOGRAPH
+ "93 B3 649E", #CJK UNIFIED IDEOGRAPH
+ "93 B4 6D1E", #CJK UNIFIED IDEOGRAPH
+ "93 B5 77B3", #CJK UNIFIED IDEOGRAPH
+ "93 B6 7AE5", #CJK UNIFIED IDEOGRAPH
+ "93 B7 80F4", #CJK UNIFIED IDEOGRAPH
+ "93 B8 8404", #CJK UNIFIED IDEOGRAPH
+ "93 B9 9053", #CJK UNIFIED IDEOGRAPH
+ "93 BA 9285", #CJK UNIFIED IDEOGRAPH
+ "93 BB 5CE0", #CJK UNIFIED IDEOGRAPH
+ "93 BC 9D07", #CJK UNIFIED IDEOGRAPH
+ "93 BD 533F", #CJK UNIFIED IDEOGRAPH
+ "93 BE 5F97", #CJK UNIFIED IDEOGRAPH
+ "93 BF 5FB3", #CJK UNIFIED IDEOGRAPH
+ "93 C0 6D9C", #CJK UNIFIED IDEOGRAPH
+ "93 C1 7279", #CJK UNIFIED IDEOGRAPH
+ "93 C2 7763", #CJK UNIFIED IDEOGRAPH
+ "93 C3 79BF", #CJK UNIFIED IDEOGRAPH
+ "93 C4 7BE4", #CJK UNIFIED IDEOGRAPH
+ "93 C5 6BD2", #CJK UNIFIED IDEOGRAPH
+ "93 C6 72EC", #CJK UNIFIED IDEOGRAPH
+ "93 C7 8AAD", #CJK UNIFIED IDEOGRAPH
+ "93 C8 6803", #CJK UNIFIED IDEOGRAPH
+ "93 C9 6A61", #CJK UNIFIED IDEOGRAPH
+ "93 CA 51F8", #CJK UNIFIED IDEOGRAPH
+ "93 CB 7A81", #CJK UNIFIED IDEOGRAPH
+ "93 CC 6934", #CJK UNIFIED IDEOGRAPH
+ "93 CD 5C4A", #CJK UNIFIED IDEOGRAPH
+ "93 CE 9CF6", #CJK UNIFIED IDEOGRAPH
+ "93 CF 82EB", #CJK UNIFIED IDEOGRAPH
+ "93 D0 5BC5", #CJK UNIFIED IDEOGRAPH
+ "93 D1 9149", #CJK UNIFIED IDEOGRAPH
+ "93 D2 701E", #CJK UNIFIED IDEOGRAPH
+ "93 D3 5678", #CJK UNIFIED IDEOGRAPH
+ "93 D4 5C6F", #CJK UNIFIED IDEOGRAPH
+ "93 D5 60C7", #CJK UNIFIED IDEOGRAPH
+ "93 D6 6566", #CJK UNIFIED IDEOGRAPH
+ "93 D7 6C8C", #CJK UNIFIED IDEOGRAPH
+ "93 D8 8C5A", #CJK UNIFIED IDEOGRAPH
+ "93 D9 9041", #CJK UNIFIED IDEOGRAPH
+ "93 DA 9813", #CJK UNIFIED IDEOGRAPH
+ "93 DB 5451", #CJK UNIFIED IDEOGRAPH
+ "93 DC 66C7", #CJK UNIFIED IDEOGRAPH
+ "93 DD 920D", #CJK UNIFIED IDEOGRAPH
+ "93 DE 5948", #CJK UNIFIED IDEOGRAPH
+ "93 DF 90A3", #CJK UNIFIED IDEOGRAPH
+ "93 E0 5185", #CJK UNIFIED IDEOGRAPH
+ "93 E1 4E4D", #CJK UNIFIED IDEOGRAPH
+ "93 E2 51EA", #CJK UNIFIED IDEOGRAPH
+ "93 E3 8599", #CJK UNIFIED IDEOGRAPH
+ "93 E4 8B0E", #CJK UNIFIED IDEOGRAPH
+ "93 E5 7058", #CJK UNIFIED IDEOGRAPH
+ "93 E6 637A", #CJK UNIFIED IDEOGRAPH
+ "93 E7 934B", #CJK UNIFIED IDEOGRAPH
+ "93 E8 6962", #CJK UNIFIED IDEOGRAPH
+ "93 E9 99B4", #CJK UNIFIED IDEOGRAPH
+ "93 EA 7E04", #CJK UNIFIED IDEOGRAPH
+ "93 EB 7577", #CJK UNIFIED IDEOGRAPH
+ "93 EC 5357", #CJK UNIFIED IDEOGRAPH
+ "93 ED 6960", #CJK UNIFIED IDEOGRAPH
+ "93 EE 8EDF", #CJK UNIFIED IDEOGRAPH
+ "93 EF 96E3", #CJK UNIFIED IDEOGRAPH
+ "93 F0 6C5D", #CJK UNIFIED IDEOGRAPH
+ "93 F1 4E8C", #CJK UNIFIED IDEOGRAPH
+ "93 F2 5C3C", #CJK UNIFIED IDEOGRAPH
+ "93 F3 5F10", #CJK UNIFIED IDEOGRAPH
+ "93 F4 8FE9", #CJK UNIFIED IDEOGRAPH
+ "93 F5 5302", #CJK UNIFIED IDEOGRAPH
+ "93 F6 8CD1", #CJK UNIFIED IDEOGRAPH
+ "93 F7 8089", #CJK UNIFIED IDEOGRAPH
+ "93 F8 8679", #CJK UNIFIED IDEOGRAPH
+ "93 F9 5EFF", #CJK UNIFIED IDEOGRAPH
+ "93 FA 65E5", #CJK UNIFIED IDEOGRAPH
+ "93 FB 4E73", #CJK UNIFIED IDEOGRAPH
+ "93 FC 5165", #CJK UNIFIED IDEOGRAPH
+ "94 40 5982", #CJK UNIFIED IDEOGRAPH
+ "94 41 5C3F", #CJK UNIFIED IDEOGRAPH
+ "94 42 97EE", #CJK UNIFIED IDEOGRAPH
+ "94 43 4EFB", #CJK UNIFIED IDEOGRAPH
+ "94 44 598A", #CJK UNIFIED IDEOGRAPH
+ "94 45 5FCD", #CJK UNIFIED IDEOGRAPH
+ "94 46 8A8D", #CJK UNIFIED IDEOGRAPH
+ "94 47 6FE1", #CJK UNIFIED IDEOGRAPH
+ "94 48 79B0", #CJK UNIFIED IDEOGRAPH
+ "94 49 7962", #CJK UNIFIED IDEOGRAPH
+ "94 4A 5BE7", #CJK UNIFIED IDEOGRAPH
+ "94 4B 8471", #CJK UNIFIED IDEOGRAPH
+ "94 4C 732B", #CJK UNIFIED IDEOGRAPH
+ "94 4D 71B1", #CJK UNIFIED IDEOGRAPH
+ "94 4E 5E74", #CJK UNIFIED IDEOGRAPH
+ "94 4F 5FF5", #CJK UNIFIED IDEOGRAPH
+ "94 50 637B", #CJK UNIFIED IDEOGRAPH
+ "94 51 649A", #CJK UNIFIED IDEOGRAPH
+ "94 52 71C3", #CJK UNIFIED IDEOGRAPH
+ "94 53 7C98", #CJK UNIFIED IDEOGRAPH
+ "94 54 4E43", #CJK UNIFIED IDEOGRAPH
+ "94 55 5EFC", #CJK UNIFIED IDEOGRAPH
+ "94 56 4E4B", #CJK UNIFIED IDEOGRAPH
+ "94 57 57DC", #CJK UNIFIED IDEOGRAPH
+ "94 58 56A2", #CJK UNIFIED IDEOGRAPH
+ "94 59 60A9", #CJK UNIFIED IDEOGRAPH
+ "94 5A 6FC3", #CJK UNIFIED IDEOGRAPH
+ "94 5B 7D0D", #CJK UNIFIED IDEOGRAPH
+ "94 5C 80FD", #CJK UNIFIED IDEOGRAPH
+ "94 5D 8133", #CJK UNIFIED IDEOGRAPH
+ "94 5E 81BF", #CJK UNIFIED IDEOGRAPH
+ "94 5F 8FB2", #CJK UNIFIED IDEOGRAPH
+ "94 60 8997", #CJK UNIFIED IDEOGRAPH
+ "94 61 86A4", #CJK UNIFIED IDEOGRAPH
+ "94 62 5DF4", #CJK UNIFIED IDEOGRAPH
+ "94 63 628A", #CJK UNIFIED IDEOGRAPH
+ "94 64 64AD", #CJK UNIFIED IDEOGRAPH
+ "94 65 8987", #CJK UNIFIED IDEOGRAPH
+ "94 66 6777", #CJK UNIFIED IDEOGRAPH
+ "94 67 6CE2", #CJK UNIFIED IDEOGRAPH
+ "94 68 6D3E", #CJK UNIFIED IDEOGRAPH
+ "94 69 7436", #CJK UNIFIED IDEOGRAPH
+ "94 6A 7834", #CJK UNIFIED IDEOGRAPH
+ "94 6B 5A46", #CJK UNIFIED IDEOGRAPH
+ "94 6C 7F75", #CJK UNIFIED IDEOGRAPH
+ "94 6D 82AD", #CJK UNIFIED IDEOGRAPH
+ "94 6E 99AC", #CJK UNIFIED IDEOGRAPH
+ "94 6F 4FF3", #CJK UNIFIED IDEOGRAPH
+ "94 70 5EC3", #CJK UNIFIED IDEOGRAPH
+ "94 71 62DD", #CJK UNIFIED IDEOGRAPH
+ "94 72 6392", #CJK UNIFIED IDEOGRAPH
+ "94 73 6557", #CJK UNIFIED IDEOGRAPH
+ "94 74 676F", #CJK UNIFIED IDEOGRAPH
+ "94 75 76C3", #CJK UNIFIED IDEOGRAPH
+ "94 76 724C", #CJK UNIFIED IDEOGRAPH
+ "94 77 80CC", #CJK UNIFIED IDEOGRAPH
+ "94 78 80BA", #CJK UNIFIED IDEOGRAPH
+ "94 79 8F29", #CJK UNIFIED IDEOGRAPH
+ "94 7A 914D", #CJK UNIFIED IDEOGRAPH
+ "94 7B 500D", #CJK UNIFIED IDEOGRAPH
+ "94 7C 57F9", #CJK UNIFIED IDEOGRAPH
+ "94 7D 5A92", #CJK UNIFIED IDEOGRAPH
+ "94 7E 6885", #CJK UNIFIED IDEOGRAPH
+ "94 80 6973", #CJK UNIFIED IDEOGRAPH
+ "94 81 7164", #CJK UNIFIED IDEOGRAPH
+ "94 82 72FD", #CJK UNIFIED IDEOGRAPH
+ "94 83 8CB7", #CJK UNIFIED IDEOGRAPH
+ "94 84 58F2", #CJK UNIFIED IDEOGRAPH
+ "94 85 8CE0", #CJK UNIFIED IDEOGRAPH
+ "94 86 966A", #CJK UNIFIED IDEOGRAPH
+ "94 87 9019", #CJK UNIFIED IDEOGRAPH
+ "94 88 877F", #CJK UNIFIED IDEOGRAPH
+ "94 89 79E4", #CJK UNIFIED IDEOGRAPH
+ "94 8A 77E7", #CJK UNIFIED IDEOGRAPH
+ "94 8B 8429", #CJK UNIFIED IDEOGRAPH
+ "94 8C 4F2F", #CJK UNIFIED IDEOGRAPH
+ "94 8D 5265", #CJK UNIFIED IDEOGRAPH
+ "94 8E 535A", #CJK UNIFIED IDEOGRAPH
+ "94 8F 62CD", #CJK UNIFIED IDEOGRAPH
+ "94 90 67CF", #CJK UNIFIED IDEOGRAPH
+ "94 91 6CCA", #CJK UNIFIED IDEOGRAPH
+ "94 92 767D", #CJK UNIFIED IDEOGRAPH
+ "94 93 7B94", #CJK UNIFIED IDEOGRAPH
+ "94 94 7C95", #CJK UNIFIED IDEOGRAPH
+ "94 95 8236", #CJK UNIFIED IDEOGRAPH
+ "94 96 8584", #CJK UNIFIED IDEOGRAPH
+ "94 97 8FEB", #CJK UNIFIED IDEOGRAPH
+ "94 98 66DD", #CJK UNIFIED IDEOGRAPH
+ "94 99 6F20", #CJK UNIFIED IDEOGRAPH
+ "94 9A 7206", #CJK UNIFIED IDEOGRAPH
+ "94 9B 7E1B", #CJK UNIFIED IDEOGRAPH
+ "94 9C 83AB", #CJK UNIFIED IDEOGRAPH
+ "94 9D 99C1", #CJK UNIFIED IDEOGRAPH
+ "94 9E 9EA6", #CJK UNIFIED IDEOGRAPH
+ "94 9F 51FD", #CJK UNIFIED IDEOGRAPH
+ "94 A0 7BB1", #CJK UNIFIED IDEOGRAPH
+ "94 A1 7872", #CJK UNIFIED IDEOGRAPH
+ "94 A2 7BB8", #CJK UNIFIED IDEOGRAPH
+ "94 A3 8087", #CJK UNIFIED IDEOGRAPH
+ "94 A4 7B48", #CJK UNIFIED IDEOGRAPH
+ "94 A5 6AE8", #CJK UNIFIED IDEOGRAPH
+ "94 A6 5E61", #CJK UNIFIED IDEOGRAPH
+ "94 A7 808C", #CJK UNIFIED IDEOGRAPH
+ "94 A8 7551", #CJK UNIFIED IDEOGRAPH
+ "94 A9 7560", #CJK UNIFIED IDEOGRAPH
+ "94 AA 516B", #CJK UNIFIED IDEOGRAPH
+ "94 AB 9262", #CJK UNIFIED IDEOGRAPH
+ "94 AC 6E8C", #CJK UNIFIED IDEOGRAPH
+ "94 AD 767A", #CJK UNIFIED IDEOGRAPH
+ "94 AE 9197", #CJK UNIFIED IDEOGRAPH
+ "94 AF 9AEA", #CJK UNIFIED IDEOGRAPH
+ "94 B0 4F10", #CJK UNIFIED IDEOGRAPH
+ "94 B1 7F70", #CJK UNIFIED IDEOGRAPH
+ "94 B2 629C", #CJK UNIFIED IDEOGRAPH
+ "94 B3 7B4F", #CJK UNIFIED IDEOGRAPH
+ "94 B4 95A5", #CJK UNIFIED IDEOGRAPH
+ "94 B5 9CE9", #CJK UNIFIED IDEOGRAPH
+ "94 B6 567A", #CJK UNIFIED IDEOGRAPH
+ "94 B7 5859", #CJK UNIFIED IDEOGRAPH
+ "94 B8 86E4", #CJK UNIFIED IDEOGRAPH
+ "94 B9 96BC", #CJK UNIFIED IDEOGRAPH
+ "94 BA 4F34", #CJK UNIFIED IDEOGRAPH
+ "94 BB 5224", #CJK UNIFIED IDEOGRAPH
+ "94 BC 534A", #CJK UNIFIED IDEOGRAPH
+ "94 BD 53CD", #CJK UNIFIED IDEOGRAPH
+ "94 BE 53DB", #CJK UNIFIED IDEOGRAPH
+ "94 BF 5E06", #CJK UNIFIED IDEOGRAPH
+ "94 C0 642C", #CJK UNIFIED IDEOGRAPH
+ "94 C1 6591", #CJK UNIFIED IDEOGRAPH
+ "94 C2 677F", #CJK UNIFIED IDEOGRAPH
+ "94 C3 6C3E", #CJK UNIFIED IDEOGRAPH
+ "94 C4 6C4E", #CJK UNIFIED IDEOGRAPH
+ "94 C5 7248", #CJK UNIFIED IDEOGRAPH
+ "94 C6 72AF", #CJK UNIFIED IDEOGRAPH
+ "94 C7 73ED", #CJK UNIFIED IDEOGRAPH
+ "94 C8 7554", #CJK UNIFIED IDEOGRAPH
+ "94 C9 7E41", #CJK UNIFIED IDEOGRAPH
+ "94 CA 822C", #CJK UNIFIED IDEOGRAPH
+ "94 CB 85E9", #CJK UNIFIED IDEOGRAPH
+ "94 CC 8CA9", #CJK UNIFIED IDEOGRAPH
+ "94 CD 7BC4", #CJK UNIFIED IDEOGRAPH
+ "94 CE 91C6", #CJK UNIFIED IDEOGRAPH
+ "94 CF 7169", #CJK UNIFIED IDEOGRAPH
+ "94 D0 9812", #CJK UNIFIED IDEOGRAPH
+ "94 D1 98EF", #CJK UNIFIED IDEOGRAPH
+ "94 D2 633D", #CJK UNIFIED IDEOGRAPH
+ "94 D3 6669", #CJK UNIFIED IDEOGRAPH
+ "94 D4 756A", #CJK UNIFIED IDEOGRAPH
+ "94 D5 76E4", #CJK UNIFIED IDEOGRAPH
+ "94 D6 78D0", #CJK UNIFIED IDEOGRAPH
+ "94 D7 8543", #CJK UNIFIED IDEOGRAPH
+ "94 D8 86EE", #CJK UNIFIED IDEOGRAPH
+ "94 D9 532A", #CJK UNIFIED IDEOGRAPH
+ "94 DA 5351", #CJK UNIFIED IDEOGRAPH
+ "94 DB 5426", #CJK UNIFIED IDEOGRAPH
+ "94 DC 5983", #CJK UNIFIED IDEOGRAPH
+ "94 DD 5E87", #CJK UNIFIED IDEOGRAPH
+ "94 DE 5F7C", #CJK UNIFIED IDEOGRAPH
+ "94 DF 60B2", #CJK UNIFIED IDEOGRAPH
+ "94 E0 6249", #CJK UNIFIED IDEOGRAPH
+ "94 E1 6279", #CJK UNIFIED IDEOGRAPH
+ "94 E2 62AB", #CJK UNIFIED IDEOGRAPH
+ "94 E3 6590", #CJK UNIFIED IDEOGRAPH
+ "94 E4 6BD4", #CJK UNIFIED IDEOGRAPH
+ "94 E5 6CCC", #CJK UNIFIED IDEOGRAPH
+ "94 E6 75B2", #CJK UNIFIED IDEOGRAPH
+ "94 E7 76AE", #CJK UNIFIED IDEOGRAPH
+ "94 E8 7891", #CJK UNIFIED IDEOGRAPH
+ "94 E9 79D8", #CJK UNIFIED IDEOGRAPH
+ "94 EA 7DCB", #CJK UNIFIED IDEOGRAPH
+ "94 EB 7F77", #CJK UNIFIED IDEOGRAPH
+ "94 EC 80A5", #CJK UNIFIED IDEOGRAPH
+ "94 ED 88AB", #CJK UNIFIED IDEOGRAPH
+ "94 EE 8AB9", #CJK UNIFIED IDEOGRAPH
+ "94 EF 8CBB", #CJK UNIFIED IDEOGRAPH
+ "94 F0 907F", #CJK UNIFIED IDEOGRAPH
+ "94 F1 975E", #CJK UNIFIED IDEOGRAPH
+ "94 F2 98DB", #CJK UNIFIED IDEOGRAPH
+ "94 F3 6A0B", #CJK UNIFIED IDEOGRAPH
+ "94 F4 7C38", #CJK UNIFIED IDEOGRAPH
+ "94 F5 5099", #CJK UNIFIED IDEOGRAPH
+ "94 F6 5C3E", #CJK UNIFIED IDEOGRAPH
+ "94 F7 5FAE", #CJK UNIFIED IDEOGRAPH
+ "94 F8 6787", #CJK UNIFIED IDEOGRAPH
+ "94 F9 6BD8", #CJK UNIFIED IDEOGRAPH
+ "94 FA 7435", #CJK UNIFIED IDEOGRAPH
+ "94 FB 7709", #CJK UNIFIED IDEOGRAPH
+ "94 FC 7F8E", #CJK UNIFIED IDEOGRAPH
+ "95 40 9F3B", #CJK UNIFIED IDEOGRAPH
+ "95 41 67CA", #CJK UNIFIED IDEOGRAPH
+ "95 42 7A17", #CJK UNIFIED IDEOGRAPH
+ "95 43 5339", #CJK UNIFIED IDEOGRAPH
+ "95 44 758B", #CJK UNIFIED IDEOGRAPH
+ "95 45 9AED", #CJK UNIFIED IDEOGRAPH
+ "95 46 5F66", #CJK UNIFIED IDEOGRAPH
+ "95 47 819D", #CJK UNIFIED IDEOGRAPH
+ "95 48 83F1", #CJK UNIFIED IDEOGRAPH
+ "95 49 8098", #CJK UNIFIED IDEOGRAPH
+ "95 4A 5F3C", #CJK UNIFIED IDEOGRAPH
+ "95 4B 5FC5", #CJK UNIFIED IDEOGRAPH
+ "95 4C 7562", #CJK UNIFIED IDEOGRAPH
+ "95 4D 7B46", #CJK UNIFIED IDEOGRAPH
+ "95 4E 903C", #CJK UNIFIED IDEOGRAPH
+ "95 4F 6867", #CJK UNIFIED IDEOGRAPH
+ "95 50 59EB", #CJK UNIFIED IDEOGRAPH
+ "95 51 5A9B", #CJK UNIFIED IDEOGRAPH
+ "95 52 7D10", #CJK UNIFIED IDEOGRAPH
+ "95 53 767E", #CJK UNIFIED IDEOGRAPH
+ "95 54 8B2C", #CJK UNIFIED IDEOGRAPH
+ "95 55 4FF5", #CJK UNIFIED IDEOGRAPH
+ "95 56 5F6A", #CJK UNIFIED IDEOGRAPH
+ "95 57 6A19", #CJK UNIFIED IDEOGRAPH
+ "95 58 6C37", #CJK UNIFIED IDEOGRAPH
+ "95 59 6F02", #CJK UNIFIED IDEOGRAPH
+ "95 5A 74E2", #CJK UNIFIED IDEOGRAPH
+ "95 5B 7968", #CJK UNIFIED IDEOGRAPH
+ "95 5C 8868", #CJK UNIFIED IDEOGRAPH
+ "95 5D 8A55", #CJK UNIFIED IDEOGRAPH
+ "95 5E 8C79", #CJK UNIFIED IDEOGRAPH
+ "95 5F 5EDF", #CJK UNIFIED IDEOGRAPH
+ "95 60 63CF", #CJK UNIFIED IDEOGRAPH
+ "95 61 75C5", #CJK UNIFIED IDEOGRAPH
+ "95 62 79D2", #CJK UNIFIED IDEOGRAPH
+ "95 63 82D7", #CJK UNIFIED IDEOGRAPH
+ "95 64 9328", #CJK UNIFIED IDEOGRAPH
+ "95 65 92F2", #CJK UNIFIED IDEOGRAPH
+ "95 66 849C", #CJK UNIFIED IDEOGRAPH
+ "95 67 86ED", #CJK UNIFIED IDEOGRAPH
+ "95 68 9C2D", #CJK UNIFIED IDEOGRAPH
+ "95 69 54C1", #CJK UNIFIED IDEOGRAPH
+ "95 6A 5F6C", #CJK UNIFIED IDEOGRAPH
+ "95 6B 658C", #CJK UNIFIED IDEOGRAPH
+ "95 6C 6D5C", #CJK UNIFIED IDEOGRAPH
+ "95 6D 7015", #CJK UNIFIED IDEOGRAPH
+ "95 6E 8CA7", #CJK UNIFIED IDEOGRAPH
+ "95 6F 8CD3", #CJK UNIFIED IDEOGRAPH
+ "95 70 983B", #CJK UNIFIED IDEOGRAPH
+ "95 71 654F", #CJK UNIFIED IDEOGRAPH
+ "95 72 74F6", #CJK UNIFIED IDEOGRAPH
+ "95 73 4E0D", #CJK UNIFIED IDEOGRAPH
+ "95 74 4ED8", #CJK UNIFIED IDEOGRAPH
+ "95 75 57E0", #CJK UNIFIED IDEOGRAPH
+ "95 76 592B", #CJK UNIFIED IDEOGRAPH
+ "95 77 5A66", #CJK UNIFIED IDEOGRAPH
+ "95 78 5BCC", #CJK UNIFIED IDEOGRAPH
+ "95 79 51A8", #CJK UNIFIED IDEOGRAPH
+ "95 7A 5E03", #CJK UNIFIED IDEOGRAPH
+ "95 7B 5E9C", #CJK UNIFIED IDEOGRAPH
+ "95 7C 6016", #CJK UNIFIED IDEOGRAPH
+ "95 7D 6276", #CJK UNIFIED IDEOGRAPH
+ "95 7E 6577", #CJK UNIFIED IDEOGRAPH
+ "95 80 65A7", #CJK UNIFIED IDEOGRAPH
+ "95 81 666E", #CJK UNIFIED IDEOGRAPH
+ "95 82 6D6E", #CJK UNIFIED IDEOGRAPH
+ "95 83 7236", #CJK UNIFIED IDEOGRAPH
+ "95 84 7B26", #CJK UNIFIED IDEOGRAPH
+ "95 85 8150", #CJK UNIFIED IDEOGRAPH
+ "95 86 819A", #CJK UNIFIED IDEOGRAPH
+ "95 87 8299", #CJK UNIFIED IDEOGRAPH
+ "95 88 8B5C", #CJK UNIFIED IDEOGRAPH
+ "95 89 8CA0", #CJK UNIFIED IDEOGRAPH
+ "95 8A 8CE6", #CJK UNIFIED IDEOGRAPH
+ "95 8B 8D74", #CJK UNIFIED IDEOGRAPH
+ "95 8C 961C", #CJK UNIFIED IDEOGRAPH
+ "95 8D 9644", #CJK UNIFIED IDEOGRAPH
+ "95 8E 4FAE", #CJK UNIFIED IDEOGRAPH
+ "95 8F 64AB", #CJK UNIFIED IDEOGRAPH
+ "95 90 6B66", #CJK UNIFIED IDEOGRAPH
+ "95 91 821E", #CJK UNIFIED IDEOGRAPH
+ "95 92 8461", #CJK UNIFIED IDEOGRAPH
+ "95 93 856A", #CJK UNIFIED IDEOGRAPH
+ "95 94 90E8", #CJK UNIFIED IDEOGRAPH
+ "95 95 5C01", #CJK UNIFIED IDEOGRAPH
+ "95 96 6953", #CJK UNIFIED IDEOGRAPH
+ "95 97 98A8", #CJK UNIFIED IDEOGRAPH
+ "95 98 847A", #CJK UNIFIED IDEOGRAPH
+ "95 99 8557", #CJK UNIFIED IDEOGRAPH
+ "95 9A 4F0F", #CJK UNIFIED IDEOGRAPH
+ "95 9B 526F", #CJK UNIFIED IDEOGRAPH
+ "95 9C 5FA9", #CJK UNIFIED IDEOGRAPH
+ "95 9D 5E45", #CJK UNIFIED IDEOGRAPH
+ "95 9E 670D", #CJK UNIFIED IDEOGRAPH
+ "95 9F 798F", #CJK UNIFIED IDEOGRAPH
+ "95 A0 8179", #CJK UNIFIED IDEOGRAPH
+ "95 A1 8907", #CJK UNIFIED IDEOGRAPH
+ "95 A2 8986", #CJK UNIFIED IDEOGRAPH
+ "95 A3 6DF5", #CJK UNIFIED IDEOGRAPH
+ "95 A4 5F17", #CJK UNIFIED IDEOGRAPH
+ "95 A5 6255", #CJK UNIFIED IDEOGRAPH
+ "95 A6 6CB8", #CJK UNIFIED IDEOGRAPH
+ "95 A7 4ECF", #CJK UNIFIED IDEOGRAPH
+ "95 A8 7269", #CJK UNIFIED IDEOGRAPH
+ "95 A9 9B92", #CJK UNIFIED IDEOGRAPH
+ "95 AA 5206", #CJK UNIFIED IDEOGRAPH
+ "95 AB 543B", #CJK UNIFIED IDEOGRAPH
+ "95 AC 5674", #CJK UNIFIED IDEOGRAPH
+ "95 AD 58B3", #CJK UNIFIED IDEOGRAPH
+ "95 AE 61A4", #CJK UNIFIED IDEOGRAPH
+ "95 AF 626E", #CJK UNIFIED IDEOGRAPH
+ "95 B0 711A", #CJK UNIFIED IDEOGRAPH
+ "95 B1 596E", #CJK UNIFIED IDEOGRAPH
+ "95 B2 7C89", #CJK UNIFIED IDEOGRAPH
+ "95 B3 7CDE", #CJK UNIFIED IDEOGRAPH
+ "95 B4 7D1B", #CJK UNIFIED IDEOGRAPH
+ "95 B5 96F0", #CJK UNIFIED IDEOGRAPH
+ "95 B6 6587", #CJK UNIFIED IDEOGRAPH
+ "95 B7 805E", #CJK UNIFIED IDEOGRAPH
+ "95 B8 4E19", #CJK UNIFIED IDEOGRAPH
+ "95 B9 4F75", #CJK UNIFIED IDEOGRAPH
+ "95 BA 5175", #CJK UNIFIED IDEOGRAPH
+ "95 BB 5840", #CJK UNIFIED IDEOGRAPH
+ "95 BC 5E63", #CJK UNIFIED IDEOGRAPH
+ "95 BD 5E73", #CJK UNIFIED IDEOGRAPH
+ "95 BE 5F0A", #CJK UNIFIED IDEOGRAPH
+ "95 BF 67C4", #CJK UNIFIED IDEOGRAPH
+ "95 C0 4E26", #CJK UNIFIED IDEOGRAPH
+ "95 C1 853D", #CJK UNIFIED IDEOGRAPH
+ "95 C2 9589", #CJK UNIFIED IDEOGRAPH
+ "95 C3 965B", #CJK UNIFIED IDEOGRAPH
+ "95 C4 7C73", #CJK UNIFIED IDEOGRAPH
+ "95 C5 9801", #CJK UNIFIED IDEOGRAPH
+ "95 C6 50FB", #CJK UNIFIED IDEOGRAPH
+ "95 C7 58C1", #CJK UNIFIED IDEOGRAPH
+ "95 C8 7656", #CJK UNIFIED IDEOGRAPH
+ "95 C9 78A7", #CJK UNIFIED IDEOGRAPH
+ "95 CA 5225", #CJK UNIFIED IDEOGRAPH
+ "95 CB 77A5", #CJK UNIFIED IDEOGRAPH
+ "95 CC 8511", #CJK UNIFIED IDEOGRAPH
+ "95 CD 7B86", #CJK UNIFIED IDEOGRAPH
+ "95 CE 504F", #CJK UNIFIED IDEOGRAPH
+ "95 CF 5909", #CJK UNIFIED IDEOGRAPH
+ "95 D0 7247", #CJK UNIFIED IDEOGRAPH
+ "95 D1 7BC7", #CJK UNIFIED IDEOGRAPH
+ "95 D2 7DE8", #CJK UNIFIED IDEOGRAPH
+ "95 D3 8FBA", #CJK UNIFIED IDEOGRAPH
+ "95 D4 8FD4", #CJK UNIFIED IDEOGRAPH
+ "95 D5 904D", #CJK UNIFIED IDEOGRAPH
+ "95 D6 4FBF", #CJK UNIFIED IDEOGRAPH
+ "95 D7 52C9", #CJK UNIFIED IDEOGRAPH
+ "95 D8 5A29", #CJK UNIFIED IDEOGRAPH
+ "95 D9 5F01", #CJK UNIFIED IDEOGRAPH
+ "95 DA 97AD", #CJK UNIFIED IDEOGRAPH
+ "95 DB 4FDD", #CJK UNIFIED IDEOGRAPH
+ "95 DC 8217", #CJK UNIFIED IDEOGRAPH
+ "95 DD 92EA", #CJK UNIFIED IDEOGRAPH
+ "95 DE 5703", #CJK UNIFIED IDEOGRAPH
+ "95 DF 6355", #CJK UNIFIED IDEOGRAPH
+ "95 E0 6B69", #CJK UNIFIED IDEOGRAPH
+ "95 E1 752B", #CJK UNIFIED IDEOGRAPH
+ "95 E2 88DC", #CJK UNIFIED IDEOGRAPH
+ "95 E3 8F14", #CJK UNIFIED IDEOGRAPH
+ "95 E4 7A42", #CJK UNIFIED IDEOGRAPH
+ "95 E5 52DF", #CJK UNIFIED IDEOGRAPH
+ "95 E6 5893", #CJK UNIFIED IDEOGRAPH
+ "95 E7 6155", #CJK UNIFIED IDEOGRAPH
+ "95 E8 620A", #CJK UNIFIED IDEOGRAPH
+ "95 E9 66AE", #CJK UNIFIED IDEOGRAPH
+ "95 EA 6BCD", #CJK UNIFIED IDEOGRAPH
+ "95 EB 7C3F", #CJK UNIFIED IDEOGRAPH
+ "95 EC 83E9", #CJK UNIFIED IDEOGRAPH
+ "95 ED 5023", #CJK UNIFIED IDEOGRAPH
+ "95 EE 4FF8", #CJK UNIFIED IDEOGRAPH
+ "95 EF 5305", #CJK UNIFIED IDEOGRAPH
+ "95 F0 5446", #CJK UNIFIED IDEOGRAPH
+ "95 F1 5831", #CJK UNIFIED IDEOGRAPH
+ "95 F2 5949", #CJK UNIFIED IDEOGRAPH
+ "95 F3 5B9D", #CJK UNIFIED IDEOGRAPH
+ "95 F4 5CF0", #CJK UNIFIED IDEOGRAPH
+ "95 F5 5CEF", #CJK UNIFIED IDEOGRAPH
+ "95 F6 5D29", #CJK UNIFIED IDEOGRAPH
+ "95 F7 5E96", #CJK UNIFIED IDEOGRAPH
+ "95 F8 62B1", #CJK UNIFIED IDEOGRAPH
+ "95 F9 6367", #CJK UNIFIED IDEOGRAPH
+ "95 FA 653E", #CJK UNIFIED IDEOGRAPH
+ "95 FB 65B9", #CJK UNIFIED IDEOGRAPH
+ "95 FC 670B", #CJK UNIFIED IDEOGRAPH
+ "96 40 6CD5", #CJK UNIFIED IDEOGRAPH
+ "96 41 6CE1", #CJK UNIFIED IDEOGRAPH
+ "96 42 70F9", #CJK UNIFIED IDEOGRAPH
+ "96 43 7832", #CJK UNIFIED IDEOGRAPH
+ "96 44 7E2B", #CJK UNIFIED IDEOGRAPH
+ "96 45 80DE", #CJK UNIFIED IDEOGRAPH
+ "96 46 82B3", #CJK UNIFIED IDEOGRAPH
+ "96 47 840C", #CJK UNIFIED IDEOGRAPH
+ "96 48 84EC", #CJK UNIFIED IDEOGRAPH
+ "96 49 8702", #CJK UNIFIED IDEOGRAPH
+ "96 4A 8912", #CJK UNIFIED IDEOGRAPH
+ "96 4B 8A2A", #CJK UNIFIED IDEOGRAPH
+ "96 4C 8C4A", #CJK UNIFIED IDEOGRAPH
+ "96 4D 90A6", #CJK UNIFIED IDEOGRAPH
+ "96 4E 92D2", #CJK UNIFIED IDEOGRAPH
+ "96 4F 98FD", #CJK UNIFIED IDEOGRAPH
+ "96 50 9CF3", #CJK UNIFIED IDEOGRAPH
+ "96 51 9D6C", #CJK UNIFIED IDEOGRAPH
+ "96 52 4E4F", #CJK UNIFIED IDEOGRAPH
+ "96 53 4EA1", #CJK UNIFIED IDEOGRAPH
+ "96 54 508D", #CJK UNIFIED IDEOGRAPH
+ "96 55 5256", #CJK UNIFIED IDEOGRAPH
+ "96 56 574A", #CJK UNIFIED IDEOGRAPH
+ "96 57 59A8", #CJK UNIFIED IDEOGRAPH
+ "96 58 5E3D", #CJK UNIFIED IDEOGRAPH
+ "96 59 5FD8", #CJK UNIFIED IDEOGRAPH
+ "96 5A 5FD9", #CJK UNIFIED IDEOGRAPH
+ "96 5B 623F", #CJK UNIFIED IDEOGRAPH
+ "96 5C 66B4", #CJK UNIFIED IDEOGRAPH
+ "96 5D 671B", #CJK UNIFIED IDEOGRAPH
+ "96 5E 67D0", #CJK UNIFIED IDEOGRAPH
+ "96 5F 68D2", #CJK UNIFIED IDEOGRAPH
+ "96 60 5192", #CJK UNIFIED IDEOGRAPH
+ "96 61 7D21", #CJK UNIFIED IDEOGRAPH
+ "96 62 80AA", #CJK UNIFIED IDEOGRAPH
+ "96 63 81A8", #CJK UNIFIED IDEOGRAPH
+ "96 64 8B00", #CJK UNIFIED IDEOGRAPH
+ "96 65 8C8C", #CJK UNIFIED IDEOGRAPH
+ "96 66 8CBF", #CJK UNIFIED IDEOGRAPH
+ "96 67 927E", #CJK UNIFIED IDEOGRAPH
+ "96 68 9632", #CJK UNIFIED IDEOGRAPH
+ "96 69 5420", #CJK UNIFIED IDEOGRAPH
+ "96 6A 982C", #CJK UNIFIED IDEOGRAPH
+ "96 6B 5317", #CJK UNIFIED IDEOGRAPH
+ "96 6C 50D5", #CJK UNIFIED IDEOGRAPH
+ "96 6D 535C", #CJK UNIFIED IDEOGRAPH
+ "96 6E 58A8", #CJK UNIFIED IDEOGRAPH
+ "96 6F 64B2", #CJK UNIFIED IDEOGRAPH
+ "96 70 6734", #CJK UNIFIED IDEOGRAPH
+ "96 71 7267", #CJK UNIFIED IDEOGRAPH
+ "96 72 7766", #CJK UNIFIED IDEOGRAPH
+ "96 73 7A46", #CJK UNIFIED IDEOGRAPH
+ "96 74 91E6", #CJK UNIFIED IDEOGRAPH
+ "96 75 52C3", #CJK UNIFIED IDEOGRAPH
+ "96 76 6CA1", #CJK UNIFIED IDEOGRAPH
+ "96 77 6B86", #CJK UNIFIED IDEOGRAPH
+ "96 78 5800", #CJK UNIFIED IDEOGRAPH
+ "96 79 5E4C", #CJK UNIFIED IDEOGRAPH
+ "96 7A 5954", #CJK UNIFIED IDEOGRAPH
+ "96 7B 672C", #CJK UNIFIED IDEOGRAPH
+ "96 7C 7FFB", #CJK UNIFIED IDEOGRAPH
+ "96 7D 51E1", #CJK UNIFIED IDEOGRAPH
+ "96 7E 76C6", #CJK UNIFIED IDEOGRAPH
+ "96 80 6469", #CJK UNIFIED IDEOGRAPH
+ "96 81 78E8", #CJK UNIFIED IDEOGRAPH
+ "96 82 9B54", #CJK UNIFIED IDEOGRAPH
+ "96 83 9EBB", #CJK UNIFIED IDEOGRAPH
+ "96 84 57CB", #CJK UNIFIED IDEOGRAPH
+ "96 85 59B9", #CJK UNIFIED IDEOGRAPH
+ "96 86 6627", #CJK UNIFIED IDEOGRAPH
+ "96 87 679A", #CJK UNIFIED IDEOGRAPH
+ "96 88 6BCE", #CJK UNIFIED IDEOGRAPH
+ "96 89 54E9", #CJK UNIFIED IDEOGRAPH
+ "96 8A 69D9", #CJK UNIFIED IDEOGRAPH
+ "96 8B 5E55", #CJK UNIFIED IDEOGRAPH
+ "96 8C 819C", #CJK UNIFIED IDEOGRAPH
+ "96 8D 6795", #CJK UNIFIED IDEOGRAPH
+ "96 8E 9BAA", #CJK UNIFIED IDEOGRAPH
+ "96 8F 67FE", #CJK UNIFIED IDEOGRAPH
+ "96 90 9C52", #CJK UNIFIED IDEOGRAPH
+ "96 91 685D", #CJK UNIFIED IDEOGRAPH
+ "96 92 4EA6", #CJK UNIFIED IDEOGRAPH
+ "96 93 4FE3", #CJK UNIFIED IDEOGRAPH
+ "96 94 53C8", #CJK UNIFIED IDEOGRAPH
+ "96 95 62B9", #CJK UNIFIED IDEOGRAPH
+ "96 96 672B", #CJK UNIFIED IDEOGRAPH
+ "96 97 6CAB", #CJK UNIFIED IDEOGRAPH
+ "96 98 8FC4", #CJK UNIFIED IDEOGRAPH
+ "96 99 4FAD", #CJK UNIFIED IDEOGRAPH
+ "96 9A 7E6D", #CJK UNIFIED IDEOGRAPH
+ "96 9B 9EBF", #CJK UNIFIED IDEOGRAPH
+ "96 9C 4E07", #CJK UNIFIED IDEOGRAPH
+ "96 9D 6162", #CJK UNIFIED IDEOGRAPH
+ "96 9E 6E80", #CJK UNIFIED IDEOGRAPH
+ "96 9F 6F2B", #CJK UNIFIED IDEOGRAPH
+ "96 A0 8513", #CJK UNIFIED IDEOGRAPH
+ "96 A1 5473", #CJK UNIFIED IDEOGRAPH
+ "96 A2 672A", #CJK UNIFIED IDEOGRAPH
+ "96 A3 9B45", #CJK UNIFIED IDEOGRAPH
+ "96 A4 5DF3", #CJK UNIFIED IDEOGRAPH
+ "96 A5 7B95", #CJK UNIFIED IDEOGRAPH
+ "96 A6 5CAC", #CJK UNIFIED IDEOGRAPH
+ "96 A7 5BC6", #CJK UNIFIED IDEOGRAPH
+ "96 A8 871C", #CJK UNIFIED IDEOGRAPH
+ "96 A9 6E4A", #CJK UNIFIED IDEOGRAPH
+ "96 AA 84D1", #CJK UNIFIED IDEOGRAPH
+ "96 AB 7A14", #CJK UNIFIED IDEOGRAPH
+ "96 AC 8108", #CJK UNIFIED IDEOGRAPH
+ "96 AD 5999", #CJK UNIFIED IDEOGRAPH
+ "96 AE 7C8D", #CJK UNIFIED IDEOGRAPH
+ "96 AF 6C11", #CJK UNIFIED IDEOGRAPH
+ "96 B0 7720", #CJK UNIFIED IDEOGRAPH
+ "96 B1 52D9", #CJK UNIFIED IDEOGRAPH
+ "96 B2 5922", #CJK UNIFIED IDEOGRAPH
+ "96 B3 7121", #CJK UNIFIED IDEOGRAPH
+ "96 B4 725F", #CJK UNIFIED IDEOGRAPH
+ "96 B5 77DB", #CJK UNIFIED IDEOGRAPH
+ "96 B6 9727", #CJK UNIFIED IDEOGRAPH
+ "96 B7 9D61", #CJK UNIFIED IDEOGRAPH
+ "96 B8 690B", #CJK UNIFIED IDEOGRAPH
+ "96 B9 5A7F", #CJK UNIFIED IDEOGRAPH
+ "96 BA 5A18", #CJK UNIFIED IDEOGRAPH
+ "96 BB 51A5", #CJK UNIFIED IDEOGRAPH
+ "96 BC 540D", #CJK UNIFIED IDEOGRAPH
+ "96 BD 547D", #CJK UNIFIED IDEOGRAPH
+ "96 BE 660E", #CJK UNIFIED IDEOGRAPH
+ "96 BF 76DF", #CJK UNIFIED IDEOGRAPH
+ "96 C0 8FF7", #CJK UNIFIED IDEOGRAPH
+ "96 C1 9298", #CJK UNIFIED IDEOGRAPH
+ "96 C2 9CF4", #CJK UNIFIED IDEOGRAPH
+ "96 C3 59EA", #CJK UNIFIED IDEOGRAPH
+ "96 C4 725D", #CJK UNIFIED IDEOGRAPH
+ "96 C5 6EC5", #CJK UNIFIED IDEOGRAPH
+ "96 C6 514D", #CJK UNIFIED IDEOGRAPH
+ "96 C7 68C9", #CJK UNIFIED IDEOGRAPH
+ "96 C8 7DBF", #CJK UNIFIED IDEOGRAPH
+ "96 C9 7DEC", #CJK UNIFIED IDEOGRAPH
+ "96 CA 9762", #CJK UNIFIED IDEOGRAPH
+ "96 CB 9EBA", #CJK UNIFIED IDEOGRAPH
+ "96 CC 6478", #CJK UNIFIED IDEOGRAPH
+ "96 CD 6A21", #CJK UNIFIED IDEOGRAPH
+ "96 CE 8302", #CJK UNIFIED IDEOGRAPH
+ "96 CF 5984", #CJK UNIFIED IDEOGRAPH
+ "96 D0 5B5F", #CJK UNIFIED IDEOGRAPH
+ "96 D1 6BDB", #CJK UNIFIED IDEOGRAPH
+ "96 D2 731B", #CJK UNIFIED IDEOGRAPH
+ "96 D3 76F2", #CJK UNIFIED IDEOGRAPH
+ "96 D4 7DB2", #CJK UNIFIED IDEOGRAPH
+ "96 D5 8017", #CJK UNIFIED IDEOGRAPH
+ "96 D6 8499", #CJK UNIFIED IDEOGRAPH
+ "96 D7 5132", #CJK UNIFIED IDEOGRAPH
+ "96 D8 6728", #CJK UNIFIED IDEOGRAPH
+ "96 D9 9ED9", #CJK UNIFIED IDEOGRAPH
+ "96 DA 76EE", #CJK UNIFIED IDEOGRAPH
+ "96 DB 6762", #CJK UNIFIED IDEOGRAPH
+ "96 DC 52FF", #CJK UNIFIED IDEOGRAPH
+ "96 DD 9905", #CJK UNIFIED IDEOGRAPH
+ "96 DE 5C24", #CJK UNIFIED IDEOGRAPH
+ "96 DF 623B", #CJK UNIFIED IDEOGRAPH
+ "96 E0 7C7E", #CJK UNIFIED IDEOGRAPH
+ "96 E1 8CB0", #CJK UNIFIED IDEOGRAPH
+ "96 E2 554F", #CJK UNIFIED IDEOGRAPH
+ "96 E3 60B6", #CJK UNIFIED IDEOGRAPH
+ "96 E4 7D0B", #CJK UNIFIED IDEOGRAPH
+ "96 E5 9580", #CJK UNIFIED IDEOGRAPH
+ "96 E6 5301", #CJK UNIFIED IDEOGRAPH
+ "96 E7 4E5F", #CJK UNIFIED IDEOGRAPH
+ "96 E8 51B6", #CJK UNIFIED IDEOGRAPH
+ "96 E9 591C", #CJK UNIFIED IDEOGRAPH
+ "96 EA 723A", #CJK UNIFIED IDEOGRAPH
+ "96 EB 8036", #CJK UNIFIED IDEOGRAPH
+ "96 EC 91CE", #CJK UNIFIED IDEOGRAPH
+ "96 ED 5F25", #CJK UNIFIED IDEOGRAPH
+ "96 EE 77E2", #CJK UNIFIED IDEOGRAPH
+ "96 EF 5384", #CJK UNIFIED IDEOGRAPH
+ "96 F0 5F79", #CJK UNIFIED IDEOGRAPH
+ "96 F1 7D04", #CJK UNIFIED IDEOGRAPH
+ "96 F2 85AC", #CJK UNIFIED IDEOGRAPH
+ "96 F3 8A33", #CJK UNIFIED IDEOGRAPH
+ "96 F4 8E8D", #CJK UNIFIED IDEOGRAPH
+ "96 F5 9756", #CJK UNIFIED IDEOGRAPH
+ "96 F6 67F3", #CJK UNIFIED IDEOGRAPH
+ "96 F7 85AE", #CJK UNIFIED IDEOGRAPH
+ "96 F8 9453", #CJK UNIFIED IDEOGRAPH
+ "96 F9 6109", #CJK UNIFIED IDEOGRAPH
+ "96 FA 6108", #CJK UNIFIED IDEOGRAPH
+ "96 FB 6CB9", #CJK UNIFIED IDEOGRAPH
+ "96 FC 7652", #CJK UNIFIED IDEOGRAPH
+ "97 40 8AED", #CJK UNIFIED IDEOGRAPH
+ "97 41 8F38", #CJK UNIFIED IDEOGRAPH
+ "97 42 552F", #CJK UNIFIED IDEOGRAPH
+ "97 43 4F51", #CJK UNIFIED IDEOGRAPH
+ "97 44 512A", #CJK UNIFIED IDEOGRAPH
+ "97 45 52C7", #CJK UNIFIED IDEOGRAPH
+ "97 46 53CB", #CJK UNIFIED IDEOGRAPH
+ "97 47 5BA5", #CJK UNIFIED IDEOGRAPH
+ "97 48 5E7D", #CJK UNIFIED IDEOGRAPH
+ "97 49 60A0", #CJK UNIFIED IDEOGRAPH
+ "97 4A 6182", #CJK UNIFIED IDEOGRAPH
+ "97 4B 63D6", #CJK UNIFIED IDEOGRAPH
+ "97 4C 6709", #CJK UNIFIED IDEOGRAPH
+ "97 4D 67DA", #CJK UNIFIED IDEOGRAPH
+ "97 4E 6E67", #CJK UNIFIED IDEOGRAPH
+ "97 4F 6D8C", #CJK UNIFIED IDEOGRAPH
+ "97 50 7336", #CJK UNIFIED IDEOGRAPH
+ "97 51 7337", #CJK UNIFIED IDEOGRAPH
+ "97 52 7531", #CJK UNIFIED IDEOGRAPH
+ "97 53 7950", #CJK UNIFIED IDEOGRAPH
+ "97 54 88D5", #CJK UNIFIED IDEOGRAPH
+ "97 55 8A98", #CJK UNIFIED IDEOGRAPH
+ "97 56 904A", #CJK UNIFIED IDEOGRAPH
+ "97 57 9091", #CJK UNIFIED IDEOGRAPH
+ "97 58 90F5", #CJK UNIFIED IDEOGRAPH
+ "97 59 96C4", #CJK UNIFIED IDEOGRAPH
+ "97 5A 878D", #CJK UNIFIED IDEOGRAPH
+ "97 5B 5915", #CJK UNIFIED IDEOGRAPH
+ "97 5C 4E88", #CJK UNIFIED IDEOGRAPH
+ "97 5D 4F59", #CJK UNIFIED IDEOGRAPH
+ "97 5E 4E0E", #CJK UNIFIED IDEOGRAPH
+ "97 5F 8A89", #CJK UNIFIED IDEOGRAPH
+ "97 60 8F3F", #CJK UNIFIED IDEOGRAPH
+ "97 61 9810", #CJK UNIFIED IDEOGRAPH
+ "97 62 50AD", #CJK UNIFIED IDEOGRAPH
+ "97 63 5E7C", #CJK UNIFIED IDEOGRAPH
+ "97 64 5996", #CJK UNIFIED IDEOGRAPH
+ "97 65 5BB9", #CJK UNIFIED IDEOGRAPH
+ "97 66 5EB8", #CJK UNIFIED IDEOGRAPH
+ "97 67 63DA", #CJK UNIFIED IDEOGRAPH
+ "97 68 63FA", #CJK UNIFIED IDEOGRAPH
+ "97 69 64C1", #CJK UNIFIED IDEOGRAPH
+ "97 6A 66DC", #CJK UNIFIED IDEOGRAPH
+ "97 6B 694A", #CJK UNIFIED IDEOGRAPH
+ "97 6C 69D8", #CJK UNIFIED IDEOGRAPH
+ "97 6D 6D0B", #CJK UNIFIED IDEOGRAPH
+ "97 6E 6EB6", #CJK UNIFIED IDEOGRAPH
+ "97 6F 7194", #CJK UNIFIED IDEOGRAPH
+ "97 70 7528", #CJK UNIFIED IDEOGRAPH
+ "97 71 7AAF", #CJK UNIFIED IDEOGRAPH
+ "97 72 7F8A", #CJK UNIFIED IDEOGRAPH
+ "97 73 8000", #CJK UNIFIED IDEOGRAPH
+ "97 74 8449", #CJK UNIFIED IDEOGRAPH
+ "97 75 84C9", #CJK UNIFIED IDEOGRAPH
+ "97 76 8981", #CJK UNIFIED IDEOGRAPH
+ "97 77 8B21", #CJK UNIFIED IDEOGRAPH
+ "97 78 8E0A", #CJK UNIFIED IDEOGRAPH
+ "97 79 9065", #CJK UNIFIED IDEOGRAPH
+ "97 7A 967D", #CJK UNIFIED IDEOGRAPH
+ "97 7B 990A", #CJK UNIFIED IDEOGRAPH
+ "97 7C 617E", #CJK UNIFIED IDEOGRAPH
+ "97 7D 6291", #CJK UNIFIED IDEOGRAPH
+ "97 7E 6B32", #CJK UNIFIED IDEOGRAPH
+ "97 80 6C83", #CJK UNIFIED IDEOGRAPH
+ "97 81 6D74", #CJK UNIFIED IDEOGRAPH
+ "97 82 7FCC", #CJK UNIFIED IDEOGRAPH
+ "97 83 7FFC", #CJK UNIFIED IDEOGRAPH
+ "97 84 6DC0", #CJK UNIFIED IDEOGRAPH
+ "97 85 7F85", #CJK UNIFIED IDEOGRAPH
+ "97 86 87BA", #CJK UNIFIED IDEOGRAPH
+ "97 87 88F8", #CJK UNIFIED IDEOGRAPH
+ "97 88 6765", #CJK UNIFIED IDEOGRAPH
+ "97 89 83B1", #CJK UNIFIED IDEOGRAPH
+ "97 8A 983C", #CJK UNIFIED IDEOGRAPH
+ "97 8B 96F7", #CJK UNIFIED IDEOGRAPH
+ "97 8C 6D1B", #CJK UNIFIED IDEOGRAPH
+ "97 8D 7D61", #CJK UNIFIED IDEOGRAPH
+ "97 8E 843D", #CJK UNIFIED IDEOGRAPH
+ "97 8F 916A", #CJK UNIFIED IDEOGRAPH
+ "97 90 4E71", #CJK UNIFIED IDEOGRAPH
+ "97 91 5375", #CJK UNIFIED IDEOGRAPH
+ "97 92 5D50", #CJK UNIFIED IDEOGRAPH
+ "97 93 6B04", #CJK UNIFIED IDEOGRAPH
+ "97 94 6FEB", #CJK UNIFIED IDEOGRAPH
+ "97 95 85CD", #CJK UNIFIED IDEOGRAPH
+ "97 96 862D", #CJK UNIFIED IDEOGRAPH
+ "97 97 89A7", #CJK UNIFIED IDEOGRAPH
+ "97 98 5229", #CJK UNIFIED IDEOGRAPH
+ "97 99 540F", #CJK UNIFIED IDEOGRAPH
+ "97 9A 5C65", #CJK UNIFIED IDEOGRAPH
+ "97 9B 674E", #CJK UNIFIED IDEOGRAPH
+ "97 9C 68A8", #CJK UNIFIED IDEOGRAPH
+ "97 9D 7406", #CJK UNIFIED IDEOGRAPH
+ "97 9E 7483", #CJK UNIFIED IDEOGRAPH
+ "97 9F 75E2", #CJK UNIFIED IDEOGRAPH
+ "97 A0 88CF", #CJK UNIFIED IDEOGRAPH
+ "97 A1 88E1", #CJK UNIFIED IDEOGRAPH
+ "97 A2 91CC", #CJK UNIFIED IDEOGRAPH
+ "97 A3 96E2", #CJK UNIFIED IDEOGRAPH
+ "97 A4 9678", #CJK UNIFIED IDEOGRAPH
+ "97 A5 5F8B", #CJK UNIFIED IDEOGRAPH
+ "97 A6 7387", #CJK UNIFIED IDEOGRAPH
+ "97 A7 7ACB", #CJK UNIFIED IDEOGRAPH
+ "97 A8 844E", #CJK UNIFIED IDEOGRAPH
+ "97 A9 63A0", #CJK UNIFIED IDEOGRAPH
+ "97 AA 7565", #CJK UNIFIED IDEOGRAPH
+ "97 AB 5289", #CJK UNIFIED IDEOGRAPH
+ "97 AC 6D41", #CJK UNIFIED IDEOGRAPH
+ "97 AD 6E9C", #CJK UNIFIED IDEOGRAPH
+ "97 AE 7409", #CJK UNIFIED IDEOGRAPH
+ "97 AF 7559", #CJK UNIFIED IDEOGRAPH
+ "97 B0 786B", #CJK UNIFIED IDEOGRAPH
+ "97 B1 7C92", #CJK UNIFIED IDEOGRAPH
+ "97 B2 9686", #CJK UNIFIED IDEOGRAPH
+ "97 B3 7ADC", #CJK UNIFIED IDEOGRAPH
+ "97 B4 9F8D", #CJK UNIFIED IDEOGRAPH
+ "97 B5 4FB6", #CJK UNIFIED IDEOGRAPH
+ "97 B6 616E", #CJK UNIFIED IDEOGRAPH
+ "97 B7 65C5", #CJK UNIFIED IDEOGRAPH
+ "97 B8 865C", #CJK UNIFIED IDEOGRAPH
+ "97 B9 4E86", #CJK UNIFIED IDEOGRAPH
+ "97 BA 4EAE", #CJK UNIFIED IDEOGRAPH
+ "97 BB 50DA", #CJK UNIFIED IDEOGRAPH
+ "97 BC 4E21", #CJK UNIFIED IDEOGRAPH
+ "97 BD 51CC", #CJK UNIFIED IDEOGRAPH
+ "97 BE 5BEE", #CJK UNIFIED IDEOGRAPH
+ "97 BF 6599", #CJK UNIFIED IDEOGRAPH
+ "97 C0 6881", #CJK UNIFIED IDEOGRAPH
+ "97 C1 6DBC", #CJK UNIFIED IDEOGRAPH
+ "97 C2 731F", #CJK UNIFIED IDEOGRAPH
+ "97 C3 7642", #CJK UNIFIED IDEOGRAPH
+ "97 C4 77AD", #CJK UNIFIED IDEOGRAPH
+ "97 C5 7A1C", #CJK UNIFIED IDEOGRAPH
+ "97 C6 7CE7", #CJK UNIFIED IDEOGRAPH
+ "97 C7 826F", #CJK UNIFIED IDEOGRAPH
+ "97 C8 8AD2", #CJK UNIFIED IDEOGRAPH
+ "97 C9 907C", #CJK UNIFIED IDEOGRAPH
+ "97 CA 91CF", #CJK UNIFIED IDEOGRAPH
+ "97 CB 9675", #CJK UNIFIED IDEOGRAPH
+ "97 CC 9818", #CJK UNIFIED IDEOGRAPH
+ "97 CD 529B", #CJK UNIFIED IDEOGRAPH
+ "97 CE 7DD1", #CJK UNIFIED IDEOGRAPH
+ "97 CF 502B", #CJK UNIFIED IDEOGRAPH
+ "97 D0 5398", #CJK UNIFIED IDEOGRAPH
+ "97 D1 6797", #CJK UNIFIED IDEOGRAPH
+ "97 D2 6DCB", #CJK UNIFIED IDEOGRAPH
+ "97 D3 71D0", #CJK UNIFIED IDEOGRAPH
+ "97 D4 7433", #CJK UNIFIED IDEOGRAPH
+ "97 D5 81E8", #CJK UNIFIED IDEOGRAPH
+ "97 D6 8F2A", #CJK UNIFIED IDEOGRAPH
+ "97 D7 96A3", #CJK UNIFIED IDEOGRAPH
+ "97 D8 9C57", #CJK UNIFIED IDEOGRAPH
+ "97 D9 9E9F", #CJK UNIFIED IDEOGRAPH
+ "97 DA 7460", #CJK UNIFIED IDEOGRAPH
+ "97 DB 5841", #CJK UNIFIED IDEOGRAPH
+ "97 DC 6D99", #CJK UNIFIED IDEOGRAPH
+ "97 DD 7D2F", #CJK UNIFIED IDEOGRAPH
+ "97 DE 985E", #CJK UNIFIED IDEOGRAPH
+ "97 DF 4EE4", #CJK UNIFIED IDEOGRAPH
+ "97 E0 4F36", #CJK UNIFIED IDEOGRAPH
+ "97 E1 4F8B", #CJK UNIFIED IDEOGRAPH
+ "97 E2 51B7", #CJK UNIFIED IDEOGRAPH
+ "97 E3 52B1", #CJK UNIFIED IDEOGRAPH
+ "97 E4 5DBA", #CJK UNIFIED IDEOGRAPH
+ "97 E5 601C", #CJK UNIFIED IDEOGRAPH
+ "97 E6 73B2", #CJK UNIFIED IDEOGRAPH
+ "97 E7 793C", #CJK UNIFIED IDEOGRAPH
+ "97 E8 82D3", #CJK UNIFIED IDEOGRAPH
+ "97 E9 9234", #CJK UNIFIED IDEOGRAPH
+ "97 EA 96B7", #CJK UNIFIED IDEOGRAPH
+ "97 EB 96F6", #CJK UNIFIED IDEOGRAPH
+ "97 EC 970A", #CJK UNIFIED IDEOGRAPH
+ "97 ED 9E97", #CJK UNIFIED IDEOGRAPH
+ "97 EE 9F62", #CJK UNIFIED IDEOGRAPH
+ "97 EF 66A6", #CJK UNIFIED IDEOGRAPH
+ "97 F0 6B74", #CJK UNIFIED IDEOGRAPH
+ "97 F1 5217", #CJK UNIFIED IDEOGRAPH
+ "97 F2 52A3", #CJK UNIFIED IDEOGRAPH
+ "97 F3 70C8", #CJK UNIFIED IDEOGRAPH
+ "97 F4 88C2", #CJK UNIFIED IDEOGRAPH
+ "97 F5 5EC9", #CJK UNIFIED IDEOGRAPH
+ "97 F6 604B", #CJK UNIFIED IDEOGRAPH
+ "97 F7 6190", #CJK UNIFIED IDEOGRAPH
+ "97 F8 6F23", #CJK UNIFIED IDEOGRAPH
+ "97 F9 7149", #CJK UNIFIED IDEOGRAPH
+ "97 FA 7C3E", #CJK UNIFIED IDEOGRAPH
+ "97 FB 7DF4", #CJK UNIFIED IDEOGRAPH
+ "97 FC 806F", #CJK UNIFIED IDEOGRAPH
+ "98 40 84EE", #CJK UNIFIED IDEOGRAPH
+ "98 41 9023", #CJK UNIFIED IDEOGRAPH
+ "98 42 932C", #CJK UNIFIED IDEOGRAPH
+ "98 43 5442", #CJK UNIFIED IDEOGRAPH
+ "98 44 9B6F", #CJK UNIFIED IDEOGRAPH
+ "98 45 6AD3", #CJK UNIFIED IDEOGRAPH
+ "98 46 7089", #CJK UNIFIED IDEOGRAPH
+ "98 47 8CC2", #CJK UNIFIED IDEOGRAPH
+ "98 48 8DEF", #CJK UNIFIED IDEOGRAPH
+ "98 49 9732", #CJK UNIFIED IDEOGRAPH
+ "98 4A 52B4", #CJK UNIFIED IDEOGRAPH
+ "98 4B 5A41", #CJK UNIFIED IDEOGRAPH
+ "98 4C 5ECA", #CJK UNIFIED IDEOGRAPH
+ "98 4D 5F04", #CJK UNIFIED IDEOGRAPH
+ "98 4E 6717", #CJK UNIFIED IDEOGRAPH
+ "98 4F 697C", #CJK UNIFIED IDEOGRAPH
+ "98 50 6994", #CJK UNIFIED IDEOGRAPH
+ "98 51 6D6A", #CJK UNIFIED IDEOGRAPH
+ "98 52 6F0F", #CJK UNIFIED IDEOGRAPH
+ "98 53 7262", #CJK UNIFIED IDEOGRAPH
+ "98 54 72FC", #CJK UNIFIED IDEOGRAPH
+ "98 55 7BED", #CJK UNIFIED IDEOGRAPH
+ "98 56 8001", #CJK UNIFIED IDEOGRAPH
+ "98 57 807E", #CJK UNIFIED IDEOGRAPH
+ "98 58 874B", #CJK UNIFIED IDEOGRAPH
+ "98 59 90CE", #CJK UNIFIED IDEOGRAPH
+ "98 5A 516D", #CJK UNIFIED IDEOGRAPH
+ "98 5B 9E93", #CJK UNIFIED IDEOGRAPH
+ "98 5C 7984", #CJK UNIFIED IDEOGRAPH
+ "98 5D 808B", #CJK UNIFIED IDEOGRAPH
+ "98 5E 9332", #CJK UNIFIED IDEOGRAPH
+ "98 5F 8AD6", #CJK UNIFIED IDEOGRAPH
+ "98 60 502D", #CJK UNIFIED IDEOGRAPH
+ "98 61 548C", #CJK UNIFIED IDEOGRAPH
+ "98 62 8A71", #CJK UNIFIED IDEOGRAPH
+ "98 63 6B6A", #CJK UNIFIED IDEOGRAPH
+ "98 64 8CC4", #CJK UNIFIED IDEOGRAPH
+ "98 65 8107", #CJK UNIFIED IDEOGRAPH
+ "98 66 60D1", #CJK UNIFIED IDEOGRAPH
+ "98 67 67A0", #CJK UNIFIED IDEOGRAPH
+ "98 68 9DF2", #CJK UNIFIED IDEOGRAPH
+ "98 69 4E99", #CJK UNIFIED IDEOGRAPH
+ "98 6A 4E98", #CJK UNIFIED IDEOGRAPH
+ "98 6B 9C10", #CJK UNIFIED IDEOGRAPH
+ "98 6C 8A6B", #CJK UNIFIED IDEOGRAPH
+ "98 6D 85C1", #CJK UNIFIED IDEOGRAPH
+ "98 6E 8568", #CJK UNIFIED IDEOGRAPH
+ "98 6F 6900", #CJK UNIFIED IDEOGRAPH
+ "98 70 6E7E", #CJK UNIFIED IDEOGRAPH
+ "98 71 7897", #CJK UNIFIED IDEOGRAPH
+ "98 72 8155", #CJK UNIFIED IDEOGRAPH
+ "98 9F 5F0C", #CJK UNIFIED IDEOGRAPH
+ "98 A0 4E10", #CJK UNIFIED IDEOGRAPH
+ "98 A1 4E15", #CJK UNIFIED IDEOGRAPH
+ "98 A2 4E2A", #CJK UNIFIED IDEOGRAPH
+ "98 A3 4E31", #CJK UNIFIED IDEOGRAPH
+ "98 A4 4E36", #CJK UNIFIED IDEOGRAPH
+ "98 A5 4E3C", #CJK UNIFIED IDEOGRAPH
+ "98 A6 4E3F", #CJK UNIFIED IDEOGRAPH
+ "98 A7 4E42", #CJK UNIFIED IDEOGRAPH
+ "98 A8 4E56", #CJK UNIFIED IDEOGRAPH
+ "98 A9 4E58", #CJK UNIFIED IDEOGRAPH
+ "98 AA 4E82", #CJK UNIFIED IDEOGRAPH
+ "98 AB 4E85", #CJK UNIFIED IDEOGRAPH
+ "98 AC 8C6B", #CJK UNIFIED IDEOGRAPH
+ "98 AD 4E8A", #CJK UNIFIED IDEOGRAPH
+ "98 AE 8212", #CJK UNIFIED IDEOGRAPH
+ "98 AF 5F0D", #CJK UNIFIED IDEOGRAPH
+ "98 B0 4E8E", #CJK UNIFIED IDEOGRAPH
+ "98 B1 4E9E", #CJK UNIFIED IDEOGRAPH
+ "98 B2 4E9F", #CJK UNIFIED IDEOGRAPH
+ "98 B3 4EA0", #CJK UNIFIED IDEOGRAPH
+ "98 B4 4EA2", #CJK UNIFIED IDEOGRAPH
+ "98 B5 4EB0", #CJK UNIFIED IDEOGRAPH
+ "98 B6 4EB3", #CJK UNIFIED IDEOGRAPH
+ "98 B7 4EB6", #CJK UNIFIED IDEOGRAPH
+ "98 B8 4ECE", #CJK UNIFIED IDEOGRAPH
+ "98 B9 4ECD", #CJK UNIFIED IDEOGRAPH
+ "98 BA 4EC4", #CJK UNIFIED IDEOGRAPH
+ "98 BB 4EC6", #CJK UNIFIED IDEOGRAPH
+ "98 BC 4EC2", #CJK UNIFIED IDEOGRAPH
+ "98 BD 4ED7", #CJK UNIFIED IDEOGRAPH
+ "98 BE 4EDE", #CJK UNIFIED IDEOGRAPH
+ "98 BF 4EED", #CJK UNIFIED IDEOGRAPH
+ "98 C0 4EDF", #CJK UNIFIED IDEOGRAPH
+ "98 C1 4EF7", #CJK UNIFIED IDEOGRAPH
+ "98 C2 4F09", #CJK UNIFIED IDEOGRAPH
+ "98 C3 4F5A", #CJK UNIFIED IDEOGRAPH
+ "98 C4 4F30", #CJK UNIFIED IDEOGRAPH
+ "98 C5 4F5B", #CJK UNIFIED IDEOGRAPH
+ "98 C6 4F5D", #CJK UNIFIED IDEOGRAPH
+ "98 C7 4F57", #CJK UNIFIED IDEOGRAPH
+ "98 C8 4F47", #CJK UNIFIED IDEOGRAPH
+ "98 C9 4F76", #CJK UNIFIED IDEOGRAPH
+ "98 CA 4F88", #CJK UNIFIED IDEOGRAPH
+ "98 CB 4F8F", #CJK UNIFIED IDEOGRAPH
+ "98 CC 4F98", #CJK UNIFIED IDEOGRAPH
+ "98 CD 4F7B", #CJK UNIFIED IDEOGRAPH
+ "98 CE 4F69", #CJK UNIFIED IDEOGRAPH
+ "98 CF 4F70", #CJK UNIFIED IDEOGRAPH
+ "98 D0 4F91", #CJK UNIFIED IDEOGRAPH
+ "98 D1 4F6F", #CJK UNIFIED IDEOGRAPH
+ "98 D2 4F86", #CJK UNIFIED IDEOGRAPH
+ "98 D3 4F96", #CJK UNIFIED IDEOGRAPH
+ "98 D4 5118", #CJK UNIFIED IDEOGRAPH
+ "98 D5 4FD4", #CJK UNIFIED IDEOGRAPH
+ "98 D6 4FDF", #CJK UNIFIED IDEOGRAPH
+ "98 D7 4FCE", #CJK UNIFIED IDEOGRAPH
+ "98 D8 4FD8", #CJK UNIFIED IDEOGRAPH
+ "98 D9 4FDB", #CJK UNIFIED IDEOGRAPH
+ "98 DA 4FD1", #CJK UNIFIED IDEOGRAPH
+ "98 DB 4FDA", #CJK UNIFIED IDEOGRAPH
+ "98 DC 4FD0", #CJK UNIFIED IDEOGRAPH
+ "98 DD 4FE4", #CJK UNIFIED IDEOGRAPH
+ "98 DE 4FE5", #CJK UNIFIED IDEOGRAPH
+ "98 DF 501A", #CJK UNIFIED IDEOGRAPH
+ "98 E0 5028", #CJK UNIFIED IDEOGRAPH
+ "98 E1 5014", #CJK UNIFIED IDEOGRAPH
+ "98 E2 502A", #CJK UNIFIED IDEOGRAPH
+ "98 E3 5025", #CJK UNIFIED IDEOGRAPH
+ "98 E4 5005", #CJK UNIFIED IDEOGRAPH
+ "98 E5 4F1C", #CJK UNIFIED IDEOGRAPH
+ "98 E6 4FF6", #CJK UNIFIED IDEOGRAPH
+ "98 E7 5021", #CJK UNIFIED IDEOGRAPH
+ "98 E8 5029", #CJK UNIFIED IDEOGRAPH
+ "98 E9 502C", #CJK UNIFIED IDEOGRAPH
+ "98 EA 4FFE", #CJK UNIFIED IDEOGRAPH
+ "98 EB 4FEF", #CJK UNIFIED IDEOGRAPH
+ "98 EC 5011", #CJK UNIFIED IDEOGRAPH
+ "98 ED 5006", #CJK UNIFIED IDEOGRAPH
+ "98 EE 5043", #CJK UNIFIED IDEOGRAPH
+ "98 EF 5047", #CJK UNIFIED IDEOGRAPH
+ "98 F0 6703", #CJK UNIFIED IDEOGRAPH
+ "98 F1 5055", #CJK UNIFIED IDEOGRAPH
+ "98 F2 5050", #CJK UNIFIED IDEOGRAPH
+ "98 F3 5048", #CJK UNIFIED IDEOGRAPH
+ "98 F4 505A", #CJK UNIFIED IDEOGRAPH
+ "98 F5 5056", #CJK UNIFIED IDEOGRAPH
+ "98 F6 506C", #CJK UNIFIED IDEOGRAPH
+ "98 F7 5078", #CJK UNIFIED IDEOGRAPH
+ "98 F8 5080", #CJK UNIFIED IDEOGRAPH
+ "98 F9 509A", #CJK UNIFIED IDEOGRAPH
+ "98 FA 5085", #CJK UNIFIED IDEOGRAPH
+ "98 FB 50B4", #CJK UNIFIED IDEOGRAPH
+ "98 FC 50B2", #CJK UNIFIED IDEOGRAPH
+ "99 40 50C9", #CJK UNIFIED IDEOGRAPH
+ "99 41 50CA", #CJK UNIFIED IDEOGRAPH
+ "99 42 50B3", #CJK UNIFIED IDEOGRAPH
+ "99 43 50C2", #CJK UNIFIED IDEOGRAPH
+ "99 44 50D6", #CJK UNIFIED IDEOGRAPH
+ "99 45 50DE", #CJK UNIFIED IDEOGRAPH
+ "99 46 50E5", #CJK UNIFIED IDEOGRAPH
+ "99 47 50ED", #CJK UNIFIED IDEOGRAPH
+ "99 48 50E3", #CJK UNIFIED IDEOGRAPH
+ "99 49 50EE", #CJK UNIFIED IDEOGRAPH
+ "99 4A 50F9", #CJK UNIFIED IDEOGRAPH
+ "99 4B 50F5", #CJK UNIFIED IDEOGRAPH
+ "99 4C 5109", #CJK UNIFIED IDEOGRAPH
+ "99 4D 5101", #CJK UNIFIED IDEOGRAPH
+ "99 4E 5102", #CJK UNIFIED IDEOGRAPH
+ "99 4F 5116", #CJK UNIFIED IDEOGRAPH
+ "99 50 5115", #CJK UNIFIED IDEOGRAPH
+ "99 51 5114", #CJK UNIFIED IDEOGRAPH
+ "99 52 511A", #CJK UNIFIED IDEOGRAPH
+ "99 53 5121", #CJK UNIFIED IDEOGRAPH
+ "99 54 513A", #CJK UNIFIED IDEOGRAPH
+ "99 55 5137", #CJK UNIFIED IDEOGRAPH
+ "99 56 513C", #CJK UNIFIED IDEOGRAPH
+ "99 57 513B", #CJK UNIFIED IDEOGRAPH
+ "99 58 513F", #CJK UNIFIED IDEOGRAPH
+ "99 59 5140", #CJK UNIFIED IDEOGRAPH
+ "99 5A 5152", #CJK UNIFIED IDEOGRAPH
+ "99 5B 514C", #CJK UNIFIED IDEOGRAPH
+ "99 5C 5154", #CJK UNIFIED IDEOGRAPH
+ "99 5D 5162", #CJK UNIFIED IDEOGRAPH
+ "99 5E 7AF8", #CJK UNIFIED IDEOGRAPH
+ "99 5F 5169", #CJK UNIFIED IDEOGRAPH
+ "99 60 516A", #CJK UNIFIED IDEOGRAPH
+ "99 61 516E", #CJK UNIFIED IDEOGRAPH
+ "99 62 5180", #CJK UNIFIED IDEOGRAPH
+ "99 63 5182", #CJK UNIFIED IDEOGRAPH
+ "99 64 56D8", #CJK UNIFIED IDEOGRAPH
+ "99 65 518C", #CJK UNIFIED IDEOGRAPH
+ "99 66 5189", #CJK UNIFIED IDEOGRAPH
+ "99 67 518F", #CJK UNIFIED IDEOGRAPH
+ "99 68 5191", #CJK UNIFIED IDEOGRAPH
+ "99 69 5193", #CJK UNIFIED IDEOGRAPH
+ "99 6A 5195", #CJK UNIFIED IDEOGRAPH
+ "99 6B 5196", #CJK UNIFIED IDEOGRAPH
+ "99 6C 51A4", #CJK UNIFIED IDEOGRAPH
+ "99 6D 51A6", #CJK UNIFIED IDEOGRAPH
+ "99 6E 51A2", #CJK UNIFIED IDEOGRAPH
+ "99 6F 51A9", #CJK UNIFIED IDEOGRAPH
+ "99 70 51AA", #CJK UNIFIED IDEOGRAPH
+ "99 71 51AB", #CJK UNIFIED IDEOGRAPH
+ "99 72 51B3", #CJK UNIFIED IDEOGRAPH
+ "99 73 51B1", #CJK UNIFIED IDEOGRAPH
+ "99 74 51B2", #CJK UNIFIED IDEOGRAPH
+ "99 75 51B0", #CJK UNIFIED IDEOGRAPH
+ "99 76 51B5", #CJK UNIFIED IDEOGRAPH
+ "99 77 51BD", #CJK UNIFIED IDEOGRAPH
+ "99 78 51C5", #CJK UNIFIED IDEOGRAPH
+ "99 79 51C9", #CJK UNIFIED IDEOGRAPH
+ "99 7A 51DB", #CJK UNIFIED IDEOGRAPH
+ "99 7B 51E0", #CJK UNIFIED IDEOGRAPH
+ "99 7C 8655", #CJK UNIFIED IDEOGRAPH
+ "99 7D 51E9", #CJK UNIFIED IDEOGRAPH
+ "99 7E 51ED", #CJK UNIFIED IDEOGRAPH
+ "99 80 51F0", #CJK UNIFIED IDEOGRAPH
+ "99 81 51F5", #CJK UNIFIED IDEOGRAPH
+ "99 82 51FE", #CJK UNIFIED IDEOGRAPH
+ "99 83 5204", #CJK UNIFIED IDEOGRAPH
+ "99 84 520B", #CJK UNIFIED IDEOGRAPH
+ "99 85 5214", #CJK UNIFIED IDEOGRAPH
+ "99 86 520E", #CJK UNIFIED IDEOGRAPH
+ "99 87 5227", #CJK UNIFIED IDEOGRAPH
+ "99 88 522A", #CJK UNIFIED IDEOGRAPH
+ "99 89 522E", #CJK UNIFIED IDEOGRAPH
+ "99 8A 5233", #CJK UNIFIED IDEOGRAPH
+ "99 8B 5239", #CJK UNIFIED IDEOGRAPH
+ "99 8C 524F", #CJK UNIFIED IDEOGRAPH
+ "99 8D 5244", #CJK UNIFIED IDEOGRAPH
+ "99 8E 524B", #CJK UNIFIED IDEOGRAPH
+ "99 8F 524C", #CJK UNIFIED IDEOGRAPH
+ "99 90 525E", #CJK UNIFIED IDEOGRAPH
+ "99 91 5254", #CJK UNIFIED IDEOGRAPH
+ "99 92 526A", #CJK UNIFIED IDEOGRAPH
+ "99 93 5274", #CJK UNIFIED IDEOGRAPH
+ "99 94 5269", #CJK UNIFIED IDEOGRAPH
+ "99 95 5273", #CJK UNIFIED IDEOGRAPH
+ "99 96 527F", #CJK UNIFIED IDEOGRAPH
+ "99 97 527D", #CJK UNIFIED IDEOGRAPH
+ "99 98 528D", #CJK UNIFIED IDEOGRAPH
+ "99 99 5294", #CJK UNIFIED IDEOGRAPH
+ "99 9A 5292", #CJK UNIFIED IDEOGRAPH
+ "99 9B 5271", #CJK UNIFIED IDEOGRAPH
+ "99 9C 5288", #CJK UNIFIED IDEOGRAPH
+ "99 9D 5291", #CJK UNIFIED IDEOGRAPH
+ "99 9E 8FA8", #CJK UNIFIED IDEOGRAPH
+ "99 9F 8FA7", #CJK UNIFIED IDEOGRAPH
+ "99 A0 52AC", #CJK UNIFIED IDEOGRAPH
+ "99 A1 52AD", #CJK UNIFIED IDEOGRAPH
+ "99 A2 52BC", #CJK UNIFIED IDEOGRAPH
+ "99 A3 52B5", #CJK UNIFIED IDEOGRAPH
+ "99 A4 52C1", #CJK UNIFIED IDEOGRAPH
+ "99 A5 52CD", #CJK UNIFIED IDEOGRAPH
+ "99 A6 52D7", #CJK UNIFIED IDEOGRAPH
+ "99 A7 52DE", #CJK UNIFIED IDEOGRAPH
+ "99 A8 52E3", #CJK UNIFIED IDEOGRAPH
+ "99 A9 52E6", #CJK UNIFIED IDEOGRAPH
+ "99 AA 98ED", #CJK UNIFIED IDEOGRAPH
+ "99 AB 52E0", #CJK UNIFIED IDEOGRAPH
+ "99 AC 52F3", #CJK UNIFIED IDEOGRAPH
+ "99 AD 52F5", #CJK UNIFIED IDEOGRAPH
+ "99 AE 52F8", #CJK UNIFIED IDEOGRAPH
+ "99 AF 52F9", #CJK UNIFIED IDEOGRAPH
+ "99 B0 5306", #CJK UNIFIED IDEOGRAPH
+ "99 B1 5308", #CJK UNIFIED IDEOGRAPH
+ "99 B2 7538", #CJK UNIFIED IDEOGRAPH
+ "99 B3 530D", #CJK UNIFIED IDEOGRAPH
+ "99 B4 5310", #CJK UNIFIED IDEOGRAPH
+ "99 B5 530F", #CJK UNIFIED IDEOGRAPH
+ "99 B6 5315", #CJK UNIFIED IDEOGRAPH
+ "99 B7 531A", #CJK UNIFIED IDEOGRAPH
+ "99 B8 5323", #CJK UNIFIED IDEOGRAPH
+ "99 B9 532F", #CJK UNIFIED IDEOGRAPH
+ "99 BA 5331", #CJK UNIFIED IDEOGRAPH
+ "99 BB 5333", #CJK UNIFIED IDEOGRAPH
+ "99 BC 5338", #CJK UNIFIED IDEOGRAPH
+ "99 BD 5340", #CJK UNIFIED IDEOGRAPH
+ "99 BE 5346", #CJK UNIFIED IDEOGRAPH
+ "99 BF 5345", #CJK UNIFIED IDEOGRAPH
+ "99 C0 4E17", #CJK UNIFIED IDEOGRAPH
+ "99 C1 5349", #CJK UNIFIED IDEOGRAPH
+ "99 C2 534D", #CJK UNIFIED IDEOGRAPH
+ "99 C3 51D6", #CJK UNIFIED IDEOGRAPH
+ "99 C4 535E", #CJK UNIFIED IDEOGRAPH
+ "99 C5 5369", #CJK UNIFIED IDEOGRAPH
+ "99 C6 536E", #CJK UNIFIED IDEOGRAPH
+ "99 C7 5918", #CJK UNIFIED IDEOGRAPH
+ "99 C8 537B", #CJK UNIFIED IDEOGRAPH
+ "99 C9 5377", #CJK UNIFIED IDEOGRAPH
+ "99 CA 5382", #CJK UNIFIED IDEOGRAPH
+ "99 CB 5396", #CJK UNIFIED IDEOGRAPH
+ "99 CC 53A0", #CJK UNIFIED IDEOGRAPH
+ "99 CD 53A6", #CJK UNIFIED IDEOGRAPH
+ "99 CE 53A5", #CJK UNIFIED IDEOGRAPH
+ "99 CF 53AE", #CJK UNIFIED IDEOGRAPH
+ "99 D0 53B0", #CJK UNIFIED IDEOGRAPH
+ "99 D1 53B6", #CJK UNIFIED IDEOGRAPH
+ "99 D2 53C3", #CJK UNIFIED IDEOGRAPH
+ "99 D3 7C12", #CJK UNIFIED IDEOGRAPH
+ "99 D4 96D9", #CJK UNIFIED IDEOGRAPH
+ "99 D5 53DF", #CJK UNIFIED IDEOGRAPH
+ "99 D6 66FC", #CJK UNIFIED IDEOGRAPH
+ "99 D7 71EE", #CJK UNIFIED IDEOGRAPH
+ "99 D8 53EE", #CJK UNIFIED IDEOGRAPH
+ "99 D9 53E8", #CJK UNIFIED IDEOGRAPH
+ "99 DA 53ED", #CJK UNIFIED IDEOGRAPH
+ "99 DB 53FA", #CJK UNIFIED IDEOGRAPH
+ "99 DC 5401", #CJK UNIFIED IDEOGRAPH
+ "99 DD 543D", #CJK UNIFIED IDEOGRAPH
+ "99 DE 5440", #CJK UNIFIED IDEOGRAPH
+ "99 DF 542C", #CJK UNIFIED IDEOGRAPH
+ "99 E0 542D", #CJK UNIFIED IDEOGRAPH
+ "99 E1 543C", #CJK UNIFIED IDEOGRAPH
+ "99 E2 542E", #CJK UNIFIED IDEOGRAPH
+ "99 E3 5436", #CJK UNIFIED IDEOGRAPH
+ "99 E4 5429", #CJK UNIFIED IDEOGRAPH
+ "99 E5 541D", #CJK UNIFIED IDEOGRAPH
+ "99 E6 544E", #CJK UNIFIED IDEOGRAPH
+ "99 E7 548F", #CJK UNIFIED IDEOGRAPH
+ "99 E8 5475", #CJK UNIFIED IDEOGRAPH
+ "99 E9 548E", #CJK UNIFIED IDEOGRAPH
+ "99 EA 545F", #CJK UNIFIED IDEOGRAPH
+ "99 EB 5471", #CJK UNIFIED IDEOGRAPH
+ "99 EC 5477", #CJK UNIFIED IDEOGRAPH
+ "99 ED 5470", #CJK UNIFIED IDEOGRAPH
+ "99 EE 5492", #CJK UNIFIED IDEOGRAPH
+ "99 EF 547B", #CJK UNIFIED IDEOGRAPH
+ "99 F0 5480", #CJK UNIFIED IDEOGRAPH
+ "99 F1 5476", #CJK UNIFIED IDEOGRAPH
+ "99 F2 5484", #CJK UNIFIED IDEOGRAPH
+ "99 F3 5490", #CJK UNIFIED IDEOGRAPH
+ "99 F4 5486", #CJK UNIFIED IDEOGRAPH
+ "99 F5 54C7", #CJK UNIFIED IDEOGRAPH
+ "99 F6 54A2", #CJK UNIFIED IDEOGRAPH
+ "99 F7 54B8", #CJK UNIFIED IDEOGRAPH
+ "99 F8 54A5", #CJK UNIFIED IDEOGRAPH
+ "99 F9 54AC", #CJK UNIFIED IDEOGRAPH
+ "99 FA 54C4", #CJK UNIFIED IDEOGRAPH
+ "99 FB 54C8", #CJK UNIFIED IDEOGRAPH
+ "99 FC 54A8", #CJK UNIFIED IDEOGRAPH
+ "9A 40 54AB", #CJK UNIFIED IDEOGRAPH
+ "9A 41 54C2", #CJK UNIFIED IDEOGRAPH
+ "9A 42 54A4", #CJK UNIFIED IDEOGRAPH
+ "9A 43 54BE", #CJK UNIFIED IDEOGRAPH
+ "9A 44 54BC", #CJK UNIFIED IDEOGRAPH
+ "9A 45 54D8", #CJK UNIFIED IDEOGRAPH
+ "9A 46 54E5", #CJK UNIFIED IDEOGRAPH
+ "9A 47 54E6", #CJK UNIFIED IDEOGRAPH
+ "9A 48 550F", #CJK UNIFIED IDEOGRAPH
+ "9A 49 5514", #CJK UNIFIED IDEOGRAPH
+ "9A 4A 54FD", #CJK UNIFIED IDEOGRAPH
+ "9A 4B 54EE", #CJK UNIFIED IDEOGRAPH
+ "9A 4C 54ED", #CJK UNIFIED IDEOGRAPH
+ "9A 4D 54FA", #CJK UNIFIED IDEOGRAPH
+ "9A 4E 54E2", #CJK UNIFIED IDEOGRAPH
+ "9A 4F 5539", #CJK UNIFIED IDEOGRAPH
+ "9A 50 5540", #CJK UNIFIED IDEOGRAPH
+ "9A 51 5563", #CJK UNIFIED IDEOGRAPH
+ "9A 52 554C", #CJK UNIFIED IDEOGRAPH
+ "9A 53 552E", #CJK UNIFIED IDEOGRAPH
+ "9A 54 555C", #CJK UNIFIED IDEOGRAPH
+ "9A 55 5545", #CJK UNIFIED IDEOGRAPH
+ "9A 56 5556", #CJK UNIFIED IDEOGRAPH
+ "9A 57 5557", #CJK UNIFIED IDEOGRAPH
+ "9A 58 5538", #CJK UNIFIED IDEOGRAPH
+ "9A 59 5533", #CJK UNIFIED IDEOGRAPH
+ "9A 5A 555D", #CJK UNIFIED IDEOGRAPH
+ "9A 5B 5599", #CJK UNIFIED IDEOGRAPH
+ "9A 5C 5580", #CJK UNIFIED IDEOGRAPH
+ "9A 5D 54AF", #CJK UNIFIED IDEOGRAPH
+ "9A 5E 558A", #CJK UNIFIED IDEOGRAPH
+ "9A 5F 559F", #CJK UNIFIED IDEOGRAPH
+ "9A 60 557B", #CJK UNIFIED IDEOGRAPH
+ "9A 61 557E", #CJK UNIFIED IDEOGRAPH
+ "9A 62 5598", #CJK UNIFIED IDEOGRAPH
+ "9A 63 559E", #CJK UNIFIED IDEOGRAPH
+ "9A 64 55AE", #CJK UNIFIED IDEOGRAPH
+ "9A 65 557C", #CJK UNIFIED IDEOGRAPH
+ "9A 66 5583", #CJK UNIFIED IDEOGRAPH
+ "9A 67 55A9", #CJK UNIFIED IDEOGRAPH
+ "9A 68 5587", #CJK UNIFIED IDEOGRAPH
+ "9A 69 55A8", #CJK UNIFIED IDEOGRAPH
+ "9A 6A 55DA", #CJK UNIFIED IDEOGRAPH
+ "9A 6B 55C5", #CJK UNIFIED IDEOGRAPH
+ "9A 6C 55DF", #CJK UNIFIED IDEOGRAPH
+ "9A 6D 55C4", #CJK UNIFIED IDEOGRAPH
+ "9A 6E 55DC", #CJK UNIFIED IDEOGRAPH
+ "9A 6F 55E4", #CJK UNIFIED IDEOGRAPH
+ "9A 70 55D4", #CJK UNIFIED IDEOGRAPH
+ "9A 71 5614", #CJK UNIFIED IDEOGRAPH
+ "9A 72 55F7", #CJK UNIFIED IDEOGRAPH
+ "9A 73 5616", #CJK UNIFIED IDEOGRAPH
+ "9A 74 55FE", #CJK UNIFIED IDEOGRAPH
+ "9A 75 55FD", #CJK UNIFIED IDEOGRAPH
+ "9A 76 561B", #CJK UNIFIED IDEOGRAPH
+ "9A 77 55F9", #CJK UNIFIED IDEOGRAPH
+ "9A 78 564E", #CJK UNIFIED IDEOGRAPH
+ "9A 79 5650", #CJK UNIFIED IDEOGRAPH
+ "9A 7A 71DF", #CJK UNIFIED IDEOGRAPH
+ "9A 7B 5634", #CJK UNIFIED IDEOGRAPH
+ "9A 7C 5636", #CJK UNIFIED IDEOGRAPH
+ "9A 7D 5632", #CJK UNIFIED IDEOGRAPH
+ "9A 7E 5638", #CJK UNIFIED IDEOGRAPH
+ "9A 80 566B", #CJK UNIFIED IDEOGRAPH
+ "9A 81 5664", #CJK UNIFIED IDEOGRAPH
+ "9A 82 562F", #CJK UNIFIED IDEOGRAPH
+ "9A 83 566C", #CJK UNIFIED IDEOGRAPH
+ "9A 84 566A", #CJK UNIFIED IDEOGRAPH
+ "9A 85 5686", #CJK UNIFIED IDEOGRAPH
+ "9A 86 5680", #CJK UNIFIED IDEOGRAPH
+ "9A 87 568A", #CJK UNIFIED IDEOGRAPH
+ "9A 88 56A0", #CJK UNIFIED IDEOGRAPH
+ "9A 89 5694", #CJK UNIFIED IDEOGRAPH
+ "9A 8A 568F", #CJK UNIFIED IDEOGRAPH
+ "9A 8B 56A5", #CJK UNIFIED IDEOGRAPH
+ "9A 8C 56AE", #CJK UNIFIED IDEOGRAPH
+ "9A 8D 56B6", #CJK UNIFIED IDEOGRAPH
+ "9A 8E 56B4", #CJK UNIFIED IDEOGRAPH
+ "9A 8F 56C2", #CJK UNIFIED IDEOGRAPH
+ "9A 90 56BC", #CJK UNIFIED IDEOGRAPH
+ "9A 91 56C1", #CJK UNIFIED IDEOGRAPH
+ "9A 92 56C3", #CJK UNIFIED IDEOGRAPH
+ "9A 93 56C0", #CJK UNIFIED IDEOGRAPH
+ "9A 94 56C8", #CJK UNIFIED IDEOGRAPH
+ "9A 95 56CE", #CJK UNIFIED IDEOGRAPH
+ "9A 96 56D1", #CJK UNIFIED IDEOGRAPH
+ "9A 97 56D3", #CJK UNIFIED IDEOGRAPH
+ "9A 98 56D7", #CJK UNIFIED IDEOGRAPH
+ "9A 99 56EE", #CJK UNIFIED IDEOGRAPH
+ "9A 9A 56F9", #CJK UNIFIED IDEOGRAPH
+ "9A 9B 5700", #CJK UNIFIED IDEOGRAPH
+ "9A 9C 56FF", #CJK UNIFIED IDEOGRAPH
+ "9A 9D 5704", #CJK UNIFIED IDEOGRAPH
+ "9A 9E 5709", #CJK UNIFIED IDEOGRAPH
+ "9A 9F 5708", #CJK UNIFIED IDEOGRAPH
+ "9A A0 570B", #CJK UNIFIED IDEOGRAPH
+ "9A A1 570D", #CJK UNIFIED IDEOGRAPH
+ "9A A2 5713", #CJK UNIFIED IDEOGRAPH
+ "9A A3 5718", #CJK UNIFIED IDEOGRAPH
+ "9A A4 5716", #CJK UNIFIED IDEOGRAPH
+ "9A A5 55C7", #CJK UNIFIED IDEOGRAPH
+ "9A A6 571C", #CJK UNIFIED IDEOGRAPH
+ "9A A7 5726", #CJK UNIFIED IDEOGRAPH
+ "9A A8 5737", #CJK UNIFIED IDEOGRAPH
+ "9A A9 5738", #CJK UNIFIED IDEOGRAPH
+ "9A AA 574E", #CJK UNIFIED IDEOGRAPH
+ "9A AB 573B", #CJK UNIFIED IDEOGRAPH
+ "9A AC 5740", #CJK UNIFIED IDEOGRAPH
+ "9A AD 574F", #CJK UNIFIED IDEOGRAPH
+ "9A AE 5769", #CJK UNIFIED IDEOGRAPH
+ "9A AF 57C0", #CJK UNIFIED IDEOGRAPH
+ "9A B0 5788", #CJK UNIFIED IDEOGRAPH
+ "9A B1 5761", #CJK UNIFIED IDEOGRAPH
+ "9A B2 577F", #CJK UNIFIED IDEOGRAPH
+ "9A B3 5789", #CJK UNIFIED IDEOGRAPH
+ "9A B4 5793", #CJK UNIFIED IDEOGRAPH
+ "9A B5 57A0", #CJK UNIFIED IDEOGRAPH
+ "9A B6 57B3", #CJK UNIFIED IDEOGRAPH
+ "9A B7 57A4", #CJK UNIFIED IDEOGRAPH
+ "9A B8 57AA", #CJK UNIFIED IDEOGRAPH
+ "9A B9 57B0", #CJK UNIFIED IDEOGRAPH
+ "9A BA 57C3", #CJK UNIFIED IDEOGRAPH
+ "9A BB 57C6", #CJK UNIFIED IDEOGRAPH
+ "9A BC 57D4", #CJK UNIFIED IDEOGRAPH
+ "9A BD 57D2", #CJK UNIFIED IDEOGRAPH
+ "9A BE 57D3", #CJK UNIFIED IDEOGRAPH
+ "9A BF 580A", #CJK UNIFIED IDEOGRAPH
+ "9A C0 57D6", #CJK UNIFIED IDEOGRAPH
+ "9A C1 57E3", #CJK UNIFIED IDEOGRAPH
+ "9A C2 580B", #CJK UNIFIED IDEOGRAPH
+ "9A C3 5819", #CJK UNIFIED IDEOGRAPH
+ "9A C4 581D", #CJK UNIFIED IDEOGRAPH
+ "9A C5 5872", #CJK UNIFIED IDEOGRAPH
+ "9A C6 5821", #CJK UNIFIED IDEOGRAPH
+ "9A C7 5862", #CJK UNIFIED IDEOGRAPH
+ "9A C8 584B", #CJK UNIFIED IDEOGRAPH
+ "9A C9 5870", #CJK UNIFIED IDEOGRAPH
+ "9A CA 6BC0", #CJK UNIFIED IDEOGRAPH
+ "9A CB 5852", #CJK UNIFIED IDEOGRAPH
+ "9A CC 583D", #CJK UNIFIED IDEOGRAPH
+ "9A CD 5879", #CJK UNIFIED IDEOGRAPH
+ "9A CE 5885", #CJK UNIFIED IDEOGRAPH
+ "9A CF 58B9", #CJK UNIFIED IDEOGRAPH
+ "9A D0 589F", #CJK UNIFIED IDEOGRAPH
+ "9A D1 58AB", #CJK UNIFIED IDEOGRAPH
+ "9A D2 58BA", #CJK UNIFIED IDEOGRAPH
+ "9A D3 58DE", #CJK UNIFIED IDEOGRAPH
+ "9A D4 58BB", #CJK UNIFIED IDEOGRAPH
+ "9A D5 58B8", #CJK UNIFIED IDEOGRAPH
+ "9A D6 58AE", #CJK UNIFIED IDEOGRAPH
+ "9A D7 58C5", #CJK UNIFIED IDEOGRAPH
+ "9A D8 58D3", #CJK UNIFIED IDEOGRAPH
+ "9A D9 58D1", #CJK UNIFIED IDEOGRAPH
+ "9A DA 58D7", #CJK UNIFIED IDEOGRAPH
+ "9A DB 58D9", #CJK UNIFIED IDEOGRAPH
+ "9A DC 58D8", #CJK UNIFIED IDEOGRAPH
+ "9A DD 58E5", #CJK UNIFIED IDEOGRAPH
+ "9A DE 58DC", #CJK UNIFIED IDEOGRAPH
+ "9A DF 58E4", #CJK UNIFIED IDEOGRAPH
+ "9A E0 58DF", #CJK UNIFIED IDEOGRAPH
+ "9A E1 58EF", #CJK UNIFIED IDEOGRAPH
+ "9A E2 58FA", #CJK UNIFIED IDEOGRAPH
+ "9A E3 58F9", #CJK UNIFIED IDEOGRAPH
+ "9A E4 58FB", #CJK UNIFIED IDEOGRAPH
+ "9A E5 58FC", #CJK UNIFIED IDEOGRAPH
+ "9A E6 58FD", #CJK UNIFIED IDEOGRAPH
+ "9A E7 5902", #CJK UNIFIED IDEOGRAPH
+ "9A E8 590A", #CJK UNIFIED IDEOGRAPH
+ "9A E9 5910", #CJK UNIFIED IDEOGRAPH
+ "9A EA 591B", #CJK UNIFIED IDEOGRAPH
+ "9A EB 68A6", #CJK UNIFIED IDEOGRAPH
+ "9A EC 5925", #CJK UNIFIED IDEOGRAPH
+ "9A ED 592C", #CJK UNIFIED IDEOGRAPH
+ "9A EE 592D", #CJK UNIFIED IDEOGRAPH
+ "9A EF 5932", #CJK UNIFIED IDEOGRAPH
+ "9A F0 5938", #CJK UNIFIED IDEOGRAPH
+ "9A F1 593E", #CJK UNIFIED IDEOGRAPH
+ "9A F2 7AD2", #CJK UNIFIED IDEOGRAPH
+ "9A F3 5955", #CJK UNIFIED IDEOGRAPH
+ "9A F4 5950", #CJK UNIFIED IDEOGRAPH
+ "9A F5 594E", #CJK UNIFIED IDEOGRAPH
+ "9A F6 595A", #CJK UNIFIED IDEOGRAPH
+ "9A F7 5958", #CJK UNIFIED IDEOGRAPH
+ "9A F8 5962", #CJK UNIFIED IDEOGRAPH
+ "9A F9 5960", #CJK UNIFIED IDEOGRAPH
+ "9A FA 5967", #CJK UNIFIED IDEOGRAPH
+ "9A FB 596C", #CJK UNIFIED IDEOGRAPH
+ "9A FC 5969", #CJK UNIFIED IDEOGRAPH
+ "9B 40 5978", #CJK UNIFIED IDEOGRAPH
+ "9B 41 5981", #CJK UNIFIED IDEOGRAPH
+ "9B 42 599D", #CJK UNIFIED IDEOGRAPH
+ "9B 43 4F5E", #CJK UNIFIED IDEOGRAPH
+ "9B 44 4FAB", #CJK UNIFIED IDEOGRAPH
+ "9B 45 59A3", #CJK UNIFIED IDEOGRAPH
+ "9B 46 59B2", #CJK UNIFIED IDEOGRAPH
+ "9B 47 59C6", #CJK UNIFIED IDEOGRAPH
+ "9B 48 59E8", #CJK UNIFIED IDEOGRAPH
+ "9B 49 59DC", #CJK UNIFIED IDEOGRAPH
+ "9B 4A 598D", #CJK UNIFIED IDEOGRAPH
+ "9B 4B 59D9", #CJK UNIFIED IDEOGRAPH
+ "9B 4C 59DA", #CJK UNIFIED IDEOGRAPH
+ "9B 4D 5A25", #CJK UNIFIED IDEOGRAPH
+ "9B 4E 5A1F", #CJK UNIFIED IDEOGRAPH
+ "9B 4F 5A11", #CJK UNIFIED IDEOGRAPH
+ "9B 50 5A1C", #CJK UNIFIED IDEOGRAPH
+ "9B 51 5A09", #CJK UNIFIED IDEOGRAPH
+ "9B 52 5A1A", #CJK UNIFIED IDEOGRAPH
+ "9B 53 5A40", #CJK UNIFIED IDEOGRAPH
+ "9B 54 5A6C", #CJK UNIFIED IDEOGRAPH
+ "9B 55 5A49", #CJK UNIFIED IDEOGRAPH
+ "9B 56 5A35", #CJK UNIFIED IDEOGRAPH
+ "9B 57 5A36", #CJK UNIFIED IDEOGRAPH
+ "9B 58 5A62", #CJK UNIFIED IDEOGRAPH
+ "9B 59 5A6A", #CJK UNIFIED IDEOGRAPH
+ "9B 5A 5A9A", #CJK UNIFIED IDEOGRAPH
+ "9B 5B 5ABC", #CJK UNIFIED IDEOGRAPH
+ "9B 5C 5ABE", #CJK UNIFIED IDEOGRAPH
+ "9B 5D 5ACB", #CJK UNIFIED IDEOGRAPH
+ "9B 5E 5AC2", #CJK UNIFIED IDEOGRAPH
+ "9B 5F 5ABD", #CJK UNIFIED IDEOGRAPH
+ "9B 60 5AE3", #CJK UNIFIED IDEOGRAPH
+ "9B 61 5AD7", #CJK UNIFIED IDEOGRAPH
+ "9B 62 5AE6", #CJK UNIFIED IDEOGRAPH
+ "9B 63 5AE9", #CJK UNIFIED IDEOGRAPH
+ "9B 64 5AD6", #CJK UNIFIED IDEOGRAPH
+ "9B 65 5AFA", #CJK UNIFIED IDEOGRAPH
+ "9B 66 5AFB", #CJK UNIFIED IDEOGRAPH
+ "9B 67 5B0C", #CJK UNIFIED IDEOGRAPH
+ "9B 68 5B0B", #CJK UNIFIED IDEOGRAPH
+ "9B 69 5B16", #CJK UNIFIED IDEOGRAPH
+ "9B 6A 5B32", #CJK UNIFIED IDEOGRAPH
+ "9B 6B 5AD0", #CJK UNIFIED IDEOGRAPH
+ "9B 6C 5B2A", #CJK UNIFIED IDEOGRAPH
+ "9B 6D 5B36", #CJK UNIFIED IDEOGRAPH
+ "9B 6E 5B3E", #CJK UNIFIED IDEOGRAPH
+ "9B 6F 5B43", #CJK UNIFIED IDEOGRAPH
+ "9B 70 5B45", #CJK UNIFIED IDEOGRAPH
+ "9B 71 5B40", #CJK UNIFIED IDEOGRAPH
+ "9B 72 5B51", #CJK UNIFIED IDEOGRAPH
+ "9B 73 5B55", #CJK UNIFIED IDEOGRAPH
+ "9B 74 5B5A", #CJK UNIFIED IDEOGRAPH
+ "9B 75 5B5B", #CJK UNIFIED IDEOGRAPH
+ "9B 76 5B65", #CJK UNIFIED IDEOGRAPH
+ "9B 77 5B69", #CJK UNIFIED IDEOGRAPH
+ "9B 78 5B70", #CJK UNIFIED IDEOGRAPH
+ "9B 79 5B73", #CJK UNIFIED IDEOGRAPH
+ "9B 7A 5B75", #CJK UNIFIED IDEOGRAPH
+ "9B 7B 5B78", #CJK UNIFIED IDEOGRAPH
+ "9B 7C 6588", #CJK UNIFIED IDEOGRAPH
+ "9B 7D 5B7A", #CJK UNIFIED IDEOGRAPH
+ "9B 7E 5B80", #CJK UNIFIED IDEOGRAPH
+ "9B 80 5B83", #CJK UNIFIED IDEOGRAPH
+ "9B 81 5BA6", #CJK UNIFIED IDEOGRAPH
+ "9B 82 5BB8", #CJK UNIFIED IDEOGRAPH
+ "9B 83 5BC3", #CJK UNIFIED IDEOGRAPH
+ "9B 84 5BC7", #CJK UNIFIED IDEOGRAPH
+ "9B 85 5BC9", #CJK UNIFIED IDEOGRAPH
+ "9B 86 5BD4", #CJK UNIFIED IDEOGRAPH
+ "9B 87 5BD0", #CJK UNIFIED IDEOGRAPH
+ "9B 88 5BE4", #CJK UNIFIED IDEOGRAPH
+ "9B 89 5BE6", #CJK UNIFIED IDEOGRAPH
+ "9B 8A 5BE2", #CJK UNIFIED IDEOGRAPH
+ "9B 8B 5BDE", #CJK UNIFIED IDEOGRAPH
+ "9B 8C 5BE5", #CJK UNIFIED IDEOGRAPH
+ "9B 8D 5BEB", #CJK UNIFIED IDEOGRAPH
+ "9B 8E 5BF0", #CJK UNIFIED IDEOGRAPH
+ "9B 8F 5BF6", #CJK UNIFIED IDEOGRAPH
+ "9B 90 5BF3", #CJK UNIFIED IDEOGRAPH
+ "9B 91 5C05", #CJK UNIFIED IDEOGRAPH
+ "9B 92 5C07", #CJK UNIFIED IDEOGRAPH
+ "9B 93 5C08", #CJK UNIFIED IDEOGRAPH
+ "9B 94 5C0D", #CJK UNIFIED IDEOGRAPH
+ "9B 95 5C13", #CJK UNIFIED IDEOGRAPH
+ "9B 96 5C20", #CJK UNIFIED IDEOGRAPH
+ "9B 97 5C22", #CJK UNIFIED IDEOGRAPH
+ "9B 98 5C28", #CJK UNIFIED IDEOGRAPH
+ "9B 99 5C38", #CJK UNIFIED IDEOGRAPH
+ "9B 9A 5C39", #CJK UNIFIED IDEOGRAPH
+ "9B 9B 5C41", #CJK UNIFIED IDEOGRAPH
+ "9B 9C 5C46", #CJK UNIFIED IDEOGRAPH
+ "9B 9D 5C4E", #CJK UNIFIED IDEOGRAPH
+ "9B 9E 5C53", #CJK UNIFIED IDEOGRAPH
+ "9B 9F 5C50", #CJK UNIFIED IDEOGRAPH
+ "9B A0 5C4F", #CJK UNIFIED IDEOGRAPH
+ "9B A1 5B71", #CJK UNIFIED IDEOGRAPH
+ "9B A2 5C6C", #CJK UNIFIED IDEOGRAPH
+ "9B A3 5C6E", #CJK UNIFIED IDEOGRAPH
+ "9B A4 4E62", #CJK UNIFIED IDEOGRAPH
+ "9B A5 5C76", #CJK UNIFIED IDEOGRAPH
+ "9B A6 5C79", #CJK UNIFIED IDEOGRAPH
+ "9B A7 5C8C", #CJK UNIFIED IDEOGRAPH
+ "9B A8 5C91", #CJK UNIFIED IDEOGRAPH
+ "9B A9 5C94", #CJK UNIFIED IDEOGRAPH
+ "9B AA 599B", #CJK UNIFIED IDEOGRAPH
+ "9B AB 5CAB", #CJK UNIFIED IDEOGRAPH
+ "9B AC 5CBB", #CJK UNIFIED IDEOGRAPH
+ "9B AD 5CB6", #CJK UNIFIED IDEOGRAPH
+ "9B AE 5CBC", #CJK UNIFIED IDEOGRAPH
+ "9B AF 5CB7", #CJK UNIFIED IDEOGRAPH
+ "9B B0 5CC5", #CJK UNIFIED IDEOGRAPH
+ "9B B1 5CBE", #CJK UNIFIED IDEOGRAPH
+ "9B B2 5CC7", #CJK UNIFIED IDEOGRAPH
+ "9B B3 5CD9", #CJK UNIFIED IDEOGRAPH
+ "9B B4 5CE9", #CJK UNIFIED IDEOGRAPH
+ "9B B5 5CFD", #CJK UNIFIED IDEOGRAPH
+ "9B B6 5CFA", #CJK UNIFIED IDEOGRAPH
+ "9B B7 5CED", #CJK UNIFIED IDEOGRAPH
+ "9B B8 5D8C", #CJK UNIFIED IDEOGRAPH
+ "9B B9 5CEA", #CJK UNIFIED IDEOGRAPH
+ "9B BA 5D0B", #CJK UNIFIED IDEOGRAPH
+ "9B BB 5D15", #CJK UNIFIED IDEOGRAPH
+ "9B BC 5D17", #CJK UNIFIED IDEOGRAPH
+ "9B BD 5D5C", #CJK UNIFIED IDEOGRAPH
+ "9B BE 5D1F", #CJK UNIFIED IDEOGRAPH
+ "9B BF 5D1B", #CJK UNIFIED IDEOGRAPH
+ "9B C0 5D11", #CJK UNIFIED IDEOGRAPH
+ "9B C1 5D14", #CJK UNIFIED IDEOGRAPH
+ "9B C2 5D22", #CJK UNIFIED IDEOGRAPH
+ "9B C3 5D1A", #CJK UNIFIED IDEOGRAPH
+ "9B C4 5D19", #CJK UNIFIED IDEOGRAPH
+ "9B C5 5D18", #CJK UNIFIED IDEOGRAPH
+ "9B C6 5D4C", #CJK UNIFIED IDEOGRAPH
+ "9B C7 5D52", #CJK UNIFIED IDEOGRAPH
+ "9B C8 5D4E", #CJK UNIFIED IDEOGRAPH
+ "9B C9 5D4B", #CJK UNIFIED IDEOGRAPH
+ "9B CA 5D6C", #CJK UNIFIED IDEOGRAPH
+ "9B CB 5D73", #CJK UNIFIED IDEOGRAPH
+ "9B CC 5D76", #CJK UNIFIED IDEOGRAPH
+ "9B CD 5D87", #CJK UNIFIED IDEOGRAPH
+ "9B CE 5D84", #CJK UNIFIED IDEOGRAPH
+ "9B CF 5D82", #CJK UNIFIED IDEOGRAPH
+ "9B D0 5DA2", #CJK UNIFIED IDEOGRAPH
+ "9B D1 5D9D", #CJK UNIFIED IDEOGRAPH
+ "9B D2 5DAC", #CJK UNIFIED IDEOGRAPH
+ "9B D3 5DAE", #CJK UNIFIED IDEOGRAPH
+ "9B D4 5DBD", #CJK UNIFIED IDEOGRAPH
+ "9B D5 5D90", #CJK UNIFIED IDEOGRAPH
+ "9B D6 5DB7", #CJK UNIFIED IDEOGRAPH
+ "9B D7 5DBC", #CJK UNIFIED IDEOGRAPH
+ "9B D8 5DC9", #CJK UNIFIED IDEOGRAPH
+ "9B D9 5DCD", #CJK UNIFIED IDEOGRAPH
+ "9B DA 5DD3", #CJK UNIFIED IDEOGRAPH
+ "9B DB 5DD2", #CJK UNIFIED IDEOGRAPH
+ "9B DC 5DD6", #CJK UNIFIED IDEOGRAPH
+ "9B DD 5DDB", #CJK UNIFIED IDEOGRAPH
+ "9B DE 5DEB", #CJK UNIFIED IDEOGRAPH
+ "9B DF 5DF2", #CJK UNIFIED IDEOGRAPH
+ "9B E0 5DF5", #CJK UNIFIED IDEOGRAPH
+ "9B E1 5E0B", #CJK UNIFIED IDEOGRAPH
+ "9B E2 5E1A", #CJK UNIFIED IDEOGRAPH
+ "9B E3 5E19", #CJK UNIFIED IDEOGRAPH
+ "9B E4 5E11", #CJK UNIFIED IDEOGRAPH
+ "9B E5 5E1B", #CJK UNIFIED IDEOGRAPH
+ "9B E6 5E36", #CJK UNIFIED IDEOGRAPH
+ "9B E7 5E37", #CJK UNIFIED IDEOGRAPH
+ "9B E8 5E44", #CJK UNIFIED IDEOGRAPH
+ "9B E9 5E43", #CJK UNIFIED IDEOGRAPH
+ "9B EA 5E40", #CJK UNIFIED IDEOGRAPH
+ "9B EB 5E4E", #CJK UNIFIED IDEOGRAPH
+ "9B EC 5E57", #CJK UNIFIED IDEOGRAPH
+ "9B ED 5E54", #CJK UNIFIED IDEOGRAPH
+ "9B EE 5E5F", #CJK UNIFIED IDEOGRAPH
+ "9B EF 5E62", #CJK UNIFIED IDEOGRAPH
+ "9B F0 5E64", #CJK UNIFIED IDEOGRAPH
+ "9B F1 5E47", #CJK UNIFIED IDEOGRAPH
+ "9B F2 5E75", #CJK UNIFIED IDEOGRAPH
+ "9B F3 5E76", #CJK UNIFIED IDEOGRAPH
+ "9B F4 5E7A", #CJK UNIFIED IDEOGRAPH
+ "9B F5 9EBC", #CJK UNIFIED IDEOGRAPH
+ "9B F6 5E7F", #CJK UNIFIED IDEOGRAPH
+ "9B F7 5EA0", #CJK UNIFIED IDEOGRAPH
+ "9B F8 5EC1", #CJK UNIFIED IDEOGRAPH
+ "9B F9 5EC2", #CJK UNIFIED IDEOGRAPH
+ "9B FA 5EC8", #CJK UNIFIED IDEOGRAPH
+ "9B FB 5ED0", #CJK UNIFIED IDEOGRAPH
+ "9B FC 5ECF", #CJK UNIFIED IDEOGRAPH
+ "9C 40 5ED6", #CJK UNIFIED IDEOGRAPH
+ "9C 41 5EE3", #CJK UNIFIED IDEOGRAPH
+ "9C 42 5EDD", #CJK UNIFIED IDEOGRAPH
+ "9C 43 5EDA", #CJK UNIFIED IDEOGRAPH
+ "9C 44 5EDB", #CJK UNIFIED IDEOGRAPH
+ "9C 45 5EE2", #CJK UNIFIED IDEOGRAPH
+ "9C 46 5EE1", #CJK UNIFIED IDEOGRAPH
+ "9C 47 5EE8", #CJK UNIFIED IDEOGRAPH
+ "9C 48 5EE9", #CJK UNIFIED IDEOGRAPH
+ "9C 49 5EEC", #CJK UNIFIED IDEOGRAPH
+ "9C 4A 5EF1", #CJK UNIFIED IDEOGRAPH
+ "9C 4B 5EF3", #CJK UNIFIED IDEOGRAPH
+ "9C 4C 5EF0", #CJK UNIFIED IDEOGRAPH
+ "9C 4D 5EF4", #CJK UNIFIED IDEOGRAPH
+ "9C 4E 5EF8", #CJK UNIFIED IDEOGRAPH
+ "9C 4F 5EFE", #CJK UNIFIED IDEOGRAPH
+ "9C 50 5F03", #CJK UNIFIED IDEOGRAPH
+ "9C 51 5F09", #CJK UNIFIED IDEOGRAPH
+ "9C 52 5F5D", #CJK UNIFIED IDEOGRAPH
+ "9C 53 5F5C", #CJK UNIFIED IDEOGRAPH
+ "9C 54 5F0B", #CJK UNIFIED IDEOGRAPH
+ "9C 55 5F11", #CJK UNIFIED IDEOGRAPH
+ "9C 56 5F16", #CJK UNIFIED IDEOGRAPH
+ "9C 57 5F29", #CJK UNIFIED IDEOGRAPH
+ "9C 58 5F2D", #CJK UNIFIED IDEOGRAPH
+ "9C 59 5F38", #CJK UNIFIED IDEOGRAPH
+ "9C 5A 5F41", #CJK UNIFIED IDEOGRAPH
+ "9C 5B 5F48", #CJK UNIFIED IDEOGRAPH
+ "9C 5C 5F4C", #CJK UNIFIED IDEOGRAPH
+ "9C 5D 5F4E", #CJK UNIFIED IDEOGRAPH
+ "9C 5E 5F2F", #CJK UNIFIED IDEOGRAPH
+ "9C 5F 5F51", #CJK UNIFIED IDEOGRAPH
+ "9C 60 5F56", #CJK UNIFIED IDEOGRAPH
+ "9C 61 5F57", #CJK UNIFIED IDEOGRAPH
+ "9C 62 5F59", #CJK UNIFIED IDEOGRAPH
+ "9C 63 5F61", #CJK UNIFIED IDEOGRAPH
+ "9C 64 5F6D", #CJK UNIFIED IDEOGRAPH
+ "9C 65 5F73", #CJK UNIFIED IDEOGRAPH
+ "9C 66 5F77", #CJK UNIFIED IDEOGRAPH
+ "9C 67 5F83", #CJK UNIFIED IDEOGRAPH
+ "9C 68 5F82", #CJK UNIFIED IDEOGRAPH
+ "9C 69 5F7F", #CJK UNIFIED IDEOGRAPH
+ "9C 6A 5F8A", #CJK UNIFIED IDEOGRAPH
+ "9C 6B 5F88", #CJK UNIFIED IDEOGRAPH
+ "9C 6C 5F91", #CJK UNIFIED IDEOGRAPH
+ "9C 6D 5F87", #CJK UNIFIED IDEOGRAPH
+ "9C 6E 5F9E", #CJK UNIFIED IDEOGRAPH
+ "9C 6F 5F99", #CJK UNIFIED IDEOGRAPH
+ "9C 70 5F98", #CJK UNIFIED IDEOGRAPH
+ "9C 71 5FA0", #CJK UNIFIED IDEOGRAPH
+ "9C 72 5FA8", #CJK UNIFIED IDEOGRAPH
+ "9C 73 5FAD", #CJK UNIFIED IDEOGRAPH
+ "9C 74 5FBC", #CJK UNIFIED IDEOGRAPH
+ "9C 75 5FD6", #CJK UNIFIED IDEOGRAPH
+ "9C 76 5FFB", #CJK UNIFIED IDEOGRAPH
+ "9C 77 5FE4", #CJK UNIFIED IDEOGRAPH
+ "9C 78 5FF8", #CJK UNIFIED IDEOGRAPH
+ "9C 79 5FF1", #CJK UNIFIED IDEOGRAPH
+ "9C 7A 5FDD", #CJK UNIFIED IDEOGRAPH
+ "9C 7B 60B3", #CJK UNIFIED IDEOGRAPH
+ "9C 7C 5FFF", #CJK UNIFIED IDEOGRAPH
+ "9C 7D 6021", #CJK UNIFIED IDEOGRAPH
+ "9C 7E 6060", #CJK UNIFIED IDEOGRAPH
+ "9C 80 6019", #CJK UNIFIED IDEOGRAPH
+ "9C 81 6010", #CJK UNIFIED IDEOGRAPH
+ "9C 82 6029", #CJK UNIFIED IDEOGRAPH
+ "9C 83 600E", #CJK UNIFIED IDEOGRAPH
+ "9C 84 6031", #CJK UNIFIED IDEOGRAPH
+ "9C 85 601B", #CJK UNIFIED IDEOGRAPH
+ "9C 86 6015", #CJK UNIFIED IDEOGRAPH
+ "9C 87 602B", #CJK UNIFIED IDEOGRAPH
+ "9C 88 6026", #CJK UNIFIED IDEOGRAPH
+ "9C 89 600F", #CJK UNIFIED IDEOGRAPH
+ "9C 8A 603A", #CJK UNIFIED IDEOGRAPH
+ "9C 8B 605A", #CJK UNIFIED IDEOGRAPH
+ "9C 8C 6041", #CJK UNIFIED IDEOGRAPH
+ "9C 8D 606A", #CJK UNIFIED IDEOGRAPH
+ "9C 8E 6077", #CJK UNIFIED IDEOGRAPH
+ "9C 8F 605F", #CJK UNIFIED IDEOGRAPH
+ "9C 90 604A", #CJK UNIFIED IDEOGRAPH
+ "9C 91 6046", #CJK UNIFIED IDEOGRAPH
+ "9C 92 604D", #CJK UNIFIED IDEOGRAPH
+ "9C 93 6063", #CJK UNIFIED IDEOGRAPH
+ "9C 94 6043", #CJK UNIFIED IDEOGRAPH
+ "9C 95 6064", #CJK UNIFIED IDEOGRAPH
+ "9C 96 6042", #CJK UNIFIED IDEOGRAPH
+ "9C 97 606C", #CJK UNIFIED IDEOGRAPH
+ "9C 98 606B", #CJK UNIFIED IDEOGRAPH
+ "9C 99 6059", #CJK UNIFIED IDEOGRAPH
+ "9C 9A 6081", #CJK UNIFIED IDEOGRAPH
+ "9C 9B 608D", #CJK UNIFIED IDEOGRAPH
+ "9C 9C 60E7", #CJK UNIFIED IDEOGRAPH
+ "9C 9D 6083", #CJK UNIFIED IDEOGRAPH
+ "9C 9E 609A", #CJK UNIFIED IDEOGRAPH
+ "9C 9F 6084", #CJK UNIFIED IDEOGRAPH
+ "9C A0 609B", #CJK UNIFIED IDEOGRAPH
+ "9C A1 6096", #CJK UNIFIED IDEOGRAPH
+ "9C A2 6097", #CJK UNIFIED IDEOGRAPH
+ "9C A3 6092", #CJK UNIFIED IDEOGRAPH
+ "9C A4 60A7", #CJK UNIFIED IDEOGRAPH
+ "9C A5 608B", #CJK UNIFIED IDEOGRAPH
+ "9C A6 60E1", #CJK UNIFIED IDEOGRAPH
+ "9C A7 60B8", #CJK UNIFIED IDEOGRAPH
+ "9C A8 60E0", #CJK UNIFIED IDEOGRAPH
+ "9C A9 60D3", #CJK UNIFIED IDEOGRAPH
+ "9C AA 60B4", #CJK UNIFIED IDEOGRAPH
+ "9C AB 5FF0", #CJK UNIFIED IDEOGRAPH
+ "9C AC 60BD", #CJK UNIFIED IDEOGRAPH
+ "9C AD 60C6", #CJK UNIFIED IDEOGRAPH
+ "9C AE 60B5", #CJK UNIFIED IDEOGRAPH
+ "9C AF 60D8", #CJK UNIFIED IDEOGRAPH
+ "9C B0 614D", #CJK UNIFIED IDEOGRAPH
+ "9C B1 6115", #CJK UNIFIED IDEOGRAPH
+ "9C B2 6106", #CJK UNIFIED IDEOGRAPH
+ "9C B3 60F6", #CJK UNIFIED IDEOGRAPH
+ "9C B4 60F7", #CJK UNIFIED IDEOGRAPH
+ "9C B5 6100", #CJK UNIFIED IDEOGRAPH
+ "9C B6 60F4", #CJK UNIFIED IDEOGRAPH
+ "9C B7 60FA", #CJK UNIFIED IDEOGRAPH
+ "9C B8 6103", #CJK UNIFIED IDEOGRAPH
+ "9C B9 6121", #CJK UNIFIED IDEOGRAPH
+ "9C BA 60FB", #CJK UNIFIED IDEOGRAPH
+ "9C BB 60F1", #CJK UNIFIED IDEOGRAPH
+ "9C BC 610D", #CJK UNIFIED IDEOGRAPH
+ "9C BD 610E", #CJK UNIFIED IDEOGRAPH
+ "9C BE 6147", #CJK UNIFIED IDEOGRAPH
+ "9C BF 613E", #CJK UNIFIED IDEOGRAPH
+ "9C C0 6128", #CJK UNIFIED IDEOGRAPH
+ "9C C1 6127", #CJK UNIFIED IDEOGRAPH
+ "9C C2 614A", #CJK UNIFIED IDEOGRAPH
+ "9C C3 613F", #CJK UNIFIED IDEOGRAPH
+ "9C C4 613C", #CJK UNIFIED IDEOGRAPH
+ "9C C5 612C", #CJK UNIFIED IDEOGRAPH
+ "9C C6 6134", #CJK UNIFIED IDEOGRAPH
+ "9C C7 613D", #CJK UNIFIED IDEOGRAPH
+ "9C C8 6142", #CJK UNIFIED IDEOGRAPH
+ "9C C9 6144", #CJK UNIFIED IDEOGRAPH
+ "9C CA 6173", #CJK UNIFIED IDEOGRAPH
+ "9C CB 6177", #CJK UNIFIED IDEOGRAPH
+ "9C CC 6158", #CJK UNIFIED IDEOGRAPH
+ "9C CD 6159", #CJK UNIFIED IDEOGRAPH
+ "9C CE 615A", #CJK UNIFIED IDEOGRAPH
+ "9C CF 616B", #CJK UNIFIED IDEOGRAPH
+ "9C D0 6174", #CJK UNIFIED IDEOGRAPH
+ "9C D1 616F", #CJK UNIFIED IDEOGRAPH
+ "9C D2 6165", #CJK UNIFIED IDEOGRAPH
+ "9C D3 6171", #CJK UNIFIED IDEOGRAPH
+ "9C D4 615F", #CJK UNIFIED IDEOGRAPH
+ "9C D5 615D", #CJK UNIFIED IDEOGRAPH
+ "9C D6 6153", #CJK UNIFIED IDEOGRAPH
+ "9C D7 6175", #CJK UNIFIED IDEOGRAPH
+ "9C D8 6199", #CJK UNIFIED IDEOGRAPH
+ "9C D9 6196", #CJK UNIFIED IDEOGRAPH
+ "9C DA 6187", #CJK UNIFIED IDEOGRAPH
+ "9C DB 61AC", #CJK UNIFIED IDEOGRAPH
+ "9C DC 6194", #CJK UNIFIED IDEOGRAPH
+ "9C DD 619A", #CJK UNIFIED IDEOGRAPH
+ "9C DE 618A", #CJK UNIFIED IDEOGRAPH
+ "9C DF 6191", #CJK UNIFIED IDEOGRAPH
+ "9C E0 61AB", #CJK UNIFIED IDEOGRAPH
+ "9C E1 61AE", #CJK UNIFIED IDEOGRAPH
+ "9C E2 61CC", #CJK UNIFIED IDEOGRAPH
+ "9C E3 61CA", #CJK UNIFIED IDEOGRAPH
+ "9C E4 61C9", #CJK UNIFIED IDEOGRAPH
+ "9C E5 61F7", #CJK UNIFIED IDEOGRAPH
+ "9C E6 61C8", #CJK UNIFIED IDEOGRAPH
+ "9C E7 61C3", #CJK UNIFIED IDEOGRAPH
+ "9C E8 61C6", #CJK UNIFIED IDEOGRAPH
+ "9C E9 61BA", #CJK UNIFIED IDEOGRAPH
+ "9C EA 61CB", #CJK UNIFIED IDEOGRAPH
+ "9C EB 7F79", #CJK UNIFIED IDEOGRAPH
+ "9C EC 61CD", #CJK UNIFIED IDEOGRAPH
+ "9C ED 61E6", #CJK UNIFIED IDEOGRAPH
+ "9C EE 61E3", #CJK UNIFIED IDEOGRAPH
+ "9C EF 61F6", #CJK UNIFIED IDEOGRAPH
+ "9C F0 61FA", #CJK UNIFIED IDEOGRAPH
+ "9C F1 61F4", #CJK UNIFIED IDEOGRAPH
+ "9C F2 61FF", #CJK UNIFIED IDEOGRAPH
+ "9C F3 61FD", #CJK UNIFIED IDEOGRAPH
+ "9C F4 61FC", #CJK UNIFIED IDEOGRAPH
+ "9C F5 61FE", #CJK UNIFIED IDEOGRAPH
+ "9C F6 6200", #CJK UNIFIED IDEOGRAPH
+ "9C F7 6208", #CJK UNIFIED IDEOGRAPH
+ "9C F8 6209", #CJK UNIFIED IDEOGRAPH
+ "9C F9 620D", #CJK UNIFIED IDEOGRAPH
+ "9C FA 620C", #CJK UNIFIED IDEOGRAPH
+ "9C FB 6214", #CJK UNIFIED IDEOGRAPH
+ "9C FC 621B", #CJK UNIFIED IDEOGRAPH
+ "9D 40 621E", #CJK UNIFIED IDEOGRAPH
+ "9D 41 6221", #CJK UNIFIED IDEOGRAPH
+ "9D 42 622A", #CJK UNIFIED IDEOGRAPH
+ "9D 43 622E", #CJK UNIFIED IDEOGRAPH
+ "9D 44 6230", #CJK UNIFIED IDEOGRAPH
+ "9D 45 6232", #CJK UNIFIED IDEOGRAPH
+ "9D 46 6233", #CJK UNIFIED IDEOGRAPH
+ "9D 47 6241", #CJK UNIFIED IDEOGRAPH
+ "9D 48 624E", #CJK UNIFIED IDEOGRAPH
+ "9D 49 625E", #CJK UNIFIED IDEOGRAPH
+ "9D 4A 6263", #CJK UNIFIED IDEOGRAPH
+ "9D 4B 625B", #CJK UNIFIED IDEOGRAPH
+ "9D 4C 6260", #CJK UNIFIED IDEOGRAPH
+ "9D 4D 6268", #CJK UNIFIED IDEOGRAPH
+ "9D 4E 627C", #CJK UNIFIED IDEOGRAPH
+ "9D 4F 6282", #CJK UNIFIED IDEOGRAPH
+ "9D 50 6289", #CJK UNIFIED IDEOGRAPH
+ "9D 51 627E", #CJK UNIFIED IDEOGRAPH
+ "9D 52 6292", #CJK UNIFIED IDEOGRAPH
+ "9D 53 6293", #CJK UNIFIED IDEOGRAPH
+ "9D 54 6296", #CJK UNIFIED IDEOGRAPH
+ "9D 55 62D4", #CJK UNIFIED IDEOGRAPH
+ "9D 56 6283", #CJK UNIFIED IDEOGRAPH
+ "9D 57 6294", #CJK UNIFIED IDEOGRAPH
+ "9D 58 62D7", #CJK UNIFIED IDEOGRAPH
+ "9D 59 62D1", #CJK UNIFIED IDEOGRAPH
+ "9D 5A 62BB", #CJK UNIFIED IDEOGRAPH
+ "9D 5B 62CF", #CJK UNIFIED IDEOGRAPH
+ "9D 5C 62FF", #CJK UNIFIED IDEOGRAPH
+ "9D 5D 62C6", #CJK UNIFIED IDEOGRAPH
+ "9D 5E 64D4", #CJK UNIFIED IDEOGRAPH
+ "9D 5F 62C8", #CJK UNIFIED IDEOGRAPH
+ "9D 60 62DC", #CJK UNIFIED IDEOGRAPH
+ "9D 61 62CC", #CJK UNIFIED IDEOGRAPH
+ "9D 62 62CA", #CJK UNIFIED IDEOGRAPH
+ "9D 63 62C2", #CJK UNIFIED IDEOGRAPH
+ "9D 64 62C7", #CJK UNIFIED IDEOGRAPH
+ "9D 65 629B", #CJK UNIFIED IDEOGRAPH
+ "9D 66 62C9", #CJK UNIFIED IDEOGRAPH
+ "9D 67 630C", #CJK UNIFIED IDEOGRAPH
+ "9D 68 62EE", #CJK UNIFIED IDEOGRAPH
+ "9D 69 62F1", #CJK UNIFIED IDEOGRAPH
+ "9D 6A 6327", #CJK UNIFIED IDEOGRAPH
+ "9D 6B 6302", #CJK UNIFIED IDEOGRAPH
+ "9D 6C 6308", #CJK UNIFIED IDEOGRAPH
+ "9D 6D 62EF", #CJK UNIFIED IDEOGRAPH
+ "9D 6E 62F5", #CJK UNIFIED IDEOGRAPH
+ "9D 6F 6350", #CJK UNIFIED IDEOGRAPH
+ "9D 70 633E", #CJK UNIFIED IDEOGRAPH
+ "9D 71 634D", #CJK UNIFIED IDEOGRAPH
+ "9D 72 641C", #CJK UNIFIED IDEOGRAPH
+ "9D 73 634F", #CJK UNIFIED IDEOGRAPH
+ "9D 74 6396", #CJK UNIFIED IDEOGRAPH
+ "9D 75 638E", #CJK UNIFIED IDEOGRAPH
+ "9D 76 6380", #CJK UNIFIED IDEOGRAPH
+ "9D 77 63AB", #CJK UNIFIED IDEOGRAPH
+ "9D 78 6376", #CJK UNIFIED IDEOGRAPH
+ "9D 79 63A3", #CJK UNIFIED IDEOGRAPH
+ "9D 7A 638F", #CJK UNIFIED IDEOGRAPH
+ "9D 7B 6389", #CJK UNIFIED IDEOGRAPH
+ "9D 7C 639F", #CJK UNIFIED IDEOGRAPH
+ "9D 7D 63B5", #CJK UNIFIED IDEOGRAPH
+ "9D 7E 636B", #CJK UNIFIED IDEOGRAPH
+ "9D 80 6369", #CJK UNIFIED IDEOGRAPH
+ "9D 81 63BE", #CJK UNIFIED IDEOGRAPH
+ "9D 82 63E9", #CJK UNIFIED IDEOGRAPH
+ "9D 83 63C0", #CJK UNIFIED IDEOGRAPH
+ "9D 84 63C6", #CJK UNIFIED IDEOGRAPH
+ "9D 85 63E3", #CJK UNIFIED IDEOGRAPH
+ "9D 86 63C9", #CJK UNIFIED IDEOGRAPH
+ "9D 87 63D2", #CJK UNIFIED IDEOGRAPH
+ "9D 88 63F6", #CJK UNIFIED IDEOGRAPH
+ "9D 89 63C4", #CJK UNIFIED IDEOGRAPH
+ "9D 8A 6416", #CJK UNIFIED IDEOGRAPH
+ "9D 8B 6434", #CJK UNIFIED IDEOGRAPH
+ "9D 8C 6406", #CJK UNIFIED IDEOGRAPH
+ "9D 8D 6413", #CJK UNIFIED IDEOGRAPH
+ "9D 8E 6426", #CJK UNIFIED IDEOGRAPH
+ "9D 8F 6436", #CJK UNIFIED IDEOGRAPH
+ "9D 90 651D", #CJK UNIFIED IDEOGRAPH
+ "9D 91 6417", #CJK UNIFIED IDEOGRAPH
+ "9D 92 6428", #CJK UNIFIED IDEOGRAPH
+ "9D 93 640F", #CJK UNIFIED IDEOGRAPH
+ "9D 94 6467", #CJK UNIFIED IDEOGRAPH
+ "9D 95 646F", #CJK UNIFIED IDEOGRAPH
+ "9D 96 6476", #CJK UNIFIED IDEOGRAPH
+ "9D 97 644E", #CJK UNIFIED IDEOGRAPH
+ "9D 98 652A", #CJK UNIFIED IDEOGRAPH
+ "9D 99 6495", #CJK UNIFIED IDEOGRAPH
+ "9D 9A 6493", #CJK UNIFIED IDEOGRAPH
+ "9D 9B 64A5", #CJK UNIFIED IDEOGRAPH
+ "9D 9C 64A9", #CJK UNIFIED IDEOGRAPH
+ "9D 9D 6488", #CJK UNIFIED IDEOGRAPH
+ "9D 9E 64BC", #CJK UNIFIED IDEOGRAPH
+ "9D 9F 64DA", #CJK UNIFIED IDEOGRAPH
+ "9D A0 64D2", #CJK UNIFIED IDEOGRAPH
+ "9D A1 64C5", #CJK UNIFIED IDEOGRAPH
+ "9D A2 64C7", #CJK UNIFIED IDEOGRAPH
+ "9D A3 64BB", #CJK UNIFIED IDEOGRAPH
+ "9D A4 64D8", #CJK UNIFIED IDEOGRAPH
+ "9D A5 64C2", #CJK UNIFIED IDEOGRAPH
+ "9D A6 64F1", #CJK UNIFIED IDEOGRAPH
+ "9D A7 64E7", #CJK UNIFIED IDEOGRAPH
+ "9D A8 8209", #CJK UNIFIED IDEOGRAPH
+ "9D A9 64E0", #CJK UNIFIED IDEOGRAPH
+ "9D AA 64E1", #CJK UNIFIED IDEOGRAPH
+ "9D AB 62AC", #CJK UNIFIED IDEOGRAPH
+ "9D AC 64E3", #CJK UNIFIED IDEOGRAPH
+ "9D AD 64EF", #CJK UNIFIED IDEOGRAPH
+ "9D AE 652C", #CJK UNIFIED IDEOGRAPH
+ "9D AF 64F6", #CJK UNIFIED IDEOGRAPH
+ "9D B0 64F4", #CJK UNIFIED IDEOGRAPH
+ "9D B1 64F2", #CJK UNIFIED IDEOGRAPH
+ "9D B2 64FA", #CJK UNIFIED IDEOGRAPH
+ "9D B3 6500", #CJK UNIFIED IDEOGRAPH
+ "9D B4 64FD", #CJK UNIFIED IDEOGRAPH
+ "9D B5 6518", #CJK UNIFIED IDEOGRAPH
+ "9D B6 651C", #CJK UNIFIED IDEOGRAPH
+ "9D B7 6505", #CJK UNIFIED IDEOGRAPH
+ "9D B8 6524", #CJK UNIFIED IDEOGRAPH
+ "9D B9 6523", #CJK UNIFIED IDEOGRAPH
+ "9D BA 652B", #CJK UNIFIED IDEOGRAPH
+ "9D BB 6534", #CJK UNIFIED IDEOGRAPH
+ "9D BC 6535", #CJK UNIFIED IDEOGRAPH
+ "9D BD 6537", #CJK UNIFIED IDEOGRAPH
+ "9D BE 6536", #CJK UNIFIED IDEOGRAPH
+ "9D BF 6538", #CJK UNIFIED IDEOGRAPH
+ "9D C0 754B", #CJK UNIFIED IDEOGRAPH
+ "9D C1 6548", #CJK UNIFIED IDEOGRAPH
+ "9D C2 6556", #CJK UNIFIED IDEOGRAPH
+ "9D C3 6555", #CJK UNIFIED IDEOGRAPH
+ "9D C4 654D", #CJK UNIFIED IDEOGRAPH
+ "9D C5 6558", #CJK UNIFIED IDEOGRAPH
+ "9D C6 655E", #CJK UNIFIED IDEOGRAPH
+ "9D C7 655D", #CJK UNIFIED IDEOGRAPH
+ "9D C8 6572", #CJK UNIFIED IDEOGRAPH
+ "9D C9 6578", #CJK UNIFIED IDEOGRAPH
+ "9D CA 6582", #CJK UNIFIED IDEOGRAPH
+ "9D CB 6583", #CJK UNIFIED IDEOGRAPH
+ "9D CC 8B8A", #CJK UNIFIED IDEOGRAPH
+ "9D CD 659B", #CJK UNIFIED IDEOGRAPH
+ "9D CE 659F", #CJK UNIFIED IDEOGRAPH
+ "9D CF 65AB", #CJK UNIFIED IDEOGRAPH
+ "9D D0 65B7", #CJK UNIFIED IDEOGRAPH
+ "9D D1 65C3", #CJK UNIFIED IDEOGRAPH
+ "9D D2 65C6", #CJK UNIFIED IDEOGRAPH
+ "9D D3 65C1", #CJK UNIFIED IDEOGRAPH
+ "9D D4 65C4", #CJK UNIFIED IDEOGRAPH
+ "9D D5 65CC", #CJK UNIFIED IDEOGRAPH
+ "9D D6 65D2", #CJK UNIFIED IDEOGRAPH
+ "9D D7 65DB", #CJK UNIFIED IDEOGRAPH
+ "9D D8 65D9", #CJK UNIFIED IDEOGRAPH
+ "9D D9 65E0", #CJK UNIFIED IDEOGRAPH
+ "9D DA 65E1", #CJK UNIFIED IDEOGRAPH
+ "9D DB 65F1", #CJK UNIFIED IDEOGRAPH
+ "9D DC 6772", #CJK UNIFIED IDEOGRAPH
+ "9D DD 660A", #CJK UNIFIED IDEOGRAPH
+ "9D DE 6603", #CJK UNIFIED IDEOGRAPH
+ "9D DF 65FB", #CJK UNIFIED IDEOGRAPH
+ "9D E0 6773", #CJK UNIFIED IDEOGRAPH
+ "9D E1 6635", #CJK UNIFIED IDEOGRAPH
+ "9D E2 6636", #CJK UNIFIED IDEOGRAPH
+ "9D E3 6634", #CJK UNIFIED IDEOGRAPH
+ "9D E4 661C", #CJK UNIFIED IDEOGRAPH
+ "9D E5 664F", #CJK UNIFIED IDEOGRAPH
+ "9D E6 6644", #CJK UNIFIED IDEOGRAPH
+ "9D E7 6649", #CJK UNIFIED IDEOGRAPH
+ "9D E8 6641", #CJK UNIFIED IDEOGRAPH
+ "9D E9 665E", #CJK UNIFIED IDEOGRAPH
+ "9D EA 665D", #CJK UNIFIED IDEOGRAPH
+ "9D EB 6664", #CJK UNIFIED IDEOGRAPH
+ "9D EC 6667", #CJK UNIFIED IDEOGRAPH
+ "9D ED 6668", #CJK UNIFIED IDEOGRAPH
+ "9D EE 665F", #CJK UNIFIED IDEOGRAPH
+ "9D EF 6662", #CJK UNIFIED IDEOGRAPH
+ "9D F0 6670", #CJK UNIFIED IDEOGRAPH
+ "9D F1 6683", #CJK UNIFIED IDEOGRAPH
+ "9D F2 6688", #CJK UNIFIED IDEOGRAPH
+ "9D F3 668E", #CJK UNIFIED IDEOGRAPH
+ "9D F4 6689", #CJK UNIFIED IDEOGRAPH
+ "9D F5 6684", #CJK UNIFIED IDEOGRAPH
+ "9D F6 6698", #CJK UNIFIED IDEOGRAPH
+ "9D F7 669D", #CJK UNIFIED IDEOGRAPH
+ "9D F8 66C1", #CJK UNIFIED IDEOGRAPH
+ "9D F9 66B9", #CJK UNIFIED IDEOGRAPH
+ "9D FA 66C9", #CJK UNIFIED IDEOGRAPH
+ "9D FB 66BE", #CJK UNIFIED IDEOGRAPH
+ "9D FC 66BC", #CJK UNIFIED IDEOGRAPH
+ "9E 40 66C4", #CJK UNIFIED IDEOGRAPH
+ "9E 41 66B8", #CJK UNIFIED IDEOGRAPH
+ "9E 42 66D6", #CJK UNIFIED IDEOGRAPH
+ "9E 43 66DA", #CJK UNIFIED IDEOGRAPH
+ "9E 44 66E0", #CJK UNIFIED IDEOGRAPH
+ "9E 45 663F", #CJK UNIFIED IDEOGRAPH
+ "9E 46 66E6", #CJK UNIFIED IDEOGRAPH
+ "9E 47 66E9", #CJK UNIFIED IDEOGRAPH
+ "9E 48 66F0", #CJK UNIFIED IDEOGRAPH
+ "9E 49 66F5", #CJK UNIFIED IDEOGRAPH
+ "9E 4A 66F7", #CJK UNIFIED IDEOGRAPH
+ "9E 4B 670F", #CJK UNIFIED IDEOGRAPH
+ "9E 4C 6716", #CJK UNIFIED IDEOGRAPH
+ "9E 4D 671E", #CJK UNIFIED IDEOGRAPH
+ "9E 4E 6726", #CJK UNIFIED IDEOGRAPH
+ "9E 4F 6727", #CJK UNIFIED IDEOGRAPH
+ "9E 50 9738", #CJK UNIFIED IDEOGRAPH
+ "9E 51 672E", #CJK UNIFIED IDEOGRAPH
+ "9E 52 673F", #CJK UNIFIED IDEOGRAPH
+ "9E 53 6736", #CJK UNIFIED IDEOGRAPH
+ "9E 54 6741", #CJK UNIFIED IDEOGRAPH
+ "9E 55 6738", #CJK UNIFIED IDEOGRAPH
+ "9E 56 6737", #CJK UNIFIED IDEOGRAPH
+ "9E 57 6746", #CJK UNIFIED IDEOGRAPH
+ "9E 58 675E", #CJK UNIFIED IDEOGRAPH
+ "9E 59 6760", #CJK UNIFIED IDEOGRAPH
+ "9E 5A 6759", #CJK UNIFIED IDEOGRAPH
+ "9E 5B 6763", #CJK UNIFIED IDEOGRAPH
+ "9E 5C 6764", #CJK UNIFIED IDEOGRAPH
+ "9E 5D 6789", #CJK UNIFIED IDEOGRAPH
+ "9E 5E 6770", #CJK UNIFIED IDEOGRAPH
+ "9E 5F 67A9", #CJK UNIFIED IDEOGRAPH
+ "9E 60 677C", #CJK UNIFIED IDEOGRAPH
+ "9E 61 676A", #CJK UNIFIED IDEOGRAPH
+ "9E 62 678C", #CJK UNIFIED IDEOGRAPH
+ "9E 63 678B", #CJK UNIFIED IDEOGRAPH
+ "9E 64 67A6", #CJK UNIFIED IDEOGRAPH
+ "9E 65 67A1", #CJK UNIFIED IDEOGRAPH
+ "9E 66 6785", #CJK UNIFIED IDEOGRAPH
+ "9E 67 67B7", #CJK UNIFIED IDEOGRAPH
+ "9E 68 67EF", #CJK UNIFIED IDEOGRAPH
+ "9E 69 67B4", #CJK UNIFIED IDEOGRAPH
+ "9E 6A 67EC", #CJK UNIFIED IDEOGRAPH
+ "9E 6B 67B3", #CJK UNIFIED IDEOGRAPH
+ "9E 6C 67E9", #CJK UNIFIED IDEOGRAPH
+ "9E 6D 67B8", #CJK UNIFIED IDEOGRAPH
+ "9E 6E 67E4", #CJK UNIFIED IDEOGRAPH
+ "9E 6F 67DE", #CJK UNIFIED IDEOGRAPH
+ "9E 70 67DD", #CJK UNIFIED IDEOGRAPH
+ "9E 71 67E2", #CJK UNIFIED IDEOGRAPH
+ "9E 72 67EE", #CJK UNIFIED IDEOGRAPH
+ "9E 73 67B9", #CJK UNIFIED IDEOGRAPH
+ "9E 74 67CE", #CJK UNIFIED IDEOGRAPH
+ "9E 75 67C6", #CJK UNIFIED IDEOGRAPH
+ "9E 76 67E7", #CJK UNIFIED IDEOGRAPH
+ "9E 77 6A9C", #CJK UNIFIED IDEOGRAPH
+ "9E 78 681E", #CJK UNIFIED IDEOGRAPH
+ "9E 79 6846", #CJK UNIFIED IDEOGRAPH
+ "9E 7A 6829", #CJK UNIFIED IDEOGRAPH
+ "9E 7B 6840", #CJK UNIFIED IDEOGRAPH
+ "9E 7C 684D", #CJK UNIFIED IDEOGRAPH
+ "9E 7D 6832", #CJK UNIFIED IDEOGRAPH
+ "9E 7E 684E", #CJK UNIFIED IDEOGRAPH
+ "9E 80 68B3", #CJK UNIFIED IDEOGRAPH
+ "9E 81 682B", #CJK UNIFIED IDEOGRAPH
+ "9E 82 6859", #CJK UNIFIED IDEOGRAPH
+ "9E 83 6863", #CJK UNIFIED IDEOGRAPH
+ "9E 84 6877", #CJK UNIFIED IDEOGRAPH
+ "9E 85 687F", #CJK UNIFIED IDEOGRAPH
+ "9E 86 689F", #CJK UNIFIED IDEOGRAPH
+ "9E 87 688F", #CJK UNIFIED IDEOGRAPH
+ "9E 88 68AD", #CJK UNIFIED IDEOGRAPH
+ "9E 89 6894", #CJK UNIFIED IDEOGRAPH
+ "9E 8A 689D", #CJK UNIFIED IDEOGRAPH
+ "9E 8B 689B", #CJK UNIFIED IDEOGRAPH
+ "9E 8C 6883", #CJK UNIFIED IDEOGRAPH
+ "9E 8D 6AAE", #CJK UNIFIED IDEOGRAPH
+ "9E 8E 68B9", #CJK UNIFIED IDEOGRAPH
+ "9E 8F 6874", #CJK UNIFIED IDEOGRAPH
+ "9E 90 68B5", #CJK UNIFIED IDEOGRAPH
+ "9E 91 68A0", #CJK UNIFIED IDEOGRAPH
+ "9E 92 68BA", #CJK UNIFIED IDEOGRAPH
+ "9E 93 690F", #CJK UNIFIED IDEOGRAPH
+ "9E 94 688D", #CJK UNIFIED IDEOGRAPH
+ "9E 95 687E", #CJK UNIFIED IDEOGRAPH
+ "9E 96 6901", #CJK UNIFIED IDEOGRAPH
+ "9E 97 68CA", #CJK UNIFIED IDEOGRAPH
+ "9E 98 6908", #CJK UNIFIED IDEOGRAPH
+ "9E 99 68D8", #CJK UNIFIED IDEOGRAPH
+ "9E 9A 6922", #CJK UNIFIED IDEOGRAPH
+ "9E 9B 6926", #CJK UNIFIED IDEOGRAPH
+ "9E 9C 68E1", #CJK UNIFIED IDEOGRAPH
+ "9E 9D 690C", #CJK UNIFIED IDEOGRAPH
+ "9E 9E 68CD", #CJK UNIFIED IDEOGRAPH
+ "9E 9F 68D4", #CJK UNIFIED IDEOGRAPH
+ "9E A0 68E7", #CJK UNIFIED IDEOGRAPH
+ "9E A1 68D5", #CJK UNIFIED IDEOGRAPH
+ "9E A2 6936", #CJK UNIFIED IDEOGRAPH
+ "9E A3 6912", #CJK UNIFIED IDEOGRAPH
+ "9E A4 6904", #CJK UNIFIED IDEOGRAPH
+ "9E A5 68D7", #CJK UNIFIED IDEOGRAPH
+ "9E A6 68E3", #CJK UNIFIED IDEOGRAPH
+ "9E A7 6925", #CJK UNIFIED IDEOGRAPH
+ "9E A8 68F9", #CJK UNIFIED IDEOGRAPH
+ "9E A9 68E0", #CJK UNIFIED IDEOGRAPH
+ "9E AA 68EF", #CJK UNIFIED IDEOGRAPH
+ "9E AB 6928", #CJK UNIFIED IDEOGRAPH
+ "9E AC 692A", #CJK UNIFIED IDEOGRAPH
+ "9E AD 691A", #CJK UNIFIED IDEOGRAPH
+ "9E AE 6923", #CJK UNIFIED IDEOGRAPH
+ "9E AF 6921", #CJK UNIFIED IDEOGRAPH
+ "9E B0 68C6", #CJK UNIFIED IDEOGRAPH
+ "9E B1 6979", #CJK UNIFIED IDEOGRAPH
+ "9E B2 6977", #CJK UNIFIED IDEOGRAPH
+ "9E B3 695C", #CJK UNIFIED IDEOGRAPH
+ "9E B4 6978", #CJK UNIFIED IDEOGRAPH
+ "9E B5 696B", #CJK UNIFIED IDEOGRAPH
+ "9E B6 6954", #CJK UNIFIED IDEOGRAPH
+ "9E B7 697E", #CJK UNIFIED IDEOGRAPH
+ "9E B8 696E", #CJK UNIFIED IDEOGRAPH
+ "9E B9 6939", #CJK UNIFIED IDEOGRAPH
+ "9E BA 6974", #CJK UNIFIED IDEOGRAPH
+ "9E BB 693D", #CJK UNIFIED IDEOGRAPH
+ "9E BC 6959", #CJK UNIFIED IDEOGRAPH
+ "9E BD 6930", #CJK UNIFIED IDEOGRAPH
+ "9E BE 6961", #CJK UNIFIED IDEOGRAPH
+ "9E BF 695E", #CJK UNIFIED IDEOGRAPH
+ "9E C0 695D", #CJK UNIFIED IDEOGRAPH
+ "9E C1 6981", #CJK UNIFIED IDEOGRAPH
+ "9E C2 696A", #CJK UNIFIED IDEOGRAPH
+ "9E C3 69B2", #CJK UNIFIED IDEOGRAPH
+ "9E C4 69AE", #CJK UNIFIED IDEOGRAPH
+ "9E C5 69D0", #CJK UNIFIED IDEOGRAPH
+ "9E C6 69BF", #CJK UNIFIED IDEOGRAPH
+ "9E C7 69C1", #CJK UNIFIED IDEOGRAPH
+ "9E C8 69D3", #CJK UNIFIED IDEOGRAPH
+ "9E C9 69BE", #CJK UNIFIED IDEOGRAPH
+ "9E CA 69CE", #CJK UNIFIED IDEOGRAPH
+ "9E CB 5BE8", #CJK UNIFIED IDEOGRAPH
+ "9E CC 69CA", #CJK UNIFIED IDEOGRAPH
+ "9E CD 69DD", #CJK UNIFIED IDEOGRAPH
+ "9E CE 69BB", #CJK UNIFIED IDEOGRAPH
+ "9E CF 69C3", #CJK UNIFIED IDEOGRAPH
+ "9E D0 69A7", #CJK UNIFIED IDEOGRAPH
+ "9E D1 6A2E", #CJK UNIFIED IDEOGRAPH
+ "9E D2 6991", #CJK UNIFIED IDEOGRAPH
+ "9E D3 69A0", #CJK UNIFIED IDEOGRAPH
+ "9E D4 699C", #CJK UNIFIED IDEOGRAPH
+ "9E D5 6995", #CJK UNIFIED IDEOGRAPH
+ "9E D6 69B4", #CJK UNIFIED IDEOGRAPH
+ "9E D7 69DE", #CJK UNIFIED IDEOGRAPH
+ "9E D8 69E8", #CJK UNIFIED IDEOGRAPH
+ "9E D9 6A02", #CJK UNIFIED IDEOGRAPH
+ "9E DA 6A1B", #CJK UNIFIED IDEOGRAPH
+ "9E DB 69FF", #CJK UNIFIED IDEOGRAPH
+ "9E DC 6B0A", #CJK UNIFIED IDEOGRAPH
+ "9E DD 69F9", #CJK UNIFIED IDEOGRAPH
+ "9E DE 69F2", #CJK UNIFIED IDEOGRAPH
+ "9E DF 69E7", #CJK UNIFIED IDEOGRAPH
+ "9E E0 6A05", #CJK UNIFIED IDEOGRAPH
+ "9E E1 69B1", #CJK UNIFIED IDEOGRAPH
+ "9E E2 6A1E", #CJK UNIFIED IDEOGRAPH
+ "9E E3 69ED", #CJK UNIFIED IDEOGRAPH
+ "9E E4 6A14", #CJK UNIFIED IDEOGRAPH
+ "9E E5 69EB", #CJK UNIFIED IDEOGRAPH
+ "9E E6 6A0A", #CJK UNIFIED IDEOGRAPH
+ "9E E7 6A12", #CJK UNIFIED IDEOGRAPH
+ "9E E8 6AC1", #CJK UNIFIED IDEOGRAPH
+ "9E E9 6A23", #CJK UNIFIED IDEOGRAPH
+ "9E EA 6A13", #CJK UNIFIED IDEOGRAPH
+ "9E EB 6A44", #CJK UNIFIED IDEOGRAPH
+ "9E EC 6A0C", #CJK UNIFIED IDEOGRAPH
+ "9E ED 6A72", #CJK UNIFIED IDEOGRAPH
+ "9E EE 6A36", #CJK UNIFIED IDEOGRAPH
+ "9E EF 6A78", #CJK UNIFIED IDEOGRAPH
+ "9E F0 6A47", #CJK UNIFIED IDEOGRAPH
+ "9E F1 6A62", #CJK UNIFIED IDEOGRAPH
+ "9E F2 6A59", #CJK UNIFIED IDEOGRAPH
+ "9E F3 6A66", #CJK UNIFIED IDEOGRAPH
+ "9E F4 6A48", #CJK UNIFIED IDEOGRAPH
+ "9E F5 6A38", #CJK UNIFIED IDEOGRAPH
+ "9E F6 6A22", #CJK UNIFIED IDEOGRAPH
+ "9E F7 6A90", #CJK UNIFIED IDEOGRAPH
+ "9E F8 6A8D", #CJK UNIFIED IDEOGRAPH
+ "9E F9 6AA0", #CJK UNIFIED IDEOGRAPH
+ "9E FA 6A84", #CJK UNIFIED IDEOGRAPH
+ "9E FB 6AA2", #CJK UNIFIED IDEOGRAPH
+ "9E FC 6AA3", #CJK UNIFIED IDEOGRAPH
+ "9F 40 6A97", #CJK UNIFIED IDEOGRAPH
+ "9F 41 8617", #CJK UNIFIED IDEOGRAPH
+ "9F 42 6ABB", #CJK UNIFIED IDEOGRAPH
+ "9F 43 6AC3", #CJK UNIFIED IDEOGRAPH
+ "9F 44 6AC2", #CJK UNIFIED IDEOGRAPH
+ "9F 45 6AB8", #CJK UNIFIED IDEOGRAPH
+ "9F 46 6AB3", #CJK UNIFIED IDEOGRAPH
+ "9F 47 6AAC", #CJK UNIFIED IDEOGRAPH
+ "9F 48 6ADE", #CJK UNIFIED IDEOGRAPH
+ "9F 49 6AD1", #CJK UNIFIED IDEOGRAPH
+ "9F 4A 6ADF", #CJK UNIFIED IDEOGRAPH
+ "9F 4B 6AAA", #CJK UNIFIED IDEOGRAPH
+ "9F 4C 6ADA", #CJK UNIFIED IDEOGRAPH
+ "9F 4D 6AEA", #CJK UNIFIED IDEOGRAPH
+ "9F 4E 6AFB", #CJK UNIFIED IDEOGRAPH
+ "9F 4F 6B05", #CJK UNIFIED IDEOGRAPH
+ "9F 50 8616", #CJK UNIFIED IDEOGRAPH
+ "9F 51 6AFA", #CJK UNIFIED IDEOGRAPH
+ "9F 52 6B12", #CJK UNIFIED IDEOGRAPH
+ "9F 53 6B16", #CJK UNIFIED IDEOGRAPH
+ "9F 54 9B31", #CJK UNIFIED IDEOGRAPH
+ "9F 55 6B1F", #CJK UNIFIED IDEOGRAPH
+ "9F 56 6B38", #CJK UNIFIED IDEOGRAPH
+ "9F 57 6B37", #CJK UNIFIED IDEOGRAPH
+ "9F 58 76DC", #CJK UNIFIED IDEOGRAPH
+ "9F 59 6B39", #CJK UNIFIED IDEOGRAPH
+ "9F 5A 98EE", #CJK UNIFIED IDEOGRAPH
+ "9F 5B 6B47", #CJK UNIFIED IDEOGRAPH
+ "9F 5C 6B43", #CJK UNIFIED IDEOGRAPH
+ "9F 5D 6B49", #CJK UNIFIED IDEOGRAPH
+ "9F 5E 6B50", #CJK UNIFIED IDEOGRAPH
+ "9F 5F 6B59", #CJK UNIFIED IDEOGRAPH
+ "9F 60 6B54", #CJK UNIFIED IDEOGRAPH
+ "9F 61 6B5B", #CJK UNIFIED IDEOGRAPH
+ "9F 62 6B5F", #CJK UNIFIED IDEOGRAPH
+ "9F 63 6B61", #CJK UNIFIED IDEOGRAPH
+ "9F 64 6B78", #CJK UNIFIED IDEOGRAPH
+ "9F 65 6B79", #CJK UNIFIED IDEOGRAPH
+ "9F 66 6B7F", #CJK UNIFIED IDEOGRAPH
+ "9F 67 6B80", #CJK UNIFIED IDEOGRAPH
+ "9F 68 6B84", #CJK UNIFIED IDEOGRAPH
+ "9F 69 6B83", #CJK UNIFIED IDEOGRAPH
+ "9F 6A 6B8D", #CJK UNIFIED IDEOGRAPH
+ "9F 6B 6B98", #CJK UNIFIED IDEOGRAPH
+ "9F 6C 6B95", #CJK UNIFIED IDEOGRAPH
+ "9F 6D 6B9E", #CJK UNIFIED IDEOGRAPH
+ "9F 6E 6BA4", #CJK UNIFIED IDEOGRAPH
+ "9F 6F 6BAA", #CJK UNIFIED IDEOGRAPH
+ "9F 70 6BAB", #CJK UNIFIED IDEOGRAPH
+ "9F 71 6BAF", #CJK UNIFIED IDEOGRAPH
+ "9F 72 6BB2", #CJK UNIFIED IDEOGRAPH
+ "9F 73 6BB1", #CJK UNIFIED IDEOGRAPH
+ "9F 74 6BB3", #CJK UNIFIED IDEOGRAPH
+ "9F 75 6BB7", #CJK UNIFIED IDEOGRAPH
+ "9F 76 6BBC", #CJK UNIFIED IDEOGRAPH
+ "9F 77 6BC6", #CJK UNIFIED IDEOGRAPH
+ "9F 78 6BCB", #CJK UNIFIED IDEOGRAPH
+ "9F 79 6BD3", #CJK UNIFIED IDEOGRAPH
+ "9F 7A 6BDF", #CJK UNIFIED IDEOGRAPH
+ "9F 7B 6BEC", #CJK UNIFIED IDEOGRAPH
+ "9F 7C 6BEB", #CJK UNIFIED IDEOGRAPH
+ "9F 7D 6BF3", #CJK UNIFIED IDEOGRAPH
+ "9F 7E 6BEF", #CJK UNIFIED IDEOGRAPH
+ "9F 80 9EBE", #CJK UNIFIED IDEOGRAPH
+ "9F 81 6C08", #CJK UNIFIED IDEOGRAPH
+ "9F 82 6C13", #CJK UNIFIED IDEOGRAPH
+ "9F 83 6C14", #CJK UNIFIED IDEOGRAPH
+ "9F 84 6C1B", #CJK UNIFIED IDEOGRAPH
+ "9F 85 6C24", #CJK UNIFIED IDEOGRAPH
+ "9F 86 6C23", #CJK UNIFIED IDEOGRAPH
+ "9F 87 6C5E", #CJK UNIFIED IDEOGRAPH
+ "9F 88 6C55", #CJK UNIFIED IDEOGRAPH
+ "9F 89 6C62", #CJK UNIFIED IDEOGRAPH
+ "9F 8A 6C6A", #CJK UNIFIED IDEOGRAPH
+ "9F 8B 6C82", #CJK UNIFIED IDEOGRAPH
+ "9F 8C 6C8D", #CJK UNIFIED IDEOGRAPH
+ "9F 8D 6C9A", #CJK UNIFIED IDEOGRAPH
+ "9F 8E 6C81", #CJK UNIFIED IDEOGRAPH
+ "9F 8F 6C9B", #CJK UNIFIED IDEOGRAPH
+ "9F 90 6C7E", #CJK UNIFIED IDEOGRAPH
+ "9F 91 6C68", #CJK UNIFIED IDEOGRAPH
+ "9F 92 6C73", #CJK UNIFIED IDEOGRAPH
+ "9F 93 6C92", #CJK UNIFIED IDEOGRAPH
+ "9F 94 6C90", #CJK UNIFIED IDEOGRAPH
+ "9F 95 6CC4", #CJK UNIFIED IDEOGRAPH
+ "9F 96 6CF1", #CJK UNIFIED IDEOGRAPH
+ "9F 97 6CD3", #CJK UNIFIED IDEOGRAPH
+ "9F 98 6CBD", #CJK UNIFIED IDEOGRAPH
+ "9F 99 6CD7", #CJK UNIFIED IDEOGRAPH
+ "9F 9A 6CC5", #CJK UNIFIED IDEOGRAPH
+ "9F 9B 6CDD", #CJK UNIFIED IDEOGRAPH
+ "9F 9C 6CAE", #CJK UNIFIED IDEOGRAPH
+ "9F 9D 6CB1", #CJK UNIFIED IDEOGRAPH
+ "9F 9E 6CBE", #CJK UNIFIED IDEOGRAPH
+ "9F 9F 6CBA", #CJK UNIFIED IDEOGRAPH
+ "9F A0 6CDB", #CJK UNIFIED IDEOGRAPH
+ "9F A1 6CEF", #CJK UNIFIED IDEOGRAPH
+ "9F A2 6CD9", #CJK UNIFIED IDEOGRAPH
+ "9F A3 6CEA", #CJK UNIFIED IDEOGRAPH
+ "9F A4 6D1F", #CJK UNIFIED IDEOGRAPH
+ "9F A5 884D", #CJK UNIFIED IDEOGRAPH
+ "9F A6 6D36", #CJK UNIFIED IDEOGRAPH
+ "9F A7 6D2B", #CJK UNIFIED IDEOGRAPH
+ "9F A8 6D3D", #CJK UNIFIED IDEOGRAPH
+ "9F A9 6D38", #CJK UNIFIED IDEOGRAPH
+ "9F AA 6D19", #CJK UNIFIED IDEOGRAPH
+ "9F AB 6D35", #CJK UNIFIED IDEOGRAPH
+ "9F AC 6D33", #CJK UNIFIED IDEOGRAPH
+ "9F AD 6D12", #CJK UNIFIED IDEOGRAPH
+ "9F AE 6D0C", #CJK UNIFIED IDEOGRAPH
+ "9F AF 6D63", #CJK UNIFIED IDEOGRAPH
+ "9F B0 6D93", #CJK UNIFIED IDEOGRAPH
+ "9F B1 6D64", #CJK UNIFIED IDEOGRAPH
+ "9F B2 6D5A", #CJK UNIFIED IDEOGRAPH
+ "9F B3 6D79", #CJK UNIFIED IDEOGRAPH
+ "9F B4 6D59", #CJK UNIFIED IDEOGRAPH
+ "9F B5 6D8E", #CJK UNIFIED IDEOGRAPH
+ "9F B6 6D95", #CJK UNIFIED IDEOGRAPH
+ "9F B7 6FE4", #CJK UNIFIED IDEOGRAPH
+ "9F B8 6D85", #CJK UNIFIED IDEOGRAPH
+ "9F B9 6DF9", #CJK UNIFIED IDEOGRAPH
+ "9F BA 6E15", #CJK UNIFIED IDEOGRAPH
+ "9F BB 6E0A", #CJK UNIFIED IDEOGRAPH
+ "9F BC 6DB5", #CJK UNIFIED IDEOGRAPH
+ "9F BD 6DC7", #CJK UNIFIED IDEOGRAPH
+ "9F BE 6DE6", #CJK UNIFIED IDEOGRAPH
+ "9F BF 6DB8", #CJK UNIFIED IDEOGRAPH
+ "9F C0 6DC6", #CJK UNIFIED IDEOGRAPH
+ "9F C1 6DEC", #CJK UNIFIED IDEOGRAPH
+ "9F C2 6DDE", #CJK UNIFIED IDEOGRAPH
+ "9F C3 6DCC", #CJK UNIFIED IDEOGRAPH
+ "9F C4 6DE8", #CJK UNIFIED IDEOGRAPH
+ "9F C5 6DD2", #CJK UNIFIED IDEOGRAPH
+ "9F C6 6DC5", #CJK UNIFIED IDEOGRAPH
+ "9F C7 6DFA", #CJK UNIFIED IDEOGRAPH
+ "9F C8 6DD9", #CJK UNIFIED IDEOGRAPH
+ "9F C9 6DE4", #CJK UNIFIED IDEOGRAPH
+ "9F CA 6DD5", #CJK UNIFIED IDEOGRAPH
+ "9F CB 6DEA", #CJK UNIFIED IDEOGRAPH
+ "9F CC 6DEE", #CJK UNIFIED IDEOGRAPH
+ "9F CD 6E2D", #CJK UNIFIED IDEOGRAPH
+ "9F CE 6E6E", #CJK UNIFIED IDEOGRAPH
+ "9F CF 6E2E", #CJK UNIFIED IDEOGRAPH
+ "9F D0 6E19", #CJK UNIFIED IDEOGRAPH
+ "9F D1 6E72", #CJK UNIFIED IDEOGRAPH
+ "9F D2 6E5F", #CJK UNIFIED IDEOGRAPH
+ "9F D3 6E3E", #CJK UNIFIED IDEOGRAPH
+ "9F D4 6E23", #CJK UNIFIED IDEOGRAPH
+ "9F D5 6E6B", #CJK UNIFIED IDEOGRAPH
+ "9F D6 6E2B", #CJK UNIFIED IDEOGRAPH
+ "9F D7 6E76", #CJK UNIFIED IDEOGRAPH
+ "9F D8 6E4D", #CJK UNIFIED IDEOGRAPH
+ "9F D9 6E1F", #CJK UNIFIED IDEOGRAPH
+ "9F DA 6E43", #CJK UNIFIED IDEOGRAPH
+ "9F DB 6E3A", #CJK UNIFIED IDEOGRAPH
+ "9F DC 6E4E", #CJK UNIFIED IDEOGRAPH
+ "9F DD 6E24", #CJK UNIFIED IDEOGRAPH
+ "9F DE 6EFF", #CJK UNIFIED IDEOGRAPH
+ "9F DF 6E1D", #CJK UNIFIED IDEOGRAPH
+ "9F E0 6E38", #CJK UNIFIED IDEOGRAPH
+ "9F E1 6E82", #CJK UNIFIED IDEOGRAPH
+ "9F E2 6EAA", #CJK UNIFIED IDEOGRAPH
+ "9F E3 6E98", #CJK UNIFIED IDEOGRAPH
+ "9F E4 6EC9", #CJK UNIFIED IDEOGRAPH
+ "9F E5 6EB7", #CJK UNIFIED IDEOGRAPH
+ "9F E6 6ED3", #CJK UNIFIED IDEOGRAPH
+ "9F E7 6EBD", #CJK UNIFIED IDEOGRAPH
+ "9F E8 6EAF", #CJK UNIFIED IDEOGRAPH
+ "9F E9 6EC4", #CJK UNIFIED IDEOGRAPH
+ "9F EA 6EB2", #CJK UNIFIED IDEOGRAPH
+ "9F EB 6ED4", #CJK UNIFIED IDEOGRAPH
+ "9F EC 6ED5", #CJK UNIFIED IDEOGRAPH
+ "9F ED 6E8F", #CJK UNIFIED IDEOGRAPH
+ "9F EE 6EA5", #CJK UNIFIED IDEOGRAPH
+ "9F EF 6EC2", #CJK UNIFIED IDEOGRAPH
+ "9F F0 6E9F", #CJK UNIFIED IDEOGRAPH
+ "9F F1 6F41", #CJK UNIFIED IDEOGRAPH
+ "9F F2 6F11", #CJK UNIFIED IDEOGRAPH
+ "9F F3 704C", #CJK UNIFIED IDEOGRAPH
+ "9F F4 6EEC", #CJK UNIFIED IDEOGRAPH
+ "9F F5 6EF8", #CJK UNIFIED IDEOGRAPH
+ "9F F6 6EFE", #CJK UNIFIED IDEOGRAPH
+ "9F F7 6F3F", #CJK UNIFIED IDEOGRAPH
+ "9F F8 6EF2", #CJK UNIFIED IDEOGRAPH
+ "9F F9 6F31", #CJK UNIFIED IDEOGRAPH
+ "9F FA 6EEF", #CJK UNIFIED IDEOGRAPH
+ "9F FB 6F32", #CJK UNIFIED IDEOGRAPH
+ "9F FC 6ECC", #CJK UNIFIED IDEOGRAPH
+ "E0 40 6F3E", #CJK UNIFIED IDEOGRAPH
+ "E0 41 6F13", #CJK UNIFIED IDEOGRAPH
+ "E0 42 6EF7", #CJK UNIFIED IDEOGRAPH
+ "E0 43 6F86", #CJK UNIFIED IDEOGRAPH
+ "E0 44 6F7A", #CJK UNIFIED IDEOGRAPH
+ "E0 45 6F78", #CJK UNIFIED IDEOGRAPH
+ "E0 46 6F81", #CJK UNIFIED IDEOGRAPH
+ "E0 47 6F80", #CJK UNIFIED IDEOGRAPH
+ "E0 48 6F6F", #CJK UNIFIED IDEOGRAPH
+ "E0 49 6F5B", #CJK UNIFIED IDEOGRAPH
+ "E0 4A 6FF3", #CJK UNIFIED IDEOGRAPH
+ "E0 4B 6F6D", #CJK UNIFIED IDEOGRAPH
+ "E0 4C 6F82", #CJK UNIFIED IDEOGRAPH
+ "E0 4D 6F7C", #CJK UNIFIED IDEOGRAPH
+ "E0 4E 6F58", #CJK UNIFIED IDEOGRAPH
+ "E0 4F 6F8E", #CJK UNIFIED IDEOGRAPH
+ "E0 50 6F91", #CJK UNIFIED IDEOGRAPH
+ "E0 51 6FC2", #CJK UNIFIED IDEOGRAPH
+ "E0 52 6F66", #CJK UNIFIED IDEOGRAPH
+ "E0 53 6FB3", #CJK UNIFIED IDEOGRAPH
+ "E0 54 6FA3", #CJK UNIFIED IDEOGRAPH
+ "E0 55 6FA1", #CJK UNIFIED IDEOGRAPH
+ "E0 56 6FA4", #CJK UNIFIED IDEOGRAPH
+ "E0 57 6FB9", #CJK UNIFIED IDEOGRAPH
+ "E0 58 6FC6", #CJK UNIFIED IDEOGRAPH
+ "E0 59 6FAA", #CJK UNIFIED IDEOGRAPH
+ "E0 5A 6FDF", #CJK UNIFIED IDEOGRAPH
+ "E0 5B 6FD5", #CJK UNIFIED IDEOGRAPH
+ "E0 5C 6FEC", #CJK UNIFIED IDEOGRAPH
+ "E0 5D 6FD4", #CJK UNIFIED IDEOGRAPH
+ "E0 5E 6FD8", #CJK UNIFIED IDEOGRAPH
+ "E0 5F 6FF1", #CJK UNIFIED IDEOGRAPH
+ "E0 60 6FEE", #CJK UNIFIED IDEOGRAPH
+ "E0 61 6FDB", #CJK UNIFIED IDEOGRAPH
+ "E0 62 7009", #CJK UNIFIED IDEOGRAPH
+ "E0 63 700B", #CJK UNIFIED IDEOGRAPH
+ "E0 64 6FFA", #CJK UNIFIED IDEOGRAPH
+ "E0 65 7011", #CJK UNIFIED IDEOGRAPH
+ "E0 66 7001", #CJK UNIFIED IDEOGRAPH
+ "E0 67 700F", #CJK UNIFIED IDEOGRAPH
+ "E0 68 6FFE", #CJK UNIFIED IDEOGRAPH
+ "E0 69 701B", #CJK UNIFIED IDEOGRAPH
+ "E0 6A 701A", #CJK UNIFIED IDEOGRAPH
+ "E0 6B 6F74", #CJK UNIFIED IDEOGRAPH
+ "E0 6C 701D", #CJK UNIFIED IDEOGRAPH
+ "E0 6D 7018", #CJK UNIFIED IDEOGRAPH
+ "E0 6E 701F", #CJK UNIFIED IDEOGRAPH
+ "E0 6F 7030", #CJK UNIFIED IDEOGRAPH
+ "E0 70 703E", #CJK UNIFIED IDEOGRAPH
+ "E0 71 7032", #CJK UNIFIED IDEOGRAPH
+ "E0 72 7051", #CJK UNIFIED IDEOGRAPH
+ "E0 73 7063", #CJK UNIFIED IDEOGRAPH
+ "E0 74 7099", #CJK UNIFIED IDEOGRAPH
+ "E0 75 7092", #CJK UNIFIED IDEOGRAPH
+ "E0 76 70AF", #CJK UNIFIED IDEOGRAPH
+ "E0 77 70F1", #CJK UNIFIED IDEOGRAPH
+ "E0 78 70AC", #CJK UNIFIED IDEOGRAPH
+ "E0 79 70B8", #CJK UNIFIED IDEOGRAPH
+ "E0 7A 70B3", #CJK UNIFIED IDEOGRAPH
+ "E0 7B 70AE", #CJK UNIFIED IDEOGRAPH
+ "E0 7C 70DF", #CJK UNIFIED IDEOGRAPH
+ "E0 7D 70CB", #CJK UNIFIED IDEOGRAPH
+ "E0 7E 70DD", #CJK UNIFIED IDEOGRAPH
+ "E0 80 70D9", #CJK UNIFIED IDEOGRAPH
+ "E0 81 7109", #CJK UNIFIED IDEOGRAPH
+ "E0 82 70FD", #CJK UNIFIED IDEOGRAPH
+ "E0 83 711C", #CJK UNIFIED IDEOGRAPH
+ "E0 84 7119", #CJK UNIFIED IDEOGRAPH
+ "E0 85 7165", #CJK UNIFIED IDEOGRAPH
+ "E0 86 7155", #CJK UNIFIED IDEOGRAPH
+ "E0 87 7188", #CJK UNIFIED IDEOGRAPH
+ "E0 88 7166", #CJK UNIFIED IDEOGRAPH
+ "E0 89 7162", #CJK UNIFIED IDEOGRAPH
+ "E0 8A 714C", #CJK UNIFIED IDEOGRAPH
+ "E0 8B 7156", #CJK UNIFIED IDEOGRAPH
+ "E0 8C 716C", #CJK UNIFIED IDEOGRAPH
+ "E0 8D 718F", #CJK UNIFIED IDEOGRAPH
+ "E0 8E 71FB", #CJK UNIFIED IDEOGRAPH
+ "E0 8F 7184", #CJK UNIFIED IDEOGRAPH
+ "E0 90 7195", #CJK UNIFIED IDEOGRAPH
+ "E0 91 71A8", #CJK UNIFIED IDEOGRAPH
+ "E0 92 71AC", #CJK UNIFIED IDEOGRAPH
+ "E0 93 71D7", #CJK UNIFIED IDEOGRAPH
+ "E0 94 71B9", #CJK UNIFIED IDEOGRAPH
+ "E0 95 71BE", #CJK UNIFIED IDEOGRAPH
+ "E0 96 71D2", #CJK UNIFIED IDEOGRAPH
+ "E0 97 71C9", #CJK UNIFIED IDEOGRAPH
+ "E0 98 71D4", #CJK UNIFIED IDEOGRAPH
+ "E0 99 71CE", #CJK UNIFIED IDEOGRAPH
+ "E0 9A 71E0", #CJK UNIFIED IDEOGRAPH
+ "E0 9B 71EC", #CJK UNIFIED IDEOGRAPH
+ "E0 9C 71E7", #CJK UNIFIED IDEOGRAPH
+ "E0 9D 71F5", #CJK UNIFIED IDEOGRAPH
+ "E0 9E 71FC", #CJK UNIFIED IDEOGRAPH
+ "E0 9F 71F9", #CJK UNIFIED IDEOGRAPH
+ "E0 A0 71FF", #CJK UNIFIED IDEOGRAPH
+ "E0 A1 720D", #CJK UNIFIED IDEOGRAPH
+ "E0 A2 7210", #CJK UNIFIED IDEOGRAPH
+ "E0 A3 721B", #CJK UNIFIED IDEOGRAPH
+ "E0 A4 7228", #CJK UNIFIED IDEOGRAPH
+ "E0 A5 722D", #CJK UNIFIED IDEOGRAPH
+ "E0 A6 722C", #CJK UNIFIED IDEOGRAPH
+ "E0 A7 7230", #CJK UNIFIED IDEOGRAPH
+ "E0 A8 7232", #CJK UNIFIED IDEOGRAPH
+ "E0 A9 723B", #CJK UNIFIED IDEOGRAPH
+ "E0 AA 723C", #CJK UNIFIED IDEOGRAPH
+ "E0 AB 723F", #CJK UNIFIED IDEOGRAPH
+ "E0 AC 7240", #CJK UNIFIED IDEOGRAPH
+ "E0 AD 7246", #CJK UNIFIED IDEOGRAPH
+ "E0 AE 724B", #CJK UNIFIED IDEOGRAPH
+ "E0 AF 7258", #CJK UNIFIED IDEOGRAPH
+ "E0 B0 7274", #CJK UNIFIED IDEOGRAPH
+ "E0 B1 727E", #CJK UNIFIED IDEOGRAPH
+ "E0 B2 7282", #CJK UNIFIED IDEOGRAPH
+ "E0 B3 7281", #CJK UNIFIED IDEOGRAPH
+ "E0 B4 7287", #CJK UNIFIED IDEOGRAPH
+ "E0 B5 7292", #CJK UNIFIED IDEOGRAPH
+ "E0 B6 7296", #CJK UNIFIED IDEOGRAPH
+ "E0 B7 72A2", #CJK UNIFIED IDEOGRAPH
+ "E0 B8 72A7", #CJK UNIFIED IDEOGRAPH
+ "E0 B9 72B9", #CJK UNIFIED IDEOGRAPH
+ "E0 BA 72B2", #CJK UNIFIED IDEOGRAPH
+ "E0 BB 72C3", #CJK UNIFIED IDEOGRAPH
+ "E0 BC 72C6", #CJK UNIFIED IDEOGRAPH
+ "E0 BD 72C4", #CJK UNIFIED IDEOGRAPH
+ "E0 BE 72CE", #CJK UNIFIED IDEOGRAPH
+ "E0 BF 72D2", #CJK UNIFIED IDEOGRAPH
+ "E0 C0 72E2", #CJK UNIFIED IDEOGRAPH
+ "E0 C1 72E0", #CJK UNIFIED IDEOGRAPH
+ "E0 C2 72E1", #CJK UNIFIED IDEOGRAPH
+ "E0 C3 72F9", #CJK UNIFIED IDEOGRAPH
+ "E0 C4 72F7", #CJK UNIFIED IDEOGRAPH
+ "E0 C5 500F", #CJK UNIFIED IDEOGRAPH
+ "E0 C6 7317", #CJK UNIFIED IDEOGRAPH
+ "E0 C7 730A", #CJK UNIFIED IDEOGRAPH
+ "E0 C8 731C", #CJK UNIFIED IDEOGRAPH
+ "E0 C9 7316", #CJK UNIFIED IDEOGRAPH
+ "E0 CA 731D", #CJK UNIFIED IDEOGRAPH
+ "E0 CB 7334", #CJK UNIFIED IDEOGRAPH
+ "E0 CC 732F", #CJK UNIFIED IDEOGRAPH
+ "E0 CD 7329", #CJK UNIFIED IDEOGRAPH
+ "E0 CE 7325", #CJK UNIFIED IDEOGRAPH
+ "E0 CF 733E", #CJK UNIFIED IDEOGRAPH
+ "E0 D0 734E", #CJK UNIFIED IDEOGRAPH
+ "E0 D1 734F", #CJK UNIFIED IDEOGRAPH
+ "E0 D2 9ED8", #CJK UNIFIED IDEOGRAPH
+ "E0 D3 7357", #CJK UNIFIED IDEOGRAPH
+ "E0 D4 736A", #CJK UNIFIED IDEOGRAPH
+ "E0 D5 7368", #CJK UNIFIED IDEOGRAPH
+ "E0 D6 7370", #CJK UNIFIED IDEOGRAPH
+ "E0 D7 7378", #CJK UNIFIED IDEOGRAPH
+ "E0 D8 7375", #CJK UNIFIED IDEOGRAPH
+ "E0 D9 737B", #CJK UNIFIED IDEOGRAPH
+ "E0 DA 737A", #CJK UNIFIED IDEOGRAPH
+ "E0 DB 73C8", #CJK UNIFIED IDEOGRAPH
+ "E0 DC 73B3", #CJK UNIFIED IDEOGRAPH
+ "E0 DD 73CE", #CJK UNIFIED IDEOGRAPH
+ "E0 DE 73BB", #CJK UNIFIED IDEOGRAPH
+ "E0 DF 73C0", #CJK UNIFIED IDEOGRAPH
+ "E0 E0 73E5", #CJK UNIFIED IDEOGRAPH
+ "E0 E1 73EE", #CJK UNIFIED IDEOGRAPH
+ "E0 E2 73DE", #CJK UNIFIED IDEOGRAPH
+ "E0 E3 74A2", #CJK UNIFIED IDEOGRAPH
+ "E0 E4 7405", #CJK UNIFIED IDEOGRAPH
+ "E0 E5 746F", #CJK UNIFIED IDEOGRAPH
+ "E0 E6 7425", #CJK UNIFIED IDEOGRAPH
+ "E0 E7 73F8", #CJK UNIFIED IDEOGRAPH
+ "E0 E8 7432", #CJK UNIFIED IDEOGRAPH
+ "E0 E9 743A", #CJK UNIFIED IDEOGRAPH
+ "E0 EA 7455", #CJK UNIFIED IDEOGRAPH
+ "E0 EB 743F", #CJK UNIFIED IDEOGRAPH
+ "E0 EC 745F", #CJK UNIFIED IDEOGRAPH
+ "E0 ED 7459", #CJK UNIFIED IDEOGRAPH
+ "E0 EE 7441", #CJK UNIFIED IDEOGRAPH
+ "E0 EF 745C", #CJK UNIFIED IDEOGRAPH
+ "E0 F0 7469", #CJK UNIFIED IDEOGRAPH
+ "E0 F1 7470", #CJK UNIFIED IDEOGRAPH
+ "E0 F2 7463", #CJK UNIFIED IDEOGRAPH
+ "E0 F3 746A", #CJK UNIFIED IDEOGRAPH
+ "E0 F4 7476", #CJK UNIFIED IDEOGRAPH
+ "E0 F5 747E", #CJK UNIFIED IDEOGRAPH
+ "E0 F6 748B", #CJK UNIFIED IDEOGRAPH
+ "E0 F7 749E", #CJK UNIFIED IDEOGRAPH
+ "E0 F8 74A7", #CJK UNIFIED IDEOGRAPH
+ "E0 F9 74CA", #CJK UNIFIED IDEOGRAPH
+ "E0 FA 74CF", #CJK UNIFIED IDEOGRAPH
+ "E0 FB 74D4", #CJK UNIFIED IDEOGRAPH
+ "E0 FC 73F1", #CJK UNIFIED IDEOGRAPH
+ "E1 40 74E0", #CJK UNIFIED IDEOGRAPH
+ "E1 41 74E3", #CJK UNIFIED IDEOGRAPH
+ "E1 42 74E7", #CJK UNIFIED IDEOGRAPH
+ "E1 43 74E9", #CJK UNIFIED IDEOGRAPH
+ "E1 44 74EE", #CJK UNIFIED IDEOGRAPH
+ "E1 45 74F2", #CJK UNIFIED IDEOGRAPH
+ "E1 46 74F0", #CJK UNIFIED IDEOGRAPH
+ "E1 47 74F1", #CJK UNIFIED IDEOGRAPH
+ "E1 48 74F8", #CJK UNIFIED IDEOGRAPH
+ "E1 49 74F7", #CJK UNIFIED IDEOGRAPH
+ "E1 4A 7504", #CJK UNIFIED IDEOGRAPH
+ "E1 4B 7503", #CJK UNIFIED IDEOGRAPH
+ "E1 4C 7505", #CJK UNIFIED IDEOGRAPH
+ "E1 4D 750C", #CJK UNIFIED IDEOGRAPH
+ "E1 4E 750E", #CJK UNIFIED IDEOGRAPH
+ "E1 4F 750D", #CJK UNIFIED IDEOGRAPH
+ "E1 50 7515", #CJK UNIFIED IDEOGRAPH
+ "E1 51 7513", #CJK UNIFIED IDEOGRAPH
+ "E1 52 751E", #CJK UNIFIED IDEOGRAPH
+ "E1 53 7526", #CJK UNIFIED IDEOGRAPH
+ "E1 54 752C", #CJK UNIFIED IDEOGRAPH
+ "E1 55 753C", #CJK UNIFIED IDEOGRAPH
+ "E1 56 7544", #CJK UNIFIED IDEOGRAPH
+ "E1 57 754D", #CJK UNIFIED IDEOGRAPH
+ "E1 58 754A", #CJK UNIFIED IDEOGRAPH
+ "E1 59 7549", #CJK UNIFIED IDEOGRAPH
+ "E1 5A 755B", #CJK UNIFIED IDEOGRAPH
+ "E1 5B 7546", #CJK UNIFIED IDEOGRAPH
+ "E1 5C 755A", #CJK UNIFIED IDEOGRAPH
+ "E1 5D 7569", #CJK UNIFIED IDEOGRAPH
+ "E1 5E 7564", #CJK UNIFIED IDEOGRAPH
+ "E1 5F 7567", #CJK UNIFIED IDEOGRAPH
+ "E1 60 756B", #CJK UNIFIED IDEOGRAPH
+ "E1 61 756D", #CJK UNIFIED IDEOGRAPH
+ "E1 62 7578", #CJK UNIFIED IDEOGRAPH
+ "E1 63 7576", #CJK UNIFIED IDEOGRAPH
+ "E1 64 7586", #CJK UNIFIED IDEOGRAPH
+ "E1 65 7587", #CJK UNIFIED IDEOGRAPH
+ "E1 66 7574", #CJK UNIFIED IDEOGRAPH
+ "E1 67 758A", #CJK UNIFIED IDEOGRAPH
+ "E1 68 7589", #CJK UNIFIED IDEOGRAPH
+ "E1 69 7582", #CJK UNIFIED IDEOGRAPH
+ "E1 6A 7594", #CJK UNIFIED IDEOGRAPH
+ "E1 6B 759A", #CJK UNIFIED IDEOGRAPH
+ "E1 6C 759D", #CJK UNIFIED IDEOGRAPH
+ "E1 6D 75A5", #CJK UNIFIED IDEOGRAPH
+ "E1 6E 75A3", #CJK UNIFIED IDEOGRAPH
+ "E1 6F 75C2", #CJK UNIFIED IDEOGRAPH
+ "E1 70 75B3", #CJK UNIFIED IDEOGRAPH
+ "E1 71 75C3", #CJK UNIFIED IDEOGRAPH
+ "E1 72 75B5", #CJK UNIFIED IDEOGRAPH
+ "E1 73 75BD", #CJK UNIFIED IDEOGRAPH
+ "E1 74 75B8", #CJK UNIFIED IDEOGRAPH
+ "E1 75 75BC", #CJK UNIFIED IDEOGRAPH
+ "E1 76 75B1", #CJK UNIFIED IDEOGRAPH
+ "E1 77 75CD", #CJK UNIFIED IDEOGRAPH
+ "E1 78 75CA", #CJK UNIFIED IDEOGRAPH
+ "E1 79 75D2", #CJK UNIFIED IDEOGRAPH
+ "E1 7A 75D9", #CJK UNIFIED IDEOGRAPH
+ "E1 7B 75E3", #CJK UNIFIED IDEOGRAPH
+ "E1 7C 75DE", #CJK UNIFIED IDEOGRAPH
+ "E1 7D 75FE", #CJK UNIFIED IDEOGRAPH
+ "E1 7E 75FF", #CJK UNIFIED IDEOGRAPH
+ "E1 80 75FC", #CJK UNIFIED IDEOGRAPH
+ "E1 81 7601", #CJK UNIFIED IDEOGRAPH
+ "E1 82 75F0", #CJK UNIFIED IDEOGRAPH
+ "E1 83 75FA", #CJK UNIFIED IDEOGRAPH
+ "E1 84 75F2", #CJK UNIFIED IDEOGRAPH
+ "E1 85 75F3", #CJK UNIFIED IDEOGRAPH
+ "E1 86 760B", #CJK UNIFIED IDEOGRAPH
+ "E1 87 760D", #CJK UNIFIED IDEOGRAPH
+ "E1 88 7609", #CJK UNIFIED IDEOGRAPH
+ "E1 89 761F", #CJK UNIFIED IDEOGRAPH
+ "E1 8A 7627", #CJK UNIFIED IDEOGRAPH
+ "E1 8B 7620", #CJK UNIFIED IDEOGRAPH
+ "E1 8C 7621", #CJK UNIFIED IDEOGRAPH
+ "E1 8D 7622", #CJK UNIFIED IDEOGRAPH
+ "E1 8E 7624", #CJK UNIFIED IDEOGRAPH
+ "E1 8F 7634", #CJK UNIFIED IDEOGRAPH
+ "E1 90 7630", #CJK UNIFIED IDEOGRAPH
+ "E1 91 763B", #CJK UNIFIED IDEOGRAPH
+ "E1 92 7647", #CJK UNIFIED IDEOGRAPH
+ "E1 93 7648", #CJK UNIFIED IDEOGRAPH
+ "E1 94 7646", #CJK UNIFIED IDEOGRAPH
+ "E1 95 765C", #CJK UNIFIED IDEOGRAPH
+ "E1 96 7658", #CJK UNIFIED IDEOGRAPH
+ "E1 97 7661", #CJK UNIFIED IDEOGRAPH
+ "E1 98 7662", #CJK UNIFIED IDEOGRAPH
+ "E1 99 7668", #CJK UNIFIED IDEOGRAPH
+ "E1 9A 7669", #CJK UNIFIED IDEOGRAPH
+ "E1 9B 766A", #CJK UNIFIED IDEOGRAPH
+ "E1 9C 7667", #CJK UNIFIED IDEOGRAPH
+ "E1 9D 766C", #CJK UNIFIED IDEOGRAPH
+ "E1 9E 7670", #CJK UNIFIED IDEOGRAPH
+ "E1 9F 7672", #CJK UNIFIED IDEOGRAPH
+ "E1 A0 7676", #CJK UNIFIED IDEOGRAPH
+ "E1 A1 7678", #CJK UNIFIED IDEOGRAPH
+ "E1 A2 767C", #CJK UNIFIED IDEOGRAPH
+ "E1 A3 7680", #CJK UNIFIED IDEOGRAPH
+ "E1 A4 7683", #CJK UNIFIED IDEOGRAPH
+ "E1 A5 7688", #CJK UNIFIED IDEOGRAPH
+ "E1 A6 768B", #CJK UNIFIED IDEOGRAPH
+ "E1 A7 768E", #CJK UNIFIED IDEOGRAPH
+ "E1 A8 7696", #CJK UNIFIED IDEOGRAPH
+ "E1 A9 7693", #CJK UNIFIED IDEOGRAPH
+ "E1 AA 7699", #CJK UNIFIED IDEOGRAPH
+ "E1 AB 769A", #CJK UNIFIED IDEOGRAPH
+ "E1 AC 76B0", #CJK UNIFIED IDEOGRAPH
+ "E1 AD 76B4", #CJK UNIFIED IDEOGRAPH
+ "E1 AE 76B8", #CJK UNIFIED IDEOGRAPH
+ "E1 AF 76B9", #CJK UNIFIED IDEOGRAPH
+ "E1 B0 76BA", #CJK UNIFIED IDEOGRAPH
+ "E1 B1 76C2", #CJK UNIFIED IDEOGRAPH
+ "E1 B2 76CD", #CJK UNIFIED IDEOGRAPH
+ "E1 B3 76D6", #CJK UNIFIED IDEOGRAPH
+ "E1 B4 76D2", #CJK UNIFIED IDEOGRAPH
+ "E1 B5 76DE", #CJK UNIFIED IDEOGRAPH
+ "E1 B6 76E1", #CJK UNIFIED IDEOGRAPH
+ "E1 B7 76E5", #CJK UNIFIED IDEOGRAPH
+ "E1 B8 76E7", #CJK UNIFIED IDEOGRAPH
+ "E1 B9 76EA", #CJK UNIFIED IDEOGRAPH
+ "E1 BA 862F", #CJK UNIFIED IDEOGRAPH
+ "E1 BB 76FB", #CJK UNIFIED IDEOGRAPH
+ "E1 BC 7708", #CJK UNIFIED IDEOGRAPH
+ "E1 BD 7707", #CJK UNIFIED IDEOGRAPH
+ "E1 BE 7704", #CJK UNIFIED IDEOGRAPH
+ "E1 BF 7729", #CJK UNIFIED IDEOGRAPH
+ "E1 C0 7724", #CJK UNIFIED IDEOGRAPH
+ "E1 C1 771E", #CJK UNIFIED IDEOGRAPH
+ "E1 C2 7725", #CJK UNIFIED IDEOGRAPH
+ "E1 C3 7726", #CJK UNIFIED IDEOGRAPH
+ "E1 C4 771B", #CJK UNIFIED IDEOGRAPH
+ "E1 C5 7737", #CJK UNIFIED IDEOGRAPH
+ "E1 C6 7738", #CJK UNIFIED IDEOGRAPH
+ "E1 C7 7747", #CJK UNIFIED IDEOGRAPH
+ "E1 C8 775A", #CJK UNIFIED IDEOGRAPH
+ "E1 C9 7768", #CJK UNIFIED IDEOGRAPH
+ "E1 CA 776B", #CJK UNIFIED IDEOGRAPH
+ "E1 CB 775B", #CJK UNIFIED IDEOGRAPH
+ "E1 CC 7765", #CJK UNIFIED IDEOGRAPH
+ "E1 CD 777F", #CJK UNIFIED IDEOGRAPH
+ "E1 CE 777E", #CJK UNIFIED IDEOGRAPH
+ "E1 CF 7779", #CJK UNIFIED IDEOGRAPH
+ "E1 D0 778E", #CJK UNIFIED IDEOGRAPH
+ "E1 D1 778B", #CJK UNIFIED IDEOGRAPH
+ "E1 D2 7791", #CJK UNIFIED IDEOGRAPH
+ "E1 D3 77A0", #CJK UNIFIED IDEOGRAPH
+ "E1 D4 779E", #CJK UNIFIED IDEOGRAPH
+ "E1 D5 77B0", #CJK UNIFIED IDEOGRAPH
+ "E1 D6 77B6", #CJK UNIFIED IDEOGRAPH
+ "E1 D7 77B9", #CJK UNIFIED IDEOGRAPH
+ "E1 D8 77BF", #CJK UNIFIED IDEOGRAPH
+ "E1 D9 77BC", #CJK UNIFIED IDEOGRAPH
+ "E1 DA 77BD", #CJK UNIFIED IDEOGRAPH
+ "E1 DB 77BB", #CJK UNIFIED IDEOGRAPH
+ "E1 DC 77C7", #CJK UNIFIED IDEOGRAPH
+ "E1 DD 77CD", #CJK UNIFIED IDEOGRAPH
+ "E1 DE 77D7", #CJK UNIFIED IDEOGRAPH
+ "E1 DF 77DA", #CJK UNIFIED IDEOGRAPH
+ "E1 E0 77DC", #CJK UNIFIED IDEOGRAPH
+ "E1 E1 77E3", #CJK UNIFIED IDEOGRAPH
+ "E1 E2 77EE", #CJK UNIFIED IDEOGRAPH
+ "E1 E3 77FC", #CJK UNIFIED IDEOGRAPH
+ "E1 E4 780C", #CJK UNIFIED IDEOGRAPH
+ "E1 E5 7812", #CJK UNIFIED IDEOGRAPH
+ "E1 E6 7926", #CJK UNIFIED IDEOGRAPH
+ "E1 E7 7820", #CJK UNIFIED IDEOGRAPH
+ "E1 E8 792A", #CJK UNIFIED IDEOGRAPH
+ "E1 E9 7845", #CJK UNIFIED IDEOGRAPH
+ "E1 EA 788E", #CJK UNIFIED IDEOGRAPH
+ "E1 EB 7874", #CJK UNIFIED IDEOGRAPH
+ "E1 EC 7886", #CJK UNIFIED IDEOGRAPH
+ "E1 ED 787C", #CJK UNIFIED IDEOGRAPH
+ "E1 EE 789A", #CJK UNIFIED IDEOGRAPH
+ "E1 EF 788C", #CJK UNIFIED IDEOGRAPH
+ "E1 F0 78A3", #CJK UNIFIED IDEOGRAPH
+ "E1 F1 78B5", #CJK UNIFIED IDEOGRAPH
+ "E1 F2 78AA", #CJK UNIFIED IDEOGRAPH
+ "E1 F3 78AF", #CJK UNIFIED IDEOGRAPH
+ "E1 F4 78D1", #CJK UNIFIED IDEOGRAPH
+ "E1 F5 78C6", #CJK UNIFIED IDEOGRAPH
+ "E1 F6 78CB", #CJK UNIFIED IDEOGRAPH
+ "E1 F7 78D4", #CJK UNIFIED IDEOGRAPH
+ "E1 F8 78BE", #CJK UNIFIED IDEOGRAPH
+ "E1 F9 78BC", #CJK UNIFIED IDEOGRAPH
+ "E1 FA 78C5", #CJK UNIFIED IDEOGRAPH
+ "E1 FB 78CA", #CJK UNIFIED IDEOGRAPH
+ "E1 FC 78EC", #CJK UNIFIED IDEOGRAPH
+ "E2 40 78E7", #CJK UNIFIED IDEOGRAPH
+ "E2 41 78DA", #CJK UNIFIED IDEOGRAPH
+ "E2 42 78FD", #CJK UNIFIED IDEOGRAPH
+ "E2 43 78F4", #CJK UNIFIED IDEOGRAPH
+ "E2 44 7907", #CJK UNIFIED IDEOGRAPH
+ "E2 45 7912", #CJK UNIFIED IDEOGRAPH
+ "E2 46 7911", #CJK UNIFIED IDEOGRAPH
+ "E2 47 7919", #CJK UNIFIED IDEOGRAPH
+ "E2 48 792C", #CJK UNIFIED IDEOGRAPH
+ "E2 49 792B", #CJK UNIFIED IDEOGRAPH
+ "E2 4A 7940", #CJK UNIFIED IDEOGRAPH
+ "E2 4B 7960", #CJK UNIFIED IDEOGRAPH
+ "E2 4C 7957", #CJK UNIFIED IDEOGRAPH
+ "E2 4D 795F", #CJK UNIFIED IDEOGRAPH
+ "E2 4E 795A", #CJK UNIFIED IDEOGRAPH
+ "E2 4F 7955", #CJK UNIFIED IDEOGRAPH
+ "E2 50 7953", #CJK UNIFIED IDEOGRAPH
+ "E2 51 797A", #CJK UNIFIED IDEOGRAPH
+ "E2 52 797F", #CJK UNIFIED IDEOGRAPH
+ "E2 53 798A", #CJK UNIFIED IDEOGRAPH
+ "E2 54 799D", #CJK UNIFIED IDEOGRAPH
+ "E2 55 79A7", #CJK UNIFIED IDEOGRAPH
+ "E2 56 9F4B", #CJK UNIFIED IDEOGRAPH
+ "E2 57 79AA", #CJK UNIFIED IDEOGRAPH
+ "E2 58 79AE", #CJK UNIFIED IDEOGRAPH
+ "E2 59 79B3", #CJK UNIFIED IDEOGRAPH
+ "E2 5A 79B9", #CJK UNIFIED IDEOGRAPH
+ "E2 5B 79BA", #CJK UNIFIED IDEOGRAPH
+ "E2 5C 79C9", #CJK UNIFIED IDEOGRAPH
+ "E2 5D 79D5", #CJK UNIFIED IDEOGRAPH
+ "E2 5E 79E7", #CJK UNIFIED IDEOGRAPH
+ "E2 5F 79EC", #CJK UNIFIED IDEOGRAPH
+ "E2 60 79E1", #CJK UNIFIED IDEOGRAPH
+ "E2 61 79E3", #CJK UNIFIED IDEOGRAPH
+ "E2 62 7A08", #CJK UNIFIED IDEOGRAPH
+ "E2 63 7A0D", #CJK UNIFIED IDEOGRAPH
+ "E2 64 7A18", #CJK UNIFIED IDEOGRAPH
+ "E2 65 7A19", #CJK UNIFIED IDEOGRAPH
+ "E2 66 7A20", #CJK UNIFIED IDEOGRAPH
+ "E2 67 7A1F", #CJK UNIFIED IDEOGRAPH
+ "E2 68 7980", #CJK UNIFIED IDEOGRAPH
+ "E2 69 7A31", #CJK UNIFIED IDEOGRAPH
+ "E2 6A 7A3B", #CJK UNIFIED IDEOGRAPH
+ "E2 6B 7A3E", #CJK UNIFIED IDEOGRAPH
+ "E2 6C 7A37", #CJK UNIFIED IDEOGRAPH
+ "E2 6D 7A43", #CJK UNIFIED IDEOGRAPH
+ "E2 6E 7A57", #CJK UNIFIED IDEOGRAPH
+ "E2 6F 7A49", #CJK UNIFIED IDEOGRAPH
+ "E2 70 7A61", #CJK UNIFIED IDEOGRAPH
+ "E2 71 7A62", #CJK UNIFIED IDEOGRAPH
+ "E2 72 7A69", #CJK UNIFIED IDEOGRAPH
+ "E2 73 9F9D", #CJK UNIFIED IDEOGRAPH
+ "E2 74 7A70", #CJK UNIFIED IDEOGRAPH
+ "E2 75 7A79", #CJK UNIFIED IDEOGRAPH
+ "E2 76 7A7D", #CJK UNIFIED IDEOGRAPH
+ "E2 77 7A88", #CJK UNIFIED IDEOGRAPH
+ "E2 78 7A97", #CJK UNIFIED IDEOGRAPH
+ "E2 79 7A95", #CJK UNIFIED IDEOGRAPH
+ "E2 7A 7A98", #CJK UNIFIED IDEOGRAPH
+ "E2 7B 7A96", #CJK UNIFIED IDEOGRAPH
+ "E2 7C 7AA9", #CJK UNIFIED IDEOGRAPH
+ "E2 7D 7AC8", #CJK UNIFIED IDEOGRAPH
+ "E2 7E 7AB0", #CJK UNIFIED IDEOGRAPH
+ "E2 80 7AB6", #CJK UNIFIED IDEOGRAPH
+ "E2 81 7AC5", #CJK UNIFIED IDEOGRAPH
+ "E2 82 7AC4", #CJK UNIFIED IDEOGRAPH
+ "E2 83 7ABF", #CJK UNIFIED IDEOGRAPH
+ "E2 84 9083", #CJK UNIFIED IDEOGRAPH
+ "E2 85 7AC7", #CJK UNIFIED IDEOGRAPH
+ "E2 86 7ACA", #CJK UNIFIED IDEOGRAPH
+ "E2 87 7ACD", #CJK UNIFIED IDEOGRAPH
+ "E2 88 7ACF", #CJK UNIFIED IDEOGRAPH
+ "E2 89 7AD5", #CJK UNIFIED IDEOGRAPH
+ "E2 8A 7AD3", #CJK UNIFIED IDEOGRAPH
+ "E2 8B 7AD9", #CJK UNIFIED IDEOGRAPH
+ "E2 8C 7ADA", #CJK UNIFIED IDEOGRAPH
+ "E2 8D 7ADD", #CJK UNIFIED IDEOGRAPH
+ "E2 8E 7AE1", #CJK UNIFIED IDEOGRAPH
+ "E2 8F 7AE2", #CJK UNIFIED IDEOGRAPH
+ "E2 90 7AE6", #CJK UNIFIED IDEOGRAPH
+ "E2 91 7AED", #CJK UNIFIED IDEOGRAPH
+ "E2 92 7AF0", #CJK UNIFIED IDEOGRAPH
+ "E2 93 7B02", #CJK UNIFIED IDEOGRAPH
+ "E2 94 7B0F", #CJK UNIFIED IDEOGRAPH
+ "E2 95 7B0A", #CJK UNIFIED IDEOGRAPH
+ "E2 96 7B06", #CJK UNIFIED IDEOGRAPH
+ "E2 97 7B33", #CJK UNIFIED IDEOGRAPH
+ "E2 98 7B18", #CJK UNIFIED IDEOGRAPH
+ "E2 99 7B19", #CJK UNIFIED IDEOGRAPH
+ "E2 9A 7B1E", #CJK UNIFIED IDEOGRAPH
+ "E2 9B 7B35", #CJK UNIFIED IDEOGRAPH
+ "E2 9C 7B28", #CJK UNIFIED IDEOGRAPH
+ "E2 9D 7B36", #CJK UNIFIED IDEOGRAPH
+ "E2 9E 7B50", #CJK UNIFIED IDEOGRAPH
+ "E2 9F 7B7A", #CJK UNIFIED IDEOGRAPH
+ "E2 A0 7B04", #CJK UNIFIED IDEOGRAPH
+ "E2 A1 7B4D", #CJK UNIFIED IDEOGRAPH
+ "E2 A2 7B0B", #CJK UNIFIED IDEOGRAPH
+ "E2 A3 7B4C", #CJK UNIFIED IDEOGRAPH
+ "E2 A4 7B45", #CJK UNIFIED IDEOGRAPH
+ "E2 A5 7B75", #CJK UNIFIED IDEOGRAPH
+ "E2 A6 7B65", #CJK UNIFIED IDEOGRAPH
+ "E2 A7 7B74", #CJK UNIFIED IDEOGRAPH
+ "E2 A8 7B67", #CJK UNIFIED IDEOGRAPH
+ "E2 A9 7B70", #CJK UNIFIED IDEOGRAPH
+ "E2 AA 7B71", #CJK UNIFIED IDEOGRAPH
+ "E2 AB 7B6C", #CJK UNIFIED IDEOGRAPH
+ "E2 AC 7B6E", #CJK UNIFIED IDEOGRAPH
+ "E2 AD 7B9D", #CJK UNIFIED IDEOGRAPH
+ "E2 AE 7B98", #CJK UNIFIED IDEOGRAPH
+ "E2 AF 7B9F", #CJK UNIFIED IDEOGRAPH
+ "E2 B0 7B8D", #CJK UNIFIED IDEOGRAPH
+ "E2 B1 7B9C", #CJK UNIFIED IDEOGRAPH
+ "E2 B2 7B9A", #CJK UNIFIED IDEOGRAPH
+ "E2 B3 7B8B", #CJK UNIFIED IDEOGRAPH
+ "E2 B4 7B92", #CJK UNIFIED IDEOGRAPH
+ "E2 B5 7B8F", #CJK UNIFIED IDEOGRAPH
+ "E2 B6 7B5D", #CJK UNIFIED IDEOGRAPH
+ "E2 B7 7B99", #CJK UNIFIED IDEOGRAPH
+ "E2 B8 7BCB", #CJK UNIFIED IDEOGRAPH
+ "E2 B9 7BC1", #CJK UNIFIED IDEOGRAPH
+ "E2 BA 7BCC", #CJK UNIFIED IDEOGRAPH
+ "E2 BB 7BCF", #CJK UNIFIED IDEOGRAPH
+ "E2 BC 7BB4", #CJK UNIFIED IDEOGRAPH
+ "E2 BD 7BC6", #CJK UNIFIED IDEOGRAPH
+ "E2 BE 7BDD", #CJK UNIFIED IDEOGRAPH
+ "E2 BF 7BE9", #CJK UNIFIED IDEOGRAPH
+ "E2 C0 7C11", #CJK UNIFIED IDEOGRAPH
+ "E2 C1 7C14", #CJK UNIFIED IDEOGRAPH
+ "E2 C2 7BE6", #CJK UNIFIED IDEOGRAPH
+ "E2 C3 7BE5", #CJK UNIFIED IDEOGRAPH
+ "E2 C4 7C60", #CJK UNIFIED IDEOGRAPH
+ "E2 C5 7C00", #CJK UNIFIED IDEOGRAPH
+ "E2 C6 7C07", #CJK UNIFIED IDEOGRAPH
+ "E2 C7 7C13", #CJK UNIFIED IDEOGRAPH
+ "E2 C8 7BF3", #CJK UNIFIED IDEOGRAPH
+ "E2 C9 7BF7", #CJK UNIFIED IDEOGRAPH
+ "E2 CA 7C17", #CJK UNIFIED IDEOGRAPH
+ "E2 CB 7C0D", #CJK UNIFIED IDEOGRAPH
+ "E2 CC 7BF6", #CJK UNIFIED IDEOGRAPH
+ "E2 CD 7C23", #CJK UNIFIED IDEOGRAPH
+ "E2 CE 7C27", #CJK UNIFIED IDEOGRAPH
+ "E2 CF 7C2A", #CJK UNIFIED IDEOGRAPH
+ "E2 D0 7C1F", #CJK UNIFIED IDEOGRAPH
+ "E2 D1 7C37", #CJK UNIFIED IDEOGRAPH
+ "E2 D2 7C2B", #CJK UNIFIED IDEOGRAPH
+ "E2 D3 7C3D", #CJK UNIFIED IDEOGRAPH
+ "E2 D4 7C4C", #CJK UNIFIED IDEOGRAPH
+ "E2 D5 7C43", #CJK UNIFIED IDEOGRAPH
+ "E2 D6 7C54", #CJK UNIFIED IDEOGRAPH
+ "E2 D7 7C4F", #CJK UNIFIED IDEOGRAPH
+ "E2 D8 7C40", #CJK UNIFIED IDEOGRAPH
+ "E2 D9 7C50", #CJK UNIFIED IDEOGRAPH
+ "E2 DA 7C58", #CJK UNIFIED IDEOGRAPH
+ "E2 DB 7C5F", #CJK UNIFIED IDEOGRAPH
+ "E2 DC 7C64", #CJK UNIFIED IDEOGRAPH
+ "E2 DD 7C56", #CJK UNIFIED IDEOGRAPH
+ "E2 DE 7C65", #CJK UNIFIED IDEOGRAPH
+ "E2 DF 7C6C", #CJK UNIFIED IDEOGRAPH
+ "E2 E0 7C75", #CJK UNIFIED IDEOGRAPH
+ "E2 E1 7C83", #CJK UNIFIED IDEOGRAPH
+ "E2 E2 7C90", #CJK UNIFIED IDEOGRAPH
+ "E2 E3 7CA4", #CJK UNIFIED IDEOGRAPH
+ "E2 E4 7CAD", #CJK UNIFIED IDEOGRAPH
+ "E2 E5 7CA2", #CJK UNIFIED IDEOGRAPH
+ "E2 E6 7CAB", #CJK UNIFIED IDEOGRAPH
+ "E2 E7 7CA1", #CJK UNIFIED IDEOGRAPH
+ "E2 E8 7CA8", #CJK UNIFIED IDEOGRAPH
+ "E2 E9 7CB3", #CJK UNIFIED IDEOGRAPH
+ "E2 EA 7CB2", #CJK UNIFIED IDEOGRAPH
+ "E2 EB 7CB1", #CJK UNIFIED IDEOGRAPH
+ "E2 EC 7CAE", #CJK UNIFIED IDEOGRAPH
+ "E2 ED 7CB9", #CJK UNIFIED IDEOGRAPH
+ "E2 EE 7CBD", #CJK UNIFIED IDEOGRAPH
+ "E2 EF 7CC0", #CJK UNIFIED IDEOGRAPH
+ "E2 F0 7CC5", #CJK UNIFIED IDEOGRAPH
+ "E2 F1 7CC2", #CJK UNIFIED IDEOGRAPH
+ "E2 F2 7CD8", #CJK UNIFIED IDEOGRAPH
+ "E2 F3 7CD2", #CJK UNIFIED IDEOGRAPH
+ "E2 F4 7CDC", #CJK UNIFIED IDEOGRAPH
+ "E2 F5 7CE2", #CJK UNIFIED IDEOGRAPH
+ "E2 F6 9B3B", #CJK UNIFIED IDEOGRAPH
+ "E2 F7 7CEF", #CJK UNIFIED IDEOGRAPH
+ "E2 F8 7CF2", #CJK UNIFIED IDEOGRAPH
+ "E2 F9 7CF4", #CJK UNIFIED IDEOGRAPH
+ "E2 FA 7CF6", #CJK UNIFIED IDEOGRAPH
+ "E2 FB 7CFA", #CJK UNIFIED IDEOGRAPH
+ "E2 FC 7D06", #CJK UNIFIED IDEOGRAPH
+ "E3 40 7D02", #CJK UNIFIED IDEOGRAPH
+ "E3 41 7D1C", #CJK UNIFIED IDEOGRAPH
+ "E3 42 7D15", #CJK UNIFIED IDEOGRAPH
+ "E3 43 7D0A", #CJK UNIFIED IDEOGRAPH
+ "E3 44 7D45", #CJK UNIFIED IDEOGRAPH
+ "E3 45 7D4B", #CJK UNIFIED IDEOGRAPH
+ "E3 46 7D2E", #CJK UNIFIED IDEOGRAPH
+ "E3 47 7D32", #CJK UNIFIED IDEOGRAPH
+ "E3 48 7D3F", #CJK UNIFIED IDEOGRAPH
+ "E3 49 7D35", #CJK UNIFIED IDEOGRAPH
+ "E3 4A 7D46", #CJK UNIFIED IDEOGRAPH
+ "E3 4B 7D73", #CJK UNIFIED IDEOGRAPH
+ "E3 4C 7D56", #CJK UNIFIED IDEOGRAPH
+ "E3 4D 7D4E", #CJK UNIFIED IDEOGRAPH
+ "E3 4E 7D72", #CJK UNIFIED IDEOGRAPH
+ "E3 4F 7D68", #CJK UNIFIED IDEOGRAPH
+ "E3 50 7D6E", #CJK UNIFIED IDEOGRAPH
+ "E3 51 7D4F", #CJK UNIFIED IDEOGRAPH
+ "E3 52 7D63", #CJK UNIFIED IDEOGRAPH
+ "E3 53 7D93", #CJK UNIFIED IDEOGRAPH
+ "E3 54 7D89", #CJK UNIFIED IDEOGRAPH
+ "E3 55 7D5B", #CJK UNIFIED IDEOGRAPH
+ "E3 56 7D8F", #CJK UNIFIED IDEOGRAPH
+ "E3 57 7D7D", #CJK UNIFIED IDEOGRAPH
+ "E3 58 7D9B", #CJK UNIFIED IDEOGRAPH
+ "E3 59 7DBA", #CJK UNIFIED IDEOGRAPH
+ "E3 5A 7DAE", #CJK UNIFIED IDEOGRAPH
+ "E3 5B 7DA3", #CJK UNIFIED IDEOGRAPH
+ "E3 5C 7DB5", #CJK UNIFIED IDEOGRAPH
+ "E3 5D 7DC7", #CJK UNIFIED IDEOGRAPH
+ "E3 5E 7DBD", #CJK UNIFIED IDEOGRAPH
+ "E3 5F 7DAB", #CJK UNIFIED IDEOGRAPH
+ "E3 60 7E3D", #CJK UNIFIED IDEOGRAPH
+ "E3 61 7DA2", #CJK UNIFIED IDEOGRAPH
+ "E3 62 7DAF", #CJK UNIFIED IDEOGRAPH
+ "E3 63 7DDC", #CJK UNIFIED IDEOGRAPH
+ "E3 64 7DB8", #CJK UNIFIED IDEOGRAPH
+ "E3 65 7D9F", #CJK UNIFIED IDEOGRAPH
+ "E3 66 7DB0", #CJK UNIFIED IDEOGRAPH
+ "E3 67 7DD8", #CJK UNIFIED IDEOGRAPH
+ "E3 68 7DDD", #CJK UNIFIED IDEOGRAPH
+ "E3 69 7DE4", #CJK UNIFIED IDEOGRAPH
+ "E3 6A 7DDE", #CJK UNIFIED IDEOGRAPH
+ "E3 6B 7DFB", #CJK UNIFIED IDEOGRAPH
+ "E3 6C 7DF2", #CJK UNIFIED IDEOGRAPH
+ "E3 6D 7DE1", #CJK UNIFIED IDEOGRAPH
+ "E3 6E 7E05", #CJK UNIFIED IDEOGRAPH
+ "E3 6F 7E0A", #CJK UNIFIED IDEOGRAPH
+ "E3 70 7E23", #CJK UNIFIED IDEOGRAPH
+ "E3 71 7E21", #CJK UNIFIED IDEOGRAPH
+ "E3 72 7E12", #CJK UNIFIED IDEOGRAPH
+ "E3 73 7E31", #CJK UNIFIED IDEOGRAPH
+ "E3 74 7E1F", #CJK UNIFIED IDEOGRAPH
+ "E3 75 7E09", #CJK UNIFIED IDEOGRAPH
+ "E3 76 7E0B", #CJK UNIFIED IDEOGRAPH
+ "E3 77 7E22", #CJK UNIFIED IDEOGRAPH
+ "E3 78 7E46", #CJK UNIFIED IDEOGRAPH
+ "E3 79 7E66", #CJK UNIFIED IDEOGRAPH
+ "E3 7A 7E3B", #CJK UNIFIED IDEOGRAPH
+ "E3 7B 7E35", #CJK UNIFIED IDEOGRAPH
+ "E3 7C 7E39", #CJK UNIFIED IDEOGRAPH
+ "E3 7D 7E43", #CJK UNIFIED IDEOGRAPH
+ "E3 7E 7E37", #CJK UNIFIED IDEOGRAPH
+ "E3 80 7E32", #CJK UNIFIED IDEOGRAPH
+ "E3 81 7E3A", #CJK UNIFIED IDEOGRAPH
+ "E3 82 7E67", #CJK UNIFIED IDEOGRAPH
+ "E3 83 7E5D", #CJK UNIFIED IDEOGRAPH
+ "E3 84 7E56", #CJK UNIFIED IDEOGRAPH
+ "E3 85 7E5E", #CJK UNIFIED IDEOGRAPH
+ "E3 86 7E59", #CJK UNIFIED IDEOGRAPH
+ "E3 87 7E5A", #CJK UNIFIED IDEOGRAPH
+ "E3 88 7E79", #CJK UNIFIED IDEOGRAPH
+ "E3 89 7E6A", #CJK UNIFIED IDEOGRAPH
+ "E3 8A 7E69", #CJK UNIFIED IDEOGRAPH
+ "E3 8B 7E7C", #CJK UNIFIED IDEOGRAPH
+ "E3 8C 7E7B", #CJK UNIFIED IDEOGRAPH
+ "E3 8D 7E83", #CJK UNIFIED IDEOGRAPH
+ "E3 8E 7DD5", #CJK UNIFIED IDEOGRAPH
+ "E3 8F 7E7D", #CJK UNIFIED IDEOGRAPH
+ "E3 90 8FAE", #CJK UNIFIED IDEOGRAPH
+ "E3 91 7E7F", #CJK UNIFIED IDEOGRAPH
+ "E3 92 7E88", #CJK UNIFIED IDEOGRAPH
+ "E3 93 7E89", #CJK UNIFIED IDEOGRAPH
+ "E3 94 7E8C", #CJK UNIFIED IDEOGRAPH
+ "E3 95 7E92", #CJK UNIFIED IDEOGRAPH
+ "E3 96 7E90", #CJK UNIFIED IDEOGRAPH
+ "E3 97 7E93", #CJK UNIFIED IDEOGRAPH
+ "E3 98 7E94", #CJK UNIFIED IDEOGRAPH
+ "E3 99 7E96", #CJK UNIFIED IDEOGRAPH
+ "E3 9A 7E8E", #CJK UNIFIED IDEOGRAPH
+ "E3 9B 7E9B", #CJK UNIFIED IDEOGRAPH
+ "E3 9C 7E9C", #CJK UNIFIED IDEOGRAPH
+ "E3 9D 7F38", #CJK UNIFIED IDEOGRAPH
+ "E3 9E 7F3A", #CJK UNIFIED IDEOGRAPH
+ "E3 9F 7F45", #CJK UNIFIED IDEOGRAPH
+ "E3 A0 7F4C", #CJK UNIFIED IDEOGRAPH
+ "E3 A1 7F4D", #CJK UNIFIED IDEOGRAPH
+ "E3 A2 7F4E", #CJK UNIFIED IDEOGRAPH
+ "E3 A3 7F50", #CJK UNIFIED IDEOGRAPH
+ "E3 A4 7F51", #CJK UNIFIED IDEOGRAPH
+ "E3 A5 7F55", #CJK UNIFIED IDEOGRAPH
+ "E3 A6 7F54", #CJK UNIFIED IDEOGRAPH
+ "E3 A7 7F58", #CJK UNIFIED IDEOGRAPH
+ "E3 A8 7F5F", #CJK UNIFIED IDEOGRAPH
+ "E3 A9 7F60", #CJK UNIFIED IDEOGRAPH
+ "E3 AA 7F68", #CJK UNIFIED IDEOGRAPH
+ "E3 AB 7F69", #CJK UNIFIED IDEOGRAPH
+ "E3 AC 7F67", #CJK UNIFIED IDEOGRAPH
+ "E3 AD 7F78", #CJK UNIFIED IDEOGRAPH
+ "E3 AE 7F82", #CJK UNIFIED IDEOGRAPH
+ "E3 AF 7F86", #CJK UNIFIED IDEOGRAPH
+ "E3 B0 7F83", #CJK UNIFIED IDEOGRAPH
+ "E3 B1 7F88", #CJK UNIFIED IDEOGRAPH
+ "E3 B2 7F87", #CJK UNIFIED IDEOGRAPH
+ "E3 B3 7F8C", #CJK UNIFIED IDEOGRAPH
+ "E3 B4 7F94", #CJK UNIFIED IDEOGRAPH
+ "E3 B5 7F9E", #CJK UNIFIED IDEOGRAPH
+ "E3 B6 7F9D", #CJK UNIFIED IDEOGRAPH
+ "E3 B7 7F9A", #CJK UNIFIED IDEOGRAPH
+ "E3 B8 7FA3", #CJK UNIFIED IDEOGRAPH
+ "E3 B9 7FAF", #CJK UNIFIED IDEOGRAPH
+ "E3 BA 7FB2", #CJK UNIFIED IDEOGRAPH
+ "E3 BB 7FB9", #CJK UNIFIED IDEOGRAPH
+ "E3 BC 7FAE", #CJK UNIFIED IDEOGRAPH
+ "E3 BD 7FB6", #CJK UNIFIED IDEOGRAPH
+ "E3 BE 7FB8", #CJK UNIFIED IDEOGRAPH
+ "E3 BF 8B71", #CJK UNIFIED IDEOGRAPH
+ "E3 C0 7FC5", #CJK UNIFIED IDEOGRAPH
+ "E3 C1 7FC6", #CJK UNIFIED IDEOGRAPH
+ "E3 C2 7FCA", #CJK UNIFIED IDEOGRAPH
+ "E3 C3 7FD5", #CJK UNIFIED IDEOGRAPH
+ "E3 C4 7FD4", #CJK UNIFIED IDEOGRAPH
+ "E3 C5 7FE1", #CJK UNIFIED IDEOGRAPH
+ "E3 C6 7FE6", #CJK UNIFIED IDEOGRAPH
+ "E3 C7 7FE9", #CJK UNIFIED IDEOGRAPH
+ "E3 C8 7FF3", #CJK UNIFIED IDEOGRAPH
+ "E3 C9 7FF9", #CJK UNIFIED IDEOGRAPH
+ "E3 CA 98DC", #CJK UNIFIED IDEOGRAPH
+ "E3 CB 8006", #CJK UNIFIED IDEOGRAPH
+ "E3 CC 8004", #CJK UNIFIED IDEOGRAPH
+ "E3 CD 800B", #CJK UNIFIED IDEOGRAPH
+ "E3 CE 8012", #CJK UNIFIED IDEOGRAPH
+ "E3 CF 8018", #CJK UNIFIED IDEOGRAPH
+ "E3 D0 8019", #CJK UNIFIED IDEOGRAPH
+ "E3 D1 801C", #CJK UNIFIED IDEOGRAPH
+ "E3 D2 8021", #CJK UNIFIED IDEOGRAPH
+ "E3 D3 8028", #CJK UNIFIED IDEOGRAPH
+ "E3 D4 803F", #CJK UNIFIED IDEOGRAPH
+ "E3 D5 803B", #CJK UNIFIED IDEOGRAPH
+ "E3 D6 804A", #CJK UNIFIED IDEOGRAPH
+ "E3 D7 8046", #CJK UNIFIED IDEOGRAPH
+ "E3 D8 8052", #CJK UNIFIED IDEOGRAPH
+ "E3 D9 8058", #CJK UNIFIED IDEOGRAPH
+ "E3 DA 805A", #CJK UNIFIED IDEOGRAPH
+ "E3 DB 805F", #CJK UNIFIED IDEOGRAPH
+ "E3 DC 8062", #CJK UNIFIED IDEOGRAPH
+ "E3 DD 8068", #CJK UNIFIED IDEOGRAPH
+ "E3 DE 8073", #CJK UNIFIED IDEOGRAPH
+ "E3 DF 8072", #CJK UNIFIED IDEOGRAPH
+ "E3 E0 8070", #CJK UNIFIED IDEOGRAPH
+ "E3 E1 8076", #CJK UNIFIED IDEOGRAPH
+ "E3 E2 8079", #CJK UNIFIED IDEOGRAPH
+ "E3 E3 807D", #CJK UNIFIED IDEOGRAPH
+ "E3 E4 807F", #CJK UNIFIED IDEOGRAPH
+ "E3 E5 8084", #CJK UNIFIED IDEOGRAPH
+ "E3 E6 8086", #CJK UNIFIED IDEOGRAPH
+ "E3 E7 8085", #CJK UNIFIED IDEOGRAPH
+ "E3 E8 809B", #CJK UNIFIED IDEOGRAPH
+ "E3 E9 8093", #CJK UNIFIED IDEOGRAPH
+ "E3 EA 809A", #CJK UNIFIED IDEOGRAPH
+ "E3 EB 80AD", #CJK UNIFIED IDEOGRAPH
+ "E3 EC 5190", #CJK UNIFIED IDEOGRAPH
+ "E3 ED 80AC", #CJK UNIFIED IDEOGRAPH
+ "E3 EE 80DB", #CJK UNIFIED IDEOGRAPH
+ "E3 EF 80E5", #CJK UNIFIED IDEOGRAPH
+ "E3 F0 80D9", #CJK UNIFIED IDEOGRAPH
+ "E3 F1 80DD", #CJK UNIFIED IDEOGRAPH
+ "E3 F2 80C4", #CJK UNIFIED IDEOGRAPH
+ "E3 F3 80DA", #CJK UNIFIED IDEOGRAPH
+ "E3 F4 80D6", #CJK UNIFIED IDEOGRAPH
+ "E3 F5 8109", #CJK UNIFIED IDEOGRAPH
+ "E3 F6 80EF", #CJK UNIFIED IDEOGRAPH
+ "E3 F7 80F1", #CJK UNIFIED IDEOGRAPH
+ "E3 F8 811B", #CJK UNIFIED IDEOGRAPH
+ "E3 F9 8129", #CJK UNIFIED IDEOGRAPH
+ "E3 FA 8123", #CJK UNIFIED IDEOGRAPH
+ "E3 FB 812F", #CJK UNIFIED IDEOGRAPH
+ "E3 FC 814B", #CJK UNIFIED IDEOGRAPH
+ "E4 40 968B", #CJK UNIFIED IDEOGRAPH
+ "E4 41 8146", #CJK UNIFIED IDEOGRAPH
+ "E4 42 813E", #CJK UNIFIED IDEOGRAPH
+ "E4 43 8153", #CJK UNIFIED IDEOGRAPH
+ "E4 44 8151", #CJK UNIFIED IDEOGRAPH
+ "E4 45 80FC", #CJK UNIFIED IDEOGRAPH
+ "E4 46 8171", #CJK UNIFIED IDEOGRAPH
+ "E4 47 816E", #CJK UNIFIED IDEOGRAPH
+ "E4 48 8165", #CJK UNIFIED IDEOGRAPH
+ "E4 49 8166", #CJK UNIFIED IDEOGRAPH
+ "E4 4A 8174", #CJK UNIFIED IDEOGRAPH
+ "E4 4B 8183", #CJK UNIFIED IDEOGRAPH
+ "E4 4C 8188", #CJK UNIFIED IDEOGRAPH
+ "E4 4D 818A", #CJK UNIFIED IDEOGRAPH
+ "E4 4E 8180", #CJK UNIFIED IDEOGRAPH
+ "E4 4F 8182", #CJK UNIFIED IDEOGRAPH
+ "E4 50 81A0", #CJK UNIFIED IDEOGRAPH
+ "E4 51 8195", #CJK UNIFIED IDEOGRAPH
+ "E4 52 81A4", #CJK UNIFIED IDEOGRAPH
+ "E4 53 81A3", #CJK UNIFIED IDEOGRAPH
+ "E4 54 815F", #CJK UNIFIED IDEOGRAPH
+ "E4 55 8193", #CJK UNIFIED IDEOGRAPH
+ "E4 56 81A9", #CJK UNIFIED IDEOGRAPH
+ "E4 57 81B0", #CJK UNIFIED IDEOGRAPH
+ "E4 58 81B5", #CJK UNIFIED IDEOGRAPH
+ "E4 59 81BE", #CJK UNIFIED IDEOGRAPH
+ "E4 5A 81B8", #CJK UNIFIED IDEOGRAPH
+ "E4 5B 81BD", #CJK UNIFIED IDEOGRAPH
+ "E4 5C 81C0", #CJK UNIFIED IDEOGRAPH
+ "E4 5D 81C2", #CJK UNIFIED IDEOGRAPH
+ "E4 5E 81BA", #CJK UNIFIED IDEOGRAPH
+ "E4 5F 81C9", #CJK UNIFIED IDEOGRAPH
+ "E4 60 81CD", #CJK UNIFIED IDEOGRAPH
+ "E4 61 81D1", #CJK UNIFIED IDEOGRAPH
+ "E4 62 81D9", #CJK UNIFIED IDEOGRAPH
+ "E4 63 81D8", #CJK UNIFIED IDEOGRAPH
+ "E4 64 81C8", #CJK UNIFIED IDEOGRAPH
+ "E4 65 81DA", #CJK UNIFIED IDEOGRAPH
+ "E4 66 81DF", #CJK UNIFIED IDEOGRAPH
+ "E4 67 81E0", #CJK UNIFIED IDEOGRAPH
+ "E4 68 81E7", #CJK UNIFIED IDEOGRAPH
+ "E4 69 81FA", #CJK UNIFIED IDEOGRAPH
+ "E4 6A 81FB", #CJK UNIFIED IDEOGRAPH
+ "E4 6B 81FE", #CJK UNIFIED IDEOGRAPH
+ "E4 6C 8201", #CJK UNIFIED IDEOGRAPH
+ "E4 6D 8202", #CJK UNIFIED IDEOGRAPH
+ "E4 6E 8205", #CJK UNIFIED IDEOGRAPH
+ "E4 6F 8207", #CJK UNIFIED IDEOGRAPH
+ "E4 70 820A", #CJK UNIFIED IDEOGRAPH
+ "E4 71 820D", #CJK UNIFIED IDEOGRAPH
+ "E4 72 8210", #CJK UNIFIED IDEOGRAPH
+ "E4 73 8216", #CJK UNIFIED IDEOGRAPH
+ "E4 74 8229", #CJK UNIFIED IDEOGRAPH
+ "E4 75 822B", #CJK UNIFIED IDEOGRAPH
+ "E4 76 8238", #CJK UNIFIED IDEOGRAPH
+ "E4 77 8233", #CJK UNIFIED IDEOGRAPH
+ "E4 78 8240", #CJK UNIFIED IDEOGRAPH
+ "E4 79 8259", #CJK UNIFIED IDEOGRAPH
+ "E4 7A 8258", #CJK UNIFIED IDEOGRAPH
+ "E4 7B 825D", #CJK UNIFIED IDEOGRAPH
+ "E4 7C 825A", #CJK UNIFIED IDEOGRAPH
+ "E4 7D 825F", #CJK UNIFIED IDEOGRAPH
+ "E4 7E 8264", #CJK UNIFIED IDEOGRAPH
+ "E4 80 8262", #CJK UNIFIED IDEOGRAPH
+ "E4 81 8268", #CJK UNIFIED IDEOGRAPH
+ "E4 82 826A", #CJK UNIFIED IDEOGRAPH
+ "E4 83 826B", #CJK UNIFIED IDEOGRAPH
+ "E4 84 822E", #CJK UNIFIED IDEOGRAPH
+ "E4 85 8271", #CJK UNIFIED IDEOGRAPH
+ "E4 86 8277", #CJK UNIFIED IDEOGRAPH
+ "E4 87 8278", #CJK UNIFIED IDEOGRAPH
+ "E4 88 827E", #CJK UNIFIED IDEOGRAPH
+ "E4 89 828D", #CJK UNIFIED IDEOGRAPH
+ "E4 8A 8292", #CJK UNIFIED IDEOGRAPH
+ "E4 8B 82AB", #CJK UNIFIED IDEOGRAPH
+ "E4 8C 829F", #CJK UNIFIED IDEOGRAPH
+ "E4 8D 82BB", #CJK UNIFIED IDEOGRAPH
+ "E4 8E 82AC", #CJK UNIFIED IDEOGRAPH
+ "E4 8F 82E1", #CJK UNIFIED IDEOGRAPH
+ "E4 90 82E3", #CJK UNIFIED IDEOGRAPH
+ "E4 91 82DF", #CJK UNIFIED IDEOGRAPH
+ "E4 92 82D2", #CJK UNIFIED IDEOGRAPH
+ "E4 93 82F4", #CJK UNIFIED IDEOGRAPH
+ "E4 94 82F3", #CJK UNIFIED IDEOGRAPH
+ "E4 95 82FA", #CJK UNIFIED IDEOGRAPH
+ "E4 96 8393", #CJK UNIFIED IDEOGRAPH
+ "E4 97 8303", #CJK UNIFIED IDEOGRAPH
+ "E4 98 82FB", #CJK UNIFIED IDEOGRAPH
+ "E4 99 82F9", #CJK UNIFIED IDEOGRAPH
+ "E4 9A 82DE", #CJK UNIFIED IDEOGRAPH
+ "E4 9B 8306", #CJK UNIFIED IDEOGRAPH
+ "E4 9C 82DC", #CJK UNIFIED IDEOGRAPH
+ "E4 9D 8309", #CJK UNIFIED IDEOGRAPH
+ "E4 9E 82D9", #CJK UNIFIED IDEOGRAPH
+ "E4 9F 8335", #CJK UNIFIED IDEOGRAPH
+ "E4 A0 8334", #CJK UNIFIED IDEOGRAPH
+ "E4 A1 8316", #CJK UNIFIED IDEOGRAPH
+ "E4 A2 8332", #CJK UNIFIED IDEOGRAPH
+ "E4 A3 8331", #CJK UNIFIED IDEOGRAPH
+ "E4 A4 8340", #CJK UNIFIED IDEOGRAPH
+ "E4 A5 8339", #CJK UNIFIED IDEOGRAPH
+ "E4 A6 8350", #CJK UNIFIED IDEOGRAPH
+ "E4 A7 8345", #CJK UNIFIED IDEOGRAPH
+ "E4 A8 832F", #CJK UNIFIED IDEOGRAPH
+ "E4 A9 832B", #CJK UNIFIED IDEOGRAPH
+ "E4 AA 8317", #CJK UNIFIED IDEOGRAPH
+ "E4 AB 8318", #CJK UNIFIED IDEOGRAPH
+ "E4 AC 8385", #CJK UNIFIED IDEOGRAPH
+ "E4 AD 839A", #CJK UNIFIED IDEOGRAPH
+ "E4 AE 83AA", #CJK UNIFIED IDEOGRAPH
+ "E4 AF 839F", #CJK UNIFIED IDEOGRAPH
+ "E4 B0 83A2", #CJK UNIFIED IDEOGRAPH
+ "E4 B1 8396", #CJK UNIFIED IDEOGRAPH
+ "E4 B2 8323", #CJK UNIFIED IDEOGRAPH
+ "E4 B3 838E", #CJK UNIFIED IDEOGRAPH
+ "E4 B4 8387", #CJK UNIFIED IDEOGRAPH
+ "E4 B5 838A", #CJK UNIFIED IDEOGRAPH
+ "E4 B6 837C", #CJK UNIFIED IDEOGRAPH
+ "E4 B7 83B5", #CJK UNIFIED IDEOGRAPH
+ "E4 B8 8373", #CJK UNIFIED IDEOGRAPH
+ "E4 B9 8375", #CJK UNIFIED IDEOGRAPH
+ "E4 BA 83A0", #CJK UNIFIED IDEOGRAPH
+ "E4 BB 8389", #CJK UNIFIED IDEOGRAPH
+ "E4 BC 83A8", #CJK UNIFIED IDEOGRAPH
+ "E4 BD 83F4", #CJK UNIFIED IDEOGRAPH
+ "E4 BE 8413", #CJK UNIFIED IDEOGRAPH
+ "E4 BF 83EB", #CJK UNIFIED IDEOGRAPH
+ "E4 C0 83CE", #CJK UNIFIED IDEOGRAPH
+ "E4 C1 83FD", #CJK UNIFIED IDEOGRAPH
+ "E4 C2 8403", #CJK UNIFIED IDEOGRAPH
+ "E4 C3 83D8", #CJK UNIFIED IDEOGRAPH
+ "E4 C4 840B", #CJK UNIFIED IDEOGRAPH
+ "E4 C5 83C1", #CJK UNIFIED IDEOGRAPH
+ "E4 C6 83F7", #CJK UNIFIED IDEOGRAPH
+ "E4 C7 8407", #CJK UNIFIED IDEOGRAPH
+ "E4 C8 83E0", #CJK UNIFIED IDEOGRAPH
+ "E4 C9 83F2", #CJK UNIFIED IDEOGRAPH
+ "E4 CA 840D", #CJK UNIFIED IDEOGRAPH
+ "E4 CB 8422", #CJK UNIFIED IDEOGRAPH
+ "E4 CC 8420", #CJK UNIFIED IDEOGRAPH
+ "E4 CD 83BD", #CJK UNIFIED IDEOGRAPH
+ "E4 CE 8438", #CJK UNIFIED IDEOGRAPH
+ "E4 CF 8506", #CJK UNIFIED IDEOGRAPH
+ "E4 D0 83FB", #CJK UNIFIED IDEOGRAPH
+ "E4 D1 846D", #CJK UNIFIED IDEOGRAPH
+ "E4 D2 842A", #CJK UNIFIED IDEOGRAPH
+ "E4 D3 843C", #CJK UNIFIED IDEOGRAPH
+ "E4 D4 855A", #CJK UNIFIED IDEOGRAPH
+ "E4 D5 8484", #CJK UNIFIED IDEOGRAPH
+ "E4 D6 8477", #CJK UNIFIED IDEOGRAPH
+ "E4 D7 846B", #CJK UNIFIED IDEOGRAPH
+ "E4 D8 84AD", #CJK UNIFIED IDEOGRAPH
+ "E4 D9 846E", #CJK UNIFIED IDEOGRAPH
+ "E4 DA 8482", #CJK UNIFIED IDEOGRAPH
+ "E4 DB 8469", #CJK UNIFIED IDEOGRAPH
+ "E4 DC 8446", #CJK UNIFIED IDEOGRAPH
+ "E4 DD 842C", #CJK UNIFIED IDEOGRAPH
+ "E4 DE 846F", #CJK UNIFIED IDEOGRAPH
+ "E4 DF 8479", #CJK UNIFIED IDEOGRAPH
+ "E4 E0 8435", #CJK UNIFIED IDEOGRAPH
+ "E4 E1 84CA", #CJK UNIFIED IDEOGRAPH
+ "E4 E2 8462", #CJK UNIFIED IDEOGRAPH
+ "E4 E3 84B9", #CJK UNIFIED IDEOGRAPH
+ "E4 E4 84BF", #CJK UNIFIED IDEOGRAPH
+ "E4 E5 849F", #CJK UNIFIED IDEOGRAPH
+ "E4 E6 84D9", #CJK UNIFIED IDEOGRAPH
+ "E4 E7 84CD", #CJK UNIFIED IDEOGRAPH
+ "E4 E8 84BB", #CJK UNIFIED IDEOGRAPH
+ "E4 E9 84DA", #CJK UNIFIED IDEOGRAPH
+ "E4 EA 84D0", #CJK UNIFIED IDEOGRAPH
+ "E4 EB 84C1", #CJK UNIFIED IDEOGRAPH
+ "E4 EC 84C6", #CJK UNIFIED IDEOGRAPH
+ "E4 ED 84D6", #CJK UNIFIED IDEOGRAPH
+ "E4 EE 84A1", #CJK UNIFIED IDEOGRAPH
+ "E4 EF 8521", #CJK UNIFIED IDEOGRAPH
+ "E4 F0 84FF", #CJK UNIFIED IDEOGRAPH
+ "E4 F1 84F4", #CJK UNIFIED IDEOGRAPH
+ "E4 F2 8517", #CJK UNIFIED IDEOGRAPH
+ "E4 F3 8518", #CJK UNIFIED IDEOGRAPH
+ "E4 F4 852C", #CJK UNIFIED IDEOGRAPH
+ "E4 F5 851F", #CJK UNIFIED IDEOGRAPH
+ "E4 F6 8515", #CJK UNIFIED IDEOGRAPH
+ "E4 F7 8514", #CJK UNIFIED IDEOGRAPH
+ "E4 F8 84FC", #CJK UNIFIED IDEOGRAPH
+ "E4 F9 8540", #CJK UNIFIED IDEOGRAPH
+ "E4 FA 8563", #CJK UNIFIED IDEOGRAPH
+ "E4 FB 8558", #CJK UNIFIED IDEOGRAPH
+ "E4 FC 8548", #CJK UNIFIED IDEOGRAPH
+ "E5 40 8541", #CJK UNIFIED IDEOGRAPH
+ "E5 41 8602", #CJK UNIFIED IDEOGRAPH
+ "E5 42 854B", #CJK UNIFIED IDEOGRAPH
+ "E5 43 8555", #CJK UNIFIED IDEOGRAPH
+ "E5 44 8580", #CJK UNIFIED IDEOGRAPH
+ "E5 45 85A4", #CJK UNIFIED IDEOGRAPH
+ "E5 46 8588", #CJK UNIFIED IDEOGRAPH
+ "E5 47 8591", #CJK UNIFIED IDEOGRAPH
+ "E5 48 858A", #CJK UNIFIED IDEOGRAPH
+ "E5 49 85A8", #CJK UNIFIED IDEOGRAPH
+ "E5 4A 856D", #CJK UNIFIED IDEOGRAPH
+ "E5 4B 8594", #CJK UNIFIED IDEOGRAPH
+ "E5 4C 859B", #CJK UNIFIED IDEOGRAPH
+ "E5 4D 85EA", #CJK UNIFIED IDEOGRAPH
+ "E5 4E 8587", #CJK UNIFIED IDEOGRAPH
+ "E5 4F 859C", #CJK UNIFIED IDEOGRAPH
+ "E5 50 8577", #CJK UNIFIED IDEOGRAPH
+ "E5 51 857E", #CJK UNIFIED IDEOGRAPH
+ "E5 52 8590", #CJK UNIFIED IDEOGRAPH
+ "E5 53 85C9", #CJK UNIFIED IDEOGRAPH
+ "E5 54 85BA", #CJK UNIFIED IDEOGRAPH
+ "E5 55 85CF", #CJK UNIFIED IDEOGRAPH
+ "E5 56 85B9", #CJK UNIFIED IDEOGRAPH
+ "E5 57 85D0", #CJK UNIFIED IDEOGRAPH
+ "E5 58 85D5", #CJK UNIFIED IDEOGRAPH
+ "E5 59 85DD", #CJK UNIFIED IDEOGRAPH
+ "E5 5A 85E5", #CJK UNIFIED IDEOGRAPH
+ "E5 5B 85DC", #CJK UNIFIED IDEOGRAPH
+ "E5 5C 85F9", #CJK UNIFIED IDEOGRAPH
+ "E5 5D 860A", #CJK UNIFIED IDEOGRAPH
+ "E5 5E 8613", #CJK UNIFIED IDEOGRAPH
+ "E5 5F 860B", #CJK UNIFIED IDEOGRAPH
+ "E5 60 85FE", #CJK UNIFIED IDEOGRAPH
+ "E5 61 85FA", #CJK UNIFIED IDEOGRAPH
+ "E5 62 8606", #CJK UNIFIED IDEOGRAPH
+ "E5 63 8622", #CJK UNIFIED IDEOGRAPH
+ "E5 64 861A", #CJK UNIFIED IDEOGRAPH
+ "E5 65 8630", #CJK UNIFIED IDEOGRAPH
+ "E5 66 863F", #CJK UNIFIED IDEOGRAPH
+ "E5 67 864D", #CJK UNIFIED IDEOGRAPH
+ "E5 68 4E55", #CJK UNIFIED IDEOGRAPH
+ "E5 69 8654", #CJK UNIFIED IDEOGRAPH
+ "E5 6A 865F", #CJK UNIFIED IDEOGRAPH
+ "E5 6B 8667", #CJK UNIFIED IDEOGRAPH
+ "E5 6C 8671", #CJK UNIFIED IDEOGRAPH
+ "E5 6D 8693", #CJK UNIFIED IDEOGRAPH
+ "E5 6E 86A3", #CJK UNIFIED IDEOGRAPH
+ "E5 6F 86A9", #CJK UNIFIED IDEOGRAPH
+ "E5 70 86AA", #CJK UNIFIED IDEOGRAPH
+ "E5 71 868B", #CJK UNIFIED IDEOGRAPH
+ "E5 72 868C", #CJK UNIFIED IDEOGRAPH
+ "E5 73 86B6", #CJK UNIFIED IDEOGRAPH
+ "E5 74 86AF", #CJK UNIFIED IDEOGRAPH
+ "E5 75 86C4", #CJK UNIFIED IDEOGRAPH
+ "E5 76 86C6", #CJK UNIFIED IDEOGRAPH
+ "E5 77 86B0", #CJK UNIFIED IDEOGRAPH
+ "E5 78 86C9", #CJK UNIFIED IDEOGRAPH
+ "E5 79 8823", #CJK UNIFIED IDEOGRAPH
+ "E5 7A 86AB", #CJK UNIFIED IDEOGRAPH
+ "E5 7B 86D4", #CJK UNIFIED IDEOGRAPH
+ "E5 7C 86DE", #CJK UNIFIED IDEOGRAPH
+ "E5 7D 86E9", #CJK UNIFIED IDEOGRAPH
+ "E5 7E 86EC", #CJK UNIFIED IDEOGRAPH
+ "E5 80 86DF", #CJK UNIFIED IDEOGRAPH
+ "E5 81 86DB", #CJK UNIFIED IDEOGRAPH
+ "E5 82 86EF", #CJK UNIFIED IDEOGRAPH
+ "E5 83 8712", #CJK UNIFIED IDEOGRAPH
+ "E5 84 8706", #CJK UNIFIED IDEOGRAPH
+ "E5 85 8708", #CJK UNIFIED IDEOGRAPH
+ "E5 86 8700", #CJK UNIFIED IDEOGRAPH
+ "E5 87 8703", #CJK UNIFIED IDEOGRAPH
+ "E5 88 86FB", #CJK UNIFIED IDEOGRAPH
+ "E5 89 8711", #CJK UNIFIED IDEOGRAPH
+ "E5 8A 8709", #CJK UNIFIED IDEOGRAPH
+ "E5 8B 870D", #CJK UNIFIED IDEOGRAPH
+ "E5 8C 86F9", #CJK UNIFIED IDEOGRAPH
+ "E5 8D 870A", #CJK UNIFIED IDEOGRAPH
+ "E5 8E 8734", #CJK UNIFIED IDEOGRAPH
+ "E5 8F 873F", #CJK UNIFIED IDEOGRAPH
+ "E5 90 8737", #CJK UNIFIED IDEOGRAPH
+ "E5 91 873B", #CJK UNIFIED IDEOGRAPH
+ "E5 92 8725", #CJK UNIFIED IDEOGRAPH
+ "E5 93 8729", #CJK UNIFIED IDEOGRAPH
+ "E5 94 871A", #CJK UNIFIED IDEOGRAPH
+ "E5 95 8760", #CJK UNIFIED IDEOGRAPH
+ "E5 96 875F", #CJK UNIFIED IDEOGRAPH
+ "E5 97 8778", #CJK UNIFIED IDEOGRAPH
+ "E5 98 874C", #CJK UNIFIED IDEOGRAPH
+ "E5 99 874E", #CJK UNIFIED IDEOGRAPH
+ "E5 9A 8774", #CJK UNIFIED IDEOGRAPH
+ "E5 9B 8757", #CJK UNIFIED IDEOGRAPH
+ "E5 9C 8768", #CJK UNIFIED IDEOGRAPH
+ "E5 9D 876E", #CJK UNIFIED IDEOGRAPH
+ "E5 9E 8759", #CJK UNIFIED IDEOGRAPH
+ "E5 9F 8753", #CJK UNIFIED IDEOGRAPH
+ "E5 A0 8763", #CJK UNIFIED IDEOGRAPH
+ "E5 A1 876A", #CJK UNIFIED IDEOGRAPH
+ "E5 A2 8805", #CJK UNIFIED IDEOGRAPH
+ "E5 A3 87A2", #CJK UNIFIED IDEOGRAPH
+ "E5 A4 879F", #CJK UNIFIED IDEOGRAPH
+ "E5 A5 8782", #CJK UNIFIED IDEOGRAPH
+ "E5 A6 87AF", #CJK UNIFIED IDEOGRAPH
+ "E5 A7 87CB", #CJK UNIFIED IDEOGRAPH
+ "E5 A8 87BD", #CJK UNIFIED IDEOGRAPH
+ "E5 A9 87C0", #CJK UNIFIED IDEOGRAPH
+ "E5 AA 87D0", #CJK UNIFIED IDEOGRAPH
+ "E5 AB 96D6", #CJK UNIFIED IDEOGRAPH
+ "E5 AC 87AB", #CJK UNIFIED IDEOGRAPH
+ "E5 AD 87C4", #CJK UNIFIED IDEOGRAPH
+ "E5 AE 87B3", #CJK UNIFIED IDEOGRAPH
+ "E5 AF 87C7", #CJK UNIFIED IDEOGRAPH
+ "E5 B0 87C6", #CJK UNIFIED IDEOGRAPH
+ "E5 B1 87BB", #CJK UNIFIED IDEOGRAPH
+ "E5 B2 87EF", #CJK UNIFIED IDEOGRAPH
+ "E5 B3 87F2", #CJK UNIFIED IDEOGRAPH
+ "E5 B4 87E0", #CJK UNIFIED IDEOGRAPH
+ "E5 B5 880F", #CJK UNIFIED IDEOGRAPH
+ "E5 B6 880D", #CJK UNIFIED IDEOGRAPH
+ "E5 B7 87FE", #CJK UNIFIED IDEOGRAPH
+ "E5 B8 87F6", #CJK UNIFIED IDEOGRAPH
+ "E5 B9 87F7", #CJK UNIFIED IDEOGRAPH
+ "E5 BA 880E", #CJK UNIFIED IDEOGRAPH
+ "E5 BB 87D2", #CJK UNIFIED IDEOGRAPH
+ "E5 BC 8811", #CJK UNIFIED IDEOGRAPH
+ "E5 BD 8816", #CJK UNIFIED IDEOGRAPH
+ "E5 BE 8815", #CJK UNIFIED IDEOGRAPH
+ "E5 BF 8822", #CJK UNIFIED IDEOGRAPH
+ "E5 C0 8821", #CJK UNIFIED IDEOGRAPH
+ "E5 C1 8831", #CJK UNIFIED IDEOGRAPH
+ "E5 C2 8836", #CJK UNIFIED IDEOGRAPH
+ "E5 C3 8839", #CJK UNIFIED IDEOGRAPH
+ "E5 C4 8827", #CJK UNIFIED IDEOGRAPH
+ "E5 C5 883B", #CJK UNIFIED IDEOGRAPH
+ "E5 C6 8844", #CJK UNIFIED IDEOGRAPH
+ "E5 C7 8842", #CJK UNIFIED IDEOGRAPH
+ "E5 C8 8852", #CJK UNIFIED IDEOGRAPH
+ "E5 C9 8859", #CJK UNIFIED IDEOGRAPH
+ "E5 CA 885E", #CJK UNIFIED IDEOGRAPH
+ "E5 CB 8862", #CJK UNIFIED IDEOGRAPH
+ "E5 CC 886B", #CJK UNIFIED IDEOGRAPH
+ "E5 CD 8881", #CJK UNIFIED IDEOGRAPH
+ "E5 CE 887E", #CJK UNIFIED IDEOGRAPH
+ "E5 CF 889E", #CJK UNIFIED IDEOGRAPH
+ "E5 D0 8875", #CJK UNIFIED IDEOGRAPH
+ "E5 D1 887D", #CJK UNIFIED IDEOGRAPH
+ "E5 D2 88B5", #CJK UNIFIED IDEOGRAPH
+ "E5 D3 8872", #CJK UNIFIED IDEOGRAPH
+ "E5 D4 8882", #CJK UNIFIED IDEOGRAPH
+ "E5 D5 8897", #CJK UNIFIED IDEOGRAPH
+ "E5 D6 8892", #CJK UNIFIED IDEOGRAPH
+ "E5 D7 88AE", #CJK UNIFIED IDEOGRAPH
+ "E5 D8 8899", #CJK UNIFIED IDEOGRAPH
+ "E5 D9 88A2", #CJK UNIFIED IDEOGRAPH
+ "E5 DA 888D", #CJK UNIFIED IDEOGRAPH
+ "E5 DB 88A4", #CJK UNIFIED IDEOGRAPH
+ "E5 DC 88B0", #CJK UNIFIED IDEOGRAPH
+ "E5 DD 88BF", #CJK UNIFIED IDEOGRAPH
+ "E5 DE 88B1", #CJK UNIFIED IDEOGRAPH
+ "E5 DF 88C3", #CJK UNIFIED IDEOGRAPH
+ "E5 E0 88C4", #CJK UNIFIED IDEOGRAPH
+ "E5 E1 88D4", #CJK UNIFIED IDEOGRAPH
+ "E5 E2 88D8", #CJK UNIFIED IDEOGRAPH
+ "E5 E3 88D9", #CJK UNIFIED IDEOGRAPH
+ "E5 E4 88DD", #CJK UNIFIED IDEOGRAPH
+ "E5 E5 88F9", #CJK UNIFIED IDEOGRAPH
+ "E5 E6 8902", #CJK UNIFIED IDEOGRAPH
+ "E5 E7 88FC", #CJK UNIFIED IDEOGRAPH
+ "E5 E8 88F4", #CJK UNIFIED IDEOGRAPH
+ "E5 E9 88E8", #CJK UNIFIED IDEOGRAPH
+ "E5 EA 88F2", #CJK UNIFIED IDEOGRAPH
+ "E5 EB 8904", #CJK UNIFIED IDEOGRAPH
+ "E5 EC 890C", #CJK UNIFIED IDEOGRAPH
+ "E5 ED 890A", #CJK UNIFIED IDEOGRAPH
+ "E5 EE 8913", #CJK UNIFIED IDEOGRAPH
+ "E5 EF 8943", #CJK UNIFIED IDEOGRAPH
+ "E5 F0 891E", #CJK UNIFIED IDEOGRAPH
+ "E5 F1 8925", #CJK UNIFIED IDEOGRAPH
+ "E5 F2 892A", #CJK UNIFIED IDEOGRAPH
+ "E5 F3 892B", #CJK UNIFIED IDEOGRAPH
+ "E5 F4 8941", #CJK UNIFIED IDEOGRAPH
+ "E5 F5 8944", #CJK UNIFIED IDEOGRAPH
+ "E5 F6 893B", #CJK UNIFIED IDEOGRAPH
+ "E5 F7 8936", #CJK UNIFIED IDEOGRAPH
+ "E5 F8 8938", #CJK UNIFIED IDEOGRAPH
+ "E5 F9 894C", #CJK UNIFIED IDEOGRAPH
+ "E5 FA 891D", #CJK UNIFIED IDEOGRAPH
+ "E5 FB 8960", #CJK UNIFIED IDEOGRAPH
+ "E5 FC 895E", #CJK UNIFIED IDEOGRAPH
+ "E6 40 8966", #CJK UNIFIED IDEOGRAPH
+ "E6 41 8964", #CJK UNIFIED IDEOGRAPH
+ "E6 42 896D", #CJK UNIFIED IDEOGRAPH
+ "E6 43 896A", #CJK UNIFIED IDEOGRAPH
+ "E6 44 896F", #CJK UNIFIED IDEOGRAPH
+ "E6 45 8974", #CJK UNIFIED IDEOGRAPH
+ "E6 46 8977", #CJK UNIFIED IDEOGRAPH
+ "E6 47 897E", #CJK UNIFIED IDEOGRAPH
+ "E6 48 8983", #CJK UNIFIED IDEOGRAPH
+ "E6 49 8988", #CJK UNIFIED IDEOGRAPH
+ "E6 4A 898A", #CJK UNIFIED IDEOGRAPH
+ "E6 4B 8993", #CJK UNIFIED IDEOGRAPH
+ "E6 4C 8998", #CJK UNIFIED IDEOGRAPH
+ "E6 4D 89A1", #CJK UNIFIED IDEOGRAPH
+ "E6 4E 89A9", #CJK UNIFIED IDEOGRAPH
+ "E6 4F 89A6", #CJK UNIFIED IDEOGRAPH
+ "E6 50 89AC", #CJK UNIFIED IDEOGRAPH
+ "E6 51 89AF", #CJK UNIFIED IDEOGRAPH
+ "E6 52 89B2", #CJK UNIFIED IDEOGRAPH
+ "E6 53 89BA", #CJK UNIFIED IDEOGRAPH
+ "E6 54 89BD", #CJK UNIFIED IDEOGRAPH
+ "E6 55 89BF", #CJK UNIFIED IDEOGRAPH
+ "E6 56 89C0", #CJK UNIFIED IDEOGRAPH
+ "E6 57 89DA", #CJK UNIFIED IDEOGRAPH
+ "E6 58 89DC", #CJK UNIFIED IDEOGRAPH
+ "E6 59 89DD", #CJK UNIFIED IDEOGRAPH
+ "E6 5A 89E7", #CJK UNIFIED IDEOGRAPH
+ "E6 5B 89F4", #CJK UNIFIED IDEOGRAPH
+ "E6 5C 89F8", #CJK UNIFIED IDEOGRAPH
+ "E6 5D 8A03", #CJK UNIFIED IDEOGRAPH
+ "E6 5E 8A16", #CJK UNIFIED IDEOGRAPH
+ "E6 5F 8A10", #CJK UNIFIED IDEOGRAPH
+ "E6 60 8A0C", #CJK UNIFIED IDEOGRAPH
+ "E6 61 8A1B", #CJK UNIFIED IDEOGRAPH
+ "E6 62 8A1D", #CJK UNIFIED IDEOGRAPH
+ "E6 63 8A25", #CJK UNIFIED IDEOGRAPH
+ "E6 64 8A36", #CJK UNIFIED IDEOGRAPH
+ "E6 65 8A41", #CJK UNIFIED IDEOGRAPH
+ "E6 66 8A5B", #CJK UNIFIED IDEOGRAPH
+ "E6 67 8A52", #CJK UNIFIED IDEOGRAPH
+ "E6 68 8A46", #CJK UNIFIED IDEOGRAPH
+ "E6 69 8A48", #CJK UNIFIED IDEOGRAPH
+ "E6 6A 8A7C", #CJK UNIFIED IDEOGRAPH
+ "E6 6B 8A6D", #CJK UNIFIED IDEOGRAPH
+ "E6 6C 8A6C", #CJK UNIFIED IDEOGRAPH
+ "E6 6D 8A62", #CJK UNIFIED IDEOGRAPH
+ "E6 6E 8A85", #CJK UNIFIED IDEOGRAPH
+ "E6 6F 8A82", #CJK UNIFIED IDEOGRAPH
+ "E6 70 8A84", #CJK UNIFIED IDEOGRAPH
+ "E6 71 8AA8", #CJK UNIFIED IDEOGRAPH
+ "E6 72 8AA1", #CJK UNIFIED IDEOGRAPH
+ "E6 73 8A91", #CJK UNIFIED IDEOGRAPH
+ "E6 74 8AA5", #CJK UNIFIED IDEOGRAPH
+ "E6 75 8AA6", #CJK UNIFIED IDEOGRAPH
+ "E6 76 8A9A", #CJK UNIFIED IDEOGRAPH
+ "E6 77 8AA3", #CJK UNIFIED IDEOGRAPH
+ "E6 78 8AC4", #CJK UNIFIED IDEOGRAPH
+ "E6 79 8ACD", #CJK UNIFIED IDEOGRAPH
+ "E6 7A 8AC2", #CJK UNIFIED IDEOGRAPH
+ "E6 7B 8ADA", #CJK UNIFIED IDEOGRAPH
+ "E6 7C 8AEB", #CJK UNIFIED IDEOGRAPH
+ "E6 7D 8AF3", #CJK UNIFIED IDEOGRAPH
+ "E6 7E 8AE7", #CJK UNIFIED IDEOGRAPH
+ "E6 80 8AE4", #CJK UNIFIED IDEOGRAPH
+ "E6 81 8AF1", #CJK UNIFIED IDEOGRAPH
+ "E6 82 8B14", #CJK UNIFIED IDEOGRAPH
+ "E6 83 8AE0", #CJK UNIFIED IDEOGRAPH
+ "E6 84 8AE2", #CJK UNIFIED IDEOGRAPH
+ "E6 85 8AF7", #CJK UNIFIED IDEOGRAPH
+ "E6 86 8ADE", #CJK UNIFIED IDEOGRAPH
+ "E6 87 8ADB", #CJK UNIFIED IDEOGRAPH
+ "E6 88 8B0C", #CJK UNIFIED IDEOGRAPH
+ "E6 89 8B07", #CJK UNIFIED IDEOGRAPH
+ "E6 8A 8B1A", #CJK UNIFIED IDEOGRAPH
+ "E6 8B 8AE1", #CJK UNIFIED IDEOGRAPH
+ "E6 8C 8B16", #CJK UNIFIED IDEOGRAPH
+ "E6 8D 8B10", #CJK UNIFIED IDEOGRAPH
+ "E6 8E 8B17", #CJK UNIFIED IDEOGRAPH
+ "E6 8F 8B20", #CJK UNIFIED IDEOGRAPH
+ "E6 90 8B33", #CJK UNIFIED IDEOGRAPH
+ "E6 91 97AB", #CJK UNIFIED IDEOGRAPH
+ "E6 92 8B26", #CJK UNIFIED IDEOGRAPH
+ "E6 93 8B2B", #CJK UNIFIED IDEOGRAPH
+ "E6 94 8B3E", #CJK UNIFIED IDEOGRAPH
+ "E6 95 8B28", #CJK UNIFIED IDEOGRAPH
+ "E6 96 8B41", #CJK UNIFIED IDEOGRAPH
+ "E6 97 8B4C", #CJK UNIFIED IDEOGRAPH
+ "E6 98 8B4F", #CJK UNIFIED IDEOGRAPH
+ "E6 99 8B4E", #CJK UNIFIED IDEOGRAPH
+ "E6 9A 8B49", #CJK UNIFIED IDEOGRAPH
+ "E6 9B 8B56", #CJK UNIFIED IDEOGRAPH
+ "E6 9C 8B5B", #CJK UNIFIED IDEOGRAPH
+ "E6 9D 8B5A", #CJK UNIFIED IDEOGRAPH
+ "E6 9E 8B6B", #CJK UNIFIED IDEOGRAPH
+ "E6 9F 8B5F", #CJK UNIFIED IDEOGRAPH
+ "E6 A0 8B6C", #CJK UNIFIED IDEOGRAPH
+ "E6 A1 8B6F", #CJK UNIFIED IDEOGRAPH
+ "E6 A2 8B74", #CJK UNIFIED IDEOGRAPH
+ "E6 A3 8B7D", #CJK UNIFIED IDEOGRAPH
+ "E6 A4 8B80", #CJK UNIFIED IDEOGRAPH
+ "E6 A5 8B8C", #CJK UNIFIED IDEOGRAPH
+ "E6 A6 8B8E", #CJK UNIFIED IDEOGRAPH
+ "E6 A7 8B92", #CJK UNIFIED IDEOGRAPH
+ "E6 A8 8B93", #CJK UNIFIED IDEOGRAPH
+ "E6 A9 8B96", #CJK UNIFIED IDEOGRAPH
+ "E6 AA 8B99", #CJK UNIFIED IDEOGRAPH
+ "E6 AB 8B9A", #CJK UNIFIED IDEOGRAPH
+ "E6 AC 8C3A", #CJK UNIFIED IDEOGRAPH
+ "E6 AD 8C41", #CJK UNIFIED IDEOGRAPH
+ "E6 AE 8C3F", #CJK UNIFIED IDEOGRAPH
+ "E6 AF 8C48", #CJK UNIFIED IDEOGRAPH
+ "E6 B0 8C4C", #CJK UNIFIED IDEOGRAPH
+ "E6 B1 8C4E", #CJK UNIFIED IDEOGRAPH
+ "E6 B2 8C50", #CJK UNIFIED IDEOGRAPH
+ "E6 B3 8C55", #CJK UNIFIED IDEOGRAPH
+ "E6 B4 8C62", #CJK UNIFIED IDEOGRAPH
+ "E6 B5 8C6C", #CJK UNIFIED IDEOGRAPH
+ "E6 B6 8C78", #CJK UNIFIED IDEOGRAPH
+ "E6 B7 8C7A", #CJK UNIFIED IDEOGRAPH
+ "E6 B8 8C82", #CJK UNIFIED IDEOGRAPH
+ "E6 B9 8C89", #CJK UNIFIED IDEOGRAPH
+ "E6 BA 8C85", #CJK UNIFIED IDEOGRAPH
+ "E6 BB 8C8A", #CJK UNIFIED IDEOGRAPH
+ "E6 BC 8C8D", #CJK UNIFIED IDEOGRAPH
+ "E6 BD 8C8E", #CJK UNIFIED IDEOGRAPH
+ "E6 BE 8C94", #CJK UNIFIED IDEOGRAPH
+ "E6 BF 8C7C", #CJK UNIFIED IDEOGRAPH
+ "E6 C0 8C98", #CJK UNIFIED IDEOGRAPH
+ "E6 C1 621D", #CJK UNIFIED IDEOGRAPH
+ "E6 C2 8CAD", #CJK UNIFIED IDEOGRAPH
+ "E6 C3 8CAA", #CJK UNIFIED IDEOGRAPH
+ "E6 C4 8CBD", #CJK UNIFIED IDEOGRAPH
+ "E6 C5 8CB2", #CJK UNIFIED IDEOGRAPH
+ "E6 C6 8CB3", #CJK UNIFIED IDEOGRAPH
+ "E6 C7 8CAE", #CJK UNIFIED IDEOGRAPH
+ "E6 C8 8CB6", #CJK UNIFIED IDEOGRAPH
+ "E6 C9 8CC8", #CJK UNIFIED IDEOGRAPH
+ "E6 CA 8CC1", #CJK UNIFIED IDEOGRAPH
+ "E6 CB 8CE4", #CJK UNIFIED IDEOGRAPH
+ "E6 CC 8CE3", #CJK UNIFIED IDEOGRAPH
+ "E6 CD 8CDA", #CJK UNIFIED IDEOGRAPH
+ "E6 CE 8CFD", #CJK UNIFIED IDEOGRAPH
+ "E6 CF 8CFA", #CJK UNIFIED IDEOGRAPH
+ "E6 D0 8CFB", #CJK UNIFIED IDEOGRAPH
+ "E6 D1 8D04", #CJK UNIFIED IDEOGRAPH
+ "E6 D2 8D05", #CJK UNIFIED IDEOGRAPH
+ "E6 D3 8D0A", #CJK UNIFIED IDEOGRAPH
+ "E6 D4 8D07", #CJK UNIFIED IDEOGRAPH
+ "E6 D5 8D0F", #CJK UNIFIED IDEOGRAPH
+ "E6 D6 8D0D", #CJK UNIFIED IDEOGRAPH
+ "E6 D7 8D10", #CJK UNIFIED IDEOGRAPH
+ "E6 D8 9F4E", #CJK UNIFIED IDEOGRAPH
+ "E6 D9 8D13", #CJK UNIFIED IDEOGRAPH
+ "E6 DA 8CCD", #CJK UNIFIED IDEOGRAPH
+ "E6 DB 8D14", #CJK UNIFIED IDEOGRAPH
+ "E6 DC 8D16", #CJK UNIFIED IDEOGRAPH
+ "E6 DD 8D67", #CJK UNIFIED IDEOGRAPH
+ "E6 DE 8D6D", #CJK UNIFIED IDEOGRAPH
+ "E6 DF 8D71", #CJK UNIFIED IDEOGRAPH
+ "E6 E0 8D73", #CJK UNIFIED IDEOGRAPH
+ "E6 E1 8D81", #CJK UNIFIED IDEOGRAPH
+ "E6 E2 8D99", #CJK UNIFIED IDEOGRAPH
+ "E6 E3 8DC2", #CJK UNIFIED IDEOGRAPH
+ "E6 E4 8DBE", #CJK UNIFIED IDEOGRAPH
+ "E6 E5 8DBA", #CJK UNIFIED IDEOGRAPH
+ "E6 E6 8DCF", #CJK UNIFIED IDEOGRAPH
+ "E6 E7 8DDA", #CJK UNIFIED IDEOGRAPH
+ "E6 E8 8DD6", #CJK UNIFIED IDEOGRAPH
+ "E6 E9 8DCC", #CJK UNIFIED IDEOGRAPH
+ "E6 EA 8DDB", #CJK UNIFIED IDEOGRAPH
+ "E6 EB 8DCB", #CJK UNIFIED IDEOGRAPH
+ "E6 EC 8DEA", #CJK UNIFIED IDEOGRAPH
+ "E6 ED 8DEB", #CJK UNIFIED IDEOGRAPH
+ "E6 EE 8DDF", #CJK UNIFIED IDEOGRAPH
+ "E6 EF 8DE3", #CJK UNIFIED IDEOGRAPH
+ "E6 F0 8DFC", #CJK UNIFIED IDEOGRAPH
+ "E6 F1 8E08", #CJK UNIFIED IDEOGRAPH
+ "E6 F2 8E09", #CJK UNIFIED IDEOGRAPH
+ "E6 F3 8DFF", #CJK UNIFIED IDEOGRAPH
+ "E6 F4 8E1D", #CJK UNIFIED IDEOGRAPH
+ "E6 F5 8E1E", #CJK UNIFIED IDEOGRAPH
+ "E6 F6 8E10", #CJK UNIFIED IDEOGRAPH
+ "E6 F7 8E1F", #CJK UNIFIED IDEOGRAPH
+ "E6 F8 8E42", #CJK UNIFIED IDEOGRAPH
+ "E6 F9 8E35", #CJK UNIFIED IDEOGRAPH
+ "E6 FA 8E30", #CJK UNIFIED IDEOGRAPH
+ "E6 FB 8E34", #CJK UNIFIED IDEOGRAPH
+ "E6 FC 8E4A", #CJK UNIFIED IDEOGRAPH
+ "E7 40 8E47", #CJK UNIFIED IDEOGRAPH
+ "E7 41 8E49", #CJK UNIFIED IDEOGRAPH
+ "E7 42 8E4C", #CJK UNIFIED IDEOGRAPH
+ "E7 43 8E50", #CJK UNIFIED IDEOGRAPH
+ "E7 44 8E48", #CJK UNIFIED IDEOGRAPH
+ "E7 45 8E59", #CJK UNIFIED IDEOGRAPH
+ "E7 46 8E64", #CJK UNIFIED IDEOGRAPH
+ "E7 47 8E60", #CJK UNIFIED IDEOGRAPH
+ "E7 48 8E2A", #CJK UNIFIED IDEOGRAPH
+ "E7 49 8E63", #CJK UNIFIED IDEOGRAPH
+ "E7 4A 8E55", #CJK UNIFIED IDEOGRAPH
+ "E7 4B 8E76", #CJK UNIFIED IDEOGRAPH
+ "E7 4C 8E72", #CJK UNIFIED IDEOGRAPH
+ "E7 4D 8E7C", #CJK UNIFIED IDEOGRAPH
+ "E7 4E 8E81", #CJK UNIFIED IDEOGRAPH
+ "E7 4F 8E87", #CJK UNIFIED IDEOGRAPH
+ "E7 50 8E85", #CJK UNIFIED IDEOGRAPH
+ "E7 51 8E84", #CJK UNIFIED IDEOGRAPH
+ "E7 52 8E8B", #CJK UNIFIED IDEOGRAPH
+ "E7 53 8E8A", #CJK UNIFIED IDEOGRAPH
+ "E7 54 8E93", #CJK UNIFIED IDEOGRAPH
+ "E7 55 8E91", #CJK UNIFIED IDEOGRAPH
+ "E7 56 8E94", #CJK UNIFIED IDEOGRAPH
+ "E7 57 8E99", #CJK UNIFIED IDEOGRAPH
+ "E7 58 8EAA", #CJK UNIFIED IDEOGRAPH
+ "E7 59 8EA1", #CJK UNIFIED IDEOGRAPH
+ "E7 5A 8EAC", #CJK UNIFIED IDEOGRAPH
+ "E7 5B 8EB0", #CJK UNIFIED IDEOGRAPH
+ "E7 5C 8EC6", #CJK UNIFIED IDEOGRAPH
+ "E7 5D 8EB1", #CJK UNIFIED IDEOGRAPH
+ "E7 5E 8EBE", #CJK UNIFIED IDEOGRAPH
+ "E7 5F 8EC5", #CJK UNIFIED IDEOGRAPH
+ "E7 60 8EC8", #CJK UNIFIED IDEOGRAPH
+ "E7 61 8ECB", #CJK UNIFIED IDEOGRAPH
+ "E7 62 8EDB", #CJK UNIFIED IDEOGRAPH
+ "E7 63 8EE3", #CJK UNIFIED IDEOGRAPH
+ "E7 64 8EFC", #CJK UNIFIED IDEOGRAPH
+ "E7 65 8EFB", #CJK UNIFIED IDEOGRAPH
+ "E7 66 8EEB", #CJK UNIFIED IDEOGRAPH
+ "E7 67 8EFE", #CJK UNIFIED IDEOGRAPH
+ "E7 68 8F0A", #CJK UNIFIED IDEOGRAPH
+ "E7 69 8F05", #CJK UNIFIED IDEOGRAPH
+ "E7 6A 8F15", #CJK UNIFIED IDEOGRAPH
+ "E7 6B 8F12", #CJK UNIFIED IDEOGRAPH
+ "E7 6C 8F19", #CJK UNIFIED IDEOGRAPH
+ "E7 6D 8F13", #CJK UNIFIED IDEOGRAPH
+ "E7 6E 8F1C", #CJK UNIFIED IDEOGRAPH
+ "E7 6F 8F1F", #CJK UNIFIED IDEOGRAPH
+ "E7 70 8F1B", #CJK UNIFIED IDEOGRAPH
+ "E7 71 8F0C", #CJK UNIFIED IDEOGRAPH
+ "E7 72 8F26", #CJK UNIFIED IDEOGRAPH
+ "E7 73 8F33", #CJK UNIFIED IDEOGRAPH
+ "E7 74 8F3B", #CJK UNIFIED IDEOGRAPH
+ "E7 75 8F39", #CJK UNIFIED IDEOGRAPH
+ "E7 76 8F45", #CJK UNIFIED IDEOGRAPH
+ "E7 77 8F42", #CJK UNIFIED IDEOGRAPH
+ "E7 78 8F3E", #CJK UNIFIED IDEOGRAPH
+ "E7 79 8F4C", #CJK UNIFIED IDEOGRAPH
+ "E7 7A 8F49", #CJK UNIFIED IDEOGRAPH
+ "E7 7B 8F46", #CJK UNIFIED IDEOGRAPH
+ "E7 7C 8F4E", #CJK UNIFIED IDEOGRAPH
+ "E7 7D 8F57", #CJK UNIFIED IDEOGRAPH
+ "E7 7E 8F5C", #CJK UNIFIED IDEOGRAPH
+ "E7 80 8F62", #CJK UNIFIED IDEOGRAPH
+ "E7 81 8F63", #CJK UNIFIED IDEOGRAPH
+ "E7 82 8F64", #CJK UNIFIED IDEOGRAPH
+ "E7 83 8F9C", #CJK UNIFIED IDEOGRAPH
+ "E7 84 8F9F", #CJK UNIFIED IDEOGRAPH
+ "E7 85 8FA3", #CJK UNIFIED IDEOGRAPH
+ "E7 86 8FAD", #CJK UNIFIED IDEOGRAPH
+ "E7 87 8FAF", #CJK UNIFIED IDEOGRAPH
+ "E7 88 8FB7", #CJK UNIFIED IDEOGRAPH
+ "E7 89 8FDA", #CJK UNIFIED IDEOGRAPH
+ "E7 8A 8FE5", #CJK UNIFIED IDEOGRAPH
+ "E7 8B 8FE2", #CJK UNIFIED IDEOGRAPH
+ "E7 8C 8FEA", #CJK UNIFIED IDEOGRAPH
+ "E7 8D 8FEF", #CJK UNIFIED IDEOGRAPH
+ "E7 8E 9087", #CJK UNIFIED IDEOGRAPH
+ "E7 8F 8FF4", #CJK UNIFIED IDEOGRAPH
+ "E7 90 9005", #CJK UNIFIED IDEOGRAPH
+ "E7 91 8FF9", #CJK UNIFIED IDEOGRAPH
+ "E7 92 8FFA", #CJK UNIFIED IDEOGRAPH
+ "E7 93 9011", #CJK UNIFIED IDEOGRAPH
+ "E7 94 9015", #CJK UNIFIED IDEOGRAPH
+ "E7 95 9021", #CJK UNIFIED IDEOGRAPH
+ "E7 96 900D", #CJK UNIFIED IDEOGRAPH
+ "E7 97 901E", #CJK UNIFIED IDEOGRAPH
+ "E7 98 9016", #CJK UNIFIED IDEOGRAPH
+ "E7 99 900B", #CJK UNIFIED IDEOGRAPH
+ "E7 9A 9027", #CJK UNIFIED IDEOGRAPH
+ "E7 9B 9036", #CJK UNIFIED IDEOGRAPH
+ "E7 9C 9035", #CJK UNIFIED IDEOGRAPH
+ "E7 9D 9039", #CJK UNIFIED IDEOGRAPH
+ "E7 9E 8FF8", #CJK UNIFIED IDEOGRAPH
+ "E7 9F 904F", #CJK UNIFIED IDEOGRAPH
+ "E7 A0 9050", #CJK UNIFIED IDEOGRAPH
+ "E7 A1 9051", #CJK UNIFIED IDEOGRAPH
+ "E7 A2 9052", #CJK UNIFIED IDEOGRAPH
+ "E7 A3 900E", #CJK UNIFIED IDEOGRAPH
+ "E7 A4 9049", #CJK UNIFIED IDEOGRAPH
+ "E7 A5 903E", #CJK UNIFIED IDEOGRAPH
+ "E7 A6 9056", #CJK UNIFIED IDEOGRAPH
+ "E7 A7 9058", #CJK UNIFIED IDEOGRAPH
+ "E7 A8 905E", #CJK UNIFIED IDEOGRAPH
+ "E7 A9 9068", #CJK UNIFIED IDEOGRAPH
+ "E7 AA 906F", #CJK UNIFIED IDEOGRAPH
+ "E7 AB 9076", #CJK UNIFIED IDEOGRAPH
+ "E7 AC 96A8", #CJK UNIFIED IDEOGRAPH
+ "E7 AD 9072", #CJK UNIFIED IDEOGRAPH
+ "E7 AE 9082", #CJK UNIFIED IDEOGRAPH
+ "E7 AF 907D", #CJK UNIFIED IDEOGRAPH
+ "E7 B0 9081", #CJK UNIFIED IDEOGRAPH
+ "E7 B1 9080", #CJK UNIFIED IDEOGRAPH
+ "E7 B2 908A", #CJK UNIFIED IDEOGRAPH
+ "E7 B3 9089", #CJK UNIFIED IDEOGRAPH
+ "E7 B4 908F", #CJK UNIFIED IDEOGRAPH
+ "E7 B5 90A8", #CJK UNIFIED IDEOGRAPH
+ "E7 B6 90AF", #CJK UNIFIED IDEOGRAPH
+ "E7 B7 90B1", #CJK UNIFIED IDEOGRAPH
+ "E7 B8 90B5", #CJK UNIFIED IDEOGRAPH
+ "E7 B9 90E2", #CJK UNIFIED IDEOGRAPH
+ "E7 BA 90E4", #CJK UNIFIED IDEOGRAPH
+ "E7 BB 6248", #CJK UNIFIED IDEOGRAPH
+ "E7 BC 90DB", #CJK UNIFIED IDEOGRAPH
+ "E7 BD 9102", #CJK UNIFIED IDEOGRAPH
+ "E7 BE 9112", #CJK UNIFIED IDEOGRAPH
+ "E7 BF 9119", #CJK UNIFIED IDEOGRAPH
+ "E7 C0 9132", #CJK UNIFIED IDEOGRAPH
+ "E7 C1 9130", #CJK UNIFIED IDEOGRAPH
+ "E7 C2 914A", #CJK UNIFIED IDEOGRAPH
+ "E7 C3 9156", #CJK UNIFIED IDEOGRAPH
+ "E7 C4 9158", #CJK UNIFIED IDEOGRAPH
+ "E7 C5 9163", #CJK UNIFIED IDEOGRAPH
+ "E7 C6 9165", #CJK UNIFIED IDEOGRAPH
+ "E7 C7 9169", #CJK UNIFIED IDEOGRAPH
+ "E7 C8 9173", #CJK UNIFIED IDEOGRAPH
+ "E7 C9 9172", #CJK UNIFIED IDEOGRAPH
+ "E7 CA 918B", #CJK UNIFIED IDEOGRAPH
+ "E7 CB 9189", #CJK UNIFIED IDEOGRAPH
+ "E7 CC 9182", #CJK UNIFIED IDEOGRAPH
+ "E7 CD 91A2", #CJK UNIFIED IDEOGRAPH
+ "E7 CE 91AB", #CJK UNIFIED IDEOGRAPH
+ "E7 CF 91AF", #CJK UNIFIED IDEOGRAPH
+ "E7 D0 91AA", #CJK UNIFIED IDEOGRAPH
+ "E7 D1 91B5", #CJK UNIFIED IDEOGRAPH
+ "E7 D2 91B4", #CJK UNIFIED IDEOGRAPH
+ "E7 D3 91BA", #CJK UNIFIED IDEOGRAPH
+ "E7 D4 91C0", #CJK UNIFIED IDEOGRAPH
+ "E7 D5 91C1", #CJK UNIFIED IDEOGRAPH
+ "E7 D6 91C9", #CJK UNIFIED IDEOGRAPH
+ "E7 D7 91CB", #CJK UNIFIED IDEOGRAPH
+ "E7 D8 91D0", #CJK UNIFIED IDEOGRAPH
+ "E7 D9 91D6", #CJK UNIFIED IDEOGRAPH
+ "E7 DA 91DF", #CJK UNIFIED IDEOGRAPH
+ "E7 DB 91E1", #CJK UNIFIED IDEOGRAPH
+ "E7 DC 91DB", #CJK UNIFIED IDEOGRAPH
+ "E7 DD 91FC", #CJK UNIFIED IDEOGRAPH
+ "E7 DE 91F5", #CJK UNIFIED IDEOGRAPH
+ "E7 DF 91F6", #CJK UNIFIED IDEOGRAPH
+ "E7 E0 921E", #CJK UNIFIED IDEOGRAPH
+ "E7 E1 91FF", #CJK UNIFIED IDEOGRAPH
+ "E7 E2 9214", #CJK UNIFIED IDEOGRAPH
+ "E7 E3 922C", #CJK UNIFIED IDEOGRAPH
+ "E7 E4 9215", #CJK UNIFIED IDEOGRAPH
+ "E7 E5 9211", #CJK UNIFIED IDEOGRAPH
+ "E7 E6 925E", #CJK UNIFIED IDEOGRAPH
+ "E7 E7 9257", #CJK UNIFIED IDEOGRAPH
+ "E7 E8 9245", #CJK UNIFIED IDEOGRAPH
+ "E7 E9 9249", #CJK UNIFIED IDEOGRAPH
+ "E7 EA 9264", #CJK UNIFIED IDEOGRAPH
+ "E7 EB 9248", #CJK UNIFIED IDEOGRAPH
+ "E7 EC 9295", #CJK UNIFIED IDEOGRAPH
+ "E7 ED 923F", #CJK UNIFIED IDEOGRAPH
+ "E7 EE 924B", #CJK UNIFIED IDEOGRAPH
+ "E7 EF 9250", #CJK UNIFIED IDEOGRAPH
+ "E7 F0 929C", #CJK UNIFIED IDEOGRAPH
+ "E7 F1 9296", #CJK UNIFIED IDEOGRAPH
+ "E7 F2 9293", #CJK UNIFIED IDEOGRAPH
+ "E7 F3 929B", #CJK UNIFIED IDEOGRAPH
+ "E7 F4 925A", #CJK UNIFIED IDEOGRAPH
+ "E7 F5 92CF", #CJK UNIFIED IDEOGRAPH
+ "E7 F6 92B9", #CJK UNIFIED IDEOGRAPH
+ "E7 F7 92B7", #CJK UNIFIED IDEOGRAPH
+ "E7 F8 92E9", #CJK UNIFIED IDEOGRAPH
+ "E7 F9 930F", #CJK UNIFIED IDEOGRAPH
+ "E7 FA 92FA", #CJK UNIFIED IDEOGRAPH
+ "E7 FB 9344", #CJK UNIFIED IDEOGRAPH
+ "E7 FC 932E", #CJK UNIFIED IDEOGRAPH
+ "E8 40 9319", #CJK UNIFIED IDEOGRAPH
+ "E8 41 9322", #CJK UNIFIED IDEOGRAPH
+ "E8 42 931A", #CJK UNIFIED IDEOGRAPH
+ "E8 43 9323", #CJK UNIFIED IDEOGRAPH
+ "E8 44 933A", #CJK UNIFIED IDEOGRAPH
+ "E8 45 9335", #CJK UNIFIED IDEOGRAPH
+ "E8 46 933B", #CJK UNIFIED IDEOGRAPH
+ "E8 47 935C", #CJK UNIFIED IDEOGRAPH
+ "E8 48 9360", #CJK UNIFIED IDEOGRAPH
+ "E8 49 937C", #CJK UNIFIED IDEOGRAPH
+ "E8 4A 936E", #CJK UNIFIED IDEOGRAPH
+ "E8 4B 9356", #CJK UNIFIED IDEOGRAPH
+ "E8 4C 93B0", #CJK UNIFIED IDEOGRAPH
+ "E8 4D 93AC", #CJK UNIFIED IDEOGRAPH
+ "E8 4E 93AD", #CJK UNIFIED IDEOGRAPH
+ "E8 4F 9394", #CJK UNIFIED IDEOGRAPH
+ "E8 50 93B9", #CJK UNIFIED IDEOGRAPH
+ "E8 51 93D6", #CJK UNIFIED IDEOGRAPH
+ "E8 52 93D7", #CJK UNIFIED IDEOGRAPH
+ "E8 53 93E8", #CJK UNIFIED IDEOGRAPH
+ "E8 54 93E5", #CJK UNIFIED IDEOGRAPH
+ "E8 55 93D8", #CJK UNIFIED IDEOGRAPH
+ "E8 56 93C3", #CJK UNIFIED IDEOGRAPH
+ "E8 57 93DD", #CJK UNIFIED IDEOGRAPH
+ "E8 58 93D0", #CJK UNIFIED IDEOGRAPH
+ "E8 59 93C8", #CJK UNIFIED IDEOGRAPH
+ "E8 5A 93E4", #CJK UNIFIED IDEOGRAPH
+ "E8 5B 941A", #CJK UNIFIED IDEOGRAPH
+ "E8 5C 9414", #CJK UNIFIED IDEOGRAPH
+ "E8 5D 9413", #CJK UNIFIED IDEOGRAPH
+ "E8 5E 9403", #CJK UNIFIED IDEOGRAPH
+ "E8 5F 9407", #CJK UNIFIED IDEOGRAPH
+ "E8 60 9410", #CJK UNIFIED IDEOGRAPH
+ "E8 61 9436", #CJK UNIFIED IDEOGRAPH
+ "E8 62 942B", #CJK UNIFIED IDEOGRAPH
+ "E8 63 9435", #CJK UNIFIED IDEOGRAPH
+ "E8 64 9421", #CJK UNIFIED IDEOGRAPH
+ "E8 65 943A", #CJK UNIFIED IDEOGRAPH
+ "E8 66 9441", #CJK UNIFIED IDEOGRAPH
+ "E8 67 9452", #CJK UNIFIED IDEOGRAPH
+ "E8 68 9444", #CJK UNIFIED IDEOGRAPH
+ "E8 69 945B", #CJK UNIFIED IDEOGRAPH
+ "E8 6A 9460", #CJK UNIFIED IDEOGRAPH
+ "E8 6B 9462", #CJK UNIFIED IDEOGRAPH
+ "E8 6C 945E", #CJK UNIFIED IDEOGRAPH
+ "E8 6D 946A", #CJK UNIFIED IDEOGRAPH
+ "E8 6E 9229", #CJK UNIFIED IDEOGRAPH
+ "E8 6F 9470", #CJK UNIFIED IDEOGRAPH
+ "E8 70 9475", #CJK UNIFIED IDEOGRAPH
+ "E8 71 9477", #CJK UNIFIED IDEOGRAPH
+ "E8 72 947D", #CJK UNIFIED IDEOGRAPH
+ "E8 73 945A", #CJK UNIFIED IDEOGRAPH
+ "E8 74 947C", #CJK UNIFIED IDEOGRAPH
+ "E8 75 947E", #CJK UNIFIED IDEOGRAPH
+ "E8 76 9481", #CJK UNIFIED IDEOGRAPH
+ "E8 77 947F", #CJK UNIFIED IDEOGRAPH
+ "E8 78 9582", #CJK UNIFIED IDEOGRAPH
+ "E8 79 9587", #CJK UNIFIED IDEOGRAPH
+ "E8 7A 958A", #CJK UNIFIED IDEOGRAPH
+ "E8 7B 9594", #CJK UNIFIED IDEOGRAPH
+ "E8 7C 9596", #CJK UNIFIED IDEOGRAPH
+ "E8 7D 9598", #CJK UNIFIED IDEOGRAPH
+ "E8 7E 9599", #CJK UNIFIED IDEOGRAPH
+ "E8 80 95A0", #CJK UNIFIED IDEOGRAPH
+ "E8 81 95A8", #CJK UNIFIED IDEOGRAPH
+ "E8 82 95A7", #CJK UNIFIED IDEOGRAPH
+ "E8 83 95AD", #CJK UNIFIED IDEOGRAPH
+ "E8 84 95BC", #CJK UNIFIED IDEOGRAPH
+ "E8 85 95BB", #CJK UNIFIED IDEOGRAPH
+ "E8 86 95B9", #CJK UNIFIED IDEOGRAPH
+ "E8 87 95BE", #CJK UNIFIED IDEOGRAPH
+ "E8 88 95CA", #CJK UNIFIED IDEOGRAPH
+ "E8 89 6FF6", #CJK UNIFIED IDEOGRAPH
+ "E8 8A 95C3", #CJK UNIFIED IDEOGRAPH
+ "E8 8B 95CD", #CJK UNIFIED IDEOGRAPH
+ "E8 8C 95CC", #CJK UNIFIED IDEOGRAPH
+ "E8 8D 95D5", #CJK UNIFIED IDEOGRAPH
+ "E8 8E 95D4", #CJK UNIFIED IDEOGRAPH
+ "E8 8F 95D6", #CJK UNIFIED IDEOGRAPH
+ "E8 90 95DC", #CJK UNIFIED IDEOGRAPH
+ "E8 91 95E1", #CJK UNIFIED IDEOGRAPH
+ "E8 92 95E5", #CJK UNIFIED IDEOGRAPH
+ "E8 93 95E2", #CJK UNIFIED IDEOGRAPH
+ "E8 94 9621", #CJK UNIFIED IDEOGRAPH
+ "E8 95 9628", #CJK UNIFIED IDEOGRAPH
+ "E8 96 962E", #CJK UNIFIED IDEOGRAPH
+ "E8 97 962F", #CJK UNIFIED IDEOGRAPH
+ "E8 98 9642", #CJK UNIFIED IDEOGRAPH
+ "E8 99 964C", #CJK UNIFIED IDEOGRAPH
+ "E8 9A 964F", #CJK UNIFIED IDEOGRAPH
+ "E8 9B 964B", #CJK UNIFIED IDEOGRAPH
+ "E8 9C 9677", #CJK UNIFIED IDEOGRAPH
+ "E8 9D 965C", #CJK UNIFIED IDEOGRAPH
+ "E8 9E 965E", #CJK UNIFIED IDEOGRAPH
+ "E8 9F 965D", #CJK UNIFIED IDEOGRAPH
+ "E8 A0 965F", #CJK UNIFIED IDEOGRAPH
+ "E8 A1 9666", #CJK UNIFIED IDEOGRAPH
+ "E8 A2 9672", #CJK UNIFIED IDEOGRAPH
+ "E8 A3 966C", #CJK UNIFIED IDEOGRAPH
+ "E8 A4 968D", #CJK UNIFIED IDEOGRAPH
+ "E8 A5 9698", #CJK UNIFIED IDEOGRAPH
+ "E8 A6 9695", #CJK UNIFIED IDEOGRAPH
+ "E8 A7 9697", #CJK UNIFIED IDEOGRAPH
+ "E8 A8 96AA", #CJK UNIFIED IDEOGRAPH
+ "E8 A9 96A7", #CJK UNIFIED IDEOGRAPH
+ "E8 AA 96B1", #CJK UNIFIED IDEOGRAPH
+ "E8 AB 96B2", #CJK UNIFIED IDEOGRAPH
+ "E8 AC 96B0", #CJK UNIFIED IDEOGRAPH
+ "E8 AD 96B4", #CJK UNIFIED IDEOGRAPH
+ "E8 AE 96B6", #CJK UNIFIED IDEOGRAPH
+ "E8 AF 96B8", #CJK UNIFIED IDEOGRAPH
+ "E8 B0 96B9", #CJK UNIFIED IDEOGRAPH
+ "E8 B1 96CE", #CJK UNIFIED IDEOGRAPH
+ "E8 B2 96CB", #CJK UNIFIED IDEOGRAPH
+ "E8 B3 96C9", #CJK UNIFIED IDEOGRAPH
+ "E8 B4 96CD", #CJK UNIFIED IDEOGRAPH
+ "E8 B5 894D", #CJK UNIFIED IDEOGRAPH
+ "E8 B6 96DC", #CJK UNIFIED IDEOGRAPH
+ "E8 B7 970D", #CJK UNIFIED IDEOGRAPH
+ "E8 B8 96D5", #CJK UNIFIED IDEOGRAPH
+ "E8 B9 96F9", #CJK UNIFIED IDEOGRAPH
+ "E8 BA 9704", #CJK UNIFIED IDEOGRAPH
+ "E8 BB 9706", #CJK UNIFIED IDEOGRAPH
+ "E8 BC 9708", #CJK UNIFIED IDEOGRAPH
+ "E8 BD 9713", #CJK UNIFIED IDEOGRAPH
+ "E8 BE 970E", #CJK UNIFIED IDEOGRAPH
+ "E8 BF 9711", #CJK UNIFIED IDEOGRAPH
+ "E8 C0 970F", #CJK UNIFIED IDEOGRAPH
+ "E8 C1 9716", #CJK UNIFIED IDEOGRAPH
+ "E8 C2 9719", #CJK UNIFIED IDEOGRAPH
+ "E8 C3 9724", #CJK UNIFIED IDEOGRAPH
+ "E8 C4 972A", #CJK UNIFIED IDEOGRAPH
+ "E8 C5 9730", #CJK UNIFIED IDEOGRAPH
+ "E8 C6 9739", #CJK UNIFIED IDEOGRAPH
+ "E8 C7 973D", #CJK UNIFIED IDEOGRAPH
+ "E8 C8 973E", #CJK UNIFIED IDEOGRAPH
+ "E8 C9 9744", #CJK UNIFIED IDEOGRAPH
+ "E8 CA 9746", #CJK UNIFIED IDEOGRAPH
+ "E8 CB 9748", #CJK UNIFIED IDEOGRAPH
+ "E8 CC 9742", #CJK UNIFIED IDEOGRAPH
+ "E8 CD 9749", #CJK UNIFIED IDEOGRAPH
+ "E8 CE 975C", #CJK UNIFIED IDEOGRAPH
+ "E8 CF 9760", #CJK UNIFIED IDEOGRAPH
+ "E8 D0 9764", #CJK UNIFIED IDEOGRAPH
+ "E8 D1 9766", #CJK UNIFIED IDEOGRAPH
+ "E8 D2 9768", #CJK UNIFIED IDEOGRAPH
+ "E8 D3 52D2", #CJK UNIFIED IDEOGRAPH
+ "E8 D4 976B", #CJK UNIFIED IDEOGRAPH
+ "E8 D5 9771", #CJK UNIFIED IDEOGRAPH
+ "E8 D6 9779", #CJK UNIFIED IDEOGRAPH
+ "E8 D7 9785", #CJK UNIFIED IDEOGRAPH
+ "E8 D8 977C", #CJK UNIFIED IDEOGRAPH
+ "E8 D9 9781", #CJK UNIFIED IDEOGRAPH
+ "E8 DA 977A", #CJK UNIFIED IDEOGRAPH
+ "E8 DB 9786", #CJK UNIFIED IDEOGRAPH
+ "E8 DC 978B", #CJK UNIFIED IDEOGRAPH
+ "E8 DD 978F", #CJK UNIFIED IDEOGRAPH
+ "E8 DE 9790", #CJK UNIFIED IDEOGRAPH
+ "E8 DF 979C", #CJK UNIFIED IDEOGRAPH
+ "E8 E0 97A8", #CJK UNIFIED IDEOGRAPH
+ "E8 E1 97A6", #CJK UNIFIED IDEOGRAPH
+ "E8 E2 97A3", #CJK UNIFIED IDEOGRAPH
+ "E8 E3 97B3", #CJK UNIFIED IDEOGRAPH
+ "E8 E4 97B4", #CJK UNIFIED IDEOGRAPH
+ "E8 E5 97C3", #CJK UNIFIED IDEOGRAPH
+ "E8 E6 97C6", #CJK UNIFIED IDEOGRAPH
+ "E8 E7 97C8", #CJK UNIFIED IDEOGRAPH
+ "E8 E8 97CB", #CJK UNIFIED IDEOGRAPH
+ "E8 E9 97DC", #CJK UNIFIED IDEOGRAPH
+ "E8 EA 97ED", #CJK UNIFIED IDEOGRAPH
+ "E8 EB 9F4F", #CJK UNIFIED IDEOGRAPH
+ "E8 EC 97F2", #CJK UNIFIED IDEOGRAPH
+ "E8 ED 7ADF", #CJK UNIFIED IDEOGRAPH
+ "E8 EE 97F6", #CJK UNIFIED IDEOGRAPH
+ "E8 EF 97F5", #CJK UNIFIED IDEOGRAPH
+ "E8 F0 980F", #CJK UNIFIED IDEOGRAPH
+ "E8 F1 980C", #CJK UNIFIED IDEOGRAPH
+ "E8 F2 9838", #CJK UNIFIED IDEOGRAPH
+ "E8 F3 9824", #CJK UNIFIED IDEOGRAPH
+ "E8 F4 9821", #CJK UNIFIED IDEOGRAPH
+ "E8 F5 9837", #CJK UNIFIED IDEOGRAPH
+ "E8 F6 983D", #CJK UNIFIED IDEOGRAPH
+ "E8 F7 9846", #CJK UNIFIED IDEOGRAPH
+ "E8 F8 984F", #CJK UNIFIED IDEOGRAPH
+ "E8 F9 984B", #CJK UNIFIED IDEOGRAPH
+ "E8 FA 986B", #CJK UNIFIED IDEOGRAPH
+ "E8 FB 986F", #CJK UNIFIED IDEOGRAPH
+ "E8 FC 9870", #CJK UNIFIED IDEOGRAPH
+ "E9 40 9871", #CJK UNIFIED IDEOGRAPH
+ "E9 41 9874", #CJK UNIFIED IDEOGRAPH
+ "E9 42 9873", #CJK UNIFIED IDEOGRAPH
+ "E9 43 98AA", #CJK UNIFIED IDEOGRAPH
+ "E9 44 98AF", #CJK UNIFIED IDEOGRAPH
+ "E9 45 98B1", #CJK UNIFIED IDEOGRAPH
+ "E9 46 98B6", #CJK UNIFIED IDEOGRAPH
+ "E9 47 98C4", #CJK UNIFIED IDEOGRAPH
+ "E9 48 98C3", #CJK UNIFIED IDEOGRAPH
+ "E9 49 98C6", #CJK UNIFIED IDEOGRAPH
+ "E9 4A 98E9", #CJK UNIFIED IDEOGRAPH
+ "E9 4B 98EB", #CJK UNIFIED IDEOGRAPH
+ "E9 4C 9903", #CJK UNIFIED IDEOGRAPH
+ "E9 4D 9909", #CJK UNIFIED IDEOGRAPH
+ "E9 4E 9912", #CJK UNIFIED IDEOGRAPH
+ "E9 4F 9914", #CJK UNIFIED IDEOGRAPH
+ "E9 50 9918", #CJK UNIFIED IDEOGRAPH
+ "E9 51 9921", #CJK UNIFIED IDEOGRAPH
+ "E9 52 991D", #CJK UNIFIED IDEOGRAPH
+ "E9 53 991E", #CJK UNIFIED IDEOGRAPH
+ "E9 54 9924", #CJK UNIFIED IDEOGRAPH
+ "E9 55 9920", #CJK UNIFIED IDEOGRAPH
+ "E9 56 992C", #CJK UNIFIED IDEOGRAPH
+ "E9 57 992E", #CJK UNIFIED IDEOGRAPH
+ "E9 58 993D", #CJK UNIFIED IDEOGRAPH
+ "E9 59 993E", #CJK UNIFIED IDEOGRAPH
+ "E9 5A 9942", #CJK UNIFIED IDEOGRAPH
+ "E9 5B 9949", #CJK UNIFIED IDEOGRAPH
+ "E9 5C 9945", #CJK UNIFIED IDEOGRAPH
+ "E9 5D 9950", #CJK UNIFIED IDEOGRAPH
+ "E9 5E 994B", #CJK UNIFIED IDEOGRAPH
+ "E9 5F 9951", #CJK UNIFIED IDEOGRAPH
+ "E9 60 9952", #CJK UNIFIED IDEOGRAPH
+ "E9 61 994C", #CJK UNIFIED IDEOGRAPH
+ "E9 62 9955", #CJK UNIFIED IDEOGRAPH
+ "E9 63 9997", #CJK UNIFIED IDEOGRAPH
+ "E9 64 9998", #CJK UNIFIED IDEOGRAPH
+ "E9 65 99A5", #CJK UNIFIED IDEOGRAPH
+ "E9 66 99AD", #CJK UNIFIED IDEOGRAPH
+ "E9 67 99AE", #CJK UNIFIED IDEOGRAPH
+ "E9 68 99BC", #CJK UNIFIED IDEOGRAPH
+ "E9 69 99DF", #CJK UNIFIED IDEOGRAPH
+ "E9 6A 99DB", #CJK UNIFIED IDEOGRAPH
+ "E9 6B 99DD", #CJK UNIFIED IDEOGRAPH
+ "E9 6C 99D8", #CJK UNIFIED IDEOGRAPH
+ "E9 6D 99D1", #CJK UNIFIED IDEOGRAPH
+ "E9 6E 99ED", #CJK UNIFIED IDEOGRAPH
+ "E9 6F 99EE", #CJK UNIFIED IDEOGRAPH
+ "E9 70 99F1", #CJK UNIFIED IDEOGRAPH
+ "E9 71 99F2", #CJK UNIFIED IDEOGRAPH
+ "E9 72 99FB", #CJK UNIFIED IDEOGRAPH
+ "E9 73 99F8", #CJK UNIFIED IDEOGRAPH
+ "E9 74 9A01", #CJK UNIFIED IDEOGRAPH
+ "E9 75 9A0F", #CJK UNIFIED IDEOGRAPH
+ "E9 76 9A05", #CJK UNIFIED IDEOGRAPH
+ "E9 77 99E2", #CJK UNIFIED IDEOGRAPH
+ "E9 78 9A19", #CJK UNIFIED IDEOGRAPH
+ "E9 79 9A2B", #CJK UNIFIED IDEOGRAPH
+ "E9 7A 9A37", #CJK UNIFIED IDEOGRAPH
+ "E9 7B 9A45", #CJK UNIFIED IDEOGRAPH
+ "E9 7C 9A42", #CJK UNIFIED IDEOGRAPH
+ "E9 7D 9A40", #CJK UNIFIED IDEOGRAPH
+ "E9 7E 9A43", #CJK UNIFIED IDEOGRAPH
+ "E9 80 9A3E", #CJK UNIFIED IDEOGRAPH
+ "E9 81 9A55", #CJK UNIFIED IDEOGRAPH
+ "E9 82 9A4D", #CJK UNIFIED IDEOGRAPH
+ "E9 83 9A5B", #CJK UNIFIED IDEOGRAPH
+ "E9 84 9A57", #CJK UNIFIED IDEOGRAPH
+ "E9 85 9A5F", #CJK UNIFIED IDEOGRAPH
+ "E9 86 9A62", #CJK UNIFIED IDEOGRAPH
+ "E9 87 9A65", #CJK UNIFIED IDEOGRAPH
+ "E9 88 9A64", #CJK UNIFIED IDEOGRAPH
+ "E9 89 9A69", #CJK UNIFIED IDEOGRAPH
+ "E9 8A 9A6B", #CJK UNIFIED IDEOGRAPH
+ "E9 8B 9A6A", #CJK UNIFIED IDEOGRAPH
+ "E9 8C 9AAD", #CJK UNIFIED IDEOGRAPH
+ "E9 8D 9AB0", #CJK UNIFIED IDEOGRAPH
+ "E9 8E 9ABC", #CJK UNIFIED IDEOGRAPH
+ "E9 8F 9AC0", #CJK UNIFIED IDEOGRAPH
+ "E9 90 9ACF", #CJK UNIFIED IDEOGRAPH
+ "E9 91 9AD1", #CJK UNIFIED IDEOGRAPH
+ "E9 92 9AD3", #CJK UNIFIED IDEOGRAPH
+ "E9 93 9AD4", #CJK UNIFIED IDEOGRAPH
+ "E9 94 9ADE", #CJK UNIFIED IDEOGRAPH
+ "E9 95 9ADF", #CJK UNIFIED IDEOGRAPH
+ "E9 96 9AE2", #CJK UNIFIED IDEOGRAPH
+ "E9 97 9AE3", #CJK UNIFIED IDEOGRAPH
+ "E9 98 9AE6", #CJK UNIFIED IDEOGRAPH
+ "E9 99 9AEF", #CJK UNIFIED IDEOGRAPH
+ "E9 9A 9AEB", #CJK UNIFIED IDEOGRAPH
+ "E9 9B 9AEE", #CJK UNIFIED IDEOGRAPH
+ "E9 9C 9AF4", #CJK UNIFIED IDEOGRAPH
+ "E9 9D 9AF1", #CJK UNIFIED IDEOGRAPH
+ "E9 9E 9AF7", #CJK UNIFIED IDEOGRAPH
+ "E9 9F 9AFB", #CJK UNIFIED IDEOGRAPH
+ "E9 A0 9B06", #CJK UNIFIED IDEOGRAPH
+ "E9 A1 9B18", #CJK UNIFIED IDEOGRAPH
+ "E9 A2 9B1A", #CJK UNIFIED IDEOGRAPH
+ "E9 A3 9B1F", #CJK UNIFIED IDEOGRAPH
+ "E9 A4 9B22", #CJK UNIFIED IDEOGRAPH
+ "E9 A5 9B23", #CJK UNIFIED IDEOGRAPH
+ "E9 A6 9B25", #CJK UNIFIED IDEOGRAPH
+ "E9 A7 9B27", #CJK UNIFIED IDEOGRAPH
+ "E9 A8 9B28", #CJK UNIFIED IDEOGRAPH
+ "E9 A9 9B29", #CJK UNIFIED IDEOGRAPH
+ "E9 AA 9B2A", #CJK UNIFIED IDEOGRAPH
+ "E9 AB 9B2E", #CJK UNIFIED IDEOGRAPH
+ "E9 AC 9B2F", #CJK UNIFIED IDEOGRAPH
+ "E9 AD 9B32", #CJK UNIFIED IDEOGRAPH
+ "E9 AE 9B44", #CJK UNIFIED IDEOGRAPH
+ "E9 AF 9B43", #CJK UNIFIED IDEOGRAPH
+ "E9 B0 9B4F", #CJK UNIFIED IDEOGRAPH
+ "E9 B1 9B4D", #CJK UNIFIED IDEOGRAPH
+ "E9 B2 9B4E", #CJK UNIFIED IDEOGRAPH
+ "E9 B3 9B51", #CJK UNIFIED IDEOGRAPH
+ "E9 B4 9B58", #CJK UNIFIED IDEOGRAPH
+ "E9 B5 9B74", #CJK UNIFIED IDEOGRAPH
+ "E9 B6 9B93", #CJK UNIFIED IDEOGRAPH
+ "E9 B7 9B83", #CJK UNIFIED IDEOGRAPH
+ "E9 B8 9B91", #CJK UNIFIED IDEOGRAPH
+ "E9 B9 9B96", #CJK UNIFIED IDEOGRAPH
+ "E9 BA 9B97", #CJK UNIFIED IDEOGRAPH
+ "E9 BB 9B9F", #CJK UNIFIED IDEOGRAPH
+ "E9 BC 9BA0", #CJK UNIFIED IDEOGRAPH
+ "E9 BD 9BA8", #CJK UNIFIED IDEOGRAPH
+ "E9 BE 9BB4", #CJK UNIFIED IDEOGRAPH
+ "E9 BF 9BC0", #CJK UNIFIED IDEOGRAPH
+ "E9 C0 9BCA", #CJK UNIFIED IDEOGRAPH
+ "E9 C1 9BB9", #CJK UNIFIED IDEOGRAPH
+ "E9 C2 9BC6", #CJK UNIFIED IDEOGRAPH
+ "E9 C3 9BCF", #CJK UNIFIED IDEOGRAPH
+ "E9 C4 9BD1", #CJK UNIFIED IDEOGRAPH
+ "E9 C5 9BD2", #CJK UNIFIED IDEOGRAPH
+ "E9 C6 9BE3", #CJK UNIFIED IDEOGRAPH
+ "E9 C7 9BE2", #CJK UNIFIED IDEOGRAPH
+ "E9 C8 9BE4", #CJK UNIFIED IDEOGRAPH
+ "E9 C9 9BD4", #CJK UNIFIED IDEOGRAPH
+ "E9 CA 9BE1", #CJK UNIFIED IDEOGRAPH
+ "E9 CB 9C3A", #CJK UNIFIED IDEOGRAPH
+ "E9 CC 9BF2", #CJK UNIFIED IDEOGRAPH
+ "E9 CD 9BF1", #CJK UNIFIED IDEOGRAPH
+ "E9 CE 9BF0", #CJK UNIFIED IDEOGRAPH
+ "E9 CF 9C15", #CJK UNIFIED IDEOGRAPH
+ "E9 D0 9C14", #CJK UNIFIED IDEOGRAPH
+ "E9 D1 9C09", #CJK UNIFIED IDEOGRAPH
+ "E9 D2 9C13", #CJK UNIFIED IDEOGRAPH
+ "E9 D3 9C0C", #CJK UNIFIED IDEOGRAPH
+ "E9 D4 9C06", #CJK UNIFIED IDEOGRAPH
+ "E9 D5 9C08", #CJK UNIFIED IDEOGRAPH
+ "E9 D6 9C12", #CJK UNIFIED IDEOGRAPH
+ "E9 D7 9C0A", #CJK UNIFIED IDEOGRAPH
+ "E9 D8 9C04", #CJK UNIFIED IDEOGRAPH
+ "E9 D9 9C2E", #CJK UNIFIED IDEOGRAPH
+ "E9 DA 9C1B", #CJK UNIFIED IDEOGRAPH
+ "E9 DB 9C25", #CJK UNIFIED IDEOGRAPH
+ "E9 DC 9C24", #CJK UNIFIED IDEOGRAPH
+ "E9 DD 9C21", #CJK UNIFIED IDEOGRAPH
+ "E9 DE 9C30", #CJK UNIFIED IDEOGRAPH
+ "E9 DF 9C47", #CJK UNIFIED IDEOGRAPH
+ "E9 E0 9C32", #CJK UNIFIED IDEOGRAPH
+ "E9 E1 9C46", #CJK UNIFIED IDEOGRAPH
+ "E9 E2 9C3E", #CJK UNIFIED IDEOGRAPH
+ "E9 E3 9C5A", #CJK UNIFIED IDEOGRAPH
+ "E9 E4 9C60", #CJK UNIFIED IDEOGRAPH
+ "E9 E5 9C67", #CJK UNIFIED IDEOGRAPH
+ "E9 E6 9C76", #CJK UNIFIED IDEOGRAPH
+ "E9 E7 9C78", #CJK UNIFIED IDEOGRAPH
+ "E9 E8 9CE7", #CJK UNIFIED IDEOGRAPH
+ "E9 E9 9CEC", #CJK UNIFIED IDEOGRAPH
+ "E9 EA 9CF0", #CJK UNIFIED IDEOGRAPH
+ "E9 EB 9D09", #CJK UNIFIED IDEOGRAPH
+ "E9 EC 9D08", #CJK UNIFIED IDEOGRAPH
+ "E9 ED 9CEB", #CJK UNIFIED IDEOGRAPH
+ "E9 EE 9D03", #CJK UNIFIED IDEOGRAPH
+ "E9 EF 9D06", #CJK UNIFIED IDEOGRAPH
+ "E9 F0 9D2A", #CJK UNIFIED IDEOGRAPH
+ "E9 F1 9D26", #CJK UNIFIED IDEOGRAPH
+ "E9 F2 9DAF", #CJK UNIFIED IDEOGRAPH
+ "E9 F3 9D23", #CJK UNIFIED IDEOGRAPH
+ "E9 F4 9D1F", #CJK UNIFIED IDEOGRAPH
+ "E9 F5 9D44", #CJK UNIFIED IDEOGRAPH
+ "E9 F6 9D15", #CJK UNIFIED IDEOGRAPH
+ "E9 F7 9D12", #CJK UNIFIED IDEOGRAPH
+ "E9 F8 9D41", #CJK UNIFIED IDEOGRAPH
+ "E9 F9 9D3F", #CJK UNIFIED IDEOGRAPH
+ "E9 FA 9D3E", #CJK UNIFIED IDEOGRAPH
+ "E9 FB 9D46", #CJK UNIFIED IDEOGRAPH
+ "E9 FC 9D48", #CJK UNIFIED IDEOGRAPH
+ "EA 40 9D5D", #CJK UNIFIED IDEOGRAPH
+ "EA 41 9D5E", #CJK UNIFIED IDEOGRAPH
+ "EA 42 9D64", #CJK UNIFIED IDEOGRAPH
+ "EA 43 9D51", #CJK UNIFIED IDEOGRAPH
+ "EA 44 9D50", #CJK UNIFIED IDEOGRAPH
+ "EA 45 9D59", #CJK UNIFIED IDEOGRAPH
+ "EA 46 9D72", #CJK UNIFIED IDEOGRAPH
+ "EA 47 9D89", #CJK UNIFIED IDEOGRAPH
+ "EA 48 9D87", #CJK UNIFIED IDEOGRAPH
+ "EA 49 9DAB", #CJK UNIFIED IDEOGRAPH
+ "EA 4A 9D6F", #CJK UNIFIED IDEOGRAPH
+ "EA 4B 9D7A", #CJK UNIFIED IDEOGRAPH
+ "EA 4C 9D9A", #CJK UNIFIED IDEOGRAPH
+ "EA 4D 9DA4", #CJK UNIFIED IDEOGRAPH
+ "EA 4E 9DA9", #CJK UNIFIED IDEOGRAPH
+ "EA 4F 9DB2", #CJK UNIFIED IDEOGRAPH
+ "EA 50 9DC4", #CJK UNIFIED IDEOGRAPH
+ "EA 51 9DC1", #CJK UNIFIED IDEOGRAPH
+ "EA 52 9DBB", #CJK UNIFIED IDEOGRAPH
+ "EA 53 9DB8", #CJK UNIFIED IDEOGRAPH
+ "EA 54 9DBA", #CJK UNIFIED IDEOGRAPH
+ "EA 55 9DC6", #CJK UNIFIED IDEOGRAPH
+ "EA 56 9DCF", #CJK UNIFIED IDEOGRAPH
+ "EA 57 9DC2", #CJK UNIFIED IDEOGRAPH
+ "EA 58 9DD9", #CJK UNIFIED IDEOGRAPH
+ "EA 59 9DD3", #CJK UNIFIED IDEOGRAPH
+ "EA 5A 9DF8", #CJK UNIFIED IDEOGRAPH
+ "EA 5B 9DE6", #CJK UNIFIED IDEOGRAPH
+ "EA 5C 9DED", #CJK UNIFIED IDEOGRAPH
+ "EA 5D 9DEF", #CJK UNIFIED IDEOGRAPH
+ "EA 5E 9DFD", #CJK UNIFIED IDEOGRAPH
+ "EA 5F 9E1A", #CJK UNIFIED IDEOGRAPH
+ "EA 60 9E1B", #CJK UNIFIED IDEOGRAPH
+ "EA 61 9E1E", #CJK UNIFIED IDEOGRAPH
+ "EA 62 9E75", #CJK UNIFIED IDEOGRAPH
+ "EA 63 9E79", #CJK UNIFIED IDEOGRAPH
+ "EA 64 9E7D", #CJK UNIFIED IDEOGRAPH
+ "EA 65 9E81", #CJK UNIFIED IDEOGRAPH
+ "EA 66 9E88", #CJK UNIFIED IDEOGRAPH
+ "EA 67 9E8B", #CJK UNIFIED IDEOGRAPH
+ "EA 68 9E8C", #CJK UNIFIED IDEOGRAPH
+ "EA 69 9E92", #CJK UNIFIED IDEOGRAPH
+ "EA 6A 9E95", #CJK UNIFIED IDEOGRAPH
+ "EA 6B 9E91", #CJK UNIFIED IDEOGRAPH
+ "EA 6C 9E9D", #CJK UNIFIED IDEOGRAPH
+ "EA 6D 9EA5", #CJK UNIFIED IDEOGRAPH
+ "EA 6E 9EA9", #CJK UNIFIED IDEOGRAPH
+ "EA 6F 9EB8", #CJK UNIFIED IDEOGRAPH
+ "EA 70 9EAA", #CJK UNIFIED IDEOGRAPH
+ "EA 71 9EAD", #CJK UNIFIED IDEOGRAPH
+ "EA 72 9761", #CJK UNIFIED IDEOGRAPH
+ "EA 73 9ECC", #CJK UNIFIED IDEOGRAPH
+ "EA 74 9ECE", #CJK UNIFIED IDEOGRAPH
+ "EA 75 9ECF", #CJK UNIFIED IDEOGRAPH
+ "EA 76 9ED0", #CJK UNIFIED IDEOGRAPH
+ "EA 77 9ED4", #CJK UNIFIED IDEOGRAPH
+ "EA 78 9EDC", #CJK UNIFIED IDEOGRAPH
+ "EA 79 9EDE", #CJK UNIFIED IDEOGRAPH
+ "EA 7A 9EDD", #CJK UNIFIED IDEOGRAPH
+ "EA 7B 9EE0", #CJK UNIFIED IDEOGRAPH
+ "EA 7C 9EE5", #CJK UNIFIED IDEOGRAPH
+ "EA 7D 9EE8", #CJK UNIFIED IDEOGRAPH
+ "EA 7E 9EEF", #CJK UNIFIED IDEOGRAPH
+ "EA 80 9EF4", #CJK UNIFIED IDEOGRAPH
+ "EA 81 9EF6", #CJK UNIFIED IDEOGRAPH
+ "EA 82 9EF7", #CJK UNIFIED IDEOGRAPH
+ "EA 83 9EF9", #CJK UNIFIED IDEOGRAPH
+ "EA 84 9EFB", #CJK UNIFIED IDEOGRAPH
+ "EA 85 9EFC", #CJK UNIFIED IDEOGRAPH
+ "EA 86 9EFD", #CJK UNIFIED IDEOGRAPH
+ "EA 87 9F07", #CJK UNIFIED IDEOGRAPH
+ "EA 88 9F08", #CJK UNIFIED IDEOGRAPH
+ "EA 89 76B7", #CJK UNIFIED IDEOGRAPH
+ "EA 8A 9F15", #CJK UNIFIED IDEOGRAPH
+ "EA 8B 9F21", #CJK UNIFIED IDEOGRAPH
+ "EA 8C 9F2C", #CJK UNIFIED IDEOGRAPH
+ "EA 8D 9F3E", #CJK UNIFIED IDEOGRAPH
+ "EA 8E 9F4A", #CJK UNIFIED IDEOGRAPH
+ "EA 8F 9F52", #CJK UNIFIED IDEOGRAPH
+ "EA 90 9F54", #CJK UNIFIED IDEOGRAPH
+ "EA 91 9F63", #CJK UNIFIED IDEOGRAPH
+ "EA 92 9F5F", #CJK UNIFIED IDEOGRAPH
+ "EA 93 9F60", #CJK UNIFIED IDEOGRAPH
+ "EA 94 9F61", #CJK UNIFIED IDEOGRAPH
+ "EA 95 9F66", #CJK UNIFIED IDEOGRAPH
+ "EA 96 9F67", #CJK UNIFIED IDEOGRAPH
+ "EA 97 9F6C", #CJK UNIFIED IDEOGRAPH
+ "EA 98 9F6A", #CJK UNIFIED IDEOGRAPH
+ "EA 99 9F77", #CJK UNIFIED IDEOGRAPH
+ "EA 9A 9F72", #CJK UNIFIED IDEOGRAPH
+ "EA 9B 9F76", #CJK UNIFIED IDEOGRAPH
+ "EA 9C 9F95", #CJK UNIFIED IDEOGRAPH
+ "EA 9D 9F9C", #CJK UNIFIED IDEOGRAPH
+ "EA 9E 9FA0", #CJK UNIFIED IDEOGRAPH
+ "EA 9F 582F", #CJK UNIFIED IDEOGRAPH
+ "EA A0 69C7", #CJK UNIFIED IDEOGRAPH
+ "EA A1 9059", #CJK UNIFIED IDEOGRAPH
+ "EA A2 7464", #CJK UNIFIED IDEOGRAPH
+ "EA A3 51DC", #CJK UNIFIED IDEOGRAPH
+ "EA A4 7199", #CJK UNIFIED IDEOGRAPH
+ "ED 40 7E8A", #CJK UNIFIED IDEOGRAPH
+ "ED 41 891C", #CJK UNIFIED IDEOGRAPH
+ "ED 42 9348", #CJK UNIFIED IDEOGRAPH
+ "ED 43 9288", #CJK UNIFIED IDEOGRAPH
+ "ED 44 84DC", #CJK UNIFIED IDEOGRAPH
+ "ED 45 4FC9", #CJK UNIFIED IDEOGRAPH
+ "ED 46 70BB", #CJK UNIFIED IDEOGRAPH
+ "ED 47 6631", #CJK UNIFIED IDEOGRAPH
+ "ED 48 68C8", #CJK UNIFIED IDEOGRAPH
+ "ED 49 92F9", #CJK UNIFIED IDEOGRAPH
+ "ED 4A 66FB", #CJK UNIFIED IDEOGRAPH
+ "ED 4B 5F45", #CJK UNIFIED IDEOGRAPH
+ "ED 4C 4E28", #CJK UNIFIED IDEOGRAPH
+ "ED 4D 4EE1", #CJK UNIFIED IDEOGRAPH
+ "ED 4E 4EFC", #CJK UNIFIED IDEOGRAPH
+ "ED 4F 4F00", #CJK UNIFIED IDEOGRAPH
+ "ED 50 4F03", #CJK UNIFIED IDEOGRAPH
+ "ED 51 4F39", #CJK UNIFIED IDEOGRAPH
+ "ED 52 4F56", #CJK UNIFIED IDEOGRAPH
+ "ED 53 4F92", #CJK UNIFIED IDEOGRAPH
+ "ED 54 4F8A", #CJK UNIFIED IDEOGRAPH
+ "ED 55 4F9A", #CJK UNIFIED IDEOGRAPH
+ "ED 56 4F94", #CJK UNIFIED IDEOGRAPH
+ "ED 57 4FCD", #CJK UNIFIED IDEOGRAPH
+ "ED 58 5040", #CJK UNIFIED IDEOGRAPH
+ "ED 59 5022", #CJK UNIFIED IDEOGRAPH
+ "ED 5A 4FFF", #CJK UNIFIED IDEOGRAPH
+ "ED 5B 501E", #CJK UNIFIED IDEOGRAPH
+ "ED 5C 5046", #CJK UNIFIED IDEOGRAPH
+ "ED 5D 5070", #CJK UNIFIED IDEOGRAPH
+ "ED 5E 5042", #CJK UNIFIED IDEOGRAPH
+ "ED 5F 5094", #CJK UNIFIED IDEOGRAPH
+ "ED 60 50F4", #CJK UNIFIED IDEOGRAPH
+ "ED 61 50D8", #CJK UNIFIED IDEOGRAPH
+ "ED 62 514A", #CJK UNIFIED IDEOGRAPH
+ "ED 63 5164", #CJK UNIFIED IDEOGRAPH
+ "ED 64 519D", #CJK UNIFIED IDEOGRAPH
+ "ED 65 51BE", #CJK UNIFIED IDEOGRAPH
+ "ED 66 51EC", #CJK UNIFIED IDEOGRAPH
+ "ED 67 5215", #CJK UNIFIED IDEOGRAPH
+ "ED 68 529C", #CJK UNIFIED IDEOGRAPH
+ "ED 69 52A6", #CJK UNIFIED IDEOGRAPH
+ "ED 6A 52C0", #CJK UNIFIED IDEOGRAPH
+ "ED 6B 52DB", #CJK UNIFIED IDEOGRAPH
+ "ED 6C 5300", #CJK UNIFIED IDEOGRAPH
+ "ED 6D 5307", #CJK UNIFIED IDEOGRAPH
+ "ED 6E 5324", #CJK UNIFIED IDEOGRAPH
+ "ED 6F 5372", #CJK UNIFIED IDEOGRAPH
+ "ED 70 5393", #CJK UNIFIED IDEOGRAPH
+ "ED 71 53B2", #CJK UNIFIED IDEOGRAPH
+ "ED 72 53DD", #CJK UNIFIED IDEOGRAPH
+ "ED 73 FA0E", #CJK COMPATIBILITY IDEOGRAPH
+ "ED 74 549C", #CJK UNIFIED IDEOGRAPH
+ "ED 75 548A", #CJK UNIFIED IDEOGRAPH
+ "ED 76 54A9", #CJK UNIFIED IDEOGRAPH
+ "ED 77 54FF", #CJK UNIFIED IDEOGRAPH
+ "ED 78 5586", #CJK UNIFIED IDEOGRAPH
+ "ED 79 5759", #CJK UNIFIED IDEOGRAPH
+ "ED 7A 5765", #CJK UNIFIED IDEOGRAPH
+ "ED 7B 57AC", #CJK UNIFIED IDEOGRAPH
+ "ED 7C 57C8", #CJK UNIFIED IDEOGRAPH
+ "ED 7D 57C7", #CJK UNIFIED IDEOGRAPH
+ "ED 7E FA0F", #CJK COMPATIBILITY IDEOGRAPH
+ "ED 80 FA10", #CJK COMPATIBILITY IDEOGRAPH
+ "ED 81 589E", #CJK UNIFIED IDEOGRAPH
+ "ED 82 58B2", #CJK UNIFIED IDEOGRAPH
+ "ED 83 590B", #CJK UNIFIED IDEOGRAPH
+ "ED 84 5953", #CJK UNIFIED IDEOGRAPH
+ "ED 85 595B", #CJK UNIFIED IDEOGRAPH
+ "ED 86 595D", #CJK UNIFIED IDEOGRAPH
+ "ED 87 5963", #CJK UNIFIED IDEOGRAPH
+ "ED 88 59A4", #CJK UNIFIED IDEOGRAPH
+ "ED 89 59BA", #CJK UNIFIED IDEOGRAPH
+ "ED 8A 5B56", #CJK UNIFIED IDEOGRAPH
+ "ED 8B 5BC0", #CJK UNIFIED IDEOGRAPH
+ "ED 8C 752F", #CJK UNIFIED IDEOGRAPH
+ "ED 8D 5BD8", #CJK UNIFIED IDEOGRAPH
+ "ED 8E 5BEC", #CJK UNIFIED IDEOGRAPH
+ "ED 8F 5C1E", #CJK UNIFIED IDEOGRAPH
+ "ED 90 5CA6", #CJK UNIFIED IDEOGRAPH
+ "ED 91 5CBA", #CJK UNIFIED IDEOGRAPH
+ "ED 92 5CF5", #CJK UNIFIED IDEOGRAPH
+ "ED 93 5D27", #CJK UNIFIED IDEOGRAPH
+ "ED 94 5D53", #CJK UNIFIED IDEOGRAPH
+ "ED 95 FA11", #CJK COMPATIBILITY IDEOGRAPH
+ "ED 96 5D42", #CJK UNIFIED IDEOGRAPH
+ "ED 97 5D6D", #CJK UNIFIED IDEOGRAPH
+ "ED 98 5DB8", #CJK UNIFIED IDEOGRAPH
+ "ED 99 5DB9", #CJK UNIFIED IDEOGRAPH
+ "ED 9A 5DD0", #CJK UNIFIED IDEOGRAPH
+ "ED 9B 5F21", #CJK UNIFIED IDEOGRAPH
+ "ED 9C 5F34", #CJK UNIFIED IDEOGRAPH
+ "ED 9D 5F67", #CJK UNIFIED IDEOGRAPH
+ "ED 9E 5FB7", #CJK UNIFIED IDEOGRAPH
+ "ED 9F 5FDE", #CJK UNIFIED IDEOGRAPH
+ "ED A0 605D", #CJK UNIFIED IDEOGRAPH
+ "ED A1 6085", #CJK UNIFIED IDEOGRAPH
+ "ED A2 608A", #CJK UNIFIED IDEOGRAPH
+ "ED A3 60DE", #CJK UNIFIED IDEOGRAPH
+ "ED A4 60D5", #CJK UNIFIED IDEOGRAPH
+ "ED A5 6120", #CJK UNIFIED IDEOGRAPH
+ "ED A6 60F2", #CJK UNIFIED IDEOGRAPH
+ "ED A7 6111", #CJK UNIFIED IDEOGRAPH
+ "ED A8 6137", #CJK UNIFIED IDEOGRAPH
+ "ED A9 6130", #CJK UNIFIED IDEOGRAPH
+ "ED AA 6198", #CJK UNIFIED IDEOGRAPH
+ "ED AB 6213", #CJK UNIFIED IDEOGRAPH
+ "ED AC 62A6", #CJK UNIFIED IDEOGRAPH
+ "ED AD 63F5", #CJK UNIFIED IDEOGRAPH
+ "ED AE 6460", #CJK UNIFIED IDEOGRAPH
+ "ED AF 649D", #CJK UNIFIED IDEOGRAPH
+ "ED B0 64CE", #CJK UNIFIED IDEOGRAPH
+ "ED B1 654E", #CJK UNIFIED IDEOGRAPH
+ "ED B2 6600", #CJK UNIFIED IDEOGRAPH
+ "ED B3 6615", #CJK UNIFIED IDEOGRAPH
+ "ED B4 663B", #CJK UNIFIED IDEOGRAPH
+ "ED B5 6609", #CJK UNIFIED IDEOGRAPH
+ "ED B6 662E", #CJK UNIFIED IDEOGRAPH
+ "ED B7 661E", #CJK UNIFIED IDEOGRAPH
+ "ED B8 6624", #CJK UNIFIED IDEOGRAPH
+ "ED B9 6665", #CJK UNIFIED IDEOGRAPH
+ "ED BA 6657", #CJK UNIFIED IDEOGRAPH
+ "ED BB 6659", #CJK UNIFIED IDEOGRAPH
+ "ED BC FA12", #CJK COMPATIBILITY IDEOGRAPH
+ "ED BD 6673", #CJK UNIFIED IDEOGRAPH
+ "ED BE 6699", #CJK UNIFIED IDEOGRAPH
+ "ED BF 66A0", #CJK UNIFIED IDEOGRAPH
+ "ED C0 66B2", #CJK UNIFIED IDEOGRAPH
+ "ED C1 66BF", #CJK UNIFIED IDEOGRAPH
+ "ED C2 66FA", #CJK UNIFIED IDEOGRAPH
+ "ED C3 670E", #CJK UNIFIED IDEOGRAPH
+ "ED C4 F929", #CJK COMPATIBILITY IDEOGRAPH
+ "ED C5 6766", #CJK UNIFIED IDEOGRAPH
+ "ED C6 67BB", #CJK UNIFIED IDEOGRAPH
+ "ED C7 6852", #CJK UNIFIED IDEOGRAPH
+ "ED C8 67C0", #CJK UNIFIED IDEOGRAPH
+ "ED C9 6801", #CJK UNIFIED IDEOGRAPH
+ "ED CA 6844", #CJK UNIFIED IDEOGRAPH
+ "ED CB 68CF", #CJK UNIFIED IDEOGRAPH
+ "ED CC FA13", #CJK COMPATIBILITY IDEOGRAPH
+ "ED CD 6968", #CJK UNIFIED IDEOGRAPH
+ "ED CE FA14", #CJK COMPATIBILITY IDEOGRAPH
+ "ED CF 6998", #CJK UNIFIED IDEOGRAPH
+ "ED D0 69E2", #CJK UNIFIED IDEOGRAPH
+ "ED D1 6A30", #CJK UNIFIED IDEOGRAPH
+ "ED D2 6A6B", #CJK UNIFIED IDEOGRAPH
+ "ED D3 6A46", #CJK UNIFIED IDEOGRAPH
+ "ED D4 6A73", #CJK UNIFIED IDEOGRAPH
+ "ED D5 6A7E", #CJK UNIFIED IDEOGRAPH
+ "ED D6 6AE2", #CJK UNIFIED IDEOGRAPH
+ "ED D7 6AE4", #CJK UNIFIED IDEOGRAPH
+ "ED D8 6BD6", #CJK UNIFIED IDEOGRAPH
+ "ED D9 6C3F", #CJK UNIFIED IDEOGRAPH
+ "ED DA 6C5C", #CJK UNIFIED IDEOGRAPH
+ "ED DB 6C86", #CJK UNIFIED IDEOGRAPH
+ "ED DC 6C6F", #CJK UNIFIED IDEOGRAPH
+ "ED DD 6CDA", #CJK UNIFIED IDEOGRAPH
+ "ED DE 6D04", #CJK UNIFIED IDEOGRAPH
+ "ED DF 6D87", #CJK UNIFIED IDEOGRAPH
+ "ED E0 6D6F", #CJK UNIFIED IDEOGRAPH
+ "ED E1 6D96", #CJK UNIFIED IDEOGRAPH
+ "ED E2 6DAC", #CJK UNIFIED IDEOGRAPH
+ "ED E3 6DCF", #CJK UNIFIED IDEOGRAPH
+ "ED E4 6DF8", #CJK UNIFIED IDEOGRAPH
+ "ED E5 6DF2", #CJK UNIFIED IDEOGRAPH
+ "ED E6 6DFC", #CJK UNIFIED IDEOGRAPH
+ "ED E7 6E39", #CJK UNIFIED IDEOGRAPH
+ "ED E8 6E5C", #CJK UNIFIED IDEOGRAPH
+ "ED E9 6E27", #CJK UNIFIED IDEOGRAPH
+ "ED EA 6E3C", #CJK UNIFIED IDEOGRAPH
+ "ED EB 6EBF", #CJK UNIFIED IDEOGRAPH
+ "ED EC 6F88", #CJK UNIFIED IDEOGRAPH
+ "ED ED 6FB5", #CJK UNIFIED IDEOGRAPH
+ "ED EE 6FF5", #CJK UNIFIED IDEOGRAPH
+ "ED EF 7005", #CJK UNIFIED IDEOGRAPH
+ "ED F0 7007", #CJK UNIFIED IDEOGRAPH
+ "ED F1 7028", #CJK UNIFIED IDEOGRAPH
+ "ED F2 7085", #CJK UNIFIED IDEOGRAPH
+ "ED F3 70AB", #CJK UNIFIED IDEOGRAPH
+ "ED F4 710F", #CJK UNIFIED IDEOGRAPH
+ "ED F5 7104", #CJK UNIFIED IDEOGRAPH
+ "ED F6 715C", #CJK UNIFIED IDEOGRAPH
+ "ED F7 7146", #CJK UNIFIED IDEOGRAPH
+ "ED F8 7147", #CJK UNIFIED IDEOGRAPH
+ "ED F9 FA15", #CJK COMPATIBILITY IDEOGRAPH
+ "ED FA 71C1", #CJK UNIFIED IDEOGRAPH
+ "ED FB 71FE", #CJK UNIFIED IDEOGRAPH
+ "ED FC 72B1", #CJK UNIFIED IDEOGRAPH
+ "EE 40 72BE", #CJK UNIFIED IDEOGRAPH
+ "EE 41 7324", #CJK UNIFIED IDEOGRAPH
+ "EE 42 FA16", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 43 7377", #CJK UNIFIED IDEOGRAPH
+ "EE 44 73BD", #CJK UNIFIED IDEOGRAPH
+ "EE 45 73C9", #CJK UNIFIED IDEOGRAPH
+ "EE 46 73D6", #CJK UNIFIED IDEOGRAPH
+ "EE 47 73E3", #CJK UNIFIED IDEOGRAPH
+ "EE 48 73D2", #CJK UNIFIED IDEOGRAPH
+ "EE 49 7407", #CJK UNIFIED IDEOGRAPH
+ "EE 4A 73F5", #CJK UNIFIED IDEOGRAPH
+ "EE 4B 7426", #CJK UNIFIED IDEOGRAPH
+ "EE 4C 742A", #CJK UNIFIED IDEOGRAPH
+ "EE 4D 7429", #CJK UNIFIED IDEOGRAPH
+ "EE 4E 742E", #CJK UNIFIED IDEOGRAPH
+ "EE 4F 7462", #CJK UNIFIED IDEOGRAPH
+ "EE 50 7489", #CJK UNIFIED IDEOGRAPH
+ "EE 51 749F", #CJK UNIFIED IDEOGRAPH
+ "EE 52 7501", #CJK UNIFIED IDEOGRAPH
+ "EE 53 756F", #CJK UNIFIED IDEOGRAPH
+ "EE 54 7682", #CJK UNIFIED IDEOGRAPH
+ "EE 55 769C", #CJK UNIFIED IDEOGRAPH
+ "EE 56 769E", #CJK UNIFIED IDEOGRAPH
+ "EE 57 769B", #CJK UNIFIED IDEOGRAPH
+ "EE 58 76A6", #CJK UNIFIED IDEOGRAPH
+ "EE 59 FA17", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 5A 7746", #CJK UNIFIED IDEOGRAPH
+ "EE 5B 52AF", #CJK UNIFIED IDEOGRAPH
+ "EE 5C 7821", #CJK UNIFIED IDEOGRAPH
+ "EE 5D 784E", #CJK UNIFIED IDEOGRAPH
+ "EE 5E 7864", #CJK UNIFIED IDEOGRAPH
+ "EE 5F 787A", #CJK UNIFIED IDEOGRAPH
+ "EE 60 7930", #CJK UNIFIED IDEOGRAPH
+ "EE 61 FA18", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 62 FA19", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 63 FA1A", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 64 7994", #CJK UNIFIED IDEOGRAPH
+ "EE 65 FA1B", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 66 799B", #CJK UNIFIED IDEOGRAPH
+ "EE 67 7AD1", #CJK UNIFIED IDEOGRAPH
+ "EE 68 7AE7", #CJK UNIFIED IDEOGRAPH
+ "EE 69 FA1C", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 6A 7AEB", #CJK UNIFIED IDEOGRAPH
+ "EE 6B 7B9E", #CJK UNIFIED IDEOGRAPH
+ "EE 6C FA1D", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 6D 7D48", #CJK UNIFIED IDEOGRAPH
+ "EE 6E 7D5C", #CJK UNIFIED IDEOGRAPH
+ "EE 6F 7DB7", #CJK UNIFIED IDEOGRAPH
+ "EE 70 7DA0", #CJK UNIFIED IDEOGRAPH
+ "EE 71 7DD6", #CJK UNIFIED IDEOGRAPH
+ "EE 72 7E52", #CJK UNIFIED IDEOGRAPH
+ "EE 73 7F47", #CJK UNIFIED IDEOGRAPH
+ "EE 74 7FA1", #CJK UNIFIED IDEOGRAPH
+ "EE 75 FA1E", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 76 8301", #CJK UNIFIED IDEOGRAPH
+ "EE 77 8362", #CJK UNIFIED IDEOGRAPH
+ "EE 78 837F", #CJK UNIFIED IDEOGRAPH
+ "EE 79 83C7", #CJK UNIFIED IDEOGRAPH
+ "EE 7A 83F6", #CJK UNIFIED IDEOGRAPH
+ "EE 7B 8448", #CJK UNIFIED IDEOGRAPH
+ "EE 7C 84B4", #CJK UNIFIED IDEOGRAPH
+ "EE 7D 8553", #CJK UNIFIED IDEOGRAPH
+ "EE 7E 8559", #CJK UNIFIED IDEOGRAPH
+ "EE 80 856B", #CJK UNIFIED IDEOGRAPH
+ "EE 81 FA1F", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 82 85B0", #CJK UNIFIED IDEOGRAPH
+ "EE 83 FA20", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 84 FA21", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 85 8807", #CJK UNIFIED IDEOGRAPH
+ "EE 86 88F5", #CJK UNIFIED IDEOGRAPH
+ "EE 87 8A12", #CJK UNIFIED IDEOGRAPH
+ "EE 88 8A37", #CJK UNIFIED IDEOGRAPH
+ "EE 89 8A79", #CJK UNIFIED IDEOGRAPH
+ "EE 8A 8AA7", #CJK UNIFIED IDEOGRAPH
+ "EE 8B 8ABE", #CJK UNIFIED IDEOGRAPH
+ "EE 8C 8ADF", #CJK UNIFIED IDEOGRAPH
+ "EE 8D FA22", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 8E 8AF6", #CJK UNIFIED IDEOGRAPH
+ "EE 8F 8B53", #CJK UNIFIED IDEOGRAPH
+ "EE 90 8B7F", #CJK UNIFIED IDEOGRAPH
+ "EE 91 8CF0", #CJK UNIFIED IDEOGRAPH
+ "EE 92 8CF4", #CJK UNIFIED IDEOGRAPH
+ "EE 93 8D12", #CJK UNIFIED IDEOGRAPH
+ "EE 94 8D76", #CJK UNIFIED IDEOGRAPH
+ "EE 95 FA23", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 96 8ECF", #CJK UNIFIED IDEOGRAPH
+ "EE 97 FA24", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 98 FA25", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 99 9067", #CJK UNIFIED IDEOGRAPH
+ "EE 9A 90DE", #CJK UNIFIED IDEOGRAPH
+ "EE 9B FA26", #CJK COMPATIBILITY IDEOGRAPH
+ "EE 9C 9115", #CJK UNIFIED IDEOGRAPH
+ "EE 9D 9127", #CJK UNIFIED IDEOGRAPH
+ "EE 9E 91DA", #CJK UNIFIED IDEOGRAPH
+ "EE 9F 91D7", #CJK UNIFIED IDEOGRAPH
+ "EE A0 91DE", #CJK UNIFIED IDEOGRAPH
+ "EE A1 91ED", #CJK UNIFIED IDEOGRAPH
+ "EE A2 91EE", #CJK UNIFIED IDEOGRAPH
+ "EE A3 91E4", #CJK UNIFIED IDEOGRAPH
+ "EE A4 91E5", #CJK UNIFIED IDEOGRAPH
+ "EE A5 9206", #CJK UNIFIED IDEOGRAPH
+ "EE A6 9210", #CJK UNIFIED IDEOGRAPH
+ "EE A7 920A", #CJK UNIFIED IDEOGRAPH
+ "EE A8 923A", #CJK UNIFIED IDEOGRAPH
+ "EE A9 9240", #CJK UNIFIED IDEOGRAPH
+ "EE AA 923C", #CJK UNIFIED IDEOGRAPH
+ "EE AB 924E", #CJK UNIFIED IDEOGRAPH
+ "EE AC 9259", #CJK UNIFIED IDEOGRAPH
+ "EE AD 9251", #CJK UNIFIED IDEOGRAPH
+ "EE AE 9239", #CJK UNIFIED IDEOGRAPH
+ "EE AF 9267", #CJK UNIFIED IDEOGRAPH
+ "EE B0 92A7", #CJK UNIFIED IDEOGRAPH
+ "EE B1 9277", #CJK UNIFIED IDEOGRAPH
+ "EE B2 9278", #CJK UNIFIED IDEOGRAPH
+ "EE B3 92E7", #CJK UNIFIED IDEOGRAPH
+ "EE B4 92D7", #CJK UNIFIED IDEOGRAPH
+ "EE B5 92D9", #CJK UNIFIED IDEOGRAPH
+ "EE B6 92D0", #CJK UNIFIED IDEOGRAPH
+ "EE B7 FA27", #CJK COMPATIBILITY IDEOGRAPH
+ "EE B8 92D5", #CJK UNIFIED IDEOGRAPH
+ "EE B9 92E0", #CJK UNIFIED IDEOGRAPH
+ "EE BA 92D3", #CJK UNIFIED IDEOGRAPH
+ "EE BB 9325", #CJK UNIFIED IDEOGRAPH
+ "EE BC 9321", #CJK UNIFIED IDEOGRAPH
+ "EE BD 92FB", #CJK UNIFIED IDEOGRAPH
+ "EE BE FA28", #CJK COMPATIBILITY IDEOGRAPH
+ "EE BF 931E", #CJK UNIFIED IDEOGRAPH
+ "EE C0 92FF", #CJK UNIFIED IDEOGRAPH
+ "EE C1 931D", #CJK UNIFIED IDEOGRAPH
+ "EE C2 9302", #CJK UNIFIED IDEOGRAPH
+ "EE C3 9370", #CJK UNIFIED IDEOGRAPH
+ "EE C4 9357", #CJK UNIFIED IDEOGRAPH
+ "EE C5 93A4", #CJK UNIFIED IDEOGRAPH
+ "EE C6 93C6", #CJK UNIFIED IDEOGRAPH
+ "EE C7 93DE", #CJK UNIFIED IDEOGRAPH
+ "EE C8 93F8", #CJK UNIFIED IDEOGRAPH
+ "EE C9 9431", #CJK UNIFIED IDEOGRAPH
+ "EE CA 9445", #CJK UNIFIED IDEOGRAPH
+ "EE CB 9448", #CJK UNIFIED IDEOGRAPH
+ "EE CC 9592", #CJK UNIFIED IDEOGRAPH
+ "EE CD F9DC", #CJK COMPATIBILITY IDEOGRAPH
+ "EE CE FA29", #CJK COMPATIBILITY IDEOGRAPH
+ "EE CF 969D", #CJK UNIFIED IDEOGRAPH
+ "EE D0 96AF", #CJK UNIFIED IDEOGRAPH
+ "EE D1 9733", #CJK UNIFIED IDEOGRAPH
+ "EE D2 973B", #CJK UNIFIED IDEOGRAPH
+ "EE D3 9743", #CJK UNIFIED IDEOGRAPH
+ "EE D4 974D", #CJK UNIFIED IDEOGRAPH
+ "EE D5 974F", #CJK UNIFIED IDEOGRAPH
+ "EE D6 9751", #CJK UNIFIED IDEOGRAPH
+ "EE D7 9755", #CJK UNIFIED IDEOGRAPH
+ "EE D8 9857", #CJK UNIFIED IDEOGRAPH
+ "EE D9 9865", #CJK UNIFIED IDEOGRAPH
+ "EE DA FA2A", #CJK COMPATIBILITY IDEOGRAPH
+ "EE DB FA2B", #CJK COMPATIBILITY IDEOGRAPH
+ "EE DC 9927", #CJK UNIFIED IDEOGRAPH
+ "EE DD FA2C", #CJK COMPATIBILITY IDEOGRAPH
+ "EE DE 999E", #CJK UNIFIED IDEOGRAPH
+ "EE DF 9A4E", #CJK UNIFIED IDEOGRAPH
+ "EE E0 9AD9", #CJK UNIFIED IDEOGRAPH
+ "EE E1 9ADC", #CJK UNIFIED IDEOGRAPH
+ "EE E2 9B75", #CJK UNIFIED IDEOGRAPH
+ "EE E3 9B72", #CJK UNIFIED IDEOGRAPH
+ "EE E4 9B8F", #CJK UNIFIED IDEOGRAPH
+ "EE E5 9BB1", #CJK UNIFIED IDEOGRAPH
+ "EE E6 9BBB", #CJK UNIFIED IDEOGRAPH
+ "EE E7 9C00", #CJK UNIFIED IDEOGRAPH
+ "EE E8 9D70", #CJK UNIFIED IDEOGRAPH
+ "EE E9 9D6B", #CJK UNIFIED IDEOGRAPH
+ "EE EA FA2D", #CJK COMPATIBILITY IDEOGRAPH
+ "EE EB 9E19", #CJK UNIFIED IDEOGRAPH
+ "EE EC 9ED1", #CJK UNIFIED IDEOGRAPH
+ "EE EF 2170", #SMALL ROMAN NUMERAL ONE
+ "EE F0 2171", #SMALL ROMAN NUMERAL TWO
+ "EE F1 2172", #SMALL ROMAN NUMERAL THREE
+ "EE F2 2173", #SMALL ROMAN NUMERAL FOUR
+ "EE F3 2174", #SMALL ROMAN NUMERAL FIVE
+ "EE F4 2175", #SMALL ROMAN NUMERAL SIX
+ "EE F5 2176", #SMALL ROMAN NUMERAL SEVEN
+ "EE F6 2177", #SMALL ROMAN NUMERAL EIGHT
+ "EE F7 2178", #SMALL ROMAN NUMERAL NINE
+ "EE F8 2179", #SMALL ROMAN NUMERAL TEN
+ "EE F9 FFE2", #FULLWIDTH NOT SIGN
+ "EE FA FFE4", #FULLWIDTH BROKEN BAR
+ "EE FB FF07", #FULLWIDTH APOSTROPHE
+ "EE FC FF02", #FULLWIDTH QUOTATION MARK
+ "FA 40 2170", #SMALL ROMAN NUMERAL ONE
+ "FA 41 2171", #SMALL ROMAN NUMERAL TWO
+ "FA 42 2172", #SMALL ROMAN NUMERAL THREE
+ "FA 43 2173", #SMALL ROMAN NUMERAL FOUR
+ "FA 44 2174", #SMALL ROMAN NUMERAL FIVE
+ "FA 45 2175", #SMALL ROMAN NUMERAL SIX
+ "FA 46 2176", #SMALL ROMAN NUMERAL SEVEN
+ "FA 47 2177", #SMALL ROMAN NUMERAL EIGHT
+ "FA 48 2178", #SMALL ROMAN NUMERAL NINE
+ "FA 49 2179", #SMALL ROMAN NUMERAL TEN
+ "FA 4A 2160", #ROMAN NUMERAL ONE
+ "FA 4B 2161", #ROMAN NUMERAL TWO
+ "FA 4C 2162", #ROMAN NUMERAL THREE
+ "FA 4D 2163", #ROMAN NUMERAL FOUR
+ "FA 4E 2164", #ROMAN NUMERAL FIVE
+ "FA 4F 2165", #ROMAN NUMERAL SIX
+ "FA 50 2166", #ROMAN NUMERAL SEVEN
+ "FA 51 2167", #ROMAN NUMERAL EIGHT
+ "FA 52 2168", #ROMAN NUMERAL NINE
+ "FA 53 2169", #ROMAN NUMERAL TEN
+ "FA 54 FFE2", #FULLWIDTH NOT SIGN
+ "FA 55 FFE4", #FULLWIDTH BROKEN BAR
+ "FA 56 FF07", #FULLWIDTH APOSTROPHE
+ "FA 57 FF02", #FULLWIDTH QUOTATION MARK
+ "FA 58 3231", #PARENTHESIZED IDEOGRAPH STOCK
+ "FA 59 2116", #NUMERO SIGN
+ "FA 5A 2121", #TELEPHONE SIGN
+ "FA 5B 2235", #BECAUSE
+ "FA 5C 7E8A", #CJK UNIFIED IDEOGRAPH
+ "FA 5D 891C", #CJK UNIFIED IDEOGRAPH
+ "FA 5E 9348", #CJK UNIFIED IDEOGRAPH
+ "FA 5F 9288", #CJK UNIFIED IDEOGRAPH
+ "FA 60 84DC", #CJK UNIFIED IDEOGRAPH
+ "FA 61 4FC9", #CJK UNIFIED IDEOGRAPH
+ "FA 62 70BB", #CJK UNIFIED IDEOGRAPH
+ "FA 63 6631", #CJK UNIFIED IDEOGRAPH
+ "FA 64 68C8", #CJK UNIFIED IDEOGRAPH
+ "FA 65 92F9", #CJK UNIFIED IDEOGRAPH
+ "FA 66 66FB", #CJK UNIFIED IDEOGRAPH
+ "FA 67 5F45", #CJK UNIFIED IDEOGRAPH
+ "FA 68 4E28", #CJK UNIFIED IDEOGRAPH
+ "FA 69 4EE1", #CJK UNIFIED IDEOGRAPH
+ "FA 6A 4EFC", #CJK UNIFIED IDEOGRAPH
+ "FA 6B 4F00", #CJK UNIFIED IDEOGRAPH
+ "FA 6C 4F03", #CJK UNIFIED IDEOGRAPH
+ "FA 6D 4F39", #CJK UNIFIED IDEOGRAPH
+ "FA 6E 4F56", #CJK UNIFIED IDEOGRAPH
+ "FA 6F 4F92", #CJK UNIFIED IDEOGRAPH
+ "FA 70 4F8A", #CJK UNIFIED IDEOGRAPH
+ "FA 71 4F9A", #CJK UNIFIED IDEOGRAPH
+ "FA 72 4F94", #CJK UNIFIED IDEOGRAPH
+ "FA 73 4FCD", #CJK UNIFIED IDEOGRAPH
+ "FA 74 5040", #CJK UNIFIED IDEOGRAPH
+ "FA 75 5022", #CJK UNIFIED IDEOGRAPH
+ "FA 76 4FFF", #CJK UNIFIED IDEOGRAPH
+ "FA 77 501E", #CJK UNIFIED IDEOGRAPH
+ "FA 78 5046", #CJK UNIFIED IDEOGRAPH
+ "FA 79 5070", #CJK UNIFIED IDEOGRAPH
+ "FA 7A 5042", #CJK UNIFIED IDEOGRAPH
+ "FA 7B 5094", #CJK UNIFIED IDEOGRAPH
+ "FA 7C 50F4", #CJK UNIFIED IDEOGRAPH
+ "FA 7D 50D8", #CJK UNIFIED IDEOGRAPH
+ "FA 7E 514A", #CJK UNIFIED IDEOGRAPH
+ "FA 80 5164", #CJK UNIFIED IDEOGRAPH
+ "FA 81 519D", #CJK UNIFIED IDEOGRAPH
+ "FA 82 51BE", #CJK UNIFIED IDEOGRAPH
+ "FA 83 51EC", #CJK UNIFIED IDEOGRAPH
+ "FA 84 5215", #CJK UNIFIED IDEOGRAPH
+ "FA 85 529C", #CJK UNIFIED IDEOGRAPH
+ "FA 86 52A6", #CJK UNIFIED IDEOGRAPH
+ "FA 87 52C0", #CJK UNIFIED IDEOGRAPH
+ "FA 88 52DB", #CJK UNIFIED IDEOGRAPH
+ "FA 89 5300", #CJK UNIFIED IDEOGRAPH
+ "FA 8A 5307", #CJK UNIFIED IDEOGRAPH
+ "FA 8B 5324", #CJK UNIFIED IDEOGRAPH
+ "FA 8C 5372", #CJK UNIFIED IDEOGRAPH
+ "FA 8D 5393", #CJK UNIFIED IDEOGRAPH
+ "FA 8E 53B2", #CJK UNIFIED IDEOGRAPH
+ "FA 8F 53DD", #CJK UNIFIED IDEOGRAPH
+ "FA 90 FA0E", #CJK COMPATIBILITY IDEOGRAPH
+ "FA 91 549C", #CJK UNIFIED IDEOGRAPH
+ "FA 92 548A", #CJK UNIFIED IDEOGRAPH
+ "FA 93 54A9", #CJK UNIFIED IDEOGRAPH
+ "FA 94 54FF", #CJK UNIFIED IDEOGRAPH
+ "FA 95 5586", #CJK UNIFIED IDEOGRAPH
+ "FA 96 5759", #CJK UNIFIED IDEOGRAPH
+ "FA 97 5765", #CJK UNIFIED IDEOGRAPH
+ "FA 98 57AC", #CJK UNIFIED IDEOGRAPH
+ "FA 99 57C8", #CJK UNIFIED IDEOGRAPH
+ "FA 9A 57C7", #CJK UNIFIED IDEOGRAPH
+ "FA 9B FA0F", #CJK COMPATIBILITY IDEOGRAPH
+ "FA 9C FA10", #CJK COMPATIBILITY IDEOGRAPH
+ "FA 9D 589E", #CJK UNIFIED IDEOGRAPH
+ "FA 9E 58B2", #CJK UNIFIED IDEOGRAPH
+ "FA 9F 590B", #CJK UNIFIED IDEOGRAPH
+ "FA A0 5953", #CJK UNIFIED IDEOGRAPH
+ "FA A1 595B", #CJK UNIFIED IDEOGRAPH
+ "FA A2 595D", #CJK UNIFIED IDEOGRAPH
+ "FA A3 5963", #CJK UNIFIED IDEOGRAPH
+ "FA A4 59A4", #CJK UNIFIED IDEOGRAPH
+ "FA A5 59BA", #CJK UNIFIED IDEOGRAPH
+ "FA A6 5B56", #CJK UNIFIED IDEOGRAPH
+ "FA A7 5BC0", #CJK UNIFIED IDEOGRAPH
+ "FA A8 752F", #CJK UNIFIED IDEOGRAPH
+ "FA A9 5BD8", #CJK UNIFIED IDEOGRAPH
+ "FA AA 5BEC", #CJK UNIFIED IDEOGRAPH
+ "FA AB 5C1E", #CJK UNIFIED IDEOGRAPH
+ "FA AC 5CA6", #CJK UNIFIED IDEOGRAPH
+ "FA AD 5CBA", #CJK UNIFIED IDEOGRAPH
+ "FA AE 5CF5", #CJK UNIFIED IDEOGRAPH
+ "FA AF 5D27", #CJK UNIFIED IDEOGRAPH
+ "FA B0 5D53", #CJK UNIFIED IDEOGRAPH
+ "FA B1 FA11", #CJK COMPATIBILITY IDEOGRAPH
+ "FA B2 5D42", #CJK UNIFIED IDEOGRAPH
+ "FA B3 5D6D", #CJK UNIFIED IDEOGRAPH
+ "FA B4 5DB8", #CJK UNIFIED IDEOGRAPH
+ "FA B5 5DB9", #CJK UNIFIED IDEOGRAPH
+ "FA B6 5DD0", #CJK UNIFIED IDEOGRAPH
+ "FA B7 5F21", #CJK UNIFIED IDEOGRAPH
+ "FA B8 5F34", #CJK UNIFIED IDEOGRAPH
+ "FA B9 5F67", #CJK UNIFIED IDEOGRAPH
+ "FA BA 5FB7", #CJK UNIFIED IDEOGRAPH
+ "FA BB 5FDE", #CJK UNIFIED IDEOGRAPH
+ "FA BC 605D", #CJK UNIFIED IDEOGRAPH
+ "FA BD 6085", #CJK UNIFIED IDEOGRAPH
+ "FA BE 608A", #CJK UNIFIED IDEOGRAPH
+ "FA BF 60DE", #CJK UNIFIED IDEOGRAPH
+ "FA C0 60D5", #CJK UNIFIED IDEOGRAPH
+ "FA C1 6120", #CJK UNIFIED IDEOGRAPH
+ "FA C2 60F2", #CJK UNIFIED IDEOGRAPH
+ "FA C3 6111", #CJK UNIFIED IDEOGRAPH
+ "FA C4 6137", #CJK UNIFIED IDEOGRAPH
+ "FA C5 6130", #CJK UNIFIED IDEOGRAPH
+ "FA C6 6198", #CJK UNIFIED IDEOGRAPH
+ "FA C7 6213", #CJK UNIFIED IDEOGRAPH
+ "FA C8 62A6", #CJK UNIFIED IDEOGRAPH
+ "FA C9 63F5", #CJK UNIFIED IDEOGRAPH
+ "FA CA 6460", #CJK UNIFIED IDEOGRAPH
+ "FA CB 649D", #CJK UNIFIED IDEOGRAPH
+ "FA CC 64CE", #CJK UNIFIED IDEOGRAPH
+ "FA CD 654E", #CJK UNIFIED IDEOGRAPH
+ "FA CE 6600", #CJK UNIFIED IDEOGRAPH
+ "FA CF 6615", #CJK UNIFIED IDEOGRAPH
+ "FA D0 663B", #CJK UNIFIED IDEOGRAPH
+ "FA D1 6609", #CJK UNIFIED IDEOGRAPH
+ "FA D2 662E", #CJK UNIFIED IDEOGRAPH
+ "FA D3 661E", #CJK UNIFIED IDEOGRAPH
+ "FA D4 6624", #CJK UNIFIED IDEOGRAPH
+ "FA D5 6665", #CJK UNIFIED IDEOGRAPH
+ "FA D6 6657", #CJK UNIFIED IDEOGRAPH
+ "FA D7 6659", #CJK UNIFIED IDEOGRAPH
+ "FA D8 FA12", #CJK COMPATIBILITY IDEOGRAPH
+ "FA D9 6673", #CJK UNIFIED IDEOGRAPH
+ "FA DA 6699", #CJK UNIFIED IDEOGRAPH
+ "FA DB 66A0", #CJK UNIFIED IDEOGRAPH
+ "FA DC 66B2", #CJK UNIFIED IDEOGRAPH
+ "FA DD 66BF", #CJK UNIFIED IDEOGRAPH
+ "FA DE 66FA", #CJK UNIFIED IDEOGRAPH
+ "FA DF 670E", #CJK UNIFIED IDEOGRAPH
+ "FA E0 F929", #CJK COMPATIBILITY IDEOGRAPH
+ "FA E1 6766", #CJK UNIFIED IDEOGRAPH
+ "FA E2 67BB", #CJK UNIFIED IDEOGRAPH
+ "FA E3 6852", #CJK UNIFIED IDEOGRAPH
+ "FA E4 67C0", #CJK UNIFIED IDEOGRAPH
+ "FA E5 6801", #CJK UNIFIED IDEOGRAPH
+ "FA E6 6844", #CJK UNIFIED IDEOGRAPH
+ "FA E7 68CF", #CJK UNIFIED IDEOGRAPH
+ "FA E8 FA13", #CJK COMPATIBILITY IDEOGRAPH
+ "FA E9 6968", #CJK UNIFIED IDEOGRAPH
+ "FA EA FA14", #CJK COMPATIBILITY IDEOGRAPH
+ "FA EB 6998", #CJK UNIFIED IDEOGRAPH
+ "FA EC 69E2", #CJK UNIFIED IDEOGRAPH
+ "FA ED 6A30", #CJK UNIFIED IDEOGRAPH
+ "FA EE 6A6B", #CJK UNIFIED IDEOGRAPH
+ "FA EF 6A46", #CJK UNIFIED IDEOGRAPH
+ "FA F0 6A73", #CJK UNIFIED IDEOGRAPH
+ "FA F1 6A7E", #CJK UNIFIED IDEOGRAPH
+ "FA F2 6AE2", #CJK UNIFIED IDEOGRAPH
+ "FA F3 6AE4", #CJK UNIFIED IDEOGRAPH
+ "FA F4 6BD6", #CJK UNIFIED IDEOGRAPH
+ "FA F5 6C3F", #CJK UNIFIED IDEOGRAPH
+ "FA F6 6C5C", #CJK UNIFIED IDEOGRAPH
+ "FA F7 6C86", #CJK UNIFIED IDEOGRAPH
+ "FA F8 6C6F", #CJK UNIFIED IDEOGRAPH
+ "FA F9 6CDA", #CJK UNIFIED IDEOGRAPH
+ "FA FA 6D04", #CJK UNIFIED IDEOGRAPH
+ "FA FB 6D87", #CJK UNIFIED IDEOGRAPH
+ "FA FC 6D6F", #CJK UNIFIED IDEOGRAPH
+ "FB 40 6D96", #CJK UNIFIED IDEOGRAPH
+ "FB 41 6DAC", #CJK UNIFIED IDEOGRAPH
+ "FB 42 6DCF", #CJK UNIFIED IDEOGRAPH
+ "FB 43 6DF8", #CJK UNIFIED IDEOGRAPH
+ "FB 44 6DF2", #CJK UNIFIED IDEOGRAPH
+ "FB 45 6DFC", #CJK UNIFIED IDEOGRAPH
+ "FB 46 6E39", #CJK UNIFIED IDEOGRAPH
+ "FB 47 6E5C", #CJK UNIFIED IDEOGRAPH
+ "FB 48 6E27", #CJK UNIFIED IDEOGRAPH
+ "FB 49 6E3C", #CJK UNIFIED IDEOGRAPH
+ "FB 4A 6EBF", #CJK UNIFIED IDEOGRAPH
+ "FB 4B 6F88", #CJK UNIFIED IDEOGRAPH
+ "FB 4C 6FB5", #CJK UNIFIED IDEOGRAPH
+ "FB 4D 6FF5", #CJK UNIFIED IDEOGRAPH
+ "FB 4E 7005", #CJK UNIFIED IDEOGRAPH
+ "FB 4F 7007", #CJK UNIFIED IDEOGRAPH
+ "FB 50 7028", #CJK UNIFIED IDEOGRAPH
+ "FB 51 7085", #CJK UNIFIED IDEOGRAPH
+ "FB 52 70AB", #CJK UNIFIED IDEOGRAPH
+ "FB 53 710F", #CJK UNIFIED IDEOGRAPH
+ "FB 54 7104", #CJK UNIFIED IDEOGRAPH
+ "FB 55 715C", #CJK UNIFIED IDEOGRAPH
+ "FB 56 7146", #CJK UNIFIED IDEOGRAPH
+ "FB 57 7147", #CJK UNIFIED IDEOGRAPH
+ "FB 58 FA15", #CJK COMPATIBILITY IDEOGRAPH
+ "FB 59 71C1", #CJK UNIFIED IDEOGRAPH
+ "FB 5A 71FE", #CJK UNIFIED IDEOGRAPH
+ "FB 5B 72B1", #CJK UNIFIED IDEOGRAPH
+ "FB 5C 72BE", #CJK UNIFIED IDEOGRAPH
+ "FB 5D 7324", #CJK UNIFIED IDEOGRAPH
+ "FB 5E FA16", #CJK COMPATIBILITY IDEOGRAPH
+ "FB 5F 7377", #CJK UNIFIED IDEOGRAPH
+ "FB 60 73BD", #CJK UNIFIED IDEOGRAPH
+ "FB 61 73C9", #CJK UNIFIED IDEOGRAPH
+ "FB 62 73D6", #CJK UNIFIED IDEOGRAPH
+ "FB 63 73E3", #CJK UNIFIED IDEOGRAPH
+ "FB 64 73D2", #CJK UNIFIED IDEOGRAPH
+ "FB 65 7407", #CJK UNIFIED IDEOGRAPH
+ "FB 66 73F5", #CJK UNIFIED IDEOGRAPH
+ "FB 67 7426", #CJK UNIFIED IDEOGRAPH
+ "FB 68 742A", #CJK UNIFIED IDEOGRAPH
+ "FB 69 7429", #CJK UNIFIED IDEOGRAPH
+ "FB 6A 742E", #CJK UNIFIED IDEOGRAPH
+ "FB 6B 7462", #CJK UNIFIED IDEOGRAPH
+ "FB 6C 7489", #CJK UNIFIED IDEOGRAPH
+ "FB 6D 749F", #CJK UNIFIED IDEOGRAPH
+ "FB 6E 7501", #CJK UNIFIED IDEOGRAPH
+ "FB 6F 756F", #CJK UNIFIED IDEOGRAPH
+ "FB 70 7682", #CJK UNIFIED IDEOGRAPH
+ "FB 71 769C", #CJK UNIFIED IDEOGRAPH
+ "FB 72 769E", #CJK UNIFIED IDEOGRAPH
+ "FB 73 769B", #CJK UNIFIED IDEOGRAPH
+ "FB 74 76A6", #CJK UNIFIED IDEOGRAPH
+ "FB 75 FA17", #CJK COMPATIBILITY IDEOGRAPH
+ "FB 76 7746", #CJK UNIFIED IDEOGRAPH
+ "FB 77 52AF", #CJK UNIFIED IDEOGRAPH
+ "FB 78 7821", #CJK UNIFIED IDEOGRAPH
+ "FB 79 784E", #CJK UNIFIED IDEOGRAPH
+ "FB 7A 7864", #CJK UNIFIED IDEOGRAPH
+ "FB 7B 787A", #CJK UNIFIED IDEOGRAPH
+ "FB 7C 7930", #CJK UNIFIED IDEOGRAPH
+ "FB 7D FA18", #CJK COMPATIBILITY IDEOGRAPH
+ "FB 7E FA19", #CJK COMPATIBILITY IDEOGRAPH
+ "FB 80 FA1A", #CJK COMPATIBILITY IDEOGRAPH
+ "FB 81 7994", #CJK UNIFIED IDEOGRAPH
+ "FB 82 FA1B", #CJK COMPATIBILITY IDEOGRAPH
+ "FB 83 799B", #CJK UNIFIED IDEOGRAPH
+ "FB 84 7AD1", #CJK UNIFIED IDEOGRAPH
+ "FB 85 7AE7", #CJK UNIFIED IDEOGRAPH
+ "FB 86 FA1C", #CJK COMPATIBILITY IDEOGRAPH
+ "FB 87 7AEB", #CJK UNIFIED IDEOGRAPH
+ "FB 88 7B9E", #CJK UNIFIED IDEOGRAPH
+ "FB 89 FA1D", #CJK COMPATIBILITY IDEOGRAPH
+ "FB 8A 7D48", #CJK UNIFIED IDEOGRAPH
+ "FB 8B 7D5C", #CJK UNIFIED IDEOGRAPH
+ "FB 8C 7DB7", #CJK UNIFIED IDEOGRAPH
+ "FB 8D 7DA0", #CJK UNIFIED IDEOGRAPH
+ "FB 8E 7DD6", #CJK UNIFIED IDEOGRAPH
+ "FB 8F 7E52", #CJK UNIFIED IDEOGRAPH
+ "FB 90 7F47", #CJK UNIFIED IDEOGRAPH
+ "FB 91 7FA1", #CJK UNIFIED IDEOGRAPH
+ "FB 92 FA1E", #CJK COMPATIBILITY IDEOGRAPH
+ "FB 93 8301", #CJK UNIFIED IDEOGRAPH
+ "FB 94 8362", #CJK UNIFIED IDEOGRAPH
+ "FB 95 837F", #CJK UNIFIED IDEOGRAPH
+ "FB 96 83C7", #CJK UNIFIED IDEOGRAPH
+ "FB 97 83F6", #CJK UNIFIED IDEOGRAPH
+ "FB 98 8448", #CJK UNIFIED IDEOGRAPH
+ "FB 99 84B4", #CJK UNIFIED IDEOGRAPH
+ "FB 9A 8553", #CJK UNIFIED IDEOGRAPH
+ "FB 9B 8559", #CJK UNIFIED IDEOGRAPH
+ "FB 9C 856B", #CJK UNIFIED IDEOGRAPH
+ "FB 9D FA1F", #CJK COMPATIBILITY IDEOGRAPH
+ "FB 9E 85B0", #CJK UNIFIED IDEOGRAPH
+ "FB 9F FA20", #CJK COMPATIBILITY IDEOGRAPH
+ "FB A0 FA21", #CJK COMPATIBILITY IDEOGRAPH
+ "FB A1 8807", #CJK UNIFIED IDEOGRAPH
+ "FB A2 88F5", #CJK UNIFIED IDEOGRAPH
+ "FB A3 8A12", #CJK UNIFIED IDEOGRAPH
+ "FB A4 8A37", #CJK UNIFIED IDEOGRAPH
+ "FB A5 8A79", #CJK UNIFIED IDEOGRAPH
+ "FB A6 8AA7", #CJK UNIFIED IDEOGRAPH
+ "FB A7 8ABE", #CJK UNIFIED IDEOGRAPH
+ "FB A8 8ADF", #CJK UNIFIED IDEOGRAPH
+ "FB A9 FA22", #CJK COMPATIBILITY IDEOGRAPH
+ "FB AA 8AF6", #CJK UNIFIED IDEOGRAPH
+ "FB AB 8B53", #CJK UNIFIED IDEOGRAPH
+ "FB AC 8B7F", #CJK UNIFIED IDEOGRAPH
+ "FB AD 8CF0", #CJK UNIFIED IDEOGRAPH
+ "FB AE 8CF4", #CJK UNIFIED IDEOGRAPH
+ "FB AF 8D12", #CJK UNIFIED IDEOGRAPH
+ "FB B0 8D76", #CJK UNIFIED IDEOGRAPH
+ "FB B1 FA23", #CJK COMPATIBILITY IDEOGRAPH
+ "FB B2 8ECF", #CJK UNIFIED IDEOGRAPH
+ "FB B3 FA24", #CJK COMPATIBILITY IDEOGRAPH
+ "FB B4 FA25", #CJK COMPATIBILITY IDEOGRAPH
+ "FB B5 9067", #CJK UNIFIED IDEOGRAPH
+ "FB B6 90DE", #CJK UNIFIED IDEOGRAPH
+ "FB B7 FA26", #CJK COMPATIBILITY IDEOGRAPH
+ "FB B8 9115", #CJK UNIFIED IDEOGRAPH
+ "FB B9 9127", #CJK UNIFIED IDEOGRAPH
+ "FB BA 91DA", #CJK UNIFIED IDEOGRAPH
+ "FB BB 91D7", #CJK UNIFIED IDEOGRAPH
+ "FB BC 91DE", #CJK UNIFIED IDEOGRAPH
+ "FB BD 91ED", #CJK UNIFIED IDEOGRAPH
+ "FB BE 91EE", #CJK UNIFIED IDEOGRAPH
+ "FB BF 91E4", #CJK UNIFIED IDEOGRAPH
+ "FB C0 91E5", #CJK UNIFIED IDEOGRAPH
+ "FB C1 9206", #CJK UNIFIED IDEOGRAPH
+ "FB C2 9210", #CJK UNIFIED IDEOGRAPH
+ "FB C3 920A", #CJK UNIFIED IDEOGRAPH
+ "FB C4 923A", #CJK UNIFIED IDEOGRAPH
+ "FB C5 9240", #CJK UNIFIED IDEOGRAPH
+ "FB C6 923C", #CJK UNIFIED IDEOGRAPH
+ "FB C7 924E", #CJK UNIFIED IDEOGRAPH
+ "FB C8 9259", #CJK UNIFIED IDEOGRAPH
+ "FB C9 9251", #CJK UNIFIED IDEOGRAPH
+ "FB CA 9239", #CJK UNIFIED IDEOGRAPH
+ "FB CB 9267", #CJK UNIFIED IDEOGRAPH
+ "FB CC 92A7", #CJK UNIFIED IDEOGRAPH
+ "FB CD 9277", #CJK UNIFIED IDEOGRAPH
+ "FB CE 9278", #CJK UNIFIED IDEOGRAPH
+ "FB CF 92E7", #CJK UNIFIED IDEOGRAPH
+ "FB D0 92D7", #CJK UNIFIED IDEOGRAPH
+ "FB D1 92D9", #CJK UNIFIED IDEOGRAPH
+ "FB D2 92D0", #CJK UNIFIED IDEOGRAPH
+ "FB D3 FA27", #CJK COMPATIBILITY IDEOGRAPH
+ "FB D4 92D5", #CJK UNIFIED IDEOGRAPH
+ "FB D5 92E0", #CJK UNIFIED IDEOGRAPH
+ "FB D6 92D3", #CJK UNIFIED IDEOGRAPH
+ "FB D7 9325", #CJK UNIFIED IDEOGRAPH
+ "FB D8 9321", #CJK UNIFIED IDEOGRAPH
+ "FB D9 92FB", #CJK UNIFIED IDEOGRAPH
+ "FB DA FA28", #CJK COMPATIBILITY IDEOGRAPH
+ "FB DB 931E", #CJK UNIFIED IDEOGRAPH
+ "FB DC 92FF", #CJK UNIFIED IDEOGRAPH
+ "FB DD 931D", #CJK UNIFIED IDEOGRAPH
+ "FB DE 9302", #CJK UNIFIED IDEOGRAPH
+ "FB DF 9370", #CJK UNIFIED IDEOGRAPH
+ "FB E0 9357", #CJK UNIFIED IDEOGRAPH
+ "FB E1 93A4", #CJK UNIFIED IDEOGRAPH
+ "FB E2 93C6", #CJK UNIFIED IDEOGRAPH
+ "FB E3 93DE", #CJK UNIFIED IDEOGRAPH
+ "FB E4 93F8", #CJK UNIFIED IDEOGRAPH
+ "FB E5 9431", #CJK UNIFIED IDEOGRAPH
+ "FB E6 9445", #CJK UNIFIED IDEOGRAPH
+ "FB E7 9448", #CJK UNIFIED IDEOGRAPH
+ "FB E8 9592", #CJK UNIFIED IDEOGRAPH
+ "FB E9 F9DC", #CJK COMPATIBILITY IDEOGRAPH
+ "FB EA FA29", #CJK COMPATIBILITY IDEOGRAPH
+ "FB EB 969D", #CJK UNIFIED IDEOGRAPH
+ "FB EC 96AF", #CJK UNIFIED IDEOGRAPH
+ "FB ED 9733", #CJK UNIFIED IDEOGRAPH
+ "FB EE 973B", #CJK UNIFIED IDEOGRAPH
+ "FB EF 9743", #CJK UNIFIED IDEOGRAPH
+ "FB F0 974D", #CJK UNIFIED IDEOGRAPH
+ "FB F1 974F", #CJK UNIFIED IDEOGRAPH
+ "FB F2 9751", #CJK UNIFIED IDEOGRAPH
+ "FB F3 9755", #CJK UNIFIED IDEOGRAPH
+ "FB F4 9857", #CJK UNIFIED IDEOGRAPH
+ "FB F5 9865", #CJK UNIFIED IDEOGRAPH
+ "FB F6 FA2A", #CJK COMPATIBILITY IDEOGRAPH
+ "FB F7 FA2B", #CJK COMPATIBILITY IDEOGRAPH
+ "FB F8 9927", #CJK UNIFIED IDEOGRAPH
+ "FB F9 FA2C", #CJK COMPATIBILITY IDEOGRAPH
+ "FB FA 999E", #CJK UNIFIED IDEOGRAPH
+ "FB FB 9A4E", #CJK UNIFIED IDEOGRAPH
+ "FB FC 9AD9", #CJK UNIFIED IDEOGRAPH
+ "FC 40 9ADC", #CJK UNIFIED IDEOGRAPH
+ "FC 41 9B75", #CJK UNIFIED IDEOGRAPH
+ "FC 42 9B72", #CJK UNIFIED IDEOGRAPH
+ "FC 43 9B8F", #CJK UNIFIED IDEOGRAPH
+ "FC 44 9BB1", #CJK UNIFIED IDEOGRAPH
+ "FC 45 9BBB", #CJK UNIFIED IDEOGRAPH
+ "FC 46 9C00", #CJK UNIFIED IDEOGRAPH
+ "FC 47 9D70", #CJK UNIFIED IDEOGRAPH
+ "FC 48 9D6B", #CJK UNIFIED IDEOGRAPH
+ "FC 49 FA2D", #CJK COMPATIBILITY IDEOGRAPH
+ "FC 4A 9E19", #CJK UNIFIED IDEOGRAPH
+ "FC 4B 9ED1", #CJK UNIFIED IDEOGRAPH
+};
diff --git a/appl/lib/convcs/gengb2312.b b/appl/lib/convcs/gengb2312.b
new file mode 100644
index 00000000..b5a9bdf5
--- /dev/null
+++ b/appl/lib/convcs/gengb2312.b
@@ -0,0 +1,1143 @@
+# generate the GB2312 character set converter data file
+implement GenGB;
+
+include "sys.m";
+include "draw.m";
+
+GBDATA : con "/lib/convcs/gb2312";
+
+GenGB : module {
+ init : fn (ctxt : ref Draw->Context, args : list of string);
+};
+
+init(nil : ref Draw->Context, nil : list of string)
+{
+ sys := load Sys Sys->PATH;
+ fd := sys->create(GBDATA, Sys->OWRITE, 8r644);
+ if (fd == nil) {
+ sys->print("cannot create %s: %r\n", GBDATA);
+ return;
+ }
+ s := "";
+ for (i := 0; i < len tabgb; i++)
+ s[len s] = tabgb[i];
+
+ buf := array of byte s;
+ for (i = 0; i < len buf;) {
+ towrite := Sys->ATOMICIO;
+ if (len buf - i < Sys->ATOMICIO)
+ towrite = len buf -i;
+ n := sys->write(fd, buf[i:], towrite);
+ if (n <= 0) {
+ sys->print("error writing %s: %r", GBDATA);
+ return;
+ }
+ i += n;
+ }
+}
+
+
+ERRchar : con 16rFFFD;
+
+tabgb := array [] of {
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r3000,16r3001,16r3002,
+16r30fb,16r02c9,16r02c7,16r00a8,16r3003,16r3005,16r2015,16r301c,
+16r2225,16r2026,16r2018,16r2019,16r201c,16r201d,16r3014,16r3015,
+16r3008,16r3009,16r300a,16r300b,16r300c,16r300d,16r300e,16r300f,
+16r3016,16r3017,16r3010,16r3011,16r00b1,16r00d7,16r00f7,16r2236,
+16r2227,16r2228,16r2211,16r220f,16r222a,16r2229,16r2208,16r2237,
+16r221a,16r22a5,16r2225,16r2220,16r2312,16r2299,16r222b,16r222e,
+16r2261,16r224c,16r2248,16r223d,16r221d,16r2260,16r226e,16r226f,
+16r2264,16r2265,16r221e,16r2235,16r2234,16r2642,16r2640,16r00b0,
+16r2032,16r2033,16r2103,16rff04,16r00a4,16rffe0,16rffe1,16r2030,
+16r00a7,16r2116,16r2606,16r2605,16r25cb,16r25cf,16r25ce,16r25c7,
+16r25c6,16r25a1,16r25a0,16r25b3,16r25b2,16r203b,16r2192,16r2190,
+16r2191,16r2193,16r3013,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r2488,16r2489,16r248a,16r248b,16r248c,16r248d,16r248e,
+16r248f,16r2490,16r2491,16r2492,16r2493,16r2494,16r2495,16r2496,
+16r2497,16r2498,16r2499,16r249a,16r249b,16r2474,16r2475,16r2476,
+16r2477,16r2478,16r2479,16r247a,16r247b,16r247c,16r247d,16r247e,
+16r247f,16r2480,16r2481,16r2482,16r2483,16r2484,16r2485,16r2486,
+16r2487,16r2460,16r2461,16r2462,16r2463,16r2464,16r2465,16r2466,
+16r2467,16r2468,16r2469,ERRchar,ERRchar,16r3220,16r3221,16r3222,
+16r3223,16r3224,16r3225,16r3226,16r3227,16r3228,16r3229,ERRchar,
+ERRchar,16r2160,16r2161,16r2162,16r2163,16r2164,16r2165,16r2166,
+16r2167,16r2168,16r2169,16r216a,16r216b,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16rff01,16rff02,16rff03,
+16rffe5,16rff05,16rff06,16rff07,16rff08,16rff09,16rff0a,16rff0b,
+16rff0c,16rff0d,16rff0e,16rff0f,16rff10,16rff11,16rff12,16rff13,
+16rff14,16rff15,16rff16,16rff17,16rff18,16rff19,16rff1a,16rff1b,
+16rff1c,16rff1d,16rff1e,16rff1f,16rff20,16rff21,16rff22,16rff23,
+16rff24,16rff25,16rff26,16rff27,16rff28,16rff29,16rff2a,16rff2b,
+16rff2c,16rff2d,16rff2e,16rff2f,16rff30,16rff31,16rff32,16rff33,
+16rff34,16rff35,16rff36,16rff37,16rff38,16rff39,16rff3a,16rff3b,
+16rff3c,16rff3d,16rff3e,16rff3f,16rff40,16rff41,16rff42,16rff43,
+16rff44,16rff45,16rff46,16rff47,16rff48,16rff49,16rff4a,16rff4b,
+16rff4c,16rff4d,16rff4e,16rff4f,16rff50,16rff51,16rff52,16rff53,
+16rff54,16rff55,16rff56,16rff57,16rff58,16rff59,16rff5a,16rff5b,
+16rff5c,16rff5d,16rffe3,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r3041,16r3042,16r3043,16r3044,16r3045,16r3046,16r3047,
+16r3048,16r3049,16r304a,16r304b,16r304c,16r304d,16r304e,16r304f,
+16r3050,16r3051,16r3052,16r3053,16r3054,16r3055,16r3056,16r3057,
+16r3058,16r3059,16r305a,16r305b,16r305c,16r305d,16r305e,16r305f,
+16r3060,16r3061,16r3062,16r3063,16r3064,16r3065,16r3066,16r3067,
+16r3068,16r3069,16r306a,16r306b,16r306c,16r306d,16r306e,16r306f,
+16r3070,16r3071,16r3072,16r3073,16r3074,16r3075,16r3076,16r3077,
+16r3078,16r3079,16r307a,16r307b,16r307c,16r307d,16r307e,16r307f,
+16r3080,16r3081,16r3082,16r3083,16r3084,16r3085,16r3086,16r3087,
+16r3088,16r3089,16r308a,16r308b,16r308c,16r308d,16r308e,16r308f,
+16r3090,16r3091,16r3092,16r3093,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r30a1,16r30a2,16r30a3,
+16r30a4,16r30a5,16r30a6,16r30a7,16r30a8,16r30a9,16r30aa,16r30ab,
+16r30ac,16r30ad,16r30ae,16r30af,16r30b0,16r30b1,16r30b2,16r30b3,
+16r30b4,16r30b5,16r30b6,16r30b7,16r30b8,16r30b9,16r30ba,16r30bb,
+16r30bc,16r30bd,16r30be,16r30bf,16r30c0,16r30c1,16r30c2,16r30c3,
+16r30c4,16r30c5,16r30c6,16r30c7,16r30c8,16r30c9,16r30ca,16r30cb,
+16r30cc,16r30cd,16r30ce,16r30cf,16r30d0,16r30d1,16r30d2,16r30d3,
+16r30d4,16r30d5,16r30d6,16r30d7,16r30d8,16r30d9,16r30da,16r30db,
+16r30dc,16r30dd,16r30de,16r30df,16r30e0,16r30e1,16r30e2,16r30e3,
+16r30e4,16r30e5,16r30e6,16r30e7,16r30e8,16r30e9,16r30ea,16r30eb,
+16r30ec,16r30ed,16r30ee,16r30ef,16r30f0,16r30f1,16r30f2,16r30f3,
+16r30f4,16r30f5,16r30f6,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r0391,16r0392,16r0393,16r0394,16r0395,16r0396,16r0397,
+16r0398,16r0399,16r039a,16r039b,16r039c,16r039d,16r039e,16r039f,
+16r03a0,16r03a1,16r03a3,16r03a4,16r03a5,16r03a6,16r03a7,16r03a8,
+16r03a9,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r03b1,16r03b2,16r03b3,16r03b4,16r03b5,16r03b6,16r03b7,
+16r03b8,16r03b9,16r03ba,16r03bb,16r03bc,16r03bd,16r03be,16r03bf,
+16r03c0,16r03c1,16r03c3,16r03c4,16r03c5,16r03c6,16r03c7,16r03c8,
+16r03c9,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r0410,16r0411,16r0412,
+16r0413,16r0414,16r0415,16r0401,16r0416,16r0417,16r0418,16r0419,
+16r041a,16r041b,16r041c,16r041d,16r041e,16r041f,16r0420,16r0421,
+16r0422,16r0423,16r0424,16r0425,16r0426,16r0427,16r0428,16r0429,
+16r042a,16r042b,16r042c,16r042d,16r042e,16r042f,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r0430,16r0431,16r0432,
+16r0433,16r0434,16r0435,16r0451,16r0436,16r0437,16r0438,16r0439,
+16r043a,16r043b,16r043c,16r043d,16r043e,16r043f,16r0440,16r0441,
+16r0442,16r0443,16r0444,16r0445,16r0446,16r0447,16r0448,16r0449,
+16r044a,16r044b,16r044c,16r044d,16r044e,16r044f,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r0101,16r00e1,16r01ce,16r00e0,16r0113,16r00e9,16r011b,
+16r00e8,16r012b,16r00ed,16r01d0,16r00ec,16r014d,16r00f3,16r01d2,
+16r00f2,16r016b,16r00fa,16r01d4,16r00f9,16r01d6,16r01d8,16r01da,
+16r01dc,16r00fc,16r00ea,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r3105,16r3106,16r3107,
+16r3108,16r3109,16r310a,16r310b,16r310c,16r310d,16r310e,16r310f,
+16r3110,16r3111,16r3112,16r3113,16r3114,16r3115,16r3116,16r3117,
+16r3118,16r3119,16r311a,16r311b,16r311c,16r311d,16r311e,16r311f,
+16r3120,16r3121,16r3122,16r3123,16r3124,16r3125,16r3126,16r3127,
+16r3128,16r3129,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+16r2500,16r2501,16r2502,16r2503,16r2504,16r2505,16r2506,16r2507,
+16r2508,16r2509,16r250a,16r250b,16r250c,16r250d,16r250e,16r250f,
+16r2510,16r2511,16r2512,16r2513,16r2514,16r2515,16r2516,16r2517,
+16r2518,16r2519,16r251a,16r251b,16r251c,16r251d,16r251e,16r251f,
+16r2520,16r2521,16r2522,16r2523,16r2524,16r2525,16r2526,16r2527,
+16r2528,16r2529,16r252a,16r252b,16r252c,16r252d,16r252e,16r252f,
+16r2530,16r2531,16r2532,16r2533,16r2534,16r2535,16r2536,16r2537,
+16r2538,16r2539,16r253a,16r253b,16r253c,16r253d,16r253e,16r253f,
+16r2540,16r2541,16r2542,16r2543,16r2544,16r2545,16r2546,16r2547,
+16r2548,16r2549,16r254a,16r254b,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r554a,16r963f,16r57c3,16r6328,16r54ce,16r5509,16r54c0,
+16r7691,16r764c,16r853c,16r77ee,16r827e,16r788d,16r7231,16r9698,
+16r978d,16r6c28,16r5b89,16r4ffa,16r6309,16r6697,16r5cb8,16r80fa,
+16r6848,16r80ae,16r6602,16r76ce,16r51f9,16r6556,16r71ac,16r7ff1,
+16r8884,16r50b2,16r5965,16r61ca,16r6fb3,16r82ad,16r634c,16r6252,
+16r53ed,16r5427,16r7b06,16r516b,16r75a4,16r5df4,16r62d4,16r8dcb,
+16r9776,16r628a,16r8019,16r575d,16r9738,16r7f62,16r7238,16r767d,
+16r67cf,16r767e,16r6446,16r4f70,16r8d25,16r62dc,16r7a17,16r6591,
+16r73ed,16r642c,16r6273,16r822c,16r9881,16r677f,16r7248,16r626e,
+16r62cc,16r4f34,16r74e3,16r534a,16r529e,16r7eca,16r90a6,16r5e2e,
+16r6886,16r699c,16r8180,16r7ed1,16r68d2,16r78c5,16r868c,16r9551,
+16r508d,16r8c24,16r82de,16r80de,16r5305,16r8912,16r5265,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r8584,16r96f9,16r4fdd,
+16r5821,16r9971,16r5b9d,16r62b1,16r62a5,16r66b4,16r8c79,16r9c8d,
+16r7206,16r676f,16r7891,16r60b2,16r5351,16r5317,16r8f88,16r80cc,
+16r8d1d,16r94a1,16r500d,16r72c8,16r5907,16r60eb,16r7119,16r88ab,
+16r5954,16r82ef,16r672c,16r7b28,16r5d29,16r7ef7,16r752d,16r6cf5,
+16r8e66,16r8ff8,16r903c,16r9f3b,16r6bd4,16r9119,16r7b14,16r5f7c,
+16r78a7,16r84d6,16r853d,16r6bd5,16r6bd9,16r6bd6,16r5e01,16r5e87,
+16r75f9,16r95ed,16r655d,16r5f0a,16r5fc5,16r8f9f,16r58c1,16r81c2,
+16r907f,16r965b,16r97ad,16r8fb9,16r7f16,16r8d2c,16r6241,16r4fbf,
+16r53d8,16r535e,16r8fa8,16r8fa9,16r8fab,16r904d,16r6807,16r5f6a,
+16r8198,16r8868,16r9cd6,16r618b,16r522b,16r762a,16r5f6c,16r658c,
+16r6fd2,16r6ee8,16r5bbe,16r6448,16r5175,16r51b0,16r67c4,16r4e19,
+16r79c9,16r997c,16r70b3,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r75c5,16r5e76,16r73bb,16r83e0,16r64ad,16r62e8,16r94b5,
+16r6ce2,16r535a,16r52c3,16r640f,16r94c2,16r7b94,16r4f2f,16r5e1b,
+16r8236,16r8116,16r818a,16r6e24,16r6cca,16r9a73,16r6355,16r535c,
+16r54fa,16r8865,16r57e0,16r4e0d,16r5e03,16r6b65,16r7c3f,16r90e8,
+16r6016,16r64e6,16r731c,16r88c1,16r6750,16r624d,16r8d22,16r776c,
+16r8e29,16r91c7,16r5f69,16r83dc,16r8521,16r9910,16r53c2,16r8695,
+16r6b8b,16r60ed,16r60e8,16r707f,16r82cd,16r8231,16r4ed3,16r6ca7,
+16r85cf,16r64cd,16r7cd9,16r69fd,16r66f9,16r8349,16r5395,16r7b56,
+16r4fa7,16r518c,16r6d4b,16r5c42,16r8e6d,16r63d2,16r53c9,16r832c,
+16r8336,16r67e5,16r78b4,16r643d,16r5bdf,16r5c94,16r5dee,16r8be7,
+16r62c6,16r67f4,16r8c7a,16r6400,16r63ba,16r8749,16r998b,16r8c17,
+16r7f20,16r94f2,16r4ea7,16r9610,16r98a4,16r660c,16r7316,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r573a,16r5c1d,16r5e38,
+16r957f,16r507f,16r80a0,16r5382,16r655e,16r7545,16r5531,16r5021,
+16r8d85,16r6284,16r949e,16r671d,16r5632,16r6f6e,16r5de2,16r5435,
+16r7092,16r8f66,16r626f,16r64a4,16r63a3,16r5f7b,16r6f88,16r90f4,
+16r81e3,16r8fb0,16r5c18,16r6668,16r5ff1,16r6c89,16r9648,16r8d81,
+16r886c,16r6491,16r79f0,16r57ce,16r6a59,16r6210,16r5448,16r4e58,
+16r7a0b,16r60e9,16r6f84,16r8bda,16r627f,16r901e,16r9a8b,16r79e4,
+16r5403,16r75f4,16r6301,16r5319,16r6c60,16r8fdf,16r5f1b,16r9a70,
+16r803b,16r9f7f,16r4f88,16r5c3a,16r8d64,16r7fc5,16r65a5,16r70bd,
+16r5145,16r51b2,16r866b,16r5d07,16r5ba0,16r62bd,16r916c,16r7574,
+16r8e0c,16r7a20,16r6101,16r7b79,16r4ec7,16r7ef8,16r7785,16r4e11,
+16r81ed,16r521d,16r51fa,16r6a71,16r53a8,16r8e87,16r9504,16r96cf,
+16r6ec1,16r9664,16r695a,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r7840,16r50a8,16r77d7,16r6410,16r89e6,16r5904,16r63e3,
+16r5ddd,16r7a7f,16r693d,16r4f20,16r8239,16r5598,16r4e32,16r75ae,
+16r7a97,16r5e62,16r5e8a,16r95ef,16r521b,16r5439,16r708a,16r6376,
+16r9524,16r5782,16r6625,16r693f,16r9187,16r5507,16r6df3,16r7eaf,
+16r8822,16r6233,16r7ef0,16r75b5,16r8328,16r78c1,16r96cc,16r8f9e,
+16r6148,16r74f7,16r8bcd,16r6b64,16r523a,16r8d50,16r6b21,16r806a,
+16r8471,16r56f1,16r5306,16r4ece,16r4e1b,16r51d1,16r7c97,16r918b,
+16r7c07,16r4fc3,16r8e7f,16r7be1,16r7a9c,16r6467,16r5d14,16r50ac,
+16r8106,16r7601,16r7cb9,16r6dec,16r7fe0,16r6751,16r5b58,16r5bf8,
+16r78cb,16r64ae,16r6413,16r63aa,16r632b,16r9519,16r642d,16r8fbe,
+16r7b54,16r7629,16r6253,16r5927,16r5446,16r6b79,16r50a3,16r6234,
+16r5e26,16r6b86,16r4ee3,16r8d37,16r888b,16r5f85,16r902e,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r6020,16r803d,16r62c5,
+16r4e39,16r5355,16r90f8,16r63b8,16r80c6,16r65e6,16r6c2e,16r4f46,
+16r60ee,16r6de1,16r8bde,16r5f39,16r86cb,16r5f53,16r6321,16r515a,
+16r8361,16r6863,16r5200,16r6363,16r8e48,16r5012,16r5c9b,16r7977,
+16r5bfc,16r5230,16r7a3b,16r60bc,16r9053,16r76d7,16r5fb7,16r5f97,
+16r7684,16r8e6c,16r706f,16r767b,16r7b49,16r77aa,16r51f3,16r9093,
+16r5824,16r4f4e,16r6ef4,16r8fea,16r654c,16r7b1b,16r72c4,16r6da4,
+16r7fdf,16r5ae1,16r62b5,16r5e95,16r5730,16r8482,16r7b2c,16r5e1d,
+16r5f1f,16r9012,16r7f14,16r98a0,16r6382,16r6ec7,16r7898,16r70b9,
+16r5178,16r975b,16r57ab,16r7535,16r4f43,16r7538,16r5e97,16r60e6,
+16r5960,16r6dc0,16r6bbf,16r7889,16r53fc,16r96d5,16r51cb,16r5201,
+16r6389,16r540a,16r9493,16r8c03,16r8dcc,16r7239,16r789f,16r8776,
+16r8fed,16r8c0d,16r53e0,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r4e01,16r76ef,16r53ee,16r9489,16r9876,16r9f0e,16r952d,
+16r5b9a,16r8ba2,16r4e22,16r4e1c,16r51ac,16r8463,16r61c2,16r52a8,
+16r680b,16r4f97,16r606b,16r51bb,16r6d1e,16r515c,16r6296,16r6597,
+16r9661,16r8c46,16r9017,16r75d8,16r90fd,16r7763,16r6bd2,16r728a,
+16r72ec,16r8bfb,16r5835,16r7779,16r8d4c,16r675c,16r9540,16r809a,
+16r5ea6,16r6e21,16r5992,16r7aef,16r77ed,16r953b,16r6bb5,16r65ad,
+16r7f0e,16r5806,16r5151,16r961f,16r5bf9,16r58a9,16r5428,16r8e72,
+16r6566,16r987f,16r56e4,16r949d,16r76fe,16r9041,16r6387,16r54c6,
+16r591a,16r593a,16r579b,16r8eb2,16r6735,16r8dfa,16r8235,16r5241,
+16r60f0,16r5815,16r86fe,16r5ce8,16r9e45,16r4fc4,16r989d,16r8bb9,
+16r5a25,16r6076,16r5384,16r627c,16r904f,16r9102,16r997f,16r6069,
+16r800c,16r513f,16r8033,16r5c14,16r9975,16r6d31,16r4e8c,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r8d30,16r53d1,16r7f5a,
+16r7b4f,16r4f10,16r4e4f,16r9600,16r6cd5,16r73d0,16r85e9,16r5e06,
+16r756a,16r7ffb,16r6a0a,16r77fe,16r9492,16r7e41,16r51e1,16r70e6,
+16r53cd,16r8fd4,16r8303,16r8d29,16r72af,16r996d,16r6cdb,16r574a,
+16r82b3,16r65b9,16r80aa,16r623f,16r9632,16r59a8,16r4eff,16r8bbf,
+16r7eba,16r653e,16r83f2,16r975e,16r5561,16r98de,16r80a5,16r532a,
+16r8bfd,16r5420,16r80ba,16r5e9f,16r6cb8,16r8d39,16r82ac,16r915a,
+16r5429,16r6c1b,16r5206,16r7eb7,16r575f,16r711a,16r6c7e,16r7c89,
+16r594b,16r4efd,16r5fff,16r6124,16r7caa,16r4e30,16r5c01,16r67ab,
+16r8702,16r5cf0,16r950b,16r98ce,16r75af,16r70fd,16r9022,16r51af,
+16r7f1d,16r8bbd,16r5949,16r51e4,16r4f5b,16r5426,16r592b,16r6577,
+16r80a4,16r5b75,16r6276,16r62c2,16r8f90,16r5e45,16r6c1f,16r7b26,
+16r4f0f,16r4fd8,16r670d,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r6d6e,16r6daa,16r798f,16r88b1,16r5f17,16r752b,16r629a,
+16r8f85,16r4fef,16r91dc,16r65a7,16r812f,16r8151,16r5e9c,16r8150,
+16r8d74,16r526f,16r8986,16r8d4b,16r590d,16r5085,16r4ed8,16r961c,
+16r7236,16r8179,16r8d1f,16r5bcc,16r8ba3,16r9644,16r5987,16r7f1a,
+16r5490,16r5676,16r560e,16r8be5,16r6539,16r6982,16r9499,16r76d6,
+16r6e89,16r5e72,16r7518,16r6746,16r67d1,16r7aff,16r809d,16r8d76,
+16r611f,16r79c6,16r6562,16r8d63,16r5188,16r521a,16r94a2,16r7f38,
+16r809b,16r7eb2,16r5c97,16r6e2f,16r6760,16r7bd9,16r768b,16r9ad8,
+16r818f,16r7f94,16r7cd5,16r641e,16r9550,16r7a3f,16r544a,16r54e5,
+16r6b4c,16r6401,16r6208,16r9e3d,16r80f3,16r7599,16r5272,16r9769,
+16r845b,16r683c,16r86e4,16r9601,16r9694,16r94ec,16r4e2a,16r5404,
+16r7ed9,16r6839,16r8ddf,16r8015,16r66f4,16r5e9a,16r7fb9,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r57c2,16r803f,16r6897,
+16r5de5,16r653b,16r529f,16r606d,16r9f9a,16r4f9b,16r8eac,16r516c,
+16r5bab,16r5f13,16r5de9,16r6c5e,16r62f1,16r8d21,16r5171,16r94a9,
+16r52fe,16r6c9f,16r82df,16r72d7,16r57a2,16r6784,16r8d2d,16r591f,
+16r8f9c,16r83c7,16r5495,16r7b8d,16r4f30,16r6cbd,16r5b64,16r59d1,
+16r9f13,16r53e4,16r86ca,16r9aa8,16r8c37,16r80a1,16r6545,16r987e,
+16r56fa,16r96c7,16r522e,16r74dc,16r5250,16r5be1,16r6302,16r8902,
+16r4e56,16r62d0,16r602a,16r68fa,16r5173,16r5b98,16r51a0,16r89c2,
+16r7ba1,16r9986,16r7f50,16r60ef,16r704c,16r8d2f,16r5149,16r5e7f,
+16r901b,16r7470,16r89c4,16r572d,16r7845,16r5f52,16r9f9f,16r95fa,
+16r8f68,16r9b3c,16r8be1,16r7678,16r6842,16r67dc,16r8dea,16r8d35,
+16r523d,16r8f8a,16r6eda,16r68cd,16r9505,16r90ed,16r56fd,16r679c,
+16r88f9,16r8fc7,16r54c8,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r9ab8,16r5b69,16r6d77,16r6c26,16r4ea5,16r5bb3,16r9a87,
+16r9163,16r61a8,16r90af,16r97e9,16r542b,16r6db5,16r5bd2,16r51fd,
+16r558a,16r7f55,16r7ff0,16r64bc,16r634d,16r65f1,16r61be,16r608d,
+16r710a,16r6c57,16r6c49,16r592f,16r676d,16r822a,16r58d5,16r568e,
+16r8c6a,16r6beb,16r90dd,16r597d,16r8017,16r53f7,16r6d69,16r5475,
+16r559d,16r8377,16r83cf,16r6838,16r79be,16r548c,16r4f55,16r5408,
+16r76d2,16r8c89,16r9602,16r6cb3,16r6db8,16r8d6b,16r8910,16r9e64,
+16r8d3a,16r563f,16r9ed1,16r75d5,16r5f88,16r72e0,16r6068,16r54fc,
+16r4ea8,16r6a2a,16r8861,16r6052,16r8f70,16r54c4,16r70d8,16r8679,
+16r9e3f,16r6d2a,16r5b8f,16r5f18,16r7ea2,16r5589,16r4faf,16r7334,
+16r543c,16r539a,16r5019,16r540e,16r547c,16r4e4e,16r5ffd,16r745a,
+16r58f6,16r846b,16r80e1,16r8774,16r72d0,16r7cca,16r6e56,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r5f27,16r864e,16r552c,
+16r62a4,16r4e92,16r6caa,16r6237,16r82b1,16r54d7,16r534e,16r733e,
+16r6ed1,16r753b,16r5212,16r5316,16r8bdd,16r69d0,16r5f8a,16r6000,
+16r6dee,16r574f,16r6b22,16r73af,16r6853,16r8fd8,16r7f13,16r6362,
+16r60a3,16r5524,16r75ea,16r8c62,16r7115,16r6da3,16r5ba6,16r5e7b,
+16r8352,16r614c,16r9ec4,16r78fa,16r8757,16r7c27,16r7687,16r51f0,
+16r60f6,16r714c,16r6643,16r5e4c,16r604d,16r8c0e,16r7070,16r6325,
+16r8f89,16r5fbd,16r6062,16r86d4,16r56de,16r6bc1,16r6094,16r6167,
+16r5349,16r60e0,16r6666,16r8d3f,16r79fd,16r4f1a,16r70e9,16r6c47,
+16r8bb3,16r8bf2,16r7ed8,16r8364,16r660f,16r5a5a,16r9b42,16r6d51,
+16r6df7,16r8c41,16r6d3b,16r4f19,16r706b,16r83b7,16r6216,16r60d1,
+16r970d,16r8d27,16r7978,16r51fb,16r573e,16r57fa,16r673a,16r7578,
+16r7a3d,16r79ef,16r7b95,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r808c,16r9965,16r8ff9,16r6fc0,16r8ba5,16r9e21,16r59ec,
+16r7ee9,16r7f09,16r5409,16r6781,16r68d8,16r8f91,16r7c4d,16r96c6,
+16r53ca,16r6025,16r75be,16r6c72,16r5373,16r5ac9,16r7ea7,16r6324,
+16r51e0,16r810a,16r5df1,16r84df,16r6280,16r5180,16r5b63,16r4f0e,
+16r796d,16r5242,16r60b8,16r6d4e,16r5bc4,16r5bc2,16r8ba1,16r8bb0,
+16r65e2,16r5fcc,16r9645,16r5993,16r7ee7,16r7eaa,16r5609,16r67b7,
+16r5939,16r4f73,16r5bb6,16r52a0,16r835a,16r988a,16r8d3e,16r7532,
+16r94be,16r5047,16r7a3c,16r4ef7,16r67b6,16r9a7e,16r5ac1,16r6b7c,
+16r76d1,16r575a,16r5c16,16r7b3a,16r95f4,16r714e,16r517c,16r80a9,
+16r8270,16r5978,16r7f04,16r8327,16r68c0,16r67ec,16r78b1,16r7877,
+16r62e3,16r6361,16r7b80,16r4fed,16r526a,16r51cf,16r8350,16r69db,
+16r9274,16r8df5,16r8d31,16r89c1,16r952e,16r7bad,16r4ef6,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r5065,16r8230,16r5251,
+16r996f,16r6e10,16r6e85,16r6da7,16r5efa,16r50f5,16r59dc,16r5c06,
+16r6d46,16r6c5f,16r7586,16r848b,16r6868,16r5956,16r8bb2,16r5320,
+16r9171,16r964d,16r8549,16r6912,16r7901,16r7126,16r80f6,16r4ea4,
+16r90ca,16r6d47,16r9a84,16r5a07,16r56bc,16r6405,16r94f0,16r77eb,
+16r4fa5,16r811a,16r72e1,16r89d2,16r997a,16r7f34,16r7ede,16r527f,
+16r6559,16r9175,16r8f7f,16r8f83,16r53eb,16r7a96,16r63ed,16r63a5,
+16r7686,16r79f8,16r8857,16r9636,16r622a,16r52ab,16r8282,16r6854,
+16r6770,16r6377,16r776b,16r7aed,16r6d01,16r7ed3,16r89e3,16r59d0,
+16r6212,16r85c9,16r82a5,16r754c,16r501f,16r4ecb,16r75a5,16r8beb,
+16r5c4a,16r5dfe,16r7b4b,16r65a4,16r91d1,16r4eca,16r6d25,16r895f,
+16r7d27,16r9526,16r4ec5,16r8c28,16r8fdb,16r9773,16r664b,16r7981,
+16r8fd1,16r70ec,16r6d78,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r5c3d,16r52b2,16r8346,16r5162,16r830e,16r775b,16r6676,
+16r9cb8,16r4eac,16r60ca,16r7cbe,16r7cb3,16r7ecf,16r4e95,16r8b66,
+16r666f,16r9888,16r9759,16r5883,16r656c,16r955c,16r5f84,16r75c9,
+16r9756,16r7adf,16r7ade,16r51c0,16r70af,16r7a98,16r63ea,16r7a76,
+16r7ea0,16r7396,16r97ed,16r4e45,16r7078,16r4e5d,16r9152,16r53a9,
+16r6551,16r65e7,16r81fc,16r8205,16r548e,16r5c31,16r759a,16r97a0,
+16r62d8,16r72d9,16r75bd,16r5c45,16r9a79,16r83ca,16r5c40,16r5480,
+16r77e9,16r4e3e,16r6cae,16r805a,16r62d2,16r636e,16r5de8,16r5177,
+16r8ddd,16r8e1e,16r952f,16r4ff1,16r53e5,16r60e7,16r70ac,16r5267,
+16r6350,16r9e43,16r5a1f,16r5026,16r7737,16r5377,16r7ee2,16r6485,
+16r652b,16r6289,16r6398,16r5014,16r7235,16r89c9,16r51b3,16r8bc0,
+16r7edd,16r5747,16r83cc,16r94a7,16r519b,16r541b,16r5cfb,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r4fca,16r7ae3,16r6d5a,
+16r90e1,16r9a8f,16r5580,16r5496,16r5361,16r54af,16r5f00,16r63e9,
+16r6977,16r51ef,16r6168,16r520a,16r582a,16r52d8,16r574e,16r780d,
+16r770b,16r5eb7,16r6177,16r7ce0,16r625b,16r6297,16r4ea2,16r7095,
+16r8003,16r62f7,16r70e4,16r9760,16r5777,16r82db,16r67ef,16r68f5,
+16r78d5,16r9897,16r79d1,16r58f3,16r54b3,16r53ef,16r6e34,16r514b,
+16r523b,16r5ba2,16r8bfe,16r80af,16r5543,16r57a6,16r6073,16r5751,
+16r542d,16r7a7a,16r6050,16r5b54,16r63a7,16r62a0,16r53e3,16r6263,
+16r5bc7,16r67af,16r54ed,16r7a9f,16r82e6,16r9177,16r5e93,16r88e4,
+16r5938,16r57ae,16r630e,16r8de8,16r80ef,16r5757,16r7b77,16r4fa9,
+16r5feb,16r5bbd,16r6b3e,16r5321,16r7b50,16r72c2,16r6846,16r77ff,
+16r7736,16r65f7,16r51b5,16r4e8f,16r76d4,16r5cbf,16r7aa5,16r8475,
+16r594e,16r9b41,16r5080,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r9988,16r6127,16r6e83,16r5764,16r6606,16r6346,16r56f0,
+16r62ec,16r6269,16r5ed3,16r9614,16r5783,16r62c9,16r5587,16r8721,
+16r814a,16r8fa3,16r5566,16r83b1,16r6765,16r8d56,16r84dd,16r5a6a,
+16r680f,16r62e6,16r7bee,16r9611,16r5170,16r6f9c,16r8c30,16r63fd,
+16r89c8,16r61d2,16r7f06,16r70c2,16r6ee5,16r7405,16r6994,16r72fc,
+16r5eca,16r90ce,16r6717,16r6d6a,16r635e,16r52b3,16r7262,16r8001,
+16r4f6c,16r59e5,16r916a,16r70d9,16r6d9d,16r52d2,16r4e50,16r96f7,
+16r956d,16r857e,16r78ca,16r7d2f,16r5121,16r5792,16r64c2,16r808b,
+16r7c7b,16r6cea,16r68f1,16r695e,16r51b7,16r5398,16r68a8,16r7281,
+16r9ece,16r7bf1,16r72f8,16r79bb,16r6f13,16r7406,16r674e,16r91cc,
+16r9ca4,16r793c,16r8389,16r8354,16r540f,16r6817,16r4e3d,16r5389,
+16r52b1,16r783e,16r5386,16r5229,16r5088,16r4f8b,16r4fd0,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r75e2,16r7acb,16r7c92,
+16r6ca5,16r96b6,16r529b,16r7483,16r54e9,16r4fe9,16r8054,16r83b2,
+16r8fde,16r9570,16r5ec9,16r601c,16r6d9f,16r5e18,16r655b,16r8138,
+16r94fe,16r604b,16r70bc,16r7ec3,16r7cae,16r51c9,16r6881,16r7cb1,
+16r826f,16r4e24,16r8f86,16r91cf,16r667e,16r4eae,16r8c05,16r64a9,
+16r804a,16r50da,16r7597,16r71ce,16r5be5,16r8fbd,16r6f66,16r4e86,
+16r6482,16r9563,16r5ed6,16r6599,16r5217,16r88c2,16r70c8,16r52a3,
+16r730e,16r7433,16r6797,16r78f7,16r9716,16r4e34,16r90bb,16r9cde,
+16r6dcb,16r51db,16r8d41,16r541d,16r62ce,16r73b2,16r83f1,16r96f6,
+16r9f84,16r94c3,16r4f36,16r7f9a,16r51cc,16r7075,16r9675,16r5cad,
+16r9886,16r53e6,16r4ee4,16r6e9c,16r7409,16r69b4,16r786b,16r998f,
+16r7559,16r5218,16r7624,16r6d41,16r67f3,16r516d,16r9f99,16r804b,
+16r5499,16r7b3c,16r7abf,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r9686,16r5784,16r62e2,16r9647,16r697c,16r5a04,16r6402,
+16r7bd3,16r6f0f,16r964b,16r82a6,16r5362,16r9885,16r5e90,16r7089,
+16r63b3,16r5364,16r864f,16r9c81,16r9e93,16r788c,16r9732,16r8def,
+16r8d42,16r9e7f,16r6f5e,16r7984,16r5f55,16r9646,16r622e,16r9a74,
+16r5415,16r94dd,16r4fa3,16r65c5,16r5c65,16r5c61,16r7f15,16r8651,
+16r6c2f,16r5f8b,16r7387,16r6ee4,16r7eff,16r5ce6,16r631b,16r5b6a,
+16r6ee6,16r5375,16r4e71,16r63a0,16r7565,16r62a1,16r8f6e,16r4f26,
+16r4ed1,16r6ca6,16r7eb6,16r8bba,16r841d,16r87ba,16r7f57,16r903b,
+16r9523,16r7ba9,16r9aa1,16r88f8,16r843d,16r6d1b,16r9a86,16r7edc,
+16r5988,16r9ebb,16r739b,16r7801,16r8682,16r9a6c,16r9a82,16r561b,
+16r5417,16r57cb,16r4e70,16r9ea6,16r5356,16r8fc8,16r8109,16r7792,
+16r9992,16r86ee,16r6ee1,16r8513,16r66fc,16r6162,16r6f2b,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r8c29,16r8292,16r832b,
+16r76f2,16r6c13,16r5fd9,16r83bd,16r732b,16r8305,16r951a,16r6bdb,
+16r77db,16r94c6,16r536f,16r8302,16r5192,16r5e3d,16r8c8c,16r8d38,
+16r4e48,16r73ab,16r679a,16r6885,16r9176,16r9709,16r7164,16r6ca1,
+16r7709,16r5a92,16r9541,16r6bcf,16r7f8e,16r6627,16r5bd0,16r59b9,
+16r5a9a,16r95e8,16r95f7,16r4eec,16r840c,16r8499,16r6aac,16r76df,
+16r9530,16r731b,16r68a6,16r5b5f,16r772f,16r919a,16r9761,16r7cdc,
+16r8ff7,16r8c1c,16r5f25,16r7c73,16r79d8,16r89c5,16r6ccc,16r871c,
+16r5bc6,16r5e42,16r68c9,16r7720,16r7ef5,16r5195,16r514d,16r52c9,
+16r5a29,16r7f05,16r9762,16r82d7,16r63cf,16r7784,16r85d0,16r79d2,
+16r6e3a,16r5e99,16r5999,16r8511,16r706d,16r6c11,16r62bf,16r76bf,
+16r654f,16r60af,16r95fd,16r660e,16r879f,16r9e23,16r94ed,16r540d,
+16r547d,16r8c2c,16r6478,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r6479,16r8611,16r6a21,16r819c,16r78e8,16r6469,16r9b54,
+16r62b9,16r672b,16r83ab,16r58a8,16r9ed8,16r6cab,16r6f20,16r5bde,
+16r964c,16r8c0b,16r725f,16r67d0,16r62c7,16r7261,16r4ea9,16r59c6,
+16r6bcd,16r5893,16r66ae,16r5e55,16r52df,16r6155,16r6728,16r76ee,
+16r7766,16r7267,16r7a46,16r62ff,16r54ea,16r5450,16r94a0,16r90a3,
+16r5a1c,16r7eb3,16r6c16,16r4e43,16r5976,16r8010,16r5948,16r5357,
+16r7537,16r96be,16r56ca,16r6320,16r8111,16r607c,16r95f9,16r6dd6,
+16r5462,16r9981,16r5185,16r5ae9,16r80fd,16r59ae,16r9713,16r502a,
+16r6ce5,16r5c3c,16r62df,16r4f60,16r533f,16r817b,16r9006,16r6eba,
+16r852b,16r62c8,16r5e74,16r78be,16r64b5,16r637b,16r5ff5,16r5a18,
+16r917f,16r9e1f,16r5c3f,16r634f,16r8042,16r5b7d,16r556e,16r954a,
+16r954d,16r6d85,16r60a8,16r67e0,16r72de,16r51dd,16r5b81,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r62e7,16r6cde,16r725b,
+16r626d,16r94ae,16r7ebd,16r8113,16r6d53,16r519c,16r5f04,16r5974,
+16r52aa,16r6012,16r5973,16r6696,16r8650,16r759f,16r632a,16r61e6,
+16r7cef,16r8bfa,16r54e6,16r6b27,16r9e25,16r6bb4,16r85d5,16r5455,
+16r5076,16r6ca4,16r556a,16r8db4,16r722c,16r5e15,16r6015,16r7436,
+16r62cd,16r6392,16r724c,16r5f98,16r6e43,16r6d3e,16r6500,16r6f58,
+16r76d8,16r78d0,16r76fc,16r7554,16r5224,16r53db,16r4e53,16r5e9e,
+16r65c1,16r802a,16r80d6,16r629b,16r5486,16r5228,16r70ae,16r888d,
+16r8dd1,16r6ce1,16r5478,16r80da,16r57f9,16r88f4,16r8d54,16r966a,
+16r914d,16r4f69,16r6c9b,16r55b7,16r76c6,16r7830,16r62a8,16r70f9,
+16r6f8e,16r5f6d,16r84ec,16r68da,16r787c,16r7bf7,16r81a8,16r670b,
+16r9e4f,16r6367,16r78b0,16r576f,16r7812,16r9739,16r6279,16r62ab,
+16r5288,16r7435,16r6bd7,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r5564,16r813e,16r75b2,16r76ae,16r5339,16r75de,16r50fb,
+16r5c41,16r8b6c,16r7bc7,16r504f,16r7247,16r9a97,16r98d8,16r6f02,
+16r74e2,16r7968,16r6487,16r77a5,16r62fc,16r9891,16r8d2b,16r54c1,
+16r8058,16r4e52,16r576a,16r82f9,16r840d,16r5e73,16r51ed,16r74f6,
+16r8bc4,16r5c4f,16r5761,16r6cfc,16r9887,16r5a46,16r7834,16r9b44,
+16r8feb,16r7c95,16r5256,16r6251,16r94fa,16r4ec6,16r8386,16r8461,
+16r83e9,16r84b2,16r57d4,16r6734,16r5703,16r666e,16r6d66,16r8c31,
+16r66dd,16r7011,16r671f,16r6b3a,16r6816,16r621a,16r59bb,16r4e03,
+16r51c4,16r6f06,16r67d2,16r6c8f,16r5176,16r68cb,16r5947,16r6b67,
+16r7566,16r5d0e,16r8110,16r9f50,16r65d7,16r7948,16r7941,16r9a91,
+16r8d77,16r5c82,16r4e5e,16r4f01,16r542f,16r5951,16r780c,16r5668,
+16r6c14,16r8fc4,16r5f03,16r6c7d,16r6ce3,16r8bab,16r6390,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r6070,16r6d3d,16r7275,
+16r6266,16r948e,16r94c5,16r5343,16r8fc1,16r7b7e,16r4edf,16r8c26,
+16r4e7e,16r9ed4,16r94b1,16r94b3,16r524d,16r6f5c,16r9063,16r6d45,
+16r8c34,16r5811,16r5d4c,16r6b20,16r6b49,16r67aa,16r545b,16r8154,
+16r7f8c,16r5899,16r8537,16r5f3a,16r62a2,16r6a47,16r9539,16r6572,
+16r6084,16r6865,16r77a7,16r4e54,16r4fa8,16r5de7,16r9798,16r64ac,
+16r7fd8,16r5ced,16r4fcf,16r7a8d,16r5207,16r8304,16r4e14,16r602f,
+16r7a83,16r94a6,16r4fb5,16r4eb2,16r79e6,16r7434,16r52e4,16r82b9,
+16r64d2,16r79bd,16r5bdd,16r6c81,16r9752,16r8f7b,16r6c22,16r503e,
+16r537f,16r6e05,16r64ce,16r6674,16r6c30,16r60c5,16r9877,16r8bf7,
+16r5e86,16r743c,16r7a77,16r79cb,16r4e18,16r90b1,16r7403,16r6c42,
+16r56da,16r914b,16r6cc5,16r8d8b,16r533a,16r86c6,16r66f2,16r8eaf,
+16r5c48,16r9a71,16r6e20,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r53d6,16r5a36,16r9f8b,16r8da3,16r53bb,16r5708,16r98a7,
+16r6743,16r919b,16r6cc9,16r5168,16r75ca,16r62f3,16r72ac,16r5238,
+16r529d,16r7f3a,16r7094,16r7638,16r5374,16r9e4a,16r69b7,16r786e,
+16r96c0,16r88d9,16r7fa4,16r7136,16r71c3,16r5189,16r67d3,16r74e4,
+16r58e4,16r6518,16r56b7,16r8ba9,16r9976,16r6270,16r7ed5,16r60f9,
+16r70ed,16r58ec,16r4ec1,16r4eba,16r5fcd,16r97e7,16r4efb,16r8ba4,
+16r5203,16r598a,16r7eab,16r6254,16r4ecd,16r65e5,16r620e,16r8338,
+16r84c9,16r8363,16r878d,16r7194,16r6eb6,16r5bb9,16r7ed2,16r5197,
+16r63c9,16r67d4,16r8089,16r8339,16r8815,16r5112,16r5b7a,16r5982,
+16r8fb1,16r4e73,16r6c5d,16r5165,16r8925,16r8f6f,16r962e,16r854a,
+16r745e,16r9510,16r95f0,16r6da6,16r82e5,16r5f31,16r6492,16r6d12,
+16r8428,16r816e,16r9cc3,16r585e,16r8d5b,16r4e09,16r53c1,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r4f1e,16r6563,16r6851,
+16r55d3,16r4e27,16r6414,16r9a9a,16r626b,16r5ac2,16r745f,16r8272,
+16r6da9,16r68ee,16r50e7,16r838e,16r7802,16r6740,16r5239,16r6c99,
+16r7eb1,16r50bb,16r5565,16r715e,16r7b5b,16r6652,16r73ca,16r82eb,
+16r6749,16r5c71,16r5220,16r717d,16r886b,16r95ea,16r9655,16r64c5,
+16r8d61,16r81b3,16r5584,16r6c55,16r6247,16r7f2e,16r5892,16r4f24,
+16r5546,16r8d4f,16r664c,16r4e0a,16r5c1a,16r88f3,16r68a2,16r634e,
+16r7a0d,16r70e7,16r828d,16r52fa,16r97f6,16r5c11,16r54e8,16r90b5,
+16r7ecd,16r5962,16r8d4a,16r86c7,16r820c,16r820d,16r8d66,16r6444,
+16r5c04,16r6151,16r6d89,16r793e,16r8bbe,16r7837,16r7533,16r547b,
+16r4f38,16r8eab,16r6df1,16r5a20,16r7ec5,16r795e,16r6c88,16r5ba1,
+16r5a76,16r751a,16r80be,16r614e,16r6e17,16r58f0,16r751f,16r7525,
+16r7272,16r5347,16r7ef3,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r7701,16r76db,16r5269,16r80dc,16r5723,16r5e08,16r5931,
+16r72ee,16r65bd,16r6e7f,16r8bd7,16r5c38,16r8671,16r5341,16r77f3,
+16r62fe,16r65f6,16r4ec0,16r98df,16r8680,16r5b9e,16r8bc6,16r53f2,
+16r77e2,16r4f7f,16r5c4e,16r9a76,16r59cb,16r5f0f,16r793a,16r58eb,
+16r4e16,16r67ff,16r4e8b,16r62ed,16r8a93,16r901d,16r52bf,16r662f,
+16r55dc,16r566c,16r9002,16r4ed5,16r4f8d,16r91ca,16r9970,16r6c0f,
+16r5e02,16r6043,16r5ba4,16r89c6,16r8bd5,16r6536,16r624b,16r9996,
+16r5b88,16r5bff,16r6388,16r552e,16r53d7,16r7626,16r517d,16r852c,
+16r67a2,16r68b3,16r6b8a,16r6292,16r8f93,16r53d4,16r8212,16r6dd1,
+16r758f,16r4e66,16r8d4e,16r5b70,16r719f,16r85af,16r6691,16r66d9,
+16r7f72,16r8700,16r9ecd,16r9f20,16r5c5e,16r672f,16r8ff0,16r6811,
+16r675f,16r620d,16r7ad6,16r5885,16r5eb6,16r6570,16r6f31,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r6055,16r5237,16r800d,
+16r6454,16r8870,16r7529,16r5e05,16r6813,16r62f4,16r971c,16r53cc,
+16r723d,16r8c01,16r6c34,16r7761,16r7a0e,16r542e,16r77ac,16r987a,
+16r821c,16r8bf4,16r7855,16r6714,16r70c1,16r65af,16r6495,16r5636,
+16r601d,16r79c1,16r53f8,16r4e1d,16r6b7b,16r8086,16r5bfa,16r55e3,
+16r56db,16r4f3a,16r4f3c,16r9972,16r5df3,16r677e,16r8038,16r6002,
+16r9882,16r9001,16r5b8b,16r8bbc,16r8bf5,16r641c,16r8258,16r64de,
+16r55fd,16r82cf,16r9165,16r4fd7,16r7d20,16r901f,16r7c9f,16r50f3,
+16r5851,16r6eaf,16r5bbf,16r8bc9,16r8083,16r9178,16r849c,16r7b97,
+16r867d,16r968b,16r968f,16r7ee5,16r9ad3,16r788e,16r5c81,16r7a57,
+16r9042,16r96a7,16r795f,16r5b59,16r635f,16r7b0b,16r84d1,16r68ad,
+16r5506,16r7f29,16r7410,16r7d22,16r9501,16r6240,16r584c,16r4ed6,
+16r5b83,16r5979,16r5854,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r736d,16r631e,16r8e4b,16r8e0f,16r80ce,16r82d4,16r62ac,
+16r53f0,16r6cf0,16r915e,16r592a,16r6001,16r6c70,16r574d,16r644a,
+16r8d2a,16r762b,16r6ee9,16r575b,16r6a80,16r75f0,16r6f6d,16r8c2d,
+16r8c08,16r5766,16r6bef,16r8892,16r78b3,16r63a2,16r53f9,16r70ad,
+16r6c64,16r5858,16r642a,16r5802,16r68e0,16r819b,16r5510,16r7cd6,
+16r5018,16r8eba,16r6dcc,16r8d9f,16r70eb,16r638f,16r6d9b,16r6ed4,
+16r7ee6,16r8404,16r6843,16r9003,16r6dd8,16r9676,16r8ba8,16r5957,
+16r7279,16r85e4,16r817e,16r75bc,16r8a8a,16r68af,16r5254,16r8e22,
+16r9511,16r63d0,16r9898,16r8e44,16r557c,16r4f53,16r66ff,16r568f,
+16r60d5,16r6d95,16r5243,16r5c49,16r5929,16r6dfb,16r586b,16r7530,
+16r751c,16r606c,16r8214,16r8146,16r6311,16r6761,16r8fe2,16r773a,
+16r8df3,16r8d34,16r94c1,16r5e16,16r5385,16r542c,16r70c3,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r6c40,16r5ef7,16r505c,
+16r4ead,16r5ead,16r633a,16r8247,16r901a,16r6850,16r916e,16r77b3,
+16r540c,16r94dc,16r5f64,16r7ae5,16r6876,16r6345,16r7b52,16r7edf,
+16r75db,16r5077,16r6295,16r5934,16r900f,16r51f8,16r79c3,16r7a81,
+16r56fe,16r5f92,16r9014,16r6d82,16r5c60,16r571f,16r5410,16r5154,
+16r6e4d,16r56e2,16r63a8,16r9893,16r817f,16r8715,16r892a,16r9000,
+16r541e,16r5c6f,16r81c0,16r62d6,16r6258,16r8131,16r9e35,16r9640,
+16r9a6e,16r9a7c,16r692d,16r59a5,16r62d3,16r553e,16r6316,16r54c7,
+16r86d9,16r6d3c,16r5a03,16r74e6,16r889c,16r6b6a,16r5916,16r8c4c,
+16r5f2f,16r6e7e,16r73a9,16r987d,16r4e38,16r70f7,16r5b8c,16r7897,
+16r633d,16r665a,16r7696,16r60cb,16r5b9b,16r5a49,16r4e07,16r8155,
+16r6c6a,16r738b,16r4ea1,16r6789,16r7f51,16r5f80,16r65fa,16r671b,
+16r5fd8,16r5984,16r5a01,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r5dcd,16r5fae,16r5371,16r97e6,16r8fdd,16r6845,16r56f4,
+16r552f,16r60df,16r4e3a,16r6f4d,16r7ef4,16r82c7,16r840e,16r59d4,
+16r4f1f,16r4f2a,16r5c3e,16r7eac,16r672a,16r851a,16r5473,16r754f,
+16r80c3,16r5582,16r9b4f,16r4f4d,16r6e2d,16r8c13,16r5c09,16r6170,
+16r536b,16r761f,16r6e29,16r868a,16r6587,16r95fb,16r7eb9,16r543b,
+16r7a33,16r7d0a,16r95ee,16r55e1,16r7fc1,16r74ee,16r631d,16r8717,
+16r6da1,16r7a9d,16r6211,16r65a1,16r5367,16r63e1,16r6c83,16r5deb,
+16r545c,16r94a8,16r4e4c,16r6c61,16r8bec,16r5c4b,16r65e0,16r829c,
+16r68a7,16r543e,16r5434,16r6bcb,16r6b66,16r4e94,16r6342,16r5348,
+16r821e,16r4f0d,16r4fae,16r575e,16r620a,16r96fe,16r6664,16r7269,
+16r52ff,16r52a1,16r609f,16r8bef,16r6614,16r7199,16r6790,16r897f,
+16r7852,16r77fd,16r6670,16r563b,16r5438,16r9521,16r727a,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r7a00,16r606f,16r5e0c,
+16r6089,16r819d,16r5915,16r60dc,16r7184,16r70ef,16r6eaa,16r6c50,
+16r7280,16r6a84,16r88ad,16r5e2d,16r4e60,16r5ab3,16r559c,16r94e3,
+16r6d17,16r7cfb,16r9699,16r620f,16r7ec6,16r778e,16r867e,16r5323,
+16r971e,16r8f96,16r6687,16r5ce1,16r4fa0,16r72ed,16r4e0b,16r53a6,
+16r590f,16r5413,16r6380,16r9528,16r5148,16r4ed9,16r9c9c,16r7ea4,
+16r54b8,16r8d24,16r8854,16r8237,16r95f2,16r6d8e,16r5f26,16r5acc,
+16r663e,16r9669,16r73b0,16r732e,16r53bf,16r817a,16r9985,16r7fa1,
+16r5baa,16r9677,16r9650,16r7ebf,16r76f8,16r53a2,16r9576,16r9999,
+16r7bb1,16r8944,16r6e58,16r4e61,16r7fd4,16r7965,16r8be6,16r60f3,
+16r54cd,16r4eab,16r9879,16r5df7,16r6a61,16r50cf,16r5411,16r8c61,
+16r8427,16r785d,16r9704,16r524a,16r54ee,16r56a3,16r9500,16r6d88,
+16r5bb5,16r6dc6,16r6653,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r5c0f,16r5b5d,16r6821,16r8096,16r5578,16r7b11,16r6548,
+16r6954,16r4e9b,16r6b47,16r874e,16r978b,16r534f,16r631f,16r643a,
+16r90aa,16r659c,16r80c1,16r8c10,16r5199,16r68b0,16r5378,16r87f9,
+16r61c8,16r6cc4,16r6cfb,16r8c22,16r5c51,16r85aa,16r82af,16r950c,
+16r6b23,16r8f9b,16r65b0,16r5ffb,16r5fc3,16r4fe1,16r8845,16r661f,
+16r8165,16r7329,16r60fa,16r5174,16r5211,16r578b,16r5f62,16r90a2,
+16r884c,16r9192,16r5e78,16r674f,16r6027,16r59d3,16r5144,16r51f6,
+16r80f8,16r5308,16r6c79,16r96c4,16r718a,16r4f11,16r4fee,16r7f9e,
+16r673d,16r55c5,16r9508,16r79c0,16r8896,16r7ee3,16r589f,16r620c,
+16r9700,16r865a,16r5618,16r987b,16r5f90,16r8bb8,16r84c4,16r9157,
+16r53d9,16r65ed,16r5e8f,16r755c,16r6064,16r7d6e,16r5a7f,16r7eea,
+16r7eed,16r8f69,16r55a7,16r5ba3,16r60ac,16r65cb,16r7384,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r9009,16r7663,16r7729,
+16r7eda,16r9774,16r859b,16r5b66,16r7a74,16r96ea,16r8840,16r52cb,
+16r718f,16r5faa,16r65ec,16r8be2,16r5bfb,16r9a6f,16r5de1,16r6b89,
+16r6c5b,16r8bad,16r8baf,16r900a,16r8fc5,16r538b,16r62bc,16r9e26,
+16r9e2d,16r5440,16r4e2b,16r82bd,16r7259,16r869c,16r5d16,16r8859,
+16r6daf,16r96c5,16r54d1,16r4e9a,16r8bb6,16r7109,16r54bd,16r9609,
+16r70df,16r6df9,16r76d0,16r4e25,16r7814,16r8712,16r5ca9,16r5ef6,
+16r8a00,16r989c,16r960e,16r708e,16r6cbf,16r5944,16r63a9,16r773c,
+16r884d,16r6f14,16r8273,16r5830,16r71d5,16r538c,16r781a,16r96c1,
+16r5501,16r5f66,16r7130,16r5bb4,16r8c1a,16r9a8c,16r6b83,16r592e,
+16r9e2f,16r79e7,16r6768,16r626c,16r4f6f,16r75a1,16r7f8a,16r6d0b,
+16r9633,16r6c27,16r4ef0,16r75d2,16r517b,16r6837,16r6f3e,16r9080,
+16r8170,16r5996,16r7476,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r6447,16r5c27,16r9065,16r7a91,16r8c23,16r59da,16r54ac,
+16r8200,16r836f,16r8981,16r8000,16r6930,16r564e,16r8036,16r7237,
+16r91ce,16r51b6,16r4e5f,16r9875,16r6396,16r4e1a,16r53f6,16r66f3,
+16r814b,16r591c,16r6db2,16r4e00,16r58f9,16r533b,16r63d6,16r94f1,
+16r4f9d,16r4f0a,16r8863,16r9890,16r5937,16r9057,16r79fb,16r4eea,
+16r80f0,16r7591,16r6c82,16r5b9c,16r59e8,16r5f5d,16r6905,16r8681,
+16r501a,16r5df2,16r4e59,16r77e3,16r4ee5,16r827a,16r6291,16r6613,
+16r9091,16r5c79,16r4ebf,16r5f79,16r81c6,16r9038,16r8084,16r75ab,
+16r4ea6,16r88d4,16r610f,16r6bc5,16r5fc6,16r4e49,16r76ca,16r6ea2,
+16r8be3,16r8bae,16r8c0a,16r8bd1,16r5f02,16r7ffc,16r7fcc,16r7ece,
+16r8335,16r836b,16r56e0,16r6bb7,16r97f3,16r9634,16r59fb,16r541f,
+16r94f6,16r6deb,16r5bc5,16r996e,16r5c39,16r5f15,16r9690,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r5370,16r82f1,16r6a31,
+16r5a74,16r9e70,16r5e94,16r7f28,16r83b9,16r8424,16r8425,16r8367,
+16r8747,16r8fce,16r8d62,16r76c8,16r5f71,16r9896,16r786c,16r6620,
+16r54df,16r62e5,16r4f63,16r81c3,16r75c8,16r5eb8,16r96cd,16r8e0a,
+16r86f9,16r548f,16r6cf3,16r6d8c,16r6c38,16r607f,16r52c7,16r7528,
+16r5e7d,16r4f18,16r60a0,16r5fe7,16r5c24,16r7531,16r90ae,16r94c0,
+16r72b9,16r6cb9,16r6e38,16r9149,16r6709,16r53cb,16r53f3,16r4f51,
+16r91c9,16r8bf1,16r53c8,16r5e7c,16r8fc2,16r6de4,16r4e8e,16r76c2,
+16r6986,16r865e,16r611a,16r8206,16r4f59,16r4fde,16r903e,16r9c7c,
+16r6109,16r6e1d,16r6e14,16r9685,16r4e88,16r5a31,16r96e8,16r4e0e,
+16r5c7f,16r79b9,16r5b87,16r8bed,16r7fbd,16r7389,16r57df,16r828b,
+16r90c1,16r5401,16r9047,16r55bb,16r5cea,16r5fa1,16r6108,16r6b32,
+16r72f1,16r80b2,16r8a89,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r6d74,16r5bd3,16r88d5,16r9884,16r8c6b,16r9a6d,16r9e33,
+16r6e0a,16r51a4,16r5143,16r57a3,16r8881,16r539f,16r63f4,16r8f95,
+16r56ed,16r5458,16r5706,16r733f,16r6e90,16r7f18,16r8fdc,16r82d1,
+16r613f,16r6028,16r9662,16r66f0,16r7ea6,16r8d8a,16r8dc3,16r94a5,
+16r5cb3,16r7ca4,16r6708,16r60a6,16r9605,16r8018,16r4e91,16r90e7,
+16r5300,16r9668,16r5141,16r8fd0,16r8574,16r915d,16r6655,16r97f5,
+16r5b55,16r531d,16r7838,16r6742,16r683d,16r54c9,16r707e,16r5bb0,
+16r8f7d,16r518d,16r5728,16r54b1,16r6512,16r6682,16r8d5e,16r8d43,
+16r810f,16r846c,16r906d,16r7cdf,16r51ff,16r85fb,16r67a3,16r65e9,
+16r6fa1,16r86a4,16r8e81,16r566a,16r9020,16r7682,16r7076,16r71e5,
+16r8d23,16r62e9,16r5219,16r6cfd,16r8d3c,16r600e,16r589e,16r618e,
+16r66fe,16r8d60,16r624e,16r55b3,16r6e23,16r672d,16r8f67,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r94e1,16r95f8,16r7728,
+16r6805,16r69a8,16r548b,16r4e4d,16r70b8,16r8bc8,16r6458,16r658b,
+16r5b85,16r7a84,16r503a,16r5be8,16r77bb,16r6be1,16r8a79,16r7c98,
+16r6cbe,16r76cf,16r65a9,16r8f97,16r5d2d,16r5c55,16r8638,16r6808,
+16r5360,16r6218,16r7ad9,16r6e5b,16r7efd,16r6a1f,16r7ae0,16r5f70,
+16r6f33,16r5f20,16r638c,16r6da8,16r6756,16r4e08,16r5e10,16r8d26,
+16r4ed7,16r80c0,16r7634,16r969c,16r62db,16r662d,16r627e,16r6cbc,
+16r8d75,16r7167,16r7f69,16r5146,16r8087,16r53ec,16r906e,16r6298,
+16r54f2,16r86f0,16r8f99,16r8005,16r9517,16r8517,16r8fd9,16r6d59,
+16r73cd,16r659f,16r771f,16r7504,16r7827,16r81fb,16r8d1e,16r9488,
+16r4fa6,16r6795,16r75b9,16r8bca,16r9707,16r632f,16r9547,16r9635,
+16r84b8,16r6323,16r7741,16r5f81,16r72f0,16r4e89,16r6014,16r6574,
+16r62ef,16r6b63,16r653f,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r5e27,16r75c7,16r90d1,16r8bc1,16r829d,16r679d,16r652f,
+16r5431,16r8718,16r77e5,16r80a2,16r8102,16r6c41,16r4e4b,16r7ec7,
+16r804c,16r76f4,16r690d,16r6b96,16r6267,16r503c,16r4f84,16r5740,
+16r6307,16r6b62,16r8dbe,16r53ea,16r65e8,16r7eb8,16r5fd7,16r631a,
+16r63b7,16r81f3,16r81f4,16r7f6e,16r5e1c,16r5cd9,16r5236,16r667a,
+16r79e9,16r7a1a,16r8d28,16r7099,16r75d4,16r6ede,16r6cbb,16r7a92,
+16r4e2d,16r76c5,16r5fe0,16r949f,16r8877,16r7ec8,16r79cd,16r80bf,
+16r91cd,16r4ef2,16r4f17,16r821f,16r5468,16r5dde,16r6d32,16r8bcc,
+16r7ca5,16r8f74,16r8098,16r5e1a,16r5492,16r76b1,16r5b99,16r663c,
+16r9aa4,16r73e0,16r682a,16r86db,16r6731,16r732a,16r8bf8,16r8bdb,
+16r9010,16r7af9,16r70db,16r716e,16r62c4,16r77a9,16r5631,16r4e3b,
+16r8457,16r67f1,16r52a9,16r86c0,16r8d2e,16r94f8,16r7b51,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r4f4f,16r6ce8,16r795d,
+16r9a7b,16r6293,16r722a,16r62fd,16r4e13,16r7816,16r8f6c,16r64b0,
+16r8d5a,16r7bc6,16r6869,16r5e84,16r88c5,16r5986,16r649e,16r58ee,
+16r72b6,16r690e,16r9525,16r8ffd,16r8d58,16r5760,16r7f00,16r8c06,
+16r51c6,16r6349,16r62d9,16r5353,16r684c,16r7422,16r8301,16r914c,
+16r5544,16r7740,16r707c,16r6d4a,16r5179,16r54a8,16r8d44,16r59ff,
+16r6ecb,16r6dc4,16r5b5c,16r7d2b,16r4ed4,16r7c7d,16r6ed3,16r5b50,
+16r81ea,16r6e0d,16r5b57,16r9b03,16r68d5,16r8e2a,16r5b97,16r7efc,
+16r603b,16r7eb5,16r90b9,16r8d70,16r594f,16r63cd,16r79df,16r8db3,
+16r5352,16r65cf,16r7956,16r8bc5,16r963b,16r7ec4,16r94bb,16r7e82,
+16r5634,16r9189,16r6700,16r7f6a,16r5c0a,16r9075,16r6628,16r5de6,
+16r4f50,16r67de,16r505a,16r4f5c,16r5750,16r5ea7,ERRchar,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r4e8d,16r4e0c,16r5140,16r4e10,16r5eff,16r5345,16r4e15,
+16r4e98,16r4e1e,16r9b32,16r5b6c,16r5669,16r4e28,16r79ba,16r4e3f,
+16r5315,16r4e47,16r592d,16r723b,16r536e,16r6c10,16r56df,16r80e4,
+16r9997,16r6bd3,16r777e,16r9f17,16r4e36,16r4e9f,16r9f10,16r4e5c,
+16r4e69,16r4e93,16r8288,16r5b5b,16r556c,16r560f,16r4ec4,16r538d,
+16r539d,16r53a3,16r53a5,16r53ae,16r9765,16r8d5d,16r531a,16r53f5,
+16r5326,16r532e,16r533e,16r8d5c,16r5366,16r5363,16r5202,16r5208,
+16r520e,16r522d,16r5233,16r523f,16r5240,16r524c,16r525e,16r5261,
+16r525c,16r84af,16r527d,16r5282,16r5281,16r5290,16r5293,16r5182,
+16r7f54,16r4ebb,16r4ec3,16r4ec9,16r4ec2,16r4ee8,16r4ee1,16r4eeb,
+16r4ede,16r4f1b,16r4ef3,16r4f22,16r4f64,16r4ef5,16r4f25,16r4f27,
+16r4f09,16r4f2b,16r4f5e,16r4f67,16r6538,16r4f5a,16r4f5d,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r4f5f,16r4f57,16r4f32,
+16r4f3d,16r4f76,16r4f74,16r4f91,16r4f89,16r4f83,16r4f8f,16r4f7e,
+16r4f7b,16r4faa,16r4f7c,16r4fac,16r4f94,16r4fe6,16r4fe8,16r4fea,
+16r4fc5,16r4fda,16r4fe3,16r4fdc,16r4fd1,16r4fdf,16r4ff8,16r5029,
+16r504c,16r4ff3,16r502c,16r500f,16r502e,16r502d,16r4ffe,16r501c,
+16r500c,16r5025,16r5028,16r507e,16r5043,16r5055,16r5048,16r504e,
+16r506c,16r507b,16r50a5,16r50a7,16r50a9,16r50ba,16r50d6,16r5106,
+16r50ed,16r50ec,16r50e6,16r50ee,16r5107,16r510b,16r4edd,16r6c3d,
+16r4f58,16r4f65,16r4fce,16r9fa0,16r6c46,16r7c74,16r516e,16r5dfd,
+16r9ec9,16r9998,16r5181,16r5914,16r52f9,16r530d,16r8a07,16r5310,
+16r51eb,16r5919,16r5155,16r4ea0,16r5156,16r4eb3,16r886e,16r88a4,
+16r4eb5,16r8114,16r88d2,16r7980,16r5b34,16r8803,16r7fb8,16r51ab,
+16r51b1,16r51bd,16r51bc,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r51c7,16r5196,16r51a2,16r51a5,16r8ba0,16r8ba6,16r8ba7,
+16r8baa,16r8bb4,16r8bb5,16r8bb7,16r8bc2,16r8bc3,16r8bcb,16r8bcf,
+16r8bce,16r8bd2,16r8bd3,16r8bd4,16r8bd6,16r8bd8,16r8bd9,16r8bdc,
+16r8bdf,16r8be0,16r8be4,16r8be8,16r8be9,16r8bee,16r8bf0,16r8bf3,
+16r8bf6,16r8bf9,16r8bfc,16r8bff,16r8c00,16r8c02,16r8c04,16r8c07,
+16r8c0c,16r8c0f,16r8c11,16r8c12,16r8c14,16r8c15,16r8c16,16r8c19,
+16r8c1b,16r8c18,16r8c1d,16r8c1f,16r8c20,16r8c21,16r8c25,16r8c27,
+16r8c2a,16r8c2b,16r8c2e,16r8c2f,16r8c32,16r8c33,16r8c35,16r8c36,
+16r5369,16r537a,16r961d,16r9622,16r9621,16r9631,16r962a,16r963d,
+16r963c,16r9642,16r9649,16r9654,16r965f,16r9667,16r966c,16r9672,
+16r9674,16r9688,16r968d,16r9697,16r96b0,16r9097,16r909b,16r909d,
+16r9099,16r90ac,16r90a1,16r90b4,16r90b3,16r90b6,16r90ba,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r90b8,16r90b0,16r90cf,
+16r90c5,16r90be,16r90d0,16r90c4,16r90c7,16r90d3,16r90e6,16r90e2,
+16r90dc,16r90d7,16r90db,16r90eb,16r90ef,16r90fe,16r9104,16r9122,
+16r911e,16r9123,16r9131,16r912f,16r9139,16r9143,16r9146,16r520d,
+16r5942,16r52a2,16r52ac,16r52ad,16r52be,16r54ff,16r52d0,16r52d6,
+16r52f0,16r53df,16r71ee,16r77cd,16r5ef4,16r51f5,16r51fc,16r9b2f,
+16r53b6,16r5f01,16r755a,16r5def,16r574c,16r57a9,16r57a1,16r587e,
+16r58bc,16r58c5,16r58d1,16r5729,16r572c,16r572a,16r5733,16r5739,
+16r572e,16r572f,16r575c,16r573b,16r5742,16r5769,16r5785,16r576b,
+16r5786,16r577c,16r577b,16r5768,16r576d,16r5776,16r5773,16r57ad,
+16r57a4,16r578c,16r57b2,16r57cf,16r57a7,16r57b4,16r5793,16r57a0,
+16r57d5,16r57d8,16r57da,16r57d9,16r57d2,16r57b8,16r57f4,16r57ef,
+16r57f8,16r57e4,16r57dd,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r580b,16r580d,16r57fd,16r57ed,16r5800,16r581e,16r5819,
+16r5844,16r5820,16r5865,16r586c,16r5881,16r5889,16r589a,16r5880,
+16r99a8,16r9f19,16r61ff,16r8279,16r827d,16r827f,16r828f,16r828a,
+16r82a8,16r8284,16r828e,16r8291,16r8297,16r8299,16r82ab,16r82b8,
+16r82be,16r82b0,16r82c8,16r82ca,16r82e3,16r8298,16r82b7,16r82ae,
+16r82cb,16r82cc,16r82c1,16r82a9,16r82b4,16r82a1,16r82aa,16r829f,
+16r82c4,16r82ce,16r82a4,16r82e1,16r8309,16r82f7,16r82e4,16r830f,
+16r8307,16r82dc,16r82f4,16r82d2,16r82d8,16r830c,16r82fb,16r82d3,
+16r8311,16r831a,16r8306,16r8314,16r8315,16r82e0,16r82d5,16r831c,
+16r8351,16r835b,16r835c,16r8308,16r8392,16r833c,16r8334,16r8331,
+16r839b,16r835e,16r832f,16r834f,16r8347,16r8343,16r835f,16r8340,
+16r8317,16r8360,16r832d,16r833a,16r8333,16r8366,16r8365,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r8368,16r831b,16r8369,
+16r836c,16r836a,16r836d,16r836e,16r83b0,16r8378,16r83b3,16r83b4,
+16r83a0,16r83aa,16r8393,16r839c,16r8385,16r837c,16r83b6,16r83a9,
+16r837d,16r83b8,16r837b,16r8398,16r839e,16r83a8,16r83ba,16r83bc,
+16r83c1,16r8401,16r83e5,16r83d8,16r5807,16r8418,16r840b,16r83dd,
+16r83fd,16r83d6,16r841c,16r8438,16r8411,16r8406,16r83d4,16r83df,
+16r840f,16r8403,16r83f8,16r83f9,16r83ea,16r83c5,16r83c0,16r8426,
+16r83f0,16r83e1,16r845c,16r8451,16r845a,16r8459,16r8473,16r8487,
+16r8488,16r847a,16r8489,16r8478,16r843c,16r8446,16r8469,16r8476,
+16r848c,16r848e,16r8431,16r846d,16r84c1,16r84cd,16r84d0,16r84e6,
+16r84bd,16r84d3,16r84ca,16r84bf,16r84ba,16r84e0,16r84a1,16r84b9,
+16r84b4,16r8497,16r84e5,16r84e3,16r850c,16r750d,16r8538,16r84f0,
+16r8539,16r851f,16r853a,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r8556,16r853b,16r84ff,16r84fc,16r8559,16r8548,16r8568,
+16r8564,16r855e,16r857a,16r77a2,16r8543,16r8572,16r857b,16r85a4,
+16r85a8,16r8587,16r858f,16r8579,16r85ae,16r859c,16r8585,16r85b9,
+16r85b7,16r85b0,16r85d3,16r85c1,16r85dc,16r85ff,16r8627,16r8605,
+16r8629,16r8616,16r863c,16r5efe,16r5f08,16r593c,16r5941,16r8037,
+16r5955,16r595a,16r5958,16r530f,16r5c22,16r5c25,16r5c2c,16r5c34,
+16r624c,16r626a,16r629f,16r62bb,16r62ca,16r62da,16r62d7,16r62ee,
+16r6322,16r62f6,16r6339,16r634b,16r6343,16r63ad,16r63f6,16r6371,
+16r637a,16r638e,16r63b4,16r636d,16r63ac,16r638a,16r6369,16r63ae,
+16r63bc,16r63f2,16r63f8,16r63e0,16r63ff,16r63c4,16r63de,16r63ce,
+16r6452,16r63c6,16r63be,16r6445,16r6441,16r640b,16r641b,16r6420,
+16r640c,16r6426,16r6421,16r645e,16r6484,16r646d,16r6496,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r647a,16r64b7,16r64b8,
+16r6499,16r64ba,16r64c0,16r64d0,16r64d7,16r64e4,16r64e2,16r6509,
+16r6525,16r652e,16r5f0b,16r5fd2,16r7519,16r5f11,16r535f,16r53f1,
+16r53fd,16r53e9,16r53e8,16r53fb,16r5412,16r5416,16r5406,16r544b,
+16r5452,16r5453,16r5454,16r5456,16r5443,16r5421,16r5457,16r5459,
+16r5423,16r5432,16r5482,16r5494,16r5477,16r5471,16r5464,16r549a,
+16r549b,16r5484,16r5476,16r5466,16r549d,16r54d0,16r54ad,16r54c2,
+16r54b4,16r54d2,16r54a7,16r54a6,16r54d3,16r54d4,16r5472,16r54a3,
+16r54d5,16r54bb,16r54bf,16r54cc,16r54d9,16r54da,16r54dc,16r54a9,
+16r54aa,16r54a4,16r54dd,16r54cf,16r54de,16r551b,16r54e7,16r5520,
+16r54fd,16r5514,16r54f3,16r5522,16r5523,16r550f,16r5511,16r5527,
+16r552a,16r5567,16r558f,16r55b5,16r5549,16r556d,16r5541,16r5555,
+16r553f,16r5550,16r553c,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r5537,16r5556,16r5575,16r5576,16r5577,16r5533,16r5530,
+16r555c,16r558b,16r55d2,16r5583,16r55b1,16r55b9,16r5588,16r5581,
+16r559f,16r557e,16r55d6,16r5591,16r557b,16r55df,16r55bd,16r55be,
+16r5594,16r5599,16r55ea,16r55f7,16r55c9,16r561f,16r55d1,16r55eb,
+16r55ec,16r55d4,16r55e6,16r55dd,16r55c4,16r55ef,16r55e5,16r55f2,
+16r55f3,16r55cc,16r55cd,16r55e8,16r55f5,16r55e4,16r8f94,16r561e,
+16r5608,16r560c,16r5601,16r5624,16r5623,16r55fe,16r5600,16r5627,
+16r562d,16r5658,16r5639,16r5657,16r562c,16r564d,16r5662,16r5659,
+16r565c,16r564c,16r5654,16r5686,16r5664,16r5671,16r566b,16r567b,
+16r567c,16r5685,16r5693,16r56af,16r56d4,16r56d7,16r56dd,16r56e1,
+16r56f5,16r56eb,16r56f9,16r56ff,16r5704,16r570a,16r5709,16r571c,
+16r5e0f,16r5e19,16r5e14,16r5e11,16r5e31,16r5e3b,16r5e3c,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r5e37,16r5e44,16r5e54,
+16r5e5b,16r5e5e,16r5e61,16r5c8c,16r5c7a,16r5c8d,16r5c90,16r5c96,
+16r5c88,16r5c98,16r5c99,16r5c91,16r5c9a,16r5c9c,16r5cb5,16r5ca2,
+16r5cbd,16r5cac,16r5cab,16r5cb1,16r5ca3,16r5cc1,16r5cb7,16r5cc4,
+16r5cd2,16r5ce4,16r5ccb,16r5ce5,16r5d02,16r5d03,16r5d27,16r5d26,
+16r5d2e,16r5d24,16r5d1e,16r5d06,16r5d1b,16r5d58,16r5d3e,16r5d34,
+16r5d3d,16r5d6c,16r5d5b,16r5d6f,16r5d5d,16r5d6b,16r5d4b,16r5d4a,
+16r5d69,16r5d74,16r5d82,16r5d99,16r5d9d,16r8c73,16r5db7,16r5dc5,
+16r5f73,16r5f77,16r5f82,16r5f87,16r5f89,16r5f8c,16r5f95,16r5f99,
+16r5f9c,16r5fa8,16r5fad,16r5fb5,16r5fbc,16r8862,16r5f61,16r72ad,
+16r72b0,16r72b4,16r72b7,16r72b8,16r72c3,16r72c1,16r72ce,16r72cd,
+16r72d2,16r72e8,16r72ef,16r72e9,16r72f2,16r72f4,16r72f7,16r7301,
+16r72f3,16r7303,16r72fa,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r72fb,16r7317,16r7313,16r7321,16r730a,16r731e,16r731d,
+16r7315,16r7322,16r7339,16r7325,16r732c,16r7338,16r7331,16r7350,
+16r734d,16r7357,16r7360,16r736c,16r736f,16r737e,16r821b,16r5925,
+16r98e7,16r5924,16r5902,16r9963,16r9967,16r9968,16r9969,16r996a,
+16r996b,16r996c,16r9974,16r9977,16r997d,16r9980,16r9984,16r9987,
+16r998a,16r998d,16r9990,16r9991,16r9993,16r9994,16r9995,16r5e80,
+16r5e91,16r5e8b,16r5e96,16r5ea5,16r5ea0,16r5eb9,16r5eb5,16r5ebe,
+16r5eb3,16r8d53,16r5ed2,16r5ed1,16r5edb,16r5ee8,16r5eea,16r81ba,
+16r5fc4,16r5fc9,16r5fd6,16r5fcf,16r6003,16r5fee,16r6004,16r5fe1,
+16r5fe4,16r5ffe,16r6005,16r6006,16r5fea,16r5fed,16r5ff8,16r6019,
+16r6035,16r6026,16r601b,16r600f,16r600d,16r6029,16r602b,16r600a,
+16r603f,16r6021,16r6078,16r6079,16r607b,16r607a,16r6042,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r606a,16r607d,16r6096,
+16r609a,16r60ad,16r609d,16r6083,16r6092,16r608c,16r609b,16r60ec,
+16r60bb,16r60b1,16r60dd,16r60d8,16r60c6,16r60da,16r60b4,16r6120,
+16r6126,16r6115,16r6123,16r60f4,16r6100,16r610e,16r612b,16r614a,
+16r6175,16r61ac,16r6194,16r61a7,16r61b7,16r61d4,16r61f5,16r5fdd,
+16r96b3,16r95e9,16r95eb,16r95f1,16r95f3,16r95f5,16r95f6,16r95fc,
+16r95fe,16r9603,16r9604,16r9606,16r9608,16r960a,16r960b,16r960c,
+16r960d,16r960f,16r9612,16r9615,16r9616,16r9617,16r9619,16r961a,
+16r4e2c,16r723f,16r6215,16r6c35,16r6c54,16r6c5c,16r6c4a,16r6ca3,
+16r6c85,16r6c90,16r6c94,16r6c8c,16r6c68,16r6c69,16r6c74,16r6c76,
+16r6c86,16r6ca9,16r6cd0,16r6cd4,16r6cad,16r6cf7,16r6cf8,16r6cf1,
+16r6cd7,16r6cb2,16r6ce0,16r6cd6,16r6cfa,16r6ceb,16r6cee,16r6cb1,
+16r6cd3,16r6cef,16r6cfe,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r6d39,16r6d27,16r6d0c,16r6d43,16r6d48,16r6d07,16r6d04,
+16r6d19,16r6d0e,16r6d2b,16r6d4d,16r6d2e,16r6d35,16r6d1a,16r6d4f,
+16r6d52,16r6d54,16r6d33,16r6d91,16r6d6f,16r6d9e,16r6da0,16r6d5e,
+16r6d93,16r6d94,16r6d5c,16r6d60,16r6d7c,16r6d63,16r6e1a,16r6dc7,
+16r6dc5,16r6dde,16r6e0e,16r6dbf,16r6de0,16r6e11,16r6de6,16r6ddd,
+16r6dd9,16r6e16,16r6dab,16r6e0c,16r6dae,16r6e2b,16r6e6e,16r6e4e,
+16r6e6b,16r6eb2,16r6e5f,16r6e86,16r6e53,16r6e54,16r6e32,16r6e25,
+16r6e44,16r6edf,16r6eb1,16r6e98,16r6ee0,16r6f2d,16r6ee2,16r6ea5,
+16r6ea7,16r6ebd,16r6ebb,16r6eb7,16r6ed7,16r6eb4,16r6ecf,16r6e8f,
+16r6ec2,16r6e9f,16r6f62,16r6f46,16r6f47,16r6f24,16r6f15,16r6ef9,
+16r6f2f,16r6f36,16r6f4b,16r6f74,16r6f2a,16r6f09,16r6f29,16r6f89,
+16r6f8d,16r6f8c,16r6f78,16r6f72,16r6f7c,16r6f7a,16r6fd1,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r6fc9,16r6fa7,16r6fb9,
+16r6fb6,16r6fc2,16r6fe1,16r6fee,16r6fde,16r6fe0,16r6fef,16r701a,
+16r7023,16r701b,16r7039,16r7035,16r704f,16r705e,16r5b80,16r5b84,
+16r5b95,16r5b93,16r5ba5,16r5bb8,16r752f,16r9a9e,16r6434,16r5be4,
+16r5bee,16r8930,16r5bf0,16r8e47,16r8b07,16r8fb6,16r8fd3,16r8fd5,
+16r8fe5,16r8fee,16r8fe4,16r8fe9,16r8fe6,16r8ff3,16r8fe8,16r9005,
+16r9004,16r900b,16r9026,16r9011,16r900d,16r9016,16r9021,16r9035,
+16r9036,16r902d,16r902f,16r9044,16r9051,16r9052,16r9050,16r9068,
+16r9058,16r9062,16r905b,16r66b9,16r9074,16r907d,16r9082,16r9088,
+16r9083,16r908b,16r5f50,16r5f57,16r5f56,16r5f58,16r5c3b,16r54ab,
+16r5c50,16r5c59,16r5b71,16r5c63,16r5c66,16r7fbc,16r5f2a,16r5f29,
+16r5f2d,16r8274,16r5f3c,16r9b3b,16r5c6e,16r5981,16r5983,16r598d,
+16r59a9,16r59aa,16r59a3,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r5997,16r59ca,16r59ab,16r599e,16r59a4,16r59d2,16r59b2,
+16r59af,16r59d7,16r59be,16r5a05,16r5a06,16r59dd,16r5a08,16r59e3,
+16r59d8,16r59f9,16r5a0c,16r5a09,16r5a32,16r5a34,16r5a11,16r5a23,
+16r5a13,16r5a40,16r5a67,16r5a4a,16r5a55,16r5a3c,16r5a62,16r5a75,
+16r80ec,16r5aaa,16r5a9b,16r5a77,16r5a7a,16r5abe,16r5aeb,16r5ab2,
+16r5ad2,16r5ad4,16r5ab8,16r5ae0,16r5ae3,16r5af1,16r5ad6,16r5ae6,
+16r5ad8,16r5adc,16r5b09,16r5b17,16r5b16,16r5b32,16r5b37,16r5b40,
+16r5c15,16r5c1c,16r5b5a,16r5b65,16r5b73,16r5b51,16r5b53,16r5b62,
+16r9a75,16r9a77,16r9a78,16r9a7a,16r9a7f,16r9a7d,16r9a80,16r9a81,
+16r9a85,16r9a88,16r9a8a,16r9a90,16r9a92,16r9a93,16r9a96,16r9a98,
+16r9a9b,16r9a9c,16r9a9d,16r9a9f,16r9aa0,16r9aa2,16r9aa3,16r9aa5,
+16r9aa7,16r7e9f,16r7ea1,16r7ea3,16r7ea5,16r7ea8,16r7ea9,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r7ead,16r7eb0,16r7ebe,
+16r7ec0,16r7ec1,16r7ec2,16r7ec9,16r7ecb,16r7ecc,16r7ed0,16r7ed4,
+16r7ed7,16r7edb,16r7ee0,16r7ee1,16r7ee8,16r7eeb,16r7eee,16r7eef,
+16r7ef1,16r7ef2,16r7f0d,16r7ef6,16r7efa,16r7efb,16r7efe,16r7f01,
+16r7f02,16r7f03,16r7f07,16r7f08,16r7f0b,16r7f0c,16r7f0f,16r7f11,
+16r7f12,16r7f17,16r7f19,16r7f1c,16r7f1b,16r7f1f,16r7f21,16r7f22,
+16r7f23,16r7f24,16r7f25,16r7f26,16r7f27,16r7f2a,16r7f2b,16r7f2c,
+16r7f2d,16r7f2f,16r7f30,16r7f31,16r7f32,16r7f33,16r7f35,16r5e7a,
+16r757f,16r5ddb,16r753e,16r9095,16r738e,16r7391,16r73ae,16r73a2,
+16r739f,16r73cf,16r73c2,16r73d1,16r73b7,16r73b3,16r73c0,16r73c9,
+16r73c8,16r73e5,16r73d9,16r987c,16r740a,16r73e9,16r73e7,16r73de,
+16r73ba,16r73f2,16r740f,16r742a,16r745b,16r7426,16r7425,16r7428,
+16r7430,16r742e,16r742c,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r741b,16r741a,16r7441,16r745c,16r7457,16r7455,16r7459,
+16r7477,16r746d,16r747e,16r749c,16r748e,16r7480,16r7481,16r7487,
+16r748b,16r749e,16r74a8,16r74a9,16r7490,16r74a7,16r74d2,16r74ba,
+16r97ea,16r97eb,16r97ec,16r674c,16r6753,16r675e,16r6748,16r6769,
+16r67a5,16r6787,16r676a,16r6773,16r6798,16r67a7,16r6775,16r67a8,
+16r679e,16r67ad,16r678b,16r6777,16r677c,16r67f0,16r6809,16r67d8,
+16r680a,16r67e9,16r67b0,16r680c,16r67d9,16r67b5,16r67da,16r67b3,
+16r67dd,16r6800,16r67c3,16r67b8,16r67e2,16r680e,16r67c1,16r67fd,
+16r6832,16r6833,16r6860,16r6861,16r684e,16r6862,16r6844,16r6864,
+16r6883,16r681d,16r6855,16r6866,16r6841,16r6867,16r6840,16r683e,
+16r684a,16r6849,16r6829,16r68b5,16r688f,16r6874,16r6877,16r6893,
+16r686b,16r68c2,16r696e,16r68fc,16r691f,16r6920,16r68f9,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r6924,16r68f0,16r690b,
+16r6901,16r6957,16r68e3,16r6910,16r6971,16r6939,16r6960,16r6942,
+16r695d,16r6984,16r696b,16r6980,16r6998,16r6978,16r6934,16r69cc,
+16r6987,16r6988,16r69ce,16r6989,16r6966,16r6963,16r6979,16r699b,
+16r69a7,16r69bb,16r69ab,16r69ad,16r69d4,16r69b1,16r69c1,16r69ca,
+16r69df,16r6995,16r69e0,16r698d,16r69ff,16r6a2f,16r69ed,16r6a17,
+16r6a18,16r6a65,16r69f2,16r6a44,16r6a3e,16r6aa0,16r6a50,16r6a5b,
+16r6a35,16r6a8e,16r6a79,16r6a3d,16r6a28,16r6a58,16r6a7c,16r6a91,
+16r6a90,16r6aa9,16r6a97,16r6aab,16r7337,16r7352,16r6b81,16r6b82,
+16r6b87,16r6b84,16r6b92,16r6b93,16r6b8d,16r6b9a,16r6b9b,16r6ba1,
+16r6baa,16r8f6b,16r8f6d,16r8f71,16r8f72,16r8f73,16r8f75,16r8f76,
+16r8f78,16r8f77,16r8f79,16r8f7a,16r8f7c,16r8f7e,16r8f81,16r8f82,
+16r8f84,16r8f87,16r8f8b,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r8f8d,16r8f8e,16r8f8f,16r8f98,16r8f9a,16r8ece,16r620b,
+16r6217,16r621b,16r621f,16r6222,16r6221,16r6225,16r6224,16r622c,
+16r81e7,16r74ef,16r74f4,16r74ff,16r750f,16r7511,16r7513,16r6534,
+16r65ee,16r65ef,16r65f0,16r660a,16r6619,16r6772,16r6603,16r6615,
+16r6600,16r7085,16r66f7,16r661d,16r6634,16r6631,16r6636,16r6635,
+16r8006,16r665f,16r6654,16r6641,16r664f,16r6656,16r6661,16r6657,
+16r6677,16r6684,16r668c,16r66a7,16r669d,16r66be,16r66db,16r66dc,
+16r66e6,16r66e9,16r8d32,16r8d33,16r8d36,16r8d3b,16r8d3d,16r8d40,
+16r8d45,16r8d46,16r8d48,16r8d49,16r8d47,16r8d4d,16r8d55,16r8d59,
+16r89c7,16r89ca,16r89cb,16r89cc,16r89ce,16r89cf,16r89d0,16r89d1,
+16r726e,16r729f,16r725d,16r7266,16r726f,16r727e,16r727f,16r7284,
+16r728b,16r728d,16r728f,16r7292,16r6308,16r6332,16r63b0,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r643f,16r64d8,16r8004,
+16r6bea,16r6bf3,16r6bfd,16r6bf5,16r6bf9,16r6c05,16r6c07,16r6c06,
+16r6c0d,16r6c15,16r6c18,16r6c19,16r6c1a,16r6c21,16r6c29,16r6c24,
+16r6c2a,16r6c32,16r6535,16r6555,16r656b,16r724d,16r7252,16r7256,
+16r7230,16r8662,16r5216,16r809f,16r809c,16r8093,16r80bc,16r670a,
+16r80bd,16r80b1,16r80ab,16r80ad,16r80b4,16r80b7,16r80e7,16r80e8,
+16r80e9,16r80ea,16r80db,16r80c2,16r80c4,16r80d9,16r80cd,16r80d7,
+16r6710,16r80dd,16r80eb,16r80f1,16r80f4,16r80ed,16r810d,16r810e,
+16r80f2,16r80fc,16r6715,16r8112,16r8c5a,16r8136,16r811e,16r812c,
+16r8118,16r8132,16r8148,16r814c,16r8153,16r8174,16r8159,16r815a,
+16r8171,16r8160,16r8169,16r817c,16r817d,16r816d,16r8167,16r584d,
+16r5ab5,16r8188,16r8182,16r8191,16r6ed5,16r81a3,16r81aa,16r81cc,
+16r6726,16r81ca,16r81bb,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r81c1,16r81a6,16r6b24,16r6b37,16r6b39,16r6b43,16r6b46,
+16r6b59,16r98d1,16r98d2,16r98d3,16r98d5,16r98d9,16r98da,16r6bb3,
+16r5f40,16r6bc2,16r89f3,16r6590,16r9f51,16r6593,16r65bc,16r65c6,
+16r65c4,16r65c3,16r65cc,16r65ce,16r65d2,16r65d6,16r7080,16r709c,
+16r7096,16r709d,16r70bb,16r70c0,16r70b7,16r70ab,16r70b1,16r70e8,
+16r70ca,16r7110,16r7113,16r7116,16r712f,16r7131,16r7173,16r715c,
+16r7168,16r7145,16r7172,16r714a,16r7178,16r717a,16r7198,16r71b3,
+16r71b5,16r71a8,16r71a0,16r71e0,16r71d4,16r71e7,16r71f9,16r721d,
+16r7228,16r706c,16r7118,16r7166,16r71b9,16r623e,16r623d,16r6243,
+16r6248,16r6249,16r793b,16r7940,16r7946,16r7949,16r795b,16r795c,
+16r7953,16r795a,16r7962,16r7957,16r7960,16r796f,16r7967,16r797a,
+16r7985,16r798a,16r799a,16r79a7,16r79b3,16r5fd1,16r5fd0,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r603c,16r605d,16r605a,
+16r6067,16r6041,16r6059,16r6063,16r60ab,16r6106,16r610d,16r615d,
+16r61a9,16r619d,16r61cb,16r61d1,16r6206,16r8080,16r807f,16r6c93,
+16r6cf6,16r6dfc,16r77f6,16r77f8,16r7800,16r7809,16r7817,16r7818,
+16r7811,16r65ab,16r782d,16r781c,16r781d,16r7839,16r783a,16r783b,
+16r781f,16r783c,16r7825,16r782c,16r7823,16r7829,16r784e,16r786d,
+16r7856,16r7857,16r7826,16r7850,16r7847,16r784c,16r786a,16r789b,
+16r7893,16r789a,16r7887,16r789c,16r78a1,16r78a3,16r78b2,16r78b9,
+16r78a5,16r78d4,16r78d9,16r78c9,16r78ec,16r78f2,16r7905,16r78f4,
+16r7913,16r7924,16r791e,16r7934,16r9f9b,16r9ef9,16r9efb,16r9efc,
+16r76f1,16r7704,16r770d,16r76f9,16r7707,16r7708,16r771a,16r7722,
+16r7719,16r772d,16r7726,16r7735,16r7738,16r7750,16r7751,16r7747,
+16r7743,16r775a,16r7768,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r7762,16r7765,16r777f,16r778d,16r777d,16r7780,16r778c,
+16r7791,16r779f,16r77a0,16r77b0,16r77b5,16r77bd,16r753a,16r7540,
+16r754e,16r754b,16r7548,16r755b,16r7572,16r7579,16r7583,16r7f58,
+16r7f61,16r7f5f,16r8a48,16r7f68,16r7f74,16r7f71,16r7f79,16r7f81,
+16r7f7e,16r76cd,16r76e5,16r8832,16r9485,16r9486,16r9487,16r948b,
+16r948a,16r948c,16r948d,16r948f,16r9490,16r9494,16r9497,16r9495,
+16r949a,16r949b,16r949c,16r94a3,16r94a4,16r94ab,16r94aa,16r94ad,
+16r94ac,16r94af,16r94b0,16r94b2,16r94b4,16r94b6,16r94b7,16r94b8,
+16r94b9,16r94ba,16r94bc,16r94bd,16r94bf,16r94c4,16r94c8,16r94c9,
+16r94ca,16r94cb,16r94cc,16r94cd,16r94ce,16r94d0,16r94d1,16r94d2,
+16r94d5,16r94d6,16r94d7,16r94d9,16r94d8,16r94db,16r94de,16r94df,
+16r94e0,16r94e2,16r94e4,16r94e5,16r94e7,16r94e8,16r94ea,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r94e9,16r94eb,16r94ee,
+16r94ef,16r94f3,16r94f4,16r94f5,16r94f7,16r94f9,16r94fc,16r94fd,
+16r94ff,16r9503,16r9502,16r9506,16r9507,16r9509,16r950a,16r950d,
+16r950e,16r950f,16r9512,16r9513,16r9514,16r9515,16r9516,16r9518,
+16r951b,16r951d,16r951e,16r951f,16r9522,16r952a,16r952b,16r9529,
+16r952c,16r9531,16r9532,16r9534,16r9536,16r9537,16r9538,16r953c,
+16r953e,16r953f,16r9542,16r9535,16r9544,16r9545,16r9546,16r9549,
+16r954c,16r954e,16r954f,16r9552,16r9553,16r9554,16r9556,16r9557,
+16r9558,16r9559,16r955b,16r955e,16r955f,16r955d,16r9561,16r9562,
+16r9564,16r9565,16r9566,16r9567,16r9568,16r9569,16r956a,16r956b,
+16r956c,16r956f,16r9571,16r9572,16r9573,16r953a,16r77e7,16r77ec,
+16r96c9,16r79d5,16r79ed,16r79e3,16r79eb,16r7a06,16r5d47,16r7a03,
+16r7a02,16r7a1e,16r7a14,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r7a39,16r7a37,16r7a51,16r9ecf,16r99a5,16r7a70,16r7688,
+16r768e,16r7693,16r7699,16r76a4,16r74de,16r74e0,16r752c,16r9e20,
+16r9e22,16r9e28,16r9e29,16r9e2a,16r9e2b,16r9e2c,16r9e32,16r9e31,
+16r9e36,16r9e38,16r9e37,16r9e39,16r9e3a,16r9e3e,16r9e41,16r9e42,
+16r9e44,16r9e46,16r9e47,16r9e48,16r9e49,16r9e4b,16r9e4c,16r9e4e,
+16r9e51,16r9e55,16r9e57,16r9e5a,16r9e5b,16r9e5c,16r9e5e,16r9e63,
+16r9e66,16r9e67,16r9e68,16r9e69,16r9e6a,16r9e6b,16r9e6c,16r9e71,
+16r9e6d,16r9e73,16r7592,16r7594,16r7596,16r75a0,16r759d,16r75ac,
+16r75a3,16r75b3,16r75b4,16r75b8,16r75c4,16r75b1,16r75b0,16r75c3,
+16r75c2,16r75d6,16r75cd,16r75e3,16r75e8,16r75e6,16r75e4,16r75eb,
+16r75e7,16r7603,16r75f1,16r75fc,16r75ff,16r7610,16r7600,16r7605,
+16r760c,16r7617,16r760a,16r7625,16r7618,16r7615,16r7619,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r761b,16r763c,16r7622,
+16r7620,16r7640,16r762d,16r7630,16r763f,16r7635,16r7643,16r763e,
+16r7633,16r764d,16r765e,16r7654,16r765c,16r7656,16r766b,16r766f,
+16r7fca,16r7ae6,16r7a78,16r7a79,16r7a80,16r7a86,16r7a88,16r7a95,
+16r7aa6,16r7aa0,16r7aac,16r7aa8,16r7aad,16r7ab3,16r8864,16r8869,
+16r8872,16r887d,16r887f,16r8882,16r88a2,16r88c6,16r88b7,16r88bc,
+16r88c9,16r88e2,16r88ce,16r88e3,16r88e5,16r88f1,16r891a,16r88fc,
+16r88e8,16r88fe,16r88f0,16r8921,16r8919,16r8913,16r891b,16r890a,
+16r8934,16r892b,16r8936,16r8941,16r8966,16r897b,16r758b,16r80e5,
+16r76b2,16r76b4,16r77dc,16r8012,16r8014,16r8016,16r801c,16r8020,
+16r8022,16r8025,16r8026,16r8027,16r8029,16r8028,16r8031,16r800b,
+16r8035,16r8043,16r8046,16r804d,16r8052,16r8069,16r8071,16r8983,
+16r9878,16r9880,16r9883,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r9889,16r988c,16r988d,16r988f,16r9894,16r989a,16r989b,
+16r989e,16r989f,16r98a1,16r98a2,16r98a5,16r98a6,16r864d,16r8654,
+16r866c,16r866e,16r867f,16r867a,16r867c,16r867b,16r86a8,16r868d,
+16r868b,16r86ac,16r869d,16r86a7,16r86a3,16r86aa,16r8693,16r86a9,
+16r86b6,16r86c4,16r86b5,16r86ce,16r86b0,16r86ba,16r86b1,16r86af,
+16r86c9,16r86cf,16r86b4,16r86e9,16r86f1,16r86f2,16r86ed,16r86f3,
+16r86d0,16r8713,16r86de,16r86f4,16r86df,16r86d8,16r86d1,16r8703,
+16r8707,16r86f8,16r8708,16r870a,16r870d,16r8709,16r8723,16r873b,
+16r871e,16r8725,16r872e,16r871a,16r873e,16r8748,16r8734,16r8731,
+16r8729,16r8737,16r873f,16r8782,16r8722,16r877d,16r877e,16r877b,
+16r8760,16r8770,16r874c,16r876e,16r878b,16r8753,16r8763,16r877c,
+16r8764,16r8759,16r8765,16r8793,16r87af,16r87a8,16r87d2,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r87c6,16r8788,16r8785,
+16r87ad,16r8797,16r8783,16r87ab,16r87e5,16r87ac,16r87b5,16r87b3,
+16r87cb,16r87d3,16r87bd,16r87d1,16r87c0,16r87ca,16r87db,16r87ea,
+16r87e0,16r87ee,16r8816,16r8813,16r87fe,16r880a,16r881b,16r8821,
+16r8839,16r883c,16r7f36,16r7f42,16r7f44,16r7f45,16r8210,16r7afa,
+16r7afd,16r7b08,16r7b03,16r7b04,16r7b15,16r7b0a,16r7b2b,16r7b0f,
+16r7b47,16r7b38,16r7b2a,16r7b19,16r7b2e,16r7b31,16r7b20,16r7b25,
+16r7b24,16r7b33,16r7b3e,16r7b1e,16r7b58,16r7b5a,16r7b45,16r7b75,
+16r7b4c,16r7b5d,16r7b60,16r7b6e,16r7b7b,16r7b62,16r7b72,16r7b71,
+16r7b90,16r7ba6,16r7ba7,16r7bb8,16r7bac,16r7b9d,16r7ba8,16r7b85,
+16r7baa,16r7b9c,16r7ba2,16r7bab,16r7bb4,16r7bd1,16r7bc1,16r7bcc,
+16r7bdd,16r7bda,16r7be5,16r7be6,16r7bea,16r7c0c,16r7bfe,16r7bfc,
+16r7c0f,16r7c16,16r7c0b,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r7c1f,16r7c2a,16r7c26,16r7c38,16r7c41,16r7c40,16r81fe,
+16r8201,16r8202,16r8204,16r81ec,16r8844,16r8221,16r8222,16r8223,
+16r822d,16r822f,16r8228,16r822b,16r8238,16r823b,16r8233,16r8234,
+16r823e,16r8244,16r8249,16r824b,16r824f,16r825a,16r825f,16r8268,
+16r887e,16r8885,16r8888,16r88d8,16r88df,16r895e,16r7f9d,16r7f9f,
+16r7fa7,16r7faf,16r7fb0,16r7fb2,16r7c7c,16r6549,16r7c91,16r7c9d,
+16r7c9c,16r7c9e,16r7ca2,16r7cb2,16r7cbc,16r7cbd,16r7cc1,16r7cc7,
+16r7ccc,16r7ccd,16r7cc8,16r7cc5,16r7cd7,16r7ce8,16r826e,16r66a8,
+16r7fbf,16r7fce,16r7fd5,16r7fe5,16r7fe1,16r7fe6,16r7fe9,16r7fee,
+16r7ff3,16r7cf8,16r7d77,16r7da6,16r7dae,16r7e47,16r7e9b,16r9eb8,
+16r9eb4,16r8d73,16r8d84,16r8d94,16r8d91,16r8db1,16r8d67,16r8d6d,
+16r8c47,16r8c49,16r914a,16r9150,16r914e,16r914f,16r9164,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r9162,16r9161,16r9170,
+16r9169,16r916f,16r917d,16r917e,16r9172,16r9174,16r9179,16r918c,
+16r9185,16r9190,16r918d,16r9191,16r91a2,16r91a3,16r91aa,16r91ad,
+16r91ae,16r91af,16r91b5,16r91b4,16r91ba,16r8c55,16r9e7e,16r8db8,
+16r8deb,16r8e05,16r8e59,16r8e69,16r8db5,16r8dbf,16r8dbc,16r8dba,
+16r8dc4,16r8dd6,16r8dd7,16r8dda,16r8dde,16r8dce,16r8dcf,16r8ddb,
+16r8dc6,16r8dec,16r8df7,16r8df8,16r8de3,16r8df9,16r8dfb,16r8de4,
+16r8e09,16r8dfd,16r8e14,16r8e1d,16r8e1f,16r8e2c,16r8e2e,16r8e23,
+16r8e2f,16r8e3a,16r8e40,16r8e39,16r8e35,16r8e3d,16r8e31,16r8e49,
+16r8e41,16r8e42,16r8e51,16r8e52,16r8e4a,16r8e70,16r8e76,16r8e7c,
+16r8e6f,16r8e74,16r8e85,16r8e8f,16r8e94,16r8e90,16r8e9c,16r8e9e,
+16r8c78,16r8c82,16r8c8a,16r8c85,16r8c98,16r8c94,16r659b,16r89d6,
+16r89de,16r89da,16r89dc,ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,
+ERRchar,16r89e5,16r89eb,16r89ef,16r8a3e,16r8b26,16r9753,16r96e9,
+16r96f3,16r96ef,16r9706,16r9701,16r9708,16r970f,16r970e,16r972a,
+16r972d,16r9730,16r973e,16r9f80,16r9f83,16r9f85,16r9f86,16r9f87,
+16r9f88,16r9f89,16r9f8a,16r9f8c,16r9efe,16r9f0b,16r9f0d,16r96b9,
+16r96bc,16r96bd,16r96ce,16r96d2,16r77bf,16r96e0,16r928e,16r92ae,
+16r92c8,16r933e,16r936a,16r93ca,16r938f,16r943e,16r946b,16r9c7f,
+16r9c82,16r9c85,16r9c86,16r9c87,16r9c88,16r7a23,16r9c8b,16r9c8e,
+16r9c90,16r9c91,16r9c92,16r9c94,16r9c95,16r9c9a,16r9c9b,16r9c9e,
+16r9c9f,16r9ca0,16r9ca1,16r9ca2,16r9ca3,16r9ca5,16r9ca6,16r9ca7,
+16r9ca8,16r9ca9,16r9cab,16r9cad,16r9cae,16r9cb0,16r9cb1,16r9cb2,
+16r9cb3,16r9cb4,16r9cb5,16r9cb6,16r9cb7,16r9cba,16r9cbb,16r9cbc,
+16r9cbd,16r9cc4,16r9cc5,16r9cc6,16r9cc7,16r9cca,16r9ccb,ERRchar,
+ERRchar,ERRchar,ERRchar,ERRchar,ERRchar,16r9ccc,16r9ccd,16r9cce,
+16r9ccf,16r9cd0,16r9cd3,16r9cd4,16r9cd5,16r9cd7,16r9cd8,16r9cd9,
+16r9cdc,16r9cdd,16r9cdf,16r9ce2,16r977c,16r9785,16r9791,16r9792,
+16r9794,16r97af,16r97ab,16r97a3,16r97b2,16r97b4,16r9ab1,16r9ab0,
+16r9ab7,16r9e58,16r9ab6,16r9aba,16r9abc,16r9ac1,16r9ac0,16r9ac5,
+16r9ac2,16r9acb,16r9acc,16r9ad1,16r9b45,16r9b43,16r9b47,16r9b49,
+16r9b48,16r9b4d,16r9b51,16r98e8,16r990d,16r992e,16r9955,16r9954,
+16r9adf,16r9ae1,16r9ae6,16r9aef,16r9aeb,16r9afb,16r9aed,16r9af9,
+16r9b08,16r9b0f,16r9b13,16r9b1f,16r9b23,16r9ebd,16r9ebe,16r7e3b,
+16r9e82,16r9e87,16r9e88,16r9e8b,16r9e92,16r93d6,16r9e9d,16r9e9f,
+16r9edb,16r9edc,16r9edd,16r9ee0,16r9edf,16r9ee2,16r9ee9,16r9ee7,
+16r9ee5,16r9eea,16r9eef,16r9f22,16r9f2c,16r9f2f,16r9f39,16r9f37,
+16r9f3d,16r9f3e,16r9f44,
+};
diff --git a/appl/lib/convcs/genjisx0201kana.b b/appl/lib/convcs/genjisx0201kana.b
new file mode 100644
index 00000000..29c67f02
--- /dev/null
+++ b/appl/lib/convcs/genjisx0201kana.b
@@ -0,0 +1,117 @@
+implement genjisx0201kana;
+
+include "sys.m";
+include "draw.m";
+
+genjisx0201kana : module {
+ init: fn(ctxt: ref Draw->Context, args: list of string);
+};
+
+DATAFILE : con "/lib/convcs/jisx0201kana";
+
+init(nil: ref Draw->Context, nil: list of string)
+{
+ sys := load Sys Sys->PATH;
+ fd := sys->create(DATAFILE, Sys->OWRITE, 8r644);
+ if (fd == nil) {
+ sys->print("cannot create %s: %r\n", DATAFILE);
+ return;
+ }
+
+ s := "";
+ for (slen := 0; slen < len mapdata; slen ++) {
+ (nil, code) := sys->tokenize(mapdata[slen], " \t");
+ u := hex2int(hd tl code);
+ s[slen] = u;
+ }
+ buf := array of byte s;
+ sys->write(fd, buf, len buf);
+}
+
+hex2int(s: string): int
+{
+ n := 0;
+ for (i := 0; i < len s; i++) {
+ case s[i] {
+ '0' to '9' =>
+ n = 16*n + s[i] - '0';
+ 'A' to 'F' =>
+ n = 16*n + s[i] + 10 - 'A';
+ 'a' to 'f' =>
+ n = 16*n + s[i] + 10 - 'a';
+ * =>
+ return n;
+ }
+ }
+ return n;
+}
+
+
+# data derived from Unicode Consortium "CharmapML" data for EUC-JP
+# (G2 charset of EUC-JP is JIS X 0201 Kana)
+# the leading code point value is not used, it just appears for convenience
+mapdata := array [] of {
+ "A1 FF61",
+ "A2 FF62",
+ "A3 FF63",
+ "A4 FF64",
+ "A5 FF65",
+ "A6 FF66",
+ "A7 FF67",
+ "A8 FF68",
+ "A9 FF69",
+ "AA FF6A",
+ "AB FF6B",
+ "AC FF6C",
+ "AD FF6D",
+ "AE FF6E",
+ "AF FF6F",
+ "B0 FF70",
+ "B1 FF71",
+ "B2 FF72",
+ "B3 FF73",
+ "B4 FF74",
+ "B5 FF75",
+ "B6 FF76",
+ "B7 FF77",
+ "B8 FF78",
+ "B9 FF79",
+ "BA FF7A",
+ "BB FF7B",
+ "BC FF7C",
+ "BD FF7D",
+ "BE FF7E",
+ "BF FF7F",
+ "C0 FF80",
+ "C1 FF81",
+ "C2 FF82",
+ "C3 FF83",
+ "C4 FF84",
+ "C5 FF85",
+ "C6 FF86",
+ "C7 FF87",
+ "C8 FF88",
+ "C9 FF89",
+ "CA FF8A",
+ "CB FF8B",
+ "CC FF8C",
+ "CD FF8D",
+ "CE FF8E",
+ "CF FF8F",
+ "D0 FF90",
+ "D1 FF91",
+ "D2 FF92",
+ "D3 FF93",
+ "D4 FF94",
+ "D5 FF95",
+ "D6 FF96",
+ "D7 FF97",
+ "D8 FF98",
+ "D9 FF99",
+ "DA FF9A",
+ "DB FF9B",
+ "DC FF9C",
+ "DD FF9D",
+ "DE FF9E",
+ "DF FF9F",
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/genjisx0208-1997.b b/appl/lib/convcs/genjisx0208-1997.b
new file mode 100644
index 00000000..71898f94
--- /dev/null
+++ b/appl/lib/convcs/genjisx0208-1997.b
@@ -0,0 +1,6958 @@
+implement genjisx0208;
+
+include "sys.m";
+include "draw.m";
+
+genjisx0208 : module {
+ init: fn(ctxt: ref Draw->Context, args: list of string);
+};
+
+DATAFILE : con "/lib/convcs/jisx0208-1997";
+
+PAGESZ : con 94;
+NPAGES : con 84;
+PAGE0 : con 16rA1;
+CHAR0 : con 16rA1;
+
+BADCHAR : con 16rFFFD;
+
+init(nil: ref Draw->Context, nil: list of string)
+{
+ sys := load Sys Sys->PATH;
+ fd := sys->create(DATAFILE, Sys->OWRITE, 8r644);
+ if (fd == nil) {
+ sys->print("cannot create %s: %r\n", DATAFILE);
+ return;
+ }
+
+ pages := array [NPAGES * PAGESZ] of { * => BADCHAR };
+
+ for (i := 0; i < len data; i++) {
+ (nil, toks) := sys->tokenize(data[i], "\t");
+ (bytes, ucode) := (hd toks, hd tl toks);
+ u := hex2int(ucode);
+ (nil, blist) := sys->tokenize(bytes, " ");
+ b1 := hex2int(hd blist);
+ b2 := hex2int(hd tl blist);
+
+ page := b1 - PAGE0;
+ char := b2 - CHAR0;
+
+ pages[(page * PAGESZ)+char] = u;
+ }
+
+ s := "";
+ for (i = 0; i < len pages; i++)
+ s[i] = pages[i];
+ pages = nil;
+ buf := array of byte s;
+ sys->write(fd, buf, len buf);
+}
+
+
+hex2int(s: string): int
+{
+ n := 0;
+ for (i := 0; i < len s; i++) {
+ case s[i] {
+ '0' to '9' =>
+ n = 16*n + s[i] - '0';
+ 'A' to 'F' =>
+ n = 16*n + s[i] + 10 - 'A';
+ 'a' to 'f' =>
+ n = 16*n + s[i] + 10 - 'a';
+ * =>
+ return n;
+ }
+ }
+ return n;
+}
+
+
+# Data is derived from Unicode Consortium "CharmapML" mapping data
+# for EUC-JP.
+# JISX0208 appears as charset G1 of the ISO-2022 based EUC-JP encoding
+# Although this format is not convenient for building our mapping data file
+# it is easily extracted from the Unicode Consortium data.
+
+data := array [] of {
+ "A1 F1 00A2",
+ "A1 F2 00A3",
+ "A1 F8 00A7",
+ "A1 AF 00A8",
+ "A2 CC 00AC",
+ "A1 EB 00B0",
+ "A1 DE 00B1",
+ "A1 AD 00B4",
+ "A2 F9 00B6",
+ "A1 DF 00D7",
+ "A1 E0 00F7",
+ "A6 A1 0391",
+ "A6 A2 0392",
+ "A6 A3 0393",
+ "A6 A4 0394",
+ "A6 A5 0395",
+ "A6 A6 0396",
+ "A6 A7 0397",
+ "A6 A8 0398",
+ "A6 A9 0399",
+ "A6 AA 039A",
+ "A6 AB 039B",
+ "A6 AC 039C",
+ "A6 AD 039D",
+ "A6 AE 039E",
+ "A6 AF 039F",
+ "A6 B0 03A0",
+ "A6 B1 03A1",
+ "A6 B2 03A3",
+ "A6 B3 03A4",
+ "A6 B4 03A5",
+ "A6 B5 03A6",
+ "A6 B6 03A7",
+ "A6 B7 03A8",
+ "A6 B8 03A9",
+ "A6 C1 03B1",
+ "A6 C2 03B2",
+ "A6 C3 03B3",
+ "A6 C4 03B4",
+ "A6 C5 03B5",
+ "A6 C6 03B6",
+ "A6 C7 03B7",
+ "A6 C8 03B8",
+ "A6 C9 03B9",
+ "A6 CA 03BA",
+ "A6 CB 03BB",
+ "A6 CC 03BC",
+ "A6 CD 03BD",
+ "A6 CE 03BE",
+ "A6 CF 03BF",
+ "A6 D0 03C0",
+ "A6 D1 03C1",
+ "A6 D2 03C3",
+ "A6 D3 03C4",
+ "A6 D4 03C5",
+ "A6 D5 03C6",
+ "A6 D6 03C7",
+ "A6 D7 03C8",
+ "A6 D8 03C9",
+ "A7 A7 0401",
+ "A7 A1 0410",
+ "A7 A2 0411",
+ "A7 A3 0412",
+ "A7 A4 0413",
+ "A7 A5 0414",
+ "A7 A6 0415",
+ "A7 A8 0416",
+ "A7 A9 0417",
+ "A7 AA 0418",
+ "A7 AB 0419",
+ "A7 AC 041A",
+ "A7 AD 041B",
+ "A7 AE 041C",
+ "A7 AF 041D",
+ "A7 B0 041E",
+ "A7 B1 041F",
+ "A7 B2 0420",
+ "A7 B3 0421",
+ "A7 B4 0422",
+ "A7 B5 0423",
+ "A7 B6 0424",
+ "A7 B7 0425",
+ "A7 B8 0426",
+ "A7 B9 0427",
+ "A7 BA 0428",
+ "A7 BB 0429",
+ "A7 BC 042A",
+ "A7 BD 042B",
+ "A7 BE 042C",
+ "A7 BF 042D",
+ "A7 C0 042E",
+ "A7 C1 042F",
+ "A7 D1 0430",
+ "A7 D2 0431",
+ "A7 D3 0432",
+ "A7 D4 0433",
+ "A7 D5 0434",
+ "A7 D6 0435",
+ "A7 D8 0436",
+ "A7 D9 0437",
+ "A7 DA 0438",
+ "A7 DB 0439",
+ "A7 DC 043A",
+ "A7 DD 043B",
+ "A7 DE 043C",
+ "A7 DF 043D",
+ "A7 E0 043E",
+ "A7 E1 043F",
+ "A7 E2 0440",
+ "A7 E3 0441",
+ "A7 E4 0442",
+ "A7 E5 0443",
+ "A7 E6 0444",
+ "A7 E7 0445",
+ "A7 E8 0446",
+ "A7 E9 0447",
+ "A7 EA 0448",
+ "A7 EB 0449",
+ "A7 EC 044A",
+ "A7 ED 044B",
+ "A7 EE 044C",
+ "A7 EF 044D",
+ "A7 F0 044E",
+ "A7 F1 044F",
+ "A7 D7 0451",
+ "A1 BE 2010",
+ "A1 BD 2015",
+ "A1 C2 2016",
+ "A1 C6 2018",
+ "A1 C7 2019",
+ "A1 C8 201C",
+ "A1 C9 201D",
+ "A2 F7 2020",
+ "A2 F8 2021",
+ "A1 C5 2025",
+ "A1 C4 2026",
+ "A2 F3 2030",
+ "A1 EC 2032",
+ "A1 ED 2033",
+ "A2 A8 203B",
+ "A1 EE 2103",
+ "A2 F2 212B",
+ "A2 AB 2190",
+ "A2 AC 2191",
+ "A2 AA 2192",
+ "A2 AD 2193",
+ "A2 CD 21D2",
+ "A2 CE 21D4",
+ "A2 CF 2200",
+ "A2 DF 2202",
+ "A2 D0 2203",
+ "A2 E0 2207",
+ "A2 BA 2208",
+ "A2 BB 220B",
+ "A1 DD 2212",
+ "A2 E5 221A",
+ "A2 E7 221D",
+ "A1 E7 221E",
+ "A2 DC 2220",
+ "A2 CA 2227",
+ "A2 CB 2228",
+ "A2 C1 2229",
+ "A2 C0 222A",
+ "A2 E9 222B",
+ "A2 EA 222C",
+ "A1 E8 2234",
+ "A2 E8 2235",
+ "A2 E6 223D",
+ "A2 E2 2252",
+ "A1 E2 2260",
+ "A2 E1 2261",
+ "A1 E5 2266",
+ "A1 E6 2267",
+ "A2 E3 226A",
+ "A2 E4 226B",
+ "A2 BE 2282",
+ "A2 BF 2283",
+ "A2 BC 2286",
+ "A2 BD 2287",
+ "A2 DD 22A5",
+ "A2 DE 2312",
+ "A8 A1 2500",
+ "A8 AC 2501",
+ "A8 A2 2502",
+ "A8 AD 2503",
+ "A8 A3 250C",
+ "A8 AE 250F",
+ "A8 A4 2510",
+ "A8 AF 2513",
+ "A8 A6 2514",
+ "A8 B1 2517",
+ "A8 A5 2518",
+ "A8 B0 251B",
+ "A8 A7 251C",
+ "A8 BC 251D",
+ "A8 B7 2520",
+ "A8 B2 2523",
+ "A8 A9 2524",
+ "A8 BE 2525",
+ "A8 B9 2528",
+ "A8 B4 252B",
+ "A8 A8 252C",
+ "A8 B8 252F",
+ "A8 BD 2530",
+ "A8 B3 2533",
+ "A8 AA 2534",
+ "A8 BA 2537",
+ "A8 BF 2538",
+ "A8 B5 253B",
+ "A8 AB 253C",
+ "A8 BB 253F",
+ "A8 C0 2542",
+ "A8 B6 254B",
+ "A2 A3 25A0",
+ "A2 A2 25A1",
+ "A2 A5 25B2",
+ "A2 A4 25B3",
+ "A2 A7 25BC",
+ "A2 A6 25BD",
+ "A2 A1 25C6",
+ "A1 FE 25C7",
+ "A1 FB 25CB",
+ "A1 FD 25CE",
+ "A1 FC 25CF",
+ "A2 FE 25EF",
+ "A1 FA 2605",
+ "A1 F9 2606",
+ "A1 EA 2640",
+ "A1 E9 2642",
+ "A2 F6 266A",
+ "A2 F5 266D",
+ "A2 F4 266F",
+ "A1 A1 3000",
+ "A1 A2 3001",
+ "A1 A3 3002",
+ "A1 B7 3003",
+ "A1 B9 3005",
+ "A1 BA 3006",
+ "A1 BB 3007",
+ "A1 D2 3008",
+ "A1 D3 3009",
+ "A1 D4 300A",
+ "A1 D5 300B",
+ "A1 D6 300C",
+ "A1 D7 300D",
+ "A1 D8 300E",
+ "A1 D9 300F",
+ "A1 DA 3010",
+ "A1 DB 3011",
+ "A2 A9 3012",
+ "A2 AE 3013",
+ "A1 CC 3014",
+ "A1 CD 3015",
+ "A1 C1 301C",
+ "A4 A1 3041",
+ "A4 A2 3042",
+ "A4 A3 3043",
+ "A4 A4 3044",
+ "A4 A5 3045",
+ "A4 A6 3046",
+ "A4 A7 3047",
+ "A4 A8 3048",
+ "A4 A9 3049",
+ "A4 AA 304A",
+ "A4 AB 304B",
+ "A4 AC 304C",
+ "A4 AD 304D",
+ "A4 AE 304E",
+ "A4 AF 304F",
+ "A4 B0 3050",
+ "A4 B1 3051",
+ "A4 B2 3052",
+ "A4 B3 3053",
+ "A4 B4 3054",
+ "A4 B5 3055",
+ "A4 B6 3056",
+ "A4 B7 3057",
+ "A4 B8 3058",
+ "A4 B9 3059",
+ "A4 BA 305A",
+ "A4 BB 305B",
+ "A4 BC 305C",
+ "A4 BD 305D",
+ "A4 BE 305E",
+ "A4 BF 305F",
+ "A4 C0 3060",
+ "A4 C1 3061",
+ "A4 C2 3062",
+ "A4 C3 3063",
+ "A4 C4 3064",
+ "A4 C5 3065",
+ "A4 C6 3066",
+ "A4 C7 3067",
+ "A4 C8 3068",
+ "A4 C9 3069",
+ "A4 CA 306A",
+ "A4 CB 306B",
+ "A4 CC 306C",
+ "A4 CD 306D",
+ "A4 CE 306E",
+ "A4 CF 306F",
+ "A4 D0 3070",
+ "A4 D1 3071",
+ "A4 D2 3072",
+ "A4 D3 3073",
+ "A4 D4 3074",
+ "A4 D5 3075",
+ "A4 D6 3076",
+ "A4 D7 3077",
+ "A4 D8 3078",
+ "A4 D9 3079",
+ "A4 DA 307A",
+ "A4 DB 307B",
+ "A4 DC 307C",
+ "A4 DD 307D",
+ "A4 DE 307E",
+ "A4 DF 307F",
+ "A4 E0 3080",
+ "A4 E1 3081",
+ "A4 E2 3082",
+ "A4 E3 3083",
+ "A4 E4 3084",
+ "A4 E5 3085",
+ "A4 E6 3086",
+ "A4 E7 3087",
+ "A4 E8 3088",
+ "A4 E9 3089",
+ "A4 EA 308A",
+ "A4 EB 308B",
+ "A4 EC 308C",
+ "A4 ED 308D",
+ "A4 EE 308E",
+ "A4 EF 308F",
+ "A4 F0 3090",
+ "A4 F1 3091",
+ "A4 F2 3092",
+ "A4 F3 3093",
+ "A1 AB 309B",
+ "A1 AC 309C",
+ "A1 B5 309D",
+ "A1 B6 309E",
+ "A5 A1 30A1",
+ "A5 A2 30A2",
+ "A5 A3 30A3",
+ "A5 A4 30A4",
+ "A5 A5 30A5",
+ "A5 A6 30A6",
+ "A5 A7 30A7",
+ "A5 A8 30A8",
+ "A5 A9 30A9",
+ "A5 AA 30AA",
+ "A5 AB 30AB",
+ "A5 AC 30AC",
+ "A5 AD 30AD",
+ "A5 AE 30AE",
+ "A5 AF 30AF",
+ "A5 B0 30B0",
+ "A5 B1 30B1",
+ "A5 B2 30B2",
+ "A5 B3 30B3",
+ "A5 B4 30B4",
+ "A5 B5 30B5",
+ "A5 B6 30B6",
+ "A5 B7 30B7",
+ "A5 B8 30B8",
+ "A5 B9 30B9",
+ "A5 BA 30BA",
+ "A5 BB 30BB",
+ "A5 BC 30BC",
+ "A5 BD 30BD",
+ "A5 BE 30BE",
+ "A5 BF 30BF",
+ "A5 C0 30C0",
+ "A5 C1 30C1",
+ "A5 C2 30C2",
+ "A5 C3 30C3",
+ "A5 C4 30C4",
+ "A5 C5 30C5",
+ "A5 C6 30C6",
+ "A5 C7 30C7",
+ "A5 C8 30C8",
+ "A5 C9 30C9",
+ "A5 CA 30CA",
+ "A5 CB 30CB",
+ "A5 CC 30CC",
+ "A5 CD 30CD",
+ "A5 CE 30CE",
+ "A5 CF 30CF",
+ "A5 D0 30D0",
+ "A5 D1 30D1",
+ "A5 D2 30D2",
+ "A5 D3 30D3",
+ "A5 D4 30D4",
+ "A5 D5 30D5",
+ "A5 D6 30D6",
+ "A5 D7 30D7",
+ "A5 D8 30D8",
+ "A5 D9 30D9",
+ "A5 DA 30DA",
+ "A5 DB 30DB",
+ "A5 DC 30DC",
+ "A5 DD 30DD",
+ "A5 DE 30DE",
+ "A5 DF 30DF",
+ "A5 E0 30E0",
+ "A5 E1 30E1",
+ "A5 E2 30E2",
+ "A5 E3 30E3",
+ "A5 E4 30E4",
+ "A5 E5 30E5",
+ "A5 E6 30E6",
+ "A5 E7 30E7",
+ "A5 E8 30E8",
+ "A5 E9 30E9",
+ "A5 EA 30EA",
+ "A5 EB 30EB",
+ "A5 EC 30EC",
+ "A5 ED 30ED",
+ "A5 EE 30EE",
+ "A5 EF 30EF",
+ "A5 F0 30F0",
+ "A5 F1 30F1",
+ "A5 F2 30F2",
+ "A5 F3 30F3",
+ "A5 F4 30F4",
+ "A5 F5 30F5",
+ "A5 F6 30F6",
+ "A1 A6 30FB",
+ "A1 BC 30FC",
+ "A1 B3 30FD",
+ "A1 B4 30FE",
+ "B0 EC 4E00",
+ "C3 FA 4E01",
+ "BC B7 4E03",
+ "CB FC 4E07",
+ "BE E6 4E08",
+ "BB B0 4E09",
+ "BE E5 4E0A",
+ "B2 BC 4E0B",
+ "C9 D4 4E0D",
+ "CD BF 4E0E",
+ "D0 A2 4E10",
+ "B1 AF 4E11",
+ "B3 EE 4E14",
+ "D0 A3 4E15",
+ "C0 A4 4E16",
+ "D2 C2 4E17",
+ "B5 D6 4E18",
+ "CA BA 4E19",
+ "BE E7 4E1E",
+ "CE BE 4E21",
+ "CA C2 4E26",
+ "D0 A4 4E2A",
+ "C3 E6 4E2D",
+ "D0 A5 4E31",
+ "B6 FA 4E32",
+ "D0 A6 4E36",
+ "B4 DD 4E38",
+ "C3 B0 4E39",
+ "BC E7 4E3B",
+ "D0 A7 4E3C",
+ "D0 A8 4E3F",
+ "D0 A9 4E42",
+ "C7 B5 4E43",
+ "B5 D7 4E45",
+ "C7 B7 4E4B",
+ "C6 E3 4E4D",
+ "B8 C3 4E4E",
+ "CB B3 4E4F",
+ "E9 C9 4E55",
+ "D0 AA 4E56",
+ "BE E8 4E57",
+ "D0 AB 4E58",
+ "B2 B5 4E59",
+ "B6 E5 4E5D",
+ "B8 F0 4E5E",
+ "CC E9 4E5F",
+ "D6 A6 4E62",
+ "CD F0 4E71",
+ "C6 FD 4E73",
+ "B4 A5 4E7E",
+ "B5 B5 4E80",
+ "D0 AC 4E82",
+ "D0 AD 4E85",
+ "CE BB 4E86",
+ "CD BD 4E88",
+ "C1 E8 4E89",
+ "D0 AF 4E8A",
+ "BB F6 4E8B",
+ "C6 F3 4E8C",
+ "D0 B2 4E8E",
+ "B1 BE 4E91",
+ "B8 DF 4E92",
+ "B8 DE 4E94",
+ "B0 E6 4E95",
+ "CF CB 4E98",
+ "CF CA 4E99",
+ "BA B3 4E9B",
+ "B0 A1 4E9C",
+ "D0 B3 4E9E",
+ "D0 B4 4E9F",
+ "D0 B5 4EA0",
+ "CB B4 4EA1",
+ "D0 B6 4EA2",
+ "B8 F2 4EA4",
+ "B0 E7 4EA5",
+ "CB F2 4EA6",
+ "B5 FC 4EA8",
+ "B5 FD 4EAB",
+ "B5 FE 4EAC",
+ "C4 E2 4EAD",
+ "CE BC 4EAE",
+ "D0 B7 4EB0",
+ "D0 B8 4EB3",
+ "D0 B9 4EB6",
+ "BF CD 4EBA",
+ "BD BA 4EC0",
+ "BF CE 4EC1",
+ "D0 BE 4EC2",
+ "D0 BC 4EC4",
+ "D0 BD 4EC6",
+ "B5 D8 4EC7",
+ "BA A3 4ECA",
+ "B2 F0 4ECB",
+ "D0 BB 4ECD",
+ "D0 BA 4ECE",
+ "CA A9 4ECF",
+ "BB C6 4ED4",
+ "BB C5 4ED5",
+ "C2 BE 4ED6",
+ "D0 BF 4ED7",
+ "C9 D5 4ED8",
+ "C0 E7 4ED9",
+ "A1 B8 4EDD",
+ "D0 C0 4EDE",
+ "D0 C2 4EDF",
+ "C2 E5 4EE3",
+ "CE E1 4EE4",
+ "B0 CA 4EE5",
+ "D0 C1 4EED",
+ "B2 BE 4EEE",
+ "B6 C4 4EF0",
+ "C3 E7 4EF2",
+ "B7 EF 4EF6",
+ "D0 C3 4EF7",
+ "C7 A4 4EFB",
+ "B4 EB 4F01",
+ "D0 C4 4F09",
+ "B0 CB 4F0A",
+ "B8 E0 4F0D",
+ "B4 EC 4F0E",
+ "C9 FA 4F0F",
+ "C8 B2 4F10",
+ "B5 D9 4F11",
+ "B2 F1 4F1A",
+ "D0 E7 4F1C",
+ "C5 C1 4F1D",
+ "C7 EC 4F2F",
+ "D0 C6 4F30",
+ "C8 BC 4F34",
+ "CE E2 4F36",
+ "BF AD 4F38",
+ "BB C7 4F3A",
+ "BB F7 4F3C",
+ "B2 C0 4F3D",
+ "C4 D1 4F43",
+ "C3 A2 4F46",
+ "D0 CA 4F47",
+ "B0 CC 4F4D",
+ "C4 E3 4F4E",
+ "BD BB 4F4F",
+ "BA B4 4F50",
+ "CD A4 4F51",
+ "C2 CE 4F53",
+ "B2 BF 4F55",
+ "D0 C9 4F57",
+ "CD BE 4F59",
+ "D0 C5 4F5A",
+ "D0 C7 4F5B",
+ "BA EE 4F5C",
+ "D0 C8 4F5D",
+ "D5 A4 4F5E",
+ "D0 D0 4F69",
+ "D0 D3 4F6F",
+ "D0 D1 4F70",
+ "B2 C2 4F73",
+ "CA BB 4F75",
+ "D0 CB 4F76",
+ "D0 CF 4F7B",
+ "B8 F3 4F7C",
+ "BB C8 4F7F",
+ "B4 A6 4F83",
+ "D0 D4 4F86",
+ "D0 CC 4F88",
+ "CE E3 4F8B",
+ "BB F8 4F8D",
+ "D0 CD 4F8F",
+ "D0 D2 4F91",
+ "D0 D5 4F96",
+ "D0 CE 4F98",
+ "B6 A1 4F9B",
+ "B0 CD 4F9D",
+ "B6 A2 4FA0",
+ "B2 C1 4FA1",
+ "D5 A5 4FAB",
+ "CB F9 4FAD",
+ "C9 EE 4FAE",
+ "B8 F4 4FAF",
+ "BF AF 4FB5",
+ "CE B7 4FB6",
+ "CA D8 4FBF",
+ "B7 B8 4FC2",
+ "C2 A5 4FC3",
+ "B2 E4 4FC4",
+ "BD D3 4FCA",
+ "D0 D9 4FCE",
+ "D0 DE 4FD0",
+ "D0 DC 4FD1",
+ "D0 D7 4FD4",
+ "C2 AF 4FD7",
+ "D0 DA 4FD8",
+ "D0 DD 4FDA",
+ "D0 DB 4FDB",
+ "CA DD 4FDD",
+ "D0 D8 4FDF",
+ "BF AE 4FE1",
+ "CB F3 4FE3",
+ "D0 DF 4FE4",
+ "D0 E0 4FE5",
+ "BD A4 4FEE",
+ "D0 ED 4FEF",
+ "C7 D0 4FF3",
+ "C9 B6 4FF5",
+ "D0 E8 4FF6",
+ "CA F0 4FF8",
+ "B2 B6 4FFA",
+ "D0 EC 4FFE",
+ "D0 E6 5005",
+ "D0 EF 5006",
+ "C1 D2 5009",
+ "B8 C4 500B",
+ "C7 DC 500D",
+ "E0 C7 500F",
+ "D0 EE 5011",
+ "C5 DD 5012",
+ "D0 E3 5014",
+ "B8 F6 5016",
+ "B8 F5 5019",
+ "D0 E1 501A",
+ "BC DA 501F",
+ "D0 E9 5021",
+ "CA EF 5023",
+ "C3 CD 5024",
+ "D0 E5 5025",
+ "B7 F1 5026",
+ "D0 E2 5028",
+ "D0 EA 5029",
+ "D0 E4 502A",
+ "CE D1 502B",
+ "D0 EB 502C",
+ "CF C1 502D",
+ "B6 E6 5036",
+ "B7 F0 5039",
+ "D0 F0 5043",
+ "D0 F1 5047",
+ "D0 F5 5048",
+ "B0 CE 5049",
+ "CA D0 504F",
+ "D0 F4 5050",
+ "D0 F3 5055",
+ "D0 F7 5056",
+ "D0 F6 505A",
+ "C4 E4 505C",
+ "B7 F2 5065",
+ "D0 F8 506C",
+ "BC C5 5072",
+ "C2 A6 5074",
+ "C4 E5 5075",
+ "B6 F6 5076",
+ "D0 F9 5078",
+ "B5 B6 507D",
+ "D0 FA 5080",
+ "D0 FC 5085",
+ "CB B5 508D",
+ "B7 E6 5091",
+ "BB B1 5098",
+ "C8 F7 5099",
+ "D0 FB 509A",
+ "BA C5 50AC",
+ "CD C3 50AD",
+ "D0 FE 50B2",
+ "D1 A3 50B3",
+ "D0 FD 50B4",
+ "BA C4 50B5",
+ "BD FD 50B7",
+ "B7 B9 50BE",
+ "D1 A4 50C2",
+ "B6 CF 50C5",
+ "D1 A1 50C9",
+ "D1 A2 50CA",
+ "C6 AF 50CD",
+ "C1 FC 50CF",
+ "B6 A3 50D1",
+ "CB CD 50D5",
+ "D1 A5 50D6",
+ "CE BD 50DA",
+ "D1 A6 50DE",
+ "D1 A9 50E3",
+ "D1 A7 50E5",
+ "C1 CE 50E7",
+ "D1 A8 50ED",
+ "D1 AA 50EE",
+ "D1 AC 50F5",
+ "D1 AB 50F9",
+ "CA C8 50FB",
+ "B5 B7 5100",
+ "D1 AE 5101",
+ "D1 AF 5102",
+ "B2 AF 5104",
+ "D1 AD 5109",
+ "BC F4 5112",
+ "D1 B2 5114",
+ "D1 B1 5115",
+ "D1 B0 5116",
+ "D0 D6 5118",
+ "D1 B3 511A",
+ "BD FE 511F",
+ "D1 B4 5121",
+ "CD A5 512A",
+ "CC D9 5132",
+ "D1 B6 5137",
+ "D1 B5 513A",
+ "D1 B8 513B",
+ "D1 B7 513C",
+ "D1 B9 513F",
+ "D1 BA 5140",
+ "B0 F4 5141",
+ "B8 B5 5143",
+ "B7 BB 5144",
+ "BD BC 5145",
+ "C3 FB 5146",
+ "B6 A4 5147",
+ "C0 E8 5148",
+ "B8 F7 5149",
+ "B9 EE 514B",
+ "D1 BC 514C",
+ "CC C8 514D",
+ "C5 C6 514E",
+ "BB F9 5150",
+ "D1 BB 5152",
+ "D1 BD 5154",
+ "C5 DE 515A",
+ "B3 F5 515C",
+ "D1 BE 5162",
+ "C6 FE 5165",
+ "C1 B4 5168",
+ "D1 C0 5169",
+ "D1 C1 516A",
+ "C8 AC 516B",
+ "B8 F8 516C",
+ "CF BB 516D",
+ "D1 C2 516E",
+ "B6 A6 5171",
+ "CA BC 5175",
+ "C2 B6 5176",
+ "B6 F1 5177",
+ "C5 B5 5178",
+ "B7 F3 517C",
+ "D1 C3 5180",
+ "D1 C4 5182",
+ "C6 E2 5185",
+ "B1 DF 5186",
+ "D1 C7 5189",
+ "BA FD 518A",
+ "D1 C6 518C",
+ "BA C6 518D",
+ "D1 C8 518F",
+ "E6 EE 5190",
+ "D1 C9 5191",
+ "CB C1 5192",
+ "D1 CA 5193",
+ "D1 CB 5195",
+ "D1 CC 5196",
+ "BE E9 5197",
+ "BC CC 5199",
+ "B4 A7 51A0",
+ "D1 CF 51A2",
+ "D1 CD 51A4",
+ "CC BD 51A5",
+ "D1 CE 51A6",
+ "C9 DA 51A8",
+ "D1 D0 51A9",
+ "D1 D1 51AA",
+ "D1 D2 51AB",
+ "C5 DF 51AC",
+ "D1 D6 51B0",
+ "D1 D4 51B1",
+ "D1 D5 51B2",
+ "D1 D3 51B3",
+ "BA E3 51B4",
+ "D1 D7 51B5",
+ "CC EA 51B6",
+ "CE E4 51B7",
+ "D1 D8 51BD",
+ "C0 A8 51C4",
+ "D1 D9 51C5",
+ "BD DA 51C6",
+ "D1 DA 51C9",
+ "C3 FC 51CB",
+ "CE BF 51CC",
+ "C5 E0 51CD",
+ "D2 C5 51D6",
+ "D1 DB 51DB",
+ "F4 A5 51DC",
+ "B6 C5 51DD",
+ "D1 DC 51E0",
+ "CB DE 51E1",
+ "BD E8 51E6",
+ "C2 FC 51E7",
+ "D1 DE 51E9",
+ "C6 E4 51EA",
+ "D1 DF 51ED",
+ "D1 E0 51F0",
+ "B3 AE 51F1",
+ "D1 E1 51F5",
+ "B6 A7 51F6",
+ "C6 CC 51F8",
+ "B1 FA 51F9",
+ "BD D0 51FA",
+ "C8 A1 51FD",
+ "D1 E2 51FE",
+ "C5 E1 5200",
+ "BF CF 5203",
+ "D1 E3 5204",
+ "CA AC 5206",
+ "C0 DA 5207",
+ "B4 A2 5208",
+ "B4 A9 520A",
+ "D1 E4 520B",
+ "D1 E6 520E",
+ "B7 BA 5211",
+ "D1 E5 5214",
+ "CE F3 5217",
+ "BD E9 521D",
+ "C8 BD 5224",
+ "CA CC 5225",
+ "D1 E7 5227",
+ "CD F8 5229",
+ "D1 E8 522A",
+ "D1 E9 522E",
+ "C5 FE 5230",
+ "D1 EA 5233",
+ "C0 A9 5236",
+ "BA FE 5237",
+ "B7 F4 5238",
+ "D1 EB 5239",
+ "BB C9 523A",
+ "B9 EF 523B",
+ "C4 E6 5243",
+ "D1 ED 5244",
+ "C2 A7 5247",
+ "BA EF 524A",
+ "D1 EE 524B",
+ "D1 EF 524C",
+ "C1 B0 524D",
+ "D1 EC 524F",
+ "D1 F1 5254",
+ "CB B6 5256",
+ "B9 E4 525B",
+ "D1 F0 525E",
+ "B7 F5 5263",
+ "BA DE 5264",
+ "C7 ED 5265",
+ "D1 F4 5269",
+ "D1 F2 526A",
+ "C9 FB 526F",
+ "BE EA 5270",
+ "D1 FB 5271",
+ "B3 E4 5272",
+ "D1 F5 5273",
+ "D1 F3 5274",
+ "C1 CF 5275",
+ "D1 F7 527D",
+ "D1 F6 527F",
+ "B3 C4 5283",
+ "B7 E0 5287",
+ "D1 FC 5288",
+ "CE AD 5289",
+ "D1 F8 528D",
+ "D1 FD 5291",
+ "D1 FA 5292",
+ "D1 F9 5294",
+ "CE CF 529B",
+ "B8 F9 529F",
+ "B2 C3 52A0",
+ "CE F4 52A3",
+ "BD F5 52A9",
+ "C5 D8 52AA",
+ "B9 E5 52AB",
+ "D2 A2 52AC",
+ "D2 A3 52AD",
+ "CE E5 52B1",
+ "CF AB 52B4",
+ "D2 A5 52B5",
+ "B8 FA 52B9",
+ "D2 A4 52BC",
+ "B3 AF 52BE",
+ "D2 A6 52C1",
+ "CB D6 52C3",
+ "C4 BC 52C5",
+ "CD A6 52C7",
+ "CA D9 52C9",
+ "D2 A7 52CD",
+ "F0 D5 52D2",
+ "C6 B0 52D5",
+ "D2 A8 52D7",
+ "B4 AA 52D8",
+ "CC B3 52D9",
+ "BE A1 52DD",
+ "D2 A9 52DE",
+ "CA E7 52DF",
+ "D2 AD 52E0",
+ "C0 AA 52E2",
+ "D2 AA 52E3",
+ "B6 D0 52E4",
+ "D2 AB 52E6",
+ "B4 AB 52E7",
+ "B7 AE 52F2",
+ "D2 AE 52F3",
+ "D2 AF 52F5",
+ "D2 B0 52F8",
+ "D2 B1 52F9",
+ "BC DB 52FA",
+ "B8 FB 52FE",
+ "CC DE 52FF",
+ "CC E8 5301",
+ "C6 F7 5302",
+ "CA F1 5305",
+ "D2 B2 5306",
+ "D2 B3 5308",
+ "D2 B5 530D",
+ "D2 B7 530F",
+ "D2 B6 5310",
+ "D2 B8 5315",
+ "B2 BD 5316",
+ "CB CC 5317",
+ "BA FC 5319",
+ "D2 B9 531A",
+ "C1 D9 531D",
+ "BE A2 5320",
+ "B6 A9 5321",
+ "D2 BA 5323",
+ "C8 DB 532A",
+ "D2 BB 532F",
+ "D2 BC 5331",
+ "D2 BD 5333",
+ "D2 BE 5338",
+ "C9 A4 5339",
+ "B6 E8 533A",
+ "B0 E5 533B",
+ "C6 BF 533F",
+ "D2 BF 5340",
+ "BD BD 5341",
+ "C0 E9 5343",
+ "D2 C1 5345",
+ "D2 C0 5346",
+ "BE A3 5347",
+ "B8 E1 5348",
+ "D2 C3 5349",
+ "C8 BE 534A",
+ "D2 C4 534D",
+ "C8 DC 5351",
+ "C2 B4 5352",
+ "C2 EE 5353",
+ "B6 A8 5354",
+ "C6 EE 5357",
+ "C3 B1 5358",
+ "C7 EE 535A",
+ "CB CE 535C",
+ "D2 C6 535E",
+ "C0 EA 5360",
+ "B7 B5 5366",
+ "D2 C7 5369",
+ "D2 C8 536E",
+ "B1 AC 536F",
+ "B0 F5 5370",
+ "B4 ED 5371",
+ "C2 A8 5373",
+ "B5 D1 5374",
+ "CD F1 5375",
+ "D2 CB 5377",
+ "B2 B7 5378",
+ "D2 CA 537B",
+ "B6 AA 537F",
+ "D2 CC 5382",
+ "CC F1 5384",
+ "D2 CD 5396",
+ "CE D2 5398",
+ "B8 FC 539A",
+ "B8 B6 539F",
+ "D2 CE 53A0",
+ "D2 D0 53A5",
+ "D2 CF 53A6",
+ "BF DF 53A8",
+ "B1 B9 53A9",
+ "B1 DE 53AD",
+ "D2 D1 53AE",
+ "D2 D2 53B0",
+ "B8 B7 53B3",
+ "D2 D3 53B6",
+ "B5 EE 53BB",
+ "BB B2 53C2",
+ "D2 D4 53C3",
+ "CB F4 53C8",
+ "BA B5 53C9",
+ "B5 DA 53CA",
+ "CD A7 53CB",
+ "C1 D0 53CC",
+ "C8 BF 53CD",
+ "BC FD 53CE",
+ "BD C7 53D4",
+ "BC E8 53D6",
+ "BC F5 53D7",
+ "BD F6 53D9",
+ "C8 C0 53DB",
+ "D2 D7 53DF",
+ "B1 C3 53E1",
+ "C1 D1 53E2",
+ "B8 FD 53E3",
+ "B8 C5 53E4",
+ "B6 E7 53E5",
+ "D2 DB 53E8",
+ "C3 A1 53E9",
+ "C2 FE 53EA",
+ "B6 AB 53EB",
+ "BE A4 53EC",
+ "D2 DC 53ED",
+ "D2 DA 53EE",
+ "B2 C4 53EF",
+ "C2 E6 53F0",
+ "BC B8 53F1",
+ "BB CB 53F2",
+ "B1 A6 53F3",
+ "B3 F0 53F6",
+ "B9 E6 53F7",
+ "BB CA 53F8",
+ "D2 DD 53FA",
+ "D2 DE 5401",
+ "B5 C9 5403",
+ "B3 C6 5404",
+ "B9 E7 5408",
+ "B5 C8 5409",
+ "C4 DF 540A",
+ "B1 A5 540B",
+ "C6 B1 540C",
+ "CC BE 540D",
+ "B9 A1 540E",
+ "CD F9 540F",
+ "C5 C7 5410",
+ "B8 FE 5411",
+ "B7 AF 541B",
+ "D2 E7 541D",
+ "B6 E3 541F",
+ "CB CA 5420",
+ "C8 DD 5426",
+ "D2 E6 5429",
+ "B4 DE 542B",
+ "D2 E1 542C",
+ "D2 E2 542D",
+ "D2 E4 542E",
+ "D2 E5 5436",
+ "B5 DB 5438",
+ "BF E1 5439",
+ "CA AD 543B",
+ "D2 E3 543C",
+ "D2 DF 543D",
+ "B8 E3 543E",
+ "D2 E0 5440",
+ "CF A4 5442",
+ "CA F2 5446",
+ "C4 E8 5448",
+ "B8 E2 5449",
+ "B9 F0 544A",
+ "D2 E8 544E",
+ "C6 DD 5451",
+ "D2 EC 545F",
+ "BC FE 5468",
+ "BC F6 546A",
+ "D2 EF 5470",
+ "D2 ED 5471",
+ "CC A3 5473",
+ "D2 EA 5475",
+ "D2 F3 5476",
+ "D2 EE 5477",
+ "D2 F1 547B",
+ "B8 C6 547C",
+ "CC BF 547D",
+ "D2 F2 5480",
+ "D2 F4 5484",
+ "D2 F6 5486",
+ "BA F0 548B",
+ "CF C2 548C",
+ "D2 EB 548E",
+ "D2 E9 548F",
+ "D2 F5 5490",
+ "D2 F0 5492",
+ "D2 F8 54A2",
+ "D3 A3 54A4",
+ "D2 FA 54A5",
+ "D2 FE 54A8",
+ "D3 A1 54AB",
+ "D2 FB 54AC",
+ "D3 BE 54AF",
+ "BA E9 54B2",
+ "B3 B1 54B3",
+ "D2 F9 54B8",
+ "D3 A5 54BC",
+ "B0 F6 54BD",
+ "D3 A4 54BE",
+ "B0 A5 54C0",
+ "C9 CA 54C1",
+ "D3 A2 54C2",
+ "D2 FC 54C4",
+ "D2 F7 54C7",
+ "D2 FD 54C8",
+ "BA C8 54C9",
+ "D3 A6 54D8",
+ "B0 F7 54E1",
+ "D3 AF 54E2",
+ "D3 A7 54E5",
+ "D3 A8 54E6",
+ "BE A5 54E8",
+ "CB E9 54E9",
+ "D3 AD 54ED",
+ "D3 AC 54EE",
+ "C5 AF 54F2",
+ "D3 AE 54FA",
+ "D3 AB 54FD",
+ "B1 B4 5504",
+ "BA B6 5506",
+ "BF B0 5507",
+ "D3 A9 550F",
+ "C5 E2 5510",
+ "D3 AA 5514",
+ "B0 A2 5516",
+ "D3 B4 552E",
+ "CD A3 552F",
+ "BE A7 5531",
+ "D3 BA 5533",
+ "D3 B9 5538",
+ "D3 B0 5539",
+ "C2 C3 553E",
+ "D3 B1 5540",
+ "C2 EF 5544",
+ "D3 B6 5545",
+ "BE A6 5546",
+ "D3 B3 554C",
+ "CC E4 554F",
+ "B7 BC 5553",
+ "D3 B7 5556",
+ "D3 B8 5557",
+ "D3 B5 555C",
+ "D3 BB 555D",
+ "D3 B2 5563",
+ "D3 C1 557B",
+ "D3 C6 557C",
+ "D3 C2 557E",
+ "D3 BD 5580",
+ "D3 C7 5583",
+ "C1 B1 5584",
+ "D3 C9 5587",
+ "B9 A2 5589",
+ "D3 BF 558A",
+ "C3 FD 558B",
+ "D3 C3 5598",
+ "D3 BC 5599",
+ "B4 AD 559A",
+ "B4 EE 559C",
+ "B3 E5 559D",
+ "D3 C4 559E",
+ "D3 C0 559F",
+ "B7 F6 55A7",
+ "D3 CA 55A8",
+ "D3 C8 55A9",
+ "C1 D3 55AA",
+ "B5 CA 55AB",
+ "B6 AC 55AC",
+ "D3 C5 55AE",
+ "B6 F4 55B0",
+ "B1 C4 55B6",
+ "D3 CE 55C4",
+ "D3 CC 55C5",
+ "D4 A7 55C7",
+ "D3 D1 55D4",
+ "D3 CB 55DA",
+ "D3 CF 55DC",
+ "D3 CD 55DF",
+ "BB CC 55E3",
+ "D3 D0 55E4",
+ "D3 D3 55F7",
+ "D3 D8 55F9",
+ "D3 D6 55FD",
+ "D3 D5 55FE",
+ "C3 B2 5606",
+ "B2 C5 5609",
+ "D3 D2 5614",
+ "D3 D4 5616",
+ "BE A8 5617",
+ "B1 B3 5618",
+ "D3 D7 561B",
+ "B2 DE 5629",
+ "D3 E2 562F",
+ "BE FC 5631",
+ "D3 DE 5632",
+ "D3 DC 5634",
+ "D3 DD 5636",
+ "D3 DF 5638",
+ "B1 BD 5642",
+ "C1 B9 564C",
+ "D3 D9 564E",
+ "D3 DA 5650",
+ "B3 FA 565B",
+ "D3 E1 5664",
+ "B4 EF 5668",
+ "D3 E4 566A",
+ "D3 E0 566B",
+ "D3 E3 566C",
+ "CA AE 5674",
+ "C6 D5 5678",
+ "C8 B8 567A",
+ "D3 E6 5680",
+ "D3 E5 5686",
+ "B3 C5 5687",
+ "D3 E7 568A",
+ "D3 EA 568F",
+ "D3 E9 5694",
+ "D3 E8 56A0",
+ "C7 B9 56A2",
+ "D3 EB 56A5",
+ "D3 EC 56AE",
+ "D3 EE 56B4",
+ "D3 ED 56B6",
+ "D3 F0 56BC",
+ "D3 F3 56C0",
+ "D3 F1 56C1",
+ "D3 EF 56C2",
+ "D3 F2 56C3",
+ "D3 F4 56C8",
+ "D3 F5 56CE",
+ "D3 F6 56D1",
+ "D3 F7 56D3",
+ "D3 F8 56D7",
+ "D1 C5 56D8",
+ "BC FC 56DA",
+ "BB CD 56DB",
+ "B2 F3 56DE",
+ "B0 F8 56E0",
+ "C3 C4 56E3",
+ "D3 F9 56EE",
+ "BA A4 56F0",
+ "B0 CF 56F2",
+ "BF DE 56F3",
+ "D3 FA 56F9",
+ "B8 C7 56FA",
+ "B9 F1 56FD",
+ "D3 FC 56FF",
+ "D3 FB 5700",
+ "CA E0 5703",
+ "D3 FD 5704",
+ "D4 A1 5708",
+ "D3 FE 5709",
+ "D4 A2 570B",
+ "D4 A3 570D",
+ "B7 F7 570F",
+ "B1 E0 5712",
+ "D4 A4 5713",
+ "D4 A6 5716",
+ "D4 A5 5718",
+ "D4 A8 571C",
+ "C5 DA 571F",
+ "D4 A9 5726",
+ "B0 B5 5727",
+ "BA DF 5728",
+ "B7 BD 572D",
+ "C3 CF 5730",
+ "D4 AA 5737",
+ "D4 AB 5738",
+ "D4 AD 573B",
+ "D4 AE 5740",
+ "BA E4 5742",
+ "B6 D1 5747",
+ "CB B7 574A",
+ "D4 AC 574E",
+ "D4 AF 574F",
+ "BA C1 5750",
+ "B9 A3 5751",
+ "D4 B3 5761",
+ "BA A5 5764",
+ "C3 B3 5766",
+ "D4 B0 5769",
+ "C4 DA 576A",
+ "D4 B4 577F",
+ "BF E2 5782",
+ "D4 B2 5788",
+ "D4 B5 5789",
+ "B7 BF 578B",
+ "D4 B6 5793",
+ "D4 B7 57A0",
+ "B9 A4 57A2",
+ "B3 C0 57A3",
+ "D4 B9 57A4",
+ "D4 BA 57AA",
+ "D4 BB 57B0",
+ "D4 B8 57B3",
+ "D4 B1 57C0",
+ "D4 BC 57C3",
+ "D4 BD 57C6",
+ "CB E4 57CB",
+ "BE EB 57CE",
+ "D4 BF 57D2",
+ "D4 C0 57D3",
+ "D4 BE 57D4",
+ "D4 C2 57D6",
+ "C7 B8 57DC",
+ "B0 E8 57DF",
+ "C9 D6 57E0",
+ "D4 C3 57E3",
+ "BE FD 57F4",
+ "BC B9 57F7",
+ "C7 DD 57F9",
+ "B4 F0 57FA",
+ "BA EB 57FC",
+ "CB D9 5800",
+ "C6 B2 5802",
+ "B7 F8 5805",
+ "C2 CF 5806",
+ "D4 C1 580A",
+ "D4 C4 580B",
+ "C2 C4 5815",
+ "D4 C5 5819",
+ "D4 C6 581D",
+ "D4 C8 5821",
+ "C4 E9 5824",
+ "B4 AE 582A",
+ "F4 A1 582F",
+ "B1 E1 5830",
+ "CA F3 5831",
+ "BE EC 5834",
+ "C5 C8 5835",
+ "BA E6 583A",
+ "D4 CE 583D",
+ "CA BD 5840",
+ "CE DD 5841",
+ "B2 F4 584A",
+ "D4 CA 584B",
+ "C1 BA 5851",
+ "D4 CD 5852",
+ "C5 E3 5854",
+ "C5 C9 5857",
+ "C5 E4 5858",
+ "C8 B9 5859",
+ "C4 CD 585A",
+ "BA C9 585E",
+ "D4 C9 5862",
+ "B1 F6 5869",
+ "C5 B6 586B",
+ "D4 CB 5870",
+ "D4 C7 5872",
+ "BF D0 5875",
+ "D4 CF 5879",
+ "BD CE 587E",
+ "B6 AD 5883",
+ "D4 D0 5885",
+ "CA E8 5893",
+ "C1 FD 5897",
+ "C4 C6 589C",
+ "D4 D2 589F",
+ "CB CF 58A8",
+ "D4 D3 58AB",
+ "D4 D8 58AE",
+ "CA AF 58B3",
+ "D4 D7 58B8",
+ "D4 D1 58B9",
+ "D4 D4 58BA",
+ "D4 D6 58BB",
+ "BA A6 58BE",
+ "CA C9 58C1",
+ "D4 D9 58C5",
+ "C3 C5 58C7",
+ "B2 F5 58CA",
+ "BE ED 58CC",
+ "D4 DB 58D1",
+ "D4 DA 58D3",
+ "B9 E8 58D5",
+ "D4 DC 58D7",
+ "D4 DE 58D8",
+ "D4 DD 58D9",
+ "D4 E0 58DC",
+ "D4 D5 58DE",
+ "D4 E2 58DF",
+ "D4 E1 58E4",
+ "D4 DF 58E5",
+ "BB CE 58EB",
+ "BF D1 58EC",
+ "C1 D4 58EE",
+ "D4 E3 58EF",
+ "C0 BC 58F0",
+ "B0 ED 58F1",
+ "C7 E4 58F2",
+ "C4 DB 58F7",
+ "D4 E5 58F9",
+ "D4 E4 58FA",
+ "D4 E6 58FB",
+ "D4 E7 58FC",
+ "D4 E8 58FD",
+ "D4 E9 5902",
+ "CA D1 5909",
+ "D4 EA 590A",
+ "B2 C6 590F",
+ "D4 EB 5910",
+ "CD BC 5915",
+ "B3 B0 5916",
+ "D2 C9 5918",
+ "BD C8 5919",
+ "C2 BF 591A",
+ "D4 EC 591B",
+ "CC EB 591C",
+ "CC B4 5922",
+ "D4 EE 5925",
+ "C2 E7 5927",
+ "C5 B7 5929",
+ "C2 C0 592A",
+ "C9 D7 592B",
+ "D4 EF 592C",
+ "D4 F0 592D",
+ "B1 FB 592E",
+ "BC BA 5931",
+ "D4 F1 5932",
+ "B0 D0 5937",
+ "D4 F2 5938",
+ "D4 F3 593E",
+ "B1 E2 5944",
+ "B4 F1 5947",
+ "C6 E0 5948",
+ "CA F4 5949",
+ "D4 F7 594E",
+ "C1 D5 594F",
+ "D4 F6 5950",
+ "B7 C0 5951",
+ "CB DB 5954",
+ "D4 F5 5955",
+ "C5 E5 5957",
+ "D4 F9 5958",
+ "D4 F8 595A",
+ "D4 FB 5960",
+ "D4 FA 5962",
+ "B1 FC 5965",
+ "D4 FC 5967",
+ "BE A9 5968",
+ "D4 FE 5969",
+ "C3 A5 596A",
+ "D4 FD 596C",
+ "CA B3 596E",
+ "BD F7 5973",
+ "C5 DB 5974",
+ "D5 A1 5978",
+ "B9 A5 597D",
+ "D5 A2 5981",
+ "C7 A1 5982",
+ "C8 DE 5983",
+ "CC D1 5984",
+ "C7 A5 598A",
+ "D5 AB 598D",
+ "B5 B8 5993",
+ "CD C5 5996",
+ "CC AF 5999",
+ "D6 AC 599B",
+ "D5 A3 599D",
+ "D5 A6 59A3",
+ "C2 C5 59A5",
+ "CB B8 59A8",
+ "C5 CA 59AC",
+ "D5 A7 59B2",
+ "CB E5 59B9",
+ "BA CA 59BB",
+ "BE AA 59BE",
+ "D5 A8 59C6",
+ "BB D0 59C9",
+ "BB CF 59CB",
+ "B0 B9 59D0",
+ "B8 C8 59D1",
+ "C0 AB 59D3",
+ "B0 D1 59D4",
+ "D5 AC 59D9",
+ "D5 AD 59DA",
+ "D5 AA 59DC",
+ "B1 B8 59E5",
+ "B4 AF 59E6",
+ "D5 A9 59E8",
+ "CC C5 59EA",
+ "C9 B1 59EB",
+ "B0 A8 59F6",
+ "B0 F9 59FB",
+ "BB D1 59FF",
+ "B0 D2 5A01",
+ "B0 A3 5A03",
+ "D5 B2 5A09",
+ "D5 B0 5A11",
+ "CC BC 5A18",
+ "D5 B3 5A1A",
+ "D5 B1 5A1C",
+ "D5 AF 5A1F",
+ "BF B1 5A20",
+ "D5 AE 5A25",
+ "CA DA 5A29",
+ "B8 E4 5A2F",
+ "D5 B7 5A35",
+ "D5 B8 5A36",
+ "BE AB 5A3C",
+ "D5 B4 5A40",
+ "CF AC 5A41",
+ "C7 CC 5A46",
+ "D5 B6 5A49",
+ "BA A7 5A5A",
+ "D5 B9 5A62",
+ "C9 D8 5A66",
+ "D5 BA 5A6A",
+ "D5 B5 5A6C",
+ "CC BB 5A7F",
+ "C7 DE 5A92",
+ "D5 BB 5A9A",
+ "C9 B2 5A9B",
+ "D5 BC 5ABC",
+ "D5 C0 5ABD",
+ "D5 BD 5ABE",
+ "B2 C7 5AC1",
+ "D5 BF 5AC2",
+ "BC BB 5AC9",
+ "D5 BE 5ACB",
+ "B7 F9 5ACC",
+ "D5 CC 5AD0",
+ "D5 C5 5AD6",
+ "D5 C2 5AD7",
+ "C3 E4 5AE1",
+ "D5 C1 5AE3",
+ "D5 C3 5AE6",
+ "D5 C4 5AE9",
+ "D5 C6 5AFA",
+ "D5 C7 5AFB",
+ "B4 F2 5B09",
+ "D5 C9 5B0B",
+ "D5 C8 5B0C",
+ "D5 CA 5B16",
+ "BE EE 5B22",
+ "D5 CD 5B2A",
+ "C4 DC 5B2C",
+ "B1 C5 5B30",
+ "D5 CB 5B32",
+ "D5 CE 5B36",
+ "D5 CF 5B3E",
+ "D5 D2 5B40",
+ "D5 D0 5B43",
+ "D5 D1 5B45",
+ "BB D2 5B50",
+ "D5 D3 5B51",
+ "B9 A6 5B54",
+ "D5 D4 5B55",
+ "BB FA 5B57",
+ "C2 B8 5B58",
+ "D5 D5 5B5A",
+ "D5 D6 5B5B",
+ "BB DA 5B5C",
+ "B9 A7 5B5D",
+ "CC D2 5B5F",
+ "B5 A8 5B63",
+ "B8 C9 5B64",
+ "D5 D7 5B65",
+ "B3 D8 5B66",
+ "D5 D8 5B69",
+ "C2 B9 5B6B",
+ "D5 D9 5B70",
+ "D6 A3 5B71",
+ "D5 DA 5B73",
+ "D5 DB 5B75",
+ "D5 DC 5B78",
+ "D5 DE 5B7A",
+ "D5 DF 5B80",
+ "D5 E0 5B83",
+ "C2 F0 5B85",
+ "B1 A7 5B87",
+ "BC E9 5B88",
+ "B0 C2 5B89",
+ "C1 D7 5B8B",
+ "B4 B0 5B8C",
+ "BC B5 5B8D",
+ "B9 A8 5B8F",
+ "C5 E6 5B95",
+ "BD A1 5B97",
+ "B4 B1 5B98",
+ "C3 E8 5B99",
+ "C4 EA 5B9A",
+ "B0 B8 5B9B",
+ "B5 B9 5B9C",
+ "CA F5 5B9D",
+ "BC C2 5B9F",
+ "B5 D2 5BA2",
+ "C0 EB 5BA3",
+ "BC BC 5BA4",
+ "CD A8 5BA5",
+ "D5 E1 5BA6",
+ "B5 DC 5BAE",
+ "BA CB 5BB0",
+ "B3 B2 5BB3",
+ "B1 E3 5BB4",
+ "BE AC 5BB5",
+ "B2 C8 5BB6",
+ "D5 E2 5BB8",
+ "CD C6 5BB9",
+ "BD C9 5BBF",
+ "BC E4 5BC2",
+ "D5 E3 5BC3",
+ "B4 F3 5BC4",
+ "C6 D2 5BC5",
+ "CC A9 5BC6",
+ "D5 E4 5BC7",
+ "D5 E5 5BC9",
+ "C9 D9 5BCC",
+ "D5 E7 5BD0",
+ "B4 A8 5BD2",
+ "B6 F7 5BD3",
+ "D5 E6 5BD4",
+ "B4 B2 5BDB",
+ "BF B2 5BDD",
+ "D5 EB 5BDE",
+ "BB A1 5BDF",
+ "B2 C9 5BE1",
+ "D5 EA 5BE2",
+ "D5 E8 5BE4",
+ "D5 EC 5BE5",
+ "D5 E9 5BE6",
+ "C7 AB 5BE7",
+ "DC CD 5BE8",
+ "BF B3 5BE9",
+ "D5 ED 5BEB",
+ "CE C0 5BEE",
+ "D5 EE 5BF0",
+ "D5 F0 5BF3",
+ "C3 FE 5BF5",
+ "D5 EF 5BF6",
+ "C0 A3 5BF8",
+ "BB FB 5BFA",
+ "C2 D0 5BFE",
+ "BC F7 5BFF",
+ "C9 F5 5C01",
+ "C0 EC 5C02",
+ "BC CD 5C04",
+ "D5 F1 5C05",
+ "BE AD 5C06",
+ "D5 F2 5C07",
+ "D5 F3 5C08",
+ "B0 D3 5C09",
+ "C2 BA 5C0A",
+ "BF D2 5C0B",
+ "D5 F4 5C0D",
+ "C6 B3 5C0E",
+ "BE AE 5C0F",
+ "BE AF 5C11",
+ "D5 F5 5C13",
+ "C0 ED 5C16",
+ "BE B0 5C1A",
+ "D5 F6 5C20",
+ "D5 F7 5C22",
+ "CC E0 5C24",
+ "D5 F8 5C28",
+ "B6 C6 5C2D",
+ "BD A2 5C31",
+ "D5 F9 5C38",
+ "D5 FA 5C39",
+ "BC DC 5C3A",
+ "BF AC 5C3B",
+ "C6 F4 5C3C",
+ "BF D4 5C3D",
+ "C8 F8 5C3E",
+ "C7 A2 5C3F",
+ "B6 C9 5C40",
+ "D5 FB 5C41",
+ "B5 EF 5C45",
+ "D5 FC 5C46",
+ "B6 FE 5C48",
+ "C6 CF 5C4A",
+ "B2 B0 5C4B",
+ "BB D3 5C4D",
+ "D5 FD 5C4E",
+ "D6 A2 5C4F",
+ "D6 A1 5C50",
+ "B6 FD 5C51",
+ "D5 FE 5C53",
+ "C5 B8 5C55",
+ "C2 B0 5C5E",
+ "C5 CB 5C60",
+ "BC C8 5C61",
+ "C1 D8 5C64",
+ "CD FA 5C65",
+ "D6 A4 5C6C",
+ "D6 A5 5C6E",
+ "C6 D6 5C6F",
+ "BB B3 5C71",
+ "D6 A7 5C76",
+ "D6 A8 5C79",
+ "D6 A9 5C8C",
+ "B4 F4 5C90",
+ "D6 AA 5C91",
+ "D6 AB 5C94",
+ "B2 AC 5CA1",
+ "C1 BB 5CA8",
+ "B4 E4 5CA9",
+ "D6 AD 5CAB",
+ "CC A8 5CAC",
+ "C2 D2 5CB1",
+ "B3 D9 5CB3",
+ "D6 AF 5CB6",
+ "D6 B1 5CB7",
+ "B4 DF 5CB8",
+ "D6 AE 5CBB",
+ "D6 B0 5CBC",
+ "D6 B3 5CBE",
+ "D6 B2 5CC5",
+ "D6 B4 5CC7",
+ "D6 B5 5CD9",
+ "C6 BD 5CE0",
+ "B6 AE 5CE1",
+ "B2 E5 5CE8",
+ "D6 B6 5CE9",
+ "D6 BB 5CEA",
+ "D6 B9 5CED",
+ "CA F7 5CEF",
+ "CA F6 5CF0",
+ "C5 E7 5CF6",
+ "D6 B8 5CFA",
+ "BD D4 5CFB",
+ "D6 B7 5CFD",
+ "BF F2 5D07",
+ "D6 BC 5D0B",
+ "BA EA 5D0E",
+ "D6 C2 5D11",
+ "D6 C3 5D14",
+ "D6 BD 5D15",
+ "B3 B3 5D16",
+ "D6 BE 5D17",
+ "D6 C7 5D18",
+ "D6 C6 5D19",
+ "D6 C5 5D1A",
+ "D6 C1 5D1B",
+ "D6 C0 5D1F",
+ "D6 C4 5D22",
+ "CA F8 5D29",
+ "D6 CB 5D4B",
+ "D6 C8 5D4C",
+ "D6 CA 5D4E",
+ "CD F2 5D50",
+ "D6 C9 5D52",
+ "D6 BF 5D5C",
+ "BF F3 5D69",
+ "D6 CC 5D6C",
+ "BA B7 5D6F",
+ "D6 CD 5D73",
+ "D6 CE 5D76",
+ "D6 D1 5D82",
+ "D6 D0 5D84",
+ "D6 CF 5D87",
+ "C5 E8 5D8B",
+ "D6 BA 5D8C",
+ "D6 D7 5D90",
+ "D6 D3 5D9D",
+ "D6 D2 5DA2",
+ "D6 D4 5DAC",
+ "D6 D5 5DAE",
+ "D6 D8 5DB7",
+ "CE E6 5DBA",
+ "D6 D9 5DBC",
+ "D6 D6 5DBD",
+ "D6 DA 5DC9",
+ "B4 E0 5DCC",
+ "D6 DB 5DCD",
+ "D6 DD 5DD2",
+ "D6 DC 5DD3",
+ "D6 DE 5DD6",
+ "D6 DF 5DDB",
+ "C0 EE 5DDD",
+ "BD A3 5DDE",
+ "BD E4 5DE1",
+ "C1 E3 5DE3",
+ "B9 A9 5DE5",
+ "BA B8 5DE6",
+ "B9 AA 5DE7",
+ "B5 F0 5DE8",
+ "D6 E0 5DEB",
+ "BA B9 5DEE",
+ "B8 CA 5DF1",
+ "D6 E1 5DF2",
+ "CC A6 5DF3",
+ "C7 C3 5DF4",
+ "D6 E2 5DF5",
+ "B9 AB 5DF7",
+ "B4 AC 5DFB",
+ "C3 A7 5DFD",
+ "B6 D2 5DFE",
+ "BB D4 5E02",
+ "C9 DB 5E03",
+ "C8 C1 5E06",
+ "D6 E3 5E0B",
+ "B4 F5 5E0C",
+ "D6 E6 5E11",
+ "C4 A1 5E16",
+ "D6 E5 5E19",
+ "D6 E4 5E1A",
+ "D6 E7 5E1B",
+ "C4 EB 5E1D",
+ "BF E3 5E25",
+ "BB D5 5E2B",
+ "C0 CA 5E2D",
+ "C2 D3 5E2F",
+ "B5 A2 5E30",
+ "C4 A2 5E33",
+ "D6 E8 5E36",
+ "D6 E9 5E37",
+ "BE EF 5E38",
+ "CB B9 5E3D",
+ "D6 EC 5E40",
+ "D6 EB 5E43",
+ "D6 EA 5E44",
+ "C9 FD 5E45",
+ "D6 F3 5E47",
+ "CB DA 5E4C",
+ "D6 ED 5E4E",
+ "D6 EF 5E54",
+ "CB EB 5E55",
+ "D6 EE 5E57",
+ "D6 F0 5E5F",
+ "C8 A8 5E61",
+ "D6 F1 5E62",
+ "CA BE 5E63",
+ "D6 F2 5E64",
+ "B4 B3 5E72",
+ "CA BF 5E73",
+ "C7 AF 5E74",
+ "D6 F4 5E75",
+ "D6 F5 5E76",
+ "B9 AC 5E78",
+ "B4 B4 5E79",
+ "D6 F6 5E7A",
+ "B8 B8 5E7B",
+ "CD C4 5E7C",
+ "CD A9 5E7D",
+ "B4 F6 5E7E",
+ "D6 F8 5E7F",
+ "C4 A3 5E81",
+ "B9 AD 5E83",
+ "BE B1 5E84",
+ "C8 DF 5E87",
+ "BE B2 5E8A",
+ "BD F8 5E8F",
+ "C4 EC 5E95",
+ "CA F9 5E96",
+ "C5 B9 5E97",
+ "B9 AE 5E9A",
+ "C9 DC 5E9C",
+ "D6 F9 5EA0",
+ "C5 D9 5EA6",
+ "BA C2 5EA7",
+ "B8 CB 5EAB",
+ "C4 ED 5EAD",
+ "B0 C3 5EB5",
+ "BD EE 5EB6",
+ "B9 AF 5EB7",
+ "CD C7 5EB8",
+ "D6 FA 5EC1",
+ "D6 FB 5EC2",
+ "C7 D1 5EC3",
+ "D6 FC 5EC8",
+ "CE F7 5EC9",
+ "CF AD 5ECA",
+ "D6 FE 5ECF",
+ "D6 FD 5ED0",
+ "B3 C7 5ED3",
+ "D7 A1 5ED6",
+ "D7 A4 5EDA",
+ "D7 A5 5EDB",
+ "D7 A3 5EDD",
+ "C9 C0 5EDF",
+ "BE B3 5EE0",
+ "D7 A7 5EE1",
+ "D7 A6 5EE2",
+ "D7 A2 5EE3",
+ "D7 A8 5EE8",
+ "D7 A9 5EE9",
+ "D7 AA 5EEC",
+ "D7 AD 5EF0",
+ "D7 AB 5EF1",
+ "D7 AC 5EF3",
+ "D7 AE 5EF4",
+ "B1 E4 5EF6",
+ "C4 EE 5EF7",
+ "D7 AF 5EF8",
+ "B7 FA 5EFA",
+ "B2 F6 5EFB",
+ "C7 B6 5EFC",
+ "D7 B0 5EFE",
+ "C6 FB 5EFF",
+ "CA DB 5F01",
+ "D7 B1 5F03",
+ "CF AE 5F04",
+ "D7 B2 5F09",
+ "CA C0 5F0A",
+ "D7 B5 5F0B",
+ "D0 A1 5F0C",
+ "D0 B1 5F0D",
+ "BC B0 5F0F",
+ "C6 F5 5F10",
+ "D7 B6 5F11",
+ "B5 DD 5F13",
+ "C4 A4 5F14",
+ "B0 FA 5F15",
+ "D7 B7 5F16",
+ "CA A6 5F17",
+ "B9 B0 5F18",
+ "C3 D0 5F1B",
+ "C4 EF 5F1F",
+ "CC EF 5F25",
+ "B8 B9 5F26",
+ "B8 CC 5F27",
+ "D7 B8 5F29",
+ "D7 B9 5F2D",
+ "D7 BF 5F2F",
+ "BC E5 5F31",
+ "C4 A5 5F35",
+ "B6 AF 5F37",
+ "D7 BA 5F38",
+ "C9 AB 5F3C",
+ "C3 C6 5F3E",
+ "D7 BB 5F41",
+ "D7 BC 5F48",
+ "B6 B0 5F4A",
+ "D7 BD 5F4C",
+ "D7 BE 5F4E",
+ "D7 C0 5F51",
+ "C5 F6 5F53",
+ "D7 C1 5F56",
+ "D7 C2 5F57",
+ "D7 C3 5F59",
+ "D7 B4 5F5C",
+ "D7 B3 5F5D",
+ "D7 C4 5F61",
+ "B7 C1 5F62",
+ "C9 A7 5F66",
+ "BA CC 5F69",
+ "C9 B7 5F6A",
+ "C4 A6 5F6B",
+ "C9 CB 5F6C",
+ "D7 C5 5F6D",
+ "BE B4 5F70",
+ "B1 C6 5F71",
+ "D7 C6 5F73",
+ "D7 C7 5F77",
+ "CC F2 5F79",
+ "C8 E0 5F7C",
+ "D7 CA 5F7F",
+ "B1 FD 5F80",
+ "C0 AC 5F81",
+ "D7 C9 5F82",
+ "D7 C8 5F83",
+ "B7 C2 5F84",
+ "C2 D4 5F85",
+ "D7 CE 5F87",
+ "D7 CC 5F88",
+ "D7 CB 5F8A",
+ "CE A7 5F8B",
+ "B8 E5 5F8C",
+ "BD F9 5F90",
+ "D7 CD 5F91",
+ "C5 CC 5F92",
+ "BD BE 5F93",
+ "C6 C0 5F97",
+ "D7 D1 5F98",
+ "D7 D0 5F99",
+ "D7 CF 5F9E",
+ "D7 D2 5FA0",
+ "B8 E6 5FA1",
+ "D7 D3 5FA8",
+ "C9 FC 5FA9",
+ "BD DB 5FAA",
+ "D7 D4 5FAD",
+ "C8 F9 5FAE",
+ "C6 C1 5FB3",
+ "C4 A7 5FB4",
+ "C5 B0 5FB9",
+ "D7 D5 5FBC",
+ "B5 AB 5FBD",
+ "BF B4 5FC3",
+ "C9 AC 5FC5",
+ "B4 F7 5FCC",
+ "C7 A6 5FCD",
+ "D7 D6 5FD6",
+ "BB D6 5FD7",
+ "CB BA 5FD8",
+ "CB BB 5FD9",
+ "B1 FE 5FDC",
+ "D7 DB 5FDD",
+ "C3 E9 5FE0",
+ "D7 D8 5FE4",
+ "B2 F7 5FEB",
+ "D8 AD 5FF0",
+ "D7 DA 5FF1",
+ "C7 B0 5FF5",
+ "D7 D9 5FF8",
+ "D7 D7 5FFB",
+ "B9 FA 5FFD",
+ "D7 DD 5FFF",
+ "D7 E3 600E",
+ "D7 E9 600F",
+ "D7 E1 6010",
+ "C5 DC 6012",
+ "D7 E6 6015",
+ "C9 DD 6016",
+ "D7 E0 6019",
+ "D7 E5 601B",
+ "CE E7 601C",
+ "BB D7 601D",
+ "C2 D5 6020",
+ "D7 DE 6021",
+ "B5 DE 6025",
+ "D7 E8 6026",
+ "C0 AD 6027",
+ "B1 E5 6028",
+ "D7 E2 6029",
+ "B2 F8 602A",
+ "D7 E7 602B",
+ "B6 B1 602F",
+ "D7 E4 6031",
+ "D7 EA 603A",
+ "D7 EC 6041",
+ "D7 F6 6042",
+ "D7 F4 6043",
+ "D7 F1 6046",
+ "D7 F0 604A",
+ "CE F8 604B",
+ "D7 F2 604D",
+ "B6 B2 6050",
+ "B9 B1 6052",
+ "BD FA 6055",
+ "D7 F9 6059",
+ "D7 EB 605A",
+ "D7 EF 605F",
+ "D7 DF 6060",
+ "B2 FA 6062",
+ "D7 F3 6063",
+ "D7 F5 6064",
+ "C3 D1 6065",
+ "BA A8 6068",
+ "B2 B8 6069",
+ "D7 ED 606A",
+ "D7 F8 606B",
+ "D7 F7 606C",
+ "B6 B3 606D",
+ "C2 A9 606F",
+ "B3 E6 6070",
+ "B7 C3 6075",
+ "D7 EE 6077",
+ "D7 FA 6081",
+ "D7 FD 6083",
+ "D8 A1 6084",
+ "BC BD 6089",
+ "D8 A7 608B",
+ "C4 F0 608C",
+ "D7 FB 608D",
+ "D8 A5 6092",
+ "B2 F9 6094",
+ "D8 A3 6096",
+ "D8 A4 6097",
+ "D7 FE 609A",
+ "D8 A2 609B",
+ "B8 E7 609F",
+ "CD AA 60A0",
+ "B4 B5 60A3",
+ "B1 D9 60A6",
+ "D8 A6 60A7",
+ "C7 BA 60A9",
+ "B0 AD 60AA",
+ "C8 E1 60B2",
+ "D7 DC 60B3",
+ "D8 AC 60B4",
+ "D8 B0 60B5",
+ "CC E5 60B6",
+ "D8 A9 60B8",
+ "C5 E9 60BC",
+ "D8 AE 60BD",
+ "BE F0 60C5",
+ "D8 AF 60C6",
+ "C6 D7 60C7",
+ "CF C7 60D1",
+ "D8 AB 60D3",
+ "D8 B1 60D8",
+ "B9 FB 60DA",
+ "C0 CB 60DC",
+ "B0 D4 60DF",
+ "D8 AA 60E0",
+ "D8 A8 60E1",
+ "C1 DA 60E3",
+ "D7 FC 60E7",
+ "BB B4 60E8",
+ "C2 C6 60F0",
+ "D8 BD 60F1",
+ "C1 DB 60F3",
+ "D8 B8 60F4",
+ "D8 B5 60F6",
+ "D8 B6 60F7",
+ "BC E6 60F9",
+ "D8 B9 60FA",
+ "D8 BC 60FB",
+ "D8 B7 6100",
+ "BD A5 6101",
+ "D8 BA 6103",
+ "D8 B4 6106",
+ "CC FC 6108",
+ "CC FB 6109",
+ "D8 BE 610D",
+ "D8 BF 610E",
+ "B0 D5 610F",
+ "D8 B3 6115",
+ "B6 F2 611A",
+ "B0 A6 611B",
+ "B4 B6 611F",
+ "D8 BB 6121",
+ "D8 C3 6127",
+ "D8 C2 6128",
+ "D8 C7 612C",
+ "D8 C8 6134",
+ "D8 C6 613C",
+ "D8 C9 613D",
+ "D8 C1 613E",
+ "D8 C5 613F",
+ "D8 CA 6142",
+ "D8 CB 6144",
+ "D8 C0 6147",
+ "BB FC 6148",
+ "D8 C4 614A",
+ "C2 D6 614B",
+ "B9 B2 614C",
+ "D8 B2 614D",
+ "BF B5 614E",
+ "D8 D8 6153",
+ "CA E9 6155",
+ "D8 CE 6158",
+ "D8 CF 6159",
+ "D8 D0 615A",
+ "D8 D7 615D",
+ "D8 D6 615F",
+ "CB FD 6162",
+ "B4 B7 6163",
+ "D8 D4 6165",
+ "B7 C5 6167",
+ "B3 B4 6168",
+ "D8 D1 616B",
+ "CE B8 616E",
+ "D8 D3 616F",
+ "B0 D6 6170",
+ "D8 D5 6171",
+ "D8 CC 6173",
+ "D8 D2 6174",
+ "D8 D9 6175",
+ "B7 C4 6176",
+ "D8 CD 6177",
+ "CD DD 617E",
+ "CD AB 6182",
+ "D8 DC 6187",
+ "D8 E0 618A",
+ "C1 FE 618E",
+ "CE F9 6190",
+ "D8 E1 6191",
+ "D8 DE 6194",
+ "D8 DB 6196",
+ "D8 DA 6199",
+ "D8 DF 619A",
+ "CA B0 61A4",
+ "C6 B4 61A7",
+ "B7 C6 61A9",
+ "D8 E2 61AB",
+ "D8 DD 61AC",
+ "D8 E3 61AE",
+ "B7 FB 61B2",
+ "B2 B1 61B6",
+ "D8 EB 61BA",
+ "B4 B8 61BE",
+ "D8 E9 61C3",
+ "D8 EA 61C6",
+ "BA A9 61C7",
+ "D8 E8 61C8",
+ "D8 E6 61C9",
+ "D8 E5 61CA",
+ "D8 EC 61CB",
+ "D8 E4 61CC",
+ "D8 EE 61CD",
+ "B2 FB 61D0",
+ "D8 F0 61E3",
+ "D8 EF 61E6",
+ "C4 A8 61F2",
+ "D8 F3 61F4",
+ "D8 F1 61F6",
+ "D8 E7 61F7",
+ "B7 FC 61F8",
+ "D8 F2 61FA",
+ "D8 F6 61FC",
+ "D8 F5 61FD",
+ "D8 F7 61FE",
+ "D8 F4 61FF",
+ "D8 F8 6200",
+ "D8 F9 6208",
+ "D8 FA 6209",
+ "CA EA 620A",
+ "D8 FC 620C",
+ "D8 FB 620D",
+ "BD BF 620E",
+ "C0 AE 6210",
+ "B2 E6 6211",
+ "B2 FC 6212",
+ "D8 FD 6214",
+ "B0 BF 6216",
+ "C0 CC 621A",
+ "D8 FE 621B",
+ "EC C3 621D",
+ "D9 A1 621E",
+ "B7 E1 621F",
+ "D9 A2 6221",
+ "C0 EF 6226",
+ "D9 A3 622A",
+ "D9 A4 622E",
+ "B5 BA 622F",
+ "D9 A5 6230",
+ "D9 A6 6232",
+ "D9 A7 6233",
+ "C2 D7 6234",
+ "B8 CD 6238",
+ "CC E1 623B",
+ "CB BC 623F",
+ "BD EA 6240",
+ "D9 A8 6241",
+ "C0 F0 6247",
+ "EE BD 6248",
+ "C8 E2 6249",
+ "BC EA 624B",
+ "BA CD 624D",
+ "D9 A9 624E",
+ "C2 C7 6253",
+ "CA A7 6255",
+ "C2 F1 6258",
+ "D9 AC 625B",
+ "D9 AA 625E",
+ "D9 AD 6260",
+ "D9 AB 6263",
+ "D9 AE 6268",
+ "CA B1 626E",
+ "B0 B7 6271",
+ "C9 DE 6276",
+ "C8 E3 6279",
+ "D9 AF 627C",
+ "D9 B2 627E",
+ "BE B5 627F",
+ "B5 BB 6280",
+ "D9 B0 6282",
+ "D9 B7 6283",
+ "BE B6 6284",
+ "D9 B1 6289",
+ "C7 C4 628A",
+ "CD DE 6291",
+ "D9 B3 6292",
+ "D9 B4 6293",
+ "D9 B8 6294",
+ "C5 EA 6295",
+ "D9 B5 6296",
+ "B9 B3 6297",
+ "C0 DE 6298",
+ "D9 C6 629B",
+ "C8 B4 629C",
+ "C2 F2 629E",
+ "C8 E4 62AB",
+ "DA AD 62AC",
+ "CA FA 62B1",
+ "C4 F1 62B5",
+ "CB F5 62B9",
+ "D9 BB 62BB",
+ "B2 A1 62BC",
+ "C3 EA 62BD",
+ "D9 C4 62C2",
+ "C3 B4 62C5",
+ "D9 BE 62C6",
+ "D9 C5 62C7",
+ "D9 C0 62C8",
+ "D9 C7 62C9",
+ "D9 C3 62CA",
+ "D9 C2 62CC",
+ "C7 EF 62CD",
+ "D9 BC 62CF",
+ "B2 FD 62D0",
+ "D9 BA 62D1",
+ "B5 F1 62D2",
+ "C2 F3 62D3",
+ "D9 B6 62D4",
+ "D9 B9 62D7",
+ "B9 B4 62D8",
+ "C0 DB 62D9",
+ "BE B7 62DB",
+ "D9 C1 62DC",
+ "C7 D2 62DD",
+ "B5 F2 62E0",
+ "B3 C8 62E1",
+ "B3 E7 62EC",
+ "BF A1 62ED",
+ "D9 C9 62EE",
+ "D9 CE 62EF",
+ "D9 CA 62F1",
+ "B7 FD 62F3",
+ "D9 CF 62F5",
+ "BB A2 62F6",
+ "B9 E9 62F7",
+ "BD A6 62FE",
+ "D9 BD 62FF",
+ "BB FD 6301",
+ "D9 CC 6302",
+ "BB D8 6307",
+ "D9 CD 6308",
+ "B0 C4 6309",
+ "D9 C8 630C",
+ "C4 A9 6311",
+ "B5 F3 6319",
+ "B6 B4 631F",
+ "D9 CB 6327",
+ "B0 A7 6328",
+ "BA C3 632B",
+ "BF B6 632F",
+ "C4 F2 633A",
+ "C8 D4 633D",
+ "D9 D1 633E",
+ "C1 DE 633F",
+ "C2 AA 6349",
+ "BB AB 634C",
+ "D9 D2 634D",
+ "D9 D4 634F",
+ "D9 D0 6350",
+ "CA E1 6355",
+ "C4 BD 6357",
+ "C1 DC 635C",
+ "CA FB 6367",
+ "BC CE 6368",
+ "D9 E0 6369",
+ "D9 DF 636B",
+ "BF F8 636E",
+ "B7 FE 6372",
+ "D9 D9 6376",
+ "BE B9 6377",
+ "C6 E8 637A",
+ "C7 B1 637B",
+ "D9 D7 6380",
+ "C1 DD 6383",
+ "BC F8 6388",
+ "D9 DC 6389",
+ "BE B8 638C",
+ "D9 D6 638E",
+ "D9 DB 638F",
+ "C7 D3 6392",
+ "D9 D5 6396",
+ "B7 A1 6398",
+ "B3 DD 639B",
+ "D9 DD 639F",
+ "CE AB 63A0",
+ "BA CE 63A1",
+ "C3 B5 63A2",
+ "D9 DA 63A3",
+ "C0 DC 63A5",
+ "B9 B5 63A7",
+ "BF E4 63A8",
+ "B1 E6 63A9",
+ "C1 BC 63AA",
+ "D9 D8 63AB",
+ "B5 C5 63AC",
+ "B7 C7 63B2",
+ "C4 CF 63B4",
+ "D9 DE 63B5",
+ "C1 DF 63BB",
+ "D9 E1 63BE",
+ "D9 E3 63C0",
+ "C2 B7 63C3",
+ "D9 E9 63C4",
+ "D9 E4 63C6",
+ "D9 E6 63C9",
+ "C9 C1 63CF",
+ "C4 F3 63D0",
+ "D9 E7 63D2",
+ "CD AC 63D6",
+ "CD C8 63DA",
+ "B4 B9 63DB",
+ "B0 AE 63E1",
+ "D9 E5 63E3",
+ "D9 E2 63E9",
+ "B4 F8 63EE",
+ "B1 E7 63F4",
+ "D9 E8 63F6",
+ "CD C9 63FA",
+ "D9 EC 6406",
+ "C2 BB 640D",
+ "D9 F3 640F",
+ "D9 ED 6413",
+ "D9 EA 6416",
+ "D9 F1 6417",
+ "D9 D3 641C",
+ "D9 EE 6426",
+ "D9 F2 6428",
+ "C8 C2 642C",
+ "C5 EB 642D",
+ "D9 EB 6434",
+ "D9 EF 6436",
+ "B7 C8 643A",
+ "BA F1 643E",
+ "C0 DD 6442",
+ "D9 F7 644E",
+ "C5 A6 6458",
+ "D9 F4 6467",
+ "CB E0 6469",
+ "D9 F5 646F",
+ "D9 F6 6476",
+ "CC CE 6478",
+ "C0 A2 647A",
+ "B7 E2 6483",
+ "D9 FD 6488",
+ "BB B5 6492",
+ "D9 FA 6493",
+ "D9 F9 6495",
+ "C7 B2 649A",
+ "C6 B5 649E",
+ "C5 B1 64A4",
+ "D9 FB 64A5",
+ "D9 FC 64A9",
+ "C9 EF 64AB",
+ "C7 C5 64AD",
+ "BB A3 64AE",
+ "C0 F1 64B0",
+ "CB D0 64B2",
+ "B3 C9 64B9",
+ "DA A5 64BB",
+ "D9 FE 64BC",
+ "CD CA 64C1",
+ "DA A7 64C2",
+ "DA A3 64C5",
+ "DA A4 64C7",
+ "C1 E0 64CD",
+ "DA A2 64D2",
+ "D9 BF 64D4",
+ "DA A6 64D8",
+ "DA A1 64DA",
+ "DA AB 64E0",
+ "DA AC 64E1",
+ "C5 A7 64E2",
+ "DA AE 64E3",
+ "BB A4 64E6",
+ "DA A9 64E7",
+ "B5 BC 64EC",
+ "DA AF 64EF",
+ "DA A8 64F1",
+ "DA B3 64F2",
+ "DA B2 64F4",
+ "DA B1 64F6",
+ "DA B4 64FA",
+ "DA B6 64FD",
+ "BE F1 64FE",
+ "DA B5 6500",
+ "DA B9 6505",
+ "DA B7 6518",
+ "DA B8 651C",
+ "D9 F0 651D",
+ "DA BB 6523",
+ "DA BA 6524",
+ "D9 F8 652A",
+ "DA BC 652B",
+ "DA B0 652C",
+ "BB D9 652F",
+ "DA BD 6534",
+ "DA BE 6535",
+ "DA C0 6536",
+ "DA BF 6537",
+ "DA C1 6538",
+ "B2 FE 6539",
+ "B9 B6 653B",
+ "CA FC 653E",
+ "C0 AF 653F",
+ "B8 CE 6545",
+ "DA C3 6548",
+ "DA C6 654D",
+ "C9 D2 654F",
+ "B5 DF 6551",
+ "DA C5 6555",
+ "DA C4 6556",
+ "C7 D4 6557",
+ "DA C7 6558",
+ "B6 B5 6559",
+ "DA C9 655D",
+ "DA C8 655E",
+ "B4 BA 6562",
+ "BB B6 6563",
+ "C6 D8 6566",
+ "B7 C9 656C",
+ "BF F4 6570",
+ "DA CA 6572",
+ "C0 B0 6574",
+ "C5 A8 6575",
+ "C9 DF 6577",
+ "DA CB 6578",
+ "DA CC 6582",
+ "DA CD 6583",
+ "CA B8 6587",
+ "D5 DD 6588",
+ "C0 C6 6589",
+ "C9 CC 658C",
+ "BA D8 658E",
+ "C8 E5 6590",
+ "C8 C3 6591",
+ "C5 CD 6597",
+ "CE C1 6599",
+ "DA CF 659B",
+ "BC D0 659C",
+ "DA D0 659F",
+ "B0 B6 65A1",
+ "B6 D4 65A4",
+ "C0 CD 65A5",
+ "C9 E0 65A7",
+ "DA D1 65AB",
+ "BB C2 65AC",
+ "C3 C7 65AD",
+ "BB DB 65AF",
+ "BF B7 65B0",
+ "DA D2 65B7",
+ "CA FD 65B9",
+ "B1 F7 65BC",
+ "BB DC 65BD",
+ "DA D5 65C1",
+ "DA D3 65C3",
+ "DA D6 65C4",
+ "CE B9 65C5",
+ "DA D4 65C6",
+ "C0 FB 65CB",
+ "DA D7 65CC",
+ "C2 B2 65CF",
+ "DA D8 65D2",
+ "B4 FA 65D7",
+ "DA DA 65D9",
+ "DA D9 65DB",
+ "DA DB 65E0",
+ "DA DC 65E1",
+ "B4 FB 65E2",
+ "C6 FC 65E5",
+ "C3 B6 65E6",
+ "B5 EC 65E7",
+ "BB DD 65E8",
+ "C1 E1 65E9",
+ "BD DC 65EC",
+ "B0 B0 65ED",
+ "DA DD 65F1",
+ "B2 A2 65FA",
+ "DA E1 65FB",
+ "B9 B7 6602",
+ "DA E0 6603",
+ "BA AB 6606",
+ "BE BA 6607",
+ "DA DF 660A",
+ "BE BB 660C",
+ "CC C0 660E",
+ "BA AA 660F",
+ "B0 D7 6613",
+ "C0 CE 6614",
+ "DA E6 661C",
+ "C0 B1 661F",
+ "B1 C7 6620",
+ "BD D5 6625",
+ "CB E6 6627",
+ "BA F2 6628",
+ "BE BC 662D",
+ "C0 A7 662F",
+ "DA E5 6634",
+ "DA E3 6635",
+ "DA E4 6636",
+ "C3 EB 663C",
+ "DB A6 663F",
+ "DA EA 6641",
+ "BB FE 6642",
+ "B9 B8 6643",
+ "DA E8 6644",
+ "DA E9 6649",
+ "BF B8 664B",
+ "DA E7 664F",
+ "BB AF 6652",
+ "DA EC 665D",
+ "DA EB 665E",
+ "DA F0 665F",
+ "DA F1 6662",
+ "DA ED 6664",
+ "B3 A2 6666",
+ "DA EE 6667",
+ "DA EF 6668",
+ "C8 D5 6669",
+ "C9 E1 666E",
+ "B7 CA 666F",
+ "DA F2 6670",
+ "C0 B2 6674",
+ "BE BD 6676",
+ "C3 D2 667A",
+ "B6 C7 6681",
+ "DA F3 6683",
+ "DA F7 6684",
+ "B2 CB 6687",
+ "DA F4 6688",
+ "DA F6 6689",
+ "DA F5 668E",
+ "BD EB 6691",
+ "C3 C8 6696",
+ "B0 C5 6697",
+ "DA F8 6698",
+ "DA F9 669D",
+ "C4 AA 66A2",
+ "CE F1 66A6",
+ "BB C3 66AB",
+ "CA EB 66AE",
+ "CB BD 66B4",
+ "DB A2 66B8",
+ "DA FB 66B9",
+ "DA FE 66BC",
+ "DA FD 66BE",
+ "DA FA 66C1",
+ "DB A1 66C4",
+ "C6 DE 66C7",
+ "DA FC 66C9",
+ "DB A3 66D6",
+ "BD EC 66D9",
+ "DB A4 66DA",
+ "CD CB 66DC",
+ "C7 F8 66DD",
+ "DB A5 66E0",
+ "DB A7 66E6",
+ "DB A8 66E9",
+ "DB A9 66F0",
+ "B6 CA 66F2",
+ "B1 C8 66F3",
+ "B9 B9 66F4",
+ "DB AA 66F5",
+ "DB AB 66F7",
+ "BD F1 66F8",
+ "C1 E2 66F9",
+ "D2 D8 66FC",
+ "C1 BE 66FD",
+ "C1 BD 66FE",
+ "C2 D8 66FF",
+ "BA C7 6700",
+ "D0 F2 6703",
+ "B7 EE 6708",
+ "CD AD 6709",
+ "CA FE 670B",
+ "C9 FE 670D",
+ "DB AC 670F",
+ "BA F3 6714",
+ "C4 BF 6715",
+ "DB AD 6716",
+ "CF AF 6717",
+ "CB BE 671B",
+ "C4 AB 671D",
+ "DB AE 671E",
+ "B4 FC 671F",
+ "DB AF 6726",
+ "DB B0 6727",
+ "CC DA 6728",
+ "CC A4 672A",
+ "CB F6 672B",
+ "CB DC 672C",
+ "BB A5 672D",
+ "DB B2 672E",
+ "BC EB 6731",
+ "CB D1 6734",
+ "DB B4 6736",
+ "DB B7 6737",
+ "DB B6 6738",
+ "B4 F9 673A",
+ "B5 E0 673D",
+ "DB B3 673F",
+ "DB B5 6741",
+ "DB B8 6746",
+ "BF F9 6749",
+ "CD FB 674E",
+ "B0 C9 674F",
+ "BA E0 6750",
+ "C2 BC 6751",
+ "BC DD 6753",
+ "BE F3 6756",
+ "DB BB 6759",
+ "C5 CE 675C",
+ "DB B9 675E",
+ "C2 AB 675F",
+ "DB BA 6760",
+ "BE F2 6761",
+ "CC DD 6762",
+ "DB BC 6763",
+ "DB BD 6764",
+ "CD E8 6765",
+ "DB C2 676A",
+ "B9 BA 676D",
+ "C7 D5 676F",
+ "DB BF 6770",
+ "C5 EC 6771",
+ "DA DE 6772",
+ "DA E2 6773",
+ "B5 CF 6775",
+ "C7 C7 6777",
+ "DB C1 677C",
+ "BE BE 677E",
+ "C8 C4 677F",
+ "DB C7 6785",
+ "C8 FA 6787",
+ "DB BE 6789",
+ "DB C4 678B",
+ "DB C3 678C",
+ "C0 CF 6790",
+ "CB ED 6795",
+ "CE D3 6797",
+ "CB E7 679A",
+ "B2 CC 679C",
+ "BB DE 679D",
+ "CF C8 67A0",
+ "DB C6 67A1",
+ "BF F5 67A2",
+ "DB C5 67A6",
+ "DB C0 67A9",
+ "B8 CF 67AF",
+ "DB CC 67B3",
+ "DB CA 67B4",
+ "B2 CD 67B6",
+ "DB C8 67B7",
+ "DB CE 67B8",
+ "DB D4 67B9",
+ "C2 C8 67C1",
+ "CA C1 67C4",
+ "DB D6 67C6",
+ "C9 A2 67CA",
+ "DB D5 67CE",
+ "C7 F0 67CF",
+ "CB BF 67D0",
+ "B4 BB 67D1",
+ "C0 F7 67D3",
+ "BD C0 67D4",
+ "C4 D3 67D8",
+ "CD AE 67DA",
+ "DB D1 67DD",
+ "DB D0 67DE",
+ "DB D2 67E2",
+ "DB CF 67E4",
+ "DB D7 67E7",
+ "DB CD 67E9",
+ "DB CB 67EC",
+ "DB D3 67EE",
+ "DB C9 67EF",
+ "C3 EC 67F1",
+ "CC F8 67F3",
+ "BC C6 67F4",
+ "BA F4 67F5",
+ "BA BA 67FB",
+ "CB EF 67FE",
+ "B3 C1 67FF",
+ "C4 CE 6802",
+ "C6 CA 6803",
+ "B1 C9 6804",
+ "C0 F2 6813",
+ "C0 B4 6816",
+ "B7 AA 6817",
+ "DB D9 681E",
+ "B9 BB 6821",
+ "B3 FC 6822",
+ "DB DB 6829",
+ "B3 F4 682A",
+ "DB E1 682B",
+ "DB DE 6832",
+ "C0 F3 6834",
+ "B3 CB 6838",
+ "BA AC 6839",
+ "B3 CA 683C",
+ "BA CF 683D",
+ "DB DC 6840",
+ "B7 E5 6841",
+ "B7 CB 6842",
+ "C5 ED 6843",
+ "DB DA 6846",
+ "B0 C6 6848",
+ "DB DD 684D",
+ "DB DF 684E",
+ "B6 CD 6850",
+ "B7 AC 6851",
+ "B4 BC 6853",
+ "B5 CB 6854",
+ "DB E2 6859",
+ "BA F9 685C",
+ "CB F1 685D",
+ "BB B7 685F",
+ "DB E3 6863",
+ "C9 B0 6867",
+ "DB EF 6874",
+ "B2 B3 6876",
+ "DB E4 6877",
+ "DB F5 687E",
+ "DB E5 687F",
+ "CE C2 6881",
+ "DB EC 6883",
+ "C7 DF 6885",
+ "DB F4 688D",
+ "DB E7 688F",
+ "B0 B4 6893",
+ "DB E9 6894",
+ "B9 BC 6897",
+ "DB EB 689B",
+ "DB EA 689D",
+ "DB E6 689F",
+ "DB F1 68A0",
+ "BE BF 68A2",
+ "D4 ED 68A6",
+ "B8 E8 68A7",
+ "CD FC 68A8",
+ "DB E8 68AD",
+ "C4 F4 68AF",
+ "B3 A3 68B0",
+ "BA AD 68B1",
+ "DB E0 68B3",
+ "DB F0 68B5",
+ "B3 E1 68B6",
+ "DB EE 68B9",
+ "DB F2 68BA",
+ "C5 EE 68BC",
+ "B4 FE 68C4",
+ "DC B2 68C6",
+ "CC C9 68C9",
+ "DB F7 68CA",
+ "B4 FD 68CB",
+ "DB FE 68CD",
+ "CB C0 68D2",
+ "DC A1 68D4",
+ "DC A3 68D5",
+ "DC A7 68D7",
+ "DB F9 68D8",
+ "C3 AA 68DA",
+ "C5 EF 68DF",
+ "DC AB 68E0",
+ "DB FC 68E1",
+ "DC A8 68E3",
+ "DC A2 68E7",
+ "BF B9 68EE",
+ "DC AC 68EF",
+ "C0 B3 68F2",
+ "DC AA 68F9",
+ "B4 BD 68FA",
+ "CF D0 6900",
+ "DB F6 6901",
+ "DC A6 6904",
+ "B0 D8 6905",
+ "DB F8 6908",
+ "CC BA 690B",
+ "DB FD 690C",
+ "BF A2 690D",
+ "C4 C7 690E",
+ "DB F3 690F",
+ "DC A5 6912",
+ "BF FA 6919",
+ "DC AF 691A",
+ "B3 F1 691B",
+ "B8 A1 691C",
+ "DC B1 6921",
+ "DB FA 6922",
+ "DC B0 6923",
+ "DC A9 6925",
+ "DB FB 6926",
+ "DC AD 6928",
+ "DC AE 692A",
+ "DC BF 6930",
+ "C6 CE 6934",
+ "DC A4 6936",
+ "DC BB 6939",
+ "DC BD 693D",
+ "C4 D8 693F",
+ "CD CC 694A",
+ "C9 F6 6953",
+ "DC B8 6954",
+ "C2 CA 6955",
+ "DC BE 6959",
+ "C1 BF 695A",
+ "DC B5 695C",
+ "DC C2 695D",
+ "DC C1 695E",
+ "C6 EF 6960",
+ "DC C0 6961",
+ "C6 EA 6962",
+ "DC C4 696A",
+ "DC B7 696B",
+ "B6 C8 696D",
+ "DC BA 696E",
+ "BD DD 696F",
+ "C7 E0 6973",
+ "DC BC 6974",
+ "B6 CB 6975",
+ "DC B4 6977",
+ "DC B6 6978",
+ "DC B3 6979",
+ "CF B0 697C",
+ "B3 DA 697D",
+ "DC B9 697E",
+ "DC C3 6981",
+ "B3 B5 6982",
+ "BA E7 698A",
+ "B1 DD 698E",
+ "DC D4 6991",
+ "CF B1 6994",
+ "DC D7 6995",
+ "BF BA 699B",
+ "DC D6 699C",
+ "DC D5 69A0",
+ "DC D2 69A7",
+ "DC C6 69AE",
+ "DC E3 69B1",
+ "DC C5 69B2",
+ "DC D8 69B4",
+ "DC D0 69BB",
+ "DC CB 69BE",
+ "DC C8 69BF",
+ "DC C9 69C1",
+ "DC D1 69C3",
+ "F4 A2 69C7",
+ "DC CE 69CA",
+ "B9 BD 69CB",
+ "C4 C8 69CC",
+ "C1 E4 69CD",
+ "DC CC 69CE",
+ "DC C7 69D0",
+ "DC CA 69D3",
+ "CD CD 69D8",
+ "CB EA 69D9",
+ "DC CF 69DD",
+ "DC D9 69DE",
+ "DC E1 69E7",
+ "DC DA 69E8",
+ "DC E7 69EB",
+ "DC E5 69ED",
+ "DC E0 69F2",
+ "DC DF 69F9",
+ "C4 D0 69FB",
+ "C1 E5 69FD",
+ "DC DD 69FF",
+ "DC DB 6A02",
+ "DC E2 6A05",
+ "DC E8 6A0A",
+ "C8 F5 6A0B",
+ "DC EE 6A0C",
+ "DC E9 6A12",
+ "DC EC 6A13",
+ "DC E6 6A14",
+ "C3 F4 6A17",
+ "C9 B8 6A19",
+ "DC DC 6A1B",
+ "DC E4 6A1E",
+ "BE C0 6A1F",
+ "CC CF 6A21",
+ "DC F8 6A22",
+ "DC EB 6A23",
+ "B8 A2 6A29",
+ "B2 A3 6A2A",
+ "B3 DF 6A2B",
+ "DC D3 6A2E",
+ "BE C1 6A35",
+ "DC F0 6A36",
+ "DC F7 6A38",
+ "BC F9 6A39",
+ "B3 F2 6A3A",
+ "C3 AE 6A3D",
+ "DC ED 6A44",
+ "DC F2 6A47",
+ "DC F6 6A48",
+ "B6 B6 6A4B",
+ "B5 CC 6A58",
+ "DC F4 6A59",
+ "B5 A1 6A5F",
+ "C6 CB 6A61",
+ "DC F3 6A62",
+ "DC F5 6A66",
+ "DC EF 6A72",
+ "DC F1 6A78",
+ "B3 E0 6A7F",
+ "C3 C9 6A80",
+ "DC FC 6A84",
+ "DC FA 6A8D",
+ "B8 E9 6A8E",
+ "DC F9 6A90",
+ "DD A1 6A97",
+ "DB D8 6A9C",
+ "DC FB 6AA0",
+ "DC FD 6AA2",
+ "DC FE 6AA3",
+ "DD AC 6AAA",
+ "DD A8 6AAC",
+ "DB ED 6AAE",
+ "DD A7 6AB3",
+ "DD A6 6AB8",
+ "DD A3 6ABB",
+ "DC EA 6AC1",
+ "DD A5 6AC2",
+ "DD A4 6AC3",
+ "DD AA 6AD1",
+ "CF A6 6AD3",
+ "DD AD 6ADA",
+ "B6 FB 6ADB",
+ "DD A9 6ADE",
+ "DD AB 6ADF",
+ "C8 A7 6AE8",
+ "DD AE 6AEA",
+ "DD B2 6AFA",
+ "DD AF 6AFB",
+ "CD F3 6B04",
+ "DD B0 6B05",
+ "DC DE 6B0A",
+ "DD B3 6B12",
+ "DD B4 6B16",
+ "B1 B5 6B1D",
+ "DD B6 6B1F",
+ "B7 E7 6B20",
+ "BC A1 6B21",
+ "B6 D5 6B23",
+ "B2 A4 6B27",
+ "CD DF 6B32",
+ "DD B8 6B37",
+ "DD B7 6B38",
+ "DD BA 6B39",
+ "B5 BD 6B3A",
+ "B6 D6 6B3D",
+ "B4 BE 6B3E",
+ "DD BD 6B43",
+ "DD BC 6B47",
+ "DD BE 6B49",
+ "B2 CE 6B4C",
+ "C3 B7 6B4E",
+ "DD BF 6B50",
+ "B4 BF 6B53",
+ "DD C1 6B54",
+ "DD C0 6B59",
+ "DD C2 6B5B",
+ "DD C3 6B5F",
+ "DD C4 6B61",
+ "BB DF 6B62",
+ "C0 B5 6B63",
+ "BA A1 6B64",
+ "C9 F0 6B66",
+ "CA E2 6B69",
+ "CF C4 6B6A",
+ "BB F5 6B6F",
+ "BA D0 6B73",
+ "CE F2 6B74",
+ "DD C5 6B78",
+ "DD C6 6B79",
+ "BB E0 6B7B",
+ "DD C7 6B7F",
+ "DD C8 6B80",
+ "DD CA 6B83",
+ "DD C9 6B84",
+ "CB D8 6B86",
+ "BD DE 6B89",
+ "BC EC 6B8A",
+ "BB C4 6B8B",
+ "DD CB 6B8D",
+ "DD CD 6B95",
+ "BF A3 6B96",
+ "DD CC 6B98",
+ "DD CE 6B9E",
+ "DD CF 6BA4",
+ "DD D0 6BAA",
+ "DD D1 6BAB",
+ "DD D2 6BAF",
+ "DD D4 6BB1",
+ "DD D3 6BB2",
+ "DD D5 6BB3",
+ "B2 A5 6BB4",
+ "C3 CA 6BB5",
+ "DD D6 6BB7",
+ "BB A6 6BBA",
+ "B3 CC 6BBB",
+ "DD D7 6BBC",
+ "C5 C2 6BBF",
+ "D4 CC 6BC0",
+ "B5 A3 6BC5",
+ "DD D8 6BC6",
+ "DD D9 6BCB",
+ "CA EC 6BCD",
+ "CB E8 6BCE",
+ "C6 C7 6BD2",
+ "DD DA 6BD3",
+ "C8 E6 6BD4",
+ "C8 FB 6BD8",
+ "CC D3 6BDB",
+ "DD DB 6BDF",
+ "DD DD 6BEB",
+ "DD DC 6BEC",
+ "DD DF 6BEF",
+ "DD DE 6BF3",
+ "DD E1 6C08",
+ "BB E1 6C0F",
+ "CC B1 6C11",
+ "DD E2 6C13",
+ "DD E3 6C14",
+ "B5 A4 6C17",
+ "DD E4 6C1B",
+ "DD E6 6C23",
+ "DD E5 6C24",
+ "BF E5 6C34",
+ "C9 B9 6C37",
+ "B1 CA 6C38",
+ "C8 C5 6C3E",
+ "C4 F5 6C40",
+ "BD C1 6C41",
+ "B5 E1 6C42",
+ "C8 C6 6C4E",
+ "BC AE 6C50",
+ "DD E8 6C55",
+ "B4 C0 6C57",
+ "B1 F8 6C5A",
+ "C6 F2 6C5D",
+ "DD E7 6C5E",
+ "B9 BE 6C5F",
+ "C3 D3 6C60",
+ "DD E9 6C62",
+ "DD F1 6C68",
+ "DD EA 6C6A",
+ "C2 C1 6C70",
+ "B5 E2 6C72",
+ "DD F2 6C73",
+ "B7 E8 6C7A",
+ "B5 A5 6C7D",
+ "DD F0 6C7E",
+ "DD EE 6C81",
+ "DD EB 6C82",
+ "CD E0 6C83",
+ "C4 C0 6C88",
+ "C6 D9 6C8C",
+ "DD EC 6C8D",
+ "DD F4 6C90",
+ "DD F3 6C92",
+ "B7 A3 6C93",
+ "B2 AD 6C96",
+ "BA BB 6C99",
+ "DD ED 6C9A",
+ "DD EF 6C9B",
+ "CB D7 6CA1",
+ "C2 F4 6CA2",
+ "CB F7 6CAB",
+ "DD FC 6CAE",
+ "DD FD 6CB1",
+ "B2 CF 6CB3",
+ "CA A8 6CB8",
+ "CC FD 6CB9",
+ "DE A1 6CBA",
+ "BC A3 6CBB",
+ "BE C2 6CBC",
+ "DD F8 6CBD",
+ "DD FE 6CBE",
+ "B1 E8 6CBF",
+ "B6 B7 6CC1",
+ "DD F5 6CC4",
+ "DD FA 6CC5",
+ "C0 F4 6CC9",
+ "C7 F1 6CCA",
+ "C8 E7 6CCC",
+ "DD F7 6CD3",
+ "CB A1 6CD5",
+ "DD F9 6CD7",
+ "DE A4 6CD9",
+ "DE A2 6CDB",
+ "DD FB 6CDD",
+ "CB A2 6CE1",
+ "C7 C8 6CE2",
+ "B5 E3 6CE3",
+ "C5 A5 6CE5",
+ "C3 ED 6CE8",
+ "DE A5 6CEA",
+ "DE A3 6CEF",
+ "C2 D9 6CF0",
+ "DD F6 6CF1",
+ "B1 CB 6CF3",
+ "CD CE 6D0B",
+ "DE B0 6D0C",
+ "DE AF 6D12",
+ "C0 F6 6D17",
+ "DE AC 6D19",
+ "CD EC 6D1B",
+ "C6 B6 6D1E",
+ "DE A6 6D1F",
+ "C4 C5 6D25",
+ "B1 CC 6D29",
+ "B9 BF 6D2A",
+ "DE A9 6D2B",
+ "BD A7 6D32",
+ "DE AE 6D33",
+ "DE AD 6D35",
+ "DE A8 6D36",
+ "DE AB 6D38",
+ "B3 E8 6D3B",
+ "DE AA 6D3D",
+ "C7 C9 6D3E",
+ "CE AE 6D41",
+ "BE F4 6D44",
+ "C0 F5 6D45",
+ "DE B6 6D59",
+ "DE B4 6D5A",
+ "C9 CD 6D5C",
+ "DE B1 6D63",
+ "DE B3 6D64",
+ "B1 BA 6D66",
+ "B9 C0 6D69",
+ "CF B2 6D6A",
+ "B3 BD 6D6C",
+ "C9 E2 6D6E",
+ "CD E1 6D74",
+ "B3 A4 6D77",
+ "BF BB 6D78",
+ "DE B5 6D79",
+ "DE BA 6D85",
+ "BE C3 6D88",
+ "CD B0 6D8C",
+ "DE B7 6D8E",
+ "DE B2 6D93",
+ "DE B8 6D95",
+ "CE DE 6D99",
+ "C5 F3 6D9B",
+ "C6 C2 6D9C",
+ "B3 B6 6DAF",
+ "B1 D5 6DB2",
+ "DE BE 6DB5",
+ "DE C1 6DB8",
+ "CE C3 6DBC",
+ "CD E4 6DC0",
+ "DE C8 6DC5",
+ "DE C2 6DC6",
+ "DE BF 6DC7",
+ "CE D4 6DCB",
+ "DE C5 6DCC",
+ "BD CA 6DD1",
+ "DE C7 6DD2",
+ "DE CC 6DD5",
+ "C5 F1 6DD8",
+ "DE CA 6DD9",
+ "DE C4 6DDE",
+ "C3 B8 6DE1",
+ "DE CB 6DE4",
+ "DE C0 6DE6",
+ "DE C6 6DE8",
+ "DE CD 6DEA",
+ "B0 FC 6DEB",
+ "DE C3 6DEC",
+ "DE CE 6DEE",
+ "BF BC 6DF1",
+ "BD DF 6DF3",
+ "CA A5 6DF5",
+ "BA AE 6DF7",
+ "DE BB 6DF9",
+ "DE C9 6DFA",
+ "C5 BA 6DFB",
+ "C0 B6 6E05",
+ "B3 E9 6E07",
+ "BA D1 6E08",
+ "BE C4 6E09",
+ "DE BD 6E0A",
+ "BD C2 6E0B",
+ "B7 CC 6E13",
+ "DE BC 6E15",
+ "DE D2 6E19",
+ "BD ED 6E1A",
+ "B8 BA 6E1B",
+ "DE E1 6E1D",
+ "DE DB 6E1F",
+ "B5 F4 6E20",
+ "C5 CF 6E21",
+ "DE D6 6E23",
+ "DE DF 6E24",
+ "B0 AF 6E25",
+ "B1 B2 6E26",
+ "B2 B9 6E29",
+ "DE D8 6E2B",
+ "C2 AC 6E2C",
+ "DE CF 6E2D",
+ "DE D1 6E2E",
+ "B9 C1 6E2F",
+ "DE E2 6E38",
+ "DE DD 6E3A",
+ "DE D5 6E3E",
+ "DE DC 6E43",
+ "CC AB 6E4A",
+ "DE DA 6E4D",
+ "DE DE 6E4E",
+ "B8 D0 6E56",
+ "BE C5 6E58",
+ "C3 B9 6E5B",
+ "DE D4 6E5F",
+ "CD AF 6E67",
+ "DE D7 6E6B",
+ "DE D0 6E6E",
+ "C5 F2 6E6F",
+ "DE D3 6E72",
+ "DE D9 6E76",
+ "CF D1 6E7E",
+ "BC BE 6E7F",
+ "CB FE 6E80",
+ "DE E3 6E82",
+ "C8 AE 6E8C",
+ "DE EF 6E8F",
+ "B8 BB 6E90",
+ "BD E0 6E96",
+ "DE E5 6E98",
+ "CE AF 6E9C",
+ "B9 C2 6E9D",
+ "DE F2 6E9F",
+ "B0 EE 6EA2",
+ "DE F0 6EA5",
+ "DE E4 6EAA",
+ "DE EA 6EAF",
+ "DE EC 6EB2",
+ "CD CF 6EB6",
+ "DE E7 6EB7",
+ "C5 AE 6EBA",
+ "DE E9 6EBD",
+ "DE F1 6EC2",
+ "DE EB 6EC4",
+ "CC C7 6EC5",
+ "DE E6 6EC9",
+ "BC A2 6ECB",
+ "DE FE 6ECC",
+ "B3 EA 6ED1",
+ "DE E8 6ED3",
+ "DE ED 6ED4",
+ "DE EE 6ED5",
+ "C2 EC 6EDD",
+ "C2 DA 6EDE",
+ "DE F6 6EEC",
+ "DE FC 6EEF",
+ "DE FA 6EF2",
+ "C5 A9 6EF4",
+ "DF A3 6EF7",
+ "DE F7 6EF8",
+ "DE F8 6EFE",
+ "DE E0 6EFF",
+ "B5 F9 6F01",
+ "C9 BA 6F02",
+ "BC BF 6F06",
+ "B9 F7 6F09",
+ "CF B3 6F0F",
+ "DE F4 6F11",
+ "DF A2 6F13",
+ "B1 E9 6F14",
+ "C1 E6 6F15",
+ "C7 F9 6F20",
+ "B4 C1 6F22",
+ "CE FA 6F23",
+ "CC A1 6F2B",
+ "C4 D2 6F2C",
+ "DE FB 6F31",
+ "DE FD 6F32",
+ "C1 B2 6F38",
+ "DF A1 6F3E",
+ "DE F9 6F3F",
+ "DE F3 6F41",
+ "B4 C3 6F45",
+ "B7 E9 6F54",
+ "DF AF 6F58",
+ "DF AA 6F5B",
+ "C0 F8 6F5C",
+ "B3 E3 6F5F",
+ "BD E1 6F64",
+ "DF B3 6F66",
+ "DF AC 6F6D",
+ "C4 AC 6F6E",
+ "DF A9 6F6F",
+ "C4 D9 6F70",
+ "DF CC 6F74",
+ "DF A6 6F78",
+ "DF A5 6F7A",
+ "DF AE 6F7C",
+ "DF A8 6F80",
+ "DF A7 6F81",
+ "DF AD 6F82",
+ "C0 A1 6F84",
+ "DF A4 6F86",
+ "DF B0 6F8E",
+ "DF B1 6F91",
+ "B4 C2 6F97",
+ "DF B6 6FA1",
+ "DF B5 6FA3",
+ "DF B7 6FA4",
+ "DF BA 6FAA",
+ "C5 C3 6FB1",
+ "DF B4 6FB3",
+ "DF B8 6FB9",
+ "B7 E3 6FC0",
+ "C2 F9 6FC1",
+ "DF B2 6FC2",
+ "C7 BB 6FC3",
+ "DF B9 6FC6",
+ "DF BE 6FD4",
+ "DF BC 6FD5",
+ "DF BF 6FD8",
+ "DF C2 6FDB",
+ "DF BB 6FDF",
+ "B9 EA 6FE0",
+ "C7 A8 6FE1",
+ "DE B9 6FE4",
+ "CD F4 6FEB",
+ "DF BD 6FEC",
+ "DF C1 6FEE",
+ "C2 F5 6FEF",
+ "DF C0 6FF1",
+ "DF AB 6FF3",
+ "EF E9 6FF6",
+ "DF C5 6FFA",
+ "DF C9 6FFE",
+ "DF C7 7001",
+ "DF C3 7009",
+ "DF C4 700B",
+ "DF C8 700F",
+ "DF C6 7011",
+ "C9 CE 7015",
+ "DF CE 7018",
+ "DF CB 701A",
+ "DF CA 701B",
+ "DF CD 701D",
+ "C6 D4 701E",
+ "DF CF 701F",
+ "C3 F5 7026",
+ "C2 ED 7027",
+ "C0 A5 702C",
+ "DF D0 7030",
+ "DF D2 7032",
+ "DF D1 703E",
+ "DE F5 704C",
+ "DF D3 7051",
+ "C6 E7 7058",
+ "DF D4 7063",
+ "B2 D0 706B",
+ "C5 F4 706F",
+ "B3 A5 7070",
+ "B5 E4 7078",
+ "BC DE 707C",
+ "BA D2 707D",
+ "CF A7 7089",
+ "BF E6 708A",
+ "B1 EA 708E",
+ "DF D6 7092",
+ "DF D5 7099",
+ "DF D9 70AC",
+ "C3 BA 70AD",
+ "DF DC 70AE",
+ "DF D7 70AF",
+ "DF DB 70B3",
+ "DF DA 70B8",
+ "C5 C0 70B9",
+ "B0 D9 70BA",
+ "CE F5 70C8",
+ "DF DE 70CB",
+ "B1 A8 70CF",
+ "DF E0 70D9",
+ "DF DF 70DD",
+ "DF DD 70DF",
+ "DF D8 70F1",
+ "CB A3 70F9",
+ "DF E2 70FD",
+ "DF E1 7109",
+ "B1 EB 7114",
+ "DF E4 7119",
+ "CA B2 711A",
+ "DF E3 711C",
+ "CC B5 7121",
+ "BE C7 7126",
+ "C1 B3 7136",
+ "BE C6 713C",
+ "CE FB 7149",
+ "DF EA 714C",
+ "C0 F9 714E",
+ "DF E6 7155",
+ "DF EB 7156",
+ "B1 EC 7159",
+ "DF E9 7162",
+ "C7 E1 7164",
+ "DF E5 7165",
+ "DF E8 7166",
+ "BE C8 7167",
+ "C8 D1 7169",
+ "DF EC 716C",
+ "BC D1 716E",
+ "C0 FA 717D",
+ "DF EF 7184",
+ "DF E7 7188",
+ "B7 A7 718A",
+ "DF ED 718F",
+ "CD D0 7194",
+ "DF F0 7195",
+ "F4 A6 7199",
+ "BD CF 719F",
+ "DF F1 71A8",
+ "DF F2 71AC",
+ "C7 AE 71B1",
+ "DF F4 71B9",
+ "DF F5 71BE",
+ "C7 B3 71C3",
+ "C5 F5 71C8",
+ "DF F7 71C9",
+ "DF F9 71CE",
+ "CE D5 71D0",
+ "DF F6 71D2",
+ "DF F8 71D4",
+ "B1 ED 71D5",
+ "DF F3 71D7",
+ "D3 DB 71DF",
+ "DF FA 71E0",
+ "C1 E7 71E5",
+ "BB B8 71E6",
+ "DF FC 71E7",
+ "DF FB 71EC",
+ "BF A4 71ED",
+ "D2 D9 71EE",
+ "DF FD 71F5",
+ "E0 A1 71F9",
+ "DF EE 71FB",
+ "DF FE 71FC",
+ "E0 A2 71FF",
+ "C7 FA 7206",
+ "E0 A3 720D",
+ "E0 A4 7210",
+ "E0 A5 721B",
+ "E0 A6 7228",
+ "C4 DE 722A",
+ "E0 A8 722C",
+ "E0 A7 722D",
+ "E0 A9 7230",
+ "E0 AA 7232",
+ "BC DF 7235",
+ "C9 E3 7236",
+ "CC EC 723A",
+ "E0 AB 723B",
+ "E0 AC 723C",
+ "C1 D6 723D",
+ "BC A4 723E",
+ "E0 AD 723F",
+ "E0 AE 7240",
+ "E0 AF 7246",
+ "CA D2 7247",
+ "C8 C7 7248",
+ "E0 B0 724B",
+ "C7 D7 724C",
+ "C4 AD 7252",
+ "E0 B1 7258",
+ "B2 E7 7259",
+ "B5 ED 725B",
+ "CC C6 725D",
+ "CC B6 725F",
+ "B2 B4 7261",
+ "CF B4 7262",
+ "CB D2 7267",
+ "CA AA 7269",
+ "C0 B7 7272",
+ "E0 B2 7274",
+ "C6 C3 7279",
+ "B8 A3 727D",
+ "E0 B3 727E",
+ "BA D4 7280",
+ "E0 B5 7281",
+ "E0 B4 7282",
+ "E0 B6 7287",
+ "E0 B7 7292",
+ "E0 B8 7296",
+ "B5 BE 72A0",
+ "E0 B9 72A2",
+ "E0 BA 72A7",
+ "B8 A4 72AC",
+ "C8 C8 72AF",
+ "E0 BC 72B2",
+ "BE F5 72B6",
+ "E0 BB 72B9",
+ "B6 B8 72C2",
+ "E0 BD 72C3",
+ "E0 BF 72C4",
+ "E0 BE 72C6",
+ "E0 C0 72CE",
+ "B8 D1 72D0",
+ "E0 C1 72D2",
+ "B6 E9 72D7",
+ "C1 C0 72D9",
+ "B9 FD 72DB",
+ "E0 C3 72E0",
+ "E0 C4 72E1",
+ "E0 C2 72E2",
+ "BC ED 72E9",
+ "C6 C8 72EC",
+ "B6 B9 72ED",
+ "E0 C6 72F7",
+ "C3 AC 72F8",
+ "E0 C5 72F9",
+ "CF B5 72FC",
+ "C7 E2 72FD",
+ "E0 C9 730A",
+ "E0 CB 7316",
+ "E0 C8 7317",
+ "CC D4 731B",
+ "E0 CA 731C",
+ "E0 CC 731D",
+ "CE C4 731F",
+ "E0 D0 7325",
+ "E0 CF 7329",
+ "C3 F6 732A",
+ "C7 AD 732B",
+ "B8 A5 732E",
+ "E0 CE 732F",
+ "E0 CD 7334",
+ "CD B1 7336",
+ "CD B2 7337",
+ "E0 D1 733E",
+ "B1 EE 733F",
+ "B9 F6 7344",
+ "BB E2 7345",
+ "E0 D2 734E",
+ "E0 D3 734F",
+ "E0 D5 7357",
+ "BD C3 7363",
+ "E0 D7 7368",
+ "E0 D6 736A",
+ "E0 D8 7370",
+ "B3 CD 7372",
+ "E0 DA 7375",
+ "E0 D9 7378",
+ "E0 DC 737A",
+ "E0 DB 737B",
+ "B8 BC 7384",
+ "CE A8 7387",
+ "B6 CC 7389",
+ "B2 A6 738B",
+ "B6 EA 7396",
+ "B4 E1 73A9",
+ "CE E8 73B2",
+ "E0 DE 73B3",
+ "E0 E0 73BB",
+ "E0 E1 73C0",
+ "B2 D1 73C2",
+ "E0 DD 73C8",
+ "BB B9 73CA",
+ "C4 C1 73CD",
+ "E0 DF 73CE",
+ "E0 E4 73DE",
+ "BC EE 73E0",
+ "E0 E2 73E5",
+ "B7 BE 73EA",
+ "C8 C9 73ED",
+ "E0 E3 73EE",
+ "E0 FE 73F1",
+ "E0 E9 73F8",
+ "B8 BD 73FE",
+ "B5 E5 7403",
+ "E0 E6 7405",
+ "CD FD 7406",
+ "CE B0 7409",
+ "C2 F6 7422",
+ "E0 E8 7425",
+ "E0 EA 7432",
+ "CE D6 7433",
+ "B6 D7 7434",
+ "C8 FC 7435",
+ "C7 CA 7436",
+ "E0 EB 743A",
+ "E0 ED 743F",
+ "E0 F0 7441",
+ "E0 EC 7455",
+ "E0 EF 7459",
+ "B8 EA 745A",
+ "B1 CD 745B",
+ "E0 F1 745C",
+ "BF F0 745E",
+ "E0 EE 745F",
+ "CE DC 7460",
+ "E0 F4 7463",
+ "F4 A4 7464",
+ "E0 F2 7469",
+ "E0 F5 746A",
+ "E0 E7 746F",
+ "E0 F3 7470",
+ "BA BC 7473",
+ "E0 F6 7476",
+ "E0 F7 747E",
+ "CD FE 7483",
+ "E0 F8 748B",
+ "E0 F9 749E",
+ "E0 E5 74A2",
+ "E0 FA 74A7",
+ "B4 C4 74B0",
+ "BC A5 74BD",
+ "E0 FB 74CA",
+ "E0 FC 74CF",
+ "E0 FD 74D4",
+ "B1 BB 74DC",
+ "E1 A1 74E0",
+ "C9 BB 74E2",
+ "E1 A2 74E3",
+ "B4 A4 74E6",
+ "E1 A3 74E7",
+ "E1 A4 74E9",
+ "E1 A5 74EE",
+ "E1 A7 74F0",
+ "E1 A8 74F1",
+ "E1 A6 74F2",
+ "C9 D3 74F6",
+ "E1 AA 74F7",
+ "E1 A9 74F8",
+ "E1 AC 7503",
+ "E1 AB 7504",
+ "E1 AD 7505",
+ "E1 AE 750C",
+ "E1 B0 750D",
+ "E1 AF 750E",
+ "B9 F9 7511",
+ "E1 B2 7513",
+ "E1 B1 7515",
+ "B4 C5 7518",
+ "BF D3 751A",
+ "C5 BC 751C",
+ "E1 B3 751E",
+ "C0 B8 751F",
+ "BB BA 7523",
+ "B1 F9 7525",
+ "E1 B4 7526",
+ "CD D1 7528",
+ "CA E3 752B",
+ "E1 B5 752C",
+ "C5 C4 7530",
+ "CD B3 7531",
+ "B9 C3 7532",
+ "BF BD 7533",
+ "C3 CB 7537",
+ "D2 B4 7538",
+ "C4 AE 753A",
+ "B2 E8 753B",
+ "E1 B6 753C",
+ "E1 B7 7544",
+ "E1 BC 7546",
+ "E1 BA 7549",
+ "E1 B9 754A",
+ "DA C2 754B",
+ "B3 A6 754C",
+ "E1 B8 754D",
+ "B0 DA 754F",
+ "C8 AA 7551",
+ "C8 CA 7554",
+ "CE B1 7559",
+ "E1 BD 755A",
+ "E1 BB 755B",
+ "C3 DC 755C",
+ "C0 A6 755D",
+ "C8 AB 7560",
+ "C9 AD 7562",
+ "E1 BF 7564",
+ "CE AC 7565",
+ "B7 CD 7566",
+ "E1 C0 7567",
+ "E1 BE 7569",
+ "C8 D6 756A",
+ "E1 C1 756B",
+ "E1 C2 756D",
+ "B0 DB 7570",
+ "BE F6 7573",
+ "E1 C7 7574",
+ "E1 C4 7576",
+ "C6 ED 7577",
+ "E1 C3 7578",
+ "B5 A6 757F",
+ "E1 CA 7582",
+ "E1 C5 7586",
+ "E1 C6 7587",
+ "E1 C9 7589",
+ "E1 C8 758A",
+ "C9 A5 758B",
+ "C1 C2 758E",
+ "C1 C1 758F",
+ "B5 BF 7591",
+ "E1 CB 7594",
+ "E1 CC 759A",
+ "E1 CD 759D",
+ "E1 CF 75A3",
+ "E1 CE 75A5",
+ "B1 D6 75AB",
+ "E1 D7 75B1",
+ "C8 E8 75B2",
+ "E1 D1 75B3",
+ "E1 D3 75B5",
+ "E1 D5 75B8",
+ "BF BE 75B9",
+ "E1 D6 75BC",
+ "E1 D4 75BD",
+ "BC C0 75BE",
+ "E1 D0 75C2",
+ "E1 D2 75C3",
+ "C9 C2 75C5",
+ "BE C9 75C7",
+ "E1 D9 75CA",
+ "E1 D8 75CD",
+ "E1 DA 75D2",
+ "BC A6 75D4",
+ "BA AF 75D5",
+ "C5 F7 75D8",
+ "E1 DB 75D9",
+ "C4 CB 75DB",
+ "E1 DD 75DE",
+ "CE A1 75E2",
+ "E1 DC 75E3",
+ "C1 E9 75E9",
+ "E1 E2 75F0",
+ "E1 E4 75F2",
+ "E1 E5 75F3",
+ "C3 D4 75F4",
+ "E1 E3 75FA",
+ "E1 E0 75FC",
+ "E1 DE 75FE",
+ "E1 DF 75FF",
+ "E1 E1 7601",
+ "E1 E8 7609",
+ "E1 E6 760B",
+ "E1 E7 760D",
+ "E1 E9 761F",
+ "E1 EB 7620",
+ "E1 EC 7621",
+ "E1 ED 7622",
+ "E1 EE 7624",
+ "E1 EA 7627",
+ "E1 F0 7630",
+ "E1 EF 7634",
+ "E1 F1 763B",
+ "CE C5 7642",
+ "E1 F4 7646",
+ "E1 F2 7647",
+ "E1 F3 7648",
+ "B4 E2 764C",
+ "CC FE 7652",
+ "CA CA 7656",
+ "E1 F6 7658",
+ "E1 F5 765C",
+ "E1 F7 7661",
+ "E1 F8 7662",
+ "E1 FC 7667",
+ "E1 F9 7668",
+ "E1 FA 7669",
+ "E1 FB 766A",
+ "E1 FD 766C",
+ "E1 FE 7670",
+ "E2 A1 7672",
+ "E2 A2 7676",
+ "E2 A3 7678",
+ "C8 AF 767A",
+ "C5 D0 767B",
+ "E2 A4 767C",
+ "C7 F2 767D",
+ "C9 B4 767E",
+ "E2 A5 7680",
+ "E2 A6 7683",
+ "C5 AA 7684",
+ "B3 A7 7686",
+ "B9 C4 7687",
+ "E2 A7 7688",
+ "E2 A8 768B",
+ "E2 A9 768E",
+ "BB A9 7690",
+ "E2 AB 7693",
+ "E2 AA 7696",
+ "E2 AC 7699",
+ "E2 AD 769A",
+ "C8 E9 76AE",
+ "E2 AE 76B0",
+ "E2 AF 76B4",
+ "F3 E9 76B7",
+ "E2 B0 76B8",
+ "E2 B1 76B9",
+ "E2 B2 76BA",
+ "BB AE 76BF",
+ "E2 B3 76C2",
+ "C7 D6 76C3",
+ "CB DF 76C6",
+ "B1 CE 76C8",
+ "B1 D7 76CA",
+ "E2 B4 76CD",
+ "E2 B6 76D2",
+ "E2 B5 76D6",
+ "C5 F0 76D7",
+ "C0 B9 76DB",
+ "DD B9 76DC",
+ "E2 B7 76DE",
+ "CC C1 76DF",
+ "E2 B8 76E1",
+ "B4 C6 76E3",
+ "C8 D7 76E4",
+ "E2 B9 76E5",
+ "E2 BA 76E7",
+ "E2 BB 76EA",
+ "CC DC 76EE",
+ "CC D5 76F2",
+ "C4 BE 76F4",
+ "C1 EA 76F8",
+ "E2 BD 76FB",
+ "BD E2 76FE",
+ "BE CA 7701",
+ "E2 C0 7704",
+ "E2 BF 7707",
+ "E2 BE 7708",
+ "C8 FD 7709",
+ "B4 C7 770B",
+ "B8 A9 770C",
+ "E2 C6 771B",
+ "E2 C3 771E",
+ "BF BF 771F",
+ "CC B2 7720",
+ "E2 C2 7724",
+ "E2 C4 7725",
+ "E2 C5 7726",
+ "E2 C1 7729",
+ "E2 C7 7737",
+ "E2 C8 7738",
+ "C4 AF 773A",
+ "B4 E3 773C",
+ "C3 E5 7740",
+ "E2 C9 7747",
+ "E2 CA 775A",
+ "E2 CD 775B",
+ "BF E7 7761",
+ "C6 C4 7763",
+ "E2 CE 7765",
+ "CB D3 7766",
+ "E2 CB 7768",
+ "E2 CC 776B",
+ "E2 D1 7779",
+ "E2 D0 777E",
+ "E2 CF 777F",
+ "E2 D3 778B",
+ "E2 D2 778E",
+ "E2 D4 7791",
+ "E2 D6 779E",
+ "E2 D5 77A0",
+ "CA CD 77A5",
+ "BD D6 77AC",
+ "CE C6 77AD",
+ "E2 D7 77B0",
+ "C6 B7 77B3",
+ "E2 D8 77B6",
+ "E2 D9 77B9",
+ "E2 DD 77BB",
+ "E2 DB 77BC",
+ "E2 DC 77BD",
+ "E2 DA 77BF",
+ "E2 DE 77C7",
+ "E2 DF 77CD",
+ "E2 E0 77D7",
+ "E2 E1 77DA",
+ "CC B7 77DB",
+ "E2 E2 77DC",
+ "CC F0 77E2",
+ "E2 E3 77E3",
+ "C3 CE 77E5",
+ "C7 EA 77E7",
+ "B6 EB 77E9",
+ "C3 BB 77ED",
+ "E2 E4 77EE",
+ "B6 BA 77EF",
+ "C0 D0 77F3",
+ "E2 E5 77FC",
+ "BA BD 7802",
+ "E2 E6 780C",
+ "E2 E7 7812",
+ "B8 A6 7814",
+ "BA D5 7815",
+ "E2 E9 7820",
+ "C5 D6 7825",
+ "BA D6 7826",
+ "B5 CE 7827",
+ "CB A4 7832",
+ "C7 CB 7834",
+ "C5 D7 783A",
+ "B9 DC 783F",
+ "E2 EB 7845",
+ "BE CB 785D",
+ "CE B2 786B",
+ "B9 C5 786C",
+ "B8 A7 786F",
+ "C8 A3 7872",
+ "E2 ED 7874",
+ "E2 EF 787C",
+ "B8 EB 7881",
+ "E2 EE 7886",
+ "C4 F6 7887",
+ "E2 F1 788C",
+ "B3 B7 788D",
+ "E2 EC 788E",
+ "C8 EA 7891",
+ "B1 B0 7893",
+ "BA EC 7895",
+ "CF D2 7897",
+ "E2 F0 789A",
+ "E2 F2 78A3",
+ "CA CB 78A7",
+ "C0 D9 78A9",
+ "E2 F4 78AA",
+ "E2 F5 78AF",
+ "E2 F3 78B5",
+ "B3 CE 78BA",
+ "E2 FB 78BC",
+ "E2 FA 78BE",
+ "BC A7 78C1",
+ "E2 FC 78C5",
+ "E2 F7 78C6",
+ "E2 FD 78CA",
+ "E2 F8 78CB",
+ "C8 D8 78D0",
+ "E2 F6 78D1",
+ "E2 F9 78D4",
+ "E3 A2 78DA",
+ "E3 A1 78E7",
+ "CB E1 78E8",
+ "E2 FE 78EC",
+ "B0 EB 78EF",
+ "E3 A4 78F4",
+ "E3 A3 78FD",
+ "BE CC 7901",
+ "E3 A5 7907",
+ "C1 C3 790E",
+ "E3 A7 7911",
+ "E3 A6 7912",
+ "E3 A8 7919",
+ "E2 E8 7926",
+ "E2 EA 792A",
+ "E3 AA 792B",
+ "E3 A9 792C",
+ "BC A8 793A",
+ "CE E9 793C",
+ "BC D2 793E",
+ "E3 AB 7940",
+ "B7 B7 7941",
+ "B5 C0 7947",
+ "B5 A7 7948",
+ "BB E3 7949",
+ "CD B4 7950",
+ "E3 B1 7953",
+ "E3 B0 7955",
+ "C1 C4 7956",
+ "E3 AD 7957",
+ "E3 AF 795A",
+ "BD CB 795D",
+ "BF C0 795E",
+ "E3 AE 795F",
+ "E3 AC 7960",
+ "C7 AA 7962",
+ "BE CD 7965",
+ "C9 BC 7968",
+ "BA D7 796D",
+ "C5 F8 7977",
+ "E3 B2 797A",
+ "E3 B3 797F",
+ "E3 C9 7980",
+ "B6 D8 7981",
+ "CF BD 7984",
+ "C1 B5 7985",
+ "E3 B4 798A",
+ "B2 D2 798D",
+ "C4 F7 798E",
+ "CA A1 798F",
+ "E3 B5 799D",
+ "B5 FA 79A6",
+ "E3 B6 79A7",
+ "E3 B8 79AA",
+ "E3 B9 79AE",
+ "C7 A9 79B0",
+ "E3 BA 79B3",
+ "E3 BB 79B9",
+ "E3 BC 79BA",
+ "B6 D9 79BD",
+ "B2 D3 79BE",
+ "C6 C5 79BF",
+ "BD A8 79C0",
+ "BB E4 79C1",
+ "E3 BD 79C9",
+ "BD A9 79CB",
+ "B2 CA 79D1",
+ "C9 C3 79D2",
+ "E3 BE 79D5",
+ "C8 EB 79D8",
+ "C1 C5 79DF",
+ "E3 C1 79E1",
+ "E3 C2 79E3",
+ "C7 E9 79E4",
+ "BF C1 79E6",
+ "E3 BF 79E7",
+ "C3 E1 79E9",
+ "E3 C0 79EC",
+ "BE CE 79F0",
+ "B0 DC 79FB",
+ "B5 A9 7A00",
+ "E3 C3 7A08",
+ "C4 F8 7A0B",
+ "E3 C4 7A0D",
+ "C0 C7 7A0E",
+ "CC AD 7A14",
+ "C9 A3 7A17",
+ "E3 C5 7A18",
+ "E3 C6 7A19",
+ "C3 D5 7A1A",
+ "CE C7 7A1C",
+ "E3 C8 7A1F",
+ "E3 C7 7A20",
+ "BC EF 7A2E",
+ "E3 CA 7A31",
+ "B0 F0 7A32",
+ "E3 CD 7A37",
+ "E3 CB 7A3B",
+ "B2 D4 7A3C",
+ "B7 CE 7A3D",
+ "E3 CC 7A3E",
+ "B9 C6 7A3F",
+ "B9 F2 7A40",
+ "CA E6 7A42",
+ "E3 CE 7A43",
+ "CB D4 7A46",
+ "E3 D0 7A49",
+ "C0 D1 7A4D",
+ "B1 CF 7A4E",
+ "B2 BA 7A4F",
+ "B0 AC 7A50",
+ "E3 CF 7A57",
+ "E3 D1 7A61",
+ "E3 D2 7A62",
+ "BE F7 7A63",
+ "E3 D3 7A69",
+ "B3 CF 7A6B",
+ "E3 D5 7A70",
+ "B7 EA 7A74",
+ "B5 E6 7A76",
+ "E3 D6 7A79",
+ "B6 F5 7A7A",
+ "E3 D7 7A7D",
+ "C0 FC 7A7F",
+ "C6 CD 7A81",
+ "C0 E0 7A83",
+ "BA F5 7A84",
+ "E3 D8 7A88",
+ "C3 E2 7A92",
+ "C1 EB 7A93",
+ "E3 DA 7A95",
+ "E3 DC 7A96",
+ "E3 D9 7A97",
+ "E3 DB 7A98",
+ "B7 A2 7A9F",
+ "E3 DD 7AA9",
+ "B7 A6 7AAA",
+ "B5 E7 7AAE",
+ "CD D2 7AAF",
+ "E3 DF 7AB0",
+ "E3 E0 7AB6",
+ "B1 AE 7ABA",
+ "E3 E3 7ABF",
+ "B3 F6 7AC3",
+ "E3 E2 7AC4",
+ "E3 E1 7AC5",
+ "E3 E5 7AC7",
+ "E3 DE 7AC8",
+ "E3 E6 7ACA",
+ "CE A9 7ACB",
+ "E3 E7 7ACD",
+ "E3 E8 7ACF",
+ "D4 F4 7AD2",
+ "E3 EA 7AD3",
+ "E3 E9 7AD5",
+ "E3 EB 7AD9",
+ "E3 EC 7ADA",
+ "CE B5 7ADC",
+ "E3 ED 7ADD",
+ "F0 EF 7ADF",
+ "BE CF 7AE0",
+ "E3 EE 7AE1",
+ "E3 EF 7AE2",
+ "BD D7 7AE3",
+ "C6 B8 7AE5",
+ "E3 F0 7AE6",
+ "C3 A8 7AEA",
+ "E3 F1 7AED",
+ "C3 BC 7AEF",
+ "E3 F2 7AF0",
+ "B6 A5 7AF6",
+ "D1 BF 7AF8",
+ "C3 DD 7AF9",
+ "BC B3 7AFA",
+ "B4 C8 7AFF",
+ "E3 F3 7B02",
+ "E4 A2 7B04",
+ "E3 F6 7B06",
+ "B5 E8 7B08",
+ "E3 F5 7B0A",
+ "E4 A4 7B0B",
+ "E3 F4 7B0F",
+ "BE D0 7B11",
+ "E3 F8 7B18",
+ "E3 F9 7B19",
+ "C5 AB 7B1B",
+ "E3 FA 7B1E",
+ "B3 DE 7B20",
+ "BF DA 7B25",
+ "C9 E4 7B26",
+ "E3 FC 7B28",
+ "C2 E8 7B2C",
+ "E3 F7 7B33",
+ "E3 FB 7B35",
+ "E3 FD 7B36",
+ "BA FB 7B39",
+ "E4 A6 7B45",
+ "C9 AE 7B46",
+ "C8 A6 7B48",
+ "C5 F9 7B49",
+ "B6 DA 7B4B",
+ "E4 A5 7B4C",
+ "E4 A3 7B4D",
+ "C8 B5 7B4F",
+ "E3 FE 7B50",
+ "C3 DE 7B51",
+ "C5 FB 7B52",
+ "C5 FA 7B54",
+ "BA F6 7B56",
+ "E4 B8 7B5D",
+ "E4 A8 7B65",
+ "E4 AA 7B67",
+ "E4 AD 7B6C",
+ "E4 AE 7B6E",
+ "E4 AB 7B70",
+ "E4 AC 7B71",
+ "E4 A9 7B74",
+ "E4 A7 7B75",
+ "E4 A1 7B7A",
+ "CA CF 7B86",
+ "B2 D5 7B87",
+ "E4 B5 7B8B",
+ "E4 B2 7B8D",
+ "E4 B7 7B8F",
+ "E4 B6 7B92",
+ "C7 F3 7B94",
+ "CC A7 7B95",
+ "BB BB 7B97",
+ "E4 B0 7B98",
+ "E4 B9 7B99",
+ "E4 B4 7B9A",
+ "E4 B3 7B9C",
+ "E4 AF 7B9D",
+ "E4 B1 7B9F",
+ "B4 C9 7BA1",
+ "C3 BD 7BAA",
+ "C0 FD 7BAD",
+ "C8 A2 7BB1",
+ "E4 BE 7BB4",
+ "C8 A4 7BB8",
+ "C0 E1 7BC0",
+ "E4 BB 7BC1",
+ "C8 CF 7BC4",
+ "E4 BF 7BC6",
+ "CA D3 7BC7",
+ "C3 DB 7BC9",
+ "E4 BA 7BCB",
+ "E4 BC 7BCC",
+ "E4 BD 7BCF",
+ "E4 C0 7BDD",
+ "BC C4 7BE0",
+ "C6 C6 7BE4",
+ "E4 C5 7BE5",
+ "E4 C4 7BE6",
+ "E4 C1 7BE9",
+ "CF B6 7BED",
+ "E4 CA 7BF3",
+ "E4 CE 7BF6",
+ "E4 CB 7BF7",
+ "E4 C7 7C00",
+ "E4 C8 7C07",
+ "E4 CD 7C0D",
+ "E4 C2 7C11",
+ "D2 D5 7C12",
+ "E4 C9 7C13",
+ "E4 C3 7C14",
+ "E4 CC 7C17",
+ "E4 D2 7C1F",
+ "B4 CA 7C21",
+ "E4 CF 7C23",
+ "E4 D0 7C27",
+ "E4 D1 7C2A",
+ "E4 D4 7C2B",
+ "E4 D3 7C37",
+ "C8 F6 7C38",
+ "E4 D5 7C3D",
+ "CE FC 7C3E",
+ "CA ED 7C3F",
+ "E4 DA 7C40",
+ "E4 D7 7C43",
+ "E4 D6 7C4C",
+ "C0 D2 7C4D",
+ "E4 D9 7C4F",
+ "E4 DB 7C50",
+ "E4 D8 7C54",
+ "E4 DF 7C56",
+ "E4 DC 7C58",
+ "E4 DD 7C5F",
+ "E4 C6 7C60",
+ "E4 DE 7C64",
+ "E4 E0 7C65",
+ "E4 E1 7C6C",
+ "CA C6 7C73",
+ "E4 E2 7C75",
+ "CC E2 7C7E",
+ "B6 CE 7C81",
+ "B7 A9 7C82",
+ "E4 E3 7C83",
+ "CA B4 7C89",
+ "BF E8 7C8B",
+ "CC B0 7C8D",
+ "E4 E4 7C90",
+ "CE B3 7C92",
+ "C7 F4 7C95",
+ "C1 C6 7C97",
+ "C7 B4 7C98",
+ "BD CD 7C9B",
+ "B0 C0 7C9F",
+ "E4 E9 7CA1",
+ "E4 E7 7CA2",
+ "E4 E5 7CA4",
+ "B4 A1 7CA5",
+ "BE D1 7CA7",
+ "E4 EA 7CA8",
+ "E4 E8 7CAB",
+ "E4 E6 7CAD",
+ "E4 EE 7CAE",
+ "E4 ED 7CB1",
+ "E4 EC 7CB2",
+ "E4 EB 7CB3",
+ "E4 EF 7CB9",
+ "E4 F0 7CBD",
+ "C0 BA 7CBE",
+ "E4 F1 7CC0",
+ "E4 F3 7CC2",
+ "E4 F2 7CC5",
+ "B8 D2 7CCA",
+ "C1 B8 7CCE",
+ "E4 F5 7CD2",
+ "C5 FC 7CD6",
+ "E4 F4 7CD8",
+ "E4 F6 7CDC",
+ "CA B5 7CDE",
+ "C1 EC 7CDF",
+ "B9 C7 7CE0",
+ "E4 F7 7CE2",
+ "CE C8 7CE7",
+ "E4 F9 7CEF",
+ "E4 FA 7CF2",
+ "E4 FB 7CF4",
+ "E4 FC 7CF6",
+ "BB E5 7CF8",
+ "E4 FD 7CFA",
+ "B7 CF 7CFB",
+ "B5 EA 7CFE",
+ "B5 AA 7D00",
+ "E5 A1 7D02",
+ "CC F3 7D04",
+ "B9 C8 7D05",
+ "E4 FE 7D06",
+ "E5 A4 7D0A",
+ "CC E6 7D0B",
+ "C7 BC 7D0D",
+ "C9 B3 7D10",
+ "BD E3 7D14",
+ "E5 A3 7D15",
+ "BC D3 7D17",
+ "B9 C9 7D18",
+ "BB E6 7D19",
+ "B5 E9 7D1A",
+ "CA B6 7D1B",
+ "E5 A2 7D1C",
+ "C1 C7 7D20",
+ "CB C2 7D21",
+ "BA F7 7D22",
+ "BB E7 7D2B",
+ "C4 DD 7D2C",
+ "E5 A7 7D2E",
+ "CE DF 7D2F",
+ "BA D9 7D30",
+ "E5 A8 7D32",
+ "BF C2 7D33",
+ "E5 AA 7D35",
+ "BE D2 7D39",
+ "BA B0 7D3A",
+ "E5 A9 7D3F",
+ "BD AA 7D42",
+ "B8 BE 7D43",
+ "C1 C8 7D44",
+ "E5 A5 7D45",
+ "E5 AB 7D46",
+ "E5 A6 7D4B",
+ "B7 D0 7D4C",
+ "E5 AE 7D4E",
+ "E5 B2 7D4F",
+ "B7 EB 7D50",
+ "E5 AD 7D56",
+ "E5 B6 7D5B",
+ "B9 CA 7D5E",
+ "CD ED 7D61",
+ "B0 BC 7D62",
+ "E5 B3 7D63",
+ "B5 EB 7D66",
+ "E5 B0 7D68",
+ "E5 B1 7D6E",
+ "C5 FD 7D71",
+ "E5 AF 7D72",
+ "E5 AC 7D73",
+ "B3 A8 7D75",
+ "C0 E4 7D76",
+ "B8 A8 7D79",
+ "E5 B8 7D7D",
+ "E5 B5 7D89",
+ "E5 B7 7D8F",
+ "E5 B4 7D93",
+ "B7 D1 7D99",
+ "C2 B3 7D9A",
+ "E5 B9 7D9B",
+ "C1 EE 7D9C",
+ "E5 C6 7D9F",
+ "E5 C2 7DA2",
+ "E5 BC 7DA3",
+ "E5 C0 7DAB",
+ "BC FA 7DAC",
+ "B0 DD 7DAD",
+ "E5 BB 7DAE",
+ "E5 C3 7DAF",
+ "E5 C7 7DB0",
+ "B9 CB 7DB1",
+ "CC D6 7DB2",
+ "C4 D6 7DB4",
+ "E5 BD 7DB5",
+ "E5 C5 7DB8",
+ "E5 BA 7DBA",
+ "C3 BE 7DBB",
+ "E5 BF 7DBD",
+ "B0 BD 7DBE",
+ "CC CA 7DBF",
+ "E5 BE 7DC7",
+ "B6 DB 7DCA",
+ "C8 EC 7DCB",
+ "C1 ED 7DCF",
+ "CE D0 7DD1",
+ "BD EF 7DD2",
+ "E5 EE 7DD5",
+ "E5 C8 7DD8",
+ "C0 FE 7DDA",
+ "E5 C4 7DDC",
+ "E5 C9 7DDD",
+ "E5 CB 7DDE",
+ "C4 F9 7DE0",
+ "E5 CE 7DE1",
+ "E5 CA 7DE4",
+ "CA D4 7DE8",
+ "B4 CB 7DE9",
+ "CC CB 7DEC",
+ "B0 DE 7DEF",
+ "E5 CD 7DF2",
+ "CE FD 7DF4",
+ "E5 CC 7DFB",
+ "B1 EF 7E01",
+ "C6 EC 7E04",
+ "E5 CF 7E05",
+ "E5 D6 7E09",
+ "E5 D0 7E0A",
+ "E5 D7 7E0B",
+ "E5 D3 7E12",
+ "C7 FB 7E1B",
+ "BC CA 7E1E",
+ "E5 D5 7E1F",
+ "E5 D2 7E21",
+ "E5 D8 7E22",
+ "E5 D1 7E23",
+ "BD C4 7E26",
+ "CB A5 7E2B",
+ "BD CC 7E2E",
+ "E5 D4 7E31",
+ "E5 E0 7E32",
+ "E5 DC 7E35",
+ "E5 DF 7E37",
+ "E5 DD 7E39",
+ "E5 E1 7E3A",
+ "E5 DB 7E3B",
+ "E5 C1 7E3D",
+ "C0 D3 7E3E",
+ "C8 CB 7E41",
+ "E5 DE 7E43",
+ "E5 D9 7E46",
+ "C1 A1 7E4A",
+ "B7 D2 7E4B",
+ "BD AB 7E4D",
+ "BF A5 7E54",
+ "C1 B6 7E55",
+ "E5 E4 7E56",
+ "E5 E6 7E59",
+ "E5 E7 7E5A",
+ "E5 E3 7E5D",
+ "E5 E5 7E5E",
+ "E5 DA 7E66",
+ "E5 E2 7E67",
+ "E5 EA 7E69",
+ "E5 E9 7E6A",
+ "CB FA 7E6D",
+ "B7 AB 7E70",
+ "E5 E8 7E79",
+ "E5 EC 7E7B",
+ "E5 EB 7E7C",
+ "E5 EF 7E7D",
+ "E5 F1 7E7F",
+ "BB BC 7E82",
+ "E5 ED 7E83",
+ "E5 F2 7E88",
+ "E5 F3 7E89",
+ "E5 F4 7E8C",
+ "E5 FA 7E8E",
+ "C5 BB 7E8F",
+ "E5 F6 7E90",
+ "E5 F5 7E92",
+ "E5 F7 7E93",
+ "E5 F8 7E94",
+ "E5 F9 7E96",
+ "E5 FB 7E9B",
+ "E5 FC 7E9C",
+ "B4 CC 7F36",
+ "E5 FD 7F38",
+ "E5 FE 7F3A",
+ "E6 A1 7F45",
+ "E6 A2 7F4C",
+ "E6 A3 7F4D",
+ "E6 A4 7F4E",
+ "E6 A5 7F50",
+ "E6 A6 7F51",
+ "E6 A8 7F54",
+ "E6 A7 7F55",
+ "E6 A9 7F58",
+ "E6 AA 7F5F",
+ "E6 AB 7F60",
+ "E6 AE 7F67",
+ "E6 AC 7F68",
+ "E6 AD 7F69",
+ "BA E1 7F6A",
+ "B7 D3 7F6B",
+ "C3 D6 7F6E",
+ "C8 B3 7F70",
+ "BD F0 7F72",
+ "C7 CD 7F75",
+ "C8 ED 7F77",
+ "E6 AF 7F78",
+ "D8 ED 7F79",
+ "E6 B0 7F82",
+ "E6 B2 7F83",
+ "CD E5 7F85",
+ "E6 B1 7F86",
+ "E6 B4 7F87",
+ "E6 B3 7F88",
+ "CD D3 7F8A",
+ "E6 B5 7F8C",
+ "C8 FE 7F8E",
+ "E6 B6 7F94",
+ "E6 B9 7F9A",
+ "E6 B8 7F9D",
+ "E6 B7 7F9E",
+ "E6 BA 7FA3",
+ "B7 B2 7FA4",
+ "C1 A2 7FA8",
+ "B5 C1 7FA9",
+ "E6 BE 7FAE",
+ "E6 BB 7FAF",
+ "E6 BC 7FB2",
+ "E6 BF 7FB6",
+ "E6 C0 7FB8",
+ "E6 BD 7FB9",
+ "B1 A9 7FBD",
+ "B2 A7 7FC1",
+ "E6 C2 7FC5",
+ "E6 C3 7FC6",
+ "E6 C4 7FCA",
+ "CD E2 7FCC",
+ "BD AC 7FD2",
+ "E6 C6 7FD4",
+ "E6 C5 7FD5",
+ "BF E9 7FE0",
+ "E6 C7 7FE1",
+ "E6 C8 7FE6",
+ "E6 C9 7FE9",
+ "B4 E5 7FEB",
+ "B4 CD 7FF0",
+ "E6 CA 7FF3",
+ "E6 CB 7FF9",
+ "CB DD 7FFB",
+ "CD E3 7FFC",
+ "CD D4 8000",
+ "CF B7 8001",
+ "B9 CD 8003",
+ "E6 CE 8004",
+ "BC D4 8005",
+ "E6 CD 8006",
+ "E6 CF 800B",
+ "BC A9 800C",
+ "C2 D1 8010",
+ "E6 D0 8012",
+ "B9 CC 8015",
+ "CC D7 8017",
+ "E6 D1 8018",
+ "E6 D2 8019",
+ "E6 D3 801C",
+ "E6 D4 8021",
+ "E6 D5 8028",
+ "BC AA 8033",
+ "CC ED 8036",
+ "E6 D7 803B",
+ "C3 BF 803D",
+ "E6 D6 803F",
+ "E6 D9 8046",
+ "E6 D8 804A",
+ "E6 DA 8052",
+ "C0 BB 8056",
+ "E6 DB 8058",
+ "E6 DC 805A",
+ "CA B9 805E",
+ "E6 DD 805F",
+ "C1 EF 8061",
+ "E6 DE 8062",
+ "E6 DF 8068",
+ "CE FE 806F",
+ "E6 E2 8070",
+ "E6 E1 8072",
+ "E6 E0 8073",
+ "C4 B0 8074",
+ "E6 E3 8076",
+ "BF A6 8077",
+ "E6 E4 8079",
+ "E6 E5 807D",
+ "CF B8 807E",
+ "E6 E6 807F",
+ "E6 E7 8084",
+ "E6 E9 8085",
+ "E6 E8 8086",
+ "C8 A5 8087",
+ "C6 F9 8089",
+ "CF BE 808B",
+ "C8 A9 808C",
+ "E6 EB 8093",
+ "BE D3 8096",
+ "C9 AA 8098",
+ "E6 EC 809A",
+ "E6 EA 809B",
+ "B4 CE 809D",
+ "B8 D4 80A1",
+ "BB E8 80A2",
+ "C8 EE 80A5",
+ "B8 AA 80A9",
+ "CB C3 80AA",
+ "E6 EF 80AC",
+ "E6 ED 80AD",
+ "B9 CE 80AF",
+ "B9 CF 80B1",
+ "B0 E9 80B2",
+ "BA E8 80B4",
+ "C7 D9 80BA",
+ "B0 DF 80C3",
+ "E6 F4 80C4",
+ "C3 C0 80C6",
+ "C7 D8 80CC",
+ "C2 DB 80CE",
+ "E6 F6 80D6",
+ "E6 F2 80D9",
+ "E6 F5 80DA",
+ "E6 F0 80DB",
+ "E6 F3 80DD",
+ "CB A6 80DE",
+ "B8 D5 80E1",
+ "B0 FD 80E4",
+ "E6 F1 80E5",
+ "E6 F8 80EF",
+ "E6 F9 80F1",
+ "C6 B9 80F4",
+ "B6 BB 80F8",
+ "E7 A6 80FC",
+ "C7 BD 80FD",
+ "BB E9 8102",
+ "B6 BC 8105",
+ "C0 C8 8106",
+ "CF C6 8107",
+ "CC AE 8108",
+ "E6 F7 8109",
+ "C0 D4 810A",
+ "B5 D3 811A",
+ "E6 FA 811B",
+ "E6 FC 8123",
+ "E6 FB 8129",
+ "E6 FD 812F",
+ "C3 A6 8131",
+ "C7 BE 8133",
+ "C4 B1 8139",
+ "E7 A3 813E",
+ "E7 A2 8146",
+ "E6 FE 814B",
+ "BF D5 814E",
+ "C9 E5 8150",
+ "E7 A5 8151",
+ "E7 A4 8153",
+ "B9 D0 8154",
+ "CF D3 8155",
+ "E7 B5 815F",
+ "E7 A9 8165",
+ "E7 AA 8166",
+ "BC F0 816B",
+ "E7 A8 816E",
+ "B9 F8 8170",
+ "E7 A7 8171",
+ "E7 AB 8174",
+ "C4 B2 8178",
+ "CA A2 8179",
+ "C1 A3 817A",
+ "C2 DC 817F",
+ "E7 AF 8180",
+ "E7 B0 8182",
+ "E7 AC 8183",
+ "E7 AD 8188",
+ "E7 AE 818A",
+ "B9 D1 818F",
+ "E7 B6 8193",
+ "E7 B2 8195",
+ "C9 E6 819A",
+ "CB EC 819C",
+ "C9 A8 819D",
+ "E7 B1 81A0",
+ "E7 B4 81A3",
+ "E7 B3 81A4",
+ "CB C4 81A8",
+ "E7 B7 81A9",
+ "E7 B8 81B0",
+ "C1 B7 81B3",
+ "E7 B9 81B5",
+ "E7 BB 81B8",
+ "E7 BF 81BA",
+ "E7 BC 81BD",
+ "E7 BA 81BE",
+ "C7 BF 81BF",
+ "E7 BD 81C0",
+ "E7 BE 81C2",
+ "B2 B2 81C6",
+ "E7 C5 81C8",
+ "E7 C0 81C9",
+ "E7 C1 81CD",
+ "E7 C2 81D1",
+ "C2 A1 81D3",
+ "E7 C4 81D8",
+ "E7 C3 81D9",
+ "E7 C6 81DA",
+ "E7 C7 81DF",
+ "E7 C8 81E0",
+ "BF C3 81E3",
+ "B2 E9 81E5",
+ "E7 C9 81E7",
+ "CE D7 81E8",
+ "BC AB 81EA",
+ "BD AD 81ED",
+ "BB EA 81F3",
+ "C3 D7 81F4",
+ "E7 CA 81FA",
+ "E7 CB 81FB",
+ "B1 B1 81FC",
+ "E7 CC 81FE",
+ "E7 CD 8201",
+ "E7 CE 8202",
+ "E7 CF 8205",
+ "E7 D0 8207",
+ "B6 BD 8208",
+ "DA AA 8209",
+ "E7 D1 820A",
+ "C0 E5 820C",
+ "E7 D2 820D",
+ "BC CB 820E",
+ "E7 D3 8210",
+ "D0 B0 8212",
+ "E7 D4 8216",
+ "CA DE 8217",
+ "B4 DC 8218",
+ "C1 A4 821B",
+ "BD D8 821C",
+ "C9 F1 821E",
+ "BD AE 821F",
+ "E7 D5 8229",
+ "B9 D2 822A",
+ "E7 D6 822B",
+ "C8 CC 822C",
+ "E7 E4 822E",
+ "E7 D8 8233",
+ "C2 C9 8235",
+ "C7 F5 8236",
+ "B8 BF 8237",
+ "E7 D7 8238",
+ "C1 A5 8239",
+ "E7 D9 8240",
+ "C4 FA 8247",
+ "E7 DB 8258",
+ "E7 DA 8259",
+ "E7 DD 825A",
+ "E7 DC 825D",
+ "E7 DE 825F",
+ "E7 E0 8262",
+ "E7 DF 8264",
+ "B4 CF 8266",
+ "E7 E1 8268",
+ "E7 E2 826A",
+ "E7 E3 826B",
+ "BA B1 826E",
+ "CE C9 826F",
+ "E7 E5 8271",
+ "BF A7 8272",
+ "B1 F0 8276",
+ "E7 E6 8277",
+ "E7 E7 8278",
+ "E7 E8 827E",
+ "B0 F2 828B",
+ "E7 E9 828D",
+ "E7 EA 8292",
+ "C9 E7 8299",
+ "BC C7 829D",
+ "E7 EC 829F",
+ "B3 A9 82A5",
+ "B0 B2 82A6",
+ "E7 EB 82AB",
+ "E7 EE 82AC",
+ "C7 CE 82AD",
+ "BF C4 82AF",
+ "B2 D6 82B1",
+ "CB A7 82B3",
+ "B7 DD 82B8",
+ "B6 DC 82B9",
+ "E7 ED 82BB",
+ "B2 EA 82BD",
+ "B4 A3 82C5",
+ "B1 F1 82D1",
+ "E7 F2 82D2",
+ "CE EA 82D3",
+ "C2 DD 82D4",
+ "C9 C4 82D7",
+ "E7 FE 82D9",
+ "B2 D7 82DB",
+ "E7 FC 82DC",
+ "E7 FA 82DE",
+ "E7 F1 82DF",
+ "E7 EF 82E1",
+ "E7 F0 82E3",
+ "BC E3 82E5",
+ "B6 EC 82E6",
+ "C3 F7 82E7",
+ "C6 D1 82EB",
+ "B1 D1 82F1",
+ "E7 F4 82F3",
+ "E7 F3 82F4",
+ "E7 F9 82F9",
+ "E7 F5 82FA",
+ "E7 F8 82FB",
+ "CC D0 8302",
+ "E7 F7 8303",
+ "B2 D8 8304",
+ "B3 FD 8305",
+ "E7 FB 8306",
+ "E7 FD 8309",
+ "B7 D4 830E",
+ "E8 A3 8316",
+ "E8 AC 8317",
+ "E8 AD 8318",
+ "B0 AB 831C",
+ "E8 B4 8323",
+ "B0 F1 8328",
+ "E8 AB 832B",
+ "E8 AA 832F",
+ "E8 A5 8331",
+ "E8 A4 8332",
+ "E8 A2 8334",
+ "E8 A1 8335",
+ "C3 E3 8336",
+ "C2 FB 8338",
+ "E8 A7 8339",
+ "E8 A6 8340",
+ "E8 A9 8345",
+ "C1 F0 8349",
+ "B7 D5 834A",
+ "B1 C1 834F",
+ "E8 A8 8350",
+ "B9 D3 8352",
+ "C1 F1 8358",
+ "E8 BA 8373",
+ "E8 BB 8375",
+ "B2 D9 8377",
+ "B2 AE 837B",
+ "E8 B8 837C",
+ "E8 AE 8385",
+ "E8 B6 8387",
+ "E8 BD 8389",
+ "E8 B7 838A",
+ "E8 B5 838E",
+ "E7 F6 8393",
+ "E8 B3 8396",
+ "E8 AF 839A",
+ "B4 D0 839E",
+ "E8 B1 839F",
+ "E8 BC 83A0",
+ "E8 B2 83A2",
+ "E8 BE 83A8",
+ "E8 B0 83AA",
+ "C7 FC 83AB",
+ "CD E9 83B1",
+ "E8 B9 83B5",
+ "E8 CF 83BD",
+ "E8 C7 83C1",
+ "BF FB 83C5",
+ "B5 C6 83CA",
+ "B6 DD 83CC",
+ "E8 C2 83CE",
+ "B2 DB 83D3",
+ "BE D4 83D6",
+ "E8 C5 83D8",
+ "BA DA 83DC",
+ "C5 D1 83DF",
+ "E8 CA 83E0",
+ "CA EE 83E9",
+ "E8 C1 83EB",
+ "B2 DA 83EF",
+ "B8 D6 83F0",
+ "C9 A9 83F1",
+ "E8 CB 83F2",
+ "E8 BF 83F4",
+ "E8 C8 83F7",
+ "E8 D2 83FB",
+ "E8 C3 83FD",
+ "E8 C4 8403",
+ "C6 BA 8404",
+ "E8 C9 8407",
+ "E8 C6 840B",
+ "CB A8 840C",
+ "E8 CC 840D",
+ "B0 E0 840E",
+ "E8 C0 8413",
+ "E8 CE 8420",
+ "E8 CD 8422",
+ "C7 EB 8429",
+ "E8 D4 842A",
+ "E8 DF 842C",
+ "B3 FE 8431",
+ "E8 E2 8435",
+ "E8 D0 8438",
+ "E8 D5 843C",
+ "CD EE 843D",
+ "E8 DE 8446",
+ "CD D5 8449",
+ "CE AA 844E",
+ "C3 F8 8457",
+ "B3 EB 845B",
+ "C9 F2 8461",
+ "E8 E4 8462",
+ "C6 A1 8463",
+ "B0 B1 8466",
+ "E8 DD 8469",
+ "E8 D9 846B",
+ "C1 F2 846C",
+ "E8 D3 846D",
+ "E8 DB 846E",
+ "E8 E0 846F",
+ "C7 AC 8471",
+ "B0 AA 8475",
+ "E8 D8 8477",
+ "E8 E1 8479",
+ "C9 F8 847A",
+ "E8 DC 8482",
+ "E8 D7 8484",
+ "BE D5 848B",
+ "BD AF 8490",
+ "BC AC 8494",
+ "CC D8 8499",
+ "C9 C7 849C",
+ "E8 E7 849F",
+ "E8 F0 84A1",
+ "E8 DA 84AD",
+ "B3 F7 84B2",
+ "BE F8 84B8",
+ "E8 E5 84B9",
+ "E8 EA 84BB",
+ "C1 F3 84BC",
+ "E8 E6 84BF",
+ "E8 ED 84C1",
+ "C3 DF 84C4",
+ "E8 EE 84C6",
+ "CD D6 84C9",
+ "E8 E3 84CA",
+ "B3 B8 84CB",
+ "E8 E9 84CD",
+ "E8 EC 84D0",
+ "CC AC 84D1",
+ "E8 EF 84D6",
+ "E8 E8 84D9",
+ "E8 EB 84DA",
+ "CB A9 84EC",
+ "CF A1 84EE",
+ "E8 F3 84F4",
+ "E8 FA 84FC",
+ "E8 F2 84FF",
+ "BC C3 8500",
+ "E8 D1 8506",
+ "CA CE 8511",
+ "CC A2 8513",
+ "E8 F9 8514",
+ "E8 F8 8515",
+ "E8 F4 8517",
+ "E8 F5 8518",
+ "B1 B6 851A",
+ "E8 F7 851F",
+ "E8 F1 8521",
+ "C4 D5 8526",
+ "E8 F6 852C",
+ "B0 FE 852D",
+ "C2 A2 8535",
+ "CA C3 853D",
+ "E8 FB 8540",
+ "E9 A1 8541",
+ "C8 D9 8543",
+ "E8 FE 8548",
+ "BE D6 8549",
+ "BC C9 854A",
+ "E9 A3 854B",
+ "B6 BE 854E",
+ "E9 A4 8555",
+ "C9 F9 8557",
+ "E8 FD 8558",
+ "E8 D6 855A",
+ "E8 FC 8563",
+ "CF CF 8568",
+ "C6 A2 8569",
+ "C9 F3 856A",
+ "E9 AB 856D",
+ "E9 B1 8577",
+ "E9 B2 857E",
+ "E9 A5 8580",
+ "C7 F6 8584",
+ "E9 AF 8587",
+ "E9 A7 8588",
+ "E9 A9 858A",
+ "E9 B3 8590",
+ "E9 A8 8591",
+ "E9 AC 8594",
+ "B1 F2 8597",
+ "C6 E5 8599",
+ "E9 AD 859B",
+ "E9 B0 859C",
+ "E9 A6 85A4",
+ "C1 A6 85A6",
+ "E9 AA 85A8",
+ "BB A7 85A9",
+ "BF C5 85AA",
+ "B7 B0 85AB",
+ "CC F4 85AC",
+ "CC F9 85AE",
+ "BD F2 85AF",
+ "E9 B7 85B9",
+ "E9 B5 85BA",
+ "CF CE 85C1",
+ "E9 B4 85C9",
+ "CD F5 85CD",
+ "E9 B6 85CF",
+ "E9 B8 85D0",
+ "E9 B9 85D5",
+ "E9 BC 85DC",
+ "E9 BA 85DD",
+ "C6 A3 85E4",
+ "E9 BB 85E5",
+ "C8 CD 85E9",
+ "E9 AE 85EA",
+ "BD F3 85F7",
+ "E9 BD 85F9",
+ "E9 C2 85FA",
+ "C1 F4 85FB",
+ "E9 C1 85FE",
+ "E9 A2 8602",
+ "E9 C3 8606",
+ "C1 C9 8607",
+ "E9 BE 860A",
+ "E9 C0 860B",
+ "E9 BF 8613",
+ "DD B1 8616",
+ "DD A2 8617",
+ "E9 C5 861A",
+ "E9 C4 8622",
+ "CD F6 862D",
+ "E2 BC 862F",
+ "E9 C6 8630",
+ "E9 C7 863F",
+ "E9 C8 864D",
+ "B8 D7 864E",
+ "B5 D4 8650",
+ "E9 CA 8654",
+ "D1 DD 8655",
+ "B5 F5 865A",
+ "CE BA 865C",
+ "B6 F3 865E",
+ "E9 CB 865F",
+ "E9 CC 8667",
+ "C3 EE 866B",
+ "E9 CD 8671",
+ "C6 FA 8679",
+ "B0 BA 867B",
+ "B2 E3 868A",
+ "E9 D2 868B",
+ "E9 D3 868C",
+ "E9 CE 8693",
+ "BB BD 8695",
+ "E9 CF 86A3",
+ "C7 C2 86A4",
+ "E9 D0 86A9",
+ "E9 D1 86AA",
+ "E9 DB 86AB",
+ "E9 D5 86AF",
+ "E9 D8 86B0",
+ "E9 D4 86B6",
+ "E9 D6 86C4",
+ "E9 D7 86C6",
+ "BC D8 86C7",
+ "E9 D9 86C9",
+ "C3 C1 86CB",
+ "B7 D6 86CD",
+ "B3 C2 86CE",
+ "E9 DC 86D4",
+ "B3 BF 86D9",
+ "E9 E1 86DB",
+ "E9 DD 86DE",
+ "E9 E0 86DF",
+ "C8 BA 86E4",
+ "E9 DE 86E9",
+ "E9 DF 86EC",
+ "C9 C8 86ED",
+ "C8 DA 86EE",
+ "E9 E2 86EF",
+ "C2 FD 86F8",
+ "E9 EC 86F9",
+ "E9 E8 86FB",
+ "B2 EB 86FE",
+ "E9 E6 8700",
+ "CB AA 8702",
+ "E9 E7 8703",
+ "E9 E4 8706",
+ "E9 E5 8708",
+ "E9 EA 8709",
+ "E9 ED 870A",
+ "E9 EB 870D",
+ "E9 E9 8711",
+ "E9 E3 8712",
+ "C3 D8 8718",
+ "E9 F4 871A",
+ "CC AA 871C",
+ "E9 F2 8725",
+ "E9 F3 8729",
+ "E9 EE 8734",
+ "E9 F0 8737",
+ "E9 F1 873B",
+ "E9 EF 873F",
+ "C0 E6 8749",
+ "CF B9 874B",
+ "E9 F8 874C",
+ "E9 F9 874E",
+ "EA A1 8753",
+ "BF AA 8755",
+ "E9 FB 8757",
+ "E9 FE 8759",
+ "E9 F6 875F",
+ "E9 F5 8760",
+ "EA A2 8763",
+ "B2 DC 8766",
+ "E9 FC 8768",
+ "EA A3 876A",
+ "E9 FD 876E",
+ "E9 FA 8774",
+ "C4 B3 8776",
+ "E9 F7 8778",
+ "C7 E8 877F",
+ "EA A7 8782",
+ "CD BB 878D",
+ "EA A6 879F",
+ "EA A5 87A2",
+ "EA AE 87AB",
+ "EA A8 87AF",
+ "EA B0 87B3",
+ "CD E6 87BA",
+ "EA B3 87BB",
+ "EA AA 87BD",
+ "EA AB 87C0",
+ "EA AF 87C4",
+ "EA B2 87C6",
+ "EA B1 87C7",
+ "EA A9 87CB",
+ "EA AC 87D0",
+ "EA BD 87D2",
+ "EA B6 87E0",
+ "EA B4 87EF",
+ "EA B5 87F2",
+ "EA BA 87F6",
+ "EA BB 87F7",
+ "B3 AA 87F9",
+ "B5 C2 87FB",
+ "EA B9 87FE",
+ "EA A4 8805",
+ "EA B8 880D",
+ "EA BC 880E",
+ "EA B7 880F",
+ "EA BE 8811",
+ "EA C0 8815",
+ "EA BF 8816",
+ "EA C2 8821",
+ "EA C1 8822",
+ "E9 DA 8823",
+ "EA C6 8827",
+ "EA C3 8831",
+ "EA C4 8836",
+ "EA C5 8839",
+ "EA C7 883B",
+ "B7 EC 8840",
+ "EA C9 8842",
+ "EA C8 8844",
+ "BD B0 8846",
+ "B9 D4 884C",
+ "DE A7 884D",
+ "EA CA 8852",
+ "BD D1 8853",
+ "B3 B9 8857",
+ "EA CB 8859",
+ "B1 D2 885B",
+ "BE D7 885D",
+ "EA CC 885E",
+ "B9 D5 8861",
+ "EA CD 8862",
+ "B0 E1 8863",
+ "C9 BD 8868",
+ "EA CE 886B",
+ "BF EA 8870",
+ "EA D5 8872",
+ "EA D2 8875",
+ "C3 EF 8877",
+ "EA D3 887D",
+ "EA D0 887E",
+ "B6 DE 887F",
+ "EA CF 8881",
+ "EA D6 8882",
+ "B7 B6 8888",
+ "C2 DE 888B",
+ "EA DC 888D",
+ "EA D8 8892",
+ "C2 B5 8896",
+ "EA D7 8897",
+ "EA DA 8899",
+ "EA D1 889E",
+ "EA DB 88A2",
+ "EA DD 88A4",
+ "C8 EF 88AB",
+ "EA D9 88AE",
+ "EA DE 88B0",
+ "EA E0 88B1",
+ "B8 D3 88B4",
+ "EA D4 88B5",
+ "B0 C1 88B7",
+ "EA DF 88BF",
+ "BA DB 88C1",
+ "CE F6 88C2",
+ "EA E1 88C3",
+ "EA E2 88C4",
+ "C1 F5 88C5",
+ "CE A2 88CF",
+ "EA E3 88D4",
+ "CD B5 88D5",
+ "EA E4 88D8",
+ "EA E5 88D9",
+ "CA E4 88DC",
+ "EA E6 88DD",
+ "BA C0 88DF",
+ "CE A3 88E1",
+ "EA EB 88E8",
+ "EA EC 88F2",
+ "BE D8 88F3",
+ "EA EA 88F4",
+ "CD E7 88F8",
+ "EA E7 88F9",
+ "EA E9 88FC",
+ "C0 BD 88FD",
+ "BF FE 88FE",
+ "EA E8 8902",
+ "EA ED 8904",
+ "CA A3 8907",
+ "EA EF 890A",
+ "EA EE 890C",
+ "B3 EC 8910",
+ "CB AB 8912",
+ "EA F0 8913",
+ "EA FC 891D",
+ "EA F2 891E",
+ "EA F3 8925",
+ "EA F4 892A",
+ "EA F5 892B",
+ "EA F9 8936",
+ "EA FA 8938",
+ "EA F8 893B",
+ "EA F6 8941",
+ "EA F1 8943",
+ "EA F7 8944",
+ "EA FB 894C",
+ "F0 B7 894D",
+ "B2 A8 8956",
+ "EA FE 895E",
+ "B6 DF 895F",
+ "EA FD 8960",
+ "EB A2 8964",
+ "EB A1 8966",
+ "EB A4 896A",
+ "EB A3 896D",
+ "EB A5 896F",
+ "BD B1 8972",
+ "EB A6 8974",
+ "EB A7 8977",
+ "EB A8 897E",
+ "C0 BE 897F",
+ "CD D7 8981",
+ "EB A9 8983",
+ "CA A4 8986",
+ "C7 C6 8987",
+ "EB AA 8988",
+ "EB AB 898A",
+ "B8 AB 898B",
+ "B5 AC 898F",
+ "EB AC 8993",
+ "BB EB 8996",
+ "C7 C1 8997",
+ "EB AD 8998",
+ "B3 D0 899A",
+ "EB AE 89A1",
+ "EB B0 89A6",
+ "CD F7 89A7",
+ "EB AF 89A9",
+ "BF C6 89AA",
+ "EB B1 89AC",
+ "EB B2 89AF",
+ "EB B3 89B2",
+ "B4 D1 89B3",
+ "EB B4 89BA",
+ "EB B5 89BD",
+ "EB B6 89BF",
+ "EB B7 89C0",
+ "B3 D1 89D2",
+ "EB B8 89DA",
+ "EB B9 89DC",
+ "EB BA 89DD",
+ "B2 F2 89E3",
+ "BF A8 89E6",
+ "EB BB 89E7",
+ "EB BC 89F4",
+ "EB BD 89F8",
+ "B8 C0 8A00",
+ "C4 FB 8A02",
+ "EB BE 8A03",
+ "B7 D7 8A08",
+ "BF D6 8A0A",
+ "EB C1 8A0C",
+ "C6 A4 8A0E",
+ "EB C0 8A10",
+ "B7 B1 8A13",
+ "EB BF 8A16",
+ "C2 F7 8A17",
+ "B5 AD 8A18",
+ "EB C2 8A1B",
+ "EB C3 8A1D",
+ "BE D9 8A1F",
+ "B7 ED 8A23",
+ "EB C4 8A25",
+ "CB AC 8A2A",
+ "C0 DF 8A2D",
+ "B5 F6 8A31",
+ "CC F5 8A33",
+ "C1 CA 8A34",
+ "EB C5 8A36",
+ "BF C7 8A3A",
+ "C3 F0 8A3B",
+ "BE DA 8A3C",
+ "EB C6 8A41",
+ "EB C9 8A46",
+ "EB CA 8A48",
+ "BA BE 8A50",
+ "C2 C2 8A51",
+ "EB C8 8A52",
+ "BE DB 8A54",
+ "C9 BE 8A55",
+ "EB C7 8A5B",
+ "BB EC 8A5E",
+ "B1 D3 8A60",
+ "EB CE 8A62",
+ "B7 D8 8A63",
+ "BB EE 8A66",
+ "BB ED 8A69",
+ "CF CD 8A6B",
+ "EB CD 8A6C",
+ "EB CC 8A6D",
+ "C1 A7 8A6E",
+ "B5 CD 8A70",
+ "CF C3 8A71",
+ "B3 BA 8A72",
+ "BE DC 8A73",
+ "EB CB 8A7C",
+ "EB D0 8A82",
+ "EB D1 8A84",
+ "EB CF 8A85",
+ "B8 D8 8A87",
+ "CD C0 8A89",
+ "BB EF 8A8C",
+ "C7 A7 8A8D",
+ "EB D4 8A91",
+ "C0 C0 8A93",
+ "C3 C2 8A95",
+ "CD B6 8A98",
+ "EB D7 8A9A",
+ "B8 EC 8A9E",
+ "C0 BF 8AA0",
+ "EB D3 8AA1",
+ "EB D8 8AA3",
+ "B8 ED 8AA4",
+ "EB D5 8AA5",
+ "EB D6 8AA6",
+ "EB D2 8AA8",
+ "C0 E2 8AAC",
+ "C6 C9 8AAD",
+ "C3 AF 8AB0",
+ "B2 DD 8AB2",
+ "C8 F0 8AB9",
+ "B5 C3 8ABC",
+ "C4 B4 8ABF",
+ "EB DB 8AC2",
+ "EB D9 8AC4",
+ "C3 CC 8AC7",
+ "C0 C1 8ACB",
+ "B4 D2 8ACC",
+ "EB DA 8ACD",
+ "BF DB 8ACF",
+ "CE CA 8AD2",
+ "CF C0 8AD6",
+ "EB DC 8ADA",
+ "EB E7 8ADB",
+ "C4 B5 8ADC",
+ "EB E6 8ADE",
+ "EB E3 8AE0",
+ "EB EB 8AE1",
+ "EB E4 8AE2",
+ "EB E0 8AE4",
+ "C4 FC 8AE6",
+ "EB DF 8AE7",
+ "EB DD 8AEB",
+ "CD A1 8AED",
+ "BB F0 8AEE",
+ "EB E1 8AF1",
+ "EB DE 8AF3",
+ "EB E5 8AF7",
+ "BD F4 8AF8",
+ "B8 C1 8AFA",
+ "C2 FA 8AFE",
+ "CB C5 8B00",
+ "B1 DA 8B01",
+ "B0 E2 8B02",
+ "C6 A5 8B04",
+ "EB E9 8B07",
+ "EB E8 8B0C",
+ "C6 E6 8B0E",
+ "EB ED 8B10",
+ "EB E2 8B14",
+ "EB EC 8B16",
+ "EB EE 8B17",
+ "B8 AC 8B19",
+ "EB EA 8B1A",
+ "B9 D6 8B1B",
+ "BC D5 8B1D",
+ "EB EF 8B20",
+ "CD D8 8B21",
+ "EB F2 8B26",
+ "EB F5 8B28",
+ "EB F3 8B2B",
+ "C9 B5 8B2C",
+ "EB F0 8B33",
+ "B6 E0 8B39",
+ "EB F4 8B3E",
+ "EB F6 8B41",
+ "EB FA 8B49",
+ "EB F7 8B4C",
+ "EB F9 8B4E",
+ "EB F8 8B4F",
+ "EB FB 8B56",
+ "BC B1 8B58",
+ "EB FD 8B5A",
+ "EB FC 8B5B",
+ "C9 E8 8B5C",
+ "EC A1 8B5F",
+ "B7 D9 8B66",
+ "EB FE 8B6B",
+ "EC A2 8B6C",
+ "EC A3 8B6F",
+ "B5 C4 8B70",
+ "E6 C1 8B71",
+ "BE F9 8B72",
+ "EC A4 8B74",
+ "B8 EE 8B77",
+ "EC A5 8B7D",
+ "EC A6 8B80",
+ "BB BE 8B83",
+ "DA CE 8B8A",
+ "EC A7 8B8C",
+ "EC A8 8B8E",
+ "BD B2 8B90",
+ "EC A9 8B92",
+ "EC AA 8B93",
+ "EC AB 8B96",
+ "EC AC 8B99",
+ "EC AD 8B9A",
+ "C3 AB 8C37",
+ "EC AE 8C3A",
+ "EC B0 8C3F",
+ "EC AF 8C41",
+ "C6 A6 8C46",
+ "EC B1 8C48",
+ "CB AD 8C4A",
+ "EC B2 8C4C",
+ "EC B3 8C4E",
+ "EC B4 8C50",
+ "EC B5 8C55",
+ "C6 DA 8C5A",
+ "BE DD 8C61",
+ "EC B6 8C62",
+ "B9 EB 8C6A",
+ "D0 AE 8C6B",
+ "EC B7 8C6C",
+ "EC B8 8C78",
+ "C9 BF 8C79",
+ "EC B9 8C7A",
+ "EC C1 8C7C",
+ "EC BA 8C82",
+ "EC BC 8C85",
+ "EC BB 8C89",
+ "EC BD 8C8A",
+ "CB C6 8C8C",
+ "EC BE 8C8D",
+ "EC BF 8C8E",
+ "EC C0 8C94",
+ "EC C2 8C98",
+ "B3 AD 8C9D",
+ "C4 E7 8C9E",
+ "C9 E9 8CA0",
+ "BA E2 8CA1",
+ "B9 D7 8CA2",
+ "C9 CF 8CA7",
+ "B2 DF 8CA8",
+ "C8 CE 8CA9",
+ "EC C5 8CAA",
+ "B4 D3 8CAB",
+ "C0 D5 8CAC",
+ "EC C4 8CAD",
+ "EC C9 8CAE",
+ "C3 F9 8CAF",
+ "CC E3 8CB0",
+ "EC C7 8CB2",
+ "EC C8 8CB3",
+ "B5 AE 8CB4",
+ "EC CA 8CB6",
+ "C7 E3 8CB7",
+ "C2 DF 8CB8",
+ "C8 F1 8CBB",
+ "C5 BD 8CBC",
+ "EC C6 8CBD",
+ "CB C7 8CBF",
+ "B2 EC 8CC0",
+ "EC CC 8CC1",
+ "CF A8 8CC2",
+ "C4 C2 8CC3",
+ "CF C5 8CC4",
+ "BB F1 8CC7",
+ "EC CB 8CC8",
+ "C2 B1 8CCA",
+ "EC DC 8CCD",
+ "C1 A8 8CCE",
+ "C6 F8 8CD1",
+ "C9 D0 8CD3",
+ "EC CF 8CDA",
+ "BB BF 8CDB",
+ "BB F2 8CDC",
+ "BE DE 8CDE",
+ "C7 E5 8CE0",
+ "B8 AD 8CE2",
+ "EC CE 8CE3",
+ "EC CD 8CE4",
+ "C9 EA 8CE6",
+ "BC C1 8CEA",
+ "C5 D2 8CED",
+ "EC D1 8CFA",
+ "EC D2 8CFB",
+ "B9 D8 8CFC",
+ "EC D0 8CFD",
+ "EC D3 8D04",
+ "EC D4 8D05",
+ "EC D6 8D07",
+ "C2 A3 8D08",
+ "EC D5 8D0A",
+ "B4 E6 8D0B",
+ "EC D8 8D0D",
+ "EC D7 8D0F",
+ "EC D9 8D10",
+ "EC DB 8D13",
+ "EC DD 8D14",
+ "EC DE 8D16",
+ "C0 D6 8D64",
+ "BC CF 8D66",
+ "EC DF 8D67",
+ "B3 D2 8D6B",
+ "EC E0 8D6D",
+ "C1 F6 8D70",
+ "EC E1 8D71",
+ "EC E2 8D73",
+ "C9 EB 8D74",
+ "B5 AF 8D77",
+ "EC E3 8D81",
+ "C4 B6 8D85",
+ "B1 DB 8D8A",
+ "EC E4 8D99",
+ "BC F1 8DA3",
+ "BF F6 8DA8",
+ "C2 AD 8DB3",
+ "EC E7 8DBA",
+ "EC E6 8DBE",
+ "EC E5 8DC2",
+ "EC ED 8DCB",
+ "EC EB 8DCC",
+ "EC E8 8DCF",
+ "EC EA 8DD6",
+ "EC E9 8DDA",
+ "EC EC 8DDB",
+ "B5 F7 8DDD",
+ "EC F0 8DDF",
+ "C0 D7 8DE1",
+ "EC F1 8DE3",
+ "B8 D9 8DE8",
+ "EC EE 8DEA",
+ "EC EF 8DEB",
+ "CF A9 8DEF",
+ "C4 B7 8DF3",
+ "C1 A9 8DF5",
+ "EC F2 8DFC",
+ "EC F5 8DFF",
+ "EC F3 8E08",
+ "EC F4 8E09",
+ "CD D9 8E0A",
+ "C6 A7 8E0F",
+ "EC F8 8E10",
+ "EC F6 8E1D",
+ "EC F7 8E1E",
+ "EC F9 8E1F",
+ "ED A9 8E2A",
+ "EC FC 8E30",
+ "EC FD 8E34",
+ "EC FB 8E35",
+ "EC FA 8E42",
+ "C4 FD 8E44",
+ "ED A1 8E47",
+ "ED A5 8E48",
+ "ED A2 8E49",
+ "EC FE 8E4A",
+ "ED A3 8E4C",
+ "ED A4 8E50",
+ "ED AB 8E55",
+ "ED A6 8E59",
+ "C0 D8 8E5F",
+ "ED A8 8E60",
+ "ED AA 8E63",
+ "ED A7 8E64",
+ "ED AD 8E72",
+ "BD B3 8E74",
+ "ED AC 8E76",
+ "ED AE 8E7C",
+ "ED AF 8E81",
+ "ED B2 8E84",
+ "ED B1 8E85",
+ "ED B0 8E87",
+ "ED B4 8E8A",
+ "ED B3 8E8B",
+ "CC F6 8E8D",
+ "ED B6 8E91",
+ "ED B5 8E93",
+ "ED B7 8E94",
+ "ED B8 8E99",
+ "ED BA 8EA1",
+ "ED B9 8EAA",
+ "BF C8 8EAB",
+ "ED BB 8EAC",
+ "B6 ED 8EAF",
+ "ED BC 8EB0",
+ "ED BE 8EB1",
+ "ED BF 8EBE",
+ "ED C0 8EC5",
+ "ED BD 8EC6",
+ "ED C1 8EC8",
+ "BC D6 8ECA",
+ "ED C2 8ECB",
+ "B5 B0 8ECC",
+ "B7 B3 8ECD",
+ "B8 AE 8ED2",
+ "ED C3 8EDB",
+ "C6 F0 8EDF",
+ "C5 BE 8EE2",
+ "ED C4 8EE3",
+ "ED C7 8EEB",
+ "BC B4 8EF8",
+ "ED C6 8EFB",
+ "ED C5 8EFC",
+ "B7 DA 8EFD",
+ "ED C8 8EFE",
+ "B3 D3 8F03",
+ "ED CA 8F05",
+ "BA DC 8F09",
+ "ED C9 8F0A",
+ "ED D2 8F0C",
+ "ED CC 8F12",
+ "ED CE 8F13",
+ "CA E5 8F14",
+ "ED CB 8F15",
+ "ED CD 8F19",
+ "ED D1 8F1B",
+ "ED CF 8F1C",
+ "B5 B1 8F1D",
+ "ED D0 8F1F",
+ "ED D3 8F26",
+ "C7 DA 8F29",
+ "CE D8 8F2A",
+ "BD B4 8F2F",
+ "ED D4 8F33",
+ "CD A2 8F38",
+ "ED D6 8F39",
+ "ED D5 8F3B",
+ "ED D9 8F3E",
+ "CD C1 8F3F",
+ "ED D8 8F42",
+ "B3 ED 8F44",
+ "ED D7 8F45",
+ "ED DC 8F46",
+ "ED DB 8F49",
+ "ED DA 8F4C",
+ "C5 B2 8F4D",
+ "ED DD 8F4E",
+ "ED DE 8F57",
+ "ED DF 8F5C",
+ "B9 EC 8F5F",
+ "B7 A5 8F61",
+ "ED E0 8F62",
+ "ED E1 8F63",
+ "ED E2 8F64",
+ "BF C9 8F9B",
+ "ED E3 8F9C",
+ "BC AD 8F9E",
+ "ED E4 8F9F",
+ "ED E5 8FA3",
+ "D2 A1 8FA7",
+ "D1 FE 8FA8",
+ "ED E6 8FAD",
+ "E5 F0 8FAE",
+ "ED E7 8FAF",
+ "C3 A4 8FB0",
+ "BF AB 8FB1",
+ "C7 C0 8FB2",
+ "ED E8 8FB7",
+ "CA D5 8FBA",
+ "C4 D4 8FBB",
+ "B9 FE 8FBC",
+ "C3 A9 8FBF",
+ "B1 AA 8FC2",
+ "CB F8 8FC4",
+ "BF D7 8FC5",
+ "B7 DE 8FCE",
+ "B6 E1 8FD1",
+ "CA D6 8FD4",
+ "ED E9 8FDA",
+ "ED EB 8FE2",
+ "ED EA 8FE5",
+ "B2 E0 8FE6",
+ "C6 F6 8FE9",
+ "ED EC 8FEA",
+ "C7 F7 8FEB",
+ "C5 B3 8FED",
+ "ED ED 8FEF",
+ "BD D2 8FF0",
+ "ED EF 8FF4",
+ "CC C2 8FF7",
+ "ED FE 8FF8",
+ "ED F1 8FF9",
+ "ED F2 8FFA",
+ "C4 C9 8FFD",
+ "C2 E0 9000",
+ "C1 F7 9001",
+ "C6 A8 9003",
+ "ED F0 9005",
+ "B5 D5 9006",
+ "ED F9 900B",
+ "ED F6 900D",
+ "EE A5 900E",
+ "C6 A9 900F",
+ "C3 E0 9010",
+ "ED F3 9011",
+ "C4 FE 9013",
+ "C5 D3 9014",
+ "ED F4 9015",
+ "ED F8 9016",
+ "BF E0 9017",
+ "C7 E7 9019",
+ "C4 CC 901A",
+ "C0 C2 901D",
+ "ED F7 901E",
+ "C2 AE 901F",
+ "C2 A4 9020",
+ "ED F5 9021",
+ "B0 A9 9022",
+ "CF A2 9023",
+ "ED FA 9027",
+ "C2 E1 902E",
+ "BD B5 9031",
+ "BF CA 9032",
+ "ED FC 9035",
+ "ED FB 9036",
+ "B0 EF 9038",
+ "ED FD 9039",
+ "C9 AF 903C",
+ "EE A7 903E",
+ "C6 DB 9041",
+ "BF EB 9042",
+ "C3 D9 9045",
+ "B6 F8 9047",
+ "EE A6 9049",
+ "CD B7 904A",
+ "B1 BF 904B",
+ "CA D7 904D",
+ "B2 E1 904E",
+ "EE A1 904F",
+ "EE A2 9050",
+ "EE A3 9051",
+ "EE A4 9052",
+ "C6 BB 9053",
+ "C3 A3 9054",
+ "B0 E3 9055",
+ "EE A8 9056",
+ "EE A9 9058",
+ "F4 A3 9059",
+ "C2 BD 905C",
+ "EE AA 905E",
+ "B1 F3 9060",
+ "C1 CC 9061",
+ "B8 AF 9063",
+ "CD DA 9065",
+ "EE AB 9068",
+ "C5 AC 9069",
+ "C1 F8 906D",
+ "BC D7 906E",
+ "EE AC 906F",
+ "EE AF 9072",
+ "BD E5 9075",
+ "EE AD 9076",
+ "C1 AB 9077",
+ "C1 AA 9078",
+ "B0 E4 907A",
+ "CE CB 907C",
+ "EE B1 907D",
+ "C8 F2 907F",
+ "EE B3 9080",
+ "EE B2 9081",
+ "EE B0 9082",
+ "E3 E4 9083",
+ "B4 D4 9084",
+ "ED EE 9087",
+ "EE B5 9089",
+ "EE B4 908A",
+ "EE B6 908F",
+ "CD B8 9091",
+ "C6 E1 90A3",
+ "CB AE 90A6",
+ "EE B7 90A8",
+ "BC D9 90AA",
+ "EE B8 90AF",
+ "EE B9 90B1",
+ "EE BA 90B5",
+ "C5 A1 90B8",
+ "B0 EA 90C1",
+ "B9 D9 90CA",
+ "CF BA 90CE",
+ "EE BE 90DB",
+ "B7 B4 90E1",
+ "EE BB 90E2",
+ "EE BC 90E4",
+ "C9 F4 90E8",
+ "B3 D4 90ED",
+ "CD B9 90F5",
+ "B6 BF 90F7",
+ "C5 D4 90FD",
+ "EE BF 9102",
+ "EE C0 9112",
+ "EE C1 9119",
+ "C5 A2 912D",
+ "EE C3 9130",
+ "EE C2 9132",
+ "C6 D3 9149",
+ "EE C4 914A",
+ "BD B6 914B",
+ "BC E0 914C",
+ "C7 DB 914D",
+ "C3 F1 914E",
+ "BC F2 9152",
+ "BF EC 9154",
+ "EE C5 9156",
+ "EE C6 9158",
+ "BF DD 9162",
+ "EE C7 9163",
+ "EE C8 9165",
+ "EE C9 9169",
+ "CD EF 916A",
+ "BD B7 916C",
+ "EE CB 9172",
+ "EE CA 9173",
+ "B9 DA 9175",
+ "B9 F3 9177",
+ "BB C0 9178",
+ "EE CE 9182",
+ "BD E6 9187",
+ "EE CD 9189",
+ "EE CC 918B",
+ "C2 E9 918D",
+ "B8 EF 9190",
+ "C0 C3 9192",
+ "C8 B0 9197",
+ "BD B9 919C",
+ "EE CF 91A2",
+ "BE DF 91A4",
+ "EE D2 91AA",
+ "EE D0 91AB",
+ "EE D1 91AF",
+ "EE D4 91B4",
+ "EE D3 91B5",
+ "BE FA 91B8",
+ "EE D5 91BA",
+ "EE D6 91C0",
+ "EE D7 91C1",
+ "C8 D0 91C6",
+ "BA D3 91C7",
+ "BC E1 91C8",
+ "EE D8 91C9",
+ "EE D9 91CB",
+ "CE A4 91CC",
+ "BD C5 91CD",
+ "CC EE 91CE",
+ "CE CC 91CF",
+ "EE DA 91D0",
+ "B6 E2 91D1",
+ "EE DB 91D6",
+ "C5 A3 91D8",
+ "EE DE 91DB",
+ "B3 F8 91DC",
+ "BF CB 91DD",
+ "EE DC 91DF",
+ "EE DD 91E1",
+ "C4 E0 91E3",
+ "CB D5 91E6",
+ "B6 FC 91E7",
+ "EE E0 91F5",
+ "EE E1 91F6",
+ "EE DF 91FC",
+ "EE E3 91FF",
+ "C6 DF 920D",
+ "B3 C3 920E",
+ "EE E7 9211",
+ "EE E4 9214",
+ "EE E6 9215",
+ "EE E2 921E",
+ "EF CF 9229",
+ "EE E5 922C",
+ "CE EB 9234",
+ "B8 DA 9237",
+ "EE EF 923F",
+ "C5 B4 9244",
+ "EE EA 9245",
+ "EE ED 9248",
+ "EE EB 9249",
+ "EE F0 924B",
+ "EE F1 9250",
+ "EE E9 9257",
+ "EE F6 925A",
+ "B1 F4 925B",
+ "EE E8 925E",
+ "C8 AD 9262",
+ "EE EC 9264",
+ "BE E0 9266",
+ "B9 DB 9271",
+ "CB C8 927E",
+ "B6 E4 9280",
+ "BD C6 9283",
+ "C6 BC 9285",
+ "C1 AD 9291",
+ "EE F4 9293",
+ "EE EE 9295",
+ "EE F3 9296",
+ "CC C3 9298",
+ "C4 B8 929A",
+ "EE F5 929B",
+ "EE F2 929C",
+ "C1 AC 92AD",
+ "EE F9 92B7",
+ "EE F8 92B9",
+ "EE F7 92CF",
+ "CB AF 92D2",
+ "BD FB 92E4",
+ "EE FA 92E9",
+ "CA DF 92EA",
+ "B1 D4 92ED",
+ "C9 C6 92F2",
+ "C3 F2 92F3",
+ "B5 F8 92F8",
+ "EE FC 92FA",
+ "B9 DD 92FC",
+ "BB AC 9306",
+ "EE FB 930F",
+ "BF ED 9310",
+ "BF EE 9318",
+ "EF A1 9319",
+ "EF A3 931A",
+ "BE FB 9320",
+ "EF A2 9322",
+ "EF A4 9323",
+ "B6 D3 9326",
+ "C9 C5 9328",
+ "BC E2 932B",
+ "CF A3 932C",
+ "EE FE 932E",
+ "BA F8 932F",
+ "CF BF 9332",
+ "EF A6 9335",
+ "EF A5 933A",
+ "EF A7 933B",
+ "EE FD 9344",
+ "C6 E9 934B",
+ "C5 D5 934D",
+ "C4 D7 9354",
+ "EF AC 9356",
+ "C3 C3 935B",
+ "EF A8 935C",
+ "EF A9 9360",
+ "B7 AD 936C",
+ "EF AB 936E",
+ "B8 B0 9375",
+ "EF AA 937C",
+ "BE E1 937E",
+ "B3 F9 938C",
+ "EF B0 9394",
+ "BA BF 9396",
+ "C1 F9 9397",
+ "C4 CA 939A",
+ "B3 BB 93A7",
+ "EF AE 93AC",
+ "EF AF 93AD",
+ "C4 C3 93AE",
+ "EF AD 93B0",
+ "EF B1 93B9",
+ "EF B7 93C3",
+ "EF BA 93C8",
+ "EF B9 93D0",
+ "C5 AD 93D1",
+ "EF B2 93D6",
+ "EF B3 93D7",
+ "EF B6 93D8",
+ "EF B8 93DD",
+ "B6 C0 93E1",
+ "EF BB 93E4",
+ "EF B5 93E5",
+ "EF B4 93E8",
+ "EF BF 9403",
+ "EF C0 9407",
+ "EF C1 9410",
+ "EF BE 9413",
+ "EF BD 9414",
+ "BE E2 9418",
+ "C6 AA 9419",
+ "EF BC 941A",
+ "EF C5 9421",
+ "EF C3 942B",
+ "EF C4 9435",
+ "EF C2 9436",
+ "C2 F8 9438",
+ "EF C6 943A",
+ "EF C7 9441",
+ "EF C9 9444",
+ "B4 D5 9451",
+ "EF C8 9452",
+ "CC FA 9453",
+ "EF D4 945A",
+ "EF CA 945B",
+ "EF CD 945E",
+ "EF CB 9460",
+ "EF CC 9462",
+ "EF CE 946A",
+ "EF D0 9470",
+ "EF D1 9475",
+ "EF D2 9477",
+ "EF D5 947C",
+ "EF D3 947D",
+ "EF D6 947E",
+ "EF D8 947F",
+ "EF D7 9481",
+ "C4 B9 9577",
+ "CC E7 9580",
+ "EF D9 9582",
+ "C1 AE 9583",
+ "EF DA 9587",
+ "CA C4 9589",
+ "EF DB 958A",
+ "B3 AB 958B",
+ "B1 BC 958F",
+ "B4 D7 9591",
+ "B4 D6 9593",
+ "EF DC 9594",
+ "EF DD 9596",
+ "EF DE 9598",
+ "EF DF 9599",
+ "EF E0 95A0",
+ "B4 D8 95A2",
+ "B3 D5 95A3",
+ "B9 DE 95A4",
+ "C8 B6 95A5",
+ "EF E2 95A7",
+ "EF E1 95A8",
+ "EF E3 95AD",
+ "B1 DC 95B2",
+ "EF E6 95B9",
+ "EF E5 95BB",
+ "EF E4 95BC",
+ "EF E7 95BE",
+ "EF EA 95C3",
+ "B0 C7 95C7",
+ "EF E8 95CA",
+ "EF EC 95CC",
+ "EF EB 95CD",
+ "EF EE 95D4",
+ "EF ED 95D5",
+ "EF EF 95D6",
+ "C6 AE 95D8",
+ "EF F0 95DC",
+ "EF F1 95E1",
+ "EF F3 95E2",
+ "EF F2 95E5",
+ "C9 EC 961C",
+ "EF F4 9621",
+ "EF F5 9628",
+ "BA E5 962A",
+ "EF F6 962E",
+ "EF F7 962F",
+ "CB C9 9632",
+ "C1 CB 963B",
+ "B0 A4 963F",
+ "C2 CB 9640",
+ "EF F8 9642",
+ "C9 ED 9644",
+ "EF FB 964B",
+ "EF F9 964C",
+ "B9 DF 964D",
+ "EF FA 964F",
+ "B8 C2 9650",
+ "CA C5 965B",
+ "EF FD 965C",
+ "F0 A1 965D",
+ "EF FE 965E",
+ "F0 A2 965F",
+ "B1 A1 9662",
+ "BF D8 9663",
+ "BD FC 9664",
+ "B4 D9 9665",
+ "F0 A3 9666",
+ "C7 E6 966A",
+ "F0 A5 966C",
+ "B1 A2 9670",
+ "F0 A4 9672",
+ "C4 C4 9673",
+ "CE CD 9675",
+ "C6 AB 9676",
+ "EF FC 9677",
+ "CE A6 9678",
+ "B8 B1 967A",
+ "CD DB 967D",
+ "B6 F9 9685",
+ "CE B4 9686",
+ "B7 A8 9688",
+ "C2 E2 968A",
+ "E7 A1 968B",
+ "F0 A6 968D",
+ "B3 AC 968E",
+ "BF EF 968F",
+ "B3 D6 9694",
+ "F0 A8 9695",
+ "F0 A9 9697",
+ "F0 A7 9698",
+ "B7 E4 9699",
+ "BA DD 969B",
+ "BE E3 969C",
+ "B1 A3 96A0",
+ "CE D9 96A3",
+ "F0 AB 96A7",
+ "EE AE 96A8",
+ "F0 AA 96AA",
+ "F0 AE 96B0",
+ "F0 AC 96B1",
+ "F0 AD 96B2",
+ "F0 AF 96B4",
+ "F0 B0 96B6",
+ "CE EC 96B7",
+ "F0 B1 96B8",
+ "F0 B2 96B9",
+ "C0 C9 96BB",
+ "C8 BB 96BC",
+ "BF FD 96C0",
+ "B4 E7 96C1",
+ "CD BA 96C4",
+ "B2 ED 96C5",
+ "BD B8 96C6",
+ "B8 DB 96C7",
+ "F0 B5 96C9",
+ "F0 B4 96CB",
+ "BB F3 96CC",
+ "F0 B6 96CD",
+ "F0 B3 96CE",
+ "BB A8 96D1",
+ "F0 BA 96D5",
+ "EA AD 96D6",
+ "D2 D6 96D9",
+ "BF F7 96DB",
+ "F0 B8 96DC",
+ "CE A5 96E2",
+ "C6 F1 96E3",
+ "B1 AB 96E8",
+ "C0 E3 96EA",
+ "BC B6 96EB",
+ "CA B7 96F0",
+ "B1 C0 96F2",
+ "CE ED 96F6",
+ "CD EB 96F7",
+ "F0 BB 96F9",
+ "C5 C5 96FB",
+ "BC FB 9700",
+ "F0 BC 9704",
+ "F0 BD 9706",
+ "BF CC 9707",
+ "F0 BE 9708",
+ "CE EE 970A",
+ "F0 B9 970D",
+ "F0 C0 970E",
+ "F0 C2 970F",
+ "F0 C1 9711",
+ "F0 BF 9713",
+ "F0 C3 9716",
+ "F0 C4 9719",
+ "C1 FA 971C",
+ "B2 E2 971E",
+ "F0 C5 9724",
+ "CC B8 9727",
+ "F0 C6 972A",
+ "F0 C7 9730",
+ "CF AA 9732",
+ "DB B1 9738",
+ "F0 C8 9739",
+ "F0 C9 973D",
+ "F0 CA 973E",
+ "F0 CE 9742",
+ "F0 CB 9744",
+ "F0 CC 9746",
+ "F0 CD 9748",
+ "F0 CF 9749",
+ "C0 C4 9752",
+ "CC F7 9756",
+ "C0 C5 9759",
+ "F0 D0 975C",
+ "C8 F3 975E",
+ "F0 D1 9760",
+ "F3 D3 9761",
+ "CC CC 9762",
+ "F0 D2 9764",
+ "F0 D3 9766",
+ "F0 D4 9768",
+ "B3 D7 9769",
+ "F0 D6 976B",
+ "BF D9 976D",
+ "F0 D7 9771",
+ "B7 A4 9774",
+ "F0 D8 9779",
+ "F0 DC 977A",
+ "F0 DA 977C",
+ "F0 DB 9781",
+ "B3 F3 9784",
+ "F0 D9 9785",
+ "F0 DD 9786",
+ "F0 DE 978B",
+ "B0 C8 978D",
+ "F0 DF 978F",
+ "F0 E0 9790",
+ "BE E4 9798",
+ "F0 E1 979C",
+ "B5 C7 97A0",
+ "F0 E4 97A3",
+ "F0 E3 97A6",
+ "F0 E2 97A8",
+ "EB F1 97AB",
+ "CA DC 97AD",
+ "F0 E5 97B3",
+ "F0 E6 97B4",
+ "F0 E7 97C3",
+ "F0 E8 97C6",
+ "F0 E9 97C8",
+ "F0 EA 97CB",
+ "B4 DA 97D3",
+ "F0 EB 97DC",
+ "F0 EC 97ED",
+ "C7 A3 97EE",
+ "F0 EE 97F2",
+ "B2 BB 97F3",
+ "F0 F1 97F5",
+ "F0 F0 97F6",
+ "B1 A4 97FB",
+ "B6 C1 97FF",
+ "CA C7 9801",
+ "C4 BA 9802",
+ "BA A2 9803",
+ "B9 E0 9805",
+ "BD E7 9806",
+ "BF DC 9808",
+ "F0 F3 980C",
+ "F0 F2 980F",
+ "CD C2 9810",
+ "B4 E8 9811",
+ "C8 D2 9812",
+ "C6 DC 9813",
+ "BF FC 9817",
+ "CE CE 9818",
+ "B7 DB 981A",
+ "F0 F6 9821",
+ "F0 F5 9824",
+ "CB CB 982C",
+ "C6 AC 982D",
+ "B1 D0 9834",
+ "F0 F7 9837",
+ "F0 F4 9838",
+ "C9 D1 983B",
+ "CD EA 983C",
+ "F0 F8 983D",
+ "F0 F9 9846",
+ "F0 FB 984B",
+ "C2 EA 984C",
+ "B3 DB 984D",
+ "B3 DC 984E",
+ "F0 FA 984F",
+ "B4 E9 9854",
+ "B8 B2 9855",
+ "B4 EA 9858",
+ "C5 BF 985B",
+ "CE E0 985E",
+ "B8 DC 9867",
+ "F0 FC 986B",
+ "F0 FD 986F",
+ "F0 FE 9870",
+ "F1 A1 9871",
+ "F1 A3 9873",
+ "F1 A2 9874",
+ "C9 F7 98A8",
+ "F1 A4 98AA",
+ "F1 A5 98AF",
+ "F1 A6 98B1",
+ "F1 A7 98B6",
+ "F1 A9 98C3",
+ "F1 A8 98C4",
+ "F1 AA 98C6",
+ "C8 F4 98DB",
+ "E6 CC 98DC",
+ "BF A9 98DF",
+ "B5 B2 98E2",
+ "F1 AB 98E9",
+ "F1 AC 98EB",
+ "D2 AC 98ED",
+ "DD BB 98EE",
+ "C8 D3 98EF",
+ "B0 FB 98F2",
+ "B0 BB 98F4",
+ "BB F4 98FC",
+ "CB B0 98FD",
+ "BE FE 98FE",
+ "F1 AD 9903",
+ "CC DF 9905",
+ "F1 AE 9909",
+ "CD DC 990A",
+ "B1 C2 990C",
+ "BB C1 9910",
+ "F1 AF 9912",
+ "B2 EE 9913",
+ "F1 B0 9914",
+ "F1 B1 9918",
+ "F1 B3 991D",
+ "F1 B4 991E",
+ "F1 B6 9920",
+ "F1 B2 9921",
+ "F1 B5 9924",
+ "B4 DB 9928",
+ "F1 B7 992C",
+ "F1 B8 992E",
+ "F1 B9 993D",
+ "F1 BA 993E",
+ "F1 BB 9942",
+ "F1 BD 9945",
+ "F1 BC 9949",
+ "F1 BF 994B",
+ "F1 C2 994C",
+ "F1 BE 9950",
+ "F1 C0 9951",
+ "F1 C1 9952",
+ "F1 C3 9955",
+ "B6 C2 9957",
+ "BC F3 9996",
+ "F1 C4 9997",
+ "F1 C5 9998",
+ "B9 E1 9999",
+ "F1 C6 99A5",
+ "B3 BE 99A8",
+ "C7 CF 99AC",
+ "F1 C7 99AD",
+ "F1 C8 99AE",
+ "C3 DA 99B3",
+ "C6 EB 99B4",
+ "F1 C9 99BC",
+ "C7 FD 99C1",
+ "C2 CC 99C4",
+ "B1 D8 99C5",
+ "B6 EE 99C6",
+ "B6 EF 99C8",
+ "C3 F3 99D0",
+ "F1 CE 99D1",
+ "B6 F0 99D2",
+ "B2 EF 99D5",
+ "F1 CD 99D8",
+ "F1 CB 99DB",
+ "F1 CC 99DD",
+ "F1 CA 99DF",
+ "F1 D8 99E2",
+ "F1 CF 99ED",
+ "F1 D0 99EE",
+ "F1 D1 99F1",
+ "F1 D2 99F2",
+ "F1 D4 99F8",
+ "F1 D3 99FB",
+ "BD D9 99FF",
+ "F1 D5 9A01",
+ "F1 D7 9A05",
+ "B5 B3 9A0E",
+ "F1 D6 9A0F",
+ "C1 FB 9A12",
+ "B8 B3 9A13",
+ "F1 D9 9A19",
+ "C2 CD 9A28",
+ "F1 DA 9A2B",
+ "C6 AD 9A30",
+ "F1 DB 9A37",
+ "F1 E0 9A3E",
+ "F1 DE 9A40",
+ "F1 DD 9A42",
+ "F1 DF 9A43",
+ "F1 DC 9A45",
+ "F1 E2 9A4D",
+ "F1 E1 9A55",
+ "F1 E4 9A57",
+ "B6 C3 9A5A",
+ "F1 E3 9A5B",
+ "F1 E5 9A5F",
+ "F1 E6 9A62",
+ "F1 E8 9A64",
+ "F1 E7 9A65",
+ "F1 E9 9A69",
+ "F1 EB 9A6A",
+ "F1 EA 9A6B",
+ "B9 FC 9AA8",
+ "F1 EC 9AAD",
+ "F1 ED 9AB0",
+ "B3 BC 9AB8",
+ "F1 EE 9ABC",
+ "F1 EF 9AC0",
+ "BF F1 9AC4",
+ "F1 F0 9ACF",
+ "F1 F1 9AD1",
+ "F1 F2 9AD3",
+ "F1 F3 9AD4",
+ "B9 E2 9AD8",
+ "F1 F4 9ADE",
+ "F1 F5 9ADF",
+ "F1 F6 9AE2",
+ "F1 F7 9AE3",
+ "F1 F8 9AE6",
+ "C8 B1 9AEA",
+ "F1 FA 9AEB",
+ "C9 A6 9AED",
+ "F1 FB 9AEE",
+ "F1 F9 9AEF",
+ "F1 FD 9AF1",
+ "F1 FC 9AF4",
+ "F1 FE 9AF7",
+ "F2 A1 9AFB",
+ "F2 A2 9B06",
+ "F2 A3 9B18",
+ "F2 A4 9B1A",
+ "F2 A5 9B1F",
+ "F2 A6 9B22",
+ "F2 A7 9B23",
+ "F2 A8 9B25",
+ "F2 A9 9B27",
+ "F2 AA 9B28",
+ "F2 AB 9B29",
+ "F2 AC 9B2A",
+ "F2 AD 9B2E",
+ "F2 AE 9B2F",
+ "DD B5 9B31",
+ "F2 AF 9B32",
+ "E4 F8 9B3B",
+ "B5 B4 9B3C",
+ "B3 A1 9B41",
+ "BA B2 9B42",
+ "F2 B1 9B43",
+ "F2 B0 9B44",
+ "CC A5 9B45",
+ "F2 B3 9B4D",
+ "F2 B4 9B4E",
+ "F2 B2 9B4F",
+ "F2 B5 9B51",
+ "CB E2 9B54",
+ "F2 B6 9B58",
+ "B5 FB 9B5A",
+ "CF A5 9B6F",
+ "F2 B7 9B74",
+ "F2 B9 9B83",
+ "B0 BE 9B8E",
+ "F2 BA 9B91",
+ "CA AB 9B92",
+ "F2 B8 9B93",
+ "F2 BB 9B96",
+ "F2 BC 9B97",
+ "F2 BD 9B9F",
+ "F2 BE 9BA0",
+ "F2 BF 9BA8",
+ "CB EE 9BAA",
+ "BB AD 9BAB",
+ "BA FA 9BAD",
+ "C1 AF 9BAE",
+ "F2 C0 9BB4",
+ "F2 C3 9BB9",
+ "F2 C1 9BC0",
+ "F2 C4 9BC6",
+ "B8 F1 9BC9",
+ "F2 C2 9BCA",
+ "F2 C5 9BCF",
+ "F2 C6 9BD1",
+ "F2 C7 9BD2",
+ "F2 CB 9BD4",
+ "BB AA 9BD6",
+ "C2 E4 9BDB",
+ "F2 CC 9BE1",
+ "F2 C9 9BE2",
+ "F2 C8 9BE3",
+ "F2 CA 9BE4",
+ "B7 DF 9BE8",
+ "F2 D0 9BF0",
+ "F2 CF 9BF1",
+ "F2 CE 9BF2",
+ "B0 B3 9BF5",
+ "F2 DA 9C04",
+ "F2 D6 9C06",
+ "F2 D7 9C08",
+ "F2 D3 9C09",
+ "F2 D9 9C0A",
+ "F2 D5 9C0C",
+ "B3 E2 9C0D",
+ "CF CC 9C10",
+ "F2 D8 9C12",
+ "F2 D4 9C13",
+ "F2 D2 9C14",
+ "F2 D1 9C15",
+ "F2 DC 9C1B",
+ "F2 DF 9C21",
+ "F2 DE 9C24",
+ "F2 DD 9C25",
+ "C9 C9 9C2D",
+ "F2 DB 9C2E",
+ "B0 F3 9C2F",
+ "F2 E0 9C30",
+ "F2 E2 9C32",
+ "B3 EF 9C39",
+ "F2 CD 9C3A",
+ "B1 B7 9C3B",
+ "F2 E4 9C3E",
+ "F2 E3 9C46",
+ "F2 E1 9C47",
+ "C3 AD 9C48",
+ "CB F0 9C52",
+ "CE DA 9C57",
+ "F2 E5 9C5A",
+ "F2 E6 9C60",
+ "F2 E7 9C67",
+ "F2 E8 9C76",
+ "F2 E9 9C78",
+ "C4 BB 9CE5",
+ "F2 EA 9CE7",
+ "C8 B7 9CE9",
+ "F2 EF 9CEB",
+ "F2 EB 9CEC",
+ "F2 EC 9CF0",
+ "CB B1 9CF3",
+ "CC C4 9CF4",
+ "C6 D0 9CF6",
+ "F2 F0 9D03",
+ "F2 F1 9D06",
+ "C6 BE 9D07",
+ "F2 EE 9D08",
+ "F2 ED 9D09",
+ "B2 AA 9D0E",
+ "F2 F9 9D12",
+ "F2 F8 9D15",
+ "B1 F5 9D1B",
+ "F2 F6 9D1F",
+ "F2 F5 9D23",
+ "F2 F3 9D26",
+ "B3 FB 9D28",
+ "F2 F2 9D2A",
+ "BC B2 9D2B",
+ "B2 A9 9D2C",
+ "B9 E3 9D3B",
+ "F2 FC 9D3E",
+ "F2 FB 9D3F",
+ "F2 FA 9D41",
+ "F2 F7 9D44",
+ "F2 FD 9D46",
+ "F2 FE 9D48",
+ "F3 A5 9D50",
+ "F3 A4 9D51",
+ "F3 A6 9D59",
+ "B1 AD 9D5C",
+ "F3 A1 9D5D",
+ "F3 A2 9D5E",
+ "B9 F4 9D60",
+ "CC B9 9D61",
+ "F3 A3 9D64",
+ "CB B2 9D6C",
+ "F3 AB 9D6F",
+ "F3 A7 9D72",
+ "F3 AC 9D7A",
+ "F3 A9 9D87",
+ "F3 A8 9D89",
+ "B7 DC 9D8F",
+ "F3 AD 9D9A",
+ "F3 AE 9DA4",
+ "F3 AF 9DA9",
+ "F3 AA 9DAB",
+ "F2 F4 9DAF",
+ "F3 B0 9DB2",
+ "C4 E1 9DB4",
+ "F3 B4 9DB8",
+ "F3 B5 9DBA",
+ "F3 B3 9DBB",
+ "F3 B2 9DC1",
+ "F3 B8 9DC2",
+ "F3 B1 9DC4",
+ "F3 B6 9DC6",
+ "F3 B7 9DCF",
+ "F3 BA 9DD3",
+ "F3 B9 9DD9",
+ "F3 BC 9DE6",
+ "F3 BD 9DED",
+ "F3 BE 9DEF",
+ "CF C9 9DF2",
+ "F3 BB 9DF8",
+ "C2 EB 9DF9",
+ "BA ED 9DFA",
+ "F3 BF 9DFD",
+ "F3 C0 9E1A",
+ "F3 C1 9E1B",
+ "F3 C2 9E1E",
+ "F3 C3 9E75",
+ "B8 B4 9E78",
+ "F3 C4 9E79",
+ "F3 C5 9E7D",
+ "BC AF 9E7F",
+ "F3 C6 9E81",
+ "F3 C7 9E88",
+ "F3 C8 9E8B",
+ "F3 C9 9E8C",
+ "F3 CC 9E91",
+ "F3 CA 9E92",
+ "CF BC 9E93",
+ "F3 CB 9E95",
+ "CE EF 9E97",
+ "F3 CD 9E9D",
+ "CE DB 9E9F",
+ "F3 CE 9EA5",
+ "C7 FE 9EA6",
+ "F3 CF 9EA9",
+ "F3 D1 9EAA",
+ "F3 D2 9EAD",
+ "F3 D0 9EB8",
+ "B9 ED 9EB9",
+ "CC CD 9EBA",
+ "CB E3 9EBB",
+ "D6 F7 9EBC",
+ "DD E0 9EBE",
+ "CB FB 9EBF",
+ "B2 AB 9EC4",
+ "F3 D4 9ECC",
+ "B5 D0 9ECD",
+ "F3 D5 9ECE",
+ "F3 D6 9ECF",
+ "F3 D7 9ED0",
+ "B9 F5 9ED2",
+ "F3 D8 9ED4",
+ "E0 D4 9ED8",
+ "CC DB 9ED9",
+ "C2 E3 9EDB",
+ "F3 D9 9EDC",
+ "F3 DB 9EDD",
+ "F3 DA 9EDE",
+ "F3 DC 9EE0",
+ "F3 DD 9EE5",
+ "F3 DE 9EE8",
+ "F3 DF 9EEF",
+ "F3 E0 9EF4",
+ "F3 E1 9EF6",
+ "F3 E2 9EF7",
+ "F3 E3 9EF9",
+ "F3 E4 9EFB",
+ "F3 E5 9EFC",
+ "F3 E6 9EFD",
+ "F3 E7 9F07",
+ "F3 E8 9F08",
+ "C5 A4 9F0E",
+ "B8 DD 9F13",
+ "F3 EA 9F15",
+ "C1 CD 9F20",
+ "F3 EB 9F21",
+ "F3 EC 9F2C",
+ "C9 A1 9F3B",
+ "F3 ED 9F3E",
+ "F3 EE 9F4A",
+ "E3 B7 9F4B",
+ "EC DA 9F4E",
+ "F0 ED 9F4F",
+ "F3 EF 9F52",
+ "F3 F0 9F54",
+ "F3 F2 9F5F",
+ "F3 F3 9F60",
+ "F3 F4 9F61",
+ "CE F0 9F62",
+ "F3 F1 9F63",
+ "F3 F5 9F66",
+ "F3 F6 9F67",
+ "F3 F8 9F6A",
+ "F3 F7 9F6C",
+ "F3 FA 9F72",
+ "F3 FB 9F76",
+ "F3 F9 9F77",
+ "CE B6 9F8D",
+ "F3 FC 9F95",
+ "F3 FD 9F9C",
+ "E3 D4 9F9D",
+ "F3 FE 9FA0",
+ "A1 AA FF01",
+ "A1 F4 FF03",
+ "A1 F0 FF04",
+ "A1 F3 FF05",
+ "A1 F5 FF06",
+ "A1 CA FF08",
+ "A1 CB FF09",
+ "A1 F6 FF0A",
+ "A1 DC FF0B",
+ "A1 A4 FF0C",
+ "A1 A5 FF0E",
+ "A1 BF FF0F",
+ "A3 B0 FF10",
+ "A3 B1 FF11",
+ "A3 B2 FF12",
+ "A3 B3 FF13",
+ "A3 B4 FF14",
+ "A3 B5 FF15",
+ "A3 B6 FF16",
+ "A3 B7 FF17",
+ "A3 B8 FF18",
+ "A3 B9 FF19",
+ "A1 A7 FF1A",
+ "A1 A8 FF1B",
+ "A1 E3 FF1C",
+ "A1 E1 FF1D",
+ "A1 E4 FF1E",
+ "A1 A9 FF1F",
+ "A1 F7 FF20",
+ "A3 C1 FF21",
+ "A3 C2 FF22",
+ "A3 C3 FF23",
+ "A3 C4 FF24",
+ "A3 C5 FF25",
+ "A3 C6 FF26",
+ "A3 C7 FF27",
+ "A3 C8 FF28",
+ "A3 C9 FF29",
+ "A3 CA FF2A",
+ "A3 CB FF2B",
+ "A3 CC FF2C",
+ "A3 CD FF2D",
+ "A3 CE FF2E",
+ "A3 CF FF2F",
+ "A3 D0 FF30",
+ "A3 D1 FF31",
+ "A3 D2 FF32",
+ "A3 D3 FF33",
+ "A3 D4 FF34",
+ "A3 D5 FF35",
+ "A3 D6 FF36",
+ "A3 D7 FF37",
+ "A3 D8 FF38",
+ "A3 D9 FF39",
+ "A3 DA FF3A",
+ "A1 CE FF3B",
+ "A1 C0 FF3C",
+ "A1 CF FF3D",
+ "A1 B0 FF3E",
+ "A1 B2 FF3F",
+ "A1 AE FF40",
+ "A3 E1 FF41",
+ "A3 E2 FF42",
+ "A3 E3 FF43",
+ "A3 E4 FF44",
+ "A3 E5 FF45",
+ "A3 E6 FF46",
+ "A3 E7 FF47",
+ "A3 E8 FF48",
+ "A3 E9 FF49",
+ "A3 EA FF4A",
+ "A3 EB FF4B",
+ "A3 EC FF4C",
+ "A3 ED FF4D",
+ "A3 EE FF4E",
+ "A3 EF FF4F",
+ "A3 F0 FF50",
+ "A3 F1 FF51",
+ "A3 F2 FF52",
+ "A3 F3 FF53",
+ "A3 F4 FF54",
+ "A3 F5 FF55",
+ "A3 F6 FF56",
+ "A3 F7 FF57",
+ "A3 F8 FF58",
+ "A3 F9 FF59",
+ "A3 FA FF5A",
+ "A1 D0 FF5B",
+ "A1 C3 FF5C",
+ "A1 D1 FF5D",
+ "A1 B1 FFE3",
+ "A1 EF FFE5",
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/genjisx0212.b b/appl/lib/convcs/genjisx0212.b
new file mode 100644
index 00000000..574cad5a
--- /dev/null
+++ b/appl/lib/convcs/genjisx0212.b
@@ -0,0 +1,6146 @@
+implement genjisx0212;
+
+include "sys.m";
+include "draw.m";
+
+genjisx0212 : module {
+ init: fn(ctxt: ref Draw->Context, args: list of string);
+};
+
+DATAFILE : con "/lib/convcs/jisx0212";
+
+PAGESZ : con 94;
+NPAGES : con 77;
+PAGE0 : con 16rA1;
+CHAR0 : con 16rA1;
+
+BADCHAR : con 16rFFFD;
+
+init(nil: ref Draw->Context, nil: list of string)
+{
+ sys := load Sys Sys->PATH;
+ fd := sys->create(DATAFILE, Sys->OWRITE, 8r644);
+ if (fd == nil) {
+ sys->print("cannot create %s: %r\n", DATAFILE);
+ return;
+ }
+
+ pages := array [NPAGES * PAGESZ] of { * => BADCHAR };
+
+ for (i := 0; i < len data; i++) {
+ (nil, toks) := sys->tokenize(data[i], "\t");
+ (bytes, ucode) := (hd toks, hd tl toks);
+ u := hex2int(ucode);
+ (nil, blist) := sys->tokenize(bytes, " ");
+ b1 := hex2int(hd blist);
+ b2 := hex2int(hd tl blist);
+
+ page := b1 - PAGE0;
+ char := b2 - CHAR0;
+
+ pages[(page * PAGESZ)+char] = u;
+ }
+
+ s := "";
+ for (i = 0; i < len pages; i++)
+ s[i] = pages[i];
+ pages = nil;
+ buf := array of byte s;
+ sys->write(fd, buf, len buf);
+}
+
+
+hex2int(s: string): int
+{
+ n := 0;
+ for (i := 0; i < len s; i++) {
+ case s[i] {
+ '0' to '9' =>
+ n = 16*n + s[i] - '0';
+ 'A' to 'F' =>
+ n = 16*n + s[i] + 10 - 'A';
+ 'a' to 'f' =>
+ n = 16*n + s[i] + 10 - 'a';
+ * =>
+ return n;
+ }
+ }
+ return n;
+}
+
+
+# Data is derived from Unicode Consortium "CharmapML" mapping data
+# for EUC-JP.
+# JISX0212 appears as charset G3 of the ISO-2022 based EUC-JP encoding
+# Although this format is not convenient for building our mapping data file
+# it is easily extracted from the Unicode Consortium data.
+
+data := array [] of {
+ "A2 C2 00A1",
+ "A2 F0 00A4",
+ "A2 C3 00A6",
+ "A2 ED 00A9",
+ "A2 EC 00AA",
+ "A2 EE 00AE",
+ "A2 B4 00AF",
+ "A2 B1 00B8",
+ "A2 EB 00BA",
+ "A2 C4 00BF",
+ "AA A2 00C0",
+ "AA A1 00C1",
+ "AA A4 00C2",
+ "AA AA 00C3",
+ "AA A3 00C4",
+ "AA A9 00C5",
+ "A9 A1 00C6",
+ "AA AE 00C7",
+ "AA B2 00C8",
+ "AA B1 00C9",
+ "AA B4 00CA",
+ "AA B3 00CB",
+ "AA C0 00CC",
+ "AA BF 00CD",
+ "AA C2 00CE",
+ "AA C1 00CF",
+ "AA D0 00D1",
+ "AA D2 00D2",
+ "AA D1 00D3",
+ "AA D4 00D4",
+ "AA D8 00D5",
+ "AA D3 00D6",
+ "A9 AC 00D8",
+ "AA E3 00D9",
+ "AA E2 00DA",
+ "AA E5 00DB",
+ "AA E4 00DC",
+ "AA F2 00DD",
+ "A9 B0 00DE",
+ "A9 CE 00DF",
+ "AB A2 00E0",
+ "AB A1 00E1",
+ "AB A4 00E2",
+ "AB AA 00E3",
+ "AB A3 00E4",
+ "AB A9 00E5",
+ "A9 C1 00E6",
+ "AB AE 00E7",
+ "AB B2 00E8",
+ "AB B1 00E9",
+ "AB B4 00EA",
+ "AB B3 00EB",
+ "AB C0 00EC",
+ "AB BF 00ED",
+ "AB C2 00EE",
+ "AB C1 00EF",
+ "A9 C3 00F0",
+ "AB D0 00F1",
+ "AB D2 00F2",
+ "AB D1 00F3",
+ "AB D4 00F4",
+ "AB D8 00F5",
+ "AB D3 00F6",
+ "A9 CC 00F8",
+ "AB E3 00F9",
+ "AB E2 00FA",
+ "AB E5 00FB",
+ "AB E4 00FC",
+ "AB F2 00FD",
+ "A9 D0 00FE",
+ "AB F3 00FF",
+ "AA A7 0100",
+ "AB A7 0101",
+ "AA A5 0102",
+ "AB A5 0103",
+ "AA A8 0104",
+ "AB A8 0105",
+ "AA AB 0106",
+ "AB AB 0107",
+ "AA AC 0108",
+ "AB AC 0109",
+ "AA AF 010A",
+ "AB AF 010B",
+ "AA AD 010C",
+ "AB AD 010D",
+ "AA B0 010E",
+ "AB B0 010F",
+ "A9 A2 0110",
+ "A9 C2 0111",
+ "AA B7 0112",
+ "AB B7 0113",
+ "AA B6 0116",
+ "AB B6 0117",
+ "AA B8 0118",
+ "AB B8 0119",
+ "AA B5 011A",
+ "AB B5 011B",
+ "AA BA 011C",
+ "AB BA 011D",
+ "AA BB 011E",
+ "AB BB 011F",
+ "AA BD 0120",
+ "AB BD 0121",
+ "AA BC 0122",
+ "AA BE 0124",
+ "AB BE 0125",
+ "A9 A4 0126",
+ "A9 C4 0127",
+ "AA C7 0128",
+ "AB C7 0129",
+ "AA C5 012A",
+ "AB C5 012B",
+ "AA C6 012E",
+ "AB C6 012F",
+ "AA C4 0130",
+ "A9 C5 0131",
+ "A9 A6 0132",
+ "A9 C6 0133",
+ "AA C8 0134",
+ "AB C8 0135",
+ "AA C9 0136",
+ "AB C9 0137",
+ "A9 C7 0138",
+ "AA CA 0139",
+ "AB CA 013A",
+ "AA CC 013B",
+ "AB CC 013C",
+ "AA CB 013D",
+ "AB CB 013E",
+ "A9 A9 013F",
+ "A9 C9 0140",
+ "A9 A8 0141",
+ "A9 C8 0142",
+ "AA CD 0143",
+ "AB CD 0144",
+ "AA CF 0145",
+ "AB CF 0146",
+ "AA CE 0147",
+ "AB CE 0148",
+ "A9 CA 0149",
+ "A9 AB 014A",
+ "A9 CB 014B",
+ "AA D7 014C",
+ "AB D7 014D",
+ "AA D6 0150",
+ "AB D6 0151",
+ "A9 AD 0152",
+ "A9 CD 0153",
+ "AA D9 0154",
+ "AB D9 0155",
+ "AA DB 0156",
+ "AB DB 0157",
+ "AA DA 0158",
+ "AB DA 0159",
+ "AA DC 015A",
+ "AB DC 015B",
+ "AA DD 015C",
+ "AB DD 015D",
+ "AA DF 015E",
+ "AB DF 015F",
+ "AA DE 0160",
+ "AB DE 0161",
+ "AA E1 0162",
+ "AB E1 0163",
+ "AA E0 0164",
+ "AB E0 0165",
+ "A9 AF 0166",
+ "A9 CF 0167",
+ "AA EC 0168",
+ "AB EC 0169",
+ "AA E9 016A",
+ "AB E9 016B",
+ "AA E6 016C",
+ "AB E6 016D",
+ "AA EB 016E",
+ "AB EB 016F",
+ "AA E8 0170",
+ "AB E8 0171",
+ "AA EA 0172",
+ "AB EA 0173",
+ "AA F1 0174",
+ "AB F1 0175",
+ "AA F4 0176",
+ "AB F4 0177",
+ "AA F3 0178",
+ "AA F5 0179",
+ "AB F5 017A",
+ "AA F7 017B",
+ "AB F7 017C",
+ "AA F6 017D",
+ "AB F6 017E",
+ "AA A6 01CD",
+ "AB A6 01CE",
+ "AA C3 01CF",
+ "AB C3 01D0",
+ "AA D5 01D1",
+ "AB D5 01D2",
+ "AA E7 01D3",
+ "AB E7 01D4",
+ "AA F0 01D5",
+ "AB F0 01D6",
+ "AA ED 01D7",
+ "AB ED 01D8",
+ "AA EF 01D9",
+ "AB EF 01DA",
+ "AA EE 01DB",
+ "AB EE 01DC",
+ "AB B9 01F5",
+ "A2 B0 02C7",
+ "A2 AF 02D8",
+ "A2 B2 02D9",
+ "A2 B6 02DA",
+ "A2 B5 02DB",
+ "A2 B3 02DD",
+ "A2 B8 0384",
+ "A2 B9 0385",
+ "A6 E1 0386",
+ "A6 E2 0388",
+ "A6 E3 0389",
+ "A6 E4 038A",
+ "A6 E7 038C",
+ "A6 E9 038E",
+ "A6 EC 038F",
+ "A6 F6 0390",
+ "A6 E5 03AA",
+ "A6 EA 03AB",
+ "A6 F1 03AC",
+ "A6 F2 03AD",
+ "A6 F3 03AE",
+ "A6 F4 03AF",
+ "A6 FB 03B0",
+ "A6 F8 03C2",
+ "A6 F5 03CA",
+ "A6 FA 03CB",
+ "A6 F7 03CC",
+ "A6 F9 03CD",
+ "A6 FC 03CE",
+ "A7 C2 0402",
+ "A7 C3 0403",
+ "A7 C4 0404",
+ "A7 C5 0405",
+ "A7 C6 0406",
+ "A7 C7 0407",
+ "A7 C8 0408",
+ "A7 C9 0409",
+ "A7 CA 040A",
+ "A7 CB 040B",
+ "A7 CC 040C",
+ "A7 CD 040E",
+ "A7 CE 040F",
+ "A7 F2 0452",
+ "A7 F3 0453",
+ "A7 F4 0454",
+ "A7 F5 0455",
+ "A7 F6 0456",
+ "A7 F7 0457",
+ "A7 F8 0458",
+ "A7 F9 0459",
+ "A7 FA 045A",
+ "A7 FB 045B",
+ "A7 FC 045C",
+ "A7 FD 045E",
+ "A7 FE 045F",
+ "A2 F1 2116",
+ "A2 EF 2122",
+ "B0 A1 4E02",
+ "B0 A2 4E04",
+ "B0 A3 4E05",
+ "B0 A4 4E0C",
+ "B0 A5 4E12",
+ "B0 A6 4E1F",
+ "B0 A7 4E23",
+ "B0 A8 4E24",
+ "B0 A9 4E28",
+ "B0 AA 4E2B",
+ "B0 AB 4E2E",
+ "B0 AC 4E2F",
+ "B0 AD 4E30",
+ "B0 AE 4E35",
+ "B0 AF 4E40",
+ "B0 B0 4E41",
+ "B0 B1 4E44",
+ "B0 B2 4E47",
+ "B0 B3 4E51",
+ "B0 B4 4E5A",
+ "B0 B5 4E5C",
+ "B0 B6 4E63",
+ "B0 B7 4E68",
+ "B0 B8 4E69",
+ "B0 B9 4E74",
+ "B0 BA 4E75",
+ "B0 BB 4E79",
+ "B0 BC 4E7F",
+ "B0 BD 4E8D",
+ "B0 BE 4E96",
+ "B0 BF 4E97",
+ "B0 C0 4E9D",
+ "B0 C1 4EAF",
+ "B0 C2 4EB9",
+ "B0 C3 4EC3",
+ "B0 C4 4ED0",
+ "B0 C5 4EDA",
+ "B0 C6 4EDB",
+ "B0 C7 4EE0",
+ "B0 C8 4EE1",
+ "B0 C9 4EE2",
+ "B0 CA 4EE8",
+ "B0 CB 4EEF",
+ "B0 CC 4EF1",
+ "B0 CD 4EF3",
+ "B0 CE 4EF5",
+ "B0 CF 4EFD",
+ "B0 D0 4EFE",
+ "B0 D1 4EFF",
+ "B0 D2 4F00",
+ "B0 D3 4F02",
+ "B0 D4 4F03",
+ "B0 D5 4F08",
+ "B0 D6 4F0B",
+ "B0 D7 4F0C",
+ "B0 D8 4F12",
+ "B0 D9 4F15",
+ "B0 DA 4F16",
+ "B0 DB 4F17",
+ "B0 DC 4F19",
+ "B0 DD 4F2E",
+ "B0 DE 4F31",
+ "B0 E0 4F33",
+ "B0 E1 4F35",
+ "B0 E2 4F37",
+ "B0 E3 4F39",
+ "B0 E4 4F3B",
+ "B0 E5 4F3E",
+ "B0 E6 4F40",
+ "B0 E7 4F42",
+ "B0 E8 4F48",
+ "B0 E9 4F49",
+ "B0 EA 4F4B",
+ "B0 EB 4F4C",
+ "B0 EC 4F52",
+ "B0 ED 4F54",
+ "B0 EE 4F56",
+ "B0 EF 4F58",
+ "B0 F0 4F5F",
+ "B0 DF 4F60",
+ "B0 F1 4F63",
+ "B0 F2 4F6A",
+ "B0 F3 4F6C",
+ "B0 F4 4F6E",
+ "B0 F5 4F71",
+ "B0 F6 4F77",
+ "B0 F7 4F78",
+ "B0 F8 4F79",
+ "B0 F9 4F7A",
+ "B0 FA 4F7D",
+ "B0 FB 4F7E",
+ "B0 FC 4F81",
+ "B0 FD 4F82",
+ "B0 FE 4F84",
+ "B1 A1 4F85",
+ "B1 A2 4F89",
+ "B1 A3 4F8A",
+ "B1 A4 4F8C",
+ "B1 A5 4F8E",
+ "B1 A6 4F90",
+ "B1 A7 4F92",
+ "B1 A8 4F93",
+ "B1 A9 4F94",
+ "B1 AA 4F97",
+ "B1 AB 4F99",
+ "B1 AC 4F9A",
+ "B1 AD 4F9E",
+ "B1 AE 4F9F",
+ "B1 AF 4FB2",
+ "B1 B0 4FB7",
+ "B1 B1 4FB9",
+ "B1 B2 4FBB",
+ "B1 B3 4FBC",
+ "B1 B4 4FBD",
+ "B1 B5 4FBE",
+ "B1 B6 4FC0",
+ "B1 B7 4FC1",
+ "B1 B8 4FC5",
+ "B1 B9 4FC6",
+ "B1 BA 4FC8",
+ "B1 BB 4FC9",
+ "B1 BC 4FCB",
+ "B1 BD 4FCC",
+ "B1 BE 4FCD",
+ "B1 BF 4FCF",
+ "B1 C0 4FD2",
+ "B1 C1 4FDC",
+ "B1 C2 4FE0",
+ "B1 C3 4FE2",
+ "B1 C4 4FF0",
+ "B1 C5 4FF2",
+ "B1 C6 4FFC",
+ "B1 C7 4FFD",
+ "B1 C8 4FFF",
+ "B1 C9 5000",
+ "B1 CA 5001",
+ "B1 CB 5004",
+ "B1 CC 5007",
+ "B1 CD 500A",
+ "B1 CE 500C",
+ "B1 CF 500E",
+ "B1 D0 5010",
+ "B1 D1 5013",
+ "B1 D2 5017",
+ "B1 D3 5018",
+ "B1 D4 501B",
+ "B1 D5 501C",
+ "B1 D6 501D",
+ "B1 D7 501E",
+ "B1 D8 5022",
+ "B1 D9 5027",
+ "B1 DA 502E",
+ "B1 DB 5030",
+ "B1 DC 5032",
+ "B1 DD 5033",
+ "B1 DE 5035",
+ "B1 F6 503B",
+ "B1 DF 5040",
+ "B1 E0 5041",
+ "B1 E1 5042",
+ "B1 E2 5045",
+ "B1 E3 5046",
+ "B1 E4 504A",
+ "B1 E5 504C",
+ "B1 E6 504E",
+ "B1 E7 5051",
+ "B1 E8 5052",
+ "B1 E9 5053",
+ "B1 EA 5057",
+ "B1 EB 5059",
+ "B1 EC 505F",
+ "B1 ED 5060",
+ "B1 EE 5062",
+ "B1 EF 5063",
+ "B1 F0 5066",
+ "B1 F1 5067",
+ "B1 F2 506A",
+ "B1 F3 506D",
+ "B1 F4 5070",
+ "B1 F5 5071",
+ "B1 F7 5081",
+ "B1 F8 5083",
+ "B1 F9 5084",
+ "B1 FA 5086",
+ "B1 FB 508A",
+ "B1 FC 508E",
+ "B1 FD 508F",
+ "B1 FE 5090",
+ "B2 A1 5092",
+ "B2 A2 5093",
+ "B2 A3 5094",
+ "B2 A4 5096",
+ "B2 A5 509B",
+ "B2 A6 509C",
+ "B2 A7 509E",
+ "B2 A8 509F",
+ "B2 A9 50A0",
+ "B2 AA 50A1",
+ "B2 AB 50A2",
+ "B2 AC 50AA",
+ "B2 AD 50AF",
+ "B2 AE 50B0",
+ "B2 AF 50B9",
+ "B2 B0 50BA",
+ "B2 B1 50BD",
+ "B2 B2 50C0",
+ "B2 B3 50C3",
+ "B2 B4 50C4",
+ "B2 B5 50C7",
+ "B2 B6 50CC",
+ "B2 B7 50CE",
+ "B2 B8 50D0",
+ "B2 B9 50D3",
+ "B2 BA 50D4",
+ "B2 BB 50D8",
+ "B2 BC 50DC",
+ "B2 BD 50DD",
+ "B2 BE 50DF",
+ "B2 BF 50E2",
+ "B2 C0 50E4",
+ "B2 C1 50E6",
+ "B2 C2 50E8",
+ "B2 C3 50E9",
+ "B2 C4 50EF",
+ "B2 C5 50F1",
+ "B2 D1 50F2",
+ "B2 C6 50F6",
+ "B2 C7 50FA",
+ "B2 C8 50FE",
+ "B2 C9 5103",
+ "B2 CA 5106",
+ "B2 CB 5107",
+ "B2 CC 5108",
+ "B2 CD 510B",
+ "B2 CE 510C",
+ "B2 CF 510D",
+ "B2 D0 510E",
+ "B2 D2 5110",
+ "B2 D3 5117",
+ "B2 D4 5119",
+ "B2 D5 511B",
+ "B2 D6 511C",
+ "B2 D7 511D",
+ "B2 D8 511E",
+ "B2 D9 5123",
+ "B2 DA 5127",
+ "B2 DB 5128",
+ "B2 DC 512C",
+ "B2 DD 512D",
+ "B2 DE 512F",
+ "B2 DF 5131",
+ "B2 E0 5133",
+ "B2 E1 5134",
+ "B2 E2 5135",
+ "B2 E3 5138",
+ "B2 E4 5139",
+ "B2 E5 5142",
+ "B2 E6 514A",
+ "B2 E7 514F",
+ "B2 E8 5153",
+ "B2 E9 5155",
+ "B2 EA 5157",
+ "B2 EB 5158",
+ "B2 EC 515F",
+ "B2 ED 5164",
+ "B2 EE 5166",
+ "B2 EF 517E",
+ "B2 F0 5183",
+ "B2 F1 5184",
+ "B2 F2 518B",
+ "B2 F3 518E",
+ "B2 F4 5198",
+ "B2 F5 519D",
+ "B2 F6 51A1",
+ "B2 F7 51A3",
+ "B2 F8 51AD",
+ "B2 F9 51B8",
+ "B2 FA 51BA",
+ "B2 FB 51BC",
+ "B2 FC 51BE",
+ "B2 FD 51BF",
+ "B2 FE 51C2",
+ "B3 A1 51C8",
+ "B3 A2 51CF",
+ "B3 A3 51D1",
+ "B3 A4 51D2",
+ "B3 A5 51D3",
+ "B3 A6 51D5",
+ "B3 A7 51D8",
+ "B3 A8 51DE",
+ "B3 A9 51E2",
+ "B3 AA 51E5",
+ "B3 AB 51EE",
+ "B3 AC 51F2",
+ "B3 AD 51F3",
+ "B3 AE 51F4",
+ "B3 AF 51F7",
+ "B3 B0 5201",
+ "B3 B1 5202",
+ "B3 B2 5205",
+ "B3 B3 5212",
+ "B3 B4 5213",
+ "B3 B5 5215",
+ "B3 B6 5216",
+ "B3 B7 5218",
+ "B3 B8 5222",
+ "B3 B9 5228",
+ "B3 BA 5231",
+ "B3 BB 5232",
+ "B3 BC 5235",
+ "B3 BD 523C",
+ "B3 BE 5245",
+ "B3 BF 5249",
+ "B3 C0 5255",
+ "B3 C1 5257",
+ "B3 C2 5258",
+ "B3 C3 525A",
+ "B3 C4 525C",
+ "B3 C5 525F",
+ "B3 C6 5260",
+ "B3 C7 5261",
+ "B3 C8 5266",
+ "B3 C9 526E",
+ "B3 CA 5277",
+ "B3 CB 5278",
+ "B3 CC 5279",
+ "B3 CD 5280",
+ "B3 CE 5282",
+ "B3 CF 5285",
+ "B3 D0 528A",
+ "B3 D1 528C",
+ "B3 D2 5293",
+ "B3 D3 5295",
+ "B3 D4 5296",
+ "B3 D5 5297",
+ "B3 D6 5298",
+ "B3 D7 529A",
+ "B3 D8 529C",
+ "B3 D9 52A4",
+ "B3 DA 52A5",
+ "B3 DB 52A6",
+ "B3 DC 52A7",
+ "B3 DD 52AF",
+ "B3 DE 52B0",
+ "B3 DF 52B6",
+ "B3 E0 52B7",
+ "B3 E1 52B8",
+ "B3 E2 52BA",
+ "B3 E3 52BB",
+ "B3 E4 52BD",
+ "B3 E5 52C0",
+ "B3 E6 52C4",
+ "B3 E7 52C6",
+ "B3 E8 52C8",
+ "B3 E9 52CC",
+ "B3 EA 52CF",
+ "B3 EB 52D1",
+ "B3 EC 52D4",
+ "B3 ED 52D6",
+ "B3 EE 52DB",
+ "B3 EF 52DC",
+ "B3 F0 52E1",
+ "B3 F1 52E5",
+ "B3 F2 52E8",
+ "B3 F3 52E9",
+ "B3 F4 52EA",
+ "B3 F5 52EC",
+ "B3 F6 52F0",
+ "B3 F7 52F1",
+ "B3 F8 52F4",
+ "B3 F9 52F6",
+ "B3 FA 52F7",
+ "B3 FB 5300",
+ "B3 FC 5303",
+ "B3 FD 530A",
+ "B3 FE 530B",
+ "B4 A1 530C",
+ "B4 A2 5311",
+ "B4 A3 5313",
+ "B4 A4 5318",
+ "B4 A5 531B",
+ "B4 A6 531C",
+ "B4 A7 531E",
+ "B4 A8 531F",
+ "B4 A9 5325",
+ "B4 AA 5327",
+ "B4 AB 5328",
+ "B4 AC 5329",
+ "B4 AD 532B",
+ "B4 AE 532C",
+ "B4 AF 532D",
+ "B4 B0 5330",
+ "B4 B1 5332",
+ "B4 B2 5335",
+ "B4 B3 533C",
+ "B4 B4 533D",
+ "B4 B5 533E",
+ "B4 B6 5342",
+ "B4 B8 534B",
+ "B4 B7 534C",
+ "B4 B9 5359",
+ "B4 BA 535B",
+ "B4 BB 5361",
+ "B4 BC 5363",
+ "B4 BD 5365",
+ "B4 BE 536C",
+ "B4 BF 536D",
+ "B4 C0 5372",
+ "B4 C1 5379",
+ "B4 C2 537E",
+ "B4 C3 5383",
+ "B4 C4 5387",
+ "B4 C5 5388",
+ "B4 C6 538E",
+ "B4 C7 5393",
+ "B4 C8 5394",
+ "B4 C9 5399",
+ "B4 CA 539D",
+ "B4 CB 53A1",
+ "B4 CC 53A4",
+ "B4 CD 53AA",
+ "B4 CE 53AB",
+ "B4 CF 53AF",
+ "B4 D0 53B2",
+ "B4 D1 53B4",
+ "B4 D2 53B5",
+ "B4 D3 53B7",
+ "B4 D4 53B8",
+ "B4 D5 53BA",
+ "B4 D6 53BD",
+ "B4 D7 53C0",
+ "B4 D8 53C5",
+ "B4 D9 53CF",
+ "B4 DA 53D2",
+ "B4 DB 53D3",
+ "B4 DC 53D5",
+ "B4 DD 53DA",
+ "B4 DE 53DD",
+ "B4 DF 53DE",
+ "B4 E0 53E0",
+ "B4 E1 53E6",
+ "B4 E2 53E7",
+ "B4 E3 53F5",
+ "B4 E4 5402",
+ "B4 E5 5413",
+ "B4 E6 541A",
+ "B4 E7 5421",
+ "B4 E8 5427",
+ "B4 E9 5428",
+ "B4 EA 542A",
+ "B4 EB 542F",
+ "B4 EC 5431",
+ "B4 ED 5434",
+ "B4 EE 5435",
+ "B4 EF 5443",
+ "B4 F0 5444",
+ "B4 F1 5447",
+ "B4 F2 544D",
+ "B4 F3 544F",
+ "B4 F4 545E",
+ "B4 F5 5462",
+ "B4 F6 5464",
+ "B4 F7 5466",
+ "B4 F8 5467",
+ "B4 F9 5469",
+ "B4 FA 546B",
+ "B4 FB 546D",
+ "B4 FC 546E",
+ "B4 FD 5474",
+ "B4 FE 547F",
+ "B5 A1 5481",
+ "B5 A2 5483",
+ "B5 A3 5485",
+ "B5 A4 5488",
+ "B5 A5 5489",
+ "B5 A6 548D",
+ "B5 A7 5491",
+ "B5 A8 5495",
+ "B5 A9 5496",
+ "B5 AA 549C",
+ "B5 AB 549F",
+ "B5 AC 54A1",
+ "B5 AD 54A6",
+ "B5 AE 54A7",
+ "B5 AF 54A9",
+ "B5 B0 54AA",
+ "B5 B1 54AD",
+ "B5 B2 54AE",
+ "B5 B3 54B1",
+ "B5 B4 54B7",
+ "B5 B5 54B9",
+ "B5 B6 54BA",
+ "B5 B7 54BB",
+ "B5 B8 54BF",
+ "B5 B9 54C6",
+ "B5 BA 54CA",
+ "B5 BB 54CD",
+ "B5 BC 54CE",
+ "B5 BD 54E0",
+ "B5 BE 54EA",
+ "B5 BF 54EC",
+ "B5 C0 54EF",
+ "B5 C1 54F6",
+ "B5 C2 54FC",
+ "B5 C3 54FE",
+ "B5 C4 54FF",
+ "B5 C5 5500",
+ "B5 C6 5501",
+ "B5 C7 5505",
+ "B5 C8 5508",
+ "B5 C9 5509",
+ "B5 CA 550C",
+ "B5 CB 550D",
+ "B5 CC 550E",
+ "B5 CD 5515",
+ "B5 CE 552A",
+ "B5 CF 552B",
+ "B5 D0 5532",
+ "B5 D1 5535",
+ "B5 D2 5536",
+ "B5 D3 553B",
+ "B5 D4 553C",
+ "B5 D5 553D",
+ "B5 D6 5541",
+ "B5 D7 5547",
+ "B5 D8 5549",
+ "B5 D9 554A",
+ "B5 DA 554D",
+ "B5 DB 5550",
+ "B5 DC 5551",
+ "B5 DD 5558",
+ "B5 DE 555A",
+ "B5 DF 555B",
+ "B5 E0 555E",
+ "B5 E1 5560",
+ "B5 E2 5561",
+ "B5 E3 5564",
+ "B5 E4 5566",
+ "B5 E5 557F",
+ "B5 E6 5581",
+ "B5 E7 5582",
+ "B5 E8 5586",
+ "B5 E9 5588",
+ "B5 EA 558E",
+ "B5 EB 558F",
+ "B5 EC 5591",
+ "B5 ED 5592",
+ "B5 EE 5593",
+ "B5 EF 5594",
+ "B5 F0 5597",
+ "B5 F1 55A3",
+ "B5 F2 55A4",
+ "B5 F3 55AD",
+ "B5 F4 55B2",
+ "B5 F5 55BF",
+ "B5 F6 55C1",
+ "B5 F7 55C3",
+ "B5 F8 55C6",
+ "B5 F9 55C9",
+ "B5 FA 55CB",
+ "B5 FB 55CC",
+ "B5 FC 55CE",
+ "B5 FD 55D1",
+ "B5 FE 55D2",
+ "B6 A1 55D3",
+ "B6 A2 55D7",
+ "B6 A3 55D8",
+ "B6 A4 55DB",
+ "B6 A5 55DE",
+ "B6 A6 55E2",
+ "B6 A7 55E9",
+ "B6 A8 55F6",
+ "B6 A9 55FF",
+ "B6 AA 5605",
+ "B6 AB 5608",
+ "B6 AC 560A",
+ "B6 AD 560D",
+ "B6 AE 560E",
+ "B6 AF 560F",
+ "B6 B0 5610",
+ "B6 B1 5611",
+ "B6 B2 5612",
+ "B6 B3 5619",
+ "B6 B4 562C",
+ "B6 B5 5630",
+ "B6 B6 5633",
+ "B6 B7 5635",
+ "B6 B8 5637",
+ "B6 B9 5639",
+ "B6 BA 563B",
+ "B6 BB 563C",
+ "B6 BC 563D",
+ "B6 BD 563F",
+ "B6 BE 5640",
+ "B6 BF 5641",
+ "B6 C0 5643",
+ "B6 C1 5644",
+ "B6 C2 5646",
+ "B6 C3 5649",
+ "B6 C4 564B",
+ "B6 C5 564D",
+ "B6 C6 564F",
+ "B6 C7 5654",
+ "B6 C8 565E",
+ "B6 C9 5660",
+ "B6 CA 5661",
+ "B6 CB 5662",
+ "B6 CC 5663",
+ "B6 CD 5666",
+ "B6 CE 5669",
+ "B6 CF 566D",
+ "B6 D0 566F",
+ "B6 D1 5671",
+ "B6 D2 5672",
+ "B6 D3 5675",
+ "B6 D4 5684",
+ "B6 D5 5685",
+ "B6 D6 5688",
+ "B6 D7 568B",
+ "B6 D8 568C",
+ "B6 D9 5695",
+ "B6 DA 5699",
+ "B6 DB 569A",
+ "B6 DC 569D",
+ "B6 DD 569E",
+ "B6 DE 569F",
+ "B6 DF 56A6",
+ "B6 E0 56A7",
+ "B6 E1 56A8",
+ "B6 E2 56A9",
+ "B6 E3 56AB",
+ "B6 E4 56AC",
+ "B6 E5 56AD",
+ "B6 E6 56B1",
+ "B6 E7 56B3",
+ "B6 E8 56B7",
+ "B6 E9 56BE",
+ "B6 EA 56C5",
+ "B6 EB 56C9",
+ "B6 EC 56CA",
+ "B6 ED 56CB",
+ "B6 F0 56CC",
+ "B6 F1 56CD",
+ "B6 EE 56CF",
+ "B6 EF 56D0",
+ "B6 F2 56D9",
+ "B6 F3 56DC",
+ "B6 F4 56DD",
+ "B6 F5 56DF",
+ "B6 F6 56E1",
+ "B6 F7 56E4",
+ "B6 F8 56E5",
+ "B6 F9 56E6",
+ "B6 FA 56E7",
+ "B6 FB 56E8",
+ "B6 FD 56EB",
+ "B6 FE 56ED",
+ "B6 FC 56F1",
+ "B7 A1 56F6",
+ "B7 A2 56F7",
+ "B7 A3 5701",
+ "B7 A4 5702",
+ "B7 A5 5707",
+ "B7 A6 570A",
+ "B7 A7 570C",
+ "B7 A8 5711",
+ "B7 A9 5715",
+ "B7 AA 571A",
+ "B7 AB 571B",
+ "B7 AC 571D",
+ "B7 AD 5720",
+ "B7 AE 5722",
+ "B7 AF 5723",
+ "B7 B0 5724",
+ "B7 B1 5725",
+ "B7 B2 5729",
+ "B7 B3 572A",
+ "B7 B4 572C",
+ "B7 B5 572E",
+ "B7 B6 572F",
+ "B7 B7 5733",
+ "B7 B8 5734",
+ "B7 B9 573D",
+ "B7 BA 573E",
+ "B7 BB 573F",
+ "B7 BC 5745",
+ "B7 BD 5746",
+ "B7 BE 574C",
+ "B7 BF 574D",
+ "B7 C0 5752",
+ "B7 C1 5762",
+ "B7 C2 5765",
+ "B7 C3 5767",
+ "B7 C4 5768",
+ "B7 C5 576B",
+ "B7 C6 576D",
+ "B7 C7 576E",
+ "B7 C8 576F",
+ "B7 C9 5770",
+ "B7 CA 5771",
+ "B7 CB 5773",
+ "B7 CC 5774",
+ "B7 CD 5775",
+ "B7 CE 5777",
+ "B7 CF 5779",
+ "B7 D0 577A",
+ "B7 D1 577B",
+ "B7 D2 577C",
+ "B7 D3 577E",
+ "B7 D4 5781",
+ "B7 D5 5783",
+ "B7 D6 578C",
+ "B7 D7 5794",
+ "B7 E0 5795",
+ "B7 D8 5797",
+ "B7 D9 5799",
+ "B7 DA 579A",
+ "B7 DB 579C",
+ "B7 DC 579D",
+ "B7 DD 579E",
+ "B7 DE 579F",
+ "B7 DF 57A1",
+ "B7 E1 57A7",
+ "B7 E2 57A8",
+ "B7 E3 57A9",
+ "B7 E4 57AC",
+ "B7 E5 57B8",
+ "B7 E6 57BD",
+ "B7 E7 57C7",
+ "B7 E8 57C8",
+ "B7 E9 57CC",
+ "B7 EA 57CF",
+ "B7 EB 57D5",
+ "B7 EC 57DD",
+ "B7 ED 57DE",
+ "B7 FE 57E1",
+ "B7 EE 57E4",
+ "B7 EF 57E6",
+ "B7 F0 57E7",
+ "B7 F1 57E9",
+ "B7 F2 57ED",
+ "B7 F3 57F0",
+ "B7 F4 57F5",
+ "B7 F5 57F6",
+ "B7 F6 57F8",
+ "B7 F7 57FD",
+ "B7 F8 57FE",
+ "B7 F9 57FF",
+ "B7 FA 5803",
+ "B7 FB 5804",
+ "B7 FC 5808",
+ "B7 FD 5809",
+ "B8 A1 580C",
+ "B8 A2 580D",
+ "B8 A3 581B",
+ "B8 A4 581E",
+ "B8 A5 581F",
+ "B8 A6 5820",
+ "B8 A7 5826",
+ "B8 A8 5827",
+ "B8 A9 582D",
+ "B8 AA 5832",
+ "B8 AB 5839",
+ "B8 AC 583F",
+ "B8 AD 5849",
+ "B8 AE 584C",
+ "B8 AF 584D",
+ "B8 B0 584F",
+ "B8 B1 5850",
+ "B8 B2 5855",
+ "B8 B3 585F",
+ "B8 B4 5861",
+ "B8 B5 5864",
+ "B8 B6 5867",
+ "B8 B7 5868",
+ "B8 B8 5878",
+ "B8 B9 587C",
+ "B8 BA 587F",
+ "B8 BB 5880",
+ "B8 BC 5881",
+ "B8 BD 5887",
+ "B8 BE 5888",
+ "B8 BF 5889",
+ "B8 C0 588A",
+ "B8 C1 588C",
+ "B8 C2 588D",
+ "B8 C3 588F",
+ "B8 C4 5890",
+ "B8 C5 5894",
+ "B8 C6 5896",
+ "B8 C7 589D",
+ "B8 C8 58A0",
+ "B8 C9 58A1",
+ "B8 CA 58A2",
+ "B8 CB 58A6",
+ "B8 CC 58A9",
+ "B8 CD 58B1",
+ "B8 CE 58B2",
+ "B8 D0 58BC",
+ "B8 D1 58C2",
+ "B8 CF 58C4",
+ "B8 D2 58C8",
+ "B8 D3 58CD",
+ "B8 D4 58CE",
+ "B8 D5 58D0",
+ "B8 D6 58D2",
+ "B8 D7 58D4",
+ "B8 D8 58D6",
+ "B8 D9 58DA",
+ "B8 DA 58DD",
+ "B8 DB 58E1",
+ "B8 DC 58E2",
+ "B8 DD 58E9",
+ "B8 DE 58F3",
+ "B8 DF 5905",
+ "B8 E0 5906",
+ "B8 E1 590B",
+ "B8 E2 590C",
+ "B8 E3 5912",
+ "B8 E4 5913",
+ "B8 E5 5914",
+ "B8 E7 591D",
+ "B8 E8 5921",
+ "B8 E9 5923",
+ "B8 EA 5924",
+ "B8 EB 5928",
+ "B8 EC 592F",
+ "B8 ED 5930",
+ "B8 EE 5933",
+ "B8 EF 5935",
+ "B8 F0 5936",
+ "B8 F1 593F",
+ "B8 F2 5943",
+ "B8 F3 5946",
+ "B8 F4 5952",
+ "B8 F5 5953",
+ "B8 F6 5959",
+ "B8 F7 595B",
+ "B8 F8 595D",
+ "B8 F9 595E",
+ "B8 FA 595F",
+ "B8 FB 5961",
+ "B8 FC 5963",
+ "B8 FD 596B",
+ "B8 FE 596D",
+ "B9 A1 596F",
+ "B9 A2 5972",
+ "B9 A3 5975",
+ "B9 A4 5976",
+ "B9 A5 5979",
+ "B9 A6 597B",
+ "B9 A7 597C",
+ "B9 A8 598B",
+ "B9 A9 598C",
+ "B9 AA 598E",
+ "B9 AB 5992",
+ "B9 AC 5995",
+ "B9 AD 5997",
+ "B9 AE 599F",
+ "B9 AF 59A4",
+ "B9 B0 59A7",
+ "B9 B1 59AD",
+ "B9 B2 59AE",
+ "B9 B3 59AF",
+ "B9 B4 59B0",
+ "B9 B5 59B3",
+ "B9 B6 59B7",
+ "B9 B7 59BA",
+ "B9 B8 59BC",
+ "B9 B9 59C1",
+ "B9 BA 59C3",
+ "B9 BB 59C4",
+ "B9 BC 59C8",
+ "B9 BD 59CA",
+ "B9 BE 59CD",
+ "B9 BF 59D2",
+ "B9 C0 59DD",
+ "B9 C1 59DE",
+ "B9 C2 59DF",
+ "B9 C3 59E3",
+ "B9 C4 59E4",
+ "B9 C5 59E7",
+ "B9 C6 59EE",
+ "B9 C7 59EF",
+ "B9 C8 59F1",
+ "B9 C9 59F2",
+ "B9 CA 59F4",
+ "B9 CB 59F7",
+ "B9 CC 5A00",
+ "B9 CD 5A04",
+ "B9 CE 5A0C",
+ "B9 CF 5A0D",
+ "B9 D0 5A0E",
+ "B9 D1 5A12",
+ "B9 D2 5A13",
+ "B9 D3 5A1E",
+ "B9 D4 5A23",
+ "B9 D5 5A24",
+ "B9 D6 5A27",
+ "B9 D7 5A28",
+ "B9 D8 5A2A",
+ "B9 D9 5A2D",
+ "B9 DA 5A30",
+ "B9 DB 5A44",
+ "B9 DC 5A45",
+ "B9 DD 5A47",
+ "B9 DE 5A48",
+ "B9 DF 5A4C",
+ "B9 E0 5A50",
+ "B9 E1 5A55",
+ "B9 E2 5A5E",
+ "B9 E3 5A63",
+ "B9 E4 5A65",
+ "B9 E5 5A67",
+ "B9 E6 5A6D",
+ "B9 E7 5A77",
+ "B9 E8 5A7A",
+ "B9 E9 5A7B",
+ "B9 EA 5A7E",
+ "B9 EB 5A8B",
+ "B9 EC 5A90",
+ "B9 ED 5A93",
+ "B9 EE 5A96",
+ "B9 EF 5A99",
+ "B9 F0 5A9C",
+ "B9 F1 5A9E",
+ "B9 F2 5A9F",
+ "B9 F3 5AA0",
+ "B9 F4 5AA2",
+ "B9 F5 5AA7",
+ "B9 F6 5AAC",
+ "B9 F7 5AB1",
+ "B9 F8 5AB2",
+ "B9 F9 5AB3",
+ "B9 FA 5AB5",
+ "B9 FB 5AB8",
+ "B9 FC 5ABA",
+ "B9 FD 5ABB",
+ "B9 FE 5ABF",
+ "BA A1 5AC4",
+ "BA A2 5AC6",
+ "BA A3 5AC8",
+ "BA A4 5ACF",
+ "BA A5 5ADA",
+ "BA A6 5ADC",
+ "BA A7 5AE0",
+ "BA A8 5AE5",
+ "BA A9 5AEA",
+ "BA AA 5AEE",
+ "BA AB 5AF5",
+ "BA AC 5AF6",
+ "BA AD 5AFD",
+ "BA AE 5B00",
+ "BA AF 5B01",
+ "BA B0 5B08",
+ "BA B1 5B17",
+ "BA B3 5B19",
+ "BA B4 5B1B",
+ "BA B5 5B1D",
+ "BA B6 5B21",
+ "BA B7 5B25",
+ "BA B8 5B2D",
+ "BA B2 5B34",
+ "BA B9 5B38",
+ "BA BA 5B41",
+ "BA BB 5B4B",
+ "BA BC 5B4C",
+ "BA BD 5B52",
+ "BA BE 5B56",
+ "BA BF 5B5E",
+ "BA C0 5B68",
+ "BA C1 5B6E",
+ "BA C2 5B6F",
+ "BA C3 5B7C",
+ "BA C4 5B7D",
+ "BA C5 5B7E",
+ "BA C6 5B7F",
+ "BA C7 5B81",
+ "BA C8 5B84",
+ "BA C9 5B86",
+ "BA CA 5B8A",
+ "BA CB 5B8E",
+ "BA CC 5B90",
+ "BA CD 5B91",
+ "BA CE 5B93",
+ "BA CF 5B94",
+ "BA D0 5B96",
+ "BA D1 5BA8",
+ "BA D2 5BA9",
+ "BA D3 5BAC",
+ "BA D4 5BAD",
+ "BA D5 5BAF",
+ "BA D6 5BB1",
+ "BA D7 5BB2",
+ "BA D8 5BB7",
+ "BA D9 5BBA",
+ "BA DA 5BBC",
+ "BA DB 5BC0",
+ "BA DC 5BC1",
+ "BA DD 5BCD",
+ "BA DE 5BCF",
+ "BA DF 5BD6",
+ "BA E0 5BD7",
+ "BA E1 5BD8",
+ "BA E2 5BD9",
+ "BA E3 5BDA",
+ "BA E4 5BE0",
+ "BA E5 5BEF",
+ "BA E6 5BF1",
+ "BA E7 5BF4",
+ "BA E8 5BFD",
+ "BA E9 5C0C",
+ "BA EA 5C17",
+ "BA EB 5C1E",
+ "BA EC 5C1F",
+ "BA ED 5C23",
+ "BA EE 5C26",
+ "BA EF 5C29",
+ "BA F0 5C2B",
+ "BA F1 5C2C",
+ "BA F2 5C2E",
+ "BA F3 5C30",
+ "BA F4 5C32",
+ "BA F5 5C35",
+ "BA F6 5C36",
+ "BA F7 5C59",
+ "BA F8 5C5A",
+ "BA F9 5C5C",
+ "BA FA 5C62",
+ "BA FB 5C63",
+ "BA FC 5C67",
+ "BA FD 5C68",
+ "BA FE 5C69",
+ "BB A1 5C6D",
+ "BB A2 5C70",
+ "BB A3 5C74",
+ "BB A4 5C75",
+ "BB A5 5C7A",
+ "BB A6 5C7B",
+ "BB A7 5C7C",
+ "BB A8 5C7D",
+ "BB A9 5C87",
+ "BB AA 5C88",
+ "BB AB 5C8A",
+ "BB AC 5C8F",
+ "BB AD 5C92",
+ "BB AE 5C9D",
+ "BB AF 5C9F",
+ "BB B0 5CA0",
+ "BB B1 5CA2",
+ "BB B2 5CA3",
+ "BB B3 5CA6",
+ "BB B4 5CAA",
+ "BB B5 5CB2",
+ "BB B6 5CB4",
+ "BB B7 5CB5",
+ "BB B8 5CBA",
+ "BB B9 5CC9",
+ "BB BA 5CCB",
+ "BB BB 5CD2",
+ "BB BD 5CD7",
+ "BB BC 5CDD",
+ "BB BE 5CEE",
+ "BB BF 5CF1",
+ "BB C0 5CF2",
+ "BB C1 5CF4",
+ "BB C2 5D01",
+ "BB C3 5D06",
+ "BB C4 5D0D",
+ "BB C5 5D12",
+ "BB C7 5D23",
+ "BB C8 5D24",
+ "BB C9 5D26",
+ "BB CA 5D27",
+ "BB C6 5D2B",
+ "BB CB 5D31",
+ "BB CC 5D34",
+ "BB CD 5D39",
+ "BB CE 5D3D",
+ "BB CF 5D3F",
+ "BB D0 5D42",
+ "BB D1 5D43",
+ "BB D2 5D46",
+ "BB D3 5D48",
+ "BB D7 5D4A",
+ "BB D5 5D51",
+ "BB D4 5D55",
+ "BB D6 5D59",
+ "BB D8 5D5F",
+ "BB D9 5D60",
+ "BB DA 5D61",
+ "BB DB 5D62",
+ "BB DC 5D64",
+ "BB DD 5D6A",
+ "BB DE 5D6D",
+ "BB DF 5D70",
+ "BB E0 5D79",
+ "BB E1 5D7A",
+ "BB E2 5D7E",
+ "BB E3 5D7F",
+ "BB E4 5D81",
+ "BB E5 5D83",
+ "BB E6 5D88",
+ "BB E7 5D8A",
+ "BB E8 5D92",
+ "BB E9 5D93",
+ "BB EA 5D94",
+ "BB EB 5D95",
+ "BB EC 5D99",
+ "BB ED 5D9B",
+ "BB EE 5D9F",
+ "BB EF 5DA0",
+ "BB F0 5DA7",
+ "BB F1 5DAB",
+ "BB F2 5DB0",
+ "E6 F4 5DB2",
+ "BB F3 5DB4",
+ "BB F4 5DB8",
+ "BB F5 5DB9",
+ "BB F6 5DC3",
+ "BB F7 5DC7",
+ "BB F8 5DCB",
+ "BB FA 5DCE",
+ "BB F9 5DD0",
+ "BB FB 5DD8",
+ "BB FC 5DD9",
+ "BB FD 5DE0",
+ "BB FE 5DE4",
+ "BC A1 5DE9",
+ "BC A2 5DF8",
+ "BC A3 5DF9",
+ "BC A4 5E00",
+ "BC A5 5E07",
+ "BC A6 5E0D",
+ "BC A7 5E12",
+ "BC A8 5E14",
+ "BC A9 5E15",
+ "BC AA 5E18",
+ "BC AB 5E1F",
+ "BC AC 5E20",
+ "BC AE 5E28",
+ "BC AD 5E2E",
+ "BC AF 5E32",
+ "BC B0 5E35",
+ "BC B1 5E3E",
+ "BC B4 5E49",
+ "BC B2 5E4B",
+ "BC B3 5E50",
+ "BC B5 5E51",
+ "BC B6 5E56",
+ "BC B7 5E58",
+ "BC B8 5E5B",
+ "BC B9 5E5C",
+ "BC BA 5E5E",
+ "BC BB 5E68",
+ "BC BC 5E6A",
+ "BC BD 5E6B",
+ "BC BE 5E6C",
+ "BC BF 5E6D",
+ "BC C0 5E6E",
+ "BC C1 5E70",
+ "BC C2 5E80",
+ "BC C3 5E8B",
+ "BC C4 5E8E",
+ "BC C5 5EA2",
+ "BC C6 5EA4",
+ "BC C7 5EA5",
+ "BC C8 5EA8",
+ "BC C9 5EAA",
+ "BC CA 5EAC",
+ "BC CB 5EB1",
+ "BC CC 5EB3",
+ "BC CD 5EBD",
+ "BC CE 5EBE",
+ "BC CF 5EBF",
+ "BC D0 5EC6",
+ "BC D2 5ECB",
+ "BC D1 5ECC",
+ "BC D3 5ECE",
+ "BC D4 5ED1",
+ "BC D5 5ED2",
+ "BC D6 5ED4",
+ "BC D7 5ED5",
+ "BC D8 5EDC",
+ "BC D9 5EDE",
+ "BC DA 5EE5",
+ "BC DB 5EEB",
+ "BC DC 5F02",
+ "BC DD 5F06",
+ "BC DE 5F07",
+ "BC DF 5F08",
+ "BC E0 5F0E",
+ "BC E1 5F19",
+ "BC E2 5F1C",
+ "BC E3 5F1D",
+ "BC E4 5F21",
+ "BC E5 5F22",
+ "BC E6 5F23",
+ "BC E7 5F24",
+ "BC E8 5F28",
+ "BC E9 5F2B",
+ "BC EA 5F2C",
+ "BC EB 5F2E",
+ "BC EC 5F30",
+ "BC ED 5F34",
+ "BC EE 5F36",
+ "BC EF 5F3B",
+ "BC F0 5F3D",
+ "BC F1 5F3F",
+ "BC F2 5F40",
+ "BC F3 5F44",
+ "BC F4 5F45",
+ "BC F5 5F47",
+ "BC F6 5F4D",
+ "BC F7 5F50",
+ "BC F8 5F54",
+ "BC F9 5F58",
+ "BC FA 5F5B",
+ "BC FB 5F60",
+ "BC FC 5F63",
+ "BC FD 5F64",
+ "BC FE 5F67",
+ "BD A1 5F6F",
+ "BD A2 5F72",
+ "BD A3 5F74",
+ "BD A4 5F75",
+ "BD A5 5F78",
+ "BD A6 5F7A",
+ "BD A7 5F7D",
+ "BD A8 5F7E",
+ "BD A9 5F89",
+ "BD AA 5F8D",
+ "BD AB 5F8F",
+ "BD AC 5F96",
+ "BD AD 5F9C",
+ "BD AE 5F9D",
+ "BD AF 5FA2",
+ "BD B2 5FA4",
+ "BD B0 5FA7",
+ "BD B1 5FAB",
+ "BD B3 5FAC",
+ "BD B4 5FAF",
+ "BD B5 5FB0",
+ "BD B6 5FB1",
+ "BD B7 5FB8",
+ "BD B8 5FC4",
+ "BD B9 5FC7",
+ "BD BA 5FC8",
+ "BD BB 5FC9",
+ "BD BC 5FCB",
+ "BD BD 5FD0",
+ "BD BE 5FD1",
+ "BD BF 5FD2",
+ "BD C0 5FD3",
+ "BD C1 5FD4",
+ "BD C2 5FDE",
+ "BD C3 5FE1",
+ "BD C4 5FE2",
+ "BD C5 5FE8",
+ "BD C6 5FE9",
+ "BD C7 5FEA",
+ "BD C8 5FEC",
+ "BD C9 5FED",
+ "BD CA 5FEE",
+ "BD CB 5FEF",
+ "BD CC 5FF2",
+ "BD CD 5FF3",
+ "BD CE 5FF6",
+ "BD CF 5FFA",
+ "BD D0 5FFC",
+ "BD D1 6007",
+ "BD D2 600A",
+ "BD D3 600D",
+ "BD D4 6013",
+ "BD D5 6014",
+ "BD D6 6017",
+ "BD D7 6018",
+ "BD D8 601A",
+ "BD D9 601F",
+ "BD DA 6024",
+ "BD DB 602D",
+ "BD DC 6033",
+ "BD DD 6035",
+ "BD DE 6040",
+ "BD DF 6047",
+ "BD E0 6048",
+ "BD E1 6049",
+ "BD E2 604C",
+ "BD E3 6051",
+ "BD E4 6054",
+ "BD E5 6056",
+ "BD E6 6057",
+ "BD E7 605D",
+ "BD E8 6061",
+ "BD E9 6067",
+ "BD EA 6071",
+ "BD EB 607E",
+ "BD EC 607F",
+ "BD ED 6082",
+ "BD EE 6086",
+ "BD EF 6088",
+ "BD F0 608A",
+ "BD F1 608E",
+ "BD F2 6091",
+ "BD F3 6093",
+ "BD F4 6095",
+ "BD F5 6098",
+ "BD F6 609D",
+ "BD F7 609E",
+ "BD F8 60A2",
+ "BD F9 60A4",
+ "BD FA 60A5",
+ "BD FB 60A8",
+ "BD FC 60B0",
+ "BD FD 60B1",
+ "BD FE 60B7",
+ "BE A1 60BB",
+ "BE A2 60BE",
+ "BE A3 60C2",
+ "BE A4 60C4",
+ "BE A5 60C8",
+ "BE A6 60C9",
+ "BE A7 60CA",
+ "BE A8 60CB",
+ "BE A9 60CE",
+ "BE AA 60CF",
+ "BE AB 60D4",
+ "BE AC 60D5",
+ "BE AD 60D9",
+ "BE AE 60DB",
+ "BE AF 60DD",
+ "BE B0 60DE",
+ "BE B1 60E2",
+ "BE B2 60E5",
+ "BE B3 60F2",
+ "BE B4 60F5",
+ "BE B5 60F8",
+ "BE B6 60FC",
+ "BE B7 60FD",
+ "BE B8 6102",
+ "BE B9 6107",
+ "BE BA 610A",
+ "BE BB 610C",
+ "BE BC 6110",
+ "BE BD 6111",
+ "BE BE 6112",
+ "BE BF 6113",
+ "BE C0 6114",
+ "BE C1 6116",
+ "BE C2 6117",
+ "BE C3 6119",
+ "BE C4 611C",
+ "BE C5 611E",
+ "BE C6 6122",
+ "BE C7 612A",
+ "BE C8 612B",
+ "BE C9 6130",
+ "BE CA 6131",
+ "BE CB 6135",
+ "BE CC 6136",
+ "BE CD 6137",
+ "BE CE 6139",
+ "BE CF 6141",
+ "BE D0 6145",
+ "BE D1 6146",
+ "BE D2 6149",
+ "BE D3 615E",
+ "BE D4 6160",
+ "BE D5 616C",
+ "BE D6 6172",
+ "BE D7 6178",
+ "BE D8 617B",
+ "BE D9 617C",
+ "BE DA 617F",
+ "BE DB 6180",
+ "BE DC 6181",
+ "BE DD 6183",
+ "BE DE 6184",
+ "BE DF 618B",
+ "BE E0 618D",
+ "BE E1 6192",
+ "BE E2 6193",
+ "BE E3 6197",
+ "BE E4 6198",
+ "BE E5 619C",
+ "BE E6 619D",
+ "BE E7 619F",
+ "BE E8 61A0",
+ "BE E9 61A5",
+ "BE EA 61A8",
+ "BE EB 61AA",
+ "BE EC 61AD",
+ "BE ED 61B8",
+ "BE EE 61B9",
+ "BE EF 61BC",
+ "BE F0 61C0",
+ "BE F1 61C1",
+ "BE F2 61C2",
+ "BE F3 61CE",
+ "BE F4 61CF",
+ "BE F5 61D5",
+ "BE F6 61DC",
+ "BE F7 61DD",
+ "BE F8 61DE",
+ "BE F9 61DF",
+ "BE FA 61E1",
+ "BE FB 61E2",
+ "BE FE 61E5",
+ "BE FC 61E7",
+ "BE FD 61E9",
+ "BF A1 61EC",
+ "BF A2 61ED",
+ "BF A3 61EF",
+ "BF A4 6201",
+ "BF A5 6203",
+ "BF A6 6204",
+ "BF A7 6207",
+ "BF A8 6213",
+ "BF A9 6215",
+ "BF AA 621C",
+ "BF AB 6220",
+ "BF AC 6222",
+ "BF AD 6223",
+ "BF AE 6227",
+ "BF AF 6229",
+ "BF B0 622B",
+ "BF B1 6239",
+ "BF B2 623D",
+ "BF B3 6242",
+ "BF B4 6243",
+ "BF B5 6244",
+ "BF B6 6246",
+ "BF B7 624C",
+ "BF B8 6250",
+ "BF B9 6251",
+ "BF BA 6252",
+ "BF BB 6254",
+ "BF BC 6256",
+ "BF BD 625A",
+ "BF BE 625C",
+ "BF BF 6264",
+ "BF C0 626D",
+ "BF C1 626F",
+ "BF C2 6273",
+ "BF C3 627A",
+ "BF C4 627D",
+ "BF C5 628D",
+ "BF C6 628E",
+ "BF C7 628F",
+ "BF C8 6290",
+ "BF C9 62A6",
+ "BF CA 62A8",
+ "BF CB 62B3",
+ "BF CC 62B6",
+ "BF CD 62B7",
+ "BF CE 62BA",
+ "BF CF 62BE",
+ "BF D0 62BF",
+ "BF D1 62C4",
+ "BF D2 62CE",
+ "BF D3 62D5",
+ "BF D4 62D6",
+ "BF D5 62DA",
+ "BF D6 62EA",
+ "BF D7 62F2",
+ "BF D8 62F4",
+ "BF D9 62FC",
+ "BF DA 62FD",
+ "BF DB 6303",
+ "BF DC 6304",
+ "BF DD 630A",
+ "BF DE 630B",
+ "BF DF 630D",
+ "BF E0 6310",
+ "BF E1 6313",
+ "BF E2 6316",
+ "BF E3 6318",
+ "BF E4 6329",
+ "BF E5 632A",
+ "BF E6 632D",
+ "BF E7 6335",
+ "BF E8 6336",
+ "BF E9 6339",
+ "BF EA 633C",
+ "BF EB 6341",
+ "BF EC 6342",
+ "BF ED 6343",
+ "BF EE 6344",
+ "BF EF 6346",
+ "BF F0 634A",
+ "BF F1 634B",
+ "BF F2 634E",
+ "BF F3 6352",
+ "BF F4 6353",
+ "BF F5 6354",
+ "BF F6 6358",
+ "BF F7 635B",
+ "BF F8 6365",
+ "BF F9 6366",
+ "BF FA 636C",
+ "BF FB 636D",
+ "BF FC 6371",
+ "BF FD 6374",
+ "BF FE 6375",
+ "C0 A1 6378",
+ "C0 A2 637C",
+ "C0 A3 637D",
+ "C0 A4 637F",
+ "C0 A5 6382",
+ "C0 A6 6384",
+ "C0 A7 6387",
+ "C0 A8 638A",
+ "C0 A9 6390",
+ "C0 AA 6394",
+ "C0 AB 6395",
+ "C0 AC 6399",
+ "C0 AD 639A",
+ "C0 AE 639E",
+ "C0 AF 63A4",
+ "C0 B0 63A6",
+ "C0 B1 63AD",
+ "C0 B2 63AE",
+ "C0 B3 63AF",
+ "C0 B4 63BD",
+ "C0 B5 63C1",
+ "C0 B6 63C5",
+ "C0 B7 63C8",
+ "C0 B8 63CE",
+ "C0 B9 63D1",
+ "C0 BA 63D3",
+ "C0 BB 63D4",
+ "C0 BC 63D5",
+ "C0 BD 63DC",
+ "C0 BE 63E0",
+ "C0 BF 63E5",
+ "C0 C0 63EA",
+ "C0 C1 63EC",
+ "C0 C2 63F2",
+ "C0 C3 63F3",
+ "C0 C4 63F5",
+ "C0 C5 63F8",
+ "C0 C6 63F9",
+ "C0 C7 6409",
+ "C0 C8 640A",
+ "C0 C9 6410",
+ "C0 CA 6412",
+ "C0 CB 6414",
+ "C0 CC 6418",
+ "C0 CD 641E",
+ "C0 CE 6420",
+ "C0 CF 6422",
+ "C0 D0 6424",
+ "C0 D1 6425",
+ "C0 D2 6429",
+ "C0 D3 642A",
+ "C0 D4 642F",
+ "C0 D5 6430",
+ "C0 D6 6435",
+ "C0 D7 643D",
+ "C0 D8 643F",
+ "C0 D9 644B",
+ "C0 DA 644F",
+ "C0 DB 6451",
+ "C0 DC 6452",
+ "C0 DD 6453",
+ "C0 DE 6454",
+ "C0 DF 645A",
+ "C0 E0 645B",
+ "C0 E1 645C",
+ "C0 E2 645D",
+ "C0 E3 645F",
+ "C0 E4 6460",
+ "C0 E5 6461",
+ "C0 E6 6463",
+ "C0 E7 646D",
+ "C0 E8 6473",
+ "C0 E9 6474",
+ "C0 EA 647B",
+ "C0 EB 647D",
+ "C0 EC 6485",
+ "C0 ED 6487",
+ "C0 EE 648F",
+ "C0 EF 6490",
+ "C0 F0 6491",
+ "C0 F1 6498",
+ "C0 F2 6499",
+ "C0 F3 649B",
+ "C0 F4 649D",
+ "C0 F5 649F",
+ "C0 F6 64A1",
+ "C0 F7 64A3",
+ "C0 F8 64A6",
+ "C0 F9 64A8",
+ "C0 FA 64AC",
+ "C0 FB 64B3",
+ "C0 FC 64BD",
+ "C0 FD 64BE",
+ "C0 FE 64BF",
+ "C1 A1 64C4",
+ "C1 A2 64C9",
+ "C1 A3 64CA",
+ "C1 A4 64CB",
+ "C1 A5 64CC",
+ "C1 A6 64CE",
+ "C1 A7 64D0",
+ "C1 A8 64D1",
+ "C1 A9 64D5",
+ "C1 AA 64D7",
+ "C1 AB 64E4",
+ "C1 AC 64E5",
+ "C1 AD 64E9",
+ "C1 AE 64EA",
+ "C1 AF 64ED",
+ "C1 B0 64F0",
+ "C1 B1 64F5",
+ "C1 B2 64F7",
+ "C1 B3 64FB",
+ "C1 B4 64FF",
+ "C1 B5 6501",
+ "C1 B6 6504",
+ "C1 B7 6508",
+ "C1 B8 6509",
+ "C1 B9 650A",
+ "C1 BA 650F",
+ "C1 BB 6513",
+ "C1 BC 6514",
+ "C1 BD 6516",
+ "C1 BE 6519",
+ "C1 BF 651B",
+ "C1 C0 651E",
+ "C1 C1 651F",
+ "C1 C2 6522",
+ "C1 C3 6526",
+ "C1 C4 6529",
+ "C1 C5 652E",
+ "C1 C6 6531",
+ "C1 C7 653A",
+ "C1 C8 653C",
+ "C1 C9 653D",
+ "C1 CA 6543",
+ "C1 CB 6547",
+ "C1 CC 6549",
+ "C1 CD 6550",
+ "C1 CE 6552",
+ "C1 CF 6554",
+ "C1 D0 655F",
+ "C1 D1 6560",
+ "C1 D2 6567",
+ "C1 D3 656B",
+ "C1 D4 657A",
+ "C1 D5 657D",
+ "C1 D6 6581",
+ "C1 D7 6585",
+ "C1 D8 658A",
+ "C1 D9 6592",
+ "C1 DA 6595",
+ "C1 DB 6598",
+ "C1 DC 659D",
+ "C1 DD 65A0",
+ "C1 DE 65A3",
+ "C1 DF 65A6",
+ "C1 E0 65AE",
+ "C1 E1 65B2",
+ "C1 E2 65B3",
+ "C1 E3 65B4",
+ "C1 E4 65BF",
+ "C1 E5 65C2",
+ "C1 E6 65C8",
+ "C1 E7 65C9",
+ "C1 E8 65CE",
+ "C1 E9 65D0",
+ "C1 EA 65D4",
+ "C1 EB 65D6",
+ "C1 EC 65D8",
+ "C1 ED 65DF",
+ "C1 EE 65F0",
+ "C1 EF 65F2",
+ "C1 F0 65F4",
+ "C1 F1 65F5",
+ "C1 F2 65F9",
+ "C1 F3 65FE",
+ "C1 F4 65FF",
+ "C1 F5 6600",
+ "C1 F6 6604",
+ "C1 F7 6608",
+ "C1 F8 6609",
+ "C1 F9 660D",
+ "C1 FA 6611",
+ "C1 FB 6612",
+ "C1 FC 6615",
+ "C1 FD 6616",
+ "C1 FE 661D",
+ "C2 A1 661E",
+ "C2 A2 6621",
+ "C2 A3 6622",
+ "C2 A4 6623",
+ "C2 A5 6624",
+ "C2 A6 6626",
+ "C2 A7 6629",
+ "C2 A8 662A",
+ "C2 A9 662B",
+ "C2 AA 662C",
+ "C2 AB 662E",
+ "C2 AC 6630",
+ "C2 AD 6631",
+ "C2 AE 6633",
+ "C2 B0 6637",
+ "C2 AF 6639",
+ "C2 B1 6640",
+ "C2 B2 6645",
+ "C2 B3 6646",
+ "C2 B4 664A",
+ "C2 B5 664C",
+ "C2 B7 664E",
+ "C2 B6 6651",
+ "C2 B8 6657",
+ "C2 B9 6658",
+ "C2 BA 6659",
+ "C2 BB 665B",
+ "C2 BC 665C",
+ "C2 BD 6660",
+ "C2 BE 6661",
+ "C2 C0 666A",
+ "C2 C1 666B",
+ "C2 C2 666C",
+ "C2 C4 6673",
+ "C2 C5 6675",
+ "C2 C7 6677",
+ "C2 C8 6678",
+ "C2 C9 6679",
+ "C2 CA 667B",
+ "C2 CC 667C",
+ "C2 C3 667E",
+ "C2 C6 667F",
+ "C2 CB 6680",
+ "C2 CD 668B",
+ "C2 CE 668C",
+ "C2 CF 668D",
+ "C2 D0 6690",
+ "C2 D1 6692",
+ "C2 D2 6699",
+ "C2 D3 669A",
+ "C2 D4 669B",
+ "C2 D5 669C",
+ "C2 D6 669F",
+ "C2 D7 66A0",
+ "C2 D8 66A4",
+ "C2 D9 66AD",
+ "C2 DA 66B1",
+ "C2 DB 66B2",
+ "C2 DC 66B5",
+ "C2 DD 66BB",
+ "C2 DE 66BF",
+ "C2 DF 66C0",
+ "C2 E0 66C2",
+ "C2 E1 66C3",
+ "C2 E2 66C8",
+ "C2 E3 66CC",
+ "C2 E4 66CE",
+ "C2 E5 66CF",
+ "C2 E6 66D4",
+ "C2 E7 66DB",
+ "C2 E8 66DF",
+ "C2 E9 66E8",
+ "C2 EA 66EB",
+ "C2 EB 66EC",
+ "C2 EC 66EE",
+ "C2 ED 66FA",
+ "C2 BF 66FB",
+ "C2 EE 6705",
+ "C2 EF 6707",
+ "C2 F0 670E",
+ "C2 F1 6713",
+ "C2 F2 6719",
+ "C2 F3 671C",
+ "C2 F4 6720",
+ "C2 F5 6722",
+ "C2 F6 6733",
+ "C2 F7 673E",
+ "C2 F8 6745",
+ "C2 F9 6747",
+ "C2 FA 6748",
+ "C2 FB 674C",
+ "C2 FC 6754",
+ "C2 FD 6755",
+ "C2 FE 675D",
+ "C3 A1 6766",
+ "C3 A2 676C",
+ "C3 A3 676E",
+ "C3 A4 6774",
+ "C3 A5 6776",
+ "C3 A6 677B",
+ "C3 A7 6781",
+ "C3 A8 6784",
+ "C3 A9 678E",
+ "C3 AA 678F",
+ "C3 AB 6791",
+ "C3 AC 6793",
+ "C3 AD 6796",
+ "C3 AE 6798",
+ "C3 AF 6799",
+ "C3 B0 679B",
+ "C3 B1 67B0",
+ "C3 B2 67B1",
+ "C3 B3 67B2",
+ "C3 B4 67B5",
+ "C3 B5 67BB",
+ "C3 B6 67BC",
+ "C3 B7 67BD",
+ "C3 B9 67C0",
+ "C3 BA 67C2",
+ "C3 BB 67C3",
+ "C3 BC 67C5",
+ "C3 BD 67C8",
+ "C3 BE 67C9",
+ "C3 BF 67D2",
+ "C3 C0 67D7",
+ "C3 C1 67D9",
+ "C3 C2 67DC",
+ "C3 C3 67E1",
+ "C3 C4 67E6",
+ "C3 C5 67F0",
+ "C3 C6 67F2",
+ "C3 C7 67F6",
+ "C3 C8 67F7",
+ "C3 B8 67F9",
+ "C3 CA 6814",
+ "C3 CB 6819",
+ "C3 CC 681D",
+ "C3 CD 681F",
+ "C3 CF 6827",
+ "C3 CE 6828",
+ "C3 D0 682C",
+ "C3 D1 682D",
+ "C3 D2 682F",
+ "C3 D3 6830",
+ "C3 D4 6831",
+ "C3 D5 6833",
+ "C3 D6 683B",
+ "C3 D7 683F",
+ "C3 D8 6844",
+ "C3 D9 6845",
+ "C3 DA 684A",
+ "C3 DB 684C",
+ "C3 C9 6852",
+ "C3 DC 6855",
+ "C3 DD 6857",
+ "C3 DE 6858",
+ "C3 DF 685B",
+ "C3 E0 686B",
+ "C3 E1 686E",
+ "C3 E2 686F",
+ "C3 E3 6870",
+ "C3 E4 6871",
+ "C3 E5 6872",
+ "C3 E6 6875",
+ "C3 E7 6879",
+ "C3 E8 687A",
+ "C3 E9 687B",
+ "C3 EA 687C",
+ "C3 EB 6882",
+ "C3 EC 6884",
+ "C3 ED 6886",
+ "C3 EE 6888",
+ "C3 EF 6896",
+ "C3 F0 6898",
+ "C3 F1 689A",
+ "C3 F2 689C",
+ "C3 F3 68A1",
+ "C3 F4 68A3",
+ "C3 F5 68A5",
+ "C3 F6 68A9",
+ "C3 F7 68AA",
+ "C3 F8 68AE",
+ "C3 F9 68B2",
+ "C3 FA 68BB",
+ "C3 FB 68C5",
+ "C3 FC 68C8",
+ "C3 FD 68CC",
+ "C3 FE 68CF",
+ "C4 A1 68D0",
+ "C4 A2 68D1",
+ "C4 A3 68D3",
+ "C4 A4 68D6",
+ "C4 A5 68D9",
+ "C4 A6 68DC",
+ "C4 A7 68DD",
+ "C4 A8 68E5",
+ "C4 A9 68E8",
+ "C4 AA 68EA",
+ "C4 AB 68EB",
+ "C4 AC 68EC",
+ "C4 AD 68ED",
+ "C4 AE 68F0",
+ "C4 AF 68F1",
+ "C4 B0 68F5",
+ "C4 B1 68F6",
+ "C4 B2 68FB",
+ "C4 B3 68FC",
+ "C4 B4 68FD",
+ "C4 B5 6906",
+ "C4 B6 6909",
+ "C4 B7 690A",
+ "C4 B8 6910",
+ "C4 B9 6911",
+ "C4 BA 6913",
+ "C4 BB 6916",
+ "C4 BC 6917",
+ "C4 BD 6931",
+ "C4 BE 6933",
+ "C4 BF 6935",
+ "C4 C0 6938",
+ "C4 C1 693B",
+ "C4 C2 6942",
+ "C4 C3 6945",
+ "C4 C4 6949",
+ "C4 C5 694E",
+ "C4 C6 6957",
+ "C4 C7 695B",
+ "C4 C8 6963",
+ "C4 C9 6964",
+ "C4 CA 6965",
+ "C4 CB 6966",
+ "C4 CC 6968",
+ "C4 CD 6969",
+ "C4 CE 696C",
+ "C4 CF 6970",
+ "C4 D0 6971",
+ "C4 D1 6972",
+ "C4 D2 697A",
+ "C4 D3 697B",
+ "C4 D4 697F",
+ "C4 D5 6980",
+ "C4 D6 698D",
+ "C4 D7 6992",
+ "C4 D8 6996",
+ "C4 D9 6998",
+ "C4 DA 69A1",
+ "C4 DB 69A5",
+ "C4 DC 69A6",
+ "C4 DD 69A8",
+ "C4 DE 69AB",
+ "C4 DF 69AD",
+ "C4 E0 69AF",
+ "C4 E1 69B7",
+ "C4 E2 69B8",
+ "C4 E3 69BA",
+ "C4 E4 69BC",
+ "C4 E5 69C5",
+ "C4 E6 69C8",
+ "C4 E7 69D1",
+ "C4 E8 69D6",
+ "C4 E9 69D7",
+ "C4 EA 69E2",
+ "C4 EB 69E5",
+ "C4 EC 69EE",
+ "C4 ED 69EF",
+ "C4 EE 69F1",
+ "C4 EF 69F3",
+ "C4 F0 69F5",
+ "C4 F1 69FE",
+ "C4 F2 6A00",
+ "C4 F3 6A01",
+ "C4 F4 6A03",
+ "C4 F5 6A0F",
+ "C4 F6 6A11",
+ "C4 F7 6A15",
+ "C4 F8 6A1A",
+ "C4 F9 6A1D",
+ "C4 FA 6A20",
+ "C4 FB 6A24",
+ "C4 FC 6A28",
+ "C4 FD 6A30",
+ "C4 FE 6A32",
+ "C5 A1 6A34",
+ "C5 A2 6A37",
+ "C5 A3 6A3B",
+ "C5 A4 6A3E",
+ "C5 A5 6A3F",
+ "C5 A6 6A45",
+ "C5 A7 6A46",
+ "C5 A8 6A49",
+ "C5 A9 6A4A",
+ "C5 AA 6A4E",
+ "C5 AB 6A50",
+ "C5 AC 6A51",
+ "C5 AD 6A52",
+ "C5 AE 6A55",
+ "C5 AF 6A56",
+ "C5 B0 6A5B",
+ "C5 B1 6A64",
+ "C5 B2 6A67",
+ "C5 B3 6A6A",
+ "C5 B4 6A71",
+ "C5 B5 6A73",
+ "C5 B6 6A7E",
+ "C5 B7 6A81",
+ "C5 B8 6A83",
+ "C5 B9 6A86",
+ "C5 BA 6A87",
+ "C5 BB 6A89",
+ "C5 BC 6A8B",
+ "C5 BD 6A91",
+ "C5 BE 6A9B",
+ "C5 BF 6A9D",
+ "C5 C0 6A9E",
+ "C5 C1 6A9F",
+ "C5 C2 6AA5",
+ "C5 C3 6AAB",
+ "C5 C4 6AAF",
+ "C5 C5 6AB0",
+ "C5 C6 6AB1",
+ "C5 C7 6AB4",
+ "C5 C8 6ABD",
+ "C5 C9 6ABE",
+ "C5 CA 6ABF",
+ "C5 CB 6AC6",
+ "C5 CD 6AC8",
+ "C5 CC 6AC9",
+ "C5 CE 6ACC",
+ "C5 CF 6AD0",
+ "C5 D0 6AD4",
+ "C5 D1 6AD5",
+ "C5 D2 6AD6",
+ "C5 D3 6ADC",
+ "C5 D4 6ADD",
+ "C5 D5 6AE4",
+ "C5 D6 6AE7",
+ "C5 D7 6AEC",
+ "C5 D8 6AF0",
+ "C5 D9 6AF1",
+ "C5 DA 6AF2",
+ "C5 DB 6AFC",
+ "C5 DC 6AFD",
+ "C5 DD 6B02",
+ "C5 DE 6B03",
+ "C5 DF 6B06",
+ "C5 E0 6B07",
+ "C5 E1 6B09",
+ "C5 E2 6B0F",
+ "C5 E3 6B10",
+ "C5 E4 6B11",
+ "C5 E5 6B17",
+ "C5 E6 6B1B",
+ "C5 E7 6B1E",
+ "C5 E8 6B24",
+ "C5 E9 6B28",
+ "C5 EA 6B2B",
+ "C5 EB 6B2C",
+ "C5 EC 6B2F",
+ "C5 ED 6B35",
+ "C5 EE 6B36",
+ "C5 EF 6B3B",
+ "C5 F0 6B3F",
+ "C5 F1 6B46",
+ "C5 F2 6B4A",
+ "C5 F3 6B4D",
+ "C5 F4 6B52",
+ "C5 F5 6B56",
+ "C5 F6 6B58",
+ "C5 F7 6B5D",
+ "C5 F8 6B60",
+ "C5 F9 6B67",
+ "C5 FA 6B6B",
+ "C5 FB 6B6E",
+ "C5 FC 6B70",
+ "C5 FD 6B75",
+ "C5 FE 6B7D",
+ "C6 A1 6B7E",
+ "C6 A2 6B82",
+ "C6 A3 6B85",
+ "C6 A4 6B97",
+ "C6 A5 6B9B",
+ "C6 A6 6B9F",
+ "C6 A7 6BA0",
+ "C6 A8 6BA2",
+ "C6 A9 6BA3",
+ "C6 AA 6BA8",
+ "C6 AB 6BA9",
+ "C6 AC 6BAC",
+ "C6 AD 6BAD",
+ "C6 AE 6BAE",
+ "C6 AF 6BB0",
+ "C6 B0 6BB8",
+ "C6 B1 6BB9",
+ "C6 B2 6BBD",
+ "C6 B3 6BBE",
+ "C6 B4 6BC3",
+ "C6 B5 6BC4",
+ "C6 B6 6BC9",
+ "C6 B7 6BCC",
+ "C6 B8 6BD6",
+ "C6 B9 6BDA",
+ "C6 BA 6BE1",
+ "C6 BB 6BE3",
+ "C6 BC 6BE6",
+ "C6 BD 6BE7",
+ "C6 BE 6BEE",
+ "C6 BF 6BF1",
+ "C6 C0 6BF7",
+ "C6 C1 6BF9",
+ "C6 C2 6BFF",
+ "C6 C3 6C02",
+ "C6 C4 6C04",
+ "C6 C5 6C05",
+ "C6 C6 6C09",
+ "C6 C7 6C0D",
+ "C6 C8 6C0E",
+ "C6 C9 6C10",
+ "C6 CA 6C12",
+ "C6 CB 6C19",
+ "C6 CC 6C1F",
+ "C6 CD 6C26",
+ "C6 CE 6C27",
+ "C6 CF 6C28",
+ "C6 D0 6C2C",
+ "C6 D1 6C2E",
+ "C6 D2 6C33",
+ "C6 D3 6C35",
+ "C6 D4 6C36",
+ "C6 D5 6C3A",
+ "C6 D6 6C3B",
+ "C6 D7 6C3F",
+ "C6 D8 6C4A",
+ "C6 D9 6C4B",
+ "C6 DA 6C4D",
+ "C6 DB 6C4F",
+ "C6 DC 6C52",
+ "C6 DD 6C54",
+ "C6 DE 6C59",
+ "C6 DF 6C5B",
+ "C6 E0 6C5C",
+ "C7 B6 6C67",
+ "C6 E1 6C6B",
+ "C6 E2 6C6D",
+ "C6 E3 6C6F",
+ "C6 E4 6C74",
+ "C6 E5 6C76",
+ "C6 E6 6C78",
+ "C6 E7 6C79",
+ "C6 E8 6C7B",
+ "C6 E9 6C85",
+ "C6 EA 6C86",
+ "C6 EB 6C87",
+ "C6 EC 6C89",
+ "C6 ED 6C94",
+ "C6 EE 6C95",
+ "C6 EF 6C97",
+ "C6 F0 6C98",
+ "C6 F1 6C9C",
+ "C6 F2 6C9F",
+ "C6 F3 6CB0",
+ "C6 F4 6CB2",
+ "C6 F5 6CB4",
+ "C6 F6 6CC2",
+ "C6 F7 6CC6",
+ "C6 F8 6CCD",
+ "C6 F9 6CCF",
+ "C6 FA 6CD0",
+ "C6 FB 6CD1",
+ "C6 FC 6CD2",
+ "C6 FD 6CD4",
+ "C6 FE 6CD6",
+ "C7 A1 6CDA",
+ "C7 A2 6CDC",
+ "C7 A3 6CE0",
+ "C7 A4 6CE7",
+ "C7 A5 6CE9",
+ "C7 A6 6CEB",
+ "C7 A7 6CEC",
+ "C7 A8 6CEE",
+ "C7 A9 6CF2",
+ "C7 AA 6CF4",
+ "C7 AB 6D04",
+ "C7 AC 6D07",
+ "C7 AD 6D0A",
+ "C7 AE 6D0E",
+ "C7 AF 6D0F",
+ "C7 B0 6D11",
+ "C7 B1 6D13",
+ "C7 B2 6D1A",
+ "C7 B3 6D26",
+ "C7 B4 6D27",
+ "C7 B5 6D28",
+ "C7 B7 6D2E",
+ "C7 B8 6D2F",
+ "C7 B9 6D31",
+ "C7 BA 6D39",
+ "C7 BB 6D3C",
+ "C7 BC 6D3F",
+ "C7 BD 6D57",
+ "C7 BE 6D5E",
+ "C7 BF 6D5F",
+ "C7 C0 6D61",
+ "C7 C1 6D65",
+ "C7 C2 6D67",
+ "C7 C3 6D6F",
+ "C7 C4 6D70",
+ "C7 C5 6D7C",
+ "C7 C6 6D82",
+ "C7 C7 6D87",
+ "C7 C8 6D91",
+ "C7 C9 6D92",
+ "C7 CA 6D94",
+ "C7 CB 6D96",
+ "C7 CC 6D97",
+ "C7 CD 6D98",
+ "C7 CE 6DAA",
+ "C7 CF 6DAC",
+ "C7 D0 6DB4",
+ "C7 D1 6DB7",
+ "C7 D2 6DB9",
+ "C7 D3 6DBD",
+ "C7 D4 6DBF",
+ "C7 D5 6DC4",
+ "C7 D6 6DC8",
+ "C7 D7 6DCA",
+ "C7 D8 6DCE",
+ "C7 D9 6DCF",
+ "C7 DA 6DD6",
+ "C7 DB 6DDB",
+ "C7 DC 6DDD",
+ "C7 DD 6DDF",
+ "C7 DE 6DE0",
+ "C7 DF 6DE2",
+ "C7 E0 6DE5",
+ "C7 E1 6DE9",
+ "C7 E2 6DEF",
+ "C7 E3 6DF0",
+ "C7 E4 6DF4",
+ "C7 E5 6DF6",
+ "C7 E6 6DFC",
+ "C7 E7 6E00",
+ "C7 E8 6E04",
+ "C7 E9 6E1E",
+ "C7 EA 6E22",
+ "C7 EB 6E27",
+ "C7 EC 6E32",
+ "C7 ED 6E36",
+ "C7 EE 6E39",
+ "C7 EF 6E3B",
+ "C7 F0 6E3C",
+ "C7 F1 6E44",
+ "C7 F2 6E45",
+ "C7 F3 6E48",
+ "C7 F4 6E49",
+ "C7 F5 6E4B",
+ "C7 F6 6E4F",
+ "C7 F7 6E51",
+ "C7 F8 6E52",
+ "C7 F9 6E53",
+ "C7 FA 6E54",
+ "C7 FB 6E57",
+ "C7 FC 6E5C",
+ "C7 FD 6E5D",
+ "C7 FE 6E5E",
+ "C8 A1 6E62",
+ "C8 A2 6E63",
+ "C8 A3 6E68",
+ "C8 A4 6E73",
+ "C8 A5 6E7B",
+ "C8 A6 6E7D",
+ "C8 A7 6E8D",
+ "C8 A8 6E93",
+ "C8 A9 6E99",
+ "C8 AA 6EA0",
+ "C8 AB 6EA7",
+ "C8 AC 6EAD",
+ "C8 AD 6EAE",
+ "C8 AE 6EB1",
+ "C8 AF 6EB3",
+ "C8 B0 6EBB",
+ "C8 B1 6EBF",
+ "C8 B2 6EC0",
+ "C8 B3 6EC1",
+ "C8 B4 6EC3",
+ "C8 B5 6EC7",
+ "C8 B6 6EC8",
+ "C8 B7 6ECA",
+ "C8 B8 6ECD",
+ "C8 B9 6ECE",
+ "C8 BA 6ECF",
+ "C8 BB 6EEB",
+ "C8 BC 6EED",
+ "C8 BD 6EEE",
+ "C8 BE 6EF9",
+ "C8 BF 6EFB",
+ "C8 C0 6EFD",
+ "C8 C1 6F04",
+ "C8 C2 6F08",
+ "C8 C3 6F0A",
+ "C8 C4 6F0C",
+ "C8 C5 6F0D",
+ "C8 C6 6F16",
+ "C8 C7 6F18",
+ "C8 C8 6F1A",
+ "C8 C9 6F1B",
+ "C8 CA 6F26",
+ "C8 CB 6F29",
+ "C8 CC 6F2A",
+ "C8 D3 6F2D",
+ "C8 CD 6F2F",
+ "C8 CE 6F30",
+ "C8 CF 6F33",
+ "C8 D0 6F36",
+ "C8 D1 6F3B",
+ "C8 D2 6F3C",
+ "C8 D4 6F4F",
+ "C8 D5 6F51",
+ "C8 D6 6F52",
+ "C8 D7 6F53",
+ "C8 D8 6F57",
+ "C8 D9 6F59",
+ "C8 DA 6F5A",
+ "C8 DB 6F5D",
+ "C8 DC 6F5E",
+ "C8 DD 6F61",
+ "C8 DE 6F62",
+ "C8 DF 6F68",
+ "C8 E0 6F6C",
+ "C8 E1 6F7D",
+ "C8 E2 6F7E",
+ "C8 E3 6F83",
+ "C8 E4 6F87",
+ "C8 E5 6F88",
+ "C8 E6 6F8B",
+ "C8 E7 6F8C",
+ "C8 E8 6F8D",
+ "C8 E9 6F90",
+ "C8 EA 6F92",
+ "C8 EB 6F93",
+ "C8 EC 6F94",
+ "C8 ED 6F96",
+ "C8 EE 6F9A",
+ "C8 EF 6F9F",
+ "C8 F0 6FA0",
+ "C8 F1 6FA5",
+ "C8 F2 6FA6",
+ "C8 F3 6FA7",
+ "C8 F4 6FA8",
+ "C8 F5 6FAE",
+ "C8 F6 6FAF",
+ "C8 F7 6FB0",
+ "C8 F8 6FB5",
+ "C8 F9 6FB6",
+ "C8 FA 6FBC",
+ "C8 FB 6FC5",
+ "C8 FC 6FC7",
+ "C8 FD 6FC8",
+ "C8 FE 6FCA",
+ "C9 A1 6FDA",
+ "C9 A2 6FDE",
+ "C9 A3 6FE8",
+ "C9 A4 6FE9",
+ "C9 A5 6FF0",
+ "C9 A6 6FF5",
+ "C9 A7 6FF9",
+ "C9 A8 6FFC",
+ "C9 A9 6FFD",
+ "C9 AA 7000",
+ "C9 AB 7005",
+ "C9 AC 7006",
+ "C9 AD 7007",
+ "C9 AE 700D",
+ "C9 AF 7017",
+ "C9 B0 7020",
+ "C9 B1 7023",
+ "C9 B2 702F",
+ "C9 B3 7034",
+ "C9 B4 7037",
+ "C9 B5 7039",
+ "C9 B6 703C",
+ "C9 B7 7043",
+ "C9 B8 7044",
+ "C9 B9 7048",
+ "C9 BA 7049",
+ "C9 BB 704A",
+ "C9 BC 704B",
+ "C9 C1 704E",
+ "C9 BD 7054",
+ "C9 BE 7055",
+ "C9 BF 705D",
+ "C9 C0 705E",
+ "C9 C2 7064",
+ "C9 C3 7065",
+ "C9 C4 706C",
+ "C9 C5 706E",
+ "C9 C6 7075",
+ "C9 C7 7076",
+ "C9 C8 707E",
+ "C9 C9 7081",
+ "C9 CA 7085",
+ "C9 CB 7086",
+ "C9 CC 7094",
+ "C9 CD 7095",
+ "C9 CE 7096",
+ "C9 CF 7097",
+ "C9 D0 7098",
+ "C9 D1 709B",
+ "C9 D2 70A4",
+ "C9 D3 70AB",
+ "C9 D4 70B0",
+ "C9 D5 70B1",
+ "C9 D6 70B4",
+ "C9 D7 70B7",
+ "C9 D8 70CA",
+ "C9 D9 70D1",
+ "C9 DA 70D3",
+ "C9 DB 70D4",
+ "C9 DC 70D5",
+ "C9 DD 70D6",
+ "C9 DE 70D8",
+ "C9 DF 70DC",
+ "C9 E0 70E4",
+ "C9 E1 70FA",
+ "C9 E2 7103",
+ "C9 E3 7104",
+ "C9 E4 7105",
+ "C9 E5 7106",
+ "C9 E6 7107",
+ "C9 E7 710B",
+ "C9 E8 710C",
+ "C9 E9 710F",
+ "C9 EA 711E",
+ "C9 EB 7120",
+ "C9 EC 712B",
+ "C9 ED 712D",
+ "C9 EE 712F",
+ "C9 EF 7130",
+ "C9 F0 7131",
+ "C9 F1 7138",
+ "C9 F2 7141",
+ "C9 F3 7145",
+ "C9 F4 7146",
+ "C9 F5 7147",
+ "C9 F6 714A",
+ "C9 F7 714B",
+ "C9 F8 7150",
+ "C9 F9 7152",
+ "C9 FA 7157",
+ "C9 FB 715A",
+ "C9 FC 715C",
+ "C9 FD 715E",
+ "C9 FE 7160",
+ "CA A1 7168",
+ "CA A2 7179",
+ "CA A3 7180",
+ "CA A4 7185",
+ "CA A5 7187",
+ "CA A6 718C",
+ "CA A7 7192",
+ "CA A8 719A",
+ "CA A9 719B",
+ "CA AA 71A0",
+ "CA AB 71A2",
+ "CA AC 71AF",
+ "CA AD 71B0",
+ "CA AE 71B2",
+ "CA AF 71B3",
+ "CA B0 71BA",
+ "CA B1 71BF",
+ "CA B2 71C0",
+ "CA B3 71C1",
+ "CA B4 71C4",
+ "CA B5 71CB",
+ "CA B6 71CC",
+ "CA B7 71D3",
+ "CA B8 71D6",
+ "CA B9 71D9",
+ "CA BA 71DA",
+ "CA BB 71DC",
+ "CA BC 71F8",
+ "CA BD 71FE",
+ "CA BE 7200",
+ "CA BF 7207",
+ "CA C0 7208",
+ "CA C1 7209",
+ "CA C2 7213",
+ "CA C3 7217",
+ "CA C4 721A",
+ "CA C5 721D",
+ "CA C6 721F",
+ "CA C7 7224",
+ "CA C8 722B",
+ "CA C9 722F",
+ "CA CA 7234",
+ "CA CB 7238",
+ "CA CC 7239",
+ "CA CD 7241",
+ "CA CE 7242",
+ "CA CF 7243",
+ "CA D0 7245",
+ "CA D1 724E",
+ "CA D2 724F",
+ "CA D3 7250",
+ "CA D4 7253",
+ "CA D5 7255",
+ "CA D6 7256",
+ "CA D7 725A",
+ "CA D8 725C",
+ "CA D9 725E",
+ "CA DA 7260",
+ "CA DB 7263",
+ "CA DC 7268",
+ "CA DD 726B",
+ "CA DE 726E",
+ "CA DF 726F",
+ "CA E0 7271",
+ "CA E1 7277",
+ "CA E2 7278",
+ "CA E3 727B",
+ "CA E4 727C",
+ "CA E5 727F",
+ "CA E6 7284",
+ "CA E7 7289",
+ "CA E8 728D",
+ "CA E9 728E",
+ "CA EA 7293",
+ "CA EB 729B",
+ "CA EC 72A8",
+ "CA ED 72AD",
+ "CA EE 72AE",
+ "CA EF 72B1",
+ "CA F0 72B4",
+ "CA F1 72BE",
+ "CA F2 72C1",
+ "CA F3 72C7",
+ "CA F4 72C9",
+ "CA F5 72CC",
+ "CA F6 72D5",
+ "CA F7 72D6",
+ "CA F8 72D8",
+ "CA F9 72DF",
+ "CA FA 72E5",
+ "CA FB 72F3",
+ "CA FC 72F4",
+ "CA FD 72FA",
+ "CA FE 72FB",
+ "CB A1 72FE",
+ "CB A2 7302",
+ "CB A3 7304",
+ "CB A4 7305",
+ "CB A5 7307",
+ "CB A6 730B",
+ "CB A7 730D",
+ "CB A8 7312",
+ "CB A9 7313",
+ "CB AA 7318",
+ "CB AB 7319",
+ "CB AC 731E",
+ "CB AD 7322",
+ "CB AE 7324",
+ "CB AF 7327",
+ "CB B0 7328",
+ "CB B1 732C",
+ "CB B2 7331",
+ "CB B3 7332",
+ "CB B4 7335",
+ "CB B5 733A",
+ "CB B6 733B",
+ "CB B7 733D",
+ "CB B8 7343",
+ "CB B9 734D",
+ "CB BA 7350",
+ "CB BB 7352",
+ "CB BC 7356",
+ "CB BD 7358",
+ "CB BE 735D",
+ "CB BF 735E",
+ "CB C0 735F",
+ "CB C1 7360",
+ "CB C2 7366",
+ "CB C3 7367",
+ "CB C4 7369",
+ "CB C5 736B",
+ "CB C6 736C",
+ "CB C7 736E",
+ "CB C8 736F",
+ "CB C9 7371",
+ "CB CA 7377",
+ "CB CB 7379",
+ "CB CC 737C",
+ "CB CD 7380",
+ "CB CE 7381",
+ "CB CF 7383",
+ "CB D0 7385",
+ "CB D1 7386",
+ "CB D2 738E",
+ "CB D3 7390",
+ "CB D4 7393",
+ "CB D5 7395",
+ "CB D6 7397",
+ "CB D7 7398",
+ "CB D8 739C",
+ "CB D9 739E",
+ "CB DA 739F",
+ "CB DB 73A0",
+ "CB DC 73A2",
+ "CB DD 73A5",
+ "CB DE 73A6",
+ "CB DF 73AA",
+ "CB E0 73AB",
+ "CB E1 73AD",
+ "CB E2 73B5",
+ "CB E3 73B7",
+ "CB E4 73B9",
+ "CB E5 73BC",
+ "CB E6 73BD",
+ "CB E7 73BF",
+ "CB E8 73C5",
+ "CB E9 73C6",
+ "CB EA 73C9",
+ "CB EB 73CB",
+ "CB EC 73CC",
+ "CB ED 73CF",
+ "CB EE 73D2",
+ "CB EF 73D3",
+ "CB F0 73D6",
+ "CB F1 73D9",
+ "CB F2 73DD",
+ "CB F3 73E1",
+ "CB F4 73E3",
+ "CB F5 73E6",
+ "CB F6 73E7",
+ "CB F7 73E9",
+ "CB F8 73F4",
+ "CB F9 73F5",
+ "CB FA 73F7",
+ "CB FB 73F9",
+ "CB FC 73FA",
+ "CB FD 73FB",
+ "CB FE 73FD",
+ "CC A1 73FF",
+ "CC A2 7400",
+ "CC A3 7401",
+ "CC A4 7404",
+ "CC A5 7407",
+ "CC A6 740A",
+ "CC A7 7411",
+ "CC A8 741A",
+ "CC A9 741B",
+ "CC AA 7424",
+ "CC AB 7426",
+ "CC AC 7428",
+ "CC AD 7429",
+ "CC AE 742A",
+ "CC AF 742B",
+ "CC B0 742C",
+ "CC B1 742D",
+ "CC B2 742E",
+ "CC B3 742F",
+ "CC B4 7430",
+ "CC B5 7431",
+ "CC B6 7439",
+ "CC B7 7440",
+ "CC B8 7443",
+ "CC B9 7444",
+ "CC BA 7446",
+ "CC BB 7447",
+ "CC BC 744B",
+ "CC BD 744D",
+ "CC BE 7451",
+ "CC BF 7452",
+ "CC C0 7457",
+ "CC C1 745D",
+ "CC C2 7462",
+ "CC C3 7466",
+ "CC C4 7467",
+ "CC C5 7468",
+ "CC C6 746B",
+ "CC C7 746D",
+ "CC C8 746E",
+ "CC C9 7471",
+ "CC CA 7472",
+ "CC CB 7480",
+ "CC CC 7481",
+ "CC CD 7485",
+ "CC CE 7486",
+ "CC CF 7487",
+ "CC D0 7489",
+ "CC D1 748F",
+ "CC D2 7490",
+ "CC D3 7491",
+ "CC D4 7492",
+ "CC D5 7498",
+ "CC D6 7499",
+ "CC D7 749A",
+ "CC D8 749C",
+ "CC D9 749F",
+ "CC DA 74A0",
+ "CC DB 74A1",
+ "CC DC 74A3",
+ "CC DD 74A6",
+ "CC DE 74A8",
+ "CC DF 74A9",
+ "CC E0 74AA",
+ "CC E1 74AB",
+ "CC E2 74AE",
+ "CC E3 74AF",
+ "CC E4 74B1",
+ "CC E5 74B2",
+ "CC E6 74B5",
+ "CC E7 74B9",
+ "CC E8 74BB",
+ "CC E9 74BF",
+ "CC EA 74C8",
+ "CC EB 74C9",
+ "CC EC 74CC",
+ "CC ED 74D0",
+ "CC EE 74D3",
+ "CC EF 74D8",
+ "CC F0 74DA",
+ "CC F1 74DB",
+ "CC F2 74DE",
+ "CC F3 74DF",
+ "CC F4 74E4",
+ "CC F5 74E8",
+ "CC F6 74EA",
+ "CC F7 74EB",
+ "CC F8 74EF",
+ "CC F9 74F4",
+ "CC FA 74FA",
+ "CC FB 74FB",
+ "CC FC 74FC",
+ "CC FD 74FF",
+ "CC FE 7506",
+ "CD A1 7512",
+ "CD A2 7516",
+ "CD A3 7517",
+ "CD A4 7520",
+ "CD A5 7521",
+ "CD A6 7524",
+ "CD A7 7527",
+ "CD A8 7529",
+ "CD A9 752A",
+ "CD AA 752F",
+ "CD AB 7536",
+ "CD AC 7539",
+ "CD AD 753D",
+ "CD AE 753E",
+ "CD AF 753F",
+ "CD B0 7540",
+ "CD B1 7543",
+ "CD B2 7547",
+ "CD B3 7548",
+ "CD B4 754E",
+ "CD B5 7550",
+ "CD B6 7552",
+ "CD B7 7557",
+ "CD B8 755E",
+ "CD B9 755F",
+ "CD BA 7561",
+ "CD BB 756F",
+ "CD BC 7571",
+ "CD BD 7579",
+ "CD BE 757A",
+ "CD BF 757B",
+ "CD C0 757C",
+ "CD C1 757D",
+ "CD C2 757E",
+ "CD C3 7581",
+ "CD C4 7585",
+ "CD C5 7590",
+ "CD C6 7592",
+ "CD C7 7593",
+ "CD C8 7595",
+ "CD C9 7599",
+ "CD CA 759C",
+ "CD CB 75A2",
+ "CD CC 75A4",
+ "CD CD 75B4",
+ "CD CE 75BA",
+ "CD CF 75BF",
+ "CD D0 75C0",
+ "CD D1 75C1",
+ "CD D2 75C4",
+ "CD D3 75C6",
+ "CD D4 75CC",
+ "CD D5 75CE",
+ "CD D6 75CF",
+ "CD D7 75D7",
+ "CD D8 75DC",
+ "CD D9 75DF",
+ "CD DA 75E0",
+ "CD DB 75E1",
+ "CD DC 75E4",
+ "CD DD 75E7",
+ "CD DE 75EC",
+ "CD DF 75EE",
+ "CD E0 75EF",
+ "CD E1 75F1",
+ "CD E2 75F9",
+ "CD E3 7600",
+ "CD E4 7602",
+ "CD E5 7603",
+ "CD E6 7604",
+ "CD E7 7607",
+ "CD E8 7608",
+ "CD E9 760A",
+ "CD EA 760C",
+ "CD EB 760F",
+ "CD EC 7612",
+ "CD ED 7613",
+ "CD EE 7615",
+ "CD EF 7616",
+ "CD F0 7619",
+ "CD F1 761B",
+ "CD F2 761C",
+ "CD F3 761D",
+ "CD F4 761E",
+ "CD F5 7623",
+ "CD F6 7625",
+ "CD F7 7626",
+ "CD F8 7629",
+ "CD F9 762D",
+ "CD FA 7632",
+ "CD FB 7633",
+ "CD FC 7635",
+ "CD FD 7638",
+ "CD FE 7639",
+ "CE A1 763A",
+ "CE A2 763C",
+ "CE A4 7640",
+ "CE A5 7641",
+ "CE A6 7643",
+ "CE A7 7644",
+ "CE A8 7645",
+ "CE A9 7649",
+ "CE A3 764A",
+ "CE AA 764B",
+ "CE AB 7655",
+ "CE AC 7659",
+ "CE AD 765F",
+ "CE AE 7664",
+ "CE AF 7665",
+ "CE B0 766D",
+ "CE B1 766E",
+ "CE B2 766F",
+ "CE B3 7671",
+ "CE B4 7674",
+ "CE B5 7681",
+ "CE B6 7685",
+ "CE B7 768C",
+ "CE B8 768D",
+ "CE B9 7695",
+ "CE BA 769B",
+ "CE BB 769C",
+ "CE BC 769D",
+ "CE BD 769F",
+ "CE BE 76A0",
+ "CE BF 76A2",
+ "CE C0 76A3",
+ "CE C1 76A4",
+ "CE C2 76A5",
+ "CE C3 76A6",
+ "CE C4 76A7",
+ "CE C5 76A8",
+ "CE C6 76AA",
+ "CE C7 76AD",
+ "CE C8 76BD",
+ "CE C9 76C1",
+ "CE CA 76C5",
+ "CE CB 76C9",
+ "CE CC 76CB",
+ "CE CD 76CC",
+ "CE CE 76CE",
+ "CE CF 76D4",
+ "CE D0 76D9",
+ "CE D1 76E0",
+ "CE D2 76E6",
+ "CE D3 76E8",
+ "CE D4 76EC",
+ "CE D5 76F0",
+ "CE D6 76F1",
+ "CE D7 76F6",
+ "CE D8 76F9",
+ "CE D9 76FC",
+ "CE DA 7700",
+ "CE DB 7706",
+ "CE DC 770A",
+ "CE DD 770E",
+ "CE DE 7712",
+ "CE DF 7714",
+ "CE E0 7715",
+ "CE E1 7717",
+ "CE E2 7719",
+ "CE E3 771A",
+ "CE E4 771C",
+ "CE E5 7722",
+ "CE E6 7728",
+ "CE E7 772D",
+ "CE E8 772E",
+ "CE E9 772F",
+ "CE EA 7734",
+ "CE EB 7735",
+ "CE EC 7736",
+ "CE ED 7739",
+ "CE EE 773D",
+ "CE EF 773E",
+ "CE F0 7742",
+ "CE F1 7745",
+ "CE F2 7746",
+ "CE F3 774A",
+ "CE F4 774D",
+ "CE F5 774E",
+ "CE F6 774F",
+ "CE F7 7752",
+ "CE F8 7756",
+ "CE F9 7757",
+ "CE FA 775C",
+ "CE FB 775E",
+ "CE FC 775F",
+ "CE FD 7760",
+ "CE FE 7762",
+ "CF A1 7764",
+ "CF A2 7767",
+ "CF A3 776A",
+ "CF A4 776C",
+ "CF A5 7770",
+ "CF A6 7772",
+ "CF A7 7773",
+ "CF A8 7774",
+ "CF A9 777A",
+ "CF AA 777D",
+ "CF AB 7780",
+ "CF AC 7784",
+ "CF AD 778C",
+ "CF AE 778D",
+ "CF AF 7794",
+ "CF B0 7795",
+ "CF B1 7796",
+ "CF B2 779A",
+ "CF B3 779F",
+ "CF B4 77A2",
+ "CF B5 77A7",
+ "CF B6 77AA",
+ "CF B7 77AE",
+ "CF B8 77AF",
+ "CF B9 77B1",
+ "CF BA 77B5",
+ "CF BB 77BE",
+ "CF BC 77C3",
+ "CF BD 77C9",
+ "CF BE 77D1",
+ "CF BF 77D2",
+ "CF C0 77D5",
+ "CF C1 77D9",
+ "CF C2 77DE",
+ "CF C3 77DF",
+ "CF C4 77E0",
+ "CF C5 77E4",
+ "CF C6 77E6",
+ "CF C7 77EA",
+ "CF C8 77EC",
+ "CF C9 77F0",
+ "CF CA 77F1",
+ "CF CB 77F4",
+ "CF CC 77F8",
+ "CF CD 77FB",
+ "CF CE 7805",
+ "CF CF 7806",
+ "CF D0 7809",
+ "CF D1 780D",
+ "CF D2 780E",
+ "CF D3 7811",
+ "CF D4 781D",
+ "CF D5 7821",
+ "CF D6 7822",
+ "CF D7 7823",
+ "CF D8 782D",
+ "CF D9 782E",
+ "CF DA 7830",
+ "CF DB 7835",
+ "CF DC 7837",
+ "CF DD 7843",
+ "CF DE 7844",
+ "CF DF 7847",
+ "CF E0 7848",
+ "CF E1 784C",
+ "CF E2 784E",
+ "CF E3 7852",
+ "CF E4 785C",
+ "CF E5 785E",
+ "CF E6 7860",
+ "CF E7 7861",
+ "CF E8 7863",
+ "CF E9 7864",
+ "CF EA 7868",
+ "CF EB 786A",
+ "CF EC 786E",
+ "CF ED 787A",
+ "CF EE 787E",
+ "CF EF 788A",
+ "CF F0 788F",
+ "CF F1 7894",
+ "CF F2 7898",
+ "CF F4 789D",
+ "CF F5 789E",
+ "CF F6 789F",
+ "CF F3 78A1",
+ "CF F7 78A4",
+ "CF F8 78A8",
+ "CF F9 78AC",
+ "CF FA 78AD",
+ "CF FB 78B0",
+ "CF FC 78B1",
+ "CF FD 78B2",
+ "CF FE 78B3",
+ "D0 A1 78BB",
+ "D0 A2 78BD",
+ "D0 A3 78BF",
+ "D0 A4 78C7",
+ "D0 A5 78C8",
+ "D0 A6 78C9",
+ "D0 A7 78CC",
+ "D0 A8 78CE",
+ "D0 A9 78D2",
+ "D0 AA 78D3",
+ "D0 AB 78D5",
+ "D0 AC 78D6",
+ "D0 AE 78DB",
+ "D0 AF 78DF",
+ "D0 B0 78E0",
+ "D0 B1 78E1",
+ "D0 AD 78E4",
+ "D0 B2 78E6",
+ "D0 B3 78EA",
+ "D0 B4 78F2",
+ "D0 B5 78F3",
+ "D0 B7 78F6",
+ "D0 B8 78F7",
+ "D0 B9 78FA",
+ "D0 BA 78FB",
+ "D0 BB 78FF",
+ "D0 B6 7900",
+ "D0 BC 7906",
+ "D0 BD 790C",
+ "D0 BE 7910",
+ "D0 BF 791A",
+ "D0 C0 791C",
+ "D0 C1 791E",
+ "D0 C2 791F",
+ "D0 C3 7920",
+ "D0 C4 7925",
+ "D0 C5 7927",
+ "D0 C6 7929",
+ "D0 C7 792D",
+ "D0 C8 7931",
+ "D0 C9 7934",
+ "D0 CA 7935",
+ "D0 CB 793B",
+ "D0 CC 793D",
+ "D0 CD 793F",
+ "D0 CE 7944",
+ "D0 CF 7945",
+ "D0 D0 7946",
+ "D0 D1 794A",
+ "D0 D2 794B",
+ "D0 D3 794F",
+ "D0 D4 7951",
+ "D0 D5 7954",
+ "D0 D6 7958",
+ "D0 D7 795B",
+ "D0 D8 795C",
+ "D0 D9 7967",
+ "D0 DA 7969",
+ "D0 DB 796B",
+ "D0 DC 7972",
+ "D0 DD 7979",
+ "D0 DE 797B",
+ "D0 DF 797C",
+ "D0 E0 797E",
+ "D0 E1 798B",
+ "D0 E2 798C",
+ "D0 E3 7991",
+ "D0 E4 7993",
+ "D0 E5 7994",
+ "D0 E6 7995",
+ "D0 E7 7996",
+ "D0 E8 7998",
+ "D0 E9 799B",
+ "D0 EA 799C",
+ "D0 EB 79A1",
+ "D0 EC 79A8",
+ "D0 ED 79A9",
+ "D0 EE 79AB",
+ "D0 EF 79AF",
+ "D0 F0 79B1",
+ "D0 F1 79B4",
+ "D0 F2 79B8",
+ "D0 F3 79BB",
+ "D0 F4 79C2",
+ "D0 F5 79C4",
+ "D0 F6 79C7",
+ "D0 F7 79C8",
+ "D0 F8 79CA",
+ "D0 F9 79CF",
+ "D0 FA 79D4",
+ "D0 FB 79D6",
+ "D0 FC 79DA",
+ "D0 FD 79DD",
+ "D0 FE 79DE",
+ "D1 A1 79E0",
+ "D1 A2 79E2",
+ "D1 A3 79E5",
+ "D1 A4 79EA",
+ "D1 A5 79EB",
+ "D1 A6 79ED",
+ "D1 A7 79F1",
+ "D1 A8 79F8",
+ "D1 A9 79FC",
+ "D1 AA 7A02",
+ "D1 AB 7A03",
+ "D1 AC 7A07",
+ "D1 AD 7A09",
+ "D1 AE 7A0A",
+ "D1 AF 7A0C",
+ "D1 B0 7A11",
+ "D1 B1 7A15",
+ "D1 B2 7A1B",
+ "D1 B3 7A1E",
+ "D1 B4 7A21",
+ "D1 B5 7A27",
+ "D1 B6 7A2B",
+ "D1 B7 7A2D",
+ "D1 B8 7A2F",
+ "D1 B9 7A30",
+ "D1 BA 7A34",
+ "D1 BB 7A35",
+ "D1 BC 7A38",
+ "D1 BD 7A39",
+ "D1 BE 7A3A",
+ "D1 BF 7A44",
+ "D1 C0 7A45",
+ "D1 C1 7A47",
+ "D1 C2 7A48",
+ "D1 C3 7A4C",
+ "D1 C4 7A55",
+ "D1 C5 7A56",
+ "D1 C6 7A59",
+ "D1 C7 7A5C",
+ "D1 C8 7A5D",
+ "D1 C9 7A5F",
+ "D1 CA 7A60",
+ "D1 CB 7A65",
+ "D1 CC 7A67",
+ "D1 CD 7A6A",
+ "D1 CE 7A6D",
+ "D1 CF 7A75",
+ "D1 D0 7A78",
+ "D1 D1 7A7E",
+ "D1 D2 7A80",
+ "D1 D3 7A82",
+ "D1 D4 7A85",
+ "D1 D5 7A86",
+ "D1 D6 7A8A",
+ "D1 D7 7A8B",
+ "D1 D8 7A90",
+ "D1 D9 7A91",
+ "D1 DA 7A94",
+ "D1 DB 7A9E",
+ "D1 DC 7AA0",
+ "D1 DD 7AA3",
+ "D1 DE 7AAC",
+ "D1 DF 7AB3",
+ "D1 E0 7AB5",
+ "D1 E1 7AB9",
+ "D1 E2 7ABB",
+ "D1 E3 7ABC",
+ "D1 E4 7AC6",
+ "D1 E5 7AC9",
+ "D1 E6 7ACC",
+ "D1 E7 7ACE",
+ "D1 E8 7AD1",
+ "D1 E9 7ADB",
+ "D1 EA 7AE8",
+ "D1 EB 7AE9",
+ "D1 EC 7AEB",
+ "D1 ED 7AEC",
+ "D1 EE 7AF1",
+ "D1 EF 7AF4",
+ "D1 F0 7AFB",
+ "D1 F1 7AFD",
+ "D1 F2 7AFE",
+ "D1 F3 7B07",
+ "D1 F4 7B14",
+ "D1 F5 7B1F",
+ "D1 F6 7B23",
+ "D1 F7 7B27",
+ "D1 F8 7B29",
+ "D1 F9 7B2A",
+ "D1 FA 7B2B",
+ "D1 FB 7B2D",
+ "D1 FC 7B2E",
+ "D1 FD 7B2F",
+ "D1 FE 7B30",
+ "D2 A1 7B31",
+ "D2 A2 7B34",
+ "D2 A3 7B3D",
+ "D2 A4 7B3F",
+ "D2 A5 7B40",
+ "D2 A6 7B41",
+ "D2 A7 7B47",
+ "D2 A8 7B4E",
+ "D2 A9 7B55",
+ "D2 AA 7B60",
+ "D2 AB 7B64",
+ "D2 AC 7B66",
+ "D2 AD 7B69",
+ "D2 AE 7B6A",
+ "D2 AF 7B6D",
+ "D2 B0 7B6F",
+ "D2 B1 7B72",
+ "D2 B2 7B73",
+ "D2 B3 7B77",
+ "D2 B4 7B84",
+ "D2 B5 7B89",
+ "D2 B6 7B8E",
+ "D2 B7 7B90",
+ "D2 B8 7B91",
+ "D2 B9 7B96",
+ "D2 BA 7B9B",
+ "D2 BB 7B9E",
+ "D2 BC 7BA0",
+ "D2 BD 7BA5",
+ "D2 BE 7BAC",
+ "D2 BF 7BAF",
+ "D2 C0 7BB0",
+ "D2 C1 7BB2",
+ "D2 C2 7BB5",
+ "D2 C3 7BB6",
+ "D2 C4 7BBA",
+ "D2 C5 7BBB",
+ "D2 C6 7BBC",
+ "D2 C7 7BBD",
+ "D2 C8 7BC2",
+ "D2 C9 7BC5",
+ "D2 CA 7BC8",
+ "D2 CB 7BCA",
+ "D2 CC 7BD4",
+ "D2 CD 7BD6",
+ "D2 CE 7BD7",
+ "D2 CF 7BD9",
+ "D2 D0 7BDA",
+ "D2 D1 7BDB",
+ "D2 D2 7BE8",
+ "D2 D3 7BEA",
+ "D2 D4 7BF2",
+ "D2 D5 7BF4",
+ "D2 D6 7BF5",
+ "D2 D7 7BF8",
+ "D2 D8 7BF9",
+ "D2 D9 7BFA",
+ "D2 DA 7BFC",
+ "D2 DB 7BFE",
+ "D2 DC 7C01",
+ "D2 DD 7C02",
+ "D2 DE 7C03",
+ "D2 DF 7C04",
+ "D2 E0 7C06",
+ "D2 E1 7C09",
+ "D2 E2 7C0B",
+ "D2 E3 7C0C",
+ "D2 E4 7C0E",
+ "D2 E5 7C0F",
+ "D2 E6 7C19",
+ "D2 E7 7C1B",
+ "D2 E8 7C20",
+ "D2 E9 7C25",
+ "D2 EA 7C26",
+ "D2 EB 7C28",
+ "D2 EC 7C2C",
+ "D2 ED 7C31",
+ "D2 EE 7C33",
+ "D2 EF 7C34",
+ "D2 F0 7C36",
+ "D2 F1 7C39",
+ "D2 F2 7C3A",
+ "D2 F3 7C46",
+ "D2 F4 7C4A",
+ "D2 F6 7C51",
+ "D2 F7 7C52",
+ "D2 F8 7C53",
+ "D2 F5 7C55",
+ "D2 F9 7C59",
+ "D2 FA 7C5A",
+ "D2 FB 7C5B",
+ "D2 FC 7C5C",
+ "D2 FD 7C5D",
+ "D2 FE 7C5E",
+ "D3 A1 7C61",
+ "D3 A2 7C63",
+ "D3 A3 7C67",
+ "D3 A4 7C69",
+ "D3 A5 7C6D",
+ "D3 A6 7C6E",
+ "D3 A7 7C70",
+ "D3 A8 7C72",
+ "D3 A9 7C79",
+ "D3 AA 7C7C",
+ "D3 AB 7C7D",
+ "D3 AC 7C86",
+ "D3 AD 7C87",
+ "D3 AE 7C8F",
+ "D3 AF 7C94",
+ "D3 B0 7C9E",
+ "D3 B1 7CA0",
+ "D3 B2 7CA6",
+ "D3 B3 7CB0",
+ "D3 B4 7CB6",
+ "D3 B5 7CB7",
+ "D3 B6 7CBA",
+ "D3 B7 7CBB",
+ "D3 B8 7CBC",
+ "D3 B9 7CBF",
+ "D3 BA 7CC4",
+ "D3 BB 7CC7",
+ "D3 BC 7CC8",
+ "D3 BD 7CC9",
+ "D3 BE 7CCD",
+ "D3 BF 7CCF",
+ "D3 C0 7CD3",
+ "D3 C1 7CD4",
+ "D3 C2 7CD5",
+ "D3 C3 7CD7",
+ "D3 C4 7CD9",
+ "D3 C5 7CDA",
+ "D3 C6 7CDD",
+ "D3 C7 7CE6",
+ "D3 C8 7CE9",
+ "D3 C9 7CEB",
+ "D3 CA 7CF5",
+ "D3 CB 7D03",
+ "D3 CC 7D07",
+ "D3 CD 7D08",
+ "D3 CE 7D09",
+ "D3 CF 7D0F",
+ "D3 D0 7D11",
+ "D3 D1 7D12",
+ "D3 D2 7D13",
+ "D3 D3 7D16",
+ "D3 D4 7D1D",
+ "D3 D5 7D1E",
+ "D3 D6 7D23",
+ "D3 D7 7D26",
+ "D3 D8 7D2A",
+ "D3 D9 7D2D",
+ "D3 DA 7D31",
+ "D3 DB 7D3C",
+ "D3 DC 7D3D",
+ "D3 DD 7D3E",
+ "D3 DE 7D40",
+ "D3 DF 7D41",
+ "D3 E0 7D47",
+ "D3 E1 7D48",
+ "D3 E2 7D4D",
+ "D3 E3 7D51",
+ "D3 E4 7D53",
+ "D3 E5 7D57",
+ "D3 E6 7D59",
+ "D3 E7 7D5A",
+ "D3 E8 7D5C",
+ "D3 E9 7D5D",
+ "D3 EA 7D65",
+ "D3 EB 7D67",
+ "D3 EC 7D6A",
+ "D3 ED 7D70",
+ "D3 EE 7D78",
+ "D3 EF 7D7A",
+ "D3 F0 7D7B",
+ "D3 F1 7D7F",
+ "D3 F2 7D81",
+ "D3 F3 7D82",
+ "D3 F4 7D83",
+ "D3 F5 7D85",
+ "D3 F6 7D86",
+ "D3 F7 7D88",
+ "D3 F8 7D8B",
+ "D3 F9 7D8C",
+ "D3 FA 7D8D",
+ "D3 FB 7D91",
+ "D3 FC 7D96",
+ "D3 FD 7D97",
+ "D3 FE 7D9D",
+ "D4 A1 7D9E",
+ "D4 A2 7DA6",
+ "D4 A3 7DA7",
+ "D4 A4 7DAA",
+ "D4 A5 7DB3",
+ "D4 A6 7DB6",
+ "D4 A7 7DB7",
+ "D4 A8 7DB9",
+ "D4 A9 7DC2",
+ "D4 AA 7DC3",
+ "D4 AB 7DC4",
+ "D4 AC 7DC5",
+ "D4 AD 7DC6",
+ "D4 AE 7DCC",
+ "D4 AF 7DCD",
+ "D4 B0 7DCE",
+ "D4 B1 7DD7",
+ "D4 B2 7DD9",
+ "D4 B4 7DE2",
+ "D4 B5 7DE5",
+ "D4 B6 7DE6",
+ "D4 B7 7DEA",
+ "D4 B8 7DEB",
+ "D4 B9 7DED",
+ "D4 BA 7DF1",
+ "D4 BB 7DF5",
+ "D4 BC 7DF6",
+ "D4 BD 7DF9",
+ "D4 BE 7DFA",
+ "D4 B3 7E00",
+ "D4 BF 7E08",
+ "D4 C0 7E10",
+ "D4 C1 7E11",
+ "D4 C2 7E15",
+ "D4 C3 7E17",
+ "D4 C4 7E1C",
+ "D4 C5 7E1D",
+ "D4 C6 7E20",
+ "D4 C7 7E27",
+ "D4 C8 7E28",
+ "D4 C9 7E2C",
+ "D4 CA 7E2D",
+ "D4 CB 7E2F",
+ "D4 CC 7E33",
+ "D4 CD 7E36",
+ "D4 CE 7E3F",
+ "D4 CF 7E44",
+ "D4 D0 7E45",
+ "D4 D1 7E47",
+ "D4 D2 7E4E",
+ "D4 D3 7E50",
+ "D4 D4 7E52",
+ "D4 D5 7E58",
+ "D4 D6 7E5F",
+ "D4 D7 7E61",
+ "D4 D8 7E62",
+ "D4 D9 7E65",
+ "D4 DA 7E6B",
+ "D4 DB 7E6E",
+ "D4 DC 7E6F",
+ "D4 DD 7E73",
+ "D4 DE 7E78",
+ "D4 DF 7E7E",
+ "D4 E0 7E81",
+ "D4 E1 7E86",
+ "D4 E2 7E87",
+ "D4 E3 7E8A",
+ "D4 E4 7E8D",
+ "D4 E5 7E91",
+ "D4 E6 7E95",
+ "D4 E7 7E98",
+ "D4 E8 7E9A",
+ "D4 E9 7E9D",
+ "D4 EA 7E9E",
+ "D4 EC 7F3B",
+ "D4 EB 7F3C",
+ "D4 ED 7F3D",
+ "D4 EE 7F3E",
+ "D4 EF 7F3F",
+ "D4 F0 7F43",
+ "D4 F1 7F44",
+ "D4 F2 7F47",
+ "D4 F3 7F4F",
+ "D4 F4 7F52",
+ "D4 F5 7F53",
+ "D4 F6 7F5B",
+ "D4 F7 7F5C",
+ "D4 F8 7F5D",
+ "D4 F9 7F61",
+ "D4 FA 7F63",
+ "D4 FB 7F64",
+ "D4 FC 7F65",
+ "D4 FD 7F66",
+ "D4 FE 7F6D",
+ "D5 A1 7F71",
+ "D5 A2 7F7D",
+ "D5 A3 7F7E",
+ "D5 A4 7F7F",
+ "D5 A5 7F80",
+ "D5 A6 7F8B",
+ "D5 A7 7F8D",
+ "D5 A8 7F8F",
+ "D5 A9 7F90",
+ "D5 AA 7F91",
+ "D5 AB 7F96",
+ "D5 AC 7F97",
+ "D5 AD 7F9C",
+ "D5 AE 7FA1",
+ "D5 AF 7FA2",
+ "D5 B0 7FA6",
+ "D5 B1 7FAA",
+ "D5 B2 7FAD",
+ "D5 B3 7FB4",
+ "D5 B4 7FBC",
+ "D5 B5 7FBF",
+ "D5 B6 7FC0",
+ "D5 B7 7FC3",
+ "D5 B8 7FC8",
+ "D5 B9 7FCE",
+ "D5 BA 7FCF",
+ "D5 BB 7FDB",
+ "D5 BC 7FDF",
+ "D5 BD 7FE3",
+ "D5 BE 7FE5",
+ "D5 BF 7FE8",
+ "D5 C0 7FEC",
+ "D5 C1 7FEE",
+ "D5 C2 7FEF",
+ "D5 C3 7FF2",
+ "D5 C4 7FFA",
+ "D5 C5 7FFD",
+ "D5 C6 7FFE",
+ "D5 C7 7FFF",
+ "D5 C8 8007",
+ "D5 C9 8008",
+ "D5 CA 800A",
+ "D5 CB 800D",
+ "D5 CC 800E",
+ "D5 CD 800F",
+ "D5 CE 8011",
+ "D5 CF 8013",
+ "D5 D0 8014",
+ "D5 D1 8016",
+ "D5 D2 801D",
+ "D5 D3 801E",
+ "D5 D4 801F",
+ "D5 D5 8020",
+ "D5 D6 8024",
+ "D5 D7 8026",
+ "D5 D8 802C",
+ "D5 D9 802E",
+ "D5 DA 8030",
+ "D5 DB 8034",
+ "D5 DC 8035",
+ "D5 DD 8037",
+ "D5 DE 8039",
+ "D5 DF 803A",
+ "D5 E0 803C",
+ "D5 E1 803E",
+ "D5 E2 8040",
+ "D5 E3 8044",
+ "D5 E4 8060",
+ "D5 E5 8064",
+ "D5 E6 8066",
+ "D5 E7 806D",
+ "D5 E8 8071",
+ "D5 E9 8075",
+ "D5 EA 8081",
+ "D5 EB 8088",
+ "D5 EC 808E",
+ "D5 ED 809C",
+ "D5 EE 809E",
+ "D5 EF 80A6",
+ "D5 F0 80A7",
+ "D5 F1 80AB",
+ "D5 F2 80B8",
+ "D5 F3 80B9",
+ "D5 F4 80C8",
+ "D5 F5 80CD",
+ "D5 F6 80CF",
+ "D5 F7 80D2",
+ "D5 F8 80D4",
+ "D5 F9 80D5",
+ "D5 FA 80D7",
+ "D5 FB 80D8",
+ "D5 FC 80E0",
+ "D5 FD 80ED",
+ "D5 FE 80EE",
+ "D6 A1 80F0",
+ "D6 A2 80F2",
+ "D6 A3 80F3",
+ "D6 A4 80F6",
+ "D6 A5 80F9",
+ "D6 A6 80FA",
+ "D6 A7 80FE",
+ "D6 A8 8103",
+ "D6 A9 810B",
+ "D6 AA 8116",
+ "D6 AB 8117",
+ "D6 AC 8118",
+ "D6 AD 811C",
+ "D6 AE 811E",
+ "D6 AF 8120",
+ "D6 B0 8124",
+ "D6 B1 8127",
+ "D6 B2 812C",
+ "D6 B3 8130",
+ "D6 B4 8135",
+ "D6 B5 813A",
+ "D6 B6 813C",
+ "D6 B7 8145",
+ "D6 B8 8147",
+ "D6 B9 814A",
+ "D6 BA 814C",
+ "D6 BB 8152",
+ "D6 BC 8157",
+ "D6 BD 8160",
+ "D6 BE 8161",
+ "D6 BF 8167",
+ "D6 C0 8168",
+ "D6 C1 8169",
+ "D6 C2 816D",
+ "D6 C3 816F",
+ "D6 C4 8177",
+ "D6 C5 8181",
+ "D6 C7 8184",
+ "D6 C8 8185",
+ "D6 C9 8186",
+ "D6 CA 818B",
+ "D6 CB 818E",
+ "D6 C6 8190",
+ "D6 CC 8196",
+ "D6 CD 8198",
+ "D6 CE 819B",
+ "D6 CF 819E",
+ "D6 D0 81A2",
+ "D6 D1 81AE",
+ "D6 D2 81B2",
+ "D6 D3 81B4",
+ "D6 D4 81BB",
+ "D6 D6 81C3",
+ "D6 D7 81C5",
+ "D6 D8 81CA",
+ "D6 D5 81CB",
+ "D6 D9 81CE",
+ "D6 DA 81CF",
+ "D6 DB 81D5",
+ "D6 DC 81D7",
+ "D6 DD 81DB",
+ "D6 DE 81DD",
+ "D6 DF 81DE",
+ "D6 E0 81E1",
+ "D6 E1 81E4",
+ "D6 E2 81EB",
+ "D6 E3 81EC",
+ "D6 E4 81F0",
+ "D6 E5 81F1",
+ "D6 E6 81F2",
+ "D6 E7 81F5",
+ "D6 E8 81F6",
+ "D6 E9 81F8",
+ "D6 EA 81F9",
+ "D6 EB 81FD",
+ "D6 EC 81FF",
+ "D6 ED 8200",
+ "D6 EE 8203",
+ "D6 EF 820F",
+ "D6 F0 8213",
+ "D6 F1 8214",
+ "D6 F2 8219",
+ "D6 F3 821A",
+ "D6 F4 821D",
+ "D6 F5 8221",
+ "D6 F6 8222",
+ "D6 F7 8228",
+ "D6 F8 8232",
+ "D6 F9 8234",
+ "D6 FA 823A",
+ "D6 FB 8243",
+ "D6 FC 8244",
+ "D6 FD 8245",
+ "D6 FE 8246",
+ "D7 A1 824B",
+ "D7 A2 824E",
+ "D7 A3 824F",
+ "D7 A4 8251",
+ "D7 A5 8256",
+ "D7 A6 825C",
+ "D7 A7 8260",
+ "D7 A8 8263",
+ "D7 A9 8267",
+ "D7 AA 826D",
+ "D7 AB 8274",
+ "D7 AC 827B",
+ "D7 AD 827D",
+ "D7 AE 827F",
+ "D7 AF 8280",
+ "D7 B0 8281",
+ "D7 B1 8283",
+ "D7 B2 8284",
+ "D7 B3 8287",
+ "D7 B4 8289",
+ "D7 B5 828A",
+ "D7 B6 828E",
+ "D7 B7 8291",
+ "D7 B8 8294",
+ "D7 B9 8296",
+ "D7 BA 8298",
+ "D7 BB 829A",
+ "D7 BC 829B",
+ "D7 BD 82A0",
+ "D7 BE 82A1",
+ "D7 BF 82A3",
+ "D7 C0 82A4",
+ "D7 C1 82A7",
+ "D7 C2 82A8",
+ "D7 C3 82A9",
+ "D7 C4 82AA",
+ "D7 C5 82AE",
+ "D7 C6 82B0",
+ "D7 C7 82B2",
+ "D7 C8 82B4",
+ "D7 C9 82B7",
+ "D7 CA 82BA",
+ "D7 CB 82BC",
+ "D7 CC 82BE",
+ "D7 CD 82BF",
+ "D7 CE 82C6",
+ "D7 CF 82D0",
+ "D7 D0 82D5",
+ "D7 D1 82DA",
+ "D7 D2 82E0",
+ "D7 D3 82E2",
+ "D7 D4 82E4",
+ "D7 D5 82E8",
+ "D7 D6 82EA",
+ "D7 D7 82ED",
+ "D7 D8 82EF",
+ "D7 D9 82F6",
+ "D7 DA 82F7",
+ "D7 DB 82FD",
+ "D7 DC 82FE",
+ "D7 DD 8300",
+ "D7 DE 8301",
+ "D7 DF 8307",
+ "D7 E0 8308",
+ "D7 E1 830A",
+ "D7 E2 830B",
+ "D7 E4 831B",
+ "D7 E5 831D",
+ "D7 E6 831E",
+ "D7 E7 831F",
+ "D7 E8 8321",
+ "D7 E9 8322",
+ "D7 EA 832C",
+ "D7 EB 832D",
+ "D7 EC 832E",
+ "D7 ED 8330",
+ "D7 EE 8333",
+ "D7 EF 8337",
+ "D7 F0 833A",
+ "D7 F1 833C",
+ "D7 F2 833D",
+ "D7 F3 8342",
+ "D7 F4 8343",
+ "D7 F5 8344",
+ "D7 F6 8347",
+ "D7 F7 834D",
+ "D7 F8 834E",
+ "D7 F9 8351",
+ "D8 BE 8353",
+ "D7 E3 8354",
+ "D7 FA 8355",
+ "D7 FB 8356",
+ "D7 FC 8357",
+ "D7 FD 8370",
+ "D7 FE 8378",
+ "D8 A1 837D",
+ "D8 A2 837F",
+ "D8 A3 8380",
+ "D8 A4 8382",
+ "D8 A5 8384",
+ "D8 A6 8386",
+ "D8 A7 838D",
+ "D8 A8 8392",
+ "D8 A9 8394",
+ "D8 AA 8395",
+ "D8 AB 8398",
+ "D8 AC 8399",
+ "D8 AD 839B",
+ "D8 AE 839C",
+ "D8 AF 839D",
+ "D8 B0 83A6",
+ "D8 B1 83A7",
+ "D8 B2 83A9",
+ "D8 B3 83AC",
+ "D8 CC 83AD",
+ "D8 B4 83BE",
+ "D8 B5 83BF",
+ "D8 B6 83C0",
+ "D8 B7 83C7",
+ "D8 B8 83C9",
+ "D8 B9 83CF",
+ "D8 BA 83D0",
+ "D8 BB 83D1",
+ "D8 BC 83D4",
+ "D8 BD 83DD",
+ "D8 BF 83E8",
+ "D8 C0 83EA",
+ "D8 C1 83F6",
+ "D8 C2 83F8",
+ "D8 C3 83F9",
+ "D8 C4 83FC",
+ "D8 C5 8401",
+ "D8 C6 8406",
+ "D8 C7 840A",
+ "D8 C8 840F",
+ "D8 C9 8411",
+ "D8 CA 8415",
+ "D8 CB 8419",
+ "D8 CD 842F",
+ "D8 CE 8439",
+ "D8 CF 8445",
+ "D8 D0 8447",
+ "D8 D1 8448",
+ "D8 D2 844A",
+ "D8 D3 844D",
+ "D8 D4 844F",
+ "D8 D5 8451",
+ "D8 D6 8452",
+ "D8 D7 8456",
+ "D8 D8 8458",
+ "D8 D9 8459",
+ "D8 DA 845A",
+ "D8 DB 845C",
+ "D8 DC 8460",
+ "D8 DD 8464",
+ "D8 DE 8465",
+ "D8 DF 8467",
+ "D8 E0 846A",
+ "D8 E1 8470",
+ "D8 E2 8473",
+ "D8 E3 8474",
+ "D8 E4 8476",
+ "D8 E5 8478",
+ "D8 E6 847C",
+ "D8 E7 847D",
+ "D8 E8 8481",
+ "D8 E9 8485",
+ "D8 EA 8492",
+ "D8 EB 8493",
+ "D8 EC 8495",
+ "D8 ED 849E",
+ "D8 EE 84A6",
+ "D8 EF 84A8",
+ "D8 F0 84A9",
+ "D8 F1 84AA",
+ "D8 F2 84AF",
+ "D8 F3 84B1",
+ "D8 F4 84B4",
+ "D8 F5 84BA",
+ "D8 F6 84BD",
+ "D8 F7 84BE",
+ "D8 F8 84C0",
+ "D8 F9 84C2",
+ "D8 FA 84C7",
+ "D8 FB 84C8",
+ "D8 FC 84CC",
+ "D8 FD 84CF",
+ "D8 FE 84D3",
+ "D9 A1 84DC",
+ "D9 A2 84E7",
+ "D9 A3 84EA",
+ "D9 A4 84EF",
+ "D9 A5 84F0",
+ "D9 A6 84F1",
+ "D9 A7 84F2",
+ "D9 A8 84F7",
+ "D9 AA 84FA",
+ "D9 AB 84FB",
+ "D9 AC 84FD",
+ "D9 AD 8502",
+ "D9 AE 8503",
+ "D9 AF 8507",
+ "D9 B0 850C",
+ "D9 B1 850E",
+ "D9 B2 8510",
+ "D9 B3 851C",
+ "D9 B4 851E",
+ "D9 B5 8522",
+ "D9 B6 8523",
+ "D9 B7 8524",
+ "D9 B8 8525",
+ "D9 B9 8527",
+ "D9 BA 852A",
+ "D9 BB 852B",
+ "D9 BC 852F",
+ "D9 A9 8532",
+ "D9 BD 8533",
+ "D9 BE 8534",
+ "D9 BF 8536",
+ "D9 C0 853F",
+ "D9 C1 8546",
+ "D9 C2 854F",
+ "D9 C3 8550",
+ "D9 C4 8551",
+ "D9 C5 8552",
+ "D9 C6 8553",
+ "D9 C7 8556",
+ "D9 C8 8559",
+ "D9 C9 855C",
+ "D9 CA 855D",
+ "D9 CB 855E",
+ "D9 CC 855F",
+ "D9 CD 8560",
+ "D9 CE 8561",
+ "D9 CF 8562",
+ "D9 D0 8564",
+ "D9 D1 856B",
+ "D9 D2 856F",
+ "D9 D3 8579",
+ "D9 D4 857A",
+ "D9 D5 857B",
+ "D9 D6 857D",
+ "D9 D7 857F",
+ "D9 D8 8581",
+ "D9 D9 8585",
+ "D9 DA 8586",
+ "D9 DB 8589",
+ "D9 DC 858B",
+ "D9 DD 858C",
+ "D9 DE 858F",
+ "D9 DF 8593",
+ "D9 E0 8598",
+ "D9 E1 859D",
+ "D9 E2 859F",
+ "D9 E3 85A0",
+ "D9 E4 85A2",
+ "D9 E5 85A5",
+ "D9 E6 85A7",
+ "D9 F4 85AD",
+ "D9 E7 85B4",
+ "D9 E8 85B6",
+ "D9 E9 85B7",
+ "D9 EA 85B8",
+ "D9 EB 85BC",
+ "D9 EC 85BD",
+ "D9 ED 85BE",
+ "D9 EE 85BF",
+ "D9 EF 85C2",
+ "D9 F0 85C7",
+ "D9 F1 85CA",
+ "D9 F2 85CB",
+ "D9 F3 85CE",
+ "D9 F5 85D8",
+ "D9 F6 85DA",
+ "D9 F7 85DF",
+ "D9 F8 85E0",
+ "D9 F9 85E6",
+ "D9 FA 85E8",
+ "D9 FB 85ED",
+ "D9 FC 85F3",
+ "D9 FD 85F6",
+ "D9 FE 85FC",
+ "DA A1 85FF",
+ "DA A2 8600",
+ "DA A3 8604",
+ "DA A4 8605",
+ "DA A5 860D",
+ "DA A6 860E",
+ "DA A7 8610",
+ "DA A8 8611",
+ "DA A9 8612",
+ "DA AA 8618",
+ "DA AB 8619",
+ "DA AC 861B",
+ "DA AD 861E",
+ "DA AE 8621",
+ "DA AF 8627",
+ "DA B0 8629",
+ "DA B1 8636",
+ "DA B2 8638",
+ "DA B3 863A",
+ "DA B4 863C",
+ "DA B5 863D",
+ "DA B6 8640",
+ "B8 E6 8641",
+ "DA B7 8642",
+ "DA B8 8646",
+ "DA B9 8652",
+ "DA BA 8653",
+ "DA BB 8656",
+ "DA BC 8657",
+ "DA BD 8658",
+ "DA BE 8659",
+ "DA BF 865D",
+ "DA C0 8660",
+ "DA C1 8661",
+ "DA C2 8662",
+ "DA C3 8663",
+ "DA C4 8664",
+ "DA C5 8669",
+ "DA C6 866C",
+ "DA C7 866F",
+ "DA C8 8675",
+ "DA C9 8676",
+ "DA CA 8677",
+ "DA CB 867A",
+ "DA ED 8688",
+ "DA CC 868D",
+ "DA CD 8691",
+ "DA CE 8696",
+ "DA CF 8698",
+ "DA D0 869A",
+ "DA D1 869C",
+ "DA D2 86A1",
+ "DA D3 86A6",
+ "DA D4 86A7",
+ "DA D5 86A8",
+ "DA D6 86AD",
+ "DA D7 86B1",
+ "DA D8 86B3",
+ "DA D9 86B4",
+ "DA DA 86B5",
+ "DA DB 86B7",
+ "DA DC 86B8",
+ "DA DD 86B9",
+ "DA DE 86BF",
+ "DA DF 86C0",
+ "DA E0 86C1",
+ "DA E1 86C3",
+ "DA E2 86C5",
+ "DA E3 86D1",
+ "DA E4 86D2",
+ "DA E5 86D5",
+ "DA E6 86D7",
+ "DA E7 86DA",
+ "DA E8 86DC",
+ "DA E9 86E0",
+ "DA EA 86E3",
+ "DA EB 86E5",
+ "DA EC 86E7",
+ "DA EE 86FA",
+ "DA EF 86FC",
+ "DA F0 86FD",
+ "DA F1 8704",
+ "DA F2 8705",
+ "DA F3 8707",
+ "DA F4 870B",
+ "DA F5 870E",
+ "DA F6 870F",
+ "DA F7 8710",
+ "DA F8 8713",
+ "DA F9 8714",
+ "DA FA 8719",
+ "DA FB 871E",
+ "DA FC 871F",
+ "DA FD 8721",
+ "DA FE 8723",
+ "DB A1 8728",
+ "DB A2 872E",
+ "DB A3 872F",
+ "DB A4 8731",
+ "DB A5 8732",
+ "DB A6 8739",
+ "DB A7 873A",
+ "DB A8 873C",
+ "DB A9 873D",
+ "DB AA 873E",
+ "DB AB 8740",
+ "DB AC 8743",
+ "DB AD 8745",
+ "DB AE 874D",
+ "DB AF 8758",
+ "DB B0 875D",
+ "DB B1 8761",
+ "DB B2 8764",
+ "DB B3 8765",
+ "DB B4 876F",
+ "DB B5 8771",
+ "DB B6 8772",
+ "DB B7 877B",
+ "DB B8 8783",
+ "DB B9 8784",
+ "DB BA 8785",
+ "DB BB 8786",
+ "DB BC 8787",
+ "DB BD 8788",
+ "DB BE 8789",
+ "DB BF 878B",
+ "DB C0 878C",
+ "DB C1 8790",
+ "DB C2 8793",
+ "DB C3 8795",
+ "DB C4 8797",
+ "DB C5 8798",
+ "DB C6 8799",
+ "DB C7 879E",
+ "DB C8 87A0",
+ "DB C9 87A3",
+ "DB CA 87A7",
+ "DB CB 87AC",
+ "DB CC 87AD",
+ "DB CD 87AE",
+ "DB CE 87B1",
+ "DB CF 87B5",
+ "DB D0 87BE",
+ "DB D1 87BF",
+ "DB D2 87C1",
+ "DB D3 87C8",
+ "DB D4 87C9",
+ "DB D5 87CA",
+ "DB D6 87CE",
+ "DB D7 87D5",
+ "DB D8 87D6",
+ "DB D9 87D9",
+ "DB DA 87DA",
+ "DB DB 87DC",
+ "DB DC 87DF",
+ "DB DD 87E2",
+ "DB DE 87E3",
+ "DB DF 87E4",
+ "DB E0 87EA",
+ "DB E1 87EB",
+ "DB E2 87ED",
+ "DB E3 87F1",
+ "DB E4 87F3",
+ "DB E5 87F8",
+ "DB E6 87FA",
+ "DB E7 87FF",
+ "DB E8 8801",
+ "DB E9 8803",
+ "DB EA 8806",
+ "DB EB 8809",
+ "DB EC 880A",
+ "DB ED 880B",
+ "DB EE 8810",
+ "DB F0 8812",
+ "DB F1 8813",
+ "DB F2 8814",
+ "DB F3 8818",
+ "DB EF 8819",
+ "DB F4 881A",
+ "DB F5 881B",
+ "DB F6 881C",
+ "DB F7 881E",
+ "DB F8 881F",
+ "DB F9 8828",
+ "DB FA 882D",
+ "DB FB 882E",
+ "DB FC 8830",
+ "DB FD 8832",
+ "DB FE 8835",
+ "DC A1 883A",
+ "DC A2 883C",
+ "DC A3 8841",
+ "DC A4 8843",
+ "DC A5 8845",
+ "DC A6 8848",
+ "DC A7 8849",
+ "DC A8 884A",
+ "DC A9 884B",
+ "DC AA 884E",
+ "DC AB 8851",
+ "DC AC 8855",
+ "DC AD 8856",
+ "DC AE 8858",
+ "DC AF 885A",
+ "DC B0 885C",
+ "DC B1 885F",
+ "DC B2 8860",
+ "DC B3 8864",
+ "DC B4 8869",
+ "DC B5 8871",
+ "DC B6 8879",
+ "DC B7 887B",
+ "DC B8 8880",
+ "DC B9 8898",
+ "DC BA 889A",
+ "DC BB 889B",
+ "DC BC 889C",
+ "DC BD 889F",
+ "DC BE 88A0",
+ "DC BF 88A8",
+ "DC C0 88AA",
+ "DC C1 88BA",
+ "DC C2 88BD",
+ "DC C3 88BE",
+ "DC C4 88C0",
+ "DC C5 88CA",
+ "DC C6 88CB",
+ "DC C7 88CC",
+ "DC C8 88CD",
+ "DC C9 88CE",
+ "DC CA 88D1",
+ "DC CB 88D2",
+ "DC CC 88D3",
+ "DC CD 88DB",
+ "DC CE 88DE",
+ "DC CF 88E7",
+ "DC D0 88EF",
+ "DC D1 88F0",
+ "DC D2 88F1",
+ "DC D3 88F5",
+ "DC D4 88F7",
+ "DC D5 8901",
+ "DC D6 8906",
+ "DC D7 890D",
+ "DC D8 890E",
+ "DC D9 890F",
+ "DC DA 8915",
+ "DC DB 8916",
+ "DC DC 8918",
+ "DC DD 8919",
+ "DC DE 891A",
+ "DC DF 891C",
+ "DC E0 8920",
+ "DC E1 8926",
+ "DC E2 8927",
+ "DC E3 8928",
+ "DC E4 8930",
+ "DC E5 8931",
+ "DC E6 8932",
+ "DC E7 8935",
+ "DC E8 8939",
+ "DC E9 893A",
+ "DC EA 893E",
+ "DC EB 8940",
+ "DC EC 8942",
+ "DC ED 8945",
+ "DC EE 8946",
+ "DC EF 8949",
+ "DC F0 894F",
+ "DC F1 8952",
+ "DC F2 8957",
+ "DC F3 895A",
+ "DC F4 895B",
+ "DC F5 895C",
+ "DC F6 8961",
+ "DC F7 8962",
+ "DC F8 8963",
+ "DC F9 896B",
+ "DC FA 896E",
+ "DC FB 8970",
+ "DC FC 8973",
+ "DC FD 8975",
+ "DC FE 897A",
+ "DD A1 897B",
+ "DD A2 897C",
+ "DD A3 897D",
+ "DD A4 8989",
+ "DD A5 898D",
+ "DD A6 8990",
+ "DD A7 8994",
+ "DD A8 8995",
+ "DD A9 899B",
+ "DD AA 899C",
+ "DD AB 899F",
+ "DD AC 89A0",
+ "DD AD 89A5",
+ "DD AE 89B0",
+ "DD AF 89B4",
+ "DD B0 89B5",
+ "DD B1 89B6",
+ "DD B2 89B7",
+ "DD B3 89BC",
+ "DD B4 89D4",
+ "DD B5 89D5",
+ "DD B6 89D6",
+ "DD B7 89D7",
+ "DD B8 89D8",
+ "DD B9 89E5",
+ "DD BA 89E9",
+ "DD BB 89EB",
+ "DD BC 89ED",
+ "DD BD 89F1",
+ "DD BE 89F3",
+ "DD BF 89F6",
+ "DD C0 89F9",
+ "DD C1 89FD",
+ "DD C2 89FF",
+ "DD C3 8A04",
+ "DD C4 8A05",
+ "DD C5 8A07",
+ "DD C6 8A0F",
+ "DD C7 8A11",
+ "DD C8 8A12",
+ "DD C9 8A14",
+ "DD CA 8A15",
+ "DD CB 8A1E",
+ "DD CC 8A20",
+ "DD CD 8A22",
+ "DD CE 8A24",
+ "DD CF 8A26",
+ "DD D0 8A2B",
+ "DD D1 8A2C",
+ "DD D2 8A2F",
+ "DD D3 8A35",
+ "DD D4 8A37",
+ "DD D5 8A3D",
+ "DD D6 8A3E",
+ "DD D7 8A40",
+ "DD D8 8A43",
+ "DD D9 8A45",
+ "DD DA 8A47",
+ "DD DB 8A49",
+ "DD DC 8A4D",
+ "DD DD 8A4E",
+ "DD DE 8A53",
+ "DD DF 8A56",
+ "DD E0 8A57",
+ "DD E1 8A58",
+ "DD E2 8A5C",
+ "DD E3 8A5D",
+ "DD E4 8A61",
+ "DD E5 8A65",
+ "DD E6 8A67",
+ "DD E7 8A75",
+ "DD E8 8A76",
+ "DD E9 8A77",
+ "DD EA 8A79",
+ "DD EB 8A7A",
+ "DD EC 8A7B",
+ "DD ED 8A7E",
+ "DD EE 8A7F",
+ "DD EF 8A80",
+ "DD F0 8A83",
+ "DD F1 8A86",
+ "DD F2 8A8B",
+ "DD F3 8A8F",
+ "DD F4 8A90",
+ "DD F5 8A92",
+ "DD F6 8A96",
+ "DD F7 8A97",
+ "DD F8 8A99",
+ "DD F9 8A9F",
+ "DD FA 8AA7",
+ "DD FB 8AA9",
+ "DD FC 8AAE",
+ "DD FD 8AAF",
+ "DD FE 8AB3",
+ "DE A1 8AB6",
+ "DE A2 8AB7",
+ "DE A3 8ABB",
+ "DE A4 8ABE",
+ "DE A5 8AC3",
+ "DE A6 8AC6",
+ "DE A7 8AC8",
+ "DE A8 8AC9",
+ "DE A9 8ACA",
+ "DE AA 8AD1",
+ "DE AB 8AD3",
+ "DE AC 8AD4",
+ "DE AD 8AD5",
+ "DE AE 8AD7",
+ "DE AF 8ADD",
+ "DE B0 8ADF",
+ "DE B1 8AEC",
+ "DE B2 8AF0",
+ "DE B3 8AF4",
+ "DE B4 8AF5",
+ "DE B5 8AF6",
+ "DE B6 8AFC",
+ "DE B7 8AFF",
+ "DE B8 8B05",
+ "DE B9 8B06",
+ "DE BF 8B0A",
+ "DE BA 8B0B",
+ "DE BB 8B11",
+ "DE BC 8B1C",
+ "DE BD 8B1E",
+ "DE BE 8B1F",
+ "DE C0 8B2D",
+ "DE C1 8B30",
+ "DE C2 8B37",
+ "DE C3 8B3C",
+ "DE C4 8B42",
+ "DE C5 8B43",
+ "DE C6 8B44",
+ "DE C7 8B45",
+ "DE C8 8B46",
+ "DE C9 8B48",
+ "DE CE 8B4D",
+ "DE CA 8B52",
+ "DE CB 8B53",
+ "DE CC 8B54",
+ "DE CD 8B59",
+ "DE CF 8B5E",
+ "DE D0 8B63",
+ "DE D1 8B6D",
+ "DE D2 8B76",
+ "DE D3 8B78",
+ "DE D4 8B79",
+ "DE D5 8B7C",
+ "DE D6 8B7E",
+ "DE D7 8B81",
+ "DE D8 8B84",
+ "DE D9 8B85",
+ "DE DA 8B8B",
+ "DE DB 8B8D",
+ "DE DC 8B8F",
+ "DE DD 8B94",
+ "DE DE 8B95",
+ "DE DF 8B9C",
+ "DE E0 8B9E",
+ "DE E1 8B9F",
+ "DE E2 8C38",
+ "DE E3 8C39",
+ "DE E4 8C3D",
+ "DE E5 8C3E",
+ "DE E6 8C45",
+ "DE E7 8C47",
+ "DE E8 8C49",
+ "DE E9 8C4B",
+ "DE EA 8C4F",
+ "DE EB 8C51",
+ "DE EC 8C53",
+ "DE ED 8C54",
+ "DE EE 8C57",
+ "DE EF 8C58",
+ "DE F2 8C59",
+ "DE F0 8C5B",
+ "DE F1 8C5D",
+ "DE F3 8C63",
+ "DE F4 8C64",
+ "DE F5 8C66",
+ "DE F6 8C68",
+ "DE F7 8C69",
+ "DE F8 8C6D",
+ "DE F9 8C73",
+ "DE FA 8C75",
+ "DE FB 8C76",
+ "DE FC 8C7B",
+ "DE FD 8C7E",
+ "DE FE 8C86",
+ "DF A1 8C87",
+ "DF A2 8C8B",
+ "DF A3 8C90",
+ "DF A4 8C92",
+ "DF A5 8C93",
+ "DF A6 8C99",
+ "DF A7 8C9B",
+ "DF A8 8C9C",
+ "DF A9 8CA4",
+ "DF AA 8CB9",
+ "DF AB 8CBA",
+ "DF AC 8CC5",
+ "DF AD 8CC6",
+ "DF AE 8CC9",
+ "DF AF 8CCB",
+ "DF B0 8CCF",
+ "DF B2 8CD5",
+ "DF B1 8CD6",
+ "DF B3 8CD9",
+ "DF B4 8CDD",
+ "DF B5 8CE1",
+ "DF B6 8CE8",
+ "DF B7 8CEC",
+ "DF B8 8CEF",
+ "DF B9 8CF0",
+ "DF BA 8CF2",
+ "DF BB 8CF5",
+ "DF BC 8CF7",
+ "DF BD 8CF8",
+ "DF BE 8CFE",
+ "DF BF 8CFF",
+ "DF C0 8D01",
+ "DF C1 8D03",
+ "DF C2 8D09",
+ "DF C3 8D12",
+ "DF C4 8D17",
+ "DF C5 8D1B",
+ "DF C6 8D65",
+ "DF C7 8D69",
+ "DF C8 8D6C",
+ "DF C9 8D6E",
+ "DF CA 8D7F",
+ "DF CB 8D82",
+ "DF CC 8D84",
+ "DF CD 8D88",
+ "DF CE 8D8D",
+ "DF CF 8D90",
+ "DF D0 8D91",
+ "DF D1 8D95",
+ "DF D2 8D9E",
+ "DF D3 8D9F",
+ "DF D4 8DA0",
+ "DF D5 8DA6",
+ "DF D6 8DAB",
+ "DF D7 8DAC",
+ "DF D8 8DAF",
+ "DF D9 8DB2",
+ "DF DA 8DB5",
+ "DF DB 8DB7",
+ "DF DC 8DB9",
+ "DF DD 8DBB",
+ "DF EF 8DBC",
+ "DF DE 8DC0",
+ "DF DF 8DC5",
+ "DF E0 8DC6",
+ "DF E1 8DC7",
+ "DF E2 8DC8",
+ "DF E3 8DCA",
+ "DF E4 8DCE",
+ "DF E5 8DD1",
+ "DF E6 8DD4",
+ "DF E7 8DD5",
+ "DF E8 8DD7",
+ "DF E9 8DD9",
+ "DF EA 8DE4",
+ "DF EB 8DE5",
+ "DF EC 8DE7",
+ "DF ED 8DEC",
+ "DF EE 8DF0",
+ "DF F0 8DF1",
+ "DF F1 8DF2",
+ "DF F2 8DF4",
+ "DF F3 8DFD",
+ "DF F4 8E01",
+ "DF F5 8E04",
+ "DF F6 8E05",
+ "DF F7 8E06",
+ "DF F8 8E0B",
+ "DF F9 8E11",
+ "DF FA 8E14",
+ "DF FB 8E16",
+ "DF FC 8E20",
+ "DF FD 8E21",
+ "DF FE 8E22",
+ "E0 A1 8E23",
+ "E0 A2 8E26",
+ "E0 A3 8E27",
+ "E0 A4 8E31",
+ "E0 A5 8E33",
+ "E0 A6 8E36",
+ "E0 A7 8E37",
+ "E0 A8 8E38",
+ "E0 A9 8E39",
+ "E0 AA 8E3D",
+ "E0 AB 8E40",
+ "E0 AC 8E41",
+ "E0 AD 8E4B",
+ "E0 AE 8E4D",
+ "E0 AF 8E4E",
+ "E0 B0 8E4F",
+ "E0 B1 8E54",
+ "E0 B2 8E5B",
+ "E0 B3 8E5C",
+ "E0 B4 8E5D",
+ "E0 B5 8E5E",
+ "E0 B6 8E61",
+ "E0 B7 8E62",
+ "E0 B8 8E69",
+ "E0 B9 8E6C",
+ "E0 BA 8E6D",
+ "E0 BB 8E6F",
+ "E0 BC 8E70",
+ "E0 BD 8E71",
+ "E0 BE 8E79",
+ "E0 BF 8E7A",
+ "E0 C0 8E7B",
+ "E0 C1 8E82",
+ "E0 C2 8E83",
+ "E0 C3 8E89",
+ "E0 C4 8E90",
+ "E0 C5 8E92",
+ "E0 C6 8E95",
+ "E0 C7 8E9A",
+ "E0 C8 8E9B",
+ "E0 C9 8E9D",
+ "E0 CA 8E9E",
+ "E0 CB 8EA2",
+ "E0 CC 8EA7",
+ "E0 CD 8EA9",
+ "E0 CE 8EAD",
+ "E0 CF 8EAE",
+ "E0 D0 8EB3",
+ "E0 D1 8EB5",
+ "E0 D2 8EBA",
+ "E0 D3 8EBB",
+ "E0 D4 8EC0",
+ "E0 D5 8EC1",
+ "E0 D6 8EC3",
+ "E0 D7 8EC4",
+ "E0 D8 8EC7",
+ "E0 D9 8ECF",
+ "E0 DA 8ED1",
+ "E0 DB 8ED4",
+ "E0 DC 8EDC",
+ "E0 DD 8EE8",
+ "E0 E4 8EED",
+ "E0 DE 8EEE",
+ "E0 DF 8EF0",
+ "E0 E0 8EF1",
+ "E0 E1 8EF7",
+ "E0 E2 8EF9",
+ "E0 E3 8EFA",
+ "E0 E5 8F00",
+ "E0 E6 8F02",
+ "E0 E7 8F07",
+ "E0 E8 8F08",
+ "E0 E9 8F0F",
+ "E0 EA 8F10",
+ "E0 EB 8F16",
+ "E0 EC 8F17",
+ "E0 ED 8F18",
+ "E0 EE 8F1E",
+ "E0 EF 8F20",
+ "E0 F0 8F21",
+ "E0 F1 8F23",
+ "E0 F2 8F25",
+ "E0 F3 8F27",
+ "E0 F4 8F28",
+ "E0 F5 8F2C",
+ "E0 F6 8F2D",
+ "E0 F7 8F2E",
+ "E0 F8 8F34",
+ "E0 F9 8F35",
+ "E0 FA 8F36",
+ "E0 FB 8F37",
+ "E0 FC 8F3A",
+ "E0 FD 8F40",
+ "E0 FE 8F41",
+ "E1 A1 8F43",
+ "E1 A2 8F47",
+ "E1 A3 8F4F",
+ "E1 A4 8F51",
+ "E1 A5 8F52",
+ "E1 A6 8F53",
+ "E1 A7 8F54",
+ "E1 A8 8F55",
+ "E1 A9 8F58",
+ "E1 AA 8F5D",
+ "E1 AB 8F5E",
+ "E1 AC 8F65",
+ "E1 AD 8F9D",
+ "E1 AE 8FA0",
+ "E1 AF 8FA1",
+ "E1 B0 8FA4",
+ "E1 B1 8FA5",
+ "E1 B2 8FA6",
+ "E1 B3 8FB5",
+ "E1 B4 8FB6",
+ "E1 B5 8FB8",
+ "E1 B6 8FBE",
+ "E1 B7 8FC0",
+ "E1 B8 8FC1",
+ "E1 B9 8FC6",
+ "E1 BA 8FCA",
+ "E1 BB 8FCB",
+ "E1 BC 8FCD",
+ "E1 BD 8FD0",
+ "E1 BE 8FD2",
+ "E1 BF 8FD3",
+ "E1 C0 8FD5",
+ "E1 C1 8FE0",
+ "E1 C2 8FE3",
+ "E1 C3 8FE4",
+ "E1 C4 8FE8",
+ "E1 C5 8FEE",
+ "E1 C6 8FF1",
+ "E1 C7 8FF5",
+ "E1 C8 8FF6",
+ "E1 C9 8FFB",
+ "E1 CA 8FFE",
+ "E1 CB 9002",
+ "E1 CC 9004",
+ "E1 CD 9008",
+ "E1 CE 900C",
+ "E1 CF 9018",
+ "E1 D0 901B",
+ "E1 D1 9028",
+ "E1 D2 9029",
+ "E1 D4 902A",
+ "E1 D5 902C",
+ "E1 D6 902D",
+ "E1 D3 902F",
+ "E1 D7 9033",
+ "E1 D8 9034",
+ "E1 D9 9037",
+ "E1 DA 903F",
+ "E1 DB 9043",
+ "E1 DC 9044",
+ "E1 DD 904C",
+ "E1 DE 905B",
+ "E1 DF 905D",
+ "E1 E0 9062",
+ "E1 E1 9066",
+ "E1 E2 9067",
+ "E1 E3 906C",
+ "E1 E4 9070",
+ "E1 E5 9074",
+ "E1 E6 9079",
+ "E1 E7 9085",
+ "E1 E8 9088",
+ "E1 E9 908B",
+ "E1 EA 908C",
+ "E1 EB 908E",
+ "E1 EC 9090",
+ "E1 ED 9095",
+ "E1 EE 9097",
+ "E1 EF 9098",
+ "E1 F0 9099",
+ "E1 F1 909B",
+ "E1 F2 90A0",
+ "E1 F3 90A1",
+ "E1 F4 90A2",
+ "E1 F5 90A5",
+ "E1 F6 90B0",
+ "E1 F7 90B2",
+ "E1 F8 90B3",
+ "E1 F9 90B4",
+ "E1 FA 90B6",
+ "E1 FB 90BD",
+ "E1 FD 90BE",
+ "E1 FE 90C3",
+ "E2 A1 90C4",
+ "E2 A2 90C5",
+ "E2 A3 90C7",
+ "E2 A4 90C8",
+ "E1 FC 90CC",
+ "E2 AD 90D2",
+ "E2 A5 90D5",
+ "E2 A6 90D7",
+ "E2 A7 90D8",
+ "E2 A8 90D9",
+ "E2 A9 90DC",
+ "E2 AA 90DD",
+ "E2 AB 90DF",
+ "E2 AC 90E5",
+ "E2 AF 90EB",
+ "E2 B0 90EF",
+ "E2 B1 90F0",
+ "E2 B2 90F4",
+ "E2 AE 90F6",
+ "E2 B3 90FE",
+ "E2 B4 90FF",
+ "E2 B5 9100",
+ "E2 B6 9104",
+ "E2 B7 9105",
+ "E2 B8 9106",
+ "E2 B9 9108",
+ "E2 BA 910D",
+ "E2 BB 9110",
+ "E2 BC 9114",
+ "E2 BD 9116",
+ "E2 BE 9117",
+ "E2 BF 9118",
+ "E2 C0 911A",
+ "E2 C1 911C",
+ "E2 C2 911E",
+ "E2 C3 9120",
+ "E2 C5 9122",
+ "E2 C6 9123",
+ "E2 C4 9125",
+ "E2 C7 9127",
+ "E2 C8 9129",
+ "E2 C9 912E",
+ "E2 CA 912F",
+ "E2 CB 9131",
+ "E2 CC 9134",
+ "E2 CD 9136",
+ "E2 CE 9137",
+ "E2 CF 9139",
+ "E2 D0 913A",
+ "E2 D1 913C",
+ "E2 D2 913D",
+ "E2 D3 9143",
+ "E2 D4 9147",
+ "E2 D5 9148",
+ "E2 D6 914F",
+ "E2 D7 9153",
+ "E2 D8 9157",
+ "E2 D9 9159",
+ "E2 DA 915A",
+ "E2 DB 915B",
+ "E2 DC 9161",
+ "E2 DD 9164",
+ "E2 DE 9167",
+ "E2 DF 916D",
+ "E2 E0 9174",
+ "E2 E1 9179",
+ "E2 E2 917A",
+ "E2 E3 917B",
+ "E2 E4 9181",
+ "E2 E5 9183",
+ "E2 E6 9185",
+ "E2 E7 9186",
+ "E2 E8 918A",
+ "E2 E9 918E",
+ "E2 EA 9191",
+ "E2 EB 9193",
+ "E2 EC 9194",
+ "E2 ED 9195",
+ "E2 EE 9198",
+ "E2 EF 919E",
+ "E2 F0 91A1",
+ "E2 F1 91A6",
+ "E2 F2 91A8",
+ "E2 F3 91AC",
+ "E2 F4 91AD",
+ "E2 F5 91AE",
+ "E2 F6 91B0",
+ "E2 F7 91B1",
+ "E2 F8 91B2",
+ "E2 F9 91B3",
+ "E2 FA 91B6",
+ "E2 FB 91BB",
+ "E2 FC 91BC",
+ "E2 FD 91BD",
+ "E2 FE 91BF",
+ "E3 A1 91C2",
+ "E3 A2 91C3",
+ "E3 A3 91C5",
+ "E3 A4 91D3",
+ "E3 A5 91D4",
+ "E3 A6 91D7",
+ "E3 A7 91D9",
+ "E3 A8 91DA",
+ "E3 A9 91DE",
+ "E3 AA 91E4",
+ "E3 AB 91E5",
+ "E3 AC 91E9",
+ "E3 AD 91EA",
+ "E3 AE 91EC",
+ "E3 AF 91ED",
+ "E3 B0 91EE",
+ "E3 B1 91EF",
+ "E3 B2 91F0",
+ "E3 B3 91F1",
+ "E3 B4 91F7",
+ "E3 B5 91F9",
+ "E3 B6 91FB",
+ "E3 B7 91FD",
+ "E3 B8 9200",
+ "E3 B9 9201",
+ "E3 BA 9204",
+ "E3 BB 9205",
+ "E3 BC 9206",
+ "E3 BD 9207",
+ "E3 BE 9209",
+ "E3 BF 920A",
+ "E3 C0 920C",
+ "E3 C1 9210",
+ "E3 C2 9212",
+ "E3 C3 9213",
+ "E3 C4 9216",
+ "E3 C5 9218",
+ "E3 C6 921C",
+ "E3 C7 921D",
+ "E3 C8 9223",
+ "E3 C9 9224",
+ "E3 CA 9225",
+ "E3 CB 9226",
+ "E3 CC 9228",
+ "E3 CD 922E",
+ "E3 CE 922F",
+ "E3 CF 9230",
+ "E3 D0 9233",
+ "E3 D1 9235",
+ "E3 D2 9236",
+ "E3 D3 9238",
+ "E3 D4 9239",
+ "E3 D5 923A",
+ "E3 D6 923C",
+ "E3 D7 923E",
+ "E3 D8 9240",
+ "E3 D9 9242",
+ "E3 DA 9243",
+ "E3 DB 9246",
+ "E3 DC 9247",
+ "E3 DD 924A",
+ "E3 DE 924D",
+ "E3 DF 924E",
+ "E3 E0 924F",
+ "E3 E1 9251",
+ "E3 E2 9258",
+ "E3 E3 9259",
+ "E3 E4 925C",
+ "E3 E5 925D",
+ "E3 E6 9260",
+ "E3 E7 9261",
+ "E3 E8 9265",
+ "E3 E9 9267",
+ "E3 EA 9268",
+ "E3 EB 9269",
+ "E3 EC 926E",
+ "E3 ED 926F",
+ "E3 EE 9270",
+ "E3 EF 9275",
+ "E3 F0 9276",
+ "E3 F1 9277",
+ "E3 F2 9278",
+ "E3 F3 9279",
+ "E3 F4 927B",
+ "E3 F5 927C",
+ "E3 F6 927D",
+ "E3 F7 927F",
+ "E3 F8 9288",
+ "E3 F9 9289",
+ "E3 FA 928A",
+ "E3 FB 928D",
+ "E3 FC 928E",
+ "E3 FD 9292",
+ "E3 FE 9297",
+ "E4 A1 9299",
+ "E4 A2 929F",
+ "E4 A3 92A0",
+ "E4 A4 92A4",
+ "E4 A5 92A5",
+ "E4 A6 92A7",
+ "E4 A7 92A8",
+ "E4 A8 92AB",
+ "E4 A9 92AF",
+ "E4 AA 92B2",
+ "E4 AB 92B6",
+ "E4 AC 92B8",
+ "E4 AD 92BA",
+ "E4 AE 92BB",
+ "E4 AF 92BC",
+ "E4 B0 92BD",
+ "E4 B1 92BF",
+ "E4 B2 92C0",
+ "E4 B3 92C1",
+ "E4 B4 92C2",
+ "E4 B5 92C3",
+ "E4 B6 92C5",
+ "E4 B7 92C6",
+ "E4 B8 92C7",
+ "E4 B9 92C8",
+ "E4 BA 92CB",
+ "E4 BB 92CC",
+ "E4 BC 92CD",
+ "E4 BD 92CE",
+ "E4 BE 92D0",
+ "E4 BF 92D3",
+ "E4 C0 92D5",
+ "E4 C1 92D7",
+ "E4 C2 92D8",
+ "E4 C3 92D9",
+ "E4 C4 92DC",
+ "E4 C5 92DD",
+ "E4 C6 92DF",
+ "E4 C7 92E0",
+ "E4 C8 92E1",
+ "E4 C9 92E3",
+ "E4 CA 92E5",
+ "E4 CB 92E7",
+ "E4 CC 92E8",
+ "E4 CD 92EC",
+ "E4 CE 92EE",
+ "E4 CF 92F0",
+ "E4 D0 92F9",
+ "E4 D1 92FB",
+ "E4 D2 92FF",
+ "E4 D3 9300",
+ "E4 D4 9302",
+ "E4 D5 9308",
+ "E4 D6 930D",
+ "E4 D7 9311",
+ "E4 D8 9314",
+ "E4 D9 9315",
+ "E4 DA 931C",
+ "E4 DB 931D",
+ "E4 DC 931E",
+ "E4 DD 931F",
+ "E4 DE 9321",
+ "E4 DF 9324",
+ "E4 E0 9325",
+ "E4 E1 9327",
+ "E4 E2 9329",
+ "E4 E3 932A",
+ "E4 E4 9333",
+ "E4 E5 9334",
+ "E4 E6 9336",
+ "E4 E7 9337",
+ "E4 E8 9347",
+ "E4 E9 9348",
+ "E4 EA 9349",
+ "E4 EB 9350",
+ "E4 EC 9351",
+ "E4 ED 9352",
+ "E4 EE 9355",
+ "E4 EF 9357",
+ "E4 F0 9358",
+ "E4 F1 935A",
+ "E4 F2 935E",
+ "E4 F3 9364",
+ "E4 F4 9365",
+ "E4 F5 9367",
+ "E4 F6 9369",
+ "E4 F7 936A",
+ "E4 F8 936D",
+ "E4 F9 936F",
+ "E4 FA 9370",
+ "E4 FB 9371",
+ "E4 FC 9373",
+ "E4 FD 9374",
+ "E4 FE 9376",
+ "E5 A1 937A",
+ "E5 A2 937D",
+ "E5 A3 937F",
+ "E5 A4 9380",
+ "E5 A5 9381",
+ "E5 A6 9382",
+ "E5 A7 9388",
+ "E5 A8 938A",
+ "E5 A9 938B",
+ "E5 AA 938D",
+ "E5 AB 938F",
+ "E5 AC 9392",
+ "E5 AD 9395",
+ "E5 AE 9398",
+ "E5 AF 939B",
+ "E5 B0 939E",
+ "E5 B1 93A1",
+ "E5 B2 93A3",
+ "E5 B3 93A4",
+ "E5 B4 93A6",
+ "E5 B5 93A8",
+ "E5 BB 93A9",
+ "E5 B6 93AB",
+ "E5 B7 93B4",
+ "E5 B8 93B5",
+ "E5 B9 93B6",
+ "E5 BA 93BA",
+ "E5 BC 93C1",
+ "E5 BD 93C4",
+ "E5 BE 93C5",
+ "E5 BF 93C6",
+ "E5 C0 93C7",
+ "E5 C1 93C9",
+ "E5 C2 93CA",
+ "E5 C3 93CB",
+ "E5 C4 93CC",
+ "E5 C5 93CD",
+ "E5 C6 93D3",
+ "E5 C7 93D9",
+ "E5 C8 93DC",
+ "E5 C9 93DE",
+ "E5 CA 93DF",
+ "E5 CB 93E2",
+ "E5 CC 93E6",
+ "E5 CD 93E7",
+ "E5 CF 93F7",
+ "E5 D0 93F8",
+ "E5 CE 93F9",
+ "E5 D1 93FA",
+ "E5 D2 93FB",
+ "E5 D3 93FD",
+ "E5 D4 9401",
+ "E5 D5 9402",
+ "E5 D6 9404",
+ "E5 D7 9408",
+ "E5 D8 9409",
+ "E5 D9 940D",
+ "E5 DA 940E",
+ "E5 DB 940F",
+ "E5 DC 9415",
+ "E5 DD 9416",
+ "E5 DE 9417",
+ "E5 DF 941F",
+ "E5 E0 942E",
+ "E5 E1 942F",
+ "E5 E2 9431",
+ "E5 E3 9432",
+ "E5 E4 9433",
+ "E5 E5 9434",
+ "E5 E6 943B",
+ "E5 E8 943D",
+ "E5 E7 943F",
+ "E5 E9 9443",
+ "E5 EA 9445",
+ "E5 EB 9448",
+ "E5 EC 944A",
+ "E5 ED 944C",
+ "E5 EE 9455",
+ "E5 EF 9459",
+ "E5 F0 945C",
+ "E5 F1 945F",
+ "E5 F2 9461",
+ "E5 F3 9463",
+ "E5 F4 9468",
+ "E5 F5 946B",
+ "E5 F6 946D",
+ "E5 F7 946E",
+ "E5 F8 946F",
+ "E5 F9 9471",
+ "E5 FA 9472",
+ "E5 FC 9483",
+ "E5 FB 9484",
+ "E5 FD 9578",
+ "E5 FE 9579",
+ "E6 A1 957E",
+ "E6 A2 9584",
+ "E6 A3 9588",
+ "E6 A4 958C",
+ "E6 A5 958D",
+ "E6 A6 958E",
+ "E6 A7 959D",
+ "E6 A8 959E",
+ "E6 A9 959F",
+ "E6 AA 95A1",
+ "E6 AB 95A6",
+ "E6 AC 95A9",
+ "E6 AD 95AB",
+ "E6 AE 95AC",
+ "E6 AF 95B4",
+ "E6 B0 95B6",
+ "E6 B1 95BA",
+ "E6 B2 95BD",
+ "E6 B3 95BF",
+ "E6 B4 95C6",
+ "E6 B5 95C8",
+ "E6 B6 95C9",
+ "E6 B7 95CB",
+ "E6 B8 95D0",
+ "E6 B9 95D1",
+ "E6 BA 95D2",
+ "E6 BB 95D3",
+ "E6 BC 95D9",
+ "E6 BD 95DA",
+ "E6 BE 95DD",
+ "E6 BF 95DE",
+ "E6 C0 95DF",
+ "E6 C1 95E0",
+ "E6 C2 95E4",
+ "E6 C3 95E6",
+ "E6 C4 961D",
+ "E6 C5 961E",
+ "E6 C6 9622",
+ "E6 C7 9624",
+ "E6 C8 9625",
+ "E6 C9 9626",
+ "E6 CA 962C",
+ "E6 CB 9631",
+ "E6 CC 9633",
+ "E6 CD 9637",
+ "E6 CE 9638",
+ "E6 CF 9639",
+ "E6 D0 963A",
+ "E6 D1 963C",
+ "E6 D2 963D",
+ "E6 D3 9641",
+ "E6 D4 9652",
+ "E6 D5 9654",
+ "E6 D6 9656",
+ "E6 D7 9657",
+ "E6 D8 9658",
+ "E6 D9 9661",
+ "E6 DA 966E",
+ "E6 DB 9674",
+ "E6 DC 967B",
+ "E6 DD 967C",
+ "E6 DE 967E",
+ "E6 DF 967F",
+ "E6 E0 9681",
+ "E6 E1 9682",
+ "E6 E2 9683",
+ "E6 E3 9684",
+ "E6 E4 9689",
+ "E6 E5 9691",
+ "E6 E6 9696",
+ "E6 E7 969A",
+ "E6 E8 969D",
+ "E6 E9 969F",
+ "E6 EA 96A4",
+ "E6 EB 96A5",
+ "E6 EC 96A6",
+ "E6 ED 96A9",
+ "E6 EE 96AE",
+ "E6 EF 96AF",
+ "E6 F0 96B3",
+ "E6 F1 96BA",
+ "E6 F2 96CA",
+ "E6 F3 96D2",
+ "E6 F5 96D8",
+ "E6 F6 96DA",
+ "E6 F7 96DD",
+ "E6 F8 96DE",
+ "E6 F9 96DF",
+ "E6 FA 96E9",
+ "E6 FB 96EF",
+ "E6 FC 96F1",
+ "E6 FD 96FA",
+ "E6 FE 9702",
+ "E7 A1 9703",
+ "E7 A2 9705",
+ "E7 A3 9709",
+ "E7 A4 971A",
+ "E7 A5 971B",
+ "E7 A6 971D",
+ "E7 A7 9721",
+ "E7 A8 9722",
+ "E7 A9 9723",
+ "E7 AA 9728",
+ "E7 AB 9731",
+ "E7 AC 9733",
+ "E7 AD 9741",
+ "E7 AE 9743",
+ "E7 AF 974A",
+ "E7 B0 974E",
+ "E7 B1 974F",
+ "E7 B2 9755",
+ "E7 B3 9757",
+ "E7 B4 9758",
+ "E7 B5 975A",
+ "E7 B6 975B",
+ "E7 B7 9763",
+ "E7 B8 9767",
+ "E7 B9 976A",
+ "E7 BA 976E",
+ "E7 BB 9773",
+ "E7 BC 9776",
+ "E7 BD 9777",
+ "E7 BE 9778",
+ "E7 BF 977B",
+ "E7 C0 977D",
+ "E7 C1 977F",
+ "E7 C2 9780",
+ "E7 C3 9789",
+ "E7 C4 9795",
+ "E7 C5 9796",
+ "E7 C6 9797",
+ "E7 C7 9799",
+ "E7 C8 979A",
+ "E7 C9 979E",
+ "E7 CA 979F",
+ "E7 CB 97A2",
+ "E7 CC 97AC",
+ "E7 CD 97AE",
+ "E7 CE 97B1",
+ "E7 CF 97B2",
+ "E7 D0 97B5",
+ "E7 D1 97B6",
+ "E7 D2 97B8",
+ "E7 D3 97B9",
+ "E7 D4 97BA",
+ "E7 D5 97BC",
+ "E7 D6 97BE",
+ "E7 D7 97BF",
+ "E7 D8 97C1",
+ "E7 D9 97C4",
+ "E7 DA 97C5",
+ "E7 DB 97C7",
+ "E7 DC 97C9",
+ "E7 DD 97CA",
+ "E7 DE 97CC",
+ "E7 DF 97CD",
+ "E7 E0 97CE",
+ "E7 E1 97D0",
+ "E7 E2 97D1",
+ "E7 E3 97D4",
+ "E7 E4 97D7",
+ "E7 E5 97D8",
+ "E7 E6 97D9",
+ "E7 EA 97DB",
+ "E7 E7 97DD",
+ "E7 E8 97DE",
+ "E7 E9 97E0",
+ "E7 EB 97E1",
+ "E7 EC 97E4",
+ "E7 ED 97EF",
+ "E7 EE 97F1",
+ "E7 EF 97F4",
+ "E7 F0 97F7",
+ "E7 F1 97F8",
+ "E7 F2 97FA",
+ "E7 F3 9807",
+ "E7 F4 980A",
+ "E7 F6 980D",
+ "E7 F7 980E",
+ "E7 F8 9814",
+ "E7 F9 9816",
+ "E7 F5 9819",
+ "E7 FA 981C",
+ "E7 FB 981E",
+ "E7 FC 9820",
+ "E7 FD 9823",
+ "E8 A8 9825",
+ "E7 FE 9826",
+ "E8 A1 982B",
+ "E8 A2 982E",
+ "E8 A3 982F",
+ "E8 A4 9830",
+ "E8 A5 9832",
+ "E8 A6 9833",
+ "E8 A7 9835",
+ "E8 A9 983E",
+ "E8 AA 9844",
+ "E8 AB 9847",
+ "E8 AC 984A",
+ "E8 AD 9851",
+ "E8 AE 9852",
+ "E8 AF 9853",
+ "E8 B0 9856",
+ "E8 B1 9857",
+ "E8 B2 9859",
+ "E8 B3 985A",
+ "E8 B4 9862",
+ "E8 B5 9863",
+ "E8 B6 9865",
+ "E8 B7 9866",
+ "E8 B8 986A",
+ "E8 B9 986C",
+ "E8 BA 98AB",
+ "E8 BB 98AD",
+ "E8 BC 98AE",
+ "E8 BD 98B0",
+ "E8 BE 98B4",
+ "E8 BF 98B7",
+ "E8 C0 98B8",
+ "E8 C1 98BA",
+ "E8 C2 98BB",
+ "E8 C3 98BF",
+ "E8 C4 98C2",
+ "E8 C5 98C5",
+ "E8 C6 98C8",
+ "E8 C7 98CC",
+ "E8 C8 98E1",
+ "E8 C9 98E3",
+ "E8 CA 98E5",
+ "E8 CB 98E6",
+ "E8 CC 98E7",
+ "E8 CD 98EA",
+ "E8 CE 98F3",
+ "E8 CF 98F6",
+ "E8 D0 9902",
+ "E8 D1 9907",
+ "E8 D2 9908",
+ "E8 D3 9911",
+ "E8 D4 9915",
+ "E8 D5 9916",
+ "E8 D6 9917",
+ "E8 D7 991A",
+ "E8 D8 991B",
+ "E8 D9 991C",
+ "E8 DA 991F",
+ "E8 DB 9922",
+ "E8 DC 9926",
+ "E8 DD 9927",
+ "E8 DE 992B",
+ "E8 DF 9931",
+ "E8 E0 9932",
+ "E8 E1 9933",
+ "E8 E2 9934",
+ "E8 E3 9935",
+ "E8 E4 9939",
+ "E8 E5 993A",
+ "E8 E6 993B",
+ "E8 E7 993C",
+ "E8 E8 9940",
+ "E8 E9 9941",
+ "E8 EA 9946",
+ "E8 EB 9947",
+ "E8 EC 9948",
+ "E8 ED 994D",
+ "E8 EE 994E",
+ "E8 EF 9954",
+ "E8 F0 9958",
+ "E8 F1 9959",
+ "E8 F2 995B",
+ "E8 F3 995C",
+ "E8 F4 995E",
+ "E8 F5 995F",
+ "E8 F6 9960",
+ "E8 F7 999B",
+ "E8 F8 999D",
+ "E8 F9 999F",
+ "E8 FA 99A6",
+ "E8 FB 99B0",
+ "E8 FC 99B1",
+ "E8 FD 99B2",
+ "E8 FE 99B5",
+ "E9 A1 99B9",
+ "E9 A2 99BA",
+ "E9 A3 99BD",
+ "E9 A4 99BF",
+ "E9 A5 99C3",
+ "E9 A6 99C9",
+ "E9 A7 99D3",
+ "E9 A8 99D4",
+ "E9 A9 99D9",
+ "E9 AA 99DA",
+ "E9 AB 99DC",
+ "E9 AC 99DE",
+ "E9 AD 99E7",
+ "E9 AE 99EA",
+ "E9 AF 99EB",
+ "E9 B0 99EC",
+ "E9 B1 99F0",
+ "E9 B2 99F4",
+ "E9 B3 99F5",
+ "E9 B4 99F9",
+ "E9 B5 99FD",
+ "E9 B6 99FE",
+ "E9 B7 9A02",
+ "E9 B8 9A03",
+ "E9 B9 9A04",
+ "E9 BA 9A0B",
+ "E9 BB 9A0C",
+ "E9 BC 9A10",
+ "E9 BD 9A11",
+ "E9 BE 9A16",
+ "E9 BF 9A1E",
+ "E9 C0 9A20",
+ "E9 C1 9A22",
+ "E9 C2 9A23",
+ "E9 C3 9A24",
+ "E9 C4 9A27",
+ "E9 C5 9A2D",
+ "E9 C6 9A2E",
+ "E9 C7 9A33",
+ "E9 C8 9A35",
+ "E9 C9 9A36",
+ "E9 CA 9A38",
+ "E9 CC 9A41",
+ "E9 CD 9A44",
+ "E9 CB 9A47",
+ "E9 CE 9A4A",
+ "E9 CF 9A4B",
+ "E9 D0 9A4C",
+ "E9 D1 9A4E",
+ "E9 D2 9A51",
+ "E9 D3 9A54",
+ "E9 D4 9A56",
+ "E9 D5 9A5D",
+ "E9 D6 9AAA",
+ "E9 D7 9AAC",
+ "E9 D8 9AAE",
+ "E9 D9 9AAF",
+ "E9 DA 9AB2",
+ "E9 DB 9AB4",
+ "E9 DC 9AB5",
+ "E9 DD 9AB6",
+ "E9 DE 9AB9",
+ "E9 DF 9ABB",
+ "E9 E0 9ABE",
+ "E9 E1 9ABF",
+ "E9 E2 9AC1",
+ "E9 E3 9AC3",
+ "E9 E4 9AC6",
+ "E9 E5 9AC8",
+ "E9 E6 9ACE",
+ "E9 E7 9AD0",
+ "E9 E8 9AD2",
+ "E9 E9 9AD5",
+ "E9 EA 9AD6",
+ "E9 EB 9AD7",
+ "E9 EC 9ADB",
+ "E9 ED 9ADC",
+ "E9 EE 9AE0",
+ "E9 EF 9AE4",
+ "E9 F0 9AE5",
+ "E9 F1 9AE7",
+ "E9 F2 9AE9",
+ "E9 F3 9AEC",
+ "E9 F4 9AF2",
+ "E9 F5 9AF3",
+ "E9 F6 9AF5",
+ "E9 F7 9AF9",
+ "E9 F8 9AFA",
+ "E9 F9 9AFD",
+ "E9 FA 9AFF",
+ "E9 FB 9B00",
+ "E9 FC 9B01",
+ "E9 FD 9B02",
+ "E9 FE 9B03",
+ "EA A1 9B04",
+ "EA A2 9B05",
+ "EA A3 9B08",
+ "EA A4 9B09",
+ "EA A5 9B0B",
+ "EA A6 9B0C",
+ "EA A7 9B0D",
+ "EA A8 9B0E",
+ "EA A9 9B10",
+ "EA AA 9B12",
+ "EA AB 9B16",
+ "EA AC 9B19",
+ "EA AD 9B1B",
+ "EA AE 9B1C",
+ "EA AF 9B20",
+ "EA B0 9B26",
+ "EA B1 9B2B",
+ "EA B2 9B2D",
+ "EA B3 9B33",
+ "EA B4 9B34",
+ "EA B5 9B35",
+ "EA B6 9B37",
+ "EA B7 9B39",
+ "EA B8 9B3A",
+ "EA B9 9B3D",
+ "EA BA 9B48",
+ "EA BB 9B4B",
+ "EA BC 9B4C",
+ "EA BD 9B55",
+ "EA BE 9B56",
+ "EA BF 9B57",
+ "EA C0 9B5B",
+ "EA C1 9B5E",
+ "EA C2 9B61",
+ "EA C3 9B63",
+ "EA C4 9B65",
+ "EA C5 9B66",
+ "EA C6 9B68",
+ "EA C7 9B6A",
+ "EA C8 9B6B",
+ "EA C9 9B6C",
+ "EA CA 9B6D",
+ "EA CB 9B6E",
+ "EA CC 9B73",
+ "EA CD 9B75",
+ "EA CE 9B77",
+ "EA CF 9B78",
+ "EA D0 9B79",
+ "EA D1 9B7F",
+ "EA D2 9B80",
+ "EA D3 9B84",
+ "EA D4 9B85",
+ "EA D5 9B86",
+ "EA D6 9B87",
+ "EA D7 9B89",
+ "EA D8 9B8A",
+ "EA D9 9B8B",
+ "EA DA 9B8D",
+ "EA DB 9B8F",
+ "EA DC 9B90",
+ "EA DD 9B94",
+ "EA DE 9B9A",
+ "EA DF 9B9D",
+ "EA E0 9B9E",
+ "EA E1 9BA6",
+ "EA E2 9BA7",
+ "EA E3 9BA9",
+ "EA E4 9BAC",
+ "EA E5 9BB0",
+ "EA E6 9BB1",
+ "EA E7 9BB2",
+ "EA E8 9BB7",
+ "EA E9 9BB8",
+ "EA EA 9BBB",
+ "EA EB 9BBC",
+ "EA EC 9BBE",
+ "EA ED 9BBF",
+ "EA EE 9BC1",
+ "EA EF 9BC7",
+ "EA F0 9BC8",
+ "EA F1 9BCE",
+ "EA F2 9BD0",
+ "EA F3 9BD7",
+ "EA F4 9BD8",
+ "EA F5 9BDD",
+ "EA F6 9BDF",
+ "EA F7 9BE5",
+ "EA F8 9BE7",
+ "EA F9 9BEA",
+ "EA FA 9BEB",
+ "EA FB 9BEF",
+ "EA FC 9BF3",
+ "EA FD 9BF7",
+ "EA FE 9BF8",
+ "EB A1 9BF9",
+ "EB A2 9BFA",
+ "EB A3 9BFD",
+ "EB A4 9BFF",
+ "EB A5 9C00",
+ "EB A6 9C02",
+ "EB A7 9C0B",
+ "EB A8 9C0F",
+ "EB A9 9C11",
+ "EB AA 9C16",
+ "EB AB 9C18",
+ "EB AC 9C19",
+ "EB AD 9C1A",
+ "EB AE 9C1C",
+ "EB AF 9C1E",
+ "EB B0 9C22",
+ "EB B1 9C23",
+ "EB B2 9C26",
+ "EB B3 9C27",
+ "EB B4 9C28",
+ "EB B5 9C29",
+ "EB B6 9C2A",
+ "EB B7 9C31",
+ "EB B8 9C35",
+ "EB B9 9C36",
+ "EB BA 9C37",
+ "EB BB 9C3D",
+ "EB BC 9C41",
+ "EB BD 9C43",
+ "EB BE 9C44",
+ "EB BF 9C45",
+ "EB C0 9C49",
+ "EB C1 9C4A",
+ "EB C2 9C4E",
+ "EB C3 9C4F",
+ "EB C4 9C50",
+ "EB C5 9C53",
+ "EB C6 9C54",
+ "EB C7 9C56",
+ "EB C8 9C58",
+ "EB C9 9C5B",
+ "EB D0 9C5C",
+ "EB CA 9C5D",
+ "EB CB 9C5E",
+ "EB CC 9C5F",
+ "EB CD 9C63",
+ "EB D2 9C68",
+ "EB CE 9C69",
+ "EB CF 9C6A",
+ "EB D1 9C6B",
+ "EB D3 9C6E",
+ "EB D4 9C70",
+ "EB D5 9C72",
+ "EB D6 9C75",
+ "EB D7 9C77",
+ "EB D8 9C7B",
+ "EB D9 9CE6",
+ "EB DA 9CF2",
+ "EB DB 9CF7",
+ "EB DC 9CF9",
+ "EB DE 9D02",
+ "EB DD 9D0B",
+ "EB DF 9D11",
+ "EB E0 9D17",
+ "EB E1 9D18",
+ "EB E2 9D1C",
+ "EB E3 9D1D",
+ "EB E4 9D1E",
+ "EB E5 9D2F",
+ "EB E6 9D30",
+ "EB E7 9D32",
+ "EB E8 9D33",
+ "EB E9 9D34",
+ "EB EA 9D3A",
+ "EB EB 9D3C",
+ "EB ED 9D3D",
+ "EB EE 9D42",
+ "EB EF 9D43",
+ "EB EC 9D45",
+ "EB F0 9D47",
+ "EB F1 9D4A",
+ "EB F2 9D53",
+ "EB F3 9D54",
+ "EB F4 9D5F",
+ "EB F6 9D62",
+ "EB F5 9D63",
+ "EB F7 9D65",
+ "EB F8 9D69",
+ "EB F9 9D6A",
+ "EB FA 9D6B",
+ "EB FB 9D70",
+ "EB FC 9D76",
+ "EB FD 9D77",
+ "EB FE 9D7B",
+ "EC A1 9D7C",
+ "EC A2 9D7E",
+ "EC A3 9D83",
+ "EC A4 9D84",
+ "EC A5 9D86",
+ "EC A6 9D8A",
+ "EC A7 9D8D",
+ "EC A8 9D8E",
+ "EC A9 9D92",
+ "EC AA 9D93",
+ "EC AB 9D95",
+ "EC AC 9D96",
+ "EC AD 9D97",
+ "EC AE 9D98",
+ "EC AF 9DA1",
+ "EC B0 9DAA",
+ "EC B1 9DAC",
+ "EC B2 9DAE",
+ "EC B3 9DB1",
+ "EC B4 9DB5",
+ "EC B5 9DB9",
+ "EC B6 9DBC",
+ "EC B7 9DBF",
+ "EC B8 9DC3",
+ "EC B9 9DC7",
+ "EC BA 9DC9",
+ "EC BB 9DCA",
+ "EC BC 9DD4",
+ "EC BD 9DD5",
+ "EC BE 9DD6",
+ "EC BF 9DD7",
+ "EC C0 9DDA",
+ "EC C1 9DDE",
+ "EC C2 9DDF",
+ "EC C3 9DE0",
+ "EC C4 9DE5",
+ "EC C5 9DE7",
+ "EC C6 9DE9",
+ "EC C7 9DEB",
+ "EC C8 9DEE",
+ "EC C9 9DF0",
+ "EC CA 9DF3",
+ "EC CB 9DF4",
+ "EC CC 9DFE",
+ "EC CE 9E02",
+ "EC CF 9E07",
+ "EC CD 9E0A",
+ "EC D0 9E0E",
+ "EC D1 9E10",
+ "EC D2 9E11",
+ "EC D3 9E12",
+ "EC D4 9E15",
+ "EC D5 9E16",
+ "EC D6 9E19",
+ "EC D7 9E1C",
+ "EC D8 9E1D",
+ "EC D9 9E7A",
+ "EC DA 9E7B",
+ "EC DB 9E7C",
+ "EC DC 9E80",
+ "EC DD 9E82",
+ "EC DE 9E83",
+ "EC DF 9E84",
+ "EC E0 9E85",
+ "EC E1 9E87",
+ "EC E2 9E8E",
+ "EC E3 9E8F",
+ "EC E4 9E96",
+ "EC E5 9E98",
+ "EC E6 9E9B",
+ "EC E7 9E9E",
+ "EC E8 9EA4",
+ "EC E9 9EA8",
+ "EC EA 9EAC",
+ "EC EB 9EAE",
+ "EC EC 9EAF",
+ "EC ED 9EB0",
+ "EC EE 9EB3",
+ "EC EF 9EB4",
+ "EC F0 9EB5",
+ "EC F1 9EC6",
+ "EC F2 9EC8",
+ "EC F3 9ECB",
+ "EC F4 9ED5",
+ "EC F5 9EDF",
+ "EC F6 9EE4",
+ "EC F7 9EE7",
+ "EC F8 9EEC",
+ "EC F9 9EED",
+ "EC FA 9EEE",
+ "EC FB 9EF0",
+ "EC FC 9EF1",
+ "EC FD 9EF2",
+ "EC FE 9EF5",
+ "ED A1 9EF8",
+ "ED A2 9EFF",
+ "ED A3 9F02",
+ "ED A4 9F03",
+ "ED A5 9F09",
+ "ED A6 9F0F",
+ "ED A7 9F10",
+ "ED A8 9F11",
+ "ED A9 9F12",
+ "ED AA 9F14",
+ "ED AB 9F16",
+ "ED AC 9F17",
+ "ED AD 9F19",
+ "ED AE 9F1A",
+ "ED AF 9F1B",
+ "ED B0 9F1F",
+ "ED B1 9F22",
+ "ED B2 9F26",
+ "ED B3 9F2A",
+ "ED B4 9F2B",
+ "ED B5 9F2F",
+ "ED B6 9F31",
+ "ED B7 9F32",
+ "ED B8 9F34",
+ "ED B9 9F37",
+ "ED BA 9F39",
+ "ED BB 9F3A",
+ "ED BC 9F3C",
+ "ED BD 9F3D",
+ "ED BE 9F3F",
+ "ED BF 9F41",
+ "ED C0 9F43",
+ "ED C1 9F44",
+ "ED C2 9F45",
+ "ED C3 9F46",
+ "ED C4 9F47",
+ "ED C5 9F53",
+ "ED C6 9F55",
+ "ED C7 9F56",
+ "ED C8 9F57",
+ "ED C9 9F58",
+ "ED CA 9F5A",
+ "ED CB 9F5D",
+ "ED CC 9F5E",
+ "ED CD 9F68",
+ "ED CE 9F69",
+ "ED CF 9F6D",
+ "ED D0 9F6E",
+ "ED D1 9F6F",
+ "ED D2 9F70",
+ "ED D3 9F71",
+ "ED D4 9F73",
+ "ED D5 9F75",
+ "ED D6 9F7A",
+ "ED D7 9F7D",
+ "ED D8 9F8F",
+ "ED D9 9F90",
+ "ED DA 9F91",
+ "ED DB 9F92",
+ "ED DC 9F94",
+ "ED DD 9F96",
+ "ED DE 9F97",
+ "ED DF 9F9E",
+ "ED E0 9FA1",
+ "ED E1 9FA2",
+ "ED E2 9FA3",
+ "ED E3 9FA5",
+ "A2 B7 FF5E",
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/ibm437.b b/appl/lib/convcs/ibm437.b
new file mode 100644
index 00000000..b867a723
--- /dev/null
+++ b/appl/lib/convcs/ibm437.b
@@ -0,0 +1,34 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "ibm437";
+
+cstab := array [] of {
+16r00,16r01,16r02,16r03,16r04,16r05,16r06,16r07,16r08,16r09,16r0a,16r0b,16r0c,16r0d,16r0e,16r0f,
+16r10,16r11,16r12,16r13,16r14,16r15,16r16,16r17,16r18,16r19,16r1a,16r1b,16r1c,16r1d,16r1e,16r1f,
+16r20,16r21,16r22,16r23,16r24,16r25,16r26,16r27,16r28,16r29,16r2a,16r2b,16r2c,16r2d,16r2e,16r2f,
+16r30,16r31,16r32,16r33,16r34,16r35,16r36,16r37,16r38,16r39,16r3a,16r3b,16r3c,16r3d,16r3e,16r3f,
+16r40,16r41,16r42,16r43,16r44,16r45,16r46,16r47,16r48,16r49,16r4a,16r4b,16r4c,16r4d,16r4e,16r4f,
+16r50,16r51,16r52,16r53,16r54,16r55,16r56,16r57,16r58,16r59,16r5a,16r5b,16r5c,16r5d,16r5e,16r5f,
+16r60,16r61,16r62,16r63,16r64,16r65,16r66,16r67,16r68,16r69,16r6a,16r6b,16r6c,16r6d,16r6e,16r6f,
+16r70,16r71,16r72,16r73,16r74,16r75,16r76,16r77,16r78,16r79,16r7a,16r7b,16r7c,16r7d,16r7e,16r7f,
+16r00c7, 16r00fc, 16r00e9, 16r00e2, 16r00e4, 16r00e0, 16r00e5, 16r00e7, # latin
+16r00ea, 16r00eb, 16r00e8, 16r00ef, 16r00ee, 16r00ec, 16r00c4, 16r00c5,
+16r00c9, 16r00e6, 16r00c6, 16r00f4, 16r00f6, 16r00f2, 16r00fb, 16r00f9,
+16r00ff, 16r00d6, 16r00dc, 16r00a2, 16r00a3, 16r00a5, 16r20a7, 16r0192,
+16r00e1, 16r00ed, 16r00f3, 16r00fa, 16r00f1, 16r00d1, 16r00aa, 16r00ba,
+16r00bf, 16r2310, 16r00ac, 16r00bd, 16r00bc, 16r00a1, 16r00ab, 16r00bb,
+16r2591, 16r2592, 16r2593, 16r2502, 16r2524, 16r2561, 16r2562, 16r2556, # forms
+16r2555, 16r2563, 16r2551, 16r2557, 16r255d, 16r255c, 16r255b, 16r2510,
+16r2514, 16r2534, 16r252c, 16r251c, 16r2500, 16r253c, 16r255e, 16r255f,
+16r255a, 16r2554, 16r2569, 16r2566, 16r2560, 16r2550, 16r256c, 16r2567,
+16r2568, 16r2564, 16r2565, 16r2559, 16r2558, 16r2552, 16r2553, 16r256b,
+16r256a, 16r2518, 16r250c, 16r2588, 16r2584, 16r258c, 16r2590, 16r2580,
+16r03b1, 16r00df, 16r0393, 16r03c0, 16r03a3, 16r03c3, 16r00b5, 16r03c4, # greek
+16r03a6, 16r0398, 16r2126, 16r03b4, 16r221e, 16r2205, 16r2208, 16r2229,
+16r2261, 16r00b1, 16r2265, 16r2264, 16r2320, 16r2321, 16r00f7, 16r2248, # math
+16r00b0, 16r2022, 16r00b7, 16r221a, 16r207f, 16r00b2, 16r220e, 16r00a0,
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/ibm850.b b/appl/lib/convcs/ibm850.b
new file mode 100644
index 00000000..350d834b
--- /dev/null
+++ b/appl/lib/convcs/ibm850.b
@@ -0,0 +1,34 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "ibm850";
+
+cstab := array [] of {
+16r00,16r01,16r02,16r03,16r04,16r05,16r06,16r07,16r08,16r09,16r0a,16r0b,16r0c,16r0d,16r0e,16r0f,
+16r10,16r11,16r12,16r13,16r14,16r15,16r16,16r17,16r18,16r19,16r1a,16r1b,16r1c,16r1d,16r1e,16r1f,
+16r20,16r21,16r22,16r23,16r24,16r25,16r26,16r27,16r28,16r29,16r2a,16r2b,16r2c,16r2d,16r2e,16r2f,
+16r30,16r31,16r32,16r33,16r34,16r35,16r36,16r37,16r38,16r39,16r3a,16r3b,16r3c,16r3d,16r3e,16r3f,
+16r40,16r41,16r42,16r43,16r44,16r45,16r46,16r47,16r48,16r49,16r4a,16r4b,16r4c,16r4d,16r4e,16r4f,
+16r50,16r51,16r52,16r53,16r54,16r55,16r56,16r57,16r58,16r59,16r5a,16r5b,16r5c,16r5d,16r5e,16r5f,
+16r60,16r61,16r62,16r63,16r64,16r65,16r66,16r67,16r68,16r69,16r6a,16r6b,16r6c,16r6d,16r6e,16r6f,
+16r70,16r71,16r72,16r73,16r74,16r75,16r76,16r77,16r78,16r79,16r7a,16r7b,16r7c,16r7d,16r7e,16r7f,
+16r00c7, 16r00fc, 16r00e9, 16r00e2, 16r00e4, 16r00e0, 16r00e5, 16r00e7, # latin-1 repertoire with forms
+16r00ea, 16r00eb, 16r00e8, 16r00ef, 16r00ee, 16r00ec, 16r00c4, 16r00c5,
+16r00c9, 16r00e6, 16r00c6, 16r00f4, 16r00f6, 16r00f2, 16r00fb, 16r00f9,
+16r00ff, 16r00d6, 16r00dc, 16r00f8, 16r00a3, 16r00d8, 16r00d7, 16r0192,
+16r00e1, 16r00ed, 16r00f3, 16r00fa, 16r00f1, 16r00d1, 16r00aa, 16r00ba,
+16r00bf, 16r00ae, 16r00ac, 16r00bd, 16r00bc, 16r00a1, 16r00ab, 16r00bb,
+16r2591, 16r2592, 16r2593, 16r2502, 16r2524, 16r00c1, 16r00c2, 16r00c0,
+16r00a9, 16r2563, 16r2551, 16r2557, 16r255d, 16r00a2, 16r00a5, 16r2510,
+16r2514, 16r2534, 16r252c, 16r251c, 16r2500, 16r253c, 16r00e3, 16r00c3,
+16r255a, 16r2554, 16r2569, 16r2566, 16r2560, 16r2550, 16r256c, 16r00a4,
+16r00f0, 16r00d0, 16r00ca, 16r00cb, 16r00c8, 16r0131, 16r00cd, 16r00ce,
+16r00cf, 16r2518, 16r250c, 16r2588, 16r2584, 16r00a6, 16r00cc, 16r2580,
+16r00d3, 16r00df, 16r00d4, 16r00d2, 16r00f5, 16r00d5, 16r00b5, 16r00fe,
+16r00de, 16r00da, 16r00db, 16r00d9, 16r00fd, 16r00dd, 16r00af, 16r00b4,
+16r00ad, 16r00b1, 16r2017, 16r00be, 16r00b6, 16r00a7, 16r00f7, 16r00b8,
+16r00b0, 16r00a8, 16r00b7, 16r00b9, 16r00b3, 16r00b2, 16r220e, 16r00a0,
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/ibm866.b b/appl/lib/convcs/ibm866.b
new file mode 100644
index 00000000..6e2aa0c2
--- /dev/null
+++ b/appl/lib/convcs/ibm866.b
@@ -0,0 +1,34 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "ibm866";
+
+cstab := array [] of {
+16r00,16r01,16r02,16r03,16r04,16r05,16r06,16r07,16r08,16r09,16r0a,16r0b,16r0c,16r0d,16r0e,16r0f,
+16r10,16r11,16r12,16r13,16r14,16r15,16r16,16r17,16r18,16r19,16r1a,16r1b,16r1c,16r1d,16r1e,16r1f,
+16r20,16r21,16r22,16r23,16r24,16r25,16r26,16r27,16r28,16r29,16r2a,16r2b,16r2c,16r2d,16r2e,16r2f,
+16r30,16r31,16r32,16r33,16r34,16r35,16r36,16r37,16r38,16r39,16r3a,16r3b,16r3c,16r3d,16r3e,16r3f,
+16r40,16r41,16r42,16r43,16r44,16r45,16r46,16r47,16r48,16r49,16r4a,16r4b,16r4c,16r4d,16r4e,16r4f,
+16r50,16r51,16r52,16r53,16r54,16r55,16r56,16r57,16r58,16r59,16r5a,16r5b,16r5c,16r5d,16r5e,16r5f,
+16r60,16r61,16r62,16r63,16r64,16r65,16r66,16r67,16r68,16r69,16r6a,16r6b,16r6c,16r6d,16r6e,16r6f,
+16r70,16r71,16r72,16r73,16r74,16r75,16r76,16r77,16r78,16r79,16r7a,16r7b,16r7c,16r7d,16r7e,16r7f,
+16r0410,16r0411,16r0412,16r0413,16r0414,16r0415,16r0416,16r0417,
+16r0418,16r0419,16r041a,16r041b,16r041c,16r041d,16r041e,16r041f,
+16r0420,16r0421,16r0422,16r0423,16r0424,16r0425,16r0426,16r0427,
+16r0428,16r0429,16r042a,16r042b,16r042c,16r042d,16r042e,16r042f,
+16r0430,16r0431,16r0432,16r0433,16r0434,16r0435,16r0436,16r0437,
+16r0438,16r0439,16r043a,16r043b,16r043c,16r043d,16r043e,16r043f,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+16r0440,16r0441,16r0442,16r0443,16r0444,16r0445,16r0446,16r0447,
+16r0448,16r0449,16r044a,16r044b,16r044c,16r044d,16r044e,16r044f,
+16r0401,16r0451, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/iso8859-1.b b/appl/lib/convcs/iso8859-1.b
new file mode 100644
index 00000000..bf8d8f16
--- /dev/null
+++ b/appl/lib/convcs/iso8859-1.b
@@ -0,0 +1,42 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "iso-8859-1";
+
+cstab := array [] of {
+ 16r00,16r01,16r02,16r03,16r04,16r05,16r06,16r07,
+ 16r08,16r09,16r0a,16r0b,16r0c,16r0d,16r0e,16r0f,
+ 16r10,16r11,16r12,16r13,16r14,16r15,16r16,16r17,
+ 16r18,16r19,16r1a,16r1b,16r1c,16r1d,16r1e,16r1f,
+ 16r20,16r21,16r22,16r23,16r24,16r25,16r26,16r27,
+ 16r28,16r29,16r2a,16r2b,16r2c,16r2d,16r2e,16r2f,
+ 16r30,16r31,16r32,16r33,16r34,16r35,16r36,16r37,
+ 16r38,16r39,16r3a,16r3b,16r3c,16r3d,16r3e,16r3f,
+ 16r40,16r41,16r42,16r43,16r44,16r45,16r46,16r47,
+ 16r48,16r49,16r4a,16r4b,16r4c,16r4d,16r4e,16r4f,
+ 16r50,16r51,16r52,16r53,16r54,16r55,16r56,16r57,
+ 16r58,16r59,16r5a,16r5b,16r5c,16r5d,16r5e,16r5f,
+ 16r60,16r61,16r62,16r63,16r64,16r65,16r66,16r67,
+ 16r68,16r69,16r6a,16r6b,16r6c,16r6d,16r6e,16r6f,
+ 16r70,16r71,16r72,16r73,16r74,16r75,16r76,16r77,
+ 16r78,16r79,16r7a,16r7b,16r7c,16r7d,16r7e,16r7f,
+ 16r80,16r81,16r82,16r83,16r84,16r85,16r86,16r87,
+ 16r88,16r89,16r8a,16r8b,16r8c,16r8d,16r8e,16r8f,
+ 16r90,16r91,16r92,16r93,16r94,16r95,16r96,16r97,
+ 16r98,16r99,16r9a,16r9b,16r9c,16r9d,16r9e,16r9f,
+ 16ra0,16ra1,16ra2,16ra3,16ra4,16ra5,16ra6,16ra7,
+ 16ra8,16ra9,16raa,16rab,16rac,16rad,16rae,16raf,
+ 16rb0,16rb1,16rb2,16rb3,16rb4,16rb5,16rb6,16rb7,
+ 16rb8,16rb9,16rba,16rbb,16rbc,16rbd,16rbe,16rbf,
+ 16rc0,16rc1,16rc2,16rc3,16rc4,16rc5,16rc6,16rc7,
+ 16rc8,16rc9,16rca,16rcb,16rcc,16rcd,16rce,16rcf,
+ 16rd0,16rd1,16rd2,16rd3,16rd4,16rd5,16rd6,16rd7,
+ 16rd8,16rd9,16rda,16rdb,16rdc,16rdd,16rde,16rdf,
+ 16re0,16re1,16re2,16re3,16re4,16re5,16re6,16re7,
+ 16re8,16re9,16rea,16reb,16rec,16red,16ree,16ref,
+ 16rf0,16rf1,16rf2,16rf3,16rf4,16rf5,16rf6,16rf7,
+ 16rf8,16rf9,16rfa,16rfb,16rfc,16rfd,16rfe,16rff,
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/iso8859-10.b b/appl/lib/convcs/iso8859-10.b
new file mode 100644
index 00000000..dd1063bd
--- /dev/null
+++ b/appl/lib/convcs/iso8859-10.b
@@ -0,0 +1,43 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "iso-8859-10";
+
+# from dkuug.dk:i18n/charmaps/ISO_8859-10:1993
+cstab := array [] of {
+ 16r0000,16r0001,16r0002,16r0003,16r0004,16r0005,16r0006,16r0007,
+ 16r0008,16r0009,16r000a,16r000b,16r000c,16r000d,16r000e,16r000f,
+ 16r0010,16r0011,16r0012,16r0013,16r0014,16r0015,16r0016,16r0017,
+ 16r0018,16r0019,16r001a,16r001b,16r001c,16r001d,16r001e,16r001f,
+ 16r0020,16r0021,16r0022,16r0023,16r0024,16r0025,16r0026,16r0027,
+ 16r0028,16r0029,16r002a,16r002b,16r002c,16r002d,16r002e,16r002f,
+ 16r0030,16r0031,16r0032,16r0033,16r0034,16r0035,16r0036,16r0037,
+ 16r0038,16r0039,16r003a,16r003b,16r003c,16r003d,16r003e,16r003f,
+ 16r0040,16r0041,16r0042,16r0043,16r0044,16r0045,16r0046,16r0047,
+ 16r0048,16r0049,16r004a,16r004b,16r004c,16r004d,16r004e,16r004f,
+ 16r0050,16r0051,16r0052,16r0053,16r0054,16r0055,16r0056,16r0057,
+ 16r0058,16r0059,16r005a,16r005b,16r005c,16r005d,16r005e,16r005f,
+ 16r0060,16r0061,16r0062,16r0063,16r0064,16r0065,16r0066,16r0067,
+ 16r0068,16r0069,16r006a,16r006b,16r006c,16r006d,16r006e,16r006f,
+ 16r0070,16r0071,16r0072,16r0073,16r0074,16r0075,16r0076,16r0077,
+ 16r0078,16r0079,16r007a,16r007b,16r007c,16r007d,16r007e,16r007f,
+ 16r0080,16r0081,16r0082,16r0083,16r0084,16r0085,16r0086,16r0087,
+ 16r0088,16r0089,16r008a,16r008b,16r008c,16r008d,16r008e,16r008f,
+ 16r0090,16r0091,16r0092,16r0093,16r0094,16r0095,16r0096,16r0097,
+ 16r0098,16r0099,16r009a,16r009b,16r009c,16r009d,16r009e,16r009f,
+ 16r00a0,16r0104,16r0112,16r0122,16r012a,16r0128,16r0136,16r00a7,
+ 16r013b,16r0110,16r0160,16r0166,16r017d,16r00ad,16r016a,16r014a,
+ 16r00b0,16r0105,16r0113,16r0123,16r012b,16r0129,16r0137,16r00b7,
+ 16r013c,16r0110,16r0161,16r0167,16r017e,16r2014,16r016b,16r014b,
+ 16r0100,16r00c1,16r00c2,16r00c3,16r00c4,16r00c5,16r00c6,16r012e,
+ 16r010c,16r00c9,16r0118,16r00cb,16r0116,16r00cd,16r00ce,16r00cf,
+ 16r00d0,16r0145,16r014c,16r00d3,16r00d4,16r00d5,16r00d6,16r0168,
+ 16r00d8,16r0172,16r00da,16r00db,16r00dc,16r00dd,16r00de,16r00df,
+ 16r0101,16r00e1,16r00e2,16r00e3,16r00e4,16r00e5,16r00e6,16r012f,
+ 16r010d,16r00e9,16r0119,16r00eb,16r0117,16r00ed,16r00ee,16r00ef,
+ 16r00f0,16r0146,16r014d,16r00f3,16r00f4,16r00f5,16r00f6,16r0169,
+ 16r00f8,16r0173,16r00fa,16r00fb,16r00fc,16r00fd,16r00fe,16r0138,
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/iso8859-2.b b/appl/lib/convcs/iso8859-2.b
new file mode 100644
index 00000000..15f6c549
--- /dev/null
+++ b/appl/lib/convcs/iso8859-2.b
@@ -0,0 +1,42 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "iso-8859-2";
+
+cstab := array [] of {
+ 16r0000,16r0001,16r0002,16r0003,16r0004,16r0005,16r0006,16r0007,
+ 16r0008,16r0009,16r000a,16r000b,16r000c,16r000d,16r000e,16r000f,
+ 16r0010,16r0011,16r0012,16r0013,16r0014,16r0015,16r0016,16r0017,
+ 16r0018,16r0019,16r001a,16r001b,16r001c,16r001d,16r001e,16r001f,
+ 16r0020,16r0021,16r0022,16r0023,16r0024,16r0025,16r0026,16r0027,
+ 16r0028,16r0029,16r002a,16r002b,16r002c,16r002d,16r002e,16r002f,
+ 16r0030,16r0031,16r0032,16r0033,16r0034,16r0035,16r0036,16r0037,
+ 16r0038,16r0039,16r003a,16r003b,16r003c,16r003d,16r003e,16r003f,
+ 16r0040,16r0041,16r0042,16r0043,16r0044,16r0045,16r0046,16r0047,
+ 16r0048,16r0049,16r004a,16r004b,16r004c,16r004d,16r004e,16r004f,
+ 16r0050,16r0051,16r0052,16r0053,16r0054,16r0055,16r0056,16r0057,
+ 16r0058,16r0059,16r005a,16r005b,16r005c,16r005d,16r005e,16r005f,
+ 16r0060,16r0061,16r0062,16r0063,16r0064,16r0065,16r0066,16r0067,
+ 16r0068,16r0069,16r006a,16r006b,16r006c,16r006d,16r006e,16r006f,
+ 16r0070,16r0071,16r0072,16r0073,16r0074,16r0075,16r0076,16r0077,
+ 16r0078,16r0079,16r007a,16r007b,16r007c,16r007d,16r007e,16r007f,
+ 16r0080,16r0081,16r0082,16r0083,16r0084,16r0085,16r0086,16r0087,
+ 16r0088,16r0089,16r008a,16r008b,16r008c,16r008d,16r008e,16r008f,
+ 16r0090,16r0091,16r0092,16r0093,16r0094,16r0095,16r0096,16r0097,
+ 16r0098,16r0099,16r009a,16r009b,16r009c,16r009d,16r009e,16r009f,
+ 16r00a0,16r0104,16r02d8,16r0141,16r00a4,16r013d,16r015a,16r00a7,
+ 16r00a8,16r0160,16r015e,16r0164,16r0179,16r00ad,16r017d,16r017b,
+ 16r00b0,16r0105,16r02db,16r0142,16r00b4,16r013e,16r015b,16r02c7,
+ 16r00b8,16r0161,16r015f,16r0165,16r017a,16r02dd,16r017e,16r017c,
+ 16r0154,16r00c1,16r00c2,16r0102,16r00c4,16r0139,16r0106,16r00c7,
+ 16r010c,16r00c9,16r0118,16r00cb,16r011a,16r00cd,16r00ce,16r010e,
+ 16r0110,16r0143,16r0147,16r00d3,16r00d4,16r0150,16r00d6,16r00d7,
+ 16r0158,16r016e,16r00da,16r0170,16r00dc,16r00dd,16r0162,16r00df,
+ 16r0155,16r00e1,16r00e2,16r0103,16r00e4,16r013a,16r0107,16r00e7,
+ 16r010d,16r00e9,16r0119,16r00eb,16r011b,16r00ed,16r00ee,16r010f,
+ 16r0111,16r0144,16r0148,16r00f3,16r00f4,16r0151,16r00f6,16r00f7,
+ 16r0159,16r016f,16r00fa,16r0171,16r00fc,16r00fd,16r0163,16r02d9,
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/iso8859-3.b b/appl/lib/convcs/iso8859-3.b
new file mode 100644
index 00000000..e4f64466
--- /dev/null
+++ b/appl/lib/convcs/iso8859-3.b
@@ -0,0 +1,42 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "iso-8859-3";
+
+cstab := array [] of {
+ 16r0000,16r0001,16r0002,16r0003,16r0004,16r0005,16r0006,16r0007,
+ 16r0008,16r0009,16r000a,16r000b,16r000c,16r000d,16r000e,16r000f,
+ 16r0010,16r0011,16r0012,16r0013,16r0014,16r0015,16r0016,16r0017,
+ 16r0018,16r0019,16r001a,16r001b,16r001c,16r001d,16r001e,16r001f,
+ 16r0020,16r0021,16r0022,16r0023,16r0024,16r0025,16r0026,16r0027,
+ 16r0028,16r0029,16r002a,16r002b,16r002c,16r002d,16r002e,16r002f,
+ 16r0030,16r0031,16r0032,16r0033,16r0034,16r0035,16r0036,16r0037,
+ 16r0038,16r0039,16r003a,16r003b,16r003c,16r003d,16r003e,16r003f,
+ 16r0040,16r0041,16r0042,16r0043,16r0044,16r0045,16r0046,16r0047,
+ 16r0048,16r0049,16r004a,16r004b,16r004c,16r004d,16r004e,16r004f,
+ 16r0050,16r0051,16r0052,16r0053,16r0054,16r0055,16r0056,16r0057,
+ 16r0058,16r0059,16r005a,16r005b,16r005c,16r005d,16r005e,16r005f,
+ 16r0060,16r0061,16r0062,16r0063,16r0064,16r0065,16r0066,16r0067,
+ 16r0068,16r0069,16r006a,16r006b,16r006c,16r006d,16r006e,16r006f,
+ 16r0070,16r0071,16r0072,16r0073,16r0074,16r0075,16r0076,16r0077,
+ 16r0078,16r0079,16r007a,16r007b,16r007c,16r007d,16r007e,16r007f,
+ 16r0080,16r0081,16r0082,16r0083,16r0084,16r0085,16r0086,16r0087,
+ 16r0088,16r0089,16r008a,16r008b,16r008c,16r008d,16r008e,16r008f,
+ 16r0090,16r0091,16r0092,16r0093,16r0094,16r0095,16r0096,16r0097,
+ 16r0098,16r0099,16r009a,16r009b,16r009c,16r009d,16r009e,16r009f,
+ 16r00a0,16r0126,16r02d8,16r00a3,16r00a4, -1,16r0124,16r00a7,
+ 16r00a8,16r0130,16r015e,16r011e,16r0134,16r00ad, -1,16r017b,
+ 16r00b0,16r0127,16r00b2,16r00b3,16r00b4,16r00b5,16r0125,16r00b7,
+ 16r00b8,16r0131,16r015f,16r011f,16r0135,16r00bd, -1,16r017c,
+ 16r00c0,16r00c1,16r00c2, -1,16r00c4,16r010a,16r0108,16r00c7,
+ 16r00c8,16r00c9,16r00ca,16r00cb,16r00cc,16r00cd,16r00ce,16r00cf,
+ -1,16r00d1,16r00d2,16r00d3,16r00d4,16r0120,16r00d6,16r00d7,
+ 16r011c,16r00d9,16r00da,16r00db,16r00dc,16r016c,16r015c,16r00df,
+ 16r00e0,16r00e1,16r00e2, -1,16r00e4,16r010b,16r0109,16r00e7,
+ 16r00e8,16r00e9,16r00ea,16r00eb,16r00ec,16r00ed,16r00ee,16r00ef,
+ -1,16r00f1,16r00f2,16r00f3,16r00f4,16r0121,16r00f6,16r00f7,
+ 16r011d,16r00f9,16r00fa,16r00fb,16r00fc,16r016d,16r015d,16r02d9,
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/iso8859-4.b b/appl/lib/convcs/iso8859-4.b
new file mode 100644
index 00000000..fbef2274
--- /dev/null
+++ b/appl/lib/convcs/iso8859-4.b
@@ -0,0 +1,42 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "iso-8859-4";
+
+cstab := array [] of {
+ 16r0000,16r0001,16r0002,16r0003,16r0004,16r0005,16r0006,16r0007,
+ 16r0008,16r0009,16r000a,16r000b,16r000c,16r000d,16r000e,16r000f,
+ 16r0010,16r0011,16r0012,16r0013,16r0014,16r0015,16r0016,16r0017,
+ 16r0018,16r0019,16r001a,16r001b,16r001c,16r001d,16r001e,16r001f,
+ 16r0020,16r0021,16r0022,16r0023,16r0024,16r0025,16r0026,16r0027,
+ 16r0028,16r0029,16r002a,16r002b,16r002c,16r002d,16r002e,16r002f,
+ 16r0030,16r0031,16r0032,16r0033,16r0034,16r0035,16r0036,16r0037,
+ 16r0038,16r0039,16r003a,16r003b,16r003c,16r003d,16r003e,16r003f,
+ 16r0040,16r0041,16r0042,16r0043,16r0044,16r0045,16r0046,16r0047,
+ 16r0048,16r0049,16r004a,16r004b,16r004c,16r004d,16r004e,16r004f,
+ 16r0050,16r0051,16r0052,16r0053,16r0054,16r0055,16r0056,16r0057,
+ 16r0058,16r0059,16r005a,16r005b,16r005c,16r005d,16r005e,16r005f,
+ 16r0060,16r0061,16r0062,16r0063,16r0064,16r0065,16r0066,16r0067,
+ 16r0068,16r0069,16r006a,16r006b,16r006c,16r006d,16r006e,16r006f,
+ 16r0070,16r0071,16r0072,16r0073,16r0074,16r0075,16r0076,16r0077,
+ 16r0078,16r0079,16r007a,16r007b,16r007c,16r007d,16r007e,16r007f,
+ 16r0080,16r0081,16r0082,16r0083,16r0084,16r0085,16r0086,16r0087,
+ 16r0088,16r0089,16r008a,16r008b,16r008c,16r008d,16r008e,16r008f,
+ 16r0090,16r0091,16r0092,16r0093,16r0094,16r0095,16r0096,16r0097,
+ 16r0098,16r0099,16r009a,16r009b,16r009c,16r009d,16r009e,16r009f,
+ 16r00a0,16r0104,16r0138,16r0156,16r00a4,16r0128,16r013b,16r00a7,
+ 16r00a8,16r0160,16r0112,16r0122,16r0166,16r00ad,16r017d,16r00af,
+ 16r00b0,16r0105,16r02db,16r0157,16r00b4,16r0129,16r013c,16r02c7,
+ 16r00b8,16r0161,16r0113,16r0123,16r0167,16r014a,16r017e,16r014b,
+ 16r0100,16r00c1,16r00c2,16r00c3,16r00c4,16r00c5,16r00c6,16r012e,
+ 16r010c,16r00c9,16r0118,16r00cb,16r0116,16r00cd,16r00ce,16r012a,
+ 16r0110,16r0145,16r014c,16r0136,16r00d4,16r00d5,16r00d6,16r00d7,
+ 16r00d8,16r0172,16r00da,16r00db,16r00dc,16r0168,16r016a,16r00df,
+ 16r0101,16r00e1,16r00e2,16r00e3,16r00e4,16r00e5,16r00e6,16r012f,
+ 16r010d,16r00e9,16r0119,16r00eb,16r0117,16r00ed,16r00ee,16r012b,
+ 16r0111,16r0146,16r014d,16r0137,16r00f4,16r00f5,16r00f6,16r00f7,
+ 16r00f8,16r0173,16r00fa,16r00fb,16r00fc,16r0169,16r016b,16r02d9,
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/iso8859-5.b b/appl/lib/convcs/iso8859-5.b
new file mode 100644
index 00000000..58d4e5b9
--- /dev/null
+++ b/appl/lib/convcs/iso8859-5.b
@@ -0,0 +1,42 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "iso-8859-5";
+
+cstab := array [] of {
+ 16r0000,16r0001,16r0002,16r0003,16r0004,16r0005,16r0006,16r0007,
+ 16r0008,16r0009,16r000a,16r000b,16r000c,16r000d,16r000e,16r000f,
+ 16r0010,16r0011,16r0012,16r0013,16r0014,16r0015,16r0016,16r0017,
+ 16r0018,16r0019,16r001a,16r001b,16r001c,16r001d,16r001e,16r001f,
+ 16r0020,16r0021,16r0022,16r0023,16r0024,16r0025,16r0026,16r0027,
+ 16r0028,16r0029,16r002a,16r002b,16r002c,16r002d,16r002e,16r002f,
+ 16r0030,16r0031,16r0032,16r0033,16r0034,16r0035,16r0036,16r0037,
+ 16r0038,16r0039,16r003a,16r003b,16r003c,16r003d,16r003e,16r003f,
+ 16r0040,16r0041,16r0042,16r0043,16r0044,16r0045,16r0046,16r0047,
+ 16r0048,16r0049,16r004a,16r004b,16r004c,16r004d,16r004e,16r004f,
+ 16r0050,16r0051,16r0052,16r0053,16r0054,16r0055,16r0056,16r0057,
+ 16r0058,16r0059,16r005a,16r005b,16r005c,16r005d,16r005e,16r005f,
+ 16r0060,16r0061,16r0062,16r0063,16r0064,16r0065,16r0066,16r0067,
+ 16r0068,16r0069,16r006a,16r006b,16r006c,16r006d,16r006e,16r006f,
+ 16r0070,16r0071,16r0072,16r0073,16r0074,16r0075,16r0076,16r0077,
+ 16r0078,16r0079,16r007a,16r007b,16r007c,16r007d,16r007e,16r007f,
+ 16r0080,16r0081,16r0082,16r0083,16r0084,16r0085,16r0086,16r0087,
+ 16r0088,16r0089,16r008a,16r008b,16r008c,16r008d,16r008e,16r008f,
+ 16r0090,16r0091,16r0092,16r0093,16r0094,16r0095,16r0096,16r0097,
+ 16r0098,16r0099,16r009a,16r009b,16r009c,16r009d,16r009e,16r009f,
+ 16r00a0,16r0401,16r0402,16r0403,16r0404,16r0405,16r0406,16r0407,
+ 16r0408,16r0409,16r040a,16r040b,16r040c,16r00ad,16r040e,16r040f,
+ 16r0410,16r0411,16r0412,16r0413,16r0414,16r0415,16r0416,16r0417,
+ 16r0418,16r0419,16r041a,16r041b,16r041c,16r041d,16r041e,16r041f,
+ 16r0420,16r0421,16r0422,16r0423,16r0424,16r0425,16r0426,16r0427,
+ 16r0428,16r0429,16r042a,16r042b,16r042c,16r042d,16r042e,16r042f,
+ 16r0430,16r0431,16r0432,16r0433,16r0434,16r0435,16r0436,16r0437,
+ 16r0438,16r0439,16r043a,16r043b,16r043c,16r043d,16r043e,16r043f,
+ 16r0440,16r0441,16r0442,16r0443,16r0444,16r0445,16r0446,16r0447,
+ 16r0448,16r0449,16r044a,16r044b,16r044c,16r044d,16r044e,16r044f,
+ 16r2116,16r0451,16r0452,16r0453,16r0454,16r0455,16r0456,16r0457,
+ 16r0458,16r0459,16r045a,16r045b,16r045c,16r00a7,16r045e,16r045f,
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/iso8859-6.b b/appl/lib/convcs/iso8859-6.b
new file mode 100644
index 00000000..26c539b7
--- /dev/null
+++ b/appl/lib/convcs/iso8859-6.b
@@ -0,0 +1,42 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "iso-8859-6";
+
+cstab := array [] of {
+ 16r0000,16r0001,16r0002,16r0003,16r0004,16r0005,16r0006,16r0007,
+ 16r0008,16r0009,16r000a,16r000b,16r000c,16r000d,16r000e,16r000f,
+ 16r0010,16r0011,16r0012,16r0013,16r0014,16r0015,16r0016,16r0017,
+ 16r0018,16r0019,16r001a,16r001b,16r001c,16r001d,16r001e,16r001f,
+ 16r0020,16r0021,16r0022,16r0023,16r0024,16r0025,16r0026,16r0027,
+ 16r0028,16r0029,16r002a,16r002b,16r002c,16r002d,16r002e,16r002f,
+ 16r0030,16r0031,16r0032,16r0033,16r0034,16r0035,16r0036,16r0037,
+ 16r0038,16r0039,16r003a,16r003b,16r003c,16r003d,16r003e,16r003f,
+ 16r0040,16r0041,16r0042,16r0043,16r0044,16r0045,16r0046,16r0047,
+ 16r0048,16r0049,16r004a,16r004b,16r004c,16r004d,16r004e,16r004f,
+ 16r0050,16r0051,16r0052,16r0053,16r0054,16r0055,16r0056,16r0057,
+ 16r0058,16r0059,16r005a,16r005b,16r005c,16r005d,16r005e,16r005f,
+ 16r0060,16r0061,16r0062,16r0063,16r0064,16r0065,16r0066,16r0067,
+ 16r0068,16r0069,16r006a,16r006b,16r006c,16r006d,16r006e,16r006f,
+ 16r0070,16r0071,16r0072,16r0073,16r0074,16r0075,16r0076,16r0077,
+ 16r0078,16r0079,16r007a,16r007b,16r007c,16r007d,16r007e,16r007f,
+ 16r0080,16r0081,16r0082,16r0083,16r0084,16r0085,16r0086,16r0087,
+ 16r0088,16r0089,16r008a,16r008b,16r008c,16r008d,16r008e,16r008f,
+ 16r0090,16r0091,16r0092,16r0093,16r0094,16r0095,16r0096,16r0097,
+ 16r0098,16r0099,16r009a,16r009b,16r009c,16r009d,16r009e,16r009f,
+ 16r00a0, -1, -1, -1,16r00a4, -1, -1, -1,
+ -1, -1, -1, -1,16r060c,16r00ad, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1,16r061b, -1, -1, -1,16r061f,
+ -1,16r0621,16r0622,16r0623,16r0624,16r0625,16r0626,16r0627,
+ 16r0628,16r0629,16r062a,16r062b,16r062c,16r062d,16r062e,16r062f,
+ 16r0630,16r0631,16r0632,16r0633,16r0634,16r0635,16r0636,16r0637,
+ 16r0638,16r0639,16r063a, -1, -1, -1, -1, -1,
+ 16r0640,16r0641,16r0642,16r0643,16r0644,16r0645,16r0646,16r0647,
+ 16r0648,16r0649,16r064a,16r064b,16r064c,16r064d,16r064e,16r064f,
+ 16r0650,16r0651,16r0652, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/iso8859-7.b b/appl/lib/convcs/iso8859-7.b
new file mode 100644
index 00000000..1a3c01b9
--- /dev/null
+++ b/appl/lib/convcs/iso8859-7.b
@@ -0,0 +1,42 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "iso-8859-7";
+
+cstab := array [] of {
+ 16r0000,16r0001,16r0002,16r0003,16r0004,16r0005,16r0006,16r0007,
+ 16r0008,16r0009,16r000a,16r000b,16r000c,16r000d,16r000e,16r000f,
+ 16r0010,16r0011,16r0012,16r0013,16r0014,16r0015,16r0016,16r0017,
+ 16r0018,16r0019,16r001a,16r001b,16r001c,16r001d,16r001e,16r001f,
+ 16r0020,16r0021,16r0022,16r0023,16r0024,16r0025,16r0026,16r0027,
+ 16r0028,16r0029,16r002a,16r002b,16r002c,16r002d,16r002e,16r002f,
+ 16r0030,16r0031,16r0032,16r0033,16r0034,16r0035,16r0036,16r0037,
+ 16r0038,16r0039,16r003a,16r003b,16r003c,16r003d,16r003e,16r003f,
+ 16r0040,16r0041,16r0042,16r0043,16r0044,16r0045,16r0046,16r0047,
+ 16r0048,16r0049,16r004a,16r004b,16r004c,16r004d,16r004e,16r004f,
+ 16r0050,16r0051,16r0052,16r0053,16r0054,16r0055,16r0056,16r0057,
+ 16r0058,16r0059,16r005a,16r005b,16r005c,16r005d,16r005e,16r005f,
+ 16r0060,16r0061,16r0062,16r0063,16r0064,16r0065,16r0066,16r0067,
+ 16r0068,16r0069,16r006a,16r006b,16r006c,16r006d,16r006e,16r006f,
+ 16r0070,16r0071,16r0072,16r0073,16r0074,16r0075,16r0076,16r0077,
+ 16r0078,16r0079,16r007a,16r007b,16r007c,16r007d,16r007e,16r007f,
+ 16r0080,16r0081,16r0082,16r0083,16r0084,16r0085,16r0086,16r0087,
+ 16r0088,16r0089,16r008a,16r008b,16r008c,16r008d,16r008e,16r008f,
+ 16r0090,16r0091,16r0092,16r0093,16r0094,16r0095,16r0096,16r0097,
+ 16r0098,16r0099,16r009a,16r009b,16r009c,16r009d,16r009e,16r009f,
+ 16r00a0,16r2018,16r2019,16r00a3, -1, -1,16r00a6,16r00a7,
+ 16r00a8,16r00a9, -1,16r00ab,16r00ac,16r00ad, -1,16r2015,
+ 16r00b0,16r00b1,16r00b2,16r00b3,16r0384,16r0385,16r0386,16r00b7,
+ 16r0388,16r0389,16r038a,16r00bb,16r038c,16r00bd,16r038e,16r038f,
+ 16r0390,16r0391,16r0392,16r0393,16r0394,16r0395,16r0396,16r0397,
+ 16r0398,16r0399,16r039a,16r039b,16r039c,16r039d,16r039e,16r039f,
+ 16r03a0,16r03a1, -1,16r03a3,16r03a4,16r03a5,16r03a6,16r03a7,
+ 16r03a8,16r03a9,16r03aa,16r03ab,16r03ac,16r03ad,16r03ae,16r03af,
+ 16r03b0,16r03b1,16r03b2,16r03b3,16r03b4,16r03b5,16r03b6,16r03b7,
+ 16r03b8,16r03b9,16r03ba,16r03bb,16r03bc,16r03bd,16r03be,16r03bf,
+ 16r03c0,16r03c1,16r03c2,16r03c3,16r03c4,16r03c5,16r03c6,16r03c7,
+ 16r03c8,16r03c9,16r03ca,16r03cb,16r03cc,16r03cd,16r03ce, -1,
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/iso8859-8.b b/appl/lib/convcs/iso8859-8.b
new file mode 100644
index 00000000..35bdd8bb
--- /dev/null
+++ b/appl/lib/convcs/iso8859-8.b
@@ -0,0 +1,42 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "iso-8859-8";
+
+cstab := array [] of {
+ 16r0000,16r0001,16r0002,16r0003,16r0004,16r0005,16r0006,16r0007,
+ 16r0008,16r0009,16r000a,16r000b,16r000c,16r000d,16r000e,16r000f,
+ 16r0010,16r0011,16r0012,16r0013,16r0014,16r0015,16r0016,16r0017,
+ 16r0018,16r0019,16r001a,16r001b,16r001c,16r001d,16r001e,16r001f,
+ 16r0020,16r0021,16r0022,16r0023,16r0024,16r0025,16r0026,16r0027,
+ 16r0028,16r0029,16r002a,16r002b,16r002c,16r002d,16r002e,16r002f,
+ 16r0030,16r0031,16r0032,16r0033,16r0034,16r0035,16r0036,16r0037,
+ 16r0038,16r0039,16r003a,16r003b,16r003c,16r003d,16r003e,16r003f,
+ 16r0040,16r0041,16r0042,16r0043,16r0044,16r0045,16r0046,16r0047,
+ 16r0048,16r0049,16r004a,16r004b,16r004c,16r004d,16r004e,16r004f,
+ 16r0050,16r0051,16r0052,16r0053,16r0054,16r0055,16r0056,16r0057,
+ 16r0058,16r0059,16r005a,16r005b,16r005c,16r005d,16r005e,16r005f,
+ 16r0060,16r0061,16r0062,16r0063,16r0064,16r0065,16r0066,16r0067,
+ 16r0068,16r0069,16r006a,16r006b,16r006c,16r006d,16r006e,16r006f,
+ 16r0070,16r0071,16r0072,16r0073,16r0074,16r0075,16r0076,16r0077,
+ 16r0078,16r0079,16r007a,16r007b,16r007c,16r007d,16r007e,16r007f,
+ 16r0080,16r0081,16r0082,16r0083,16r0084,16r0085,16r0086,16r0087,
+ 16r0088,16r0089,16r008a,16r008b,16r008c,16r008d,16r008e,16r008f,
+ 16r0090,16r0091,16r0092,16r0093,16r0094,16r0095,16r0096,16r0097,
+ 16r0098,16r0099,16r009a,16r009b,16r009c,16r009d,16r009e,16r009f,
+ 16r00a0, -1,16r00a2,16r00a3,16r00a4,16r00a5,16r00a6,16r00a7,
+ 16r00a8,16r00a9,16r00d7,16r00ab,16r00ac,16r00ad,16r00ae,16r203e,
+ 16r00b0,16r00b1,16r00b2,16r00b3,16r00b4,16r00b5,16r00b6,16r00b7,
+ 16r00b8,16r00b9,16r00f7,16r00bb,16r00bc,16r00bd,16r00be, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1,16r2017,
+ 16r05d0,16r05d1,16r05d2,16r05d3,16r05d4,16r05d5,16r05d6,16r05d7,
+ 16r05d8,16r05d9,16r05da,16r05db,16r05dc,16r05dd,16r05de,16r05df,
+ 16r05e0,16r05e1,16r05e2,16r05e3,16r05e4,16r05e5,16r05e6,16r05e7,
+ 16r05e8,16r05e9,16r05ea, -1, -1, -1, -1, -1,
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/iso8859-9.b b/appl/lib/convcs/iso8859-9.b
new file mode 100644
index 00000000..53b3a5e3
--- /dev/null
+++ b/appl/lib/convcs/iso8859-9.b
@@ -0,0 +1,42 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "iso-8859-9";
+
+cstab := array [] of {
+ 16r0000,16r0001,16r0002,16r0003,16r0004,16r0005,16r0006,16r0007,
+ 16r0008,16r0009,16r000a,16r000b,16r000c,16r000d,16r000e,16r000f,
+ 16r0010,16r0011,16r0012,16r0013,16r0014,16r0015,16r0016,16r0017,
+ 16r0018,16r0019,16r001a,16r001b,16r001c,16r001d,16r001e,16r001f,
+ 16r0020,16r0021,16r0022,16r0023,16r0024,16r0025,16r0026,16r0027,
+ 16r0028,16r0029,16r002a,16r002b,16r002c,16r002d,16r002e,16r002f,
+ 16r0030,16r0031,16r0032,16r0033,16r0034,16r0035,16r0036,16r0037,
+ 16r0038,16r0039,16r003a,16r003b,16r003c,16r003d,16r003e,16r003f,
+ 16r0040,16r0041,16r0042,16r0043,16r0044,16r0045,16r0046,16r0047,
+ 16r0048,16r0049,16r004a,16r004b,16r004c,16r004d,16r004e,16r004f,
+ 16r0050,16r0051,16r0052,16r0053,16r0054,16r0055,16r0056,16r0057,
+ 16r0058,16r0059,16r005a,16r005b,16r005c,16r005d,16r005e,16r005f,
+ 16r0060,16r0061,16r0062,16r0063,16r0064,16r0065,16r0066,16r0067,
+ 16r0068,16r0069,16r006a,16r006b,16r006c,16r006d,16r006e,16r006f,
+ 16r0070,16r0071,16r0072,16r0073,16r0074,16r0075,16r0076,16r0077,
+ 16r0078,16r0079,16r007a,16r007b,16r007c,16r007d,16r007e,16r007f,
+ 16r0080,16r0081,16r0082,16r0083,16r0084,16r0085,16r0086,16r0087,
+ 16r0088,16r0089,16r008a,16r008b,16r008c,16r008d,16r008e,16r008f,
+ 16r0090,16r0091,16r0092,16r0093,16r0094,16r0095,16r0096,16r0097,
+ 16r0098,16r0099,16r009a,16r009b,16r009c,16r009d,16r009e,16r009f,
+ 16r00a0,16r00a1,16r00a2,16r00a3,16r00a4,16r00a5,16r00a6,16r00a7,
+ 16r00a8,16r00a9,16r00aa,16r00ab,16r00ac,16r00ad,16r00ae,16r00af,
+ 16r00b0,16r00b1,16r00b2,16r00b3,16r00b4,16r00b5,16r00b6,16r00b7,
+ 16r00b8,16r00b9,16r00ba,16r00bb,16r00bc,16r00bd,16r00be,16r00bf,
+ 16r00c0,16r00c1,16r00c2,16r00c3,16r00c4,16r00c5,16r00c6,16r00c7,
+ 16r00c8,16r00c9,16r00ca,16r00cb,16r00cc,16r00cd,16r00ce,16r00cf,
+ 16r011e,16r00d1,16r00d2,16r00d3,16r00d4,16r00d5,16r00d6,16r00d7,
+ 16r00d8,16r00d9,16r00da,16r00db,16r00dc,16r0130,16r015e,16r00df,
+ 16r00e0,16r00e1,16r00e2,16r00e3,16r00e4,16r00e5,16r00e6,16r00e7,
+ 16r00e8,16r00e9,16r00ea,16r00eb,16r00ec,16r00ed,16r00ee,16r00ef,
+ 16r011f,16r00f1,16r00f2,16r00f3,16r00f4,16r00f5,16r00f6,16r00f7,
+ 16r00f8,16r00f9,16r00fa,16r00fb,16r00fc,16r0131,16r015f,16r00ff,
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/koi8-r.b b/appl/lib/convcs/koi8-r.b
new file mode 100644
index 00000000..6fe1c053
--- /dev/null
+++ b/appl/lib/convcs/koi8-r.b
@@ -0,0 +1,43 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET: con "koi8-r";
+
+cstab := array [] of {
+ 16r0000,16r0001,16r0002,16r0003,16r0004,16r0005,16r0006,16r0007,
+ 16r0008,16r0009,16r000a,16r000b,16r000c,16r000d,16r000e,16r000f,
+ 16r0010,16r0011,16r0012,16r0013,16r0014,16r0015,16r0016,16r0017,
+ 16r0018,16r0019,16r001a,16r001b,16r001c,16r001d,16r001e,16r001f,
+ 16r0020,16r0021,16r0022,16r0023,16r0024,16r0025,16r0026,16r0027,
+ 16r0028,16r0029,16r002a,16r002b,16r002c,16r002d,16r002e,16r002f,
+ 16r0030,16r0031,16r0032,16r0033,16r0034,16r0035,16r0036,16r0037,
+ 16r0038,16r0039,16r003a,16r003b,16r003c,16r003d,16r003e,16r003f,
+ 16r0040,16r0041,16r0042,16r0043,16r0044,16r0045,16r0046,16r0047,
+ 16r0048,16r0049,16r004a,16r004b,16r004c,16r004d,16r004e,16r004f,
+ 16r0050,16r0051,16r0052,16r0053,16r0054,16r0055,16r0056,16r0057,
+ 16r0058,16r0059,16r005a,16r005b,16r005c,16r005d,16r005e,16r005f,
+ 16r0060,16r0061,16r0062,16r0063,16r0064,16r0065,16r0066,16r0067,
+ 16r0068,16r0069,16r006a,16r006b,16r006c,16r006d,16r006e,16r006f,
+ 16r0070,16r0071,16r0072,16r0073,16r0074,16r0075,16r0076,16r0077,
+ 16r0078,16r0079,16r007a,16r007b,16r007c,16r007d,16r007e,16r007f,
+ 16r2500,16r2502,16r250c,16r2510,16r2514,16r2518,16r251c,16r2524,
+ 16r252c,16r2534,16r253c,16r2580,16r2584,16r2588,16r258c,16r2590,
+ 16r2591,16r2592,16r2593,16r2320,16r25a0,16r2219,16r221a,16r2248,
+ 16r2264,16r2265,16r00a0,16r2321,16r00b0,16r00b2,16r00b7,16r00f7,
+ 16r2550,16r2551,16r2552,16r0451,16r2553,16r2554,16r2555,16r2556,
+ 16r2557,16r2558,16r2559,16r255a,16r255b,16r255c,16r255d,16r255e,
+ 16r255f,16r2560,16r2561,16r0401,16r2562,16r2563,16r2564,16r2565,
+ 16r2566,16r2567,16r2568,16r2569,16r256a,16r256b,16r256c,16r00a9,
+ 16r044e,16r0430,16r0431,16r0446,16r0434,16r0435,16r0444,16r0433,
+ 16r0445,16r0438,16r0439,16r043a,16r043b,16r043c,16r043d,16r043e,
+ 16r043f,16r044f,16r0440,16r0441,16r0442,16r0443,16r0436,16r0432,
+ 16r044c,16r044b,16r0437,16r0448,16r044d,16r0449,16r0447,16r044a,
+ 16r042e,16r0410,16r0411,16r0426,16r0414,16r0415,16r0424,16r0413,
+ 16r0425,16r0418,16r0419,16r041a,16r041b,16r041c,16r041d,16r041e,
+ 16r041f,16r042f,16r0420,16r0421,16r0422,16r0423,16r0416,16r0412,
+ 16r042c,16r042b,16r0417,16r0428,16r042d,16r0429,16r0427,16r042a,
+};
+
diff --git a/appl/lib/convcs/mkdata b/appl/lib/convcs/mkdata
new file mode 100755
index 00000000..eb22e39f
--- /dev/null
+++ b/appl/lib/convcs/mkdata
@@ -0,0 +1,39 @@
+#!/dis/sh
+load std
+
+# codepage generators
+GENERATORS=(
+ gencp932
+ genbig5
+ gengb2312
+ genjisx0201kana
+ genjisx0208-1997
+ genjisx0212
+ ibm437
+ ibm850
+ ibm866
+ iso8859-1
+ iso8859-10
+ iso8859-2
+ iso8859-3
+ iso8859-4
+ iso8859-5
+ iso8859-6
+ iso8859-7
+ iso8859-8
+ iso8859-9
+ koi8-r
+ windows-1250
+ windows-1251
+ windows-1252
+)
+
+for i in $GENERATORS {
+ (1st 2nd)=`{ls -t $i.b $i.dis >[2]/dev/null}
+ if {~ $1st $i.b} {
+ echo building $i
+ limbo $i.b
+ }
+ echo running $i
+ $i
+}
diff --git a/appl/lib/convcs/mkfile b/appl/lib/convcs/mkfile
new file mode 100644
index 00000000..e309f5a0
--- /dev/null
+++ b/appl/lib/convcs/mkfile
@@ -0,0 +1,27 @@
+<../../../mkconfig
+
+TARG=\
+ 8bit_stob.dis\
+ big5_btos.dis\
+ big5_stob.dis\
+ convcs.dis\
+ cp932_btos.dis\
+ cp_btos.dis\
+ cp_stob.dis\
+ euc-jp_btos.dis\
+ gb2312_btos.dis\
+ utf8_btos.dis\
+ utf8_stob.dis\
+
+MODULES=\
+
+SYSMODULES= \
+ bufio.m\
+ cfg.m\
+ convcs.m\
+ string.m\
+ sys.m\
+
+DISBIN=$ROOT/dis/lib/convcs
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/lib/convcs/utf8_btos.b b/appl/lib/convcs/utf8_btos.b
new file mode 100644
index 00000000..7ed10ddd
--- /dev/null
+++ b/appl/lib/convcs/utf8_btos.b
@@ -0,0 +1,35 @@
+implement Btos;
+
+include "sys.m";
+include "convcs.m";
+
+sys : Sys;
+
+init(nil : string) : string
+{
+ sys = load Sys Sys->PATH;
+ return nil;
+}
+
+btos(nil : Convcs->State, b : array of byte, n : int) : (Convcs->State, string, int)
+{
+ nbytes := 0;
+ str := "";
+
+ if (n == -1) {
+ # gather as much as possible
+ nbytes = sys->utfbytes(b, len b);
+ if (nbytes > 0)
+ str = string b[:nbytes];
+ } else {
+ for (; nbytes < len b && len str < n;) {
+ (ch, l, s) := sys->byte2char(b, nbytes);
+ if (l > 0) {
+ str[len str] = ch;
+ nbytes += l;
+ } else
+ break;
+ }
+ }
+ return (nil, str, nbytes);
+}
diff --git a/appl/lib/convcs/utf8_stob.b b/appl/lib/convcs/utf8_stob.b
new file mode 100644
index 00000000..a1be8bb7
--- /dev/null
+++ b/appl/lib/convcs/utf8_stob.b
@@ -0,0 +1,17 @@
+implement Stob;
+
+include "sys.m";
+include "convcs.m";
+
+sys : Sys;
+
+init(nil : string) : string
+{
+ sys = load Sys Sys->PATH;
+ return nil;
+}
+
+stob(nil : Convcs->State, str : string) : (Convcs->State, array of byte)
+{
+ return (nil, array of byte str);
+}
diff --git a/appl/lib/convcs/windows-1250.b b/appl/lib/convcs/windows-1250.b
new file mode 100644
index 00000000..c0bc6986
--- /dev/null
+++ b/appl/lib/convcs/windows-1250.b
@@ -0,0 +1,26 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "windows-1250";
+
+cstab := array [] of {
+16r0000, 16r0001, 16r0002, 16r0003, 16r0004, 16r0005, 16r0006, 16r0007, 16r0008, 16r0009, 16r000A, 16r000B, 16r000C, 16r000D, 16r000E, 16r000F,
+16r0010, 16r0011, 16r0012, 16r0013, 16r0014, 16r0015, 16r0016, 16r0017, 16r0018, 16r0019, 16r001A, 16r001B, 16r001C, 16r001D, 16r001E, 16r001F,
+16r0020, 16r0021, 16r0022, 16r0023, 16r0024, 16r0025, 16r0026, 16r0027, 16r0028, 16r0029, 16r002A, 16r002B, 16r002C, 16r002D, 16r002E, 16r002F,
+16r0030, 16r0031, 16r0032, 16r0033, 16r0034, 16r0035, 16r0036, 16r0037, 16r0038, 16r0039, 16r003A, 16r003B, 16r003C, 16r003D, 16r003E, 16r003F,
+16r0040, 16r0041, 16r0042, 16r0043, 16r0044, 16r0045, 16r0046, 16r0047, 16r0048, 16r0049, 16r004A, 16r004B, 16r004C, 16r004D, 16r004E, 16r004F,
+16r0050, 16r0051, 16r0052, 16r0053, 16r0054, 16r0055, 16r0056, 16r0057, 16r0058, 16r0059, 16r005A, 16r005B, 16r005C, 16r005D, 16r005E, 16r005F,
+16r0060, 16r0061, 16r0062, 16r0063, 16r0064, 16r0065, 16r0066, 16r0067, 16r0068, 16r0069, 16r006A, 16r006B, 16r006C, 16r006D, 16r006E, 16r006F,
+16r0070, 16r0071, 16r0072, 16r0073, 16r0074, 16r0075, 16r0076, 16r0077, 16r0078, 16r0079, 16r007A, 16r007B, 16r007C, 16r007D, 16r007E, 16r007F,
+16r20AC, -1, 16r201A, -1, 16r201E, 16r2026, 16r2020, 16r2021, -1, 16r2030, 16r0160, 16r2039, 16r015A, 16r0164, 16r017D, 16r0179,
+-1, 16r2018, 16r2019, 16r201C, 16r201D, 16r2022, 16r2013, 16r2014, -1, 16r2122, 16r0161, 16r203A, 16r015B, 16r0165, 16r017E, 16r017A,
+16r00A0, 16r02C7, 16r02D8, 16r0141, 16r00A4, 16r0104, 16r00A6, 16r00A7, 16r00A8, 16r00A9, 16r015E, 16r00AB, 16r00AC, 16r00AD, 16r00AE, 16r017B,
+16r00B0, 16r00B1, 16r02DB, 16r0142, 16r00B4, 16r00B5, 16r00B6, 16r00B7, 16r00B8, 16r0105, 16r015F, 16r00BB, 16r013D, 16r02DD, 16r013E, 16r017C,
+16r0154, 16r00C1, 16r00C2, 16r0102, 16r00C4, 16r0139, 16r0106, 16r00C7, 16r010C, 16r00C9, 16r0118, 16r00CB, 16r011A, 16r00CD, 16r00CE, 16r010E,
+16r0110, 16r0143, 16r0147, 16r00D3, 16r00D4, 16r0150, 16r00D6, 16r00D7, 16r0158, 16r016E, 16r00DA, 16r0170, 16r00DC, 16r00DD, 16r0162, 16r00DF,
+16r0155, 16r00E1, 16r00E2, 16r0103, 16r00E4, 16r013A, 16r0107, 16r00E7, 16r010D, 16r00E9, 16r0119, 16r00EB, 16r011B, 16r00ED, 16r00EE, 16r010F,
+16r0111, 16r0144, 16r0148, 16r00F3, 16r00F4, 16r0151, 16r00F6, 16r00F7, 16r0159, 16r016F, 16r00FA, 16r0171, 16r00FC, 16r00FD, 16r0163, 16r02D9,
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/windows-1251.b b/appl/lib/convcs/windows-1251.b
new file mode 100644
index 00000000..a65a41ae
--- /dev/null
+++ b/appl/lib/convcs/windows-1251.b
@@ -0,0 +1,34 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "windows-1251";
+
+cstab := array [] of {
+16r00,16r01,16r02,16r03,16r04,16r05,16r06,16r07,16r08,16r09,16r0a,16r0b,16r0c,16r0d,16r0e,16r0f,
+16r10,16r11,16r12,16r13,16r14,16r15,16r16,16r17,16r18,16r19,16r1a,16r1b,16r1c,16r1d,16r1e,16r1f,
+16r20,16r21,16r22,16r23,16r24,16r25,16r26,16r27,16r28,16r29,16r2a,16r2b,16r2c,16r2d,16r2e,16r2f,
+16r30,16r31,16r32,16r33,16r34,16r35,16r36,16r37,16r38,16r39,16r3a,16r3b,16r3c,16r3d,16r3e,16r3f,
+16r40,16r41,16r42,16r43,16r44,16r45,16r46,16r47,16r48,16r49,16r4a,16r4b,16r4c,16r4d,16r4e,16r4f,
+16r50,16r51,16r52,16r53,16r54,16r55,16r56,16r57,16r58,16r59,16r5a,16r5b,16r5c,16r5d,16r5e,16r5f,
+16r60,16r61,16r62,16r63,16r64,16r65,16r66,16r67,16r68,16r69,16r6a,16r6b,16r6c,16r6d,16r6e,16r6f,
+16r70,16r71,16r72,16r73,16r74,16r75,16r76,16r77,16r78,16r79,16r7a,16r7b,16r7c,16r7d,16r7e,16r7f,
+16r0402,16r0403,16r201a,16r0453,16r201e,16r2026,16r2020,16r2021,
+ -1,16r2030,16r0409,16r2039,16r040a,16r040c,16r040b,16r040f,
+16r0452,16r2018,16r2019,16r201c,16r201d,16r2022,16r2013,16r2014,
+ -1,16r2122,16r0459,16r203a,16r045a,16r045c,16r045b,16r045f,
+16r00a0,16r040e,16r045e,16r0408,16r00a4,16r0490,16r00a6,16r00a7,
+16r0401,16r00a9,16r0404,16r00ab,16r00ac,16r00ad,16r00ae,16r0407,
+16r00b0,16r00b1,16r0406,16r0456,16r0491,16r00b5,16r00b6,16r00b7,
+16r0451,16r2116,16r0454,16r00bb,16r0458,16r0405,16r0455,16r0457,
+16r0410,16r0411,16r0412,16r0413,16r0414,16r0415,16r0416,16r0417,
+16r0418,16r0419,16r041a,16r041b,16r041c,16r041d,16r041e,16r041f,
+16r0420,16r0421,16r0422,16r0423,16r0424,16r0425,16r0426,16r0427,
+16r0428,16r0429,16r042a,16r042b,16r042c,16r042d,16r042e,16r042f,
+16r0430,16r0431,16r0432,16r0433,16r0434,16r0435,16r0436,16r0437,
+16r0438,16r0439,16r043a,16r043b,16r043c,16r043d,16r043e,16r043f,
+16r0440,16r0441,16r0442,16r0443,16r0444,16r0445,16r0446,16r0447,
+16r0448,16r0449,16r044a,16r044b,16r044c,16r044d,16r044e,16r044f,
+}; \ No newline at end of file
diff --git a/appl/lib/convcs/windows-1252.b b/appl/lib/convcs/windows-1252.b
new file mode 100644
index 00000000..f465f7de
--- /dev/null
+++ b/appl/lib/convcs/windows-1252.b
@@ -0,0 +1,26 @@
+implement GenCP;
+
+include "sys.m";
+include "draw.m";
+include "gencp.b";
+
+CHARSET : con "windows-1252";
+
+cstab := array [] of {
+16r0000, 16r0001, 16r0002, 16r0003, 16r0004, 16r0005, 16r0006, 16r0007, 16r0008, 16r0009, 16r000A, 16r000B, 16r000C, 16r000D, 16r000E, 16r000F,
+16r0010, 16r0011, 16r0012, 16r0013, 16r0014, 16r0015, 16r0016, 16r0017, 16r0018, 16r0019, 16r001A, 16r001B, 16r001C, 16r001D, 16r001E, 16r001F,
+16r0020, 16r0021, 16r0022, 16r0023, 16r0024, 16r0025, 16r0026, 16r0027, 16r0028, 16r0029, 16r002A, 16r002B, 16r002C, 16r002D, 16r002E, 16r002F,
+16r0030, 16r0031, 16r0032, 16r0033, 16r0034, 16r0035, 16r0036, 16r0037, 16r0038, 16r0039, 16r003A, 16r003B, 16r003C, 16r003D, 16r003E, 16r003F,
+16r0040, 16r0041, 16r0042, 16r0043, 16r0044, 16r0045, 16r0046, 16r0047, 16r0048, 16r0049, 16r004A, 16r004B, 16r004C, 16r004D, 16r004E, 16r004F,
+16r0050, 16r0051, 16r0052, 16r0053, 16r0054, 16r0055, 16r0056, 16r0057, 16r0058, 16r0059, 16r005A, 16r005B, 16r005C, 16r005D, 16r005E, 16r005F,
+16r0060, 16r0061, 16r0062, 16r0063, 16r0064, 16r0065, 16r0066, 16r0067, 16r0068, 16r0069, 16r006A, 16r006B, 16r006C, 16r006D, 16r006E, 16r006F,
+16r0070, 16r0071, 16r0072, 16r0073, 16r0074, 16r0075, 16r0076, 16r0077, 16r0078, 16r0079, 16r007A, 16r007B, 16r007C, 16r007D, 16r007E, 16r007F,
+16r20AC, -1, 16r201A, 16r0192, 16r201E, 16r2026, 16r2020, 16r2021, 16r02C6, 16r2030, 16r0160, 16r2039, 16r0152, -1, 16r017D, -1,
+-1, 16r2018, 16r2019, 16r201C, 16r201D, 16r2022, 16r2013, 16r2014, 16r02DC, 16r2122, 16r0161, 16r203A, 16r0153, -1, 16r017E, 16r0178,
+16r00A0, 16r00A1, 16r00A2, 16r00A3, 16r00A4, 16r00A5, 16r00A6, 16r00A7, 16r00A8, 16r00A9, 16r00AA, 16r00AB, 16r00AC, 16r00AD, 16r00AE, 16r00AF,
+16r00B0, 16r00B1, 16r00B2, 16r00B3, 16r00B4, 16r00B5, 16r00B6, 16r00B7, 16r00B8, 16r00B9, 16r00BA, 16r00BB, 16r00BC, 16r00BD, 16r00BE, 16r00BF,
+16r00C0, 16r00C1, 16r00C2, 16r00C3, 16r00C4, 16r00C5, 16r00C6, 16r00C7, 16r00C8, 16r00C9, 16r00CA, 16r00CB, 16r00CC, 16r00CD, 16r00CE, 16r00CF,
+16r00D0, 16r00D1, 16r00D2, 16r00D3, 16r00D4, 16r00D5, 16r00D6, 16r00D7, 16r00D8, 16r00D9, 16r00DA, 16r00DB, 16r00DC, 16r00DD, 16r00DE, 16r00DF,
+16r00E0, 16r00E1, 16r00E2, 16r00E3, 16r00E4, 16r00E5, 16r00E6, 16r00E7, 16r00E8, 16r00E9, 16r00EA, 16r00EB, 16r00EC, 16r00ED, 16r00EE, 16r00EF,
+16r00F0, 16r00F1, 16r00F2, 16r00F3, 16r00F4, 16r00F5, 16r00F6, 16r00F7, 16r00F8, 16r00F9, 16r00FA, 16r00FB, 16r00FC, 16r00FD, 16r00FE, 16r00FF,
+}; \ No newline at end of file
diff --git a/appl/lib/crc.b b/appl/lib/crc.b
new file mode 100644
index 00000000..d25eeec2
--- /dev/null
+++ b/appl/lib/crc.b
@@ -0,0 +1,45 @@
+implement Crc;
+
+include "crc.m";
+
+init(poly : int, reg : int) : ref CRCstate
+{
+ if (poly == 0)
+ poly = int 16redb88320;
+ tab := array[256] of int;
+ for(i := 0; i < 256; i++){
+ crc := i;
+ for(j := 0; j < 8; j++){
+ c := crc & 1;
+ crc = (crc >> 1) & 16r7fffffff;
+ if(c)
+ crc ^= poly;
+ }
+ tab[i] = crc;
+ }
+ crcs := ref CRCstate;
+ crcs.crc = 0;
+ crcs.crctab = tab;
+ crcs.reg = reg;
+ return crcs;
+}
+
+crc(crcs : ref CRCstate, buf : array of byte, nb : int) : int
+{
+ n := nb;
+ if (n > len buf)
+ n = len buf;
+ crc := crcs.crc;
+ tab := crcs.crctab;
+ crc ^= crcs.reg;
+ for (i := 0; i < n; i++)
+ crc = tab[int(byte crc ^ buf[i])] ^ ((crc >> 8) & 16r00ffffff);
+ crc ^= crcs.reg;
+ crcs.crc = crc;
+ return crc;
+}
+
+reset(crcs : ref CRCstate)
+{
+ crcs.crc = 0;
+}
diff --git a/appl/lib/crypt/mkfile b/appl/lib/crypt/mkfile
new file mode 100644
index 00000000..c97dab08
--- /dev/null
+++ b/appl/lib/crypt/mkfile
@@ -0,0 +1,24 @@
+<../../../mkconfig
+
+TARG=\
+ pkcs.dis\
+ ssl3.dis\
+ sslsession.dis\
+ x509.dis\
+
+MODULES=
+
+SYSMODULES= \
+ asn1.m\
+ daytime.m\
+ keyring.m\
+ pkcs.m\
+ security.m\
+ ssl3.m\
+ sslsession.m\
+ sys.m\
+ x509.m\
+
+DISBIN=$ROOT/dis/lib/crypt
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/lib/crypt/pkcs.b b/appl/lib/crypt/pkcs.b
new file mode 100644
index 00000000..e74391ca
--- /dev/null
+++ b/appl/lib/crypt/pkcs.b
@@ -0,0 +1,572 @@
+implement PKCS;
+
+include "sys.m";
+ sys : Sys;
+
+include "keyring.m";
+ keyring : Keyring;
+ IPint : import keyring;
+ DESstate : import keyring;
+
+include "security.m";
+ random : Random;
+
+include "asn1.m";
+ asn1 : ASN1;
+ Elem, Oid : import asn1;
+
+include "pkcs.m";
+
+# pkcs object identifiers
+
+objIdTab = array [] of {
+ id_pkcs => Oid(array [] of {1,2,840,113549,1}),
+ id_pkcs_1 => Oid(array [] of {1,2,840,113549,1,1}),
+ id_pkcs_rsaEncryption => Oid(array [] of {1,2,840,113549,1,1,1}),
+ id_pkcs_md2WithRSAEncryption => Oid(array [] of {1,2,840,113549,1,1,2}),
+ id_pkcs_md4WithRSAEncryption => Oid(array [] of {1,2,840,113549,1,1,3}),
+ id_pkcs_md5WithRSAEncryption => Oid(array [] of {1,2,840,113549,1,1,4}),
+
+ id_pkcs_3 => Oid(array [] of {1,2,840,113549,1,3}),
+ id_pkcs_dhKeyAgreement => Oid(array [] of {1,2,840,113549,1,3,1}),
+
+ id_pkcs_5 => Oid(array [] of {1,2,840,113549,1,5}),
+ id_pkcs_pbeWithMD2AndDESCBC => Oid(array [] of {1,2,840,113549,1,5,1}),
+ id_pkcs_pbeWithMD5AndDESCBC => Oid(array [] of {1,2,840,113549,1,5,3}),
+
+ id_pkcs_7 => Oid(array [] of {1,2,840,113549,1,7}),
+ id_pkcs_data => Oid(array [] of {1,2,840,113549,1,7,1}),
+ id_pkcs_singnedData => Oid(array [] of {1,2,840,113549,1,7,2}),
+ id_pkcs_envelopedData => Oid(array [] of {1,2,840,113549,1,7,3}),
+ id_pkcs_signedAndEnvelopedData =>
+ Oid(array [] of {1,2,840,113549,1,7,4}),
+ id_pkcs_digestData => Oid(array [] of {1,2,840,113549,1,7,5}),
+ id_pkcs_encryptedData => Oid(array [] of {1,2,840,113549,1,7,6}),
+
+ id_pkcs_9 => Oid(array [] of {1,2,840,113549,1,9}),
+ id_pkcs_emailAddress => Oid(array [] of {1,2,840,113549,1,9,1}),
+ id_pkcs_unstructuredName => Oid(array [] of {1,2,840,113549,1,9,2}),
+ id_pkcs_contentType => Oid(array [] of {1,2,840,113549,1,9,3}),
+ id_pkcs_messageDigest => Oid(array [] of {1,2,840,113549,1,9,4}),
+ id_pkcs_signingTime => Oid(array [] of {1,2,840,113549,1,9,5}),
+ id_pkcs_countersignature => Oid(array [] of {1,2,840,113549,1,9,6}),
+ id_pkcs_challengePassword => Oid(array [] of {1,2,840,113549,1,9,7}),
+ id_pkcs_unstructuredAddress => Oid(array [] of {1,2,840,113549,1,9,8}),
+ id_pkcs_extCertAttrs => Oid(array [] of {1,2,840,113549,1,9,9}),
+ id_algorithm_shaWithDSS => Oid(array [] of {1,3,14,3,2,13})
+};
+
+# [public]
+# initialize PKCS module
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ if(sys == nil)
+ return "load sys module failed";
+
+ keyring = load Keyring Keyring->PATH;
+ if(keyring == nil)
+ return "load keyring module failed";
+
+ random = load Random Random->PATH;
+ if(random == nil)
+ return "load random module failed";
+
+ asn1 = load ASN1 ASN1->PATH;
+ if(asn1 == nil)
+ return "load asn1 module failed";
+ asn1->init();
+
+ return "";
+}
+
+# [public]
+# Encrypt data according to PKCS#1, with given blocktype, using given key.
+
+rsa_encrypt(data: array of byte, key: ref RSAKey, blocktype: int)
+ : (string, array of byte)
+{
+ if(key == nil)
+ return ("null pkcs#1 key", nil);
+ k := key.modlen;
+ dlen := len data;
+ if(k < 12 || dlen > k-11)
+ return ("bad parameters for pkcs#1", nil);
+ padlen := k-3-dlen;
+ pad := random->randombuf(Random->NotQuiteRandom, padlen);
+ for(i:=0; i < padlen; i++) {
+ if(blocktype == 0)
+ pad[i] = byte 0;
+ else if(blocktype == 1)
+ pad[i] = byte 16rff;
+ else if(pad[i] == byte 0)
+ pad[i] = byte 1;
+ }
+ eb := array[k] of byte;
+ eb[0] = byte 0;
+ eb[1] = byte blocktype;
+ eb[2:] = pad[0:];
+ eb[padlen+2] = byte 0;
+ eb[padlen+3:] = data[0:];
+ return ("", rsacomp(eb, key));
+}
+
+# [public]
+# Decrypt data according to PKCS#1, with given key.
+# If public is true, expect a block type of 0 or 1, else 2.
+
+rsa_decrypt(data: array of byte, key: ref RSAKey, public: int)
+ : (string, array of byte)
+{
+ eb := rsacomp(data, key);
+ k := key.modlen;
+ if(len eb == k) {
+ bt := int eb[1];
+ if(int eb[0] == 0 && ((public && (bt == 0 || bt == 1)) || (!public && bt == 2))) {
+ for(i := 2; i < k; i++)
+ if(eb[i] == byte 0)
+ break;
+ if(i < k-1) {
+ ans := array[k-(i+1)] of byte;
+ ans[0:] = eb[i+1:];
+ return ("", ans);
+ }
+ }
+ }
+ return ("pkcs1 decryption error", nil);
+}
+
+# [private]
+# Do RSA computation on block according to key, and pad
+# result on left with zeros to make it key.modlen long.
+
+rsacomp(block: array of byte, key: ref RSAKey): array of byte
+{
+ x := keyring->IPint.bebytestoip(block);
+ y := x.expmod(key.exponent, key.modulus);
+ ybytes := y.iptobebytes();
+ k := key.modlen;
+ ylen := len ybytes;
+ if(ylen < k) {
+ a := array[k] of { * => byte 0};
+ a[k-ylen:] = ybytes[0:];
+ ybytes = a;
+ }
+ else if(ylen > k) {
+ # assume it has leading zeros (mod should make it so)
+ a := array[k] of byte;
+ a[0:] = ybytes[ylen-k:];
+ ybytes = a;
+ }
+ return ybytes;
+}
+
+# [public]
+
+rsa_sign(data: array of byte, sk: ref RSAKey, algid: int): (string, array of byte)
+{
+ # digesting and add proper padding to it
+ ph := padhash(data, algid);
+
+ return rsa_encrypt(ph, sk, 0); # blocktype <- padding with zero
+}
+
+# [public]
+
+rsa_verify(data, signature: array of byte, pk: ref RSAKey, algid: int): int
+{
+ # digesting and add proper padding to it
+ ph := padhash(data, algid);
+
+ (err, orig) := rsa_decrypt(signature, pk, 0); # blocktype ?
+ if(err != "" || !byte_cmp(orig, ph))
+ return 0;
+
+ return 1;
+}
+
+# [private]
+# padding block A
+PA := array [] of {
+ byte 16r30, byte 16r20, byte 16r30, byte 16r0c,
+ byte 16r06, byte 16r08, byte 16r2a, byte 16r86,
+ byte 16r48, byte 16r86, byte 16rf7, byte 16r0d,
+ byte 16r02
+};
+
+# [private]
+# padding block B
+PB := array [] of {byte 16r05, byte 16r00, byte 16r04, byte 16r10};
+
+# [private]
+# require either md5 or md2 of 16 bytes digest
+# length of padded digest = 13 + 1 + 4 + 16
+
+padhash(data: array of byte, algid: int): array of byte
+{
+ padded := array [34] of byte;
+ case algid {
+ MD2_WithRSAEncryption =>
+ padded[13] = byte 2;
+ # TODO: implement md2 in keyring module
+ # keyring->md2(data, len data, padded[18:], nil);
+
+ MD5_WithRSAEncryption =>
+ padded[13] = byte 5;
+ keyring->md5(data, len data, padded[18:], nil);
+ * =>
+ return nil;
+ }
+ padded[0:] = PA;
+ padded[14:] = PB;
+
+ return padded;
+}
+
+# [private]
+# compare byte to byte of two array of byte
+
+byte_cmp(a, b: array of byte): int
+{
+ if(len a != len b)
+ return 0;
+
+ for(i := 0; i < len a; i++) {
+ if(a[i] != b[i])
+ return 0;
+ }
+
+ return 1;
+}
+
+# [public]
+
+RSAKey.bits(key: self ref RSAKey): int
+{
+ return key.modulus.bits();
+}
+
+# [public]
+# Decode an RSAPublicKey ASN1 type, defined as:
+#
+# RSAPublickKey :: SEQUENCE {
+# modulus INTEGER,
+# publicExponent INTEGER
+# }
+
+decode_rsapubkey(a: array of byte): (string, ref RSAKey)
+{
+parse:
+ for(;;) {
+ (err, e) := asn1->decode(a);
+ if(err != "")
+ break parse;
+ (ok, el) := e.is_seq();
+ if(!ok || len el != 2)
+ break parse;
+ modbytes, expbytes: array of byte;
+ (ok, modbytes) = (hd el).is_bigint();
+ if(!ok)
+ break parse;
+ modulus := IPint.bebytestoip(modbytes);
+ # get modlen this way, because sometimes it
+ # comes with leading zeros that are to be ignored!
+ mbytes := modulus.iptobebytes();
+ modlen := len mbytes;
+ (ok, expbytes) = (hd tl el).is_bigint();
+ if(!ok)
+ break parse;
+ exponent := keyring->IPint.bebytestoip(expbytes);
+ return ("", ref RSAKey(modulus, modlen, exponent));
+ }
+ return ("rsa public key: syntax error", nil);
+}
+
+
+# [public]
+# generate a pair of DSS public and private keys
+
+generateDSSKeyPair(strength: int): (ref DSSPublicKey, ref DSSPrivateKey)
+{
+ # TODO: need add getRandBetween in IPint
+ return (nil, nil);
+}
+
+# [public]
+
+dss_sign(a: array of byte, sk: ref DSSPrivateKey): (string, array of byte)
+{
+ #signature, digest: array of byte;
+
+ #case hash {
+ #Keyring->MD4 =>
+ # digest = array [Keyring->MD4dlen] of byte;
+ # keyring->md4(a, len a, digest, nil);
+ #Keyring->MD5 =>
+ # digest = array [Keyring->MD5dlen] of byte;
+ # keyring->md5(a, len a, digest, nil);
+ #Keyring->SHA =>
+ # digest = array [Keyring->SHA1dlen] of byte;
+ # keyring->sha1(a, len a, digest, nil);
+ #* =>
+ # return ("unknown hash algorithm", nil);
+ #}
+
+ # TODO: add gcd or getRandBetween in Keyring->IPint
+ return ("unsupported error", nil);
+}
+
+# [public]
+
+dss_verify(a, signa: array of byte, pk: ref DSSPublicKey): int
+{
+ unsigned: array of byte;
+
+ #case hash {
+ #Keyring->MD4 =>
+ # digest = array [Keyring->MD4dlen] of byte;
+ # keyring->md4(a, len a, digest, nil);
+ #Keyring->MD5 =>
+ # digest = array [Keyring->MD5dlen] of byte;
+ # keyring->md5(a, len a, digest, nil);
+ #Keyring->SHA =>
+ # digest = array [Keyring->SHA1dlen] of byte;
+ # keyring->sha1(a, len a, digest, nil);
+ #* =>
+ # return 0;
+ #}
+
+ # get unsigned from signa and compare it with digest
+
+ if(byte_cmp(unsigned, a))
+ return 1;
+
+ return 0;
+}
+
+# [public]
+decode_dsspubkey(a: array of byte): (string, ref DSSPublicKey)
+{
+ return ("unsupported error", nil);
+}
+
+
+# [public]
+# generate DH parameters with prime length at least (default) 512 bits
+
+generateDHParams(primelen: int): ref DHParams
+{
+ # prime - at least 512 bits
+ if(primelen < 512) # DHmodlen
+ primelen = 512;
+
+ # generate prime and base (generator) integers
+ (p, g) := keyring->dhparams(primelen);
+ if(p == nil || g == nil)
+ return nil;
+
+ return ref DHParams(p, g, 0);
+}
+
+# [public]
+# generate public and private key pair
+# Note: use iptobytes as integer to octet string conversion
+# and bytestoip as octect string to integer reversion
+
+setupDHAgreement(dh: ref DHParams): (ref DHPrivateKey, ref DHPublicKey)
+{
+ if(dh == nil || dh.prime == nil || dh.base == nil)
+ return (nil, nil);
+
+ # prime length in bits
+ bits := dh.prime.bits();
+
+ # generate random private key of length between bits/4 and bits
+ x := IPint.random(bits/4, bits);
+ if(x == nil)
+ return (nil, nil);
+ dh.privateValueLength = x.bits();
+
+ # calculate public key
+ y := dh.base.expmod(x, dh.prime);
+ if(y == nil)
+ return (nil, nil);
+
+ return (ref DHPrivateKey(dh, y, x), ref DHPublicKey(dh, x));
+}
+
+# [public]
+# The second phase of Diffie-Hellman key agreement
+
+computeDHAgreedKey(dh: ref DHParams, mysk, upk: ref IPint)
+ : array of byte
+{
+ if(mysk == nil || upk == nil)
+ return nil;
+
+ # exponential - calculate agreed key (shared secret)
+ z := upk.expmod(mysk, dh.prime);
+
+ # integer to octet conversion
+ return z.iptobebytes();
+}
+
+# [public]
+# ASN1 encoding
+
+decode_dhpubkey(a: array of byte): (string, ref DHPublicKey)
+{
+ return ("unsupported error", nil);
+}
+
+
+# [public]
+# Digest the concatenation of password and salt with count iterations of
+# selected message-digest algorithm (either md2 or md5).
+# The first 8 bytes of the message digest become the DES key.
+# The last 8 bytes of the message digest become the initializing vector IV.
+
+generateDESKey(pw: array of byte, param: ref PBEParams, alg: int)
+ : (ref DESstate, array of byte, array of byte)
+{
+ if(param.iterationCount < 1)
+ return (nil, nil, nil);
+
+ # concanate password and salt
+ pwlen := len pw;
+ pslen := pwlen + len param.salt;
+ ps := array [pslen] of byte;
+ ps[0:] = pw;
+ ps[pwlen:] = param.salt;
+ key, iv: array of byte;
+
+ # digest iterations
+ case alg {
+ PBE_MD2_DESCBC =>
+ ds : ref Keyring->DigestState = nil;
+ # TODO: implement md2 in keyring module
+ #result := array [Keyring->MD2dlen] of byte;
+ #for(i := 0; i < param.iterationCount; i++)
+ # ds = keyring->md2(ps, pslen, nil, ds);
+ #keyring->md2(ps, pslen, result, ds);
+ #key = result[0:8];
+ #iv = result[8:];
+
+ PBE_MD5_DESCBC =>
+ ds: ref Keyring->DigestState = nil;
+ result := array [Keyring->MD5dlen] of byte;
+ for(i := 0; i < param.iterationCount; i++)
+ ds = keyring->md5(ps, pslen, nil, ds);
+ keyring->md5(ps, pslen, result, ds);
+ key = result[0:8];
+ iv = result[8:];
+
+ * =>
+ return (nil, nil, nil);
+ }
+
+ state := keyring->dessetup(key, iv);
+
+ return (state, key, iv);
+}
+
+# [public]
+# The message M and a padding string PS shall be formatted into
+# an octet string EB
+# EB = M + PS
+# where
+# PS = 1 if M mod 8 = 7;
+# PS = 2 + 2 if M mod 8 = 6;
+# ...
+# PS = 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8 if M mod 8 = 0;
+
+pbe_encrypt(state: ref DESstate, m: array of byte): array of byte
+{
+ mlen := len m;
+ padvalue := mlen % 8;
+ pdlen := 8 - padvalue;
+
+ eb := array [mlen + pdlen] of byte;
+ eb[0:] = m;
+ for(i := mlen; i < pdlen; i++)
+ eb[i] = byte padvalue;
+
+ keyring->descbc(state, eb, len eb, Keyring->Encrypt);
+
+ return eb;
+}
+
+# [public]
+
+pbe_decrypt(state: ref DESstate, eb: array of byte): array of byte
+{
+ eblen := len eb;
+ if(eblen%8 != 0) # must a multiple of 8 bytes
+ return nil;
+
+ keyring->descbc(state, eb, eblen, Keyring->Decrypt);
+
+ # remove padding
+ for(i := eblen -8; i < 8; i++) {
+ if(int eb[i] == i) {
+ for(j := i; j < 8; j++)
+ if(int eb[j] != i)
+ break;
+ if(j == 8)
+ break;
+ }
+ }
+
+ return eb[0:i];
+}
+
+# [public]
+
+PrivateKeyInfo.encode(p: self ref PrivateKeyInfo): (string, array of byte)
+{
+
+ return ("unsupported error", nil);
+}
+
+# [public]
+
+PrivateKeyInfo.decode(a: array of byte): (string, ref PrivateKeyInfo)
+{
+ return ("unsupported error", nil);
+}
+
+# [public]
+
+EncryptedPrivateKeyInfo.encode(p: self ref EncryptedPrivateKeyInfo)
+ : (string, array of byte)
+{
+
+ return ("unsupported error", nil);
+}
+
+# [public]
+
+EncryptedPrivateKeyInfo.decode(a: array of byte)
+ : (string, ref EncryptedPrivateKeyInfo)
+{
+ return ("unsupported error", nil);
+}
+
+# [public]
+
+decode_extcertorcert(a: array of byte): (int, int, array of byte)
+{
+ (err, all) := asn1->decode(a);
+ if(err == "") {
+
+ }
+}
+
+# [public]
+
+encode_extcertorcert(a: array of byte, which: int): (int, array of byte)
+{
+
+}
+
diff --git a/appl/lib/crypt/ssl3.b b/appl/lib/crypt/ssl3.b
new file mode 100644
index 00000000..91819851
--- /dev/null
+++ b/appl/lib/crypt/ssl3.b
@@ -0,0 +1,5557 @@
+#
+# SSL 3.0 protocol
+#
+implement SSL3;
+
+include "sys.m";
+ sys : Sys;
+
+include "keyring.m";
+ keyring : Keyring;
+ IPint, DigestState : import keyring;
+
+include "security.m";
+ random : Random;
+ ssl : SSL;
+
+include "daytime.m";
+ daytime : Daytime;
+
+include "asn1.m";
+ asn1 : ASN1;
+
+include "pkcs.m";
+ pkcs : PKCS;
+ DHParams, DHPublicKey, DHPrivateKey,
+ RSAParams, RSAKey,
+ DSSPrivateKey, DSSPublicKey : import PKCS;
+
+include "x509.m";
+ x509 : X509;
+ Signed, Certificate, SubjectPKInfo : import x509;
+
+include "sslsession.m";
+ sslsession : SSLsession;
+ Session : import sslsession;
+
+include "ssl3.m";
+
+#
+# debug mode
+#
+SSL_DEBUG : con 0;
+logfd : ref Sys->FD;
+
+#
+# version {major, minor}
+#
+SSL_VERSION_2_0 := array [] of {byte 0, byte 16r02};
+SSL_VERSION_3_0 := array [] of {byte 16r03, byte 0};
+
+
+# SSL Record Protocol
+
+SSL_CHANGE_CIPHER_SPEC : con 20;
+ SSL_ALERT : con 21;
+ SSL_HANDSHAKE : con 22;
+ SSL_APPLICATION_DATA : con 23;
+ SSL_V2HANDSHAKE : con 0; # escape to sslv2
+
+# SSL Application Protocol consists of alert protocol, change cipher spec protocol
+# v2 and v3 handshake protocol and application data protocol. The v2 handshake
+# protocol is included only for backward compatibility
+
+Protocol: adt {
+ pick {
+ pAlert =>
+ alert : ref Alert;
+ pChangeCipherSpec =>
+ change_cipher_spec : ref ChangeCipherSpec;
+ pHandshake =>
+ handshake : ref Handshake;
+ pV2Handshake =>
+ handshake : ref V2Handshake;
+ pApplicationData =>
+ data : array of byte;
+ }
+
+ decode: fn(r: ref Record, ctx: ref Context): (ref Protocol, string);
+ encode: fn(p: self ref Protocol, vers: array of byte): (ref Record, string);
+ tostring: fn(p: self ref Protocol): string;
+};
+
+#
+# ssl alert protocol
+#
+SSL_WARNING : con 1;
+ SSL_FATAL : con 2;
+
+SSL_CLOSE_NOTIFY : con 0;
+ SSL_UNEXPECTED_MESSAGE : con 10;
+ SSL_BAD_RECORD_MAC : con 20;
+ SSL_DECOMPRESSION_FAILURE : con 30;
+ SSL_HANDSHAKE_FAILURE : con 40;
+ SSL_NO_CERTIFICATE : con 41;
+ SSL_BAD_CERTIFICATE : con 42;
+ SSL_UNSUPPORTED_CERTIFICATE : con 43;
+ SSL_CERTIFICATE_REVOKED : con 44;
+ SSL_CERTIFICATE_EXPIRED : con 45;
+ SSL_CERTIFICATE_UNKNOWN : con 46;
+ SSL_ILLEGAL_PARAMETER : con 47;
+
+Alert: adt {
+ level : int;
+ description : int;
+
+ tostring: fn(a: self ref Alert): string;
+};
+
+#
+# ssl change cipher spec protocol
+#
+ChangeCipherSpec: adt {
+ value : int;
+};
+
+#
+# ssl handshake protocol
+#
+SSL_HANDSHAKE_HELLO_REQUEST : con 0;
+ SSL_HANDSHAKE_CLIENT_HELLO : con 1;
+ SSL_HANDSHAKE_SERVER_HELLO : con 2;
+ SSL_HANDSHAKE_CERTIFICATE : con 11;
+ SSL_HANDSHAKE_SERVER_KEY_EXCHANGE : con 12;
+ SSL_HANDSHAKE_CERTIFICATE_REQUEST : con 13;
+ SSL_HANDSHAKE_SERVER_HELLO_DONE : con 14;
+ SSL_HANDSHAKE_CERTIFICATE_VERIFY : con 15;
+ SSL_HANDSHAKE_CLIENT_KEY_EXCHANGE : con 16;
+ SSL_HANDSHAKE_FINISHED : con 20;
+
+Handshake: adt {
+ pick {
+ HelloRequest =>
+ ClientHello =>
+ version : array of byte; # [2]
+ random : array of byte; # [32]
+ session_id : array of byte; # <0..32>
+ suites : array of byte; # [2] x
+ compressions : array of byte; # [1] x
+ ServerHello =>
+ version : array of byte; # [2]
+ random : array of byte; # [32]
+ session_id : array of byte; # <0..32>
+ suite : array of byte; # [2]
+ compression : byte; # [1]
+ Certificate =>
+ cert_list : list of array of byte; # X509 cert chain
+ ServerKeyExchange =>
+ xkey : array of byte; # exchange_keys
+ CertificateRequest =>
+ cert_types : array of byte;
+ dn_list : list of array of byte; # DN list
+ ServerHelloDone =>
+ CertificateVerify =>
+ signature : array of byte;
+ ClientKeyExchange =>
+ xkey : array of byte;
+ Finished =>
+ md5_hash : array of byte; # [16] Keyring->MD5dlen
+ sha_hash : array of byte; # [20] Keyring->SHA1dlen
+ }
+
+ decode: fn(buf: array of byte): (ref Handshake, string);
+ encode: fn(hm: self ref Handshake): (array of byte, string);
+ tostring: fn(hm: self ref Handshake): string;
+};
+
+# cipher suites and cipher specs (default, not all supported)
+# - key exchange, signature, encrypt and digest algorithms
+
+SSL3_Suites := array [] of {
+ NULL_WITH_NULL_NULL => array [] of {byte 0, byte 16r00},
+
+ RSA_WITH_NULL_MD5 => array [] of {byte 0, byte 16r01},
+ RSA_WITH_NULL_SHA => array [] of {byte 0, byte 16r02},
+ RSA_EXPORT_WITH_RC4_40_MD5 => array [] of {byte 0, byte 16r03},
+ RSA_WITH_RC4_128_MD5 => array [] of {byte 0, byte 16r04},
+ RSA_WITH_RC4_128_SHA => array [] of {byte 0, byte 16r05},
+ RSA_EXPORT_WITH_RC2_CBC_40_MD5 => array [] of {byte 0, byte 16r06},
+ RSA_WITH_IDEA_CBC_SHA => array [] of {byte 0, byte 16r07},
+ RSA_EXPORT_WITH_DES40_CBC_SHA => array [] of {byte 0, byte 16r08},
+ RSA_WITH_DES_CBC_SHA => array [] of {byte 0, byte 16r09},
+ RSA_WITH_3DES_EDE_CBC_SHA => array [] of {byte 0, byte 16r0A},
+
+ DH_DSS_EXPORT_WITH_DES40_CBC_SHA => array [] of {byte 0, byte 16r0B},
+ DH_DSS_WITH_DES_CBC_SHA => array [] of {byte 0, byte 16r0C},
+ DH_DSS_WITH_3DES_EDE_CBC_SHA => array [] of {byte 0, byte 16r0D},
+ DH_RSA_EXPORT_WITH_DES40_CBC_SHA => array [] of {byte 0, byte 16r0E},
+ DH_RSA_WITH_DES_CBC_SHA => array [] of {byte 0, byte 16r0F},
+ DH_RSA_WITH_3DES_EDE_CBC_SHA => array [] of {byte 0, byte 16r10},
+ DHE_DSS_EXPORT_WITH_DES40_CBC_SHA => array [] of {byte 0, byte 16r11},
+ DHE_DSS_WITH_DES_CBC_SHA => array [] of {byte 0, byte 16r12},
+ DHE_DSS_WITH_3DES_EDE_CBC_SHA => array [] of {byte 0, byte 16r13},
+ DHE_RSA_EXPORT_WITH_DES40_CBC_SHA => array [] of {byte 0, byte 16r14},
+ DHE_RSA_WITH_DES_CBC_SHA => array [] of {byte 0, byte 16r15},
+ DHE_RSA_WITH_3DES_EDE_CBC_SHA => array [] of {byte 0, byte 16r16},
+
+ DH_anon_EXPORT_WITH_RC4_40_MD5 => array [] of {byte 0, byte 16r17},
+ DH_anon_WITH_RC4_128_MD5 => array [] of {byte 0, byte 16r18},
+ DH_anon_EXPORT_WITH_DES40_CBC_SHA => array [] of {byte 0, byte 16r19},
+ DH_anon_WITH_DES_CBC_SHA => array [] of {byte 0, byte 16r1A},
+ DH_anon_WITH_3DES_EDE_CBC_SHA => array [] of {byte 0, byte 16r1B},
+
+ FORTEZZA_KEA_WITH_NULL_SHA => array [] of {byte 0, byte 16r1C},
+ FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA => array [] of {byte 0, byte 16r1D},
+ FORTEZZA_KEA_WITH_RC4_128_SHA => array [] of {byte 0, byte 16r1E},
+};
+
+#
+# key exchange algorithms
+#
+DHmodlen : con 512; # default length
+
+
+#
+# certificate types
+#
+SSL_RSA_sign : con 1;
+ SSL_DSS_sign : con 2;
+ SSL_RSA_fixed_DH : con 3;
+ SSL_DSS_fixed_DH : con 4;
+ SSL_RSA_emhemeral_DH : con 5;
+ SSL_DSS_empemeral_DH : con 6;
+ SSL_FORTEZZA_MISSI : con 20;
+
+#
+# cipher definitions
+#
+SSL_EXPORT_TRUE : con 0;
+ SSL_EXPORT_FALSE : con 1;
+
+SSL_NULL_CIPHER,
+ SSL_RC4,
+ SSL_RC2_CBC,
+ SSL_IDEA_CBC,
+ SSL_DES_CBC,
+ SSL_3DES_EDE_CBC,
+ SSL_FORTEZZA_CBC : con iota;
+
+SSL_STREAM_CIPHER,
+ SSL_BLOCK_CIPHER : con iota;
+
+SSL_NULL_MAC,
+ SSL_MD5,
+ SSL_SHA : con iota;
+
+#
+# MAC paddings
+#
+SSL_MAX_MAC_PADDING : con 48;
+SSL_MAC_PAD1 := array [] of {
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+};
+SSL_MAC_PAD2 := array [] of {
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+};
+
+#
+# finished messages
+#
+SSL_CLIENT_SENDER := array [] of {
+ byte 16r43, byte 16r4C, byte 16r4E, byte 16r54};
+SSL_SERVER_SENDER := array [] of {
+ byte 16r53, byte 16r52, byte 16r56, byte 16r52};
+
+#
+# a default distiguished names
+#
+RSA_COMMERCIAL_CA_ROOT_SUBJECT_NAME := array [] of {
+ byte 16r30, byte 16r5F, byte 16r31, byte 16r0B,
+ byte 16r30, byte 16r09, byte 16r06, byte 16r03,
+ byte 16r55, byte 16r04, byte 16r06, byte 16r13,
+ byte 16r02, byte 16r55, byte 16r53, byte 16r31,
+ byte 16r20, byte 16r30, byte 16r1E, byte 16r06,
+ byte 16r03, byte 16r55, byte 16r04, byte 16r0A,
+ byte 16r13, byte 16r17, byte 16r52, byte 16r53,
+ byte 16r41, byte 16r20, byte 16r44, byte 16r61,
+ byte 16r74, byte 16r61, byte 16r20, byte 16r53,
+ byte 16r65, byte 16r63, byte 16r75, byte 16r72,
+ byte 16r69, byte 16r74, byte 16r79, byte 16r2C,
+ byte 16r20, byte 16r49, byte 16r6E, byte 16r63,
+ byte 16r2E, byte 16r31, byte 16r2E, byte 16r30,
+ byte 16r2C, byte 16r06, byte 16r03, byte 16r55,
+ byte 16r04, byte 16r0B, byte 16r13, byte 16r25,
+ byte 16r53, byte 16r65, byte 16r63, byte 16r75,
+ byte 16r72, byte 16r65, byte 16r20, byte 16r53,
+ byte 16r65, byte 16r72, byte 16r76, byte 16r65,
+ byte 16r72, byte 16r20, byte 16r43, byte 16r65,
+ byte 16r72, byte 16r74, byte 16r69, byte 16r66,
+ byte 16r69, byte 16r63, byte 16r61, byte 16r74,
+ byte 16r69, byte 16r6F, byte 16r6E, byte 16r20,
+ byte 16r41, byte 16r75, byte 16r74, byte 16r68,
+ byte 16r6F, byte 16r72, byte 16r69, byte 16r74,
+ byte 16r79,
+};
+
+# SSL internal status
+USE_DEVSSL,
+ SSL3_RECORD,
+ SSL3_HANDSHAKE,
+ SSL2_HANDSHAKE,
+ CLIENT_SIDE,
+ SESSION_RESUMABLE,
+ CLIENT_AUTH,
+ CERT_REQUEST,
+ CERT_SENT,
+ CERT_RECEIVED,
+ OUT_READY,
+ IN_READY : con 1 << iota;
+
+# SSL internal state
+STATE_EXIT,
+ STATE_CHANGE_CIPHER_SPEC,
+ STATE_HELLO_REQUEST,
+ STATE_CLIENT_HELLO,
+ STATE_SERVER_HELLO,
+ STATE_CLIENT_KEY_EXCHANGE,
+ STATE_SERVER_KEY_EXCHANGE,
+ STATE_SERVER_HELLO_DONE,
+ STATE_CLIENT_CERTIFICATE,
+ STATE_SERVER_CERTIFICATE,
+ STATE_CERTIFICATE_VERIFY,
+ STATE_FINISHED : con iota;
+
+#
+# load necessary modules
+#
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ if(sys == nil)
+ return "ssl3: load sys module failed";
+ logfd = sys->fildes(1);
+
+ keyring = load Keyring Keyring->PATH;
+ if(keyring == nil)
+ return "ssl3: load keyring module failed";
+
+ random = load Random Random->PATH;
+ if(random == nil)
+ return "ssl3: load random module failed";
+
+ daytime = load Daytime Daytime->PATH;
+ if(daytime == nil)
+ return "ssl3: load Daytime module failed";
+
+ pkcs = load PKCS PKCS->PATH;
+ if(pkcs == nil)
+ return "ssl3: load pkcs module failed";
+ pkcs->init();
+
+ x509 = load X509 X509->PATH;
+ if(x509 == nil)
+ return "ssl3: load x509 module failed";
+ x509->init();
+
+ ssl = load SSL SSL->PATH;
+ if(ssl == nil)
+ return "ssl3: load SSL module failed";
+ sslsession = load SSLsession SSLsession->PATH;
+ if(sslsession == nil)
+ return "ssl3: load sslsession module failed";
+ e := sslsession->init();
+ if(e != nil)
+ return "ssl3: sslsession init failed: "+e;
+
+ return "";
+}
+
+log(s: string)
+{
+ a := array of byte (s + "\n");
+ sys->write(logfd, a, len a);
+}
+
+#
+# protocol context
+#
+
+Context.new(): ref Context
+{
+ ctx := ref Context;
+
+ ctx.c = nil;
+ ctx.session = nil;
+ ctx.local_info = nil;
+
+ ctx.sha_state = nil;
+ ctx.md5_state = nil;
+
+ ctx.status = 0;
+ ctx.state = 0;
+
+ ctx.client_random = array [32] of byte;
+ ctx.server_random = array [32] of byte;
+
+ ctx.cw_mac = nil;
+ ctx.sw_mac = nil;
+ ctx.cw_key = nil;
+ ctx.sw_key = nil;
+ ctx.cw_IV = nil;
+ ctx.sw_IV = nil;
+
+ ctx.in_queue = RecordQueue.new();
+ ctx.in_queue.data = ref Record(0, nil, array [1<<15] of byte) :: nil;
+ ctx.out_queue = RecordQueue.new();
+
+ # set session resumable as default
+ ctx.status |= SESSION_RESUMABLE;
+
+ return ctx;
+}
+
+Context.client(ctx: self ref Context, fd: ref Sys->FD, peername: string, ver: int, info: ref Authinfo)
+ : (string, int)
+{
+ if(SSL_DEBUG)
+ log(sys->sprint("ssl3: Context.Client peername=%s ver=%d\n", peername, ver));
+ if ((ckstr := cksuites(info.suites)) != nil)
+ return (ckstr, ver);
+ # the order is important
+ ctx.local_info = info;
+ ctx.state = STATE_HELLO_REQUEST;
+ e := ctx.connect(fd);
+ if(e != "")
+ return (e, ver);
+ ctx.session = sslsession->get_session_byname(peername);
+
+ # Request to resume an SSL 3.0 session should use an SSL 3.0 client hello
+ if(ctx.session.session_id != nil) {
+ if((ctx.session.version[0] == SSL_VERSION_3_0[0]) &&
+ (ctx.session.version[1] == SSL_VERSION_3_0[1])) {
+ ver = 3;
+ ctx.status |= SSL3_HANDSHAKE;
+ ctx.status &= ~SSL2_HANDSHAKE;
+ }
+ }
+ e = ctx.set_version(ver);
+ if(e != "")
+ return (e, ver);
+ reset_client_random(ctx);
+ ctx.status |= CLIENT_SIDE;
+ e = do_protocol(ctx);
+ if(e != nil)
+ return (e, ver);
+
+ if(ctx.status & SSL3_RECORD)
+ ver = 3;
+ else
+ ver = 2;
+ return (nil, ver);
+}
+
+Context.server(ctx: self ref Context, fd: ref Sys->FD, info: ref Authinfo, client_auth: int)
+ : string
+{
+ if ((ckstr := cksuites(info.suites)) != nil)
+ return ckstr;
+ ctx.local_info = info;
+ if(client_auth)
+ ctx.status |= CLIENT_AUTH;
+ ctx.state = STATE_CLIENT_HELLO;
+ e := ctx.connect(fd);
+ if(e != "")
+ return e;
+ reset_server_random(ctx);
+ e = ctx.set_version(3); # set ssl device to version 3
+ if(e != "")
+ return e;
+ ctx.status &= ~CLIENT_SIDE;
+ e = do_protocol(ctx);
+ if(e != nil)
+ return e;
+
+ return "";
+}
+
+
+Context.use_devssl(ctx: self ref Context)
+{
+ if(!(ctx.status & IN_READY) && !(ctx.status & OUT_READY))
+ ctx.status |= USE_DEVSSL;
+}
+
+Context.set_version(ctx: self ref Context, vers: int): string
+{
+ err := "";
+
+ if(ctx.c == nil) {
+ err = "no connection provided";
+ }
+ else {
+ if(SSL_DEBUG)
+ log("ssl3: record version = " + string vers);
+
+ if(vers == 2) {
+ ctx.status &= ~SSL3_RECORD;
+ ctx.status &= ~SSL3_HANDSHAKE;
+ ctx.status |= SSL2_HANDSHAKE;
+ if (ctx.session != nil)
+ ctx.session.version = SSL_VERSION_2_0;
+ }
+ else if(vers == 3) { # may be sslv2 handshake using ssl3 record
+ ctx.status |= SSL3_RECORD;
+ ctx.status |= SSL3_HANDSHAKE;
+ ctx.status &= ~SSL2_HANDSHAKE; # tmp test only
+ if (ctx.session != nil)
+ ctx.session.version = SSL_VERSION_3_0;
+ }
+ else if(vers == 23) { # may be sslv2 handshake using ssl3 record
+ ctx.status &= ~SSL3_RECORD;
+ ctx.status |= SSL3_HANDSHAKE;
+ ctx.status |= SSL2_HANDSHAKE;
+ vers = 2;
+ }
+ else
+ err = "unsupported ssl device version";
+
+ if((err == "") && (ctx.status & USE_DEVSSL)) {
+ if(sys->fprint(ctx.c.cfd, "ver %d", vers) < 0)
+ err = sys->sprint("ssl3: set ssl device version failed: %r");
+ }
+ }
+
+ return err;
+}
+
+Context.connect(ctx: self ref Context, fd: ref Sys->FD): string
+{
+ err := "";
+
+ if(ctx.status & USE_DEVSSL)
+ (err, ctx.c) = ssl->connect(fd);
+ else {
+ ctx.c = ref Sys->Connection(fd, nil, "");
+ ctx.in_queue.sequence_numbers[0] = 0;
+ ctx.out_queue.sequence_numbers[1] = 0;
+ }
+
+ return err;
+}
+
+Context.read(ctx: self ref Context, a: array of byte, n: int): int
+{
+ if(ctx.state != STATE_EXIT || !(ctx.status & IN_READY)) {
+ if(SSL_DEBUG)
+ log("ssl3: read not ready\n");
+ return -1;
+ }
+
+ if(ctx.out_queue.data != nil)
+ record_write_queue(ctx);
+
+ if(ctx.status & USE_DEVSSL) {
+ fd := ctx.c.dfd;
+ if(ctx.status & SSL3_RECORD) {
+ buf := array [n+3] of byte;
+ m := sys->read(fd, buf, n+3); # header + n bytes
+ if(m < 3) {
+ if(SSL_DEBUG)
+ log(sys->sprint("ssl3: read failure: %r"));
+ return -1;
+ }
+
+ if(buf[1] != SSL_VERSION_3_0[0] || buf[2] != SSL_VERSION_3_0[1]) {
+ if(SSL_DEBUG)
+ log("ssl3: not ssl version 3 data: header = [" + bastr(buf[0:3]) + "]");
+ return -1;
+ }
+
+ a[0:] = buf[3:m];
+
+ content_type := int buf[0];
+ case content_type {
+ SSL_APPLICATION_DATA =>
+ break;
+ SSL_ALERT =>
+ if(SSL_DEBUG)
+ log("ssl3: expect application data, got alert: [" + bastr(buf[3:m]) +"]");
+ return 0;
+ SSL_HANDSHAKE =>
+ if(SSL_DEBUG)
+ log("ssl3: expect application data, got handshake message");
+ return 0;
+ SSL_CHANGE_CIPHER_SPEC =>
+ if(SSL_DEBUG)
+ log("ssl3: dynamic change cipher spec not supported yet");
+ return 0;
+ }
+ return m-3;
+ }
+ else
+ return sys->read(fd, a, n);
+ }
+ else {
+ q := ctx.in_queue;
+ got := 0;
+ if(q.fragment) {
+ d := (hd q.data).data;
+ m := q.e - q.b;
+ i := q.e - q.fragment;
+ if(q.fragment > n) {
+ a[0:] = d[i:i+n];
+ q.fragment -= n;
+ got = n;
+ }
+ else {
+ a[0:] = d[i:q.e];
+ got = q.fragment;
+ q.fragment = 0;
+ }
+ }
+out:
+ while(got < n) {
+ err := q.read(ctx, ctx.c.dfd);
+ if(err != "") {
+ if(SSL_DEBUG)
+ log("ssl3: read: " + err);
+ break;
+ }
+ r := hd q.data;
+ if(ctx.status & SSL3_RECORD) {
+ case r.content_type {
+ SSL_APPLICATION_DATA =>
+ break;
+ SSL_ALERT =>
+ if(SSL_DEBUG)
+ log("ssl3: read: got alert\n\t\t" + bastr(r.data[q.b:q.e]));
+ # delete session id
+ ctx.session.session_id = nil;
+ ctx.status &= ~IN_READY;
+ break out;
+ SSL_CHANGE_CIPHER_SPEC =>
+ if(SSL_DEBUG)
+ log("ssl3: read: got change cipher spec\n");
+ SSL_HANDSHAKE =>
+ if(SSL_DEBUG)
+ log("ssl3: read: got handshake data\n");
+ #do_handshake(ctx, r); # ?
+ * =>
+ if(SSL_DEBUG)
+ log("ssl3: read: unknown data\n");
+ }
+ }
+
+ if((n - got) <= (q.e - q.b)) {
+ a[got:] = r.data[q.b:q.b+n-got];
+ q.fragment = q.e - q.b - n + got;
+ got = n;
+ }
+ else {
+ a[got:] = r.data[q.b:q.e];
+ q.fragment = 0;
+ got += q.e - q.b;
+ }
+ }
+ if(SSL_DEBUG)
+ log(sys->sprint("ssl3: read: returning %d bytes\n", got));
+ return got;
+ }
+}
+
+Context.write(ctx: self ref Context, a: array of byte, n: int): int
+{
+ if(ctx.state != STATE_EXIT || !(ctx.status & OUT_READY))
+ return-1;
+
+ if(ctx.out_queue.data != nil)
+ record_write_queue(ctx);
+
+ if(ctx.status & USE_DEVSSL) {
+ if(ctx.status & SSL3_RECORD) {
+ buf := array [n+3] of byte;
+ buf[0] = byte SSL_APPLICATION_DATA;
+ buf[1:] = SSL_VERSION_3_0;
+ buf[3:] = a[0:n];
+ n = sys->write(ctx.c.dfd, buf, n+3);
+ if(n > 0)
+ n -= 3;
+ }
+ else
+ n = sys->write(ctx.c.dfd, a, n);
+ }
+ else {
+ q := ctx.out_queue;
+ v := SSL_VERSION_2_0;
+ if(ctx.status&SSL3_RECORD)
+ v = SSL_VERSION_3_0;
+ for(i := 0; i < n;){
+ m := n-i;
+ if(m > q.length)
+ m = q.length;
+ r := ref Record(SSL_APPLICATION_DATA, v, a[i:i+m]);
+ record_write(r, ctx); # return error?
+ i += m;
+ }
+ }
+ return n;
+}
+
+devssl_read(ctx: ref Context): (ref Record, string)
+{
+ buf := array [Sys->ATOMICIO] of byte;
+ r: ref Record;
+ c := ctx.c;
+
+ n := sys->read(c.dfd, buf, 3);
+ if(n < 0)
+ return (nil, sys->sprint("record read: read failure: %r"));
+
+ # in case of undetermined, do auto record version detection
+ if((ctx.state == SSL2_STATE_SERVER_HELLO) &&
+ (ctx.status & SSL2_HANDSHAKE) && (ctx.status & SSL3_HANDSHAKE)) {
+
+ fstatus := sys->open(ctx.c.dir + "/status", Sys->OREAD);
+ if(fstatus == nil)
+ return (nil, "open status: " + sys->sprint("%r"));
+ status := array [64] of byte;
+ nbyte := sys->read(fstatus, status, len status);
+ if(nbyte != 1)
+ return (nil, "read status: " + sys->sprint("%r"));
+
+ ver := int status[0];
+
+ if(SSL_DEBUG)
+ log("ssl3: auto record version detect as: " + string ver);
+
+ # assert ctx.status & SSL2_RECORD true ? before reset
+ if(ver == 2) {
+ ctx.status &= ~SSL3_RECORD;
+ ctx.status |= SSL2_HANDSHAKE;
+ ctx.status &= ~SSL3_HANDSHAKE;
+ }
+ else {
+ ctx.status |= SSL3_RECORD;
+ }
+ }
+
+ if(ctx.status & SSL3_RECORD) {
+ if(n < 3)
+ return (nil, sys->sprint("record read: read failure: %r"));
+
+ # assert only major version number
+ if(buf[1] != SSL_VERSION_3_0[0])
+ return (nil, "record read: version mismatch");
+
+ case int buf[0] {
+ SSL_ALERT =>
+ n = sys->read(c.dfd, buf, 5); # read in header plus rest
+ if(n != 5)
+ return (nil, sys->sprint("read alert failed: %r"));
+ r = ref Record(SSL_ALERT, SSL_VERSION_3_0, buf[3:5]);
+
+ SSL_CHANGE_CIPHER_SPEC =>
+ n = sys->read(c.dfd, buf, 4); # read in header plus rest
+ if(n != 4)
+ return (nil, sys->sprint("read change_cipher_spec failed: %r"));
+ r = ref Record(SSL_CHANGE_CIPHER_SPEC, SSL_VERSION_3_0, buf[3:4]);
+
+ SSL_HANDSHAKE =>
+ n = sys->read(c.dfd, buf, 7); # header + msg length
+ if(n != 7)
+ return (nil, sys->sprint("read handshake header + msg length failed: %r"));
+ m := int_decode(buf[4:7]);
+ if(m < 0)
+ return (nil, "read handshake failed: unexpected length");
+ data := array [m+4] of byte;
+ data[0:] = buf[3:7]; # msg type + length
+ if(m != 0) {
+ # need exact m bytes (exclude header), otherwise failure
+ remain := m;
+ now := 4;
+ while(remain > 0) {
+ n = sys->read(c.dfd, buf, remain+3); # header + msg
+ if(n < 3 || int buf[0] != SSL_HANDSHAKE)
+ return (nil, sys->sprint("read handshake msg body failed: %r"));
+ sys->print("expect %d, got %d bytes\n", m, n-3);
+ remain -= n - 3;
+ data[now:] = buf[3:n];
+ now += n - 3;
+ }
+ }
+
+ r = ref Record(SSL_HANDSHAKE, SSL_VERSION_3_0, data);
+ * =>
+ return (nil, "trying to read unknown protocol message");
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: record_read: \n\theader = \n\t\t" + bastr(buf[0:3])
+ + "\n\tdata = \n\t\t" + bastr(r.data) + "\n");
+ }
+ # v2 record layer
+ else {
+ # assume the handshake record size less than Sys->ATOMICIO
+ # in most case, this is ok
+ if(n == 3) {
+ n = sys->read(c.dfd, buf[3:], Sys->ATOMICIO - 3);
+ if(n < 0)
+ return (nil, sys->sprint("v2 record read: read failure: %r"));
+ }
+
+ r = ref Record(SSL_V2HANDSHAKE, SSL_VERSION_2_0, buf[0:n+3]);
+
+ if(SSL_DEBUG)
+ log("ssl3: v2 record_read: \n\tdata = \n\t\t" + bastr(r.data) + "\n");
+ }
+
+ return (r, nil);
+}
+
+record_read(ctx: ref Context): (ref Record, string)
+{
+ q := ctx.in_queue;
+ if(q.fragment == 0) {
+ err := q.read(ctx, ctx.c.dfd);
+ if(err != "")
+ return (nil, err);
+ q.fragment = q.e - q.b;
+ }
+
+ r := hd q.data;
+ if(ctx.status & SSL3_RECORD) {
+ # confirm only major version number
+ if(r.version[0] != SSL_VERSION_3_0[0])
+ return (nil, "record read: not v3 record");
+
+ case r.content_type {
+ SSL_ALERT =>
+ a := array [2] of byte;
+ n := fetch_data(ctx, a, 2);
+ if(n != 2)
+ return (nil, "read alert failed");
+ r = ref Record(SSL_ALERT, SSL_VERSION_3_0, a);
+
+ SSL_CHANGE_CIPHER_SPEC =>
+ a := array [1] of byte;
+ n := fetch_data(ctx, a, 1);
+ if(n != 1)
+ return (nil, "read change_cipher_spec failed");
+ r = ref Record(SSL_CHANGE_CIPHER_SPEC, SSL_VERSION_3_0, a);
+
+ SSL_HANDSHAKE =>
+ a := array [4] of byte;
+ n := fetch_data(ctx, a, 4);
+ if(n != 4)
+ return (nil, "read message length failed");
+ m := int_decode(a[1:]);
+ if(m < 0)
+ return (nil, "unexpected handshake message length");
+ b := array [m+4] of byte;
+ b[0:] = a;
+ n = fetch_data(ctx, b[4:], m);
+ if(n != m)
+ return (nil, "read message body failed");
+ r = ref Record(SSL_HANDSHAKE, SSL_VERSION_3_0, b);
+ * =>
+ return (nil, "trying to read unknown protocol message");
+ }
+ }
+ # v2 record layer
+ else {
+ r = ref Record(SSL_V2HANDSHAKE, SSL_VERSION_2_0, r.data[q.b:q.e]);
+ q.fragment = 0;
+ }
+
+ return (r, nil);
+}
+
+fetch_data(ctx: ref Context, a: array of byte, n: int): int
+{
+ q := ctx.in_queue;
+ r := hd q.data;
+
+ got := 0;
+ cnt := -1;
+out:
+ while(got < n) {
+ if(q.fragment) {
+ cnt = r.content_type;
+ i := q.e - q.fragment;
+ if(n-got <= q.fragment) {
+ a[got:] = r.data[i:i+n-got];
+ q.fragment -= n - got;
+ got = n;
+ }
+ else {
+ a[got:] = r.data[i:q.e];
+ got += q.fragment;
+ q.fragment = 0;
+ }
+ }
+ else {
+ err := q.read(ctx, ctx.c.dfd);
+ if(err != "")
+ break out;
+ if(cnt == -1)
+ cnt = r.content_type;
+ if(ctx.status & SSL3_RECORD) {
+ case r.content_type {
+ SSL_APPLICATION_DATA =>
+ break;
+ * =>
+ if(cnt != r.content_type)
+ break out;
+ }
+ }
+ else {
+ r.content_type = SSL_V2HANDSHAKE;
+ }
+ }
+ }
+ return got;
+}
+
+record_write(r: ref Record, ctx: ref Context)
+{
+ if(ctx.status & USE_DEVSSL) {
+ buf: array of byte;
+ n: int;
+ c := ctx.c;
+
+ if(ctx.status & SSL3_RECORD) {
+ buf = array [3 + len r.data] of byte;
+ buf[0] = byte r.content_type;
+ buf[1:] = r.version;
+ buf[3:] = r.data;
+ n = sys->write(c.dfd, buf, len buf);
+ if(n < 0 || n != len buf) {
+ if(SSL_DEBUG)
+ log(sys->sprint("ssl3: v3 record write error: %d %r", n));
+ return; # don't terminated until alerts being read
+ }
+ }
+ else {
+ buf = r.data;
+ n = sys->write(c.dfd, buf, len buf);
+ if(n < 0 || n != len buf) {
+ if(SSL_DEBUG)
+ log(sys->sprint("ssl3: v2 record write error: %d %r", n));
+ return; # don't terminated until alerts being read
+ }
+ }
+ }
+ else
+ ctx.out_queue.write(ctx, ctx.c.dfd, r);
+
+ # if(SSL_DEBUG)
+ # log("ssl3: record_write: \n\t\t" + bastr(buf) + "\n");
+}
+
+RecordQueue.new(): ref RecordQueue
+{
+ q := ref RecordQueue(
+ ref MacState.null(0),
+ ref CipherState.null(1),
+ 1 << 15,
+ array [2] of { * => 0},
+ nil,
+ 0,
+ 0, # b
+ 0 # e
+ );
+ return q;
+}
+
+RecordQueue.read(q: self ref RecordQueue, ctx: ref Context, fd: ref Sys->FD): string
+{
+ r := hd q.data;
+ a := r.data;
+ if(ensure(fd, a, 2) < 0)
+ return "no more data";
+ # auto record version detection
+ m, h, pad: int = 0;
+ if(int a[0] < 20 || int a[0] > 23) {
+ ctx.status &= ~SSL3_RECORD;
+ if(int a[0] & 16r80) {
+ h = 2;
+ m = ((int a[0] & 16r7f) << 8) | int a[1];
+ pad = 0;
+ } else {
+ h = 3;
+ m = ((int a[0] & 16r3f) << 8) | int a[1];
+ if(ensure(fd, a[2:], 1) < 0)
+ return "bad v2 record";
+ pad = int a[2];
+ if(pad > m)
+ return "bad v2 pad";
+ }
+ r.content_type = SSL_V2HANDSHAKE;
+ r.version = SSL_VERSION_2_0;
+ }
+ else {
+ ctx.status |= SSL3_RECORD;
+ h = 5;
+ if(ensure(fd, a[2:], 3) < 0)
+ return "bad v3 record";
+ m = ((int a[3]) << 8) | int a[4];
+ r.content_type = int a[0];
+ r.version = a[1:3];
+ }
+ if(ensure(fd, a[h:], m) < 0)
+# return "data too short";
+ return sys->sprint("data too short wanted %d", m);
+ if(SSL_DEBUG) {
+ log("ssl3: record read\n\tbefore decrypt\n\t\t" + bastr(a[0:m+h]));
+ log(sys->sprint("SSL3=%d\n", ctx.status & SSL3_RECORD));
+ }
+
+ # decrypt (data, pad, mac)
+ pick dec := q.cipherState {
+ null =>
+ rc4 =>
+ keyring->rc4(dec.es, a[h:], m);
+ if (SSL_DEBUG) log("rc4 1");
+ descbc =>
+ keyring->descbc(dec.es, a[h:], m, 1);
+ if (SSL_DEBUG) log("descbc 1");
+ ideacbc =>
+ keyring->ideacbc(dec.es, a[h:], m, 1);
+ if (SSL_DEBUG) log("ideacbc 1");
+ * =>
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: record read\n\tafter decrypt\n\t\t" + bastr(a[0:m]));
+
+ idata, imac, ipad: int = 0;
+ if(ctx.status & SSL3_RECORD) {
+ if(q.cipherState.block_size > 1){
+ pad = int a[h + m - 1];
+ if(pad >= q.cipherState.block_size)
+ return "bad v3 pad";
+ # pad++;
+ ipad = h+m-pad-1;
+ }
+ else
+ ipad = h+m-pad;
+ imac = ipad - q.macState.hash_size;
+ idata = h;
+ }
+ else {
+ imac = h;
+ idata = imac + q.macState.hash_size;
+ ipad = h + m - pad;
+ }
+ if(tagof q.macState != tagof MacState.null) {
+ if (ctx.status & SSL3_RECORD)
+ mac := q.calcmac(ctx, r.content_type, a, idata, imac-idata);
+ else
+ mac = q.calcmac(ctx, r.content_type, a, idata, ipad+pad-idata);
+ if(bytes_cmp(mac, a[imac:imac+len mac]) < 0)
+ return "bad mac";
+ }
+ q.b = idata;
+ if (ctx.status & SSL3_RECORD)
+ q.e = imac;
+ else
+ q.e = ipad;
+ q.fragment = q.e - q.b;
+
+ if((++q.sequence_numbers[0] == 0) && (ctx.status&SSL3_RECORD))
+ ++q.sequence_numbers[1];
+
+ return "";
+}
+
+ensure(fd: ref Sys->FD, a: array of byte, n: int): int
+{
+ i, m: int = 0;
+ while(i < n) {
+ m = sys->read(fd, a[i:], n - i);
+ if(m <= 0) {
+ return -1;
+ }
+ i += m;
+ }
+ return n;
+}
+
+RecordQueue.write(q: self ref RecordQueue, ctx: ref Context, fd: ref Sys->FD,
+ r: ref Record): string
+{
+ m := len r.data;
+ h, pad: int = 0;
+ if(ctx.status & SSL3_RECORD) {
+ h = 5;
+ if(q.cipherState.block_size > 1) {
+ pad = (m+q.macState.hash_size+1)%q.cipherState.block_size;
+ if (pad)
+ pad = q.cipherState.block_size - pad;
+ }
+ }
+ else {
+ h = 2;
+ if(q.cipherState.block_size > 1) {
+ pad = m%q.cipherState.block_size;
+ if(pad) {
+ pad = q.cipherState.block_size - pad;
+ h++;
+ }
+ }
+ }
+
+ m += pad + q.macState.hash_size;
+ if ((ctx.status & SSL3_RECORD) && q.cipherState.block_size > 1)
+ m++;
+ a := array [h+m] of byte;
+
+ idata, imac, ipad: int = 0;
+ if(ctx.status & SSL3_RECORD) {
+ a[0] = byte r.content_type;
+ a[1:] = r.version;
+ a[3] = byte (m >> 8); #CJL - netscape ssl3 traces do not show top bit set
+# a[3] = byte ((m >> 8) | 16r80); #CJL
+# a[3] = byte (m | 16r8000) >> 8;
+ a[4] = byte m;
+ idata = h;
+ imac = idata + len r.data;
+ ipad = imac + q.macState.hash_size;
+ if (q.cipherState.block_size > 1)
+ a[h+m-1] = byte pad;
+ }
+ else {
+ if(pad) {
+ a[0] = byte m >> 8;
+ a[2] = byte pad;
+ }
+ else
+ a[0] = byte ((m >> 8) | 16r80);
+ a[1] = byte m;
+ imac = h;
+ idata = imac + q.macState.hash_size;
+ ipad = idata + len r.data;
+ }
+ a[idata:] = r.data;
+ if(pad)
+ a[ipad:] = array [pad] of { * => byte (pad-1)};
+
+ if(tagof q.macState != tagof MacState.null) {
+ if (ctx.status & SSL3_RECORD)
+ a[imac:] = q.calcmac(ctx, r.content_type, a, idata, len r.data);
+ else
+ a[imac:] = q.calcmac(ctx, r.content_type, a, idata, ipad+pad-idata);
+ }
+
+ if(SSL_DEBUG) {
+ log("ssl3: record write\n\tbefore encrypt\n\t\t" + bastr(a));
+ log(sys->sprint("SSL3=%d\n", ctx.status & SSL3_RECORD));
+ }
+
+ # encrypt (data, pad, mac)
+ pick enc := q.cipherState {
+ null =>
+ rc4 =>
+ keyring->rc4(enc.es, a[h:], m);
+ if (SSL_DEBUG) log("rc4 0");
+ descbc =>
+ keyring->descbc(enc.es, a[h:], m, 0);
+ if (SSL_DEBUG) log(sys->sprint("descbc 0 %d", m));
+ ideacbc =>
+ keyring->ideacbc(enc.es, a[h:], m, 0);
+ if (SSL_DEBUG) log(sys->sprint("ideacbc 0 %d", m));
+ * =>
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: record write\n\tafter encrypt\n\t\t" + bastr(a));
+
+ if(sys->write(fd, a, h+m) < 0)
+ return sys->sprint("ssl3: record write: %r");
+
+ if((++q.sequence_numbers[0] == 0) && (ctx.status&SSL3_RECORD))
+ ++q.sequence_numbers[1];
+
+ return "";
+}
+
+RecordQueue.calcmac(q: self ref RecordQueue, ctx: ref Context, cntype: int, a: array of byte,
+ ofs, n: int) : array of byte
+{
+ digest, b: array of byte;
+
+ if(ctx.status & SSL3_RECORD) {
+ b = array [11] of byte;
+ i := putn(b, 0, q.sequence_numbers[1], 4);
+ i = putn(b, i, q.sequence_numbers[0], 4);
+ b[i++] = byte cntype;
+ putn(b, i, n, 2);
+ }
+ else {
+ b = array [4] of byte;
+ putn(b, 0, q.sequence_numbers[0], 4);
+ }
+
+ # if(SSL_DEBUG)
+ # log("ssl3: record mac\n\tother =\n\t\t" + bastr(b));
+
+ pick ms := q.macState {
+ md5 =>
+ digest = array [Keyring->MD5dlen] of byte;
+ ds0 := ms.ds[0].copy();
+ if(ctx.status & SSL3_RECORD) {
+ keyring->md5(b, len b, nil, ds0);
+ keyring->md5(a[ofs:], n, digest, ds0);
+ ds1 := ms.ds[1].copy();
+ keyring->md5(digest, len digest, digest, ds1);
+ }
+ else {
+ keyring->md5(a[ofs:], n, nil, ds0);
+ keyring->md5(b, len b, digest, ds0);
+ }
+ sha =>
+ digest = array [Keyring->SHA1dlen] of byte;
+ ds0 := ms.ds[0].copy();
+ if(ctx.status & SSL3_RECORD) {
+ keyring->sha1(b, len b, nil, ds0);
+ keyring->sha1(a[ofs:], n, digest, ds0);
+ ds1 := ms.ds[1].copy();
+ keyring->sha1(digest, len digest, digest, ds1);
+ }
+ else {
+ keyring->sha1(a[ofs:], n, nil, ds0);
+ keyring->sha1(b, len b, digest, ds0);
+ }
+ }
+ return digest;
+}
+
+set_queues(ctx: ref Context): string
+{
+ sw: array of byte;
+ if(ctx.sw_key != nil) {
+ sw = array [len ctx.sw_key + len ctx.sw_IV] of byte;
+ sw[0:] = ctx.sw_key;
+ sw[len ctx.sw_key:] = ctx.sw_IV;
+ }
+ cw: array of byte;
+ if(ctx.cw_key != nil) {
+ cw = array [len ctx.cw_key + len ctx.cw_IV] of byte;
+ cw[0:] = ctx.cw_key;
+ cw[len ctx.cw_key:] = ctx.cw_IV;
+ }
+
+ err := "";
+ if(ctx.status & USE_DEVSSL) {
+ err = set_secrets(ctx.c, ctx.sw_mac, ctx.cw_mac, sw, cw);
+ if(err == "")
+ err = set_cipher_algs(ctx);
+ }
+ else {
+ err = set_out_queue(ctx);
+ if(err == "")
+ err = set_in_queue(ctx);
+ }
+
+ return err;
+}
+
+set_in_queue(ctx: ref Context): string
+{
+ sw: array of byte;
+ if(ctx.sw_key != nil) {
+ sw = array [len ctx.sw_key + len ctx.sw_IV] of byte;
+ sw[0:] = ctx.sw_key;
+ sw[len ctx.sw_key:] = ctx.sw_IV;
+ }
+
+ err := "";
+ if(ctx.status & USE_DEVSSL) {
+ err = set_secrets(ctx.c, ctx.sw_mac, nil, sw, nil);
+ if(err == "")
+ err = set_cipher_algs(ctx);
+ }
+ else
+ err = set_queue(ctx, ctx.in_queue, ctx.sw_mac, sw);
+
+ return err;
+}
+
+set_out_queue(ctx: ref Context): string
+{
+ cw: array of byte;
+ if(ctx.cw_key != nil) {
+ cw = array [len ctx.cw_key + len ctx.cw_IV] of byte;
+ cw[0:] = ctx.cw_key;
+ cw[len ctx.cw_key:] = ctx.cw_IV;
+ }
+
+ err := "";
+ if(ctx.status & USE_DEVSSL) {
+ err = set_secrets(ctx.c, nil, ctx.cw_mac, nil, cw);
+ if(err == "")
+ err = set_cipher_algs(ctx);
+ }
+ else
+ err = set_queue(ctx, ctx.out_queue, ctx.cw_mac, cw);
+
+ return err;
+}
+
+set_queue(ctx: ref Context, q: ref RecordQueue, mac, key: array of byte): string
+{
+ e := "";
+
+ case ctx.sel_ciph.mac_algorithm {
+ SSL_NULL_MAC =>
+ q.macState = ref MacState.null(0);
+ SSL_MD5 =>
+ ds: array of ref DigestState;
+ if(ctx.status & SSL3_RECORD) {
+ ds = array [2] of ref DigestState;
+ ds[0] = keyring->md5(mac, len mac, nil, nil);
+ ds[1] = keyring->md5(mac, len mac, nil, nil);
+ ds[0] = keyring->md5(SSL_MAC_PAD1, 48, nil, ds[0]);
+ ds[1] = keyring->md5(SSL_MAC_PAD2, 48, nil, ds[1]);
+ }
+ else {
+ ds = array [1] of ref DigestState;
+ ds[0] = keyring->md5(mac, len mac, nil, nil);
+ }
+ q.macState = ref MacState.md5(Keyring->MD5dlen, ds);
+ SSL_SHA =>
+ ds: array of ref DigestState;
+ if(ctx.status & SSL3_RECORD) {
+ ds = array [2] of ref DigestState;
+ ds[0] = keyring->sha1(mac, len mac, nil, nil);
+ ds[1] = keyring->sha1(mac, len mac, nil, nil);
+ ds[0] = keyring->sha1(SSL_MAC_PAD1, 40, nil, ds[0]);
+ ds[1] = keyring->sha1(SSL_MAC_PAD2, 40, nil, ds[1]);
+ }
+ else {
+ ds = array [1] of ref DigestState;
+ ds[0] = keyring->sha1(mac, len mac, nil, nil);
+ }
+ q.macState = ref MacState.sha(Keyring->SHA1dlen, ds);
+ * =>
+ e = "ssl3: digest method: unknown";
+ }
+
+ case ctx.sel_ciph.bulk_cipher_algorithm {
+ SSL_NULL_CIPHER =>
+ q.cipherState = ref CipherState.null(1);
+ SSL_RC4 =>
+ if (SSL_DEBUG) log("rc4setup");
+ rcs := keyring->rc4setup(key);
+ q.cipherState = ref CipherState.rc4(1, rcs);
+ SSL_DES_CBC =>
+ dcs : ref keyring->DESstate;
+
+ if (SSL_DEBUG) log(sys->sprint("dessetup %d", len key));
+ if (len key >= 16)
+ dcs = keyring->dessetup(key[0:8], key[8:16]);
+ else if (len key >= 8)
+ dcs = keyring->dessetup(key[0:8], nil);
+ else
+ e = "ssl3: bad DES key length";
+ q.cipherState = ref CipherState.descbc(8, dcs);
+ SSL_IDEA_CBC =>
+ ics : ref keyring->IDEAstate;
+
+ if (SSL_DEBUG) log(sys->sprint("ideasetup %d", len key));
+ if (len key >= 24)
+ ics = keyring->ideasetup(key[0:16], key[16:24]);
+ else if (len key >= 16)
+ ics = keyring->ideasetup(key[0:16], nil);
+ else
+ e = "ssl3: bad IDEA key length";
+ q.cipherState = ref CipherState.ideacbc(8, ics);
+ SSL_RC2_CBC or
+ SSL_3DES_EDE_CBC or
+ SSL_FORTEZZA_CBC =>
+ e = "ssl3: unsupported cipher";
+ * =>
+ e = "ssl3: unknown cipher";
+ }
+
+ if(ctx.status & SSL3_RECORD) {
+ q.length = 1 << 14;
+ if(tagof q.macState != tagof MacState.null)
+ q.length += 2048;
+ }
+ else {
+ if(q.cipherState.block_size > 1) {
+ q.length = (1<<14) - q.macState.hash_size - 1;
+ q.length -= q.length % q.cipherState.block_size;
+ }
+ else
+ q.length = (1<<15) - q.macState.hash_size - 1;
+ }
+ if(ctx.status & SSL3_RECORD)
+ q.sequence_numbers[0] = q.sequence_numbers[1] = 0;
+
+ return e;
+}
+
+set_cipher_algs(ctx: ref Context) : string
+{
+ e: string;
+
+ algspec := "alg";
+
+ case enc := ctx.sel_ciph.bulk_cipher_algorithm {
+ SSL_NULL_CIPHER =>
+ algspec += " clear";
+ SSL_RC4 => # stream cipher
+ algspec += " rc4_128";
+ SSL_DES_CBC => # block cipher
+ algspec += " descbc";
+ SSL_IDEA_CBC => # block cipher
+ algspec += " ideacbc";
+ SSL_RC2_CBC or
+ SSL_3DES_EDE_CBC or
+ SSL_FORTEZZA_CBC =>
+ e = "ssl3: encrypt method: unsupported";
+ * =>
+ e = "ssl3: encrypt method: unknown";
+ }
+
+ case mac := ctx.sel_ciph.mac_algorithm {
+ SSL_NULL_MAC =>
+ algspec += " clear";
+ SSL_MD5 =>
+ algspec += " md5";
+ SSL_SHA =>
+ algspec += " sha1";
+ * =>
+ e = "ssl3: digest method: unknown";
+ }
+
+ e = set_ctl(ctx.c, algspec);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("failed to set cipher algs: " + e);
+ }
+
+ return e;
+}
+
+set_ctl(c: ref Sys->Connection, s: string): string
+{
+ a := array of byte s;
+ if(sys->write(c.cfd, a, len a) < 0)
+ return sys->sprint("error writing sslctl: %r");
+
+ if(SSL_DEBUG)
+ log("ssl3: set cipher algorithm:\n\t\t" + s + "\n");
+
+ return "";
+}
+
+set_secrets(c: ref Sys->Connection, min, mout, sin, sout: array of byte) : string
+{
+ fmin := sys->open(c.dir + "/macin", Sys->OWRITE);
+ fmout := sys->open(c.dir + "/macout", Sys->OWRITE);
+ fsin := sys->open(c.dir + "/secretin", Sys->OWRITE);
+ fsout := sys->open(c.dir + "/secretout", Sys->OWRITE);
+ if(fmin == nil || fmout == nil || fsin == nil || fsout == nil)
+ return sys->sprint("can't open ssl secret files: %r\n");
+
+ if(sin != nil) {
+ if(SSL_DEBUG)
+ log("ssl3: set encryption secret and IV\n\tsecretin:\n\t\t" + bastr(sin) + "\n");
+ if(sys->write(fsin, sin, len sin) < 0)
+ return sys->sprint("error writing secretin: %r");
+ }
+ if(sout != nil) {
+ if(SSL_DEBUG)
+ log("ssl3: set encryption secret and IV\n\tsecretout:\n\t\t" + bastr(sout) + "\n");
+ if(sys->write(fsout, sout, len sout) < 0)
+ return sys->sprint("error writing secretout: %r");
+ }
+ if(min != nil) {
+ if(SSL_DEBUG)
+ log("ssl3: set digest secret\n\tmacin:\n\t\t" + bastr(min) + "\n");
+ if(sys->write(fmin, min, len min) < 0)
+ return sys->sprint("error writing macin: %r");
+ }
+ if(mout != nil) {
+ if(SSL_DEBUG)
+ log("ssl3: set digest secret\n\tmacout:\n\t\t" + bastr(mout) + "\n");
+ if(sys->write(fmout, mout, len mout) < 0)
+ return sys->sprint("error writing macout: %r");
+ }
+
+ return "";
+}
+
+#
+# description must be alert description
+#
+fatal(description: int, debug_msg: string, ctx: ref Context)
+{
+ if(SSL_DEBUG)
+ log("ssl3: " + debug_msg);
+
+ # TODO: use V2Handshake.Error for v2
+ alert_enque(ref Alert(SSL_FATAL, description), ctx);
+
+ # delete session id
+ ctx.session.session_id = nil;
+
+ ctx.state = STATE_EXIT;
+}
+
+alert_enque(a: ref Alert, ctx: ref Context)
+{
+ p := ref Protocol.pAlert(a);
+
+ protocol_write(p, ctx);
+}
+
+# clean up out queue before switch cipher. this is why
+# change cipher spec differs from handshake message by ssl spec
+
+ccs_enque(cs: ref ChangeCipherSpec, ctx: ref Context)
+{
+ p := ref Protocol.pChangeCipherSpec(cs);
+
+ protocol_write(p, ctx);
+
+ record_write_queue(ctx);
+ ctx.out_queue.data = nil;
+}
+
+handshake_enque(h: ref Handshake, ctx: ref Context)
+{
+ p := ref Protocol.pHandshake(h);
+
+ protocol_write(p, ctx);
+}
+
+protocol_write(p: ref Protocol, ctx: ref Context)
+{
+ record_version := SSL_VERSION_2_0;
+ if(ctx.status & SSL3_RECORD)
+ record_version = SSL_VERSION_3_0;
+ (r, e) := p.encode(record_version);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("ssl3: protocol_write: " + e);
+ exit;
+ }
+
+ # Note: only for sslv3
+ if((ctx.status&SSL2_HANDSHAKE) && (ctx.status&SSL3_HANDSHAKE)) {
+ if(ctx.state == STATE_HELLO_REQUEST) {
+ e = update_handshake_hash(ctx, r);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("ssl3: protocol_write: " + e);
+ exit;
+ }
+ }
+ }
+ if((ctx.status&SSL3_HANDSHAKE) && (r.content_type == SSL_HANDSHAKE)) {
+ e = update_handshake_hash(ctx, r);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("ssl3: protocol_write: " + e);
+ exit;
+ }
+ }
+
+ ctx.out_queue.data = r :: ctx.out_queue.data;
+}
+
+#feed_data(ctx: ref Context, a: array of byte, n: int): int
+#{
+#
+#}
+
+# FIFO
+record_write_queue(ctx: ref Context)
+{
+ write_queue : list of ref Record;
+
+ wq := ctx.out_queue.data;
+ while(wq != nil) {
+ write_queue = hd wq :: write_queue;
+ wq = tl wq;
+ }
+
+ wq = write_queue;
+ while(wq != nil) {
+ record_write(hd wq, ctx);
+ wq = tl wq;
+ }
+}
+
+# Possible combinations are v2 only, v3 only and both (undetermined). The v2 only must be
+# v2 handshake and v2 record layer. The v3 only must be v3 handshake and v3 record layer.
+# If both v2 and v3 are supported, it may be v2 handshake and v2 record layer, or v3
+# handshake and v3 record layer, or v2 handshake and v3 record layer. In the case of
+# both, the client should send a v2 client hello message with handshake protocol version v3.
+
+do_protocol(ctx: ref Context): string
+{
+ r: ref Record;
+ in: ref Protocol;
+ e: string = nil;
+
+ while(ctx.state != STATE_EXIT) {
+
+ if(SSL_DEBUG)
+ log("ssl3: state = " + state_info(ctx));
+
+ # init a new handshake
+ if(ctx.state == STATE_HELLO_REQUEST) {
+ # v2 and v3
+ if((ctx.status&SSL2_HANDSHAKE) && (ctx.status&SSL3_HANDSHAKE)) {
+ ch := ref V2Handshake.ClientHello(
+ SSL_VERSION_3_0,
+ v3tov2specs(ctx.local_info.suites),
+ ctx.session.session_id,
+ ctx.client_random
+ );
+ v2handshake_enque(ch, ctx);
+ in = ref Protocol.pV2Handshake(ch);
+ }
+ # v3 only
+ else if(ctx.status&SSL3_HANDSHAKE) {
+ in = ref Protocol.pHandshake(ref Handshake.HelloRequest());
+ }
+ # v2 only
+ else if(ctx.status&SSL2_HANDSHAKE) {
+ ch := ref V2Handshake.ClientHello(
+ SSL_VERSION_2_0,
+ v3tov2specs(ctx.local_info.suites),
+ ctx.session.session_id,
+ ctx.client_random[32-SSL2_CHALLENGE_LENGTH:32]
+ );
+ v2handshake_enque(ch, ctx);
+ in = ref Protocol.pV2Handshake(ch);
+ }
+ # unknown version
+ else {
+ e = "unknown ssl device version";
+ fatal(SSL_CLOSE_NOTIFY, "ssl3: " + e, ctx);
+ continue;
+ }
+ }
+
+ if(in == nil) {
+ (r, in, e) = protocol_read(ctx);
+ if(e != "") {
+ fatal(SSL_CLOSE_NOTIFY, "ssl3: " + e, ctx);
+ continue;
+ }
+ if(SSL_DEBUG)
+ log("ssl3: protocol_read: ------\n" + in.tostring());
+ }
+
+ pick p := in {
+ pAlert =>
+ do_alert(p.alert, ctx);
+
+ pChangeCipherSpec =>
+ if(ctx.state != STATE_CHANGE_CIPHER_SPEC) {
+ e += "ChangeCipherSpec";
+ break;
+ }
+ do_change_cipher_spec(ctx);
+
+ pHandshake =>
+ if(!(ctx.status & SSL3_HANDSHAKE)) {
+ e = "Wrong Handshake";
+ break;
+ }
+ if((ctx.status & SSL3_RECORD) &&
+ (ctx.state == SSL2_STATE_SERVER_HELLO)) {
+ ctx.state = STATE_SERVER_HELLO;
+ ctx.status &= ~SSL2_HANDSHAKE;
+ }
+ e = do_handshake(p.handshake, ctx);
+
+ pV2Handshake =>
+ if(ctx.state != STATE_HELLO_REQUEST) {
+ if(!(ctx.status & SSL2_HANDSHAKE)) {
+ e = "Wrong Handshake";
+ break;
+ }
+ e = do_v2handshake(p.handshake, ctx);
+ }
+ else
+ ctx.state = SSL2_STATE_SERVER_HELLO;
+
+
+ * =>
+ e = "unknown protocol message";
+ }
+
+ if(e != nil) {
+ e = "do_protocol: wrong protocol side or protocol message: " + e;
+ fatal(SSL_UNEXPECTED_MESSAGE, e, ctx);
+ }
+
+ in = nil;
+
+ record_write_queue(ctx);
+ ctx.out_queue.data = nil;
+ }
+
+ return e;
+}
+
+state_info(ctx: ref Context): string
+{
+ info: string;
+
+ if(ctx.status & SSL3_RECORD)
+ info = "\n\tRecord Version 3: ";
+ else
+ info = "\n\tRecord Version 2: ";
+
+ if(ctx.status & SSL2_HANDSHAKE) {
+
+ if(ctx.status & SSL3_HANDSHAKE) {
+ info += "\n\tHandshake Version Undetermined: Client Hello";
+ }
+ else {
+ info += "\n\tHandshake Version 2: ";
+
+ case ctx.state {
+ SSL2_STATE_CLIENT_HELLO =>
+ info += "Client Hello";
+ SSL2_STATE_SERVER_HELLO =>
+ info += "Server Hello";
+ SSL2_STATE_CLIENT_MASTER_KEY =>
+ info += "Client Master Key";
+ SSL2_STATE_SERVER_VERIFY =>
+ info += "Server Verify";
+ SSL2_STATE_REQUEST_CERTIFICATE =>
+ info += "Request Certificate";
+ SSL2_STATE_CLIENT_CERTIFICATE =>
+ info += "Client Certificate";
+ SSL2_STATE_CLIENT_FINISHED =>
+ info += "Client Finished";
+ SSL2_STATE_SERVER_FINISHED =>
+ info += "Server Finished";
+ SSL2_STATE_ERROR =>
+ info += "Error";
+ }
+ }
+ }
+ else {
+ info = "\n\tHandshake Version 3: ";
+
+ case ctx.state {
+ STATE_EXIT =>
+ info += "Exit";
+
+ STATE_CHANGE_CIPHER_SPEC =>
+ info += "Change Cipher Spec";
+
+ STATE_HELLO_REQUEST =>
+ info += "Hello Request";
+
+ STATE_CLIENT_HELLO =>
+ info += "Client Hello";
+
+ STATE_SERVER_HELLO =>
+ info += "Server Hello";
+
+ STATE_CLIENT_KEY_EXCHANGE =>
+ info += "Client Key Exchange";
+
+ STATE_SERVER_KEY_EXCHANGE =>
+ info += "Server Key Exchange";
+
+ STATE_SERVER_HELLO_DONE =>
+ info += "Server Hello Done";
+
+ STATE_CLIENT_CERTIFICATE =>
+ info += "Client Certificate";
+
+ STATE_SERVER_CERTIFICATE =>
+ info += "Server Certificate";
+
+ STATE_CERTIFICATE_VERIFY =>
+ info += "Certificate Verify";
+
+ STATE_FINISHED =>
+ info += "Finished";
+ }
+ }
+
+ if(ctx.status & CLIENT_AUTH)
+ info += ": Client Auth";
+ if(ctx.status & CERT_REQUEST)
+ info += ": Cert Request";
+ if(ctx.status & CERT_SENT)
+ info += ": Cert Sent";
+ if(ctx.status & CERT_RECEIVED)
+ info += ": Cert Received";
+
+ return info;
+}
+
+reset_client_random(ctx: ref Context)
+{
+ ctx.client_random[0:] = int_encode(ctx.session.connection_time, 4);
+ ctx.client_random[4:] = random->randombuf(Random->NotQuiteRandom, 28);
+}
+
+reset_server_random(ctx: ref Context)
+{
+ ctx.server_random[0:] = int_encode(ctx.session.connection_time, 4);
+ ctx.server_random[4:] = random->randombuf(Random->NotQuiteRandom, 28);
+}
+
+update_handshake_hash(ctx: ref Context, r: ref Record): string
+{
+ err := "";
+
+ ctx.sha_state = keyring->sha1(r.data, len r.data, nil, ctx.sha_state);
+ ctx.md5_state = keyring->md5(r.data, len r.data, nil, ctx.md5_state);
+ if(ctx.sha_state == nil || ctx.md5_state == nil)
+ err = "update handshake hash failed";
+
+ # if(SSL_DEBUG)
+ # log("ssl3: update_handshake_hash\n\tmessage_data =\n\t\t" + bastr(r.data) + "\n");
+
+ return err;
+}
+
+# Note:
+# this depends on the record protocol
+protocol_read(ctx: ref Context): (ref Record, ref Protocol, string)
+{
+ p: ref Protocol;
+ r: ref Record;
+ e: string;
+
+ vers := SSL_VERSION_2_0;
+ if(ctx.status & SSL3_RECORD)
+ vers = SSL_VERSION_3_0;
+ if(ctx.status & USE_DEVSSL)
+ (r, e) = devssl_read(ctx);
+ else
+ (r, e) = record_read(ctx);
+ if(e != "")
+ return (nil, nil, e);
+
+ (p, e) = Protocol.decode(r, ctx);
+ if(e != "")
+ return (r, nil, e);
+
+ return (r, p, nil);
+}
+
+# Alert messages with a level of fatal result in the immediate
+# termination of the connection and zero out session.
+
+do_alert(a: ref Alert, ctx: ref Context)
+{
+ case a.level {
+ SSL_FATAL =>
+
+ case a.description {
+ SSL_UNEXPECTED_MESSAGE =>
+
+ # should never be observed in communication
+ # between proper implementations.
+ break;
+
+ SSL_HANDSHAKE_FAILURE =>
+
+ # unable to negotiate an acceptable set of security
+ # parameters given the options available.
+ break;
+
+ * =>
+ break;
+ }
+
+ ctx.session.session_id = nil;
+ ctx.state = STATE_EXIT;
+
+ SSL_WARNING =>
+
+ case a.description {
+ SSL_CLOSE_NOTIFY =>
+
+ if(SSL_DEBUG)
+ log("ssl3: do_alert SSL_WARNING:SSL_CLOSE_NOTIFY\n");
+ # notifies the recipient that the sender will not
+ # send any more messages on this connection.
+
+ ctx.state = STATE_EXIT;
+ fatal(SSL_CLOSE_NOTIFY, "ssl3: response close notify", ctx);
+
+ SSL_NO_CERTIFICATE =>
+
+ # A no_certificate alert message may be sent in
+ # response to a certification request if no
+ # appropriate certificate is available.
+
+ if(ctx.state == STATE_CLIENT_CERTIFICATE) {
+ hm := ref Handshake.Certificate(ctx.local_info.certs);
+ handshake_enque(hm, ctx);
+ }
+
+ SSL_BAD_CERTIFICATE or
+
+ # A certificate was corrupt, contained signatures
+ # that did not verify correctly, etc.
+
+ SSL_UNSUPPORTED_CERTIFICATE or
+
+ # A certificate was of an unsupported type.
+
+ SSL_CERTIFICATE_REVOKED or
+
+ # A certificate was revoked by its signer.
+
+ SSL_CERTIFICATE_EXPIRED or
+
+ # A certificate has expired or is not currently
+ # valid.
+
+ SSL_CERTIFICATE_UNKNOWN =>
+
+ # Some other (unspecified) issue arose in
+ # processing the certificate, rendering it
+ # unacceptable.
+ break;
+
+ * =>
+ ctx.session.session_id = nil;
+ fatal(SSL_ILLEGAL_PARAMETER, "ssl3: unknown alert description", ctx);
+ }
+
+ * =>
+ ctx.session.session_id = nil;
+ fatal(SSL_ILLEGAL_PARAMETER, "ssl3: unknown alert level received", ctx);
+ }
+}
+
+# notify the receiving party that subsequent records will
+# be protected under the just-negotiated CipherSpec and keys.
+
+do_change_cipher_spec(ctx: ref Context)
+{
+ # calculate and set new keys
+ if(!(ctx.status & IN_READY)) {
+ e := set_in_queue(ctx);
+ if(e != "") {
+ fatal(SSL_CLOSE_NOTIFY, "do_change_cipher_spec: setup new cipher failed", ctx);
+ return;
+ }
+ ctx.status |= IN_READY;
+
+ if(SSL_DEBUG)
+ log("ssl3: set in cipher done\n");
+ }
+
+ ctx.state = STATE_FINISHED;
+}
+
+
+# process and advance handshake messages, update internal stack and switch to next
+# expected state(s).
+
+do_handshake(handshake: ref Handshake, ctx: ref Context) : string
+{
+ e := "";
+
+ pick h := handshake {
+ HelloRequest =>
+ if(!(ctx.status & CLIENT_SIDE) || ctx.state != STATE_HELLO_REQUEST) {
+ e = "HelloRequest";
+ break;
+ }
+ do_hello_request(ctx);
+
+ ClientHello =>
+ if((ctx.status & CLIENT_SIDE) || ctx.state != STATE_CLIENT_HELLO) {
+ e = "ClientHello";
+ break;
+ }
+ do_client_hello(h, ctx);
+
+ ServerHello =>
+ if(!(ctx.status & CLIENT_SIDE) || ctx.state != STATE_SERVER_HELLO) {
+ e = "ServerHello";
+ break;
+ }
+ do_server_hello(h, ctx);
+
+ ClientKeyExchange =>
+ if((ctx.status & CLIENT_SIDE) || ctx.state != STATE_CLIENT_KEY_EXCHANGE) {
+ e = "ClientKeyExchange";
+ break;
+ }
+ do_client_keyex(h, ctx);
+
+ ServerKeyExchange =>
+ if(!(ctx.status & CLIENT_SIDE) ||
+ (ctx.state != STATE_SERVER_KEY_EXCHANGE && ctx.state != STATE_SERVER_HELLO_DONE)) {
+ e = "ServerKeyExchange";
+ break;
+ }
+ do_server_keyex(h, ctx);
+
+ ServerHelloDone =>
+ # diff from SSLRef, to support variant impl
+ if(!(ctx.status & CLIENT_SIDE) ||
+ (ctx.state != STATE_SERVER_HELLO_DONE && ctx.state != STATE_SERVER_KEY_EXCHANGE)) {
+ e = "ServerHelloDone";
+ break;
+ }
+ do_server_done(ctx);
+
+ Certificate =>
+ if(ctx.status & CLIENT_SIDE) {
+ if(ctx.state != STATE_SERVER_CERTIFICATE) {
+ e = "ServerCertificate";
+ break;
+ }
+ do_server_cert(h, ctx);
+ }
+ else {
+ if(ctx.state != STATE_CLIENT_CERTIFICATE) {
+ e = "ClientCertificate";
+ break;
+ }
+ do_client_cert(h, ctx); # server_side
+ }
+
+ CertificateRequest =>
+ if(!(ctx.status & CLIENT_SIDE) || ctx.state != STATE_SERVER_HELLO_DONE
+ || ctx.state != STATE_SERVER_KEY_EXCHANGE) {
+ e = "CertificateRequest";
+ break;
+ }
+ do_cert_request(h, ctx);
+
+ CertificateVerify =>
+ if((ctx.status & CLIENT_SIDE) || ctx.state != STATE_CERTIFICATE_VERIFY) {
+ e = "CertificateVerify";
+ break;
+ }
+ do_cert_verify(h, ctx);
+
+ Finished =>
+ if(ctx.status & CLIENT_SIDE) {
+ if(ctx.state != STATE_FINISHED) {
+ e = "ClientFinished";
+ break;
+ }
+ do_finished(SSL_CLIENT_SENDER, ctx);
+ }
+ else {
+ if(ctx.state != STATE_FINISHED) {
+ e = "ServerFinished";
+ break;
+ }
+ do_finished(SSL_SERVER_SENDER, ctx);
+ }
+
+ * =>
+ e = "unknown handshake message";
+ }
+
+ if(e != nil)
+ e = "do_handshake: " + e;
+
+ return e;
+}
+
+# [client side]
+# The hello request message may be sent by server at any time, but will be ignored by
+# the client if the handshake protocol is already underway. It is simple notification
+# that the client should begin the negotiation process anew by sending a client hello
+# message.
+
+do_hello_request(ctx: ref Context)
+{
+ # start from new handshake digest state
+ ctx.sha_state = ctx.md5_state = nil;
+
+ # Note:
+ # sending ctx.local_info.suites instead of ctx.session.suite,
+ # if session is resumable by server, ctx.session.suite will be used.
+ handshake_enque(
+ ref Handshake.ClientHello(
+ ctx.session.version,
+ ctx.client_random,
+ ctx.session.session_id,
+ ctx.local_info.suites,
+ ctx.local_info.comprs
+ ),
+ ctx
+ );
+
+ ctx.state = STATE_SERVER_HELLO;
+}
+
+# [client side]
+# Processes the received server hello handshake message and determines if the session
+# is resumable. (The client sends a client hello using the session id of the session
+# to be resumed. The server then checks its session cache for a match. If a match is
+# FOUND, and the server is WILLING to re-establish the connection under the specified
+# session state, it will send a server hello with the SAME session id value.) If the
+# session is resumed, at this point both client and server must send change cipher
+# spec messages. If the session is not resumable, the client and server perform
+# a full handshake. (On the server side, if a session id match is not found, the
+# server generates a new session id or if the server is not willing to resume, the
+# server uses a null session id).
+
+do_server_hello(hm: ref Handshake.ServerHello, ctx: ref Context)
+{
+ # trying to resume
+ if(bytes_cmp(ctx.session.session_id, hm.session_id) == 0) {
+
+ if(SSL_DEBUG)
+ log("ssl3: session resumed\n");
+
+ ctx.status |= SESSION_RESUMABLE;
+ # avoid version attack
+ if(ctx.session.version[0] != hm.version[0] ||
+ ctx.session.version[1] != hm.version[1]) {
+ fatal(SSL_CLOSE_NOTIFY, "do_server_hello: version mismatch", ctx);
+ return;
+ }
+
+ ctx.server_random = hm.random;
+
+ # uses the retrieved session suite by server (should be same by client)
+ (ciph, keyx, sign, e)
+ := suite_to_spec(hm.suite, SSL3_Suites);
+ if(e != nil) {
+ fatal(SSL_UNEXPECTED_MESSAGE, "server hello: suite not found", ctx);
+ return;
+ }
+ ctx.sel_ciph = ciph;
+ ctx.sel_keyx = keyx;
+ ctx.sel_sign = sign;
+ ctx.sel_cmpr = int ctx.session.compression; # not supported by ssl3 yet
+
+ # calculate keys
+ (ctx.cw_mac, ctx.sw_mac, ctx.cw_key, ctx.sw_key, ctx.cw_IV, ctx.sw_IV)
+ = calc_keys(ctx.sel_ciph, ctx.session.master_secret,
+ ctx.client_random, ctx.server_random);
+
+
+ ctx.state = STATE_CHANGE_CIPHER_SPEC;
+ }
+ else {
+ ctx.status &= ~SESSION_RESUMABLE;
+
+ # On the server side, if a session id match is not found, the
+ # server generates a new session id or if the server is not willing
+ # to resume, the server uses an empty session id and cannot be
+ # cached by both client and server.
+
+ ctx.session.session_id = hm.session_id;
+ ctx.session.version = hm.version;
+ ctx.server_random = hm.random;
+
+ if(SSL_DEBUG)
+ log("ssl3: do_server_hello:\n\tselected cipher suite =\n\t\t"
+ + cipher_suite_info(hm.suite, SSL3_Suites) + "\n");
+
+ (ciph, keyx, sign, e) := suite_to_spec(hm.suite, SSL3_Suites);
+ if(e != nil) {
+ fatal(SSL_UNEXPECTED_MESSAGE, "server hello: suite not found", ctx);
+ return;
+ }
+
+ ctx.sel_ciph = ciph;
+ ctx.sel_keyx = keyx;
+ ctx.sel_sign = sign;
+ ctx.sel_cmpr = int hm.compression; # not supported by ssl3 yet
+
+ # next state is determined by selected key exchange and signature methods
+ # the ctx.sel_keyx and ctx.sel_sign are completed by the following handshake
+ # Certificate and/or ServerKeyExchange
+
+ if(tagof ctx.sel_keyx == tagof KeyExAlg.DH &&
+ tagof ctx.sel_sign == tagof SigAlg.anon)
+ ctx.state = STATE_SERVER_KEY_EXCHANGE;
+ else
+ ctx.state = STATE_SERVER_CERTIFICATE;
+ }
+}
+
+# [client side]
+# Processes the received server key exchange message. The server key exchange message
+# is sent by the server if it has no certificate, has a certificate only used for
+# signing, or FORTEZZA KEA key exchange is used.
+
+do_server_keyex(hm: ref Handshake.ServerKeyExchange, ctx: ref Context)
+{
+ # install exchange keys sent by server, this may require public key
+ # retrieved from certificate sent by Handshake.Certificate message
+
+ (err, i) := install_server_xkey(hm.xkey, ctx.sel_keyx);
+ if(err == "")
+ err = verify_server_xkey(ctx.client_random, ctx.server_random, hm.xkey, i, ctx.sel_sign);
+
+ if(err == "")
+ ctx.state = STATE_SERVER_HELLO_DONE;
+ else
+ fatal(SSL_HANDSHAKE_FAILURE, "do_server_keyex: " + err, ctx);
+}
+
+# [client side]
+# Processes the received server hello done message by verifying that the server
+# provided a valid certificate if required and checking that the server hello
+# parameters are acceptable.
+
+do_server_done(ctx: ref Context)
+{
+ # On client side, optionally send client cert chain if client_auth
+ # is required by the server. The server may drop the connection,
+ # if it does not receive client certificate in the following
+ # Handshake.ClientCertificate message
+ if(ctx.status & CLIENT_AUTH) {
+ if(ctx.local_info.certs != nil) {
+ handshake_enque(
+ ref Handshake.Certificate(ctx.local_info.certs),
+ ctx
+ );
+ ctx.status |= CERT_SENT;
+ }
+ else {
+ alert_enque(
+ ref Alert(SSL_WARNING, SSL_NO_CERTIFICATE),
+ ctx
+ );
+ }
+ }
+
+ # calculate premaster secrect, client exchange keys and update ref KeyExAlg
+ # of the client side
+ (x, pm, e) := calc_client_xkey(ctx.sel_keyx);
+ if(e != "") {
+ fatal(SSL_HANDSHAKE_FAILURE, e, ctx);
+ return;
+ }
+ handshake_enque(ref Handshake.ClientKeyExchange(x), ctx);
+
+ ms := calc_master_secret(pm, ctx.client_random, ctx.server_random);
+ if(ms == nil) {
+ fatal(SSL_HANDSHAKE_FAILURE, "server hello done: calc master secret failed", ctx);
+ return;
+ }
+ # ctx.premaster_secret = pm;
+ ctx.session.master_secret = ms;
+
+ # sending certificate verifiy message if the client auth is required
+ # and client certificate has been sent,
+ if(ctx.status & CERT_SENT) {
+ sig : array of byte;
+ (md5_hash, sha_hash)
+ := calc_finished(nil, ctx.session.master_secret, ctx.sha_state, ctx.md5_state);
+ # check type of client cert being sent
+ pick sk := ctx.local_info.sk {
+ RSA =>
+ hashes := array [36] of byte;
+ hashes[0:] = md5_hash;
+ hashes[16:] = sha_hash;
+ #(e, sig) = pkcs->rsa_sign(hashes, sk, PKCS->MD5_WithRSAEncryption);
+ DSS =>
+ #(e, sig) = pkcs->dss_sign(sha_hash, sk);
+ * =>
+ e = "unknown sign";
+ }
+ if(e != "") {
+ fatal(SSL_HANDSHAKE_FAILURE, "server hello done: sign cert verify failed", ctx);
+ return;
+ }
+ handshake_enque(ref Handshake.CertificateVerify(sig), ctx);
+ }
+
+ ccs_enque(ref ChangeCipherSpec(1), ctx);
+ (ctx.cw_mac, ctx.sw_mac, ctx.cw_key, ctx.sw_key, ctx.cw_IV, ctx.sw_IV)
+ = calc_keys(ctx.sel_ciph, ctx.session.master_secret,
+ ctx.client_random, ctx.server_random);
+
+ # set cipher on write channel
+ e = set_out_queue(ctx);
+ if(e != nil) {
+ fatal(SSL_HANDSHAKE_FAILURE, "do_server_done: " + e, ctx);
+ return;
+ }
+ ctx.status |= OUT_READY;
+
+ if(SSL_DEBUG)
+ log("ssl3: set out cipher done\n");
+ (mh, sh) := calc_finished(SSL_CLIENT_SENDER, ctx.session.master_secret,
+ ctx.sha_state, ctx.md5_state);
+# sending out the Finished msg causes MS https servers to hangup
+#sys->print("RETURNING FROM DO_SERVER_DONE\n");
+#return;
+ handshake_enque(ref Handshake.Finished(mh, sh), ctx);
+
+ ctx.state = STATE_CHANGE_CIPHER_SPEC;
+}
+
+# [client side]
+# Process the received certificate message.
+# Note:
+# according to current US export law, RSA moduli larger than 512 bits
+# may not be used for key exchange in software exported from US. With
+# this message, larger RSA keys may be used as signature only
+# certificates to sign temporary shorter RSA keys for key exchange.
+
+do_server_cert(hm: ref Handshake.Certificate, ctx: ref Context)
+{
+ if(hm.cert_list == nil) {
+ fatal(SSL_UNEXPECTED_MESSAGE, "nil peer certificate", ctx);
+ return;
+ }
+
+ # server's certificate is the last one in the chain (reverse required)
+ cl := hm.cert_list;
+ ctx.session.peer_certs = nil;
+ while(cl != nil) {
+ ctx.session.peer_certs = hd cl::ctx.session.peer_certs;
+ cl = tl cl;
+ }
+
+ # TODO: verify certificate chain
+ # check if in the acceptable dnlist
+ # ctx.sel_keyx.peer_pk = x509->verify_chain(ctx.session.peer_certs);
+ if(SSL_DEBUG)
+ log("ssl3: number certificates got: " + string len ctx.session.peer_certs);
+ peer_cert := hd ctx.session.peer_certs;
+ (e, signed) := x509->Signed.decode(peer_cert);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("ss3: server certificate: " + e);
+ fatal(SSL_HANDSHAKE_FAILURE, "server certificate: " + e, ctx);
+ return;
+ }
+
+ srv_cert: ref Certificate;
+ (e, srv_cert) = x509->Certificate.decode(signed.tobe_signed);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("ss3: server certificate: " + e);
+ fatal(SSL_HANDSHAKE_FAILURE, "server certificate: " + e, ctx);
+ return;
+ }
+ if(SSL_DEBUG)
+ log("ssl3: " + srv_cert.tostring());
+
+ # extract and determine byte of user certificate
+ id: int;
+ peer_pk: ref X509->PublicKey;
+ (e, id, peer_pk) = srv_cert.subject_pkinfo.getPublicKey();
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("ss3: server certificate: " + e);
+ fatal(SSL_HANDSHAKE_FAILURE, "server certificate:" + e, ctx);
+ return;
+ }
+
+ pick key := peer_pk {
+ RSA =>
+ # TODO: to allow checking X509v3 KeyUsage extension
+ if((0 && key.pk.modulus.bits() > 512 && ctx.sel_ciph.is_exportable)
+ || id == PKCS->id_pkcs_md2WithRSAEncryption
+ || id == PKCS->id_pkcs_md4WithRSAEncryption
+ || id == PKCS->id_pkcs_md5WithRSAEncryption) {
+ pick sign := ctx.sel_sign {
+ anon =>
+ break;
+ RSA =>
+ break;
+ * =>
+ # error
+ }
+ if(ctx.local_info.sk == nil)
+ ctx.sel_sign = ref SigAlg.RSA(nil, key.pk);
+ else {
+ pick mysk := ctx.local_info.sk {
+ RSA =>
+ ctx.sel_sign = ref SigAlg.RSA(mysk.sk, key.pk);
+ * =>
+ ctx.sel_sign = ref SigAlg.RSA(nil, key.pk);
+ }
+ }
+ # key exchange may be tmp RSA, emhemeral DH depending on cipher suite
+ ctx.state = STATE_SERVER_KEY_EXCHANGE;
+ }
+ # TODO: allow id == PKCS->id_rsa
+ else if(id == PKCS->id_pkcs_rsaEncryption) {
+ pick sign := ctx.sel_sign {
+ anon =>
+ break;
+ * =>
+ # error
+ }
+ ctx.sel_sign = ref SigAlg.anon();
+ pick keyx := ctx.sel_keyx {
+ RSA =>
+ keyx.peer_pk = key.pk;
+ * =>
+ # error
+ }
+ ctx.state = STATE_SERVER_HELLO_DONE;
+ }
+ else {
+ # error
+ }
+ DSS =>
+ pick sign := ctx.sel_sign {
+ DSS =>
+ sign.peer_pk = key.pk;
+ break;
+ * =>
+ # error
+ }
+ # should be key exchagne such as emhemeral DH
+ ctx.state = STATE_SERVER_KEY_EXCHANGE;
+ DH =>
+ # fixed DH signed in certificate either by RSA or DSS???
+ pick keyx := ctx.sel_keyx {
+ DH =>
+ keyx.peer_pk = key.pk;
+ * =>
+ # error
+ }
+ ctx.state = STATE_SERVER_KEY_EXCHANGE;
+ }
+
+ if(e != nil) {
+ fatal(SSL_HANDSHAKE_FAILURE, "do_server_cert: " + e, ctx);
+ return;
+ }
+}
+
+# [client side]
+# Processes certificate request message. A non-anonymous server can optionally
+# request a certificate from the client, if appropriate for the selected cipher
+# suite It is a fatal handshake failure alert for an anonymous server to
+# request client identification.
+
+# TODO: use another module to do x509 certs, lookup and matching rules
+
+do_cert_request(hm: ref Handshake.CertificateRequest, ctx: ref Context)
+{
+ found := 0;
+ for(i := 0; i < len hm.cert_types; i++) {
+ if(ctx.local_info.root_type == int hm.cert_types[i]) {
+ found = 1;
+ break;
+ }
+ }
+ if(!found) {
+ fatal(SSL_HANDSHAKE_FAILURE, "do_cert_request: no required type of cert", ctx);
+ return;
+ }
+ if(dn_cmp(ctx.local_info.dns, hm.dn_list) < 0) {
+ fatal(SSL_HANDSHAKE_FAILURE, "do_cert_request: no required dn", ctx);
+ return;
+ }
+ if(ctx.session.peer_certs == nil) {
+ fatal(SSL_NO_CERTIFICATE, "certificate request: no peer certificates", ctx);
+ return;
+ }
+
+ ctx.status |= CLIENT_AUTH;
+}
+
+dn_cmp(a, b: list of array of byte): int
+{
+ return -1;
+}
+
+# [server side]
+# Process client hello message.
+
+do_client_hello(hm: ref Handshake.ClientHello, ctx: ref Context)
+{
+ sndm : ref Handshake;
+ e : string;
+
+ if(hm.version[0] != SSL_VERSION_3_0[0] || hm.version[1] != SSL_VERSION_3_0[1]) {
+ fatal(SSL_UNEXPECTED_MESSAGE, "client hello: version mismatch", ctx);
+ return;
+ }
+ # else SSL_VERSION_2_0
+
+ if(hm.session_id != nil) { # trying to resume
+ if(ctx.status & SESSION_RESUMABLE) {
+ s := sslsession->get_session_byid(hm.session_id);
+ if(s == nil) {
+ fatal(SSL_UNEXPECTED_MESSAGE, "client hello: retrieve nil session", ctx);
+ return;
+ }
+
+ if(s.version[0] != hm.version[0] || s.version[1] != hm.version[1]) {
+ # avoid version attack
+ fatal(SSL_UNEXPECTED_MESSAGE, "client hello: protocol mismatch", ctx);
+ return;
+ }
+
+ reset_server_random(ctx);
+ ctx.client_random = hm.random;
+
+ sndm = ref Handshake.ServerHello(s.version, ctx.server_random,
+ s.session_id, s.suite, s.compression);
+ handshake_enque(sndm, ctx);
+
+ ccs_enque(ref ChangeCipherSpec(1), ctx);
+ # use existing master_secret, calc keys
+ (ctx.cw_mac, ctx.sw_mac, ctx.cw_key, ctx.sw_key, ctx.cw_IV, ctx.sw_IV)
+ = calc_keys(ctx.sel_ciph, ctx.session.master_secret, ctx.client_random,
+ ctx.server_random);
+ e = set_out_queue(ctx);
+ if(e != nil) {
+ fatal(SSL_CLOSE_NOTIFY, "client hello: setup new cipher failure", ctx);
+ return;
+ }
+ if(SSL_DEBUG)
+ log("do_client_hello: set out cipher done\n");
+
+ (md5_hash, sha_hash) := calc_finished(SSL_SERVER_SENDER,
+ s.master_secret, ctx.sha_state, ctx.md5_state);
+
+ handshake_enque(ref Handshake.Finished(md5_hash, sha_hash), ctx);
+
+ ctx.session = s;
+ ctx.state = STATE_CHANGE_CIPHER_SPEC;
+ return;
+ }
+
+ fatal(SSL_CLOSE_NOTIFY, "client hello: resume session failed", ctx);
+ return;
+ }
+
+ ctx.session.version = hm.version;
+ if(ctx.session.peer != nil) {
+ ctx.session.session_id = random->randombuf(Random->NotQuiteRandom, 32);
+ if(ctx.session.session_id == nil) {
+ fatal(SSL_CLOSE_NOTIFY, "client hello: generate session id failed", ctx);
+ return;
+ }
+ }
+
+ suite := find_cipher_suite(hm.suites, ctx.local_info.suites);
+ if(suite != nil) {
+ fatal(SSL_HANDSHAKE_FAILURE, "client hello: find cipher suite failed", ctx);
+ return;
+ }
+
+ (ctx.sel_ciph, ctx.sel_keyx, ctx.sel_sign, e) = suite_to_spec(suite, SSL3_Suites);
+ if(e != nil) {
+ fatal(SSL_HANDSHAKE_FAILURE, "client hello: find cipher suite failed" + e, ctx);
+ return;
+ }
+
+ # not supported by ssl3 yet
+ ctx.sel_cmpr = int hm.compressions[0];
+ ctx.client_random = hm.random;
+ ctx.sha_state = nil;
+ ctx.md5_state = nil;
+
+ sndm = ref Handshake.ServerHello(ctx.session.version, ctx.server_random,
+ ctx.session.session_id, ctx.session.suite, ctx.session.compression);
+ handshake_enque(sndm, ctx);
+
+ # set up keys based on algorithms
+
+ if(tagof ctx.sel_keyx != tagof KeyExAlg.DH) {
+ if(ctx.local_info.certs == nil || ctx.local_info.sk == nil) {
+ fatal(SSL_HANDSHAKE_FAILURE, "client hello: no local cert or key", ctx);
+ return;
+ }
+
+ sndm = ref Handshake.Certificate(ctx.local_info.certs);
+ handshake_enque(sndm, ctx);
+ }
+
+ if(tagof ctx.sel_keyx != tagof KeyExAlg.RSA ||
+ tagof ctx.sel_sign != tagof SigAlg.anon) {
+ params, signed_params, xkey: array of byte;
+ (params, e) = calc_server_xkey(ctx.sel_keyx);
+ if(e == "")
+ (signed_params, e) = sign_server_xkey(ctx.sel_sign, params,
+ ctx.client_random, ctx.server_random);
+ if(e != "")
+
+ n := len params + 2 + len signed_params;
+ xkey = array [n] of byte;
+ xkey[0:] = params;
+ xkey[len params:] = int_encode(len signed_params, 2);
+ xkey[len params+2:] = signed_params;
+ handshake_enque(ref Handshake.ServerKeyExchange(xkey), ctx);
+ }
+
+ if(ctx.status & CLIENT_AUTH) {
+ sndm = ref Handshake.CertificateRequest(ctx.local_info.types, ctx.local_info.dns);
+ handshake_enque(sndm, ctx);
+
+ ctx.status |= CERT_REQUEST;
+ ctx.state = STATE_CLIENT_CERTIFICATE;
+ }
+ else
+ ctx.state = STATE_CLIENT_KEY_EXCHANGE;
+
+ handshake_enque(ref Handshake.ServerHelloDone(), ctx);
+}
+
+# [server side]
+# Process the received client key exchange message.
+
+do_client_keyex(hm: ref Handshake.ClientKeyExchange, ctx: ref Context)
+{
+ (premaster_secret, err) := install_client_xkey(hm.xkey, ctx.sel_keyx);
+ if(err != "") {
+ fatal(SSL_HANDSHAKE_FAILURE, err, ctx);
+ return;
+ }
+
+ ctx.session.master_secret = calc_master_secret(premaster_secret,
+ ctx.client_random, ctx.server_random);
+
+ if(ctx.status & CERT_RECEIVED)
+ ctx.state = STATE_CERTIFICATE_VERIFY;
+ else
+ ctx.state = STATE_CHANGE_CIPHER_SPEC;
+}
+
+# [server side]
+# Process the received certificate message from client.
+
+do_client_cert(hm: ref Handshake.Certificate, ctx: ref Context)
+{
+ ctx.session.peer_certs = hm.cert_list;
+
+ # verify cert chain and determine the type of cert
+ # ctx.peer_info.sk = x509->verify_chain(ctx.session.peer_certs);
+ # if(ctx.peer_info.key == nil) {
+ # fatal(SSL_HANDSHAKE_FAILURE, "client certificate: cert verify failed", ctx);
+ # return;
+ # }
+
+ ctx.status |= CERT_RECEIVED;
+
+ ctx.state = STATE_CLIENT_KEY_EXCHANGE;
+}
+
+# [server side]
+# Process the received certificate verify message from client.
+
+do_cert_verify(hm: ref Handshake.CertificateVerify, ctx: ref Context)
+{
+ if(ctx.status & CERT_RECEIVED) {
+ # exp : array of byte;
+ (md5_hash, sha_hash)
+ := calc_finished(nil, ctx.session.master_secret, ctx.sha_state, ctx.md5_state);
+ ok := 0;
+ pick upk := ctx.sel_sign {
+ RSA =>
+ hashes := array [36] of byte;
+ hashes[0:] = md5_hash;
+ hashes[16:] = sha_hash;
+ ok = pkcs->rsa_verify(hashes, hm.signature, upk.peer_pk, PKCS->MD5_WithRSAEncryption);
+ DSS =>
+ ok = pkcs->dss_verify(sha_hash, hm.signature, upk.peer_pk);
+ }
+
+ if(!ok) {
+ fatal(SSL_HANDSHAKE_FAILURE, "do_cert_verify: client auth failed", ctx);
+ return;
+ }
+ }
+ else {
+ alert_enque(ref Alert(SSL_WARNING, SSL_NO_CERTIFICATE), ctx);
+ return;
+ }
+
+ ctx.state = STATE_CHANGE_CIPHER_SPEC;
+}
+
+# [client or server side]
+# Process the received finished message either from client or server.
+
+do_finished(sender: array of byte, ctx: ref Context)
+{
+ # setup write_cipher if not yet
+ if(!(ctx.status & OUT_READY)) {
+ ccs_enque(ref ChangeCipherSpec(1), ctx);
+ e := set_out_queue(ctx);
+ if(e != nil) {
+ fatal(SSL_CLOSE_NOTIFY, "do_finished: setup new cipher failed", ctx);
+ return;
+ }
+ ctx.status |= OUT_READY;
+
+ if(SSL_DEBUG)
+ log("ssl3: set out cipher done\n");
+
+ (md5_hash, sha_hash) := calc_finished(sender, ctx.session.master_secret,
+ ctx.sha_state, ctx.md5_state);
+ handshake_enque(ref Handshake.Finished(md5_hash, sha_hash), ctx);
+ }
+
+ ctx.state = STATE_EXIT; # normal
+
+ # clean read queue
+ ctx.in_queue.fragment = 0;
+
+ sslsession->add_session(ctx.session);
+
+ if(SSL_DEBUG)
+ log("ssl3: add session to session database done\n");
+}
+
+install_client_xkey(a: array of byte, keyx: ref KeyExAlg): (array of byte, string)
+{
+ pmaster, x : array of byte;
+ err := "";
+ pick kx := keyx {
+ DH =>
+ i := 0;
+ (kx.peer_pk, i) = dh_params_decode(a);
+ if(kx.peer_pk != nil)
+ pmaster = pkcs->computeDHAgreedKey(kx.sk.param, kx.sk.sk, kx.peer_pk.pk);
+ else
+ err = "decode dh params failed";
+ RSA =>
+ (err, x) = pkcs->rsa_decrypt(a, kx.sk, 2);
+ if(err != "" || len x != 48) {
+ err = "impl error";
+ }
+ else {
+ if(x[0] != SSL_VERSION_3_0[0] && x[1] != SSL_VERSION_3_0[1])
+ err = "version wrong: possible version attack";
+ else
+ pmaster = x[2:];
+ }
+ FORTEZZA_KEA =>
+ err = "Fortezza unsupported";
+ }
+ return (pmaster, err);
+}
+
+install_server_xkey(a: array of byte, keyx: ref KeyExAlg): (string, int)
+{
+ err := "";
+ i := 0;
+
+ pick kx := keyx {
+ DH =>
+ (kx.peer_pk, i) = dh_params_decode(a);
+ if(kx.peer_pk != nil)
+ kx.peer_params = kx.peer_pk.param;
+ RSA =>
+ peer_tmp: ref RSAParams;
+ (peer_tmp, i, err) = rsa_params_decode(a);
+ if(err == "") {
+ modlen := len peer_tmp.modulus.iptobebytes();
+ kx.peer_pk = ref RSAKey(peer_tmp.modulus, modlen, peer_tmp.exponent);
+ }
+ FORTEZZA_KEA =>
+ return ("Fortezza unsupported", i);
+ }
+
+ return (err, i);
+}
+
+verify_server_xkey(crand, srand: array of byte, a: array of byte, i : int, sign: ref SigAlg)
+ : string
+{
+ pick sg := sign {
+ anon =>
+ RSA =>
+ lb := a[0:i]::crand::srand::nil;
+ (exp, nil, nil) := md5_sha_hash(lb, nil, nil);
+ ok := pkcs->rsa_verify(exp, a[i+2:], sg.peer_pk, PKCS->MD5_WithRSAEncryption);
+ if(!ok)
+ return "RSA sigature verification failed";
+ DSS =>
+ lb := a[0:i]::crand::srand::nil;
+ (exp, nil) := sha_hash(lb, nil);
+ ok := pkcs->dss_verify(exp, a[i+2:], sg.peer_pk);
+ if(!ok)
+ return "DSS sigature verification failed";
+ }
+
+ return "";
+}
+
+calc_client_xkey(keyx: ref KeyExAlg): (array of byte, array of byte, string)
+{
+ pm, x : array of byte;
+ err := "";
+ pick kx := keyx {
+ DH =>
+ # generate our own DH keys based on DH params of peer side
+ (kx.sk, kx.exch_pk) = pkcs->setupDHAgreement(kx.peer_params);
+ # TODO: need check type of client cert if(!ctx.status & CLIENT_AUTH)
+ # for implicit case
+ (x, err) = dh_exchpub_encode(kx.exch_pk);
+ pm = pkcs->computeDHAgreedKey(kx.sk.param, kx.sk.sk, kx.peer_pk.pk);
+ RSA =>
+ pm = array [48] of byte;
+ pm[0:] = SSL_VERSION_3_0; # against version attack
+ pm[2:] = random->randombuf(Random->NotQuiteRandom, 46);
+ (err, x) = pkcs->rsa_encrypt(pm, kx.peer_pk, 2);
+ FORTEZZA_KEA =>
+ err = "Fortezza unsupported";
+ }
+ if(SSL_DEBUG)
+ log("ssl3: calc_client_xkey: " + bastr(x));
+ return (x, pm, err);
+}
+
+calc_server_xkey(keyx: ref KeyExAlg): (array of byte, string)
+{
+ params: array of byte;
+ err: string;
+ pick kx := keyx {
+ DH =>
+ (kx.sk, kx.exch_pk) = pkcs->setupDHAgreement(kx.params);
+ (params, err) = dh_params_encode(kx.exch_pk);
+ RSA =>
+ tmp := ref RSAParams(kx.export_key.modulus, kx.export_key.exponent);
+ (params, err) = rsa_params_encode(tmp);
+
+ FORTEZZA_KEA =>
+ err = "Fortezza unsupported";
+ }
+ return (params, err);
+}
+
+sign_server_xkey(sign: ref SigAlg, params, cr, sr: array of byte): (array of byte, string)
+{
+ signed_params: array of byte;
+ err: string;
+ pick sg := sign {
+ anon =>
+ RSA =>
+ lb := cr::sr::params::nil;
+ (hashes, nil, nil) := md5_sha_hash(lb, nil, nil);
+ (err, signed_params) = pkcs->rsa_sign(hashes, sg.sk, PKCS->MD5_WithRSAEncryption);
+ DSS =>
+ lb := cr::sr::params::nil;
+ (hashes, nil) := sha_hash(lb, nil);
+ (err, signed_params) = pkcs->dss_sign(hashes, sg.sk);
+ }
+ return (signed_params, err);
+}
+
+# ssl encoding of DH exchange public key
+
+dh_exchpub_encode(dh: ref DHPublicKey): (array of byte, string)
+{
+ if(dh != nil) {
+ yb := dh.pk.iptobebytes();
+ if(yb != nil) {
+ n := 2 + len yb;
+ a := array [n] of byte;
+ i := 0;
+ a[i:] = int_encode(len yb, 2);
+ i += 2;
+ a[i:] = yb;
+ return (a, nil);
+ }
+ }
+ return (nil, "nil dh params");
+}
+
+dh_params_encode(dh: ref DHPublicKey): (array of byte, string)
+{
+ if(dh != nil && dh.param != nil) {
+ pb := dh.param.prime.iptobebytes();
+ gb := dh.param.base.iptobebytes();
+ yb := dh.pk.iptobebytes();
+ if(pb != nil && gb != nil && yb != nil) {
+ n := 6 + len pb + len gb + len yb;
+ a := array [n] of byte;
+ i := 0;
+ a[i:] = int_encode(len pb, 2);
+ i += 2;
+ a[i:] = pb;
+ i += len pb;
+ a[i:] = int_encode(len gb, 2);
+ i += 2;
+ a[i:] = gb;
+ i += len gb;
+ a[i:] = int_encode(len yb, 2);
+ i += 2;
+ a[i:] = yb;
+ i += len yb;
+ return (a, nil);
+ }
+ }
+ return (nil, "nil dh public key");
+}
+
+dh_params_decode(a: array of byte): (ref DHPublicKey, int)
+{
+ i := 0;
+ for(;;) {
+ n := int_decode(a[i:i+2]);
+ i += 2;
+ if(i+n > len a)
+ break;
+ p := a[i:i+n];
+ i += n;
+ n = int_decode(a[i:i+2]);
+ i += 2;
+ if(i+n > len a)
+ break;
+ g := a[i:i+n];
+ i += n;
+ n = int_decode(a[i:i+2]);
+ i += 2;
+ if(i+n > len a)
+ break;
+ Ys := a[i:i+n];
+ i += n;
+
+ if(SSL_DEBUG)
+ log("ssl3: dh_params_decode:" + "\n\tp =\n\t\t" + bastr(p)
+ + "\n\tg =\n\t\t" + bastr(g) + "\n\tYs =\n\t\t" + bastr(Ys) + "\n");
+
+ # don't care privateValueLength
+ param := ref DHParams(IPint.bebytestoip(p), IPint.bebytestoip(g), 0);
+ return (ref DHPublicKey(param, IPint.bebytestoip(Ys)), i);
+ }
+ return (nil, i);
+}
+
+rsa_params_encode(rsa_params: ref RSAParams): (array of byte, string)
+{
+ if(rsa_params != nil) {
+ mod := rsa_params.modulus.iptobebytes();
+ exp := rsa_params.exponent.iptobebytes();
+ if(mod != nil || exp != nil) {
+ n := 4 + len mod + len exp;
+ a := array [n] of byte;
+ i := 0;
+ a[i:] = int_encode(len mod, 2);
+ i += 2;
+ a[i:] = mod;
+ i += len mod;
+ a[i:] = int_encode(len exp, 2);
+ i += 2;
+ a[i:] = exp;
+ i += len exp;
+ return (a, nil);
+ }
+ }
+ return (nil, "nil rsa params");
+}
+
+rsa_params_decode(a: array of byte): (ref RSAParams, int, string)
+{
+ i := 0;
+ for(;;) {
+ if(len a < 2)
+ break;
+ n := int_decode(a[i:i+2]);
+ i += 2;
+ if(n < 0 || n + i > len a)
+ break;
+ mod := a[i:i+n];
+ i += n;
+ n = int_decode(a[i:i+2]);
+ i += 2;
+ if(n < 0 || n + i > len a)
+ break;
+ exp := a[i:i+n];
+ i += n;
+ m := i;
+ modulus := IPint.bebytestoip(mod);
+ exponent := IPint.bebytestoip(exp);
+
+ if(SSL_DEBUG)
+ log("ssl3: decode RSA params\n\tmodulus = \n\t\t" + bastr(mod)
+ + "\n\texponent = \n\t\t" + bastr(exp) + "\n");
+
+ if(len a < i+2)
+ break;
+ n = int_decode(a[i:i+2]);
+ i += 2;
+ if(len a != i + n)
+ break;
+ return (ref RSAParams(modulus, exponent), m, nil);
+ }
+ return (nil, i, "encoding error");
+}
+
+# md5_hash MD5(master_secret + pad2 +
+# MD5(handshake_messages + Sender +
+# master_secret + pad1));
+# sha_hash SHA(master_secret + pad2 +
+# SHA(handshake_messages + Sender +
+# master_secret + pad1));
+#
+# handshake_messages All of the data from all handshake messages
+# up to but not including this message. This
+# is only data visible at the handshake layer
+# and does not include record layer headers.
+#
+# sender [4], master_secret [48]
+# pad1 and pad2, 48 bytes for md5, 40 bytes for sha
+
+calc_finished(sender, master_secret: array of byte, sha_state, md5_state: ref DigestState)
+ : (array of byte, array of byte)
+{
+ sha_value := array [Keyring->SHA1dlen] of byte;
+ md5_value := array [Keyring->MD5dlen] of byte;
+ sha_inner := array [Keyring->SHA1dlen] of byte;
+ md5_inner := array [Keyring->MD5dlen] of byte;
+
+ lb := master_secret::SSL_MAC_PAD1[0:48]::nil;
+ if(sender != nil)
+ lb = sender::lb;
+ (md5_inner, nil) = md5_hash(lb, md5_state);
+
+ lb = master_secret::SSL_MAC_PAD1[0:40]::nil;
+ if(sender != nil)
+ lb = sender::lb;
+ (sha_inner, nil) = sha_hash(lb, sha_state);
+
+ (md5_value, nil) = md5_hash(master_secret::SSL_MAC_PAD2[0:48]::md5_inner::nil, nil);
+ (sha_value, nil) = sha_hash(master_secret::SSL_MAC_PAD2[0:40]::sha_inner::nil, nil);
+
+ # if(SSL_DEBUG)
+ # log("ssl3: calc_finished:"
+ # + "\n\tmd5_inner = \n\t\t" + bastr(md5_inner)
+ # + "\n\tsha_inner = \n\t\t" + bastr(sha_inner)
+ # + "\n\tmd5_value = \n\t\t" + bastr(md5_value)
+ # + "\n\tsha_value = \n\t\t" + bastr(sha_value)
+ # + "\n");
+
+ return (md5_value, sha_value);
+}
+
+
+# master_secret =
+# MD5(premaster_secret + SHA('A' + premaster_secret +
+# ClientHello.random + ServerHello.random)) +
+# MD5(premaster_secret + SHA('BB' + premaster_secret +
+# ClientHello.random + ServerHello.random)) +
+# MD5(premaster_secret + SHA('CCC' + premaster_secret +
+# ClientHello.random + ServerHello.random));
+
+calc_master_secret(pm, cr, sr: array of byte): array of byte
+{
+ ms := array [48] of byte;
+ sha_value := array [Keyring->SHA1dlen] of byte;
+ leader := array [3] of byte;
+
+ j := 0;
+ lb := pm::cr::sr::nil;
+ for(i := 1; i <= 3; i++) {
+ leader[0] = leader[1] = leader[2] = byte (16r40 + i);
+ (sha_value, nil) = sha_hash(leader[0:i]::lb, nil);
+ (ms[j:], nil) = md5_hash(pm::sha_value::nil, nil);
+ j += 16; # Keyring->MD5dlen
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: calc_master_secret:\n\tmaster_secret = \n\t\t" + bastr(ms) + "\n");
+
+ return ms;
+}
+
+
+# key_block =
+# MD5(master_secret + SHA(`A' + master_secret +
+# ServerHello.random + ClientHello.random)) +
+# MD5(master_secret + SHA(`BB' + master_secret +
+# ServerHello.random + ClientHello.random)) +
+# MD5(master_secret + SHA(`CCC' + master_secret +
+# ServerHello.random + ClientHello.random)) +
+# [...];
+
+calc_key_material(n: int, ms, cr, sr: array of byte): array of byte
+{
+ key_block := array [n] of byte;
+ sha_value := array [Keyring->SHA1dlen] of byte; # [20]
+ md5_value := array [Keyring->MD5dlen] of byte; # [16]
+ leader := array [10] of byte;
+
+ if(n > 16*(len leader)) {
+ if(SSL_DEBUG)
+ log(sys->sprint("ssl3: calc key block: key size too long [%d]", n));
+ return nil;
+ }
+
+ m := n;
+ i, j, consumed, next : int = 0;
+ lb := ms::sr::cr::nil;
+ for(i = 0; m > 0; i++) {
+ for(j = 0; j <= i; j++)
+ leader[j] = byte (16r41 + i); # 'A', 'BB', 'CCC', etc.
+
+ (sha_value, nil) = sha_hash(leader[0:i+1]::lb, nil);
+ (md5_value, nil) = md5_hash(ms::sha_value::nil, nil);
+
+ consumed = Keyring->MD5dlen;
+ if(m < Keyring->MD5dlen)
+ consumed = m;
+ m -= consumed;
+
+ key_block[next:] = md5_value[0:consumed];
+ next += consumed;
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: calc_key_material:" + "\n\tkey_block = \n\t\t" + bastr(key_block) + "\n");
+
+ return key_block;
+}
+
+# Then the key_block is partitioned as follows.
+#
+# client_write_MAC_secret[CipherSpec.hash_size]
+# server_write_MAC_secret[CipherSpec.hash_size]
+# client_write_key[CipherSpec.key_material]
+# server_write_key[CipherSpec.key_material]
+# client_write_IV[CipherSpec.IV_size] /* non-export ciphers */
+# server_write_IV[CipherSpec.IV_size] /* non-export ciphers */
+#
+# Any extra key_block material is discarded.
+#
+# Exportable encryption algorithms (for which
+# CipherSpec.is_exportable is true) require additional processing as
+# follows to derive their final write keys:
+#
+# final_client_write_key = MD5(client_write_key +
+# ClientHello.random +
+# ServerHello.random);
+# final_server_write_key = MD5(server_write_key +
+# ServerHello.random +
+# ClientHello.random);
+#
+# Exportable encryption algorithms derive their IVs from the random
+# messages:
+#
+# client_write_IV = MD5(ClientHello.random + ServerHello.random);
+# server_write_IV = MD5(ServerHello.random + ClientHello.random);
+
+calc_keys(ciph: ref CipherSpec, ms, cr, sr: array of byte)
+ : (array of byte, array of byte, array of byte, array of byte, array of byte, array of byte)
+{
+ cw_mac, sw_mac, cw_key, sw_key, cw_IV, sw_IV: array of byte;
+
+ n := ciph.key_material + ciph.hash_size;
+ if(ciph.is_exportable == SSL_EXPORT_FALSE)
+ n += ciph.IV_size;
+ n *= 2;
+
+ key_block := calc_key_material(n, ms, cr, sr);
+
+ i := 0;
+ if(ciph.hash_size != 0) {
+ cw_mac = key_block[i:i+ciph.hash_size];
+ i += ciph.hash_size;
+ sw_mac = key_block[i:i+ciph.hash_size];
+ i += ciph.hash_size;
+ }
+
+ if(ciph.is_exportable == SSL_EXPORT_FALSE) {
+ if(ciph.key_material != 0) {
+ cw_key = key_block[i:i+ciph.key_material];
+ i += ciph.key_material;
+ sw_key = key_block[i:i+ciph.key_material];
+ i += ciph.key_material;
+ }
+ if(ciph.IV_size != 0) {
+ cw_IV = key_block[i:i+ciph.IV_size];
+ i += ciph.IV_size;
+ sw_IV = key_block[i:i+ciph.IV_size];
+ i += ciph.IV_size;
+ }
+ }
+ else {
+ if(ciph.key_material != 0) {
+ cw_key = key_block[i:i+ciph.key_material];
+ i += ciph.key_material;
+ sw_key = key_block[i:i+ciph.key_material];
+ i += ciph.key_material;
+ (cw_key, nil) = md5_hash(cw_key::cr::sr::nil, nil);
+ (sw_key, nil) = md5_hash(sw_key::sr::cr::nil, nil);
+ }
+ if(ciph.IV_size != 0) {
+ (cw_IV, nil) = md5_hash(cr::sr::nil, nil);
+ (sw_IV, nil) = md5_hash(sr::cr::nil, nil);
+ }
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: calc_keys:"
+ + "\n\tclient_write_mac = \n\t\t" + bastr(cw_mac)
+ + "\n\tserver_write_mac = \n\t\t" + bastr(sw_mac)
+ + "\n\tclient_write_key = \n\t\t" + bastr(cw_key)
+ + "\n\tserver_write_key = \n\t\t" + bastr(sw_key)
+ + "\n\tclient_write_IV = \n\t\t" + bastr(cw_IV)
+ + "\n\tserver_write_IV = \n\t\t" + bastr(sw_IV) + "\n");
+
+ return (cw_mac, sw_mac, cw_key, sw_key, cw_IV, sw_IV);
+}
+
+#
+# decode protocol message
+#
+Protocol.decode(r: ref Record, ctx: ref Context): (ref Protocol, string)
+{
+ p : ref Protocol;
+
+ case r.content_type {
+ SSL_ALERT =>
+ if(len r.data != 2)
+ return (nil, "alert decode failed");
+
+ p = ref Protocol.pAlert(ref Alert(int r.data[0], int r.data[1]));
+
+ SSL_CHANGE_CIPHER_SPEC =>
+ if(len r.data != 1 || r.data[0] != byte 1)
+ return (nil, "ChangeCipherSpec decode failed");
+
+ p = ref Protocol.pChangeCipherSpec(ref ChangeCipherSpec(1));
+
+ SSL_HANDSHAKE =>
+ (hm, e) := Handshake.decode(r.data);
+ if(e != nil)
+ return (nil, e);
+
+ pick h := hm {
+ Finished =>
+ exp_sender := SSL_CLIENT_SENDER;
+ if(ctx.status & CLIENT_SIDE)
+ exp_sender = SSL_SERVER_SENDER;
+
+ (md5_hash, sha_hash) := calc_finished(exp_sender,
+ ctx.session.master_secret, ctx.sha_state, ctx.md5_state);
+
+ if(SSL_DEBUG)
+ log("ssl3: handshake_decode: finished"
+ + "\n\texpected_md5_hash = \n\t\t" + bastr(md5_hash)
+ + "\n\tgot_md5_hash = \n\t\t" + bastr(h.md5_hash)
+ + "\n\texpected_sha_hash = \n\t\t" + bastr(sha_hash)
+ + "\n\tgot_sha_hash = \n\t\t" + bastr(h.sha_hash) + "\n");
+
+ #if(string md5_hash != string h.md5_hash || string sha_hash != string h.sha_hash)
+ if(bytes_cmp(md5_hash, h.md5_hash) < 0 || bytes_cmp(sha_hash, h.sha_hash) < 0)
+ return (nil, "finished: sender mismatch");
+
+ e = update_handshake_hash(ctx, r);
+ if(e != nil)
+ return (nil, e);
+
+ CertificateVerify =>
+
+ e = update_handshake_hash(ctx, r);
+ if(e != nil)
+ return (nil, e);
+
+ * =>
+ e = update_handshake_hash(ctx, r);
+ if(e != nil)
+ return (nil, e);
+ }
+
+ p = ref Protocol.pHandshake(hm);
+
+ SSL_V2HANDSHAKE =>
+
+ (hm, e) := V2Handshake.decode(r.data);
+ if(e != "")
+ return (nil, e);
+
+ p = ref Protocol.pV2Handshake(hm);
+
+ * =>
+ return (nil, "protocol read: unknown protocol");
+ }
+
+ return (p, nil);
+
+}
+
+
+# encode protocol message and return tuple of data record and error message,
+# may be v2 or v3 record depending on vers.
+
+Protocol.encode(protocol: self ref Protocol, vers: array of byte): (ref Record, string)
+{
+ r: ref Record;
+ e: string;
+
+ pick p := protocol {
+ pAlert =>
+ r = ref Record(
+ SSL_ALERT,
+ vers,
+ array [] of {byte p.alert.level, byte p.alert.description}
+ );
+
+ pChangeCipherSpec =>
+ r = ref Record(
+ SSL_CHANGE_CIPHER_SPEC,
+ vers,
+ array [] of {byte p.change_cipher_spec.value}
+ );
+
+ pHandshake =>
+ data: array of byte;
+ (data, e) = p.handshake.encode();
+ if(e != "")
+ break;
+ r = ref Record(
+ SSL_HANDSHAKE,
+ vers,
+ data
+ );
+
+ pV2Handshake =>
+ data: array of byte;
+ (data, e) = p.handshake.encode();
+ if(e != "")
+ break;
+ r = ref Record(
+ SSL_V2HANDSHAKE,
+ vers,
+ data
+ );
+
+ * =>
+ e = "unknown protocol";
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: protocol encode\n" + protocol.tostring());
+
+ return (r, e);
+}
+
+#
+# protocol message description
+#
+Protocol.tostring(protocol: self ref Protocol): string
+{
+ info : string;
+
+ pick p := protocol {
+ pAlert =>
+ info = "\tAlert\n" + p.alert.tostring();
+
+ pChangeCipherSpec =>
+ info = "\tChangeCipherSpec\n";
+
+ pHandshake =>
+ info = "\tHandshake\n" + p.handshake.tostring();
+
+ pV2Handshake =>
+ info = "\tV2Handshake\n" + p.handshake.tostring();
+
+ pApplicationData =>
+ info = "\tApplicationData\n";
+
+ * =>
+ info = "\tUnknownProtocolType\n";
+ }
+
+ return "ssl3: Protocol:\n" + info;
+}
+
+Handshake.decode(buf: array of byte): (ref Handshake, string)
+{
+ m : ref Handshake;
+ e : string;
+
+ a := buf[4:]; # ignore msg length
+
+ i := 0;
+ case int buf[0] {
+ SSL_HANDSHAKE_HELLO_REQUEST =>
+ m = ref Handshake.HelloRequest();
+
+ SSL_HANDSHAKE_CLIENT_HELLO =>
+ if(len a < 38) {
+ e = "client hello: unexpected message";
+ break;
+ }
+ cv := a[i:i+2];
+ i += 2;
+ rd := a[i:i+32];
+ i += 32;
+ lsi := int a[i++];
+ if(len a < 38 + lsi) {
+ e = "client hello: unexpected message";
+ break;
+ }
+ sid: array of byte;
+ if(lsi != 0) {
+ sid = a[i:i+lsi];
+ i += lsi;
+ }
+ else
+ sid = nil;
+ lcs := int_decode(a[i:i+2]);
+ i += 2;
+ if((lcs & 1) || lcs < 2 || len a < 40 + lsi + lcs) {
+ e = "client hello: unexpected message";
+ break;
+ }
+ cs := array [lcs/2] of byte;
+ cs = a[i:i+lcs];
+ i += lcs;
+ lcm := int a[i++];
+ cr := a[i:i+lcm];
+ i += lcm;
+ # In the interest of forward compatibility, it is
+ # permitted for a client hello message to include
+ # extra data after the compression methods. This
+ # data must be included in the handshake hashes,
+ # but otherwise be ignored.
+ # if(i != len a) {
+ # e = "client hello: unexpected message";
+ # break;
+ # }
+ m = ref Handshake.ClientHello(cv, rd, sid, cs, cr);
+
+ SSL_HANDSHAKE_SERVER_HELLO =>
+ if(len a < 38) {
+ e = "server hello: unexpected message";
+ break;
+ }
+ sv := a[i:i+2];
+ i += 2;
+ rd := a[i:i+32];
+ i += 32;
+ lsi := int a[i++];
+ if(len a < 38 + lsi) {
+ e = "server hello: unexpected message";
+ break;
+ }
+ sid : array of byte;
+ if(lsi != 0) {
+ sid = a[i:i+lsi];
+ i += lsi;
+ }
+ else
+ sid = nil;
+ cs := a[i:i+2];
+ i += 2;
+ cr := a[i++];
+ if(i != len a) {
+ e = "server hello: unexpected message";
+ break;
+ }
+ m = ref Handshake.ServerHello(sv, rd, sid, cs, cr);
+
+ SSL_HANDSHAKE_CERTIFICATE =>
+ n := int_decode(a[i:i+3]);
+ i += 3;
+ if(len a != n + 3) {
+ e = "certificate: unexpected message";
+ break;
+ }
+ cl : list of array of byte;
+ k : int;
+ while(i < n) {
+ k = int_decode(a[i:i+3]);
+ i += 3;
+ if(k < 0 || i + k > len a) {
+ e = "certificate: unexpected message";
+ break;
+ }
+ cl = a[i:i+k] :: cl;
+ i += k;
+ }
+ if(e != nil)
+ break;
+ m = ref Handshake.Certificate(cl);
+
+ SSL_HANDSHAKE_SERVER_KEY_EXCHANGE =>
+
+ m = ref Handshake.ServerKeyExchange(a[i:]);
+
+ SSL_HANDSHAKE_CERTIFICATE_REQUEST =>
+ ln := int_decode(a[i:i+2]);
+ i += 2;
+ types := a[i:i+ln];
+ i += ln;
+ ln = int_decode(a[i:i+2]);
+ i += 2;
+ auths : list of array of byte;
+ for(j := 0; j < ln; j++) {
+ ln = int_decode(a[i:i+2]);
+ i += 2;
+ auths = a[i:i+ln]::auths;
+ i += ln;
+ }
+ m = ref Handshake.CertificateRequest(types, auths);
+
+ SSL_HANDSHAKE_SERVER_HELLO_DONE =>
+ if(len a != 0) {
+ e = "server hello done: unexpected message";
+ break;
+ }
+ m = ref Handshake.ServerHelloDone();
+
+ SSL_HANDSHAKE_CERTIFICATE_VERIFY =>
+ ln := int_decode(a[i:i+2]);
+ i +=2;
+ sig := a[i:];
+ i += ln;
+ if(i != len a) {
+ e = "certificate verify: unexpected message";
+ break;
+ }
+ m = ref Handshake.CertificateVerify(sig);
+
+ SSL_HANDSHAKE_CLIENT_KEY_EXCHANGE =>
+ m = ref Handshake.ClientKeyExchange(a);
+
+ SSL_HANDSHAKE_FINISHED =>
+ if(len a != Keyring->MD5dlen + Keyring->SHA1dlen) { # 16+20
+ e = "finished: unexpected message";
+ break;
+ }
+ md5_hash := a[i:i+Keyring->MD5dlen];
+ i += Keyring->MD5dlen;
+ sha_hash := a[i:i+Keyring->SHA1dlen];
+ i += Keyring->SHA1dlen;
+ if(i != len a) {
+ e = "finished: unexpected message";
+ break;
+ }
+ m = ref Handshake.Finished(md5_hash, sha_hash);
+
+ * =>
+ e = "unknown message";
+ }
+
+ if(e != nil)
+ return (nil, "Handshake decode: " + e);
+
+ return (m, nil);
+}
+
+Handshake.encode(hm: self ref Handshake): (array of byte, string)
+{
+ a : array of byte;
+ n : int;
+ e : string;
+
+ i := 0;
+ pick m := hm {
+ HelloRequest =>
+ a = array [4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_HELLO_REQUEST;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ if(i != 4)
+ e = "hello request: wrong message length";
+
+ ClientHello =>
+ lsi := len m.session_id;
+ lcs := len m.suites;
+ if((lcs &1) || lcs < 2) {
+ e = "client hello: cipher suites is not multiple of 2 bytes";
+ break;
+ }
+ lcm := len m.compressions;
+ n = 38 + lsi + lcs + lcm; # 2+32+1+2+1
+ a = array[n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_CLIENT_HELLO;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ a[i:] = m.version;
+ i += 2;
+ a[i:] = m.random;
+ i += 32;
+ a[i++] = byte lsi;
+ if(lsi != 0) {
+ a[i:] = m.session_id;
+ i += lsi;
+ }
+ a[i:] = int_encode(lcs, 2);
+ i += 2;
+ a[i:] = m.suites; # not nil
+ i += lcs;
+ a[i++] = byte lcm;
+ a[i:] = m.compressions; # not nil
+ i += lcm;
+ if(i != n+4)
+ e = "client hello: wrong message length";
+
+ ServerHello =>
+ lsi := len m.session_id;
+ n = 38 + lsi; # 2+32+1+2+1
+ a = array [n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_SERVER_HELLO;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ a[i:] = m.version;
+ i += 2;
+ a[i:] = m.random;
+ i += 32;
+ a[i++] = byte lsi;
+ if(lsi != 0) {
+ a[i:] = m.session_id;
+ i += lsi;
+ }
+ a[i:] = m.suite; # should be verified, not nil
+ i += 2;
+ a[i++] = m.compression; # should be verified, not nil
+ if(i != n+4)
+ e = "server hello: wrong message length";
+
+ Certificate =>
+ cl := m.cert_list;
+ while(cl != nil) {
+ n += 3 + len hd cl;
+ cl = tl cl;
+ }
+ a = array [n+7] of byte;
+ a[i++] = byte SSL_HANDSHAKE_CERTIFICATE;
+ a[i:] = int_encode(n+3, 3); # length of record
+ i += 3;
+ a[i:] = int_encode(n, 3); # total length of cert chain
+ i += 3;
+ cl = m.cert_list;
+ while(cl != nil) {
+ a[i:] = int_encode(len hd cl, 3);
+ i += 3;
+ a[i:] = hd cl;
+ i += len hd cl;
+ cl = tl cl;
+ }
+ if(i != n+7)
+ e = "certificate: wrong message length";
+
+ ServerKeyExchange =>
+ n = len m.xkey;
+ a = array [n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_SERVER_KEY_EXCHANGE;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ a[i:] = m.xkey;
+ i += len m.xkey;
+ if(i != n+4)
+ e = "server key exchange: wrong message length";
+
+ CertificateRequest =>
+ ntypes := len m.cert_types;
+ nauths := len m.dn_list;
+ n = 1 + ntypes;
+ dl := m.dn_list;
+ while(dl != nil) {
+ n += 2 + len hd dl;
+ dl = tl dl;
+ }
+ n += 2;
+ a = array [n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_CERTIFICATE_REQUEST;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ a[i++] = byte ntypes;
+ a[i:] = m.cert_types;
+ i += ntypes;
+ a[i:] = int_encode(nauths, 2);
+ i += 2;
+ dl = m.dn_list;
+ while(dl != nil) {
+ a[i:] = int_encode(len hd dl, 2);
+ i += 2;
+ a[i:] = hd dl;
+ i += len hd dl;
+ dl = tl dl;
+ }
+ if(i != n+4)
+ e = "certificate request: wrong message length";
+
+ ServerHelloDone =>
+ n = 0;
+ a = array[n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_SERVER_HELLO_DONE;
+ a[i:] = int_encode(0, 3); # message has 0 length
+ i += 3;
+ if(i != n+4)
+ e = "server hello done: wrong message length";
+
+ CertificateVerify =>
+ n = 2 + len m.signature;
+ a = array [n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_CERTIFICATE_VERIFY;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ a[i:] = int_encode(n-2, 2);
+ i += 2;
+ a[i:] = m.signature;
+ i += n-2;
+ if(i != n+4)
+ e = "certificate verify: wrong message length";
+
+ ClientKeyExchange =>
+ n = len m.xkey;
+ a = array [n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_CLIENT_KEY_EXCHANGE;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ a[i:] = m.xkey;
+ i += n;
+ if(i != n+4)
+ e = "client key exchange: wrong message length";
+
+ Finished =>
+ n = len m.md5_hash + len m.sha_hash;
+ a = array [n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_FINISHED;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ a[i:] = m.md5_hash;
+ i += len m.md5_hash;
+ a[i:] = m.sha_hash;
+ i += len m.sha_hash;
+ if(i != n+4)
+ e = "finished: wrong message length";
+
+ * =>
+ e = "unknown message";
+ }
+
+ if(e != nil)
+ return (nil, "Handshake encode: " + e);
+
+ return (a, e);
+}
+
+Handshake.tostring(handshake: self ref Handshake): string
+{
+ info: string;
+
+ pick m := handshake {
+ HelloRequest =>
+ info = "\tHelloRequest\n";
+
+ ClientHello =>
+ info = "\tClientHello\n" +
+ "\tversion = \n\t\t" + bastr(m.version) + "\n" +
+ "\trandom = \n\t\t" + bastr(m.random) + "\n" +
+ "\tsession_id = \n\t\t" + bastr(m.session_id) + "\n" +
+ "\tsuites = \n\t\t" + bastr(m.suites) + "\n" +
+ "\tcomperssion_methods = \n\t\t" + bastr(m.compressions) +"\n";
+
+ ServerHello =>
+ info = "\tServerHello\n" +
+ "\tversion = \n\t\t" + bastr(m.version) + "\n" +
+ "\trandom = \n\t\t" + bastr(m.random) + "\n" +
+ "\tsession_id = \n\t\t" + bastr(m.session_id) + "\n" +
+ "\tsuite = \n\t\t" + bastr(m.suite) + "\n" +
+ "\tcomperssion_method = \n\t\t" + string m.compression +"\n";
+
+ Certificate =>
+ info = "\tCertificate\n" +
+ "\tcert_list = \n\t\t" + lbastr(m.cert_list) + "\n";
+
+ ServerKeyExchange =>
+ info = "\tServerKeyExchange\n" +
+ "\txkey = \n\t\t" + bastr(m.xkey) +"\n";
+
+ CertificateRequest =>
+ info = "\tCertificateRequest\n" +
+ "\tcert_types = \n\t\t" + bastr(m.cert_types) + "\n" +
+ "\tdn_list = \n\t\t" + lbastr(m.dn_list) + "\n";
+
+ ServerHelloDone =>
+ info = "\tServerDone\n";
+
+ CertificateVerify =>
+ info = "\tCertificateVerify\n" +
+ "\tsignature = \n\t\t" + bastr(m.signature) + "\n";
+
+ ClientKeyExchange =>
+ info = "\tClientKeyExchange\n" +
+ "\txkey = \n\t\t" + bastr(m.xkey) +"\n";
+
+ Finished =>
+ info = "\tFinished\n" +
+ "\tmd5_hash = \n\t\t" + bastr(m.md5_hash) + "\n" +
+ "\tsha_hash = \n\t\t" + bastr(m.sha_hash) + "\n";
+ }
+
+ return info;
+}
+
+Alert.tostring(alert: self ref Alert): string
+{
+ info: string;
+
+ case alert.level {
+ SSL_WARNING =>
+ info += "\t\twarning: ";
+
+ SSL_FATAL =>
+ info += "\t\tfatal: ";
+
+ * =>
+ info += sys->sprint("unknown alert level[%d]: ", alert.level);
+ }
+
+ case alert.description {
+ SSL_CLOSE_NOTIFY =>
+ info += "close notify";
+
+ SSL_NO_CERTIFICATE =>
+ info += "no certificate";
+
+ SSL_BAD_CERTIFICATE =>
+ info += "bad certificate";
+
+ SSL_UNSUPPORTED_CERTIFICATE =>
+ info += "unsupported certificate";
+
+ SSL_CERTIFICATE_REVOKED =>
+ info += "certificate revoked";
+
+ SSL_CERTIFICATE_EXPIRED =>
+ info += "certificate expired";
+
+ SSL_CERTIFICATE_UNKNOWN =>
+ info += "certificate unknown";
+
+ SSL_UNEXPECTED_MESSAGE =>
+ info += "unexpected message";
+
+ SSL_BAD_RECORD_MAC =>
+ info += "bad record mac";
+
+ SSL_DECOMPRESSION_FAILURE =>
+ info += "decompression failure";
+
+ SSL_HANDSHAKE_FAILURE =>
+ info += "handshake failure";
+
+ SSL_ILLEGAL_PARAMETER =>
+ info += "illegal parameter";
+
+ * =>
+ info += sys->sprint("unknown alert description[%d]", alert.description);
+ }
+
+ return info;
+}
+
+find_cipher_suite(s, suites: array of byte) : array of byte
+{
+ i, j : int;
+ a, b : array of byte;
+
+ n := len s;
+ if((n & 1) || n < 2)
+ return nil;
+
+ m := len suites;
+ if((m & 1) || m < 2)
+ return nil;
+
+ for(i = 0; i < n; ) {
+ a = s[i:i+2];
+ i += 2;
+ for(j = 0; j < m; ) {
+ b = suites[j:j+2];
+ j += 2;
+ if(a[0] == b[0] && a[1] == b[1])
+ return b;
+ }
+ }
+
+ return nil;
+}
+
+#
+# cipher suites and specs
+#
+suite_to_spec(cs: array of byte, cipher_suites: array of array of byte)
+ : (ref CipherSpec, ref KeyExAlg, ref SigAlg, string)
+{
+ cip : ref CipherSpec;
+ kex : ref KeyExAlg;
+ sig : ref SigAlg;
+
+ n := len cipher_suites;
+ i : int;
+ found := array [2] of byte;
+ for(i = 0; i < n; i++) {
+ found = cipher_suites[i];
+ if(found[0]==cs[0] && found[1]==cs[1]) break;
+ }
+
+ if(i == n)
+ return (nil, nil, nil, "fail to find a matched spec");
+
+ case i {
+ NULL_WITH_NULL_NULL =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_NULL_CIPHER,
+ SSL_STREAM_CIPHER, 0, 0, SSL_NULL_MAC, 0);
+ kex = ref KeyExAlg.NULL();
+ sig = ref SigAlg.anon();
+
+ RSA_WITH_NULL_MD5 => # sign only certificate
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_NULL_CIPHER,
+ SSL_STREAM_CIPHER, 0, 0, SSL_MD5, Keyring->MD5dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ RSA_WITH_NULL_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_NULL_CIPHER,
+ SSL_STREAM_CIPHER, 0, 0, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ RSA_EXPORT_WITH_RC4_40_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_RC4,
+ SSL_STREAM_CIPHER, 5, 0, SSL_MD5, Keyring->MD5dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ RSA_WITH_RC4_128_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_RC4,
+ SSL_STREAM_CIPHER, 16, 0, SSL_MD5, Keyring->MD5dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ RSA_WITH_RC4_128_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_RC4,
+ SSL_STREAM_CIPHER, 16, 0, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ RSA_EXPORT_WITH_RC2_CBC_40_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_RC2_CBC,
+ SSL_BLOCK_CIPHER, 5, 8, SSL_MD5, Keyring->MD5dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ RSA_WITH_IDEA_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_IDEA_CBC,
+ SSL_BLOCK_CIPHER, 16, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ RSA_EXPORT_WITH_DES40_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 5, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ RSA_WITH_DES_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 8, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ RSA_WITH_3DES_EDE_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_3DES_EDE_CBC,
+ SSL_BLOCK_CIPHER, 24, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ DH_DSS_EXPORT_WITH_DES40_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 5, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.DSS(nil, nil);
+
+ DH_DSS_WITH_DES_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 8, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.DSS(nil, nil);
+
+ DH_DSS_WITH_3DES_EDE_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_3DES_EDE_CBC,
+ SSL_BLOCK_CIPHER, 24, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.DSS(nil, nil);
+
+ DH_RSA_EXPORT_WITH_DES40_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 5, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ DH_RSA_WITH_DES_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC,
+ SSL_STREAM_CIPHER, 8, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ DH_RSA_WITH_3DES_EDE_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_3DES_EDE_CBC,
+ SSL_BLOCK_CIPHER, 24, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ DHE_DSS_EXPORT_WITH_DES40_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 5, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.DSS(nil, nil);
+
+ DHE_DSS_WITH_DES_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 8, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.DSS(nil, nil);
+
+ DHE_DSS_WITH_3DES_EDE_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_3DES_EDE_CBC,
+ SSL_BLOCK_CIPHER, 24, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.DSS(nil, nil);
+
+ DHE_RSA_EXPORT_WITH_DES40_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 5, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ DHE_RSA_WITH_DES_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 8, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ DHE_RSA_WITH_3DES_EDE_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_3DES_EDE_CBC,
+ SSL_BLOCK_CIPHER, 24, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ DH_anon_EXPORT_WITH_RC4_40_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_RC4,
+ SSL_STREAM_CIPHER, 5, 0, SSL_MD5, Keyring->MD5dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ DH_anon_WITH_RC4_128_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_RC4,
+ SSL_STREAM_CIPHER, 16, 0, SSL_MD5, Keyring->MD5dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ DH_anon_EXPORT_WITH_DES40_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 5, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ DH_anon_WITH_DES_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 8, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ DH_anon_WITH_3DES_EDE_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_3DES_EDE_CBC,
+ SSL_BLOCK_CIPHER, 24, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ FORTEZZA_KEA_WITH_NULL_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_NULL_CIPHER,
+ SSL_STREAM_CIPHER, 0, 0, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.FORTEZZA_KEA();
+ sig = ref SigAlg.FORTEZZA_KEA();
+
+ FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_FORTEZZA_CBC,
+ SSL_BLOCK_CIPHER, 0, 0, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.FORTEZZA_KEA();
+ sig = ref SigAlg.FORTEZZA_KEA();
+
+ FORTEZZA_KEA_WITH_RC4_128_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_RC4,
+ SSL_STREAM_CIPHER, 16, 0, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.FORTEZZA_KEA();
+ sig = ref SigAlg.FORTEZZA_KEA();
+
+ }
+
+ return (cip, kex, sig, nil);
+}
+
+#
+# use suites as default SSL3_Suites
+#
+cipher_suite_info(cs: array of byte, suites: array of array of byte) : string
+{
+ tag : string;
+
+ a := array [2] of byte;
+ n := len suites;
+ for(i := 0; i < n; i++) {
+ a = suites[i];
+ if(a[0]==cs[0] && a[1]==cs[1]) break;
+ }
+
+ if(i == n)
+ return "unknown cipher suite [" + string cs + "]";
+
+ case i {
+ NULL_WITH_NULL_NULL =>
+ tag = "NULL_WITH_NULL_NULL";
+
+ RSA_WITH_NULL_MD5 =>
+ tag = "RSA_WITH_NULL_MD5";
+
+ RSA_WITH_NULL_SHA =>
+ tag = "RSA_WITH_NULL_SHA";
+
+ RSA_EXPORT_WITH_RC4_40_MD5 =>
+ tag = "RSA_EXPORT_WITH_RC4_40_MD5";
+
+ RSA_WITH_RC4_128_MD5 =>
+ tag = "RSA_WITH_RC4_128_MD5";
+
+ RSA_WITH_RC4_128_SHA =>
+ tag = "RSA_WITH_RC4_128_SHA";
+
+ RSA_EXPORT_WITH_RC2_CBC_40_MD5 =>
+ tag = "RSA_EXPORT_WITH_RC2_CBC_40_MD5";
+
+ RSA_WITH_IDEA_CBC_SHA =>
+ tag = "RSA_WITH_IDEA_CBC_SHA";
+
+ RSA_EXPORT_WITH_DES40_CBC_SHA =>
+ tag ="RSA_EXPORT_WITH_DES40_CBC_SHA";
+
+ RSA_WITH_DES_CBC_SHA =>
+ tag = "RSA_WITH_DES_CBC_SHA";
+
+ RSA_WITH_3DES_EDE_CBC_SHA =>
+ tag = "RSA_WITH_3DES_EDE_CBC_SHA";
+
+ DH_DSS_EXPORT_WITH_DES40_CBC_SHA =>
+ tag = "DH_DSS_EXPORT_WITH_DES40_CBC_SHA";
+
+ DH_DSS_WITH_DES_CBC_SHA =>
+ tag = "DH_DSS_WITH_DES_CBC_SHA";
+
+ DH_DSS_WITH_3DES_EDE_CBC_SHA =>
+ tag = "DH_DSS_WITH_3DES_EDE_CBC_SHA";
+
+ DH_RSA_EXPORT_WITH_DES40_CBC_SHA =>
+ tag = "DH_RSA_EXPORT_WITH_DES40_CBC_SHA";
+
+ DH_RSA_WITH_DES_CBC_SHA =>
+ tag = "DH_RSA_WITH_DES_CBC_SHA";
+
+ DH_RSA_WITH_3DES_EDE_CBC_SHA =>
+ tag = "DH_RSA_WITH_3DES_EDE_CBC_SHA";
+
+ DHE_DSS_EXPORT_WITH_DES40_CBC_SHA =>
+ tag = "DHE_DSS_EXPORT_WITH_DES40_CBC_SHA";
+
+ DHE_DSS_WITH_DES_CBC_SHA =>
+ tag = "DHE_DSS_WITH_DES_CBC_SHA";
+
+ DHE_DSS_WITH_3DES_EDE_CBC_SHA =>
+ tag = "DHE_DSS_WITH_3DES_EDE_CBC_SHA";
+
+ DHE_RSA_EXPORT_WITH_DES40_CBC_SHA =>
+ tag = "DHE_RSA_EXPORT_WITH_DES40_CBC_SHA";
+
+ DHE_RSA_WITH_DES_CBC_SHA =>
+ tag = "DHE_RSA_WITH_DES_CBC_SHA";
+
+ DHE_RSA_WITH_3DES_EDE_CBC_SHA =>
+ tag = "DHE_RSA_WITH_3DES_EDE_CBC_SHA";
+
+ DH_anon_EXPORT_WITH_RC4_40_MD5 =>
+ tag = "DH_anon_EXPORT_WITH_RC4_40_MD5";
+
+ DH_anon_WITH_RC4_128_MD5 =>
+ tag = "DH_anon_WITH_RC4_128_MD5";
+
+ DH_anon_EXPORT_WITH_DES40_CBC_SHA =>
+ tag = "DH_anon_EXPORT_WITH_DES40_CBC_SHA";
+
+ DH_anon_WITH_DES_CBC_SHA =>
+ tag = "DH_anon_WITH_DES_CBC_SHA";
+
+ DH_anon_WITH_3DES_EDE_CBC_SHA =>
+ tag = "DH_anon_WITH_3DES_EDE_CBC_SHA";
+
+ FORTEZZA_KEA_WITH_NULL_SHA =>
+ tag = "FORTEZZA_KEA_WITH_NULL_SHA";
+
+ FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA =>
+ tag = "FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA";
+
+ FORTEZZA_KEA_WITH_RC4_128_SHA =>
+ tag = "FORTEZZA_KEA_WITH_RC4_128_SHA";
+ }
+
+ return "cipher suite = [" + tag + "]";
+}
+
+#################################
+## FOR SSLv2 BACKWARD COMPATIBLE
+#################################
+
+# Protocol Version Codes
+SSL2_CLIENT_VERSION := array [] of {byte 0, byte 16r02};
+SSL2_SERVER_VERSION := array [] of {byte 0, byte 16r02};
+
+# Protocol Message Codes
+
+SSL2_MT_ERROR,
+ SSL2_MT_CLIENT_HELLO,
+ SSL2_MT_CLIENT_MASTER_KEY,
+ SSL2_MT_CLIENT_FINISHED,
+ SSL2_MT_SERVER_HELLO,
+ SSL2_MT_SERVER_VERIFY,
+ SSL2_MT_SERVER_FINISHED,
+ SSL2_MT_REQUEST_CERTIFICATE,
+ SSL2_MT_CLIENT_CERTIFICATE : con iota;
+
+# Error Message Codes
+
+SSL2_PE_NO_CIPHER := array [] of {byte 0, byte 16r01};
+SSL2_PE_NO_CERTIFICATE := array [] of {byte 0, byte 16r02};
+SSL2_PE_BAD_CERTIFICATE := array [] of {byte 0, byte 16r04};
+SSL2_PE_UNSUPPORTED_CERTIFICATE_TYPE := array [] of {byte 0, byte 16r06};
+
+# Cipher Kind Values
+
+SSL2_CK_RC4_128_WITH_MD5,
+ SSL2_CK_RC4_128_EXPORT40_WITH_MD5,
+ SSL2_CK_RC2_CBC_128_CBC_WITH_MD5,
+ SSL2_CK_RC2_CBC_128_CBC_EXPORT40_WITH_MD5,
+ SSL2_CK_IDEA_128_CBC_WITH_MD5,
+ SSL2_CK_DES_64_CBC_WITH_MD5,
+ SSL2_CK_DES_192_EDE3_CBC_WITH_MD5 : con iota;
+
+SSL2_Cipher_Kinds := array [] of {
+ SSL2_CK_RC4_128_WITH_MD5 => array [] of {byte 16r01, byte 0, byte 16r80},
+ SSL2_CK_RC4_128_EXPORT40_WITH_MD5 => array [] of {byte 16r02, byte 0, byte 16r80},
+ SSL2_CK_RC2_CBC_128_CBC_WITH_MD5 => array [] of {byte 16r03, byte 0, byte 16r80},
+ SSL2_CK_RC2_CBC_128_CBC_EXPORT40_WITH_MD5 =>
+ array [] of {byte 16r04, byte 0, byte 16r80},
+ SSL2_CK_IDEA_128_CBC_WITH_MD5 => array [] of {byte 16r05, byte 0, byte 16r80},
+ SSL2_CK_DES_64_CBC_WITH_MD5 => array [] of {byte 16r06, byte 0, byte 16r40},
+ SSL2_CK_DES_192_EDE3_CBC_WITH_MD5 => array [] of {byte 16r07, byte 0, byte 16rC0},
+};
+
+# Certificate Type Codes
+
+SSL2_CT_X509_CERTIFICATE : con 16r01; # encode as one byte
+
+# Authentication Type Codes
+
+SSL2_AT_MD5_WITH_RSA_ENCRYPTION : con byte 16r01;
+
+# Upper/Lower Bounds
+
+SSL2_MAX_MASTER_KEY_LENGTH_IN_BITS : con 256;
+SSL2_MAX_SESSION_ID_LENGTH_IN_BYTES : con 16;
+SSL2_MIN_RSA_MODULUS_LENGTH_IN_BYTES : con 64;
+SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER : con 32767;
+SSL2_MAX_RECORD_LENGTH_3_BYTE_HEADER : con 16383;
+
+# Handshake Internal State
+
+SSL2_STATE_CLIENT_HELLO,
+ SSL2_STATE_SERVER_HELLO,
+ SSL2_STATE_CLIENT_MASTER_KEY,
+ SSL2_STATE_SERVER_VERIFY,
+ SSL2_STATE_REQUEST_CERTIFICATE,
+ SSL2_STATE_CLIENT_CERTIFICATE,
+ SSL2_STATE_CLIENT_FINISHED,
+ SSL2_STATE_SERVER_FINISHED,
+ SSL2_STATE_ERROR : con iota;
+
+# The client's challenge to the server for the server to identify itself is a
+# (near) arbitary length random. The v3 server will right justify the challenge
+# data to become the ClientHello.random data (padding with leading zeros, if
+# necessary). If the length of the challenge is greater than 32 bytes, then only
+# the last 32 bytes are used. It is legitimate (but not necessary) for a v3
+# server to reject a v2 ClientHello that has fewer than 16 bytes of challenge
+# data.
+
+SSL2_CHALLENGE_LENGTH : con 16;
+
+V2Handshake: adt {
+ pick {
+ Error =>
+ code : array of byte; # [2];
+ ClientHello =>
+ version : array of byte; # [2]
+ cipher_specs : array of byte; # [3] x
+ session_id : array of byte;
+ challenge : array of byte;
+ ServerHello =>
+ session_id_hit : int;
+ certificate_type : int;
+ version : array of byte; # [2]
+ certificate : array of byte; # only user certificate
+ cipher_specs : array of byte; # [3] x
+ connection_id : array of byte;
+ ClientMasterKey =>
+ cipher_kind : array of byte; # [3]
+ clear_key : array of byte;
+ encrypt_key : array of byte;
+ key_arg : array of byte;
+ ServerVerify =>
+ challenge : array of byte;
+ RequestCertificate =>
+ authentication_type : int;
+ certificate_challenge : array of byte;
+ ClientCertificate =>
+ certificate_type : int;
+ certificate : array of byte; # only user certificate
+ response : array of byte;
+ ClientFinished =>
+ connection_id : array of byte;
+ ServerFinished =>
+ session_id : array of byte;
+ }
+
+ encode: fn(hm: self ref V2Handshake): (array of byte, string);
+ decode: fn(a: array of byte): (ref V2Handshake, string);
+ tostring: fn(h: self ref V2Handshake): string;
+};
+
+
+V2Handshake.tostring(handshake: self ref V2Handshake): string
+{
+ info := "";
+
+ pick m := handshake {
+ ClientHello =>
+ info += "\tClientHello\n" +
+ "\tversion = \n\t\t" + bastr(m.version) + "\n" +
+ "\tcipher_specs = \n\t\t" + bastr(m.cipher_specs) + "\n" +
+ "\tsession_id = \n\t\t" + bastr(m.session_id) + "\n" +
+ "\tchallenge = \n\t\t" + bastr(m.challenge) + "\n";
+
+ ServerHello =>
+ info += "\tServerHello\n" +
+ "\tsession_id_hit = \n\t\t" + string m.session_id_hit + "\n" +
+ "\tcertificate_type = \n\t\t" + string m.certificate_type + "\n" +
+ "\tversion = \n\t\t" + bastr(m.version) + "\n" +
+ "\tcertificate = \n\t\t" + bastr(m.certificate) + "\n" +
+ "\tcipher_specs = \n\t\t" + bastr(m.cipher_specs) + "\n" +
+ "\tconnection_id = \n\t\t" + bastr(m.connection_id) + "\n";
+
+ ClientMasterKey =>
+ info += "\tClientMasterKey\n" +
+ "\tcipher_kind = \n\t\t" + bastr(m.cipher_kind) + "\n" +
+ "\tclear_key = \n\t\t" + bastr(m.clear_key) + "\n" +
+ "\tencrypt_key = \n\t\t" + bastr(m.encrypt_key) + "\n" +
+ "\tkey_arg = \n\t\t" + bastr(m.key_arg) + "\n";
+
+ ServerVerify =>
+ info += "\tServerVerify\n" +
+ "\tchallenge = \n\t\t" + bastr(m.challenge) + "\n";
+
+ RequestCertificate =>
+ info += "\tRequestCertificate\n" +
+ "\tauthentication_type = \n\t\t" + string m.authentication_type + "\n" +
+ "\tcertificate_challenge = \n\t\t" + bastr(m.certificate_challenge) + "\n";
+
+ ClientCertificate =>
+ info += "ClientCertificate\n" +
+ "\tcertificate_type = \n\t\t" + string m.certificate_type + "\n" +
+ "\tcertificate = \n\t\t" + bastr(m.certificate) + "\n" +
+ "\tresponse = \n\t\t" + bastr(m.response) + "\n";
+
+ ClientFinished =>
+ info += "\tClientFinished\n" +
+ "\tconnection_id = \n\t\t" + bastr(m.connection_id) + "\n";
+
+ ServerFinished =>
+ info += "\tServerFinished\n" +
+ "\tsession_id = \n\t\t" + bastr(m.session_id) + "\n";
+ }
+
+ return info;
+}
+
+
+# v2 handshake protocol - message driven, v2 and v3 sharing the same context stack
+
+do_v2handshake(v2hs: ref V2Handshake, ctx: ref Context): string
+{
+ e: string = nil;
+
+ pick h := v2hs {
+ Error =>
+ do_v2error(h, ctx);
+
+ ClientHello =>
+ if((ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_CLIENT_HELLO) {
+ e = "V2ClientHello";
+ break;
+ }
+ do_v2client_hello(h, ctx);
+
+ ServerHello =>
+ if(!(ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_SERVER_HELLO) {
+ e = "V2ServerHello";
+ break;
+ }
+ do_v2server_hello(h, ctx);
+
+ ClientMasterKey =>
+ if((ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_CLIENT_MASTER_KEY) {
+ e = "V2ClientMasterKey";
+ break;
+ }
+ do_v2client_master_key(h, ctx);
+
+ ServerVerify =>
+ if(!(ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_SERVER_VERIFY) {
+ e = "V2ServerVerify";
+ break;
+ }
+ do_v2server_verify(h, ctx);
+
+ RequestCertificate =>
+ if(!(ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_SERVER_VERIFY) {
+ e = "V2RequestCertificate";
+ break;
+ }
+ do_v2req_cert(h, ctx);
+
+ ClientCertificate =>
+ if((ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_CLIENT_CERTIFICATE) {
+ e = "V2ClientCertificate";
+ break;
+ }
+ do_v2client_certificate(h, ctx);
+
+ ClientFinished =>
+ if((ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_CLIENT_FINISHED) {
+ e = "V2ClientFinished";
+ break;
+ }
+ do_v2client_finished(h, ctx);
+
+ ServerFinished =>
+ if(!(ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_SERVER_FINISHED) {
+ e = "V2ServerFinished";
+ break;
+ }
+ do_v2server_finished(h, ctx);
+ }
+
+ return e;
+}
+
+do_v2error(v2hs: ref V2Handshake.Error, ctx: ref Context)
+{
+ if(SSL_DEBUG)
+ log("do_v2error: " + string v2hs.code);
+ ctx.state = STATE_EXIT;
+}
+
+# [server side]
+do_v2client_hello(v2hs: ref V2Handshake.ClientHello, ctx: ref Context)
+{
+ if(v2hs.version[0] != SSL2_CLIENT_VERSION[0] || v2hs.version[1] != SSL2_CLIENT_VERSION[1]) {
+ # promote this message to v3 handshake protocol
+ ctx.state = STATE_CLIENT_HELLO;
+ return;
+ }
+
+ # trying to resume
+ s: ref Session;
+ if((v2hs.session_id != nil) && (ctx.status & SESSION_RESUMABLE))
+ s = sslsession->get_session_byid(v2hs.session_id);
+ if(s != nil) { # found a hit
+ # prepare and send v2 handshake hello message
+ v2handshake_enque(
+ ref V2Handshake.ServerHello(
+ 1, # hit found
+ 0, # no certificate required
+ SSL2_SERVER_VERSION,
+ nil, # no authetication required
+ s.suite, # use hit session cipher kind
+ ctx.server_random # connection_id
+ ),
+ ctx
+ );
+ # TODO: should in supported cipher_kinds
+ err: string;
+ (ctx.sel_ciph, ctx.sel_keyx, ctx.sel_sign, err)
+ = v2suite_to_spec(ctx.session.suite, SSL2_Cipher_Kinds);
+ if(err != "") {
+ if(SSL_DEBUG)
+ log("do_v2client_hello: " + err);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ ctx.state = SSL2_STATE_SERVER_FINISHED;
+ }
+ else {
+ # find matching cipher kinds
+ n := len v2hs.cipher_specs;
+ matchs := array [n] of byte;
+ j, k: int = 0;
+ while(j < n) {
+ # ignore those not in SSL2_Cipher_Kinds
+ matchs[k:] = v2hs.cipher_specs[j:j+3];
+ for(i := 0; i < len SSL2_Cipher_Kinds; i++) {
+ ck := SSL2_Cipher_Kinds[i];
+ if(matchs[k] == ck[0] && matchs[k+1] == ck[1] && matchs[k+2] == ck[2])
+ k +=3;
+ }
+ j += 3;
+ }
+ if(k == 0) {
+ if(SSL_DEBUG)
+ log("do_v2client_hello: No matching cipher kind");
+ ctx.state = SSL2_STATE_ERROR;
+ }
+ else {
+ matchs = matchs[0:k];
+
+ # Note:
+ # v2 challenge -> v3 client_random
+ # v2 connection_id -> v3 server_random
+
+ chlen := len v2hs.challenge;
+ if(chlen > 32)
+ chlen = 32;
+ ctx.client_random = array [chlen] of byte;
+ if(chlen > 32)
+ ctx.client_random[0:] = v2hs.challenge[chlen-32:];
+ else
+ ctx.client_random[0:] = v2hs.challenge;
+ ctx.server_random = random->randombuf(Random->NotQuiteRandom, 16);
+ s.session_id = random->randombuf (
+ Random->NotQuiteRandom,
+ SSL2_MAX_SESSION_ID_LENGTH_IN_BYTES
+ );
+ s.suite = matchs;
+ ctx.session = s;
+ v2handshake_enque(
+ ref V2Handshake.ServerHello(
+ 0, # no hit - not resumable
+ SSL2_CT_X509_CERTIFICATE,
+ SSL2_SERVER_VERSION,
+ hd ctx.local_info.certs, # the first is user certificate
+ ctx.session.suite, # matched cipher kinds
+ ctx.server_random # connection_id
+ ),
+ ctx
+ );
+ ctx.state = SSL2_STATE_CLIENT_MASTER_KEY;
+ }
+ }
+}
+
+# [client side]
+
+do_v2server_hello(v2hs: ref V2Handshake.ServerHello, ctx: ref Context)
+{
+ # must be v2 server hello otherwise it will be v3 server hello
+ # determined by auto record layer version detection
+ if(v2hs.version[0] != SSL2_SERVER_VERSION[0]
+ || v2hs.version[1] != SSL2_SERVER_VERSION[1]) {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: not a v2 version");
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+
+ ctx.session.version = SSL2_SERVER_VERSION;
+ ctx.server_random = v2hs.connection_id;
+
+ # check if a resumable session is found
+ if(v2hs.session_id_hit != 0) { # resume ok
+ err: string;
+ # TODO: should in supported cipher_kinds
+ (ctx.sel_ciph, nil, nil, err) = v2suite_to_spec(ctx.session.suite, SSL2_Cipher_Kinds);
+ if(err != "") {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: " + err);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ }
+ else { # not resumable session
+
+ # use the first matched cipher kind; install cipher spec
+ if(len v2hs.cipher_specs < 3) {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: too few bytes");
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ ctx.session.suite = array [3] of byte;
+ ctx.session.suite[0:] = v2hs.cipher_specs[0:3];
+ err: string;
+ (ctx.sel_ciph, nil, nil, err) = v2suite_to_spec(ctx.session.suite, SSL2_Cipher_Kinds);
+ if(err != "") {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: " + err);
+ return;
+ }
+
+ # decode x509 certificates, authenticate server and extract
+ # public key from server certificate
+ if(v2hs.certificate_type != int SSL2_CT_X509_CERTIFICATE) {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: not x509 certificate");
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ ctx.session.peer_certs = v2hs.certificate :: nil;
+ # TODO: decode v2hs.certificate as list of certificate
+ # verify the list of certificate
+ (e, signed) := x509->Signed.decode(v2hs.certificate);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: " + e);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ certificate: ref Certificate;
+ (e, certificate) = x509->Certificate.decode(signed.tobe_signed);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: " + e);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ id: int;
+ peer_pk: ref X509->PublicKey;
+ (e, id, peer_pk) = certificate.subject_pkinfo.getPublicKey();
+ if(e != nil) {
+ ctx.state = SSL2_STATE_ERROR; # protocol error
+ return;
+ }
+ pk: ref RSAKey;
+ pick key := peer_pk {
+ RSA =>
+ pk = key.pk;
+ * =>
+ }
+ # prepare and send client master key message
+ # TODO: change CipherSpec adt for more key info
+ # Temporary solution
+ # mkey (master key), ckey (clear key), skey(secret key)
+ mkey, ckey, skey, keyarg: array of byte;
+ (mkeylen, ckeylen, keyarglen) := v2suite_more(ctx.sel_ciph);
+ mkey = random->randombuf(Random->NotQuiteRandom, mkeylen);
+ if(ckeylen != 0)
+ ckey = mkey[0:ckeylen];
+ if(mkeylen > ckeylen)
+ skey = mkey[ckeylen:];
+ if(keyarglen > 0)
+ keyarg = random->randombuf(Random->NotQuiteRandom, keyarglen);
+ ekey: array of byte;
+ (e, ekey) = pkcs->rsa_encrypt(skey, pk, 2);
+ if(e != nil) {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: " + e);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ ctx.session.master_secret = mkey;
+ v2handshake_enque(
+ ref V2Handshake.ClientMasterKey(ctx.session.suite, ckey, ekey, keyarg),
+ ctx
+ );
+ }
+
+ # clean up out_queue before switch cipher
+ record_write_queue(ctx);
+ ctx.out_queue.data = nil;
+
+ # install keys onto ctx that will be pushed on ssl record when ready
+ (ctx.cw_mac, ctx.sw_mac, ctx.cw_key, ctx.sw_key, ctx.cw_IV, ctx.sw_IV)
+ = v2calc_keys(ctx.sel_ciph, ctx.session.master_secret,
+ ctx.client_random, ctx.server_random);
+ e := set_queues(ctx);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("do_v2server_finished: " + e);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ ctx.status |= IN_READY;
+ ctx.status |= OUT_READY;
+
+ # prepare and send client finished message
+ v2handshake_enque(
+ ref V2Handshake.ClientFinished(ctx.server_random), # as connection_id
+ ctx
+ );
+
+ ctx.state = SSL2_STATE_SERVER_VERIFY;
+}
+
+# [server side]
+
+do_v2client_master_key(v2hs: ref V2Handshake.ClientMasterKey, ctx: ref Context)
+{
+ #if(cmk.cipher == -1 || cipher_info[cmk.cipher].cryptalg == -1) {
+ # # return ("protocol error: bad cipher in masterkey", nullc);
+ # ctx.state = SSL2_STATE_ERROR; # protocol error
+ # return;
+ #}
+
+ ctx.session.suite = v2hs.cipher_kind;
+
+ # TODO:
+ # someplace shall be able to install the key
+ # need further encapsulate encrypt and decrypt functions from KeyExAlg adt
+ master_key_length: int;
+ secret_key: array of byte;
+ pick alg := ctx.sel_keyx {
+ RSA =>
+ e: string;
+ (e, secret_key) = pkcs->rsa_decrypt(v2hs.encrypt_key, alg.sk, 0);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("do_v2client_master_key: " + e);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ master_key_length = len v2hs.clear_key + len secret_key;
+ * =>
+ if(SSL_DEBUG)
+ log("do_v2client_master_key: unknown public key algorithm");
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ #TODO: do the following lines after modifying the CipherSpec adt
+ #if(master_key_length != ci.keylen) {
+ # ctx.state = SSL2_STATE_ERROR; # protocol error
+ # return;
+ #}
+
+ ctx.session.master_secret = array [master_key_length] of byte;
+ ctx.session.master_secret[0:] = v2hs.clear_key;
+ ctx.session.master_secret[len v2hs.clear_key:] = secret_key;
+
+ # install keys onto ctx that will be pushed on ssl record when ready
+ (ctx.cw_mac, ctx.sw_mac, ctx.cw_key, ctx.sw_key, ctx.cw_IV, ctx.sw_IV)
+ = v2calc_keys(ctx.sel_ciph, ctx.session.master_secret,
+ ctx.client_random, ctx.server_random);
+ v2handshake_enque(
+ ref V2Handshake.ServerVerify(ctx.client_random[16:]),
+ ctx
+ );
+ v2handshake_enque(
+ ref V2Handshake.ServerFinished(ctx.session.session_id),
+ ctx
+ );
+ ctx.state = SSL2_STATE_CLIENT_FINISHED;
+}
+
+# used by client side
+do_v2server_verify(v2hs: ref V2Handshake.ServerVerify, ctx: ref Context)
+{
+ # TODO:
+ # the challenge length may not be 16 bytes
+ if(bytes_cmp(v2hs.challenge, ctx.client_random[32-SSL2_CHALLENGE_LENGTH:]) < 0) {
+ if(SSL_DEBUG)
+ log("do_v2server_verify: challenge mismatch");
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+
+ ctx.state = SSL2_STATE_SERVER_FINISHED;
+}
+
+# [client side]
+
+do_v2req_cert(v2hs: ref V2Handshake.RequestCertificate, ctx: ref Context)
+{
+ # not supported until v3
+ if(SSL_DEBUG)
+ log("do_v2req_cert: authenticate client not supported");
+ v2hs = nil;
+ ctx.state = SSL2_STATE_ERROR;
+}
+
+# [server side]
+
+do_v2client_certificate(v2hs: ref V2Handshake.ClientCertificate, ctx: ref Context)
+{
+ # not supported until v3
+ if(SSL_DEBUG)
+ log("do_v2client_certificate: authenticate client not supported");
+ v2handshake_enque (
+ ref V2Handshake.Error(SSL2_PE_NO_CERTIFICATE),
+ ctx
+ );
+ v2hs = nil;
+ ctx.state = SSL2_STATE_ERROR;
+}
+
+# [server side]
+
+do_v2client_finished(v2hs: ref V2Handshake.ClientFinished, ctx: ref Context)
+{
+ if(bytes_cmp(ctx.server_random, v2hs.connection_id) < 0) {
+ ctx.session.session_id = nil;
+ if(SSL_DEBUG)
+ log("do_v2client_finished: connection id mismatch");
+ ctx.state = SSL2_STATE_ERROR;
+ }
+ # TODO:
+ # the challenge length may not be 16 bytes
+ v2handshake_enque(
+ ref V2Handshake.ServerVerify(ctx.client_random[32-SSL2_CHALLENGE_LENGTH:]),
+ ctx
+ );
+ if(ctx.session.session_id == nil)
+ ctx.session.session_id = random->randombuf(Random->NotQuiteRandom, 16);
+ v2handshake_enque(
+ ref V2Handshake.ServerFinished(ctx.session.session_id),
+ ctx
+ );
+ e := set_queues(ctx);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("do_v2client_finished: " + e);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ ctx.status |= IN_READY;
+ ctx.status |= OUT_READY;
+ sslsession->add_session(ctx.session);
+
+ ctx.state = STATE_EXIT;
+}
+
+# [client side]
+
+do_v2server_finished(v2hs: ref V2Handshake.ServerFinished, ctx: ref Context)
+{
+ if(ctx.session.session_id == nil)
+ ctx.session.session_id = array [16] of byte;
+ ctx.session.session_id[0:] = v2hs.session_id[0:];
+
+ sslsession->add_session(ctx.session);
+
+ ctx.state = STATE_EXIT;
+}
+
+
+# Note:
+# the key partitioning for v2 is different from v3
+
+v2calc_keys(ciph: ref CipherSpec, ms, cr, sr: array of byte)
+ : (array of byte, array of byte, array of byte, array of byte, array of byte, array of byte)
+{
+ cw_mac, sw_mac, cw_key, sw_key, cw_IV, sw_IV: array of byte;
+
+ # TODO: check the size of key block if IV exists
+ (mkeylen, ckeylen, keyarglen) := v2suite_more(ciph);
+ kblen := 2*mkeylen;
+ if(kblen%Keyring->MD5dlen != 0) {
+ if(SSL_DEBUG)
+ log("v2calc_keys: key block length is not multiple of MD5 hash length");
+ }
+ else {
+ key_block := array [kblen] of byte;
+
+ challenge := cr[32-SSL2_CHALLENGE_LENGTH:32]; # TODO: if challenge length != 16 ?
+ connection_id := sr[0:16]; # TODO: if connection_id length != 16 ?
+ var := array [1] of byte;
+ var[0] = byte 16r30;
+ i := 0;
+ while(i < kblen) {
+ (hash, nil) := md5_hash(ms::var::challenge::connection_id::nil, nil);
+ key_block[i:] = hash;
+ i += Keyring->MD5dlen;
+ ++var[0];
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: calc_keys:"
+ + "\n\tmaster key = \n\t\t" + bastr(ms)
+ + "\n\tchallenge = \n\t\t" + bastr(challenge)
+ + "\n\tconnection id = \n\t\t" + bastr(connection_id)
+ + "\n\tkey block = \n\t\t" + bastr(key_block) + "\n");
+
+ i = 0;
+ # server write key == client read key
+ # server write mac == server write key
+ sw_key = array [mkeylen] of byte;
+ sw_key[0:] = key_block[i:mkeylen];
+ sw_mac = array [mkeylen] of byte;
+ sw_mac[0:] = key_block[i:mkeylen];
+ # client write key == server read key
+ # client write mac == client write key
+ i += mkeylen;
+ cw_key = array [mkeylen] of byte;
+ cw_key[0:] = key_block[i:i+mkeylen];
+ cw_mac = array [mkeylen] of byte;
+ cw_mac[0:] = key_block[i:i+mkeylen];
+ # client IV == server IV
+ # Note:
+ # IV is a part of writing or reading key for ssl device
+ # this is composed again in setctl
+ cw_IV = array [keyarglen] of byte;
+ cw_IV[0:] = ms[mkeylen:mkeylen+keyarglen];
+ sw_IV = array [keyarglen] of byte;
+ sw_IV[0:] = ms[mkeylen:mkeylen+keyarglen];
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: calc_keys:"
+ + "\n\tclient_write_mac = \n\t\t" + bastr(cw_mac)
+ + "\n\tserver_write_mac = \n\t\t" + bastr(sw_mac)
+ + "\n\tclient_write_key = \n\t\t" + bastr(cw_key)
+ + "\n\tserver_write_key = \n\t\t" + bastr(sw_key)
+ + "\n\tclient_write_IV = \n\t\t" + bastr(cw_IV)
+ + "\n\tserver_write_IV = \n\t\t" + bastr(sw_IV) + "\n");
+
+ return (cw_mac, sw_mac, cw_key, sw_key, cw_IV, sw_IV);
+}
+
+v3tov2specs(suites: array of byte): array of byte
+{
+ # v3 suite codes are 2 bytes each, v2 codes are 3 bytes
+ n := len suites / 2;
+ kinds := array [n*3*2] of byte;
+ k := 0;
+ for(i := 0; i < n;) {
+ a := suites[i:i+2];
+ i += 2;
+ m := len SSL3_Suites;
+ for(j := 0; j < m; j++) {
+ b := SSL3_Suites[j];
+ if(a[0]==b[0] && a[1]==b[1])
+ break;
+ }
+ if (j == m) {
+ if(SSL_DEBUG)
+ log("ssl3: unknown v3 suite");
+ continue;
+ }
+ case j {
+ RSA_EXPORT_WITH_RC4_40_MD5 =>
+ kinds[k:] = SSL2_Cipher_Kinds[SSL2_CK_RC4_128_EXPORT40_WITH_MD5];
+ k += 3;
+ RSA_WITH_RC4_128_MD5 =>
+ kinds[k:] = SSL2_Cipher_Kinds[SSL2_CK_RC4_128_WITH_MD5];
+ k += 3;
+ RSA_WITH_IDEA_CBC_SHA =>
+ kinds[k:] = SSL2_Cipher_Kinds[SSL2_CK_IDEA_128_CBC_WITH_MD5];
+ k += 3;
+ RSA_WITH_DES_CBC_SHA =>
+ ;
+ * =>
+ if(SSL_DEBUG)
+ log("ssl3: unable to convert v3 suite to v2 kind");
+ }
+ # append v3 code in v2-safe manner
+ # (suite[0] == 0) => will be ignored by v2 server, picked up by v3 server
+ kinds[k] = byte 16r00;
+ kinds[k+1:] = SSL3_Suites[j];
+ k += 3;
+ }
+ return kinds[0:k];
+}
+
+v2suite_to_spec(cs: array of byte, cipher_kinds: array of array of byte)
+ : (ref CipherSpec, ref KeyExAlg, ref SigAlg, string)
+{
+ cip : ref CipherSpec;
+ kex : ref KeyExAlg;
+ sig : ref SigAlg;
+
+ n := len cipher_kinds;
+ for(i := 0; i < n; i++) {
+ found := cipher_kinds[i];
+ if(found[0]==cs[0] && found[1]==cs[1] && found[2]==cs[2]) break;
+ }
+
+ if(i == n)
+ return (nil, nil, nil, "fail to find a matched spec");
+
+ case i {
+ SSL2_CK_RC4_128_WITH_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_RC4, SSL_STREAM_CIPHER,
+ 16, 0, SSL_MD5, Keyring->MD4dlen);
+
+ SSL2_CK_RC4_128_EXPORT40_WITH_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_RC4, SSL_STREAM_CIPHER,
+ 5, 0, SSL_MD5, Keyring->MD4dlen);
+
+ SSL2_CK_RC2_CBC_128_CBC_WITH_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_RC2_CBC, SSL_BLOCK_CIPHER,
+ 16, 8, SSL_MD5, Keyring->MD4dlen);
+
+ SSL2_CK_RC2_CBC_128_CBC_EXPORT40_WITH_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_RC2_CBC, SSL_BLOCK_CIPHER,
+ 5, 8, SSL_MD5, Keyring->MD4dlen);
+
+ SSL2_CK_IDEA_128_CBC_WITH_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_IDEA_CBC, SSL_BLOCK_CIPHER,
+ 16, 8, SSL_MD5, Keyring->MD4dlen);
+
+ SSL2_CK_DES_64_CBC_WITH_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC, SSL_BLOCK_CIPHER,
+ 8, 8, SSL_MD5, Keyring->MD4dlen);
+
+ SSL2_CK_DES_192_EDE3_CBC_WITH_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_3DES_EDE_CBC, SSL_BLOCK_CIPHER,
+ 24, 8, SSL_MD5, Keyring->MD4dlen);
+ }
+
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ return (cip, kex, sig, nil);
+}
+
+v2suite_more(ciph: ref CipherSpec): (int, int, int)
+{
+ mkeylen, ckeylen, keyarglen: int;
+
+ case ciph.bulk_cipher_algorithm {
+ SSL_RC4 =>
+ mkeylen = 16;
+ if(ciph.key_material == 5)
+ ckeylen = 16 - 5;
+ else
+ ckeylen = 0;
+ keyarglen = 0;
+
+ SSL_RC2_CBC =>
+ mkeylen = 16;
+ if(ciph.key_material == 5)
+ ckeylen = 16 - 5;
+ else
+ ckeylen = 0;
+ keyarglen = 8;
+
+ SSL_IDEA_CBC =>
+ mkeylen = 16;
+ ckeylen = 0;
+ keyarglen = 8;
+
+ SSL_DES_CBC =>
+ mkeylen = 8;
+ if(ciph.key_material == 5)
+ ckeylen = 8 - 5;
+ else
+ ckeylen = 0;
+ keyarglen = 8;
+
+ SSL_3DES_EDE_CBC =>
+ mkeylen = 24;
+ ckeylen = 0;
+ keyarglen = 8;
+ }
+
+ return (mkeylen, ckeylen, keyarglen);
+}
+
+v2handshake_enque(h: ref V2Handshake, ctx: ref Context)
+{
+ p := ref Protocol.pV2Handshake(h);
+
+ protocol_write(p, ctx);
+}
+
+V2Handshake.encode(hm: self ref V2Handshake): (array of byte, string)
+{
+ a : array of byte;
+ n : int;
+ e : string;
+
+ i := 0;
+ pick m := hm {
+ Error =>
+ n = 3;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_ERROR;
+ a[i:] = m.code;
+
+ ClientHello =>
+ specslen := len m.cipher_specs;
+ sidlen := len m.session_id;
+ challen := len m.challenge;
+ n = 9+specslen + sidlen + challen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_CLIENT_HELLO;
+ a[i:] = m.version;
+ i += 2;
+ a[i:] = int_encode(specslen, 2);
+ i += 2;
+ a[i:] = int_encode(sidlen, 2);
+ i += 2;
+ a[i:] = int_encode(challen, 2);
+ i += 2;
+ a[i:] = m.cipher_specs;
+ i += specslen;
+ if(sidlen != 0) {
+ a[i:] = m.session_id;
+ i += sidlen;
+ }
+ if(challen != 0) {
+ a[i:] = m.challenge;
+ i += challen;
+ }
+
+ ServerHello =>
+ # use only the user certificate
+ certlen := len m.certificate;
+# specslen := 3*len m.cipher_specs;
+ specslen := len m.cipher_specs;
+ cidlen := len m.connection_id;
+ n = 11 + certlen + specslen + cidlen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_SERVER_HELLO;
+ a[i++] = byte m.session_id_hit;
+ a[i++] = byte m.certificate_type;
+ a[i:] = m.version;
+ i += 2;
+ a[i:] = int_encode(certlen, 2);
+ i += 2;
+ a[i:] = int_encode(specslen, 2);
+ i += 2;
+ a[i:] = int_encode(cidlen, 2);
+ i += 2;
+ a[i:] = m.certificate;
+ i += certlen;
+ a[i:] = m.cipher_specs;
+ i += specslen;
+ a[i:] = m.connection_id;
+ i += cidlen;
+
+ ClientMasterKey =>
+ ckeylen := len m.clear_key;
+ ekeylen := len m.encrypt_key;
+ karglen := len m.key_arg;
+ n = 10 + ckeylen + ekeylen + karglen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_CLIENT_MASTER_KEY;
+ a[i:] = m.cipher_kind;
+ i += 3;
+ a[i:] = int_encode(ckeylen, 2);
+ i += 2;
+ a[i:] = int_encode(ekeylen, 2);
+ i += 2;
+ a[i:] = int_encode(karglen, 2);
+ i += 2;
+ a[i:] = m.clear_key;
+ i += ckeylen;
+ a[i:] = m.encrypt_key;
+ i += ekeylen;
+ a[i:] = m.key_arg;
+ i += karglen;
+
+ ServerVerify =>
+ challen := len m.challenge;
+ n = 1 + challen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_SERVER_VERIFY;
+ a[i:] = m.challenge;
+
+ RequestCertificate =>
+ cclen := len m.certificate_challenge;
+ n = 2 + cclen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_REQUEST_CERTIFICATE;
+ a[i++] = byte m.authentication_type;
+ a[i:] = m.certificate_challenge;
+ i += cclen;
+
+ ClientCertificate =>
+ # use only the user certificate
+ certlen := len m.certificate;
+ resplen := len m.response;
+ n = 6 + certlen + resplen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_CLIENT_CERTIFICATE;
+ a[i++] = byte m.certificate_type;
+ a[i:] = int_encode(certlen, 2);
+ i += 2;
+ a[i:] = int_encode(resplen, 2);
+ i += 2;
+ a[i:] = m.certificate;
+ i += certlen;
+ a[i:] = m.response;
+ i += resplen;
+
+ ClientFinished =>
+ cidlen := len m.connection_id;
+ n = 1 + cidlen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_CLIENT_FINISHED;
+ a[i:] = m.connection_id;
+ i += cidlen;
+
+ ServerFinished =>
+ sidlen := len m.session_id;
+ n = 1 + sidlen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_SERVER_FINISHED;
+ a[i:] = m.session_id;
+ i += sidlen;
+ }
+
+ return (a, e);
+}
+
+V2Handshake.decode(a: array of byte): (ref V2Handshake, string)
+{
+ m : ref V2Handshake;
+ e : string;
+
+ n := len a;
+ i := 1;
+ case int a[0] {
+ SSL2_MT_ERROR =>
+ if(n != 3)
+ break;
+ code := a[i:i+2];
+ i += 2;
+ m = ref V2Handshake.Error(code);
+
+ SSL2_MT_CLIENT_HELLO =>
+ if(n < 9) {
+ e = "client hello: message too short";
+ break;
+ }
+ ver := a[i:i+2];
+ i += 2;
+ specslen := int_decode(a[i:i+2]);
+ i += 2;
+ sidlen := int_decode(a[i:i+2]);
+ i += 2;
+ challen := int_decode(a[i:i+2]);
+ i += 2;
+ if(n != 9+specslen+sidlen+challen) {
+ e = "client hello: length mismatch";
+ break;
+ }
+ if(specslen%3 != 0) {
+ e = "client hello: must multiple of 3 bytes";
+ break;
+ }
+ specs: array of byte;
+ if(specslen != 0) {
+ specs = a[i:i+specslen];
+ i += specslen;
+ }
+ sid: array of byte;
+ if(sidlen != 0) {
+ sid = a[i:i+sidlen];
+ i += sidlen;
+ }
+ chal: array of byte;
+ if(challen != 0) {
+ chal = a[i:i+challen];
+ i += challen;
+ }
+ m = ref V2Handshake.ClientHello(ver, specs, sid, chal);
+
+ SSL2_MT_CLIENT_MASTER_KEY =>
+ if(n < 10) {
+ e = "client master key: message too short";
+ break;
+ }
+ kind := a[i:i+3];
+ i += 3;
+ ckeylen := int_decode(a[i:i+2]);
+ i += 2;
+ ekeylen := int_decode(a[i:i+2]);
+ i += 2;
+ karglen := int_decode(a[i:i+2]);
+ i += 2;
+ if(n != 10 + ckeylen + ekeylen + karglen) {
+ e = "client master key: length mismatch";
+ break;
+ }
+ ckey := a[i:i+ckeylen];
+ i += ckeylen;
+ ekey := a[i:i+ekeylen];
+ i += ekeylen;
+ karg := a[i:i+karglen];
+ i += karglen;
+ m = ref V2Handshake.ClientMasterKey(kind, ckey, ekey, karg);
+
+ SSL2_MT_CLIENT_FINISHED =>
+ cid := a[i:n];
+ i = n;
+ m = ref V2Handshake.ClientFinished(cid);
+
+ SSL2_MT_SERVER_HELLO =>
+ if(n < 11) {
+ e = "server hello: messsage too short";
+ break;
+ }
+ sidhit := int a[i++];
+ certtype := int a[i++];
+ ver := a[i:i+2];
+ i += 2;
+ certlen := int_decode(a[i:i+2]);
+ i += 2;
+ specslen := int_decode(a[i:i+2]);
+ i += 2;
+ cidlen := int_decode(a[i:i+2]);
+ i += 2;
+ if(n != 11+certlen+specslen+cidlen) {
+ e = "server hello: length mismatch";
+ break;
+ }
+ cert := a[i:i+certlen];
+ i += certlen;
+ if(specslen%3 != 0) {
+ e = "server hello: must be multiple of 3 bytes";
+ break;
+ }
+ specs := a[i:i+specslen];
+ i += specslen;
+ if(cidlen < 16 || cidlen > 32) {
+ e = "server hello: connection id length out of range";
+ break;
+ }
+ cid := a[i:i+cidlen];
+ i += cidlen;
+ m = ref V2Handshake.ServerHello(sidhit, certtype, ver, cert, specs, cid);
+
+ SSL2_MT_SERVER_VERIFY =>
+ chal := a[i:n];
+ i = n;
+ m = ref V2Handshake.ServerVerify(chal);
+
+ SSL2_MT_SERVER_FINISHED =>
+ sid := a[i:n];
+ m = ref V2Handshake.ServerFinished(sid);
+
+ SSL2_MT_REQUEST_CERTIFICATE =>
+ if(n < 2) {
+ e = "request certificate: message too short";
+ break;
+ }
+ authtype := int a[i++];
+ certchal := a[i:n];
+ i = n;
+ m = ref V2Handshake.RequestCertificate(authtype, certchal);
+
+ SSL2_MT_CLIENT_CERTIFICATE =>
+ if(n < 6) {
+ e = "client certificate: message too short";
+ break;
+ }
+ certtype := int a[i++];
+ certlen := int_decode(a[i:i+2]);
+ i += 2;
+ resplen := int_decode(a[i:i+2]);
+ i += 2;
+ if(n != 6+certlen+resplen) {
+ e = "client certificate: length mismatch";
+ break;
+ }
+ cert := a[i:i+certlen];
+ i += certlen;
+ resp := a[i:i+resplen];
+ m = ref V2Handshake.ClientCertificate(certtype, cert, resp);
+
+ * =>
+ e = "unknown message [" + string a[0] + "]";
+ }
+
+ return (m, e);
+}
+
+# utilities
+
+md5_hash(input: list of array of byte, md5_ds: ref DigestState): (array of byte, ref DigestState)
+{
+ hash_value := array [Keyring->MD5dlen] of byte;
+ ds : ref DigestState;
+
+ if(md5_ds != nil)
+ ds = md5_ds.copy();
+
+ lab := input;
+ for(i := 0; i < len input - 1; i++) {
+ ds = keyring->md5(hd lab, len hd lab, nil, ds);
+ lab = tl lab;
+ }
+ ds = keyring->md5(hd lab, len hd lab, hash_value, ds);
+
+ return (hash_value, ds);
+}
+
+sha_hash(input: list of array of byte, sha_ds: ref DigestState): (array of byte, ref DigestState)
+{
+ hash_value := array [Keyring->SHA1dlen] of byte;
+ ds : ref DigestState;
+
+ if(sha_ds != nil)
+ ds = sha_ds.copy();
+
+ lab := input;
+ for(i := 0; i < len input - 1; i++) {
+ ds = keyring->sha1(hd lab, len hd lab, nil, ds);
+ lab = tl lab;
+ }
+ ds = keyring->sha1(hd lab, len hd lab, hash_value, ds);
+
+ return (hash_value, ds);
+}
+
+md5_sha_hash(input: list of array of byte, md5_ds, sha_ds: ref DigestState)
+ : (array of byte, ref DigestState, ref DigestState)
+{
+ buf := array [Keyring->MD5dlen+Keyring->SHA1dlen] of byte;
+
+ (buf[0:], md5_ds) = md5_hash(input, md5_ds);
+ (buf[Keyring->MD5dlen:], sha_ds) = sha_hash(input, sha_ds);
+
+ return (buf, md5_ds, sha_ds);
+}
+
+int_decode(buf: array of byte): int
+{
+ val := 0;
+ for(i := 0; i < len buf; i++)
+ val = (val << 8) | (int buf[i]);
+
+ return val;
+}
+
+int_encode(value, length: int): array of byte
+{
+ buf := array [length] of byte;
+
+ while(length--) {
+ buf[length] = byte value;
+ value >>= 8;
+ }
+
+ return buf;
+}
+
+
+bastr(a: array of byte) : string
+{
+ ans : string = "";
+
+ for(i := 0; i < len a; i++) {
+ if(i < len a - 1 && i != 0 && i%10 == 0)
+ ans += "\n\t\t";
+ if(i == len a -1)
+ ans += sys->sprint("%2x", int a[i]);
+ else
+ ans += sys->sprint("%2x ", int a[i]);
+ }
+
+ return ans;
+}
+
+bbastr(a: array of array of byte) : string
+{
+ info := "";
+
+ for(i := 0; i < len a; i++)
+ info += bastr(a[i]);
+
+ return info;
+}
+
+lbastr(a: list of array of byte) : string
+{
+ info := "";
+
+ l := a;
+ while(l != nil) {
+ info += bastr(hd l) + "\n\t\t";
+ l = tl l;
+ }
+
+ return info;
+}
+
+# need to fix (string a == string b)
+bytes_cmp(a, b: array of byte): int
+{
+ if(len a != len b)
+ return -1;
+
+ n := len a;
+ for(i := 0; i < n; i++) {
+ if(a[i] != b[i])
+ return -1;
+ }
+
+ return 0;
+}
+
+putn(a: array of byte, i, value, n: int): int
+{
+ j := n;
+ while(j--) {
+ a[i+j] = byte value;
+ value >>= 8;
+ }
+ return i+n;
+}
+
+INVALID_SUITE : con "invalid suite list";
+ILLEGAL_SUITE : con "illegal suite list";
+
+cksuites(suites : array of byte) : string
+{
+ m := len suites;
+ if (m == 0 || (m&1))
+ return INVALID_SUITE;
+ n := len SSL3_Suites;
+ ssl3s := array [2] of byte;
+ for (j := 0; j < m; j += 2) {
+ for( i := 0; i < n; i++) {
+ ssl3s = SSL3_Suites[i];
+ if(suites[j] == ssl3s[0] && suites[j+1] == ssl3s[1])
+ break;
+ }
+ if (i == n)
+ return ILLEGAL_SUITE;
+ }
+ return nil;
+}
diff --git a/appl/lib/crypt/sslsession.b b/appl/lib/crypt/sslsession.b
new file mode 100644
index 00000000..cce8399e
--- /dev/null
+++ b/appl/lib/crypt/sslsession.b
@@ -0,0 +1,176 @@
+#
+# SSL Session Cache
+#
+implement SSLsession;
+
+include "sys.m";
+ sys : Sys;
+
+include "daytime.m";
+ daytime : Daytime;
+
+include "sslsession.m";
+
+
+# default session id timeout
+TIMEOUT_SECS : con 5*60; # sec
+
+SessionCache: adt {
+ db : list of ref Session;
+ time_out : int;
+};
+
+# The shared session cache by all ssl contexts is available for efficiently resumming
+# sessions for different run time contexts.
+
+Session_Cache : ref SessionCache;
+
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ if(sys == nil)
+ return "sslsession: load sys module failed";
+
+ daytime = load Daytime Daytime->PATH;
+ if(daytime == nil)
+ return "sslsession: load Daytime module failed";
+
+ Session_Cache = ref SessionCache(nil, TIMEOUT_SECS);
+
+ return "";
+}
+
+Session.new(peer: string, time: int, ver: array of byte): ref Session
+{
+ s := ref Session;
+
+ s.peer = peer;
+ s.connection_time = time;
+ s.version = array [2] of byte;
+ s.version[0:] = ver;
+ s.session_id = nil;
+ s.suite = nil;
+ s.master_secret = nil;
+ s.peer_certs = nil;
+
+ return s;
+}
+
+Session.duplicate(s: self ref Session): ref Session
+{
+ new := ref Session;
+
+ new.peer = s.peer;
+ new.connection_time = s.connection_time;
+ new.version = array [len s.version] of byte;
+ new.version[0:] = s.version;
+ new.session_id = array [len s.session_id] of byte;
+ new.session_id[0:] = s.session_id;
+ new.suite = array [len s.suite] of byte;
+ new.suite[0:] = s.suite;
+ new.master_secret = array [len s.master_secret] of byte;
+ new.master_secret[0:] = s.master_secret;
+ l: list of array of byte;
+ pcs := s.peer_certs;
+ while(pcs != nil) {
+ a := hd pcs;
+ b := array [len a] of byte;
+ b[0:] = a;
+ l = b :: l;
+ pcs = tl pcs;
+ }
+ while(l != nil) {
+ new.peer_certs = (hd l) :: new.peer_certs;
+ l = tl l;
+ }
+ return new;
+}
+
+# Each request process should get a copy of a session. A session will be
+# removed from database if it is expired. The garbage
+# collector will finally remove it from memory if there are no more
+# references to it.
+
+get_session_byname(peer: string): ref Session
+{
+ s: ref Session;
+ now := daytime->now(); # not accurate but more efficient
+
+ l := Session_Cache.db;
+ while(l != nil) {
+ if((hd l).peer == peer) {
+ s = hd l;
+ # TODO: remove expired session
+ if(now > s.connection_time+Session_Cache.time_out)
+ s = nil;
+ break;
+ }
+ l = tl l;
+ }
+ if(s == nil)
+ s = Session.new(peer, now, nil);
+ else
+ s = s.duplicate();
+
+ return s;
+}
+
+# replace the old by the new one
+add_session(s: ref Session)
+{
+ #old : ref Session;
+
+ #ls := Session_Cache.db;
+ #while(ls != nil) {
+ # old = hd ls;
+ # if(s.session_id == old.session_id) {
+ # # old = s;
+ # return;
+ # }
+ #}
+
+ # always resume the most recent
+ if(s != nil)
+ Session_Cache.db = s :: Session_Cache.db;
+}
+
+get_session_byid(session_id: array of byte): ref Session
+{
+ s: ref Session;
+ now := daytime->now(); # not accurate but more efficient
+ l := Session_Cache.db;
+ while(l != nil) {
+ if(bytes_cmp((hd l).session_id, session_id) == 0) {
+ s = hd l;
+ # replace expired session
+ if(now > s.connection_time+Session_Cache.time_out)
+ s = Session.new(s.peer, now, nil);
+ else
+ s = s.duplicate();
+ break;
+ }
+ l = tl l;
+ }
+ return s;
+}
+
+set_timeout(t: int)
+{
+ Session_Cache.time_out = t;
+}
+
+bytes_cmp(a, b: array of byte): int
+{
+ if(len a != len b)
+ return -1;
+
+ n := len a;
+ for(i := 0; i < n; i++) {
+ if(a[i] != b[i])
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/appl/lib/crypt/x509.b b/appl/lib/crypt/x509.b
new file mode 100644
index 00000000..059a58f6
--- /dev/null
+++ b/appl/lib/crypt/x509.b
@@ -0,0 +1,4125 @@
+implement X509;
+
+include "sys.m";
+ sys : Sys;
+
+include "asn1.m";
+ asn1 : ASN1;
+ Elem, Tag, Value, Oid,
+ Universal, Context,
+ BOOLEAN,
+ INTEGER,
+ BIT_STRING,
+ OCTET_STRING,
+ OBJECT_ID,
+ SEQUENCE,
+ UTCTime,
+ IA5String,
+ GeneralString,
+ GeneralizedTime : import asn1;
+
+include "keyring.m";
+ keyring : Keyring;
+ MD4, MD5, SHA1, IPint, DESstate : import keyring;
+
+include "security.m";
+ random : Random;
+
+include "daytime.m";
+ daytime : Daytime;
+
+include "pkcs.m";
+ pkcs : PKCS;
+
+include "x509.m";
+
+X509_DEBUG : con 0;
+logfd : ref Sys->FD;
+
+TAG_MASK : con 16r1F;
+CONSTR_MASK : con 16r20;
+CLASS_MASK : con 16rC0;
+
+# object identifiers
+
+objIdTab = array [] of {
+ id_at => Oid(array [] of {2,5,4}),
+ id_at_commonName => Oid(array [] of {2,5,4,3}),
+ id_at_countryName => Oid(array [] of {2,5,4,6}),
+ id_at_localityName => Oid(array [] of {2,5,4,7}),
+ id_at_stateOrProvinceName => Oid(array [] of {2,5,4,8}),
+ id_at_organizationName => Oid(array [] of {2,5,4,10}),
+ id_at_organizationalUnitName => Oid(array [] of {2,5,4,11}),
+ id_at_userPassword => Oid(array [] of {2,5,4,35}),
+ id_at_userCertificate => Oid(array [] of {2,5,4,36}),
+ id_at_cAcertificate => Oid(array [] of {2,5,4,37}),
+ id_at_authorityRevocationList =>
+ Oid(array [] of {2,5,4,38}),
+ id_at_certificateRevocationList =>
+ Oid(array [] of {2,5,4,39}),
+ id_at_crossCertificatePair => Oid(array [] of {2,5,4,40}),
+# id_at_crossCertificatePair => Oid(array [] of {2,5,4,58}),
+ id_at_supportedAlgorithms => Oid(array [] of {2,5,4,52}),
+ id_at_deltaRevocationList => Oid(array [] of {2,5,4,53}),
+
+ id_ce => Oid(array [] of {2,5,29}),
+ id_ce_subjectDirectoryAttributes =>
+ Oid(array [] of {2,5,29,9}),
+ id_ce_subjectKeyIdentifier => Oid(array [] of {2,5,29,14}),
+ id_ce_keyUsage => Oid(array [] of {2,5,29,15}),
+ id_ce_privateKeyUsage => Oid(array [] of {2,5,29,16}),
+ id_ce_subjectAltName => Oid(array [] of {2,5,29,17}),
+ id_ce_issuerAltName => Oid(array [] of {2,5,29,18}),
+ id_ce_basicConstraints => Oid(array [] of {2,5,29,19}),
+ id_ce_cRLNumber => Oid(array [] of {2,5,29,20}),
+ id_ce_reasonCode => Oid(array [] of {2,5,29,21}),
+ id_ce_instructionCode => Oid(array [] of {2,5,29,23}),
+ id_ce_invalidityDate => Oid(array [] of {2,5,29,24}),
+ id_ce_deltaCRLIndicator => Oid(array [] of {2,5,29,27}),
+ id_ce_issuingDistributionPoint =>
+ Oid(array [] of {2,5,29,28}),
+ id_ce_certificateIssuer => Oid(array [] of {2,5,29,29}),
+ id_ce_nameConstraints => Oid(array [] of {2,5,29,30}),
+ id_ce_cRLDistributionPoint => Oid(array [] of {2,5,29,31}),
+ id_ce_certificatePolicies => Oid(array [] of {2,5,29,32}),
+ id_ce_policyMapping => Oid(array [] of {2,5,29,33}),
+ id_ce_authorityKeyIdentifier =>
+ Oid(array [] of {2,5,29,35}),
+ id_ce_policyConstraints => Oid(array [] of {2,5,29,36}),
+
+# id_mr => Oid(array [] of {2,5,?}),
+# id_mr_certificateMatch => Oid(array [] of {2,5,?,35}),
+# id_mr_certificatePairExactMatch =>
+# Oid(array [] of {2,5,?,36}),
+# id_mr_certificatePairMatch => Oid(array [] of {2,5,?,37}),
+# id_mr_certificateListExactMatch =>
+# Oid(array [] of {2,5,?,38}),
+# id_mr_certificateListMatch => Oid(array [] of {2,5,?,39}),
+# id_mr_algorithmidentifierMatch =>
+# Oid(array [] of {2,5,?,40})
+};
+
+# [public]
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+
+ if(X509_DEBUG)
+ logfd = sys->fildes(1);
+
+ keyring = load Keyring Keyring->PATH;
+ if(keyring == nil)
+ return sys->sprint("load %s: %r", Keyring->PATH);
+
+ random = load Random Random->PATH;
+ if(random == nil)
+ return sys->sprint("load %s: %r", Random->PATH);
+
+ daytime = load Daytime Daytime->PATH;
+ if(daytime == nil)
+ return sys->sprint("load %s: %r", Daytime->PATH);
+
+ asn1 = load ASN1 ASN1->PATH;
+ if(asn1 == nil)
+ return sys->sprint("load %s: %r", ASN1->PATH);
+ asn1->init();
+
+ pkcs = load PKCS PKCS->PATH;
+ if(pkcs == nil)
+ return sys->sprint("load %s: %r", PKCS->PATH);
+ if((e := pkcs->init()) != nil)
+ return sys->sprint("pkcs: %s", e);
+
+ return nil;
+}
+
+# [private]
+
+log(s: string)
+{
+ if(X509_DEBUG)
+ sys->fprint(logfd, "x509: %s\n", s);
+}
+
+## SIGNED { ToBeSigned } ::= SEQUENCE {
+## toBeSigned ToBeSigned,
+## COMPONENTS OF SIGNATURE { ToBeSigned }}
+##
+## SIGNATURE {OfSignature} ::= SEQUENCE {
+## algorithmIdentifier AlgorithmIdentifier,
+## encrypted ENCRYPTED { HASHED { OfSignature }}}
+##
+## ENCRYPTED { ToBeEnciphered } ::= BIT STRING ( CONSTRAINED BY {
+## -- must be the result of applying an encipherment procedure --
+## -- to the BER-encoded octets of a value of -- ToBeEnciphered } )
+
+# [public]
+
+Signed.decode(a: array of byte): (string, ref Signed)
+{
+parse:
+ for(;;) {
+ # interpret the enclosing structure
+ (ok, tag, i, n) := der_dec1(a, 0, len a);
+ if(!ok || n != len a || !tag.constr ||
+ tag.class != Universal || tag.num != SEQUENCE)
+ break parse;
+ s := ref Signed;
+ # SIGNED sequence
+ istart := i;
+ (ok, tag, i, n) = der_dec1(a, i, len a);
+ if(!ok || n == len a)
+ break parse;
+ s.tobe_signed = a[istart:n];
+ # AlgIdentifier
+ istart = n;
+ (ok, tag, i, n) = der_dec1(a, n, len a);
+ if(!ok || n == len a
+ || !tag.constr || tag.class != Universal || tag.num != SEQUENCE) {
+ if(X509_DEBUG)
+ log("signed: der data: " +
+ sys->sprint("ok=%d, n=%d, constr=%d, class=%d, num=%d",
+ ok, n, tag.constr, tag.class, tag.num));
+ break parse;
+ }
+ (ok, s.alg) = decode_algid(a[istart:n]);
+ if(!ok) {
+ if(X509_DEBUG)
+ log("signed: alg identifier: syntax error");
+ break;
+ }
+ # signature
+ (ok, tag, i, n) = der_dec1(a, n, len a);
+ if(!ok || n != len a
+ || tag.constr || tag.class != Universal || tag.num != BIT_STRING) {
+ if(X509_DEBUG)
+ log("signed: signature: " +
+ sys->sprint("ok=%d, n=%d, constr=%d, class=%d, num=%d",
+ ok, n, tag.constr, tag.class, tag.num));
+ break parse;
+ }
+ s.signature = a[i:n];
+ # to the end of no error been found
+ return ("", s);
+ }
+ return ("signed: syntax error", nil);
+}
+
+# [public]
+# der encoding of signed data
+
+Signed.encode(s: self ref Signed): (string, array of byte)
+{
+ (err, e_dat) := asn1->decode(s.tobe_signed); # why?
+ if(err != "")
+ return (err, nil);
+ e_alg := pack_alg(s.alg);
+ e_sig := ref Elem(
+ Tag(Universal, BIT_STRING, 0),
+ ref Value.BitString(0,s.signature) # DER encode of BIT STRING
+ );
+ all := ref Elem(
+ Tag(Universal, SEQUENCE, 1),
+ ref Value.Seq(e_dat::e_alg::e_sig::nil)
+ );
+ return asn1->encode(all);
+}
+
+# [public]
+
+Signed.sign(s: self ref Signed, sk: ref PrivateKey, hash: int): (string, array of byte)
+{
+ # we require tobe_signed has 0 bits of padding
+ if(int s.tobe_signed[0] != 0)
+ return ("syntax error", nil);
+ pick key := sk {
+ RSA =>
+ (err, signature) := pkcs->rsa_sign(s.tobe_signed, key.sk, hash);
+ s.signature = signature;
+ # TODO: add AlgIdentifier based on public key and hash
+ return (err, signature);
+ DSS =>
+ # TODO: hash s.tobe_signed for signing
+ (err, signature) := pkcs->dss_sign(s.tobe_signed, key.sk);
+ s.signature = signature;
+ return (err, signature);
+ DH =>
+ return ("cannot sign using DH algorithm", nil);
+ }
+ return ("sign: failed", nil);
+}
+
+# [public]
+# hash algorithm should be MD2, MD4, MD5 or SHA
+
+Signed.verify(s: self ref Signed, pk: ref PublicKey, hash: int): int
+{
+ ok := 0;
+
+ pick key := pk {
+ RSA =>
+ ok = pkcs->rsa_verify(s.tobe_signed, s.signature, key.pk, hash);
+ DSS =>
+ # TODO: hash s.tobe_signed for verifying
+ ok = pkcs->dss_verify(s.tobe_signed, s.signature, key.pk);
+ DH =>
+ # simply failure
+ }
+
+ return ok;
+}
+
+# [public]
+
+Signed.tostring(s: self ref Signed): string
+{
+ str := "Signed";
+
+ str += "\nToBeSigned: " + bastr(s.tobe_signed);
+ str += "\nAlgorithm: " + s.alg.tostring();
+ str += "\nSignature: " + bastr(s.signature);
+
+ return str + "\n";
+}
+
+# DER
+# a) the definite form of length encoding shall be used, encoded in the minimum number of
+# octets;
+# b) for string types, the constructed form of encoding shall not be used;
+# c) if the value of a type is its default value, it shall be absent;
+# d) the components of a Set type shall be encoded in ascending order of their tag value;
+# e) the components of a Set-of type shall be encoded in ascending order of their octet value;
+# f) if the value of a Boolean type is true, the encoding shall have its contents octet
+# set to "FF"16;
+# g) each unused bits in the final octet of the encoding of a Bit String value, if there are
+# any, shall be set to zero;
+# h) the encoding of a Real type shall be such that bases 8, 10, and 16 shall not be used,
+# and the binary scaling factor shall be zero.
+
+# [private]
+# decode ASN1 one record at a time and return (err, tag, start of data,
+# end of data) for indefinite length, the end of data is same as 'n'
+
+der_dec1(a: array of byte, i, n: int): (int, Tag, int, int)
+{
+ length: int;
+ tag: Tag;
+ ok := 1;
+ (ok, i, tag) = der_dectag(a, i, n);
+ if(ok) {
+ (ok, i, length) = der_declen(a, i, n);
+ if(ok) {
+ if(length == -1) {
+ if(!tag.constr)
+ ok = 0;
+ length = n - i;
+ }
+ else {
+ if(i+length > n)
+ ok = 0;
+ }
+ }
+ }
+ if(!ok && X509_DEBUG)
+ log("der_dec1: syntax error");
+ return (ok, tag, i, i+length);
+}
+
+# [private]
+# der tag decode
+
+der_dectag(a: array of byte, i, n: int): (int, int, Tag)
+{
+ ok := 1;
+ class, num, constr: int;
+ if(n-i >= 2) {
+ v := int a[i++];
+ class = v & CLASS_MASK;
+ if(v & CONSTR_MASK)
+ constr = 1;
+ else
+ constr = 0;
+ num = v & TAG_MASK;
+ if(num == TAG_MASK)
+ # long tag number
+ (ok, i, num) = uint7_decode(a, i, n);
+ }
+ else
+ ok = 0;
+ if(!ok && X509_DEBUG)
+ log("der_declen: syntax error");
+ return (ok, i, Tag(class, num, constr));
+}
+
+# [private]
+
+int_decode(a: array of byte, i, n, count, unsigned: int): (int, int, int)
+{
+ ok := 1;
+ num := 0;
+ if(n-i >= count) {
+ if((count > 4) || (unsigned && count == 4 && (int a[i] & 16r80)))
+ ok = 1;
+ else {
+ if(!unsigned && count > 0 && count < 4 && (int a[i] & 16r80))
+ num = -1; # all bits set
+ for(j := 0; j < count; j++) {
+ v := int a[i++];
+ num = (num << 8) | v;
+ }
+ }
+ }
+ else
+ ok = 0;
+ if(!ok && X509_DEBUG)
+ log("int_decode: syntax error");
+ return (ok, i, num);
+}
+
+
+# [private]
+
+uint7_decode(a: array of byte, i, n: int) : (int, int, int)
+{
+ ok := 1;
+ num := 0;
+ more := 1;
+ while(more && i < n) {
+ v := int a[i++];
+ if(num & 16r7F000000) {
+ ok = 0;
+ break;
+ }
+ num <<= 7;
+ more = v & 16r80;
+ num |= (v & 16r7F);
+ }
+ if(n == i)
+ ok = 0;
+ if(!ok && X509_DEBUG)
+ log("uint7_decode: syntax error");
+ return (ok, i, num);
+}
+
+
+# [private]
+# der length decode - the definite form of length encoding shall be used, encoded
+# in the minimum number of octets
+
+der_declen(a: array of byte, i, n: int): (int, int, int)
+{
+ ok := 1;
+ num := 0;
+ if(i < n) {
+ v := int a[i++];
+ if(v & 16r80)
+ return int_decode(a, i, n, v&16r7F, 1);
+ else if(v == 16r80) # indefinite length
+ ok = 0;
+ else
+ num = v;
+ }
+ else
+ ok = 0;
+ if(!ok && X509_DEBUG)
+ log("der_declen: syntax error");
+ return (ok, i, num);
+}
+
+# [private]
+# parse der encoded algorithm identifier
+
+decode_algid(a: array of byte): (int, ref AlgIdentifier)
+{
+ (err, el) := asn1->decode(a);
+ if(err != "") {
+ if(X509_DEBUG)
+ log("decode_algid: " + err);
+ return (0, nil);
+ }
+ return parse_alg(el);
+}
+
+
+## TBS (Public Key) Certificate is signed by Certificate Authority and contains
+## information of public key usage (as a comparison of Certificate Revocation List
+## and Attribute Certificate).
+
+# [public]
+# constructs a certificate by parsing a der encoded certificate
+# returns error if parsing is failed or nil if parsing is ok
+
+certsyn(s: string): (string, ref Certificate)
+{
+ if(0)
+ sys->fprint(sys->fildes(2), "cert: %s\n", s);
+ return ("certificate syntax: "+s, nil);
+}
+
+# Certificate ::= SEQUENCE {
+# certificateInfo CertificateInfo,
+# signatureAlgorithm AlgorithmIdentifier,
+# signature BIT STRING }
+#
+# CertificateInfo ::= SEQUENCE {
+# version [0] INTEGER DEFAULT v1 (0),
+# serialNumber INTEGER,
+# signature AlgorithmIdentifier,
+# issuer Name,
+# validity Validity,
+# subject Name,
+# subjectPublicKeyInfo SubjectPublicKeyInfo }
+# (version v2 has two more fields, optional unique identifiers for
+# issuer and subject; since we ignore these anyway, we won't parse them)
+#
+# Validity ::= SEQUENCE {
+# notBefore UTCTime,
+# notAfter UTCTime }
+#
+# SubjectPublicKeyInfo ::= SEQUENCE {
+# algorithm AlgorithmIdentifier,
+# subjectPublicKey BIT STRING }
+#
+# AlgorithmIdentifier ::= SEQUENCE {
+# algorithm OBJECT IDENTIFER,
+# parameters ANY DEFINED BY ALGORITHM OPTIONAL }
+#
+# Name ::= SEQUENCE OF RelativeDistinguishedName
+#
+# RelativeDistinguishedName ::= SETOF SIZE(1..MAX) OF AttributeTypeAndValue
+#
+# AttributeTypeAndValue ::= SEQUENCE {
+# type OBJECT IDENTIFER,
+# value DirectoryString }
+# (selected attributes have these Object Ids:
+# commonName {2 5 4 3}
+# countryName {2 5 4 6}
+# localityName {2 5 4 7}
+# stateOrProvinceName {2 5 4 8}
+# organizationName {2 5 4 10}
+# organizationalUnitName {2 5 4 11}
+# )
+#
+# DirectoryString ::= CHOICE {
+# teletexString TeletexString,
+# printableString PrintableString,
+# universalString UniversalString }
+#
+# See rfc1423, rfc2437 for AlgorithmIdentifier, subjectPublicKeyInfo, signature.
+
+Certificate.decode(a: array of byte): (string, ref Certificate)
+{
+parse:
+ # break on error
+ for(;;) {
+ (err, all) := asn1->decode(a);
+ if(err != "")
+ return certsyn(err);
+ c := ref Certificate;
+
+ # certificate must be a ASN1 sequence
+ (ok, el) := all.is_seq();
+ if(!ok)
+ return certsyn("invalid certificate sequence");
+
+ if(len el == 3){ # ssl3.b uses CertificateInfo; others use Certificate (TO DO: fix this botch)
+ certinfo := hd el;
+ sigalgid := hd tl el;
+ sigbits := hd tl tl el;
+
+ # certificate info is another ASN1 sequence
+ (ok, el) = certinfo.is_seq();
+ if(!ok || len el < 6)
+ return certsyn("invalid certificate info sequence");
+ }
+
+ c.version = 0; # set to default (v1)
+ (ok, c.version) = parse_version(hd el);
+ if(!ok)
+ return certsyn("can't parse version");
+ if(c.version > 0) {
+ el = tl el;
+ if(len el < 6)
+ break parse;
+ }
+ # serial number
+ (ok, c.serial_number) = parse_sernum(hd el);
+ if(!ok)
+ return certsyn("can't parse serial number");
+ el = tl el;
+ # signature algorithm
+ (ok, c.sig) = parse_alg(hd el);
+ if(!ok)
+ return certsyn("can't parse sigalg");
+ el = tl el;
+ # issuer
+ (ok, c.issuer) = parse_name(hd el);
+ if(!ok)
+ return certsyn("can't parse issuer");
+ el = tl el;
+ # validity
+ evalidity := hd el;
+ (ok, c.validity) = parse_validity(evalidity);
+ if(!ok)
+ return certsyn("can't parse validity");
+ el = tl el;
+ # Subject
+ (ok, c.subject) = parse_name(hd el);
+ if(!ok)
+ return certsyn("can't parse subject");
+ el = tl el;
+ # SubjectPublicKeyInfo
+ (ok, c.subject_pkinfo) = parse_pkinfo(hd el);
+ if(!ok)
+ return certsyn("can't parse subject pk info");
+ el = tl el;
+ # OPTIONAL for v2 and v3, must be in order
+ # issuer unique identifier
+ if(c.version == 0 && el != nil)
+ return certsyn("bad unique ID");
+ if(el != nil) {
+ if(c.version < 1) # at least v2
+ return certsyn("invalid v1 cert");
+ (ok, c.issuer_uid) = parse_uid(hd el, 1);
+ if(ok)
+ el = tl el;
+ }
+ # subject unique identifier
+ if(el != nil) {
+ if(c.version < 1) # v2 or v3
+ return certsyn("invalid v1 cert");
+ (ok, c.issuer_uid) = parse_uid(hd el, 2);
+ if(ok)
+ el = tl el;
+ }
+ # extensions
+ if(el != nil) {
+ if(c.version < 2) # must be v3
+ return certsyn("invalid v1/v2 cert");
+ e : ref Elem;
+ (ok, e) = is_context(hd el, 3);
+ if (!ok)
+ break parse;
+ (ok, c.exts) = parse_extlist(e);
+ if(!ok)
+ return certsyn("can't parse extension list");
+ el = tl el;
+ }
+ # must be no more left
+ if(el != nil)
+ return certsyn("unexpected data at end");
+ return ("", c);
+ }
+
+ return ("certificate: syntax error", nil);
+}
+
+# [public]
+# a der encoding of certificate data; returns (error, nil) tuple in failure
+
+Certificate.encode(c: self ref Certificate): (string, array of byte)
+{
+pack:
+ for(;;) {
+ el: list of ref Elem;
+ # always has a version packed
+ e_version := pack_version(c.version);
+ if(e_version == nil)
+ break pack;
+ el = e_version :: el;
+ # serial number
+ e_sernum := pack_sernum(c.serial_number);
+ if(e_sernum == nil)
+ break pack;
+ el = e_sernum :: el;
+ # algorithm
+ e_sig := pack_alg(c.sig);
+ if(e_sig == nil)
+ break pack;
+ el = e_sig :: el;
+ # issuer
+ e_issuer := pack_name(c.issuer);
+ if(e_issuer == nil)
+ break pack;
+ el = e_issuer :: el;
+ # validity
+ e_validity := pack_validity(c.validity);
+ if(e_validity == nil)
+ break pack;
+ el = e_validity :: el;
+ # subject
+ e_subject := pack_name(c.subject);
+ if(e_subject == nil)
+ break pack;
+ el = e_subject :: el;
+ # public key info
+ e_pkinfo := pack_pkinfo(c.subject_pkinfo);
+ if(e_pkinfo == nil)
+ break pack;
+ el = e_pkinfo :: el;
+ # issuer unique identifier
+ if(c.issuer_uid != nil) {
+ e_issuer_uid := pack_uid(c.issuer_uid);
+ if(e_issuer_uid == nil)
+ break pack;
+ el = e_issuer_uid :: el;
+ }
+ # subject unique identifier
+ if(c.subject_uid != nil) {
+ e_subject_uid := pack_uid(c.subject_uid);
+ if(e_subject_uid == nil)
+ break pack;
+ el = e_subject_uid :: el;
+ }
+ # extensions
+ if(c.exts != nil) {
+ e_exts := pack_extlist(c.exts);
+ if(e_exts == nil)
+ break pack;
+ el = e_exts :: el;
+ }
+ # SEQUENCE order is important
+ lseq: list of ref Elem;
+ while(el != nil) {
+ lseq = (hd el) :: lseq;
+ el = tl el;
+ }
+ all := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(lseq));
+ return asn1->encode(all);
+ }
+ return ("incompleted certificate; unable to pack", nil);
+}
+
+# [public]
+# converts content of a certificate as visible string
+
+Certificate.tostring(c: self ref Certificate): string
+{
+ s := "\tTBS Certificate";
+ s += "\n\tVersion:\n\t\t" + string c.version;
+ s += "\n\tSerialNumber:\n\t\t" + c.serial_number.iptostr(10);
+ s += "\n\tSignature: " + c.sig.tostring();
+ s += "\n\tIssuer: " + c.issuer.tostring();
+ s += "\n\tValidity: " + c.validity.tostring("local");
+ s += "\n\tSubject: " + c.subject.tostring();
+ s += "\n\tSubjectPKInfo: " + c.subject_pkinfo.tostring();
+ s += "\n\tIssuerUID: " + bastr(c.issuer_uid);
+ s += "\n\tSubjectUID: " + bastr(c.subject_uid);
+ s += "\n\tExtensions: ";
+ exts := c.exts;
+ while(exts != nil) {
+ s += "\t\t" + (hd exts).tostring();
+ exts = tl exts;
+ }
+ return s;
+}
+
+# [public]
+
+Certificate.is_expired(c: self ref Certificate, date: int): int
+{
+ if(date > c.validity.not_after || date < c.validity.not_before)
+ return 1;
+
+ return 0;
+}
+
+
+# [private]
+# version is optional marked by explicit context tag 0; no version is
+# required if default version (v1) is used
+
+parse_version(e: ref Elem): (int, int)
+{
+ ver := 0;
+ (ans, ec) := is_context(e, 0);
+ if(ans) {
+ ok := 0;
+ (ok, ver) = ec.is_int();
+ if(!ok || ver < 0 || ver > 2)
+ return (0, -1);
+ }
+ return (1, ver);
+}
+
+# [private]
+
+pack_version(v: int): ref Elem
+{
+ return ref Elem(Tag(Universal, INTEGER, 0), ref Value.Int(v));
+}
+
+# [private]
+
+parse_sernum(e: ref Elem): (int, ref IPint)
+{
+ (ok, a) := e.is_bigint();
+ if(ok)
+ return (1, IPint.bebytestoip(a));
+ if(X509_DEBUG)
+ log("parse_sernum: syntax error");
+ return (0, nil);
+}
+
+# [private]
+
+pack_sernum(sn: ref IPint): ref Elem
+{
+ return ref Elem(Tag(Universal, INTEGER, 0), ref Value.BigInt(sn.iptobebytes()));
+}
+
+# [private]
+
+parse_alg(e: ref Elem): (int, ref AlgIdentifier)
+{
+parse:
+ for(;;) {
+ (ok, el) := e.is_seq();
+ if(!ok || el == nil)
+ break parse;
+ oid: ref Oid;
+ (ok, oid) = (hd el).is_oid();
+ if(!ok)
+ break parse;
+ el = tl el;
+ params: array of byte;
+ if(el != nil) {
+ # TODO: determine the object type based on oid
+ # then parse params
+ #unused: int;
+ #(ok, unused, params) = (hd el).is_bitstring();
+ #if(!ok || unused || tl el != nil)
+ # break parse;
+ }
+ return (1, ref AlgIdentifier(oid, params));
+ }
+ if(X509_DEBUG)
+ log("parse_alg: syntax error");
+ return (0, nil);
+}
+
+# [private]
+
+pack_alg(a: ref AlgIdentifier): ref Elem
+{
+ if(a.oid != nil) {
+ el: list of ref Elem;
+ el = ref Elem(Tag(Universal, ASN1->OBJECT_ID, 0), ref Value.ObjId(a.oid)) :: nil;
+ if(a.parameter != nil) {
+ el = ref Elem(
+ Tag(Universal, BIT_STRING, 0),
+ ref Value.BitString(0, a.parameter)
+ ) :: el;
+ }
+ return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ }
+ return nil;
+}
+
+# [private]
+
+parse_name(e: ref Elem): (int, ref Name)
+{
+parse:
+ for(;;) {
+ (ok, el) := e.is_seq();
+ if(!ok)
+ break parse;
+ lrd: list of ref RDName;
+ while(el != nil) {
+ rd: ref RDName;
+ (ok, rd) = parse_rdname(hd el);
+ if(!ok)
+ break parse;
+ lrd = rd :: lrd;
+ el = tl el;
+ }
+ # SEQUENCE
+ l: list of ref RDName;
+ while(lrd != nil) {
+ l = (hd lrd) :: l;
+ lrd = tl lrd;
+ }
+ return (1, ref Name(l));
+ }
+ if(X509_DEBUG)
+ log("parse_name: syntax error");
+ return (0, nil);
+}
+
+# [private]
+
+pack_name(n: ref Name): ref Elem
+{
+ el: list of ref Elem;
+
+ lrd := n.rd_names;
+ while(lrd != nil) {
+ rd := pack_rdname(hd lrd);
+ if(rd == nil)
+ return nil;
+ el = rd :: el;
+ lrd = tl lrd;
+ }
+ # reverse order
+ l: list of ref Elem;
+ while(el != nil) {
+ l = (hd el) :: l;
+ el = tl el;
+ }
+
+ return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(l));
+}
+
+# [private]
+
+parse_rdname(e: ref Elem): (int, ref RDName)
+{
+parse:
+ for(;;) {
+ (ok, el) := e.is_set(); # unordered
+ if(!ok)
+ break parse;
+ lava: list of ref AVA;
+ while(el != nil) {
+ ava: ref AVA;
+ (ok, ava) = parse_ava(hd el);
+ if(!ok)
+ break parse;
+ lava = ava :: lava;
+ el = tl el;
+ }
+ return (1, ref RDName(lava));
+ }
+ if(X509_DEBUG)
+ log("parse_rdname: syntax error");
+ return (0, nil);
+}
+
+# [private]
+
+pack_rdname(r: ref RDName): ref Elem
+{
+ el: list of ref Elem;
+ lava := r.avas;
+ while(lava != nil) {
+ ava := pack_ava(hd lava);
+ if(ava == nil)
+ return nil;
+ el = ava :: el;
+ lava = tl lava;
+ }
+ return ref Elem(Tag(Universal, ASN1->SET, 1), ref Value.Set(el));
+}
+
+# [private]
+
+parse_ava(e: ref Elem): (int, ref AVA)
+{
+parse:
+ for(;;) {
+ (ok, el) := e.is_seq();
+ if(!ok || len el != 2)
+ break parse;
+ a := ref AVA;
+ (ok, a.oid) = (hd el).is_oid();
+ if(!ok)
+ break parse;
+ el = tl el;
+ (ok, a.value) = (hd el).is_string();
+ if(!ok)
+ break parse;
+ return (1, a);
+ }
+ if(X509_DEBUG)
+ log("parse_ava: syntax error");
+ return (0, nil);
+}
+
+# [private]
+
+pack_ava(a: ref AVA): ref Elem
+{
+ el: list of ref Elem;
+ if(a.oid == nil || a.value == "")
+ return nil;
+ # Note: order is important
+ el = ref Elem(Tag(Universal, ASN1->GeneralString, 0), ref Value.String(a.value)) :: el;
+ el = ref Elem(Tag(Universal, ASN1->OBJECT_ID, 0), ref Value.ObjId(a.oid)) :: el;
+ return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+}
+
+# [private]
+
+parse_validity(e: ref Elem): (int, ref Validity)
+{
+parse:
+ for(;;) {
+ (ok, el) := e.is_seq();
+ if(!ok || len el != 2)
+ break parse;
+ v := ref Validity;
+ (ok, v.not_before) = parse_time(hd el, UTCTime);
+ if(!ok)
+ break parse;
+ el = tl el;
+ (ok, v.not_after) = parse_time(hd el, UTCTime);
+ if(!ok)
+ break parse;
+ return (1, v);
+ }
+ if(X509_DEBUG)
+ log("parse_validity: syntax error");
+ return (0, nil);
+}
+
+# [private]
+# standard says only UTC Time allowed for TBS Certificate, but there is exception of
+# GeneralizedTime for CRL and Attribute Certificate. Parsing is based on format of
+# UTCTime, GeneralizedTime or undetermined (any int not UTCTime or GeneralizedTime).
+
+parse_time(e: ref Elem, format: int): (int, int)
+{
+parse:
+ for(;;) {
+ (ok, date) := e.is_time();
+ if(!ok)
+ break parse;
+ if(e.tag.num != UTCTime && e.tag.num != GeneralizedTime)
+ break parse;
+ if(format == UTCTime && e.tag.num != UTCTime)
+ break parse;
+ if(format == GeneralizedTime && e.tag.num != GeneralizedTime)
+ break parse;
+ t := decode_time(date, e.tag.num);
+ if(t < 0)
+ break parse;
+ return (1, t);
+ }
+ if(X509_DEBUG)
+ log("parse_time: syntax error");
+ return (0, -1);
+}
+
+# [private]
+# decode a BER encoded UTC or Generalized time into epoch (seconds since 1/1/1970 GMT)
+# UTC time format: YYMMDDhhmm[ss](Z|(+|-)hhmm)
+# Generalized time format: YYYYMMDDhhmm[ss.s(...)](Z|(+|-)hhmm[ss.s(...))
+
+decode_time(date: string, format: int): int
+{
+ time := ref Daytime->Tm;
+parse:
+ for(;;) {
+ i := 0;
+ if(format == UTCTime) {
+ if(len date < 11)
+ break parse;
+ time.year = get2(date, i);
+ if(time.year < 0)
+ break parse;
+ if(time.year < 70)
+ time.year += 100;
+ i += 2;
+ }
+ else {
+ if(len date < 13)
+ break parse;
+ time.year = get2(date, i);
+ if(time.year-19 < 0)
+ break parse;
+ time.year = (time.year - 19)*100;
+ i += 2;
+ time.year += get2(date, i);
+ i += 2;
+ }
+ time.mon = get2(date, i) - 1;
+ if(time.mon < 0 || time.mon > 11)
+ break parse;
+ i += 2;
+ time.mday = get2(date, i);
+ if(time.mday < 1 || time.mday > 31)
+ break parse;
+ i += 2;
+ time.hour = get2(date, i);
+ if(time.hour < 0 || time.hour > 23)
+ break parse;
+ i += 2;
+ time.min = get2(date, i);
+ if(time.min < 0 || time.min > 59)
+ break parse;
+ i += 2;
+ if(int date[i] >= '0' && int date[i] <= '9') {
+ if(len date < i+3)
+ break parse;
+ time.sec = get2(date, i);
+ if(time.sec < 0 || time.sec > 59)
+ break parse;
+ i += 2;
+ if(format == GeneralizedTime) {
+ if((len date < i+3) || int date[i++] != '.')
+ break parse;
+ # ignore rest
+ ig := int date[i];
+ while(ig >= '0' && ig <= '9' && i++ < len date) {
+ ig = int date[i];
+ }
+ }
+ }
+ else {
+ time.sec = 0;
+ }
+ zf := int date[i];
+ if(zf != 'Z' && zf != '+' && zf != '-')
+ break parse;
+ if(zf == 'Z') {
+ if(len date != i+1)
+ break parse;
+ time.tzoff = 0;
+ }
+ else {
+ if(len date < i + 3)
+ break parse;
+ time.tzoff = get2(date, i+1);
+ if(time.tzoff < 0 || time.tzoff > 23)
+ break parse;
+ i += 2;
+ min := get2(date, i);
+ if(min < 0 || min > 59)
+ break parse;
+ i += 2;
+ sec := 0;
+ if(i != len date) {
+ if(format == UTCTime || len date < i+4)
+ break parse;
+ sec = get2(date, i);
+ i += 2;
+ # ignore the rest
+ }
+ time.tzoff = (time.tzoff*60 + min)*60 + sec;
+ if(zf == '-')
+ time.tzoff = -time.tzoff;
+ }
+ return daytime->tm2epoch(time);
+ }
+ if(X509_DEBUG)
+ log("decode_time: syntax error: " +
+ sys->sprint("year=%d mon=%d mday=%d hour=%d min=%d, sec=%d",
+ time.year, time.mon, time.mday, time.hour, time.min, time.sec));
+ return -1;
+}
+
+# [private]
+# pack as UTC time
+
+pack_validity(v: ref Validity): ref Elem
+{
+ el: list of ref Elem;
+ el = ref Elem(
+ Tag(Universal, UTCTime, 0),
+ ref Value.String(pack_time(v.not_before, UTCTime))
+ ) :: nil;
+ el = ref Elem(
+ Tag(Universal, UTCTime, 0),
+ ref Value.String(pack_time(v.not_after, UTCTime))
+ ) :: el;
+ return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+}
+
+# [private]
+# Format must be either UTCTime or GeneralizedTime
+# TODO: convert to coordinate time
+
+pack_time(t: int, format: int): string
+{
+ date := array [32] of byte;
+ tm := daytime->gmt(t);
+
+ i := 0;
+ if(format == UTCTime) {
+ i = put2(date, tm.year, i);
+ }
+ else { # GeneralizedTime
+ i = put2(date, 19 + tm.year/100, i);
+ i = put2(date, tm.year%100, i);
+ }
+ i = put2(date, tm.mon, i);
+ i = put2(date, tm.mday, i);
+ i = put2(date, tm.hour, i);
+ i = put2(date, tm.min, i);
+ if(tm.sec != 0) {
+ if(format == UTCTime)
+ i = put2(date, tm.sec, i);
+ else {
+ i = put2(date, tm.sec, i);
+ date[i++] = byte '.';
+ date[i++] = byte 0;
+ }
+ }
+ if(tm.tzoff == 0) {
+ date[i++] = byte 'Z';
+ }
+ else {
+ off := tm.tzoff;
+ if(tm.tzoff < 0) {
+ off = -off;
+ date[i++] = byte '-';
+ }
+ else {
+ date[i++] = byte '+';
+ }
+ hoff := int (off/3600);
+ moff := int ((off%3600)/60);
+ soff := int ((off%3600)%60);
+ i = put2(date, hoff, i);
+ i = put2(date, moff, i);
+ if(soff) {
+ if(format == UTCTime)
+ i = put2(date, soff, i);
+ else {
+ i = put2(date, soff, i);
+ date[i++] = byte '.';
+ date[i++] = byte 0;
+ }
+ }
+ }
+ return string date[0:i];
+}
+
+# [private]
+
+parse_pkinfo(e: ref Elem): (int, ref SubjectPKInfo)
+{
+parse:
+ for(;;) {
+ p := ref SubjectPKInfo;
+ (ok, el) := e.is_seq();
+ if(!ok || len el != 2)
+ break parse;
+ (ok, p.alg_id) = parse_alg(hd el);
+ if(!ok)
+ break parse;
+ unused: int;
+ (ok, unused, p.subject_pk) = (hd tl el).is_bitstring();
+ if(!ok || unused != 0)
+ break parse;
+ return (1, p);
+ }
+ if(X509_DEBUG)
+ log("parse_pkinfo: syntax error");
+ return (0, nil);
+}
+
+# [private]
+
+pack_pkinfo(p: ref SubjectPKInfo): ref Elem
+{
+ el: list of ref Elem;
+ # SEQUENCE order is important
+ el = ref Elem(
+ Tag(Universal, BIT_STRING, 0),
+ ref Value.BitString(0, p.subject_pk) # 0 bits unused ?
+ ) :: nil;
+ el = pack_alg(p.alg_id) :: el;
+ return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+}
+
+# [private]
+
+parse_uid(e: ref Elem, num: int): (int, array of byte)
+{
+ ok, unused : int;
+ uid : array of byte;
+ e2 : ref Elem;
+parse:
+ for(;;) {
+ (ok, e2) = is_context(e, num);
+ if (!ok)
+ break parse;
+ e = e2;
+
+ (ok, unused, uid) = e.is_bitstring();
+# if(!ok || unused != 0)
+ if(!ok)
+ break parse;
+ return (1, uid);
+ }
+ if(X509_DEBUG)
+ log("parse_uid: syntax error");
+ return (0, nil);
+}
+
+# [private]
+
+pack_uid(u: array of byte): ref Elem
+{
+ return ref Elem(Tag(Universal, ASN1->BIT_STRING, 0), ref Value.BitString(0,u));
+}
+
+# [private]
+
+parse_extlist(e: ref Elem): (int, list of ref Extension)
+{
+parse:
+ # dummy loop for breaking out of
+ for(;;) {
+ l: list of ref Extension;
+ (ok, el) := e.is_seq();
+ if(!ok)
+ break parse;
+ while(el != nil) {
+ ext := ref Extension;
+ (ok, ext) = parse_extension(hd el);
+ if(!ok)
+ break parse;
+ l = ext :: l;
+ el = tl el;
+ }
+ # sort to order
+ nl: list of ref Extension;
+ while(l != nil) {
+ nl = (hd l) :: nl;
+ l = tl l;
+ }
+ return (1, nl);
+ }
+ if(X509_DEBUG)
+ log("parse_extlist: syntax error");
+ return (0, nil);
+}
+
+# [private]
+
+pack_extlist(e: list of ref Extension): ref Elem
+{
+ el: list of ref Elem;
+ exts := e;
+ while(exts != nil) {
+ ext := pack_extension(hd exts);
+ if(ext == nil)
+ return nil;
+ el = ext :: el;
+ exts = tl exts;
+ }
+ # reverse order
+ l: list of ref Elem;
+ while(el != nil) {
+ l = (hd el) :: l;
+ el = tl el;
+ }
+ return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(l));
+}
+
+# [private]
+# Require further parse to check oid if critical is set to TRUE (see parse_exts)
+
+parse_extension(e: ref Elem): (int, ref Extension)
+{
+parse:
+ for(;;) {
+ ext := ref Extension;
+ (ok, el) := e.is_seq();
+ if(!ok)
+ break parse;
+ oid: ref Oid;
+ (ok, oid) = (hd el).is_oid();
+ if(!ok)
+ break parse;
+ ext.oid = oid;
+ el = tl el;
+ # BOOLEAN DEFAULT FALSE
+ (ok, ext.critical) = (hd el).is_int();
+ if(ok)
+ el = tl el;
+ else
+ ext.critical = 0;
+ if (len el != 1) {
+ break parse;
+ }
+ (ok, ext.value) = (hd el).is_octetstring();
+ if(!ok)
+ break parse;
+ return (1, ext);
+ }
+ if(X509_DEBUG)
+ log("parse_extension: syntax error");
+ return (0, nil);
+}
+
+# [private]
+
+pack_extension(e: ref Extension): ref Elem
+{
+ el: list of ref Elem;
+
+ if(e.oid == nil || (e.critical !=0 && e.critical != 1) || e.value == nil)
+ return nil;
+ # SEQUENCE order
+ el = ref Elem(Tag(Universal, OCTET_STRING, 0), ref Value.Octets(e.value)) :: el;
+ el = ref Elem(Tag(Universal, BOOLEAN, 0), ref Value.Bool(e.critical)) :: el;
+ el = ref Elem(Tag(Universal, OBJECT_ID, 0), ref Value.ObjId(e.oid)) :: el;
+ return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+}
+
+# [public]
+
+AlgIdentifier.tostring(a: self ref AlgIdentifier): string
+{
+ return "\n\t\toid: " + a.oid.tostring() + "\n\t\twith parameter: "+ bastr(a.parameter);
+}
+
+# [public]
+
+Name.equal(a: self ref Name, b: ref Name): int
+{
+ rda := a.rd_names;
+ rdb := b.rd_names;
+ if(len rda != len rdb)
+ return 0;
+ while(rda != nil && rdb != nil) {
+ ok := (hd rda).equal(hd rdb);
+ if(!ok)
+ return 0;
+ rda = tl rda;
+ rdb = tl rdb;
+ }
+
+ return 1;
+}
+
+# [public]
+# The sequence of RelativeDistinguishedName's gives a sort of pathname, from most general to
+# most specific. Each element of the path can be one or more (but usually just one)
+# attribute-value pair, such as countryName="US". We'll just form a "postal-style" address
+# string by concatenating the elements from most specific to least specific, separated by commas.
+
+Name.tostring(a: self ref Name): string
+{
+ path: string;
+ rdn := a.rd_names;
+ while(rdn != nil) {
+ path += (hd rdn).tostring();
+ rdn = tl rdn;
+ if(rdn != nil)
+ path += ",";
+ }
+ return path;
+}
+
+# [public]
+# The allocation of distinguished names is the responsibility of the Naming Authorities.
+# Each user shall therefore trust the Naming Authorities not to issue duplicate distinguished
+# names. The comparison shall be unique one to one match but may not in the same order.
+
+RDName.equal(a: self ref RDName, b: ref RDName): int
+{
+ if(len a.avas != len b.avas)
+ return 0;
+ aa := a.avas;
+ ba := b.avas;
+ while(aa != nil) {
+ found:= 0;
+ rest: list of ref AVA;
+ while(ba != nil) {
+ ok := (hd ba).equal(hd ba);
+ if(!ok)
+ rest = (hd aa) :: rest;
+ else {
+ if(found)
+ return 0;
+ found = 1;
+ }
+ ba = tl ba;
+ }
+ if(found == 0)
+ return 0;
+ ba = rest;
+ aa = tl aa;
+ }
+ return 1;
+}
+
+# [public]
+
+RDName.tostring(a: self ref RDName): string
+{
+ s: string;
+ avas := a.avas;
+ while(avas != nil) {
+ s += (hd avas).tostring();
+ avas = tl avas;
+ if(avas != nil)
+ s += "-";
+ }
+ return s;
+}
+
+# [public]
+# AVA are equal if they have the same type oid and value
+
+AVA.equal(a: self ref AVA, b: ref AVA): int
+{
+ # TODO: need to match different encoding (T61String vs. IA5String)
+ if(a.value != b.value)
+ return 0;
+
+ return oid_cmp(a.oid, b.oid);
+}
+
+# [public]
+
+AVA.tostring(a: self ref AVA): string
+{
+ return a.value;
+}
+
+# [public]
+
+Validity.tostring(v: self ref Validity, format: string): string
+{
+ s: string;
+ if(format == "local") {
+ s = "\n\t\tnot_before[local]: ";
+ s += daytime->text(daytime->local(v.not_before));
+ s += "\n\t\tnot_after[local]: ";
+ s += daytime->text(daytime->local(v.not_after));
+ }
+ else if(format == "gmt") {
+ s = "\n\t\tnot_before[gmt]: ";
+ s += daytime->text(daytime->gmt(v.not_before));
+ s += "\n\t\tnot_after[gmt]: ";
+ s += daytime->text(daytime->gmt(v.not_after));
+ }
+ else
+ s += "unknown format: " + format;
+ return s;
+}
+
+# [public]
+
+SubjectPKInfo.getPublicKey(pkinfo: self ref SubjectPKInfo): (string, int, ref PublicKey)
+{
+parse:
+ for(;;) {
+ pk: ref PublicKey;
+ id := asn1->oid_lookup(pkinfo.alg_id.oid, pkcs->objIdTab);
+ case id {
+ PKCS->id_pkcs_rsaEncryption or
+ PKCS->id_pkcs_md2WithRSAEncryption or
+ PKCS->id_pkcs_md4WithRSAEncryption or
+ PKCS->id_pkcs_md5WithRSAEncryption =>
+ (err, k) := pkcs->decode_rsapubkey(pkinfo.subject_pk);
+ if(err != nil)
+ break parse;
+ pk = ref PublicKey.RSA(k);
+ PKCS->id_algorithm_shaWithDSS =>
+ (err, k) := pkcs->decode_dsspubkey(pkinfo.subject_pk);
+ if(err != nil)
+ break parse;
+ pk = ref PublicKey.DSS(k);
+ PKCS->id_pkcs_dhKeyAgreement =>
+ (err, k) := pkcs->decode_dhpubkey(pkinfo.subject_pk);
+ if(err != nil)
+ break parse;
+ pk = ref PublicKey.DH(k);
+ * =>
+ break parse;
+ }
+ return ("", id, pk);
+ }
+ return ("subject public key: syntax error", -1, nil);
+}
+
+# [public]
+
+SubjectPKInfo.tostring(pkinfo: self ref SubjectPKInfo): string
+{
+ s := pkinfo.alg_id.tostring();
+ s += "\n\t\tencoded key: " + bastr(pkinfo.subject_pk);
+ return s;
+}
+
+# [public]
+
+Extension.tostring(e: self ref Extension): string
+{
+ s := "oid: " + e.oid.tostring();
+ s += "critical: ";
+ if(e.critical)
+ s += "true ";
+ else
+ s += "false ";
+ s += bastr(e.value);
+ return s;
+}
+
+## Certificate PATH
+## A list of certificates needed to allow a particular user to obtain
+## the public key of another, is known as a certificate path. A
+## certificate path logically forms an unbroken chain of trusted
+## points in the DIT between two users wishing to authenticate.
+## To establish a certification path between user A and user B using
+## the Directory without any prior information, each CA may store
+## one certificate and one reverse certificate designated as
+## corresponding to its superior CA.
+
+# The ASN.1 data byte definitions for certificates and a certificate
+# path is
+#
+# Certificates ::= SEQUENCE {
+# userCertificate Certificate,
+# certificationPath ForwardCertificationPath OPTIONAL }
+#
+# ForwardCertificationPath ::= SEQUENCE OF CrossCertificates
+# CrossCertificates ::= SET OF Certificate
+#
+
+# [public]
+# Verify a decoded certificate chain in order of root to user. This is useful for
+# non_ASN.1 encoding of certificates, e.g. in SSL. Return (0, error string) if
+# verification failure or (1, "") if verification ok
+
+verify_certchain(cs: list of array of byte): (int, string)
+{
+ lsc: list of (ref Signed, ref Certificate);
+
+ l := cs;
+ while(l != nil) {
+ (err, s) := Signed.decode(hd l);
+ if(err != "")
+ return (0, err);
+ c: ref Certificate;
+ (err, c) = Certificate.decode(s.tobe_signed);
+ if(err != "")
+ return (0, err);
+ lsc = (s, c) :: lsc;
+ l = tl l;
+ }
+ # reverse order
+ a: list of (ref Signed, ref Certificate);
+ while(lsc != nil) {
+ a = (hd lsc) :: a;
+ lsc = tl lsc;
+ }
+ return verify_certpath(a);
+}
+
+# [private]
+# along certificate path; first certificate is root
+
+verify_certpath(sc: list of (ref Signed, ref Certificate)): (int, string)
+{
+ # verify self-signed root certificate
+ (s, c) := hd sc;
+ # TODO: check root RDName with known CAs and using
+ # external verification of root - Directory service
+ (err, id, pk) := c.subject_pkinfo.getPublicKey();
+ if(err != "")
+ return (0, err);
+ if(!is_validtime(c.validity)
+ || !c.issuer.equal(c.subject)
+ || !s.verify(pk, 0)) # TODO: prototype verify(key, ref AlgIdentifier)?
+ return (0, "verification failure");
+
+ sc = tl sc;
+ while(sc != nil) {
+ (ns, nc) := hd sc;
+ # TODO: check critical flags of extension list
+ # check alt names field
+ (err, id, pk) = c.subject_pkinfo.getPublicKey();
+ if(err != "")
+ return (0, err);
+ if(!is_validtime(nc.validity)
+ || !nc.issuer.equal(c.subject)
+ || !ns.verify(pk, 0)) # TODO: move prototype as ?
+ return (0, "verification failure");
+ (s, c) = (ns, nc);
+ sc = tl sc;
+ }
+
+ return (1, "");
+}
+
+# [public]
+is_validtime(validity: ref Validity): int
+{
+ # a little more expensive but more accurate
+ now := daytime->now();
+
+ # need some conversion here
+ if(now < validity.not_before || now > validity.not_after)
+ return 0;
+
+ return 1;
+}
+
+is_validpair(): int
+{
+ return 0;
+}
+
+## Certificate Revocation List (CRL)
+##
+## A CRL is a time-stampted list identifying revoked certificates. It is signed by a
+## Certificate Authority (CA) and made freely available in a public repository.
+##
+## Each revoked certificate is identified in a CRL by its certificate serial number.
+## When a certificate-using system uses a certificate (e.g., for verifying a remote
+## user's digital signature), that system not only checks the certificate signature
+## and validity but also acquires a suitably-recent CRL and checks that the certificate
+## serial number is not on that CRL. The meaning of "suitably-recent" may vary with
+## local policy, but it usually means the most recently-issued CRL. A CA issues a new
+## CRL on a regular periodic basis (e.g., hourly, daily, or weekly). Entries are added
+## on CRLs as revocations occur, and an entry may be removed when the certificate
+## expiration date is reached.
+
+# [public]
+
+CRL.decode(a: array of byte): (string, ref CRL)
+{
+parse:
+ # break on error
+ for(;;) {
+ (err, all) := asn1->decode(a);
+ if(err != "")
+ break parse;
+ c := ref CRL;
+ # CRL must be a ASN1 sequence
+ (ok, el) := all.is_seq();
+ if(!ok || len el < 3)
+ break parse;
+ c.version = 1; # set to default (v2)
+ (ok, c.version) = parse_version(hd el);
+ if(!ok)
+ break parse;
+ if(c.version < 0) {
+ el = tl el;
+ if(len el < 4)
+ break parse;
+ }
+ # signature algorithm
+ (ok, c.sig) = parse_alg(hd el);
+ if(!ok)
+ break parse;
+ el = tl el;
+ # issuer: who issues the CRL
+ (ok, c.issuer) = parse_name(hd el);
+ if(!ok)
+ break parse;
+ el = tl el;
+ # this update
+ (ok, c.this_update) = parse_time(hd el, UTCTime);
+ if(!ok)
+ break parse;
+ el = tl el;
+ # OPTIONAL, must be in order
+ # next_update
+ if(el != nil) {
+ (ok, c.next_update) = parse_time(hd el, UTCTime);
+ if(!ok)
+ break parse;
+ el = tl el;
+ }
+ # revoked certificates
+ if(el != nil) {
+ (ok, c.revoked_certs) = parse_revoked_certs(hd el);
+ if(!ok)
+ break parse;
+ el = tl el;
+ }
+ # extensions
+ if(el != nil) {
+ (ok, c.exts) = parse_extlist(hd el);
+ if(!ok)
+ break parse;
+ el = tl el;
+ }
+ # must be no more left
+ if(el != nil)
+ break parse;
+ return ("", c);
+ }
+ return ("CRL: syntax error", nil);
+}
+
+# [public]
+
+CRL.encode(c: self ref CRL): (string, array of byte)
+{
+pack:
+ for(;;) {
+ el: list of ref Elem;
+ # always has a version packed
+ e_version := pack_version(c.version);
+ if(e_version == nil)
+ break pack;
+ el = e_version :: el;
+ # algorithm
+ e_sig := pack_alg(c.sig);
+ if(e_sig == nil)
+ break pack;
+ el = e_sig :: el;
+ # crl issuer
+ e_issuer := pack_name(c.issuer);
+ if(e_issuer == nil)
+ break pack;
+ el = e_issuer :: el;
+ # validity
+ e_this_update := pack_time(c.this_update, UTCTime);
+ if(e_this_update == nil)
+ break pack;
+ el = ref Elem(
+ Tag(Universal, ASN1->UTCTime, 0),
+ ref Value.String(e_this_update)
+ ) :: el;
+ # next crl update
+ if(c.next_update != 0) {
+ e_next_update := pack_time(c.next_update, UTCTime);
+ if(e_next_update == nil)
+ break pack;
+ el = ref Elem(
+ Tag(Universal, ASN1->UTCTime, 0),
+ ref Value.String(e_next_update)
+ ) :: el;
+ }
+ # revoked certificates
+ if(c.revoked_certs != nil) {
+ e_revoked_certs := pack_revoked_certs(c.revoked_certs);
+ if(e_revoked_certs == nil)
+ break pack;
+ el = e_revoked_certs :: el;
+ }
+ # crl extensions
+ if(c.exts != nil) {
+ e_exts := pack_extlist(c.exts);
+ if(e_exts == nil)
+ break pack;
+ el = e_exts :: el;
+ }
+ # compose all elements
+ lseq: list of ref Elem;
+ while(el != nil) {
+ lseq = (hd el) :: lseq;
+ el = tl el;
+ }
+ all := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(lseq));
+ (err, ret) := asn1->encode(all);
+ if(err != "")
+ break;
+ return ("", ret);
+ }
+ return ("incompleted CRL; unable to pack", nil);
+}
+
+# [public]
+
+CRL.tostring(c: self ref CRL): string
+{
+ s := "Certificate Revocation List (CRL)";
+ s += "\nVersion: " + string c.version;
+ s += "\nSignature: " + c.sig.tostring();
+ s += "\nIssuer: " + c.issuer.tostring();
+ s += "\nThis Update: " + daytime->text(daytime->local(c.this_update));
+ s += "\nNext Update: " + daytime->text(daytime->local(c.next_update));
+ s += "\nRevoked Certificates: ";
+ rcs := c.revoked_certs;
+ while(rcs != nil) {
+ s += "\t" + (hd rcs).tostring();
+ rcs = tl rcs;
+ }
+ s += "\nExtensions: ";
+ exts := c.exts;
+ while(exts != nil) {
+ s += "\t" + (hd exts).tostring();
+ exts = tl exts;
+ }
+ return s;
+}
+
+# [public]
+
+CRL.is_revoked(c: self ref CRL, sn: ref IPint): int
+{
+ es := c.revoked_certs;
+ while(es != nil) {
+ if(sn.eq((hd es).user_cert))
+ return 1;
+ es = tl es;
+ }
+ return 0;
+}
+
+# [public]
+
+RevokedCert.tostring(rc: self ref RevokedCert): string
+{
+ s := "Revoked Certificate";
+ if(rc.user_cert == nil)
+ return s + " [Bad Format]\n";
+ s += "\nSerial Number: " + rc.user_cert.iptostr(10);
+ if(rc.revoc_date != 0)
+ s += "\nRevocation Date: " + daytime->text(daytime->local(rc.revoc_date));
+ if(rc.exts != nil) {
+ exts := rc.exts;
+ while(exts != nil) {
+ s += "\t" + (hd exts).tostring();
+ exts = tl exts;
+ }
+ }
+ return s;
+}
+
+
+# [private]
+
+parse_revoked_certs(e: ref Elem): (int, list of ref RevokedCert)
+{
+ lc: list of ref RevokedCert;
+parse:
+ for(;;) {
+ (ok, el) := e.is_seq();
+ if(!ok)
+ break parse;
+ while(el != nil) {
+ c: ref RevokedCert;
+ (ok, c) = parse_revoked(hd el);
+ if(!ok)
+ break parse;
+ lc = c :: lc;
+ el = tl el;
+ }
+
+ return (1, lc);
+ }
+
+ return (0, nil);
+}
+
+# [private]
+
+pack_revoked_certs(r: list of ref RevokedCert): ref Elem
+{
+ el: list of ref Elem;
+
+ rs := r;
+ while(rs != nil) {
+ rc := pack_revoked(hd rs);
+ if(rc == nil)
+ return nil;
+ el = rc :: el;
+ rs = tl rs;
+ }
+ # reverse order
+ l: list of ref Elem;
+ while(el != nil) {
+ l = (hd el) :: l;
+ el = tl el;
+ }
+ return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(l));
+
+}
+
+# [private]
+
+parse_revoked(e: ref Elem): (int, ref RevokedCert)
+{
+parse:
+ for(;;) {
+ c: ref RevokedCert;
+ (ok, el) := e.is_seq();
+ if(!ok || len el < 2)
+ break parse;
+ uc: array of byte;
+ (ok, uc) = (hd el).is_bigint();
+ if(!ok)
+ break parse;
+ c.user_cert = IPint.bebytestoip(uc);
+ el = tl el;
+ (ok, c.revoc_date) = parse_time(hd el, UTCTime);
+ if(!ok)
+ break parse;
+ el = tl el;
+ if(el != nil) {
+ (ok, c.exts) = parse_extlist(hd el);
+ if(!ok)
+ break parse;
+ }
+ return (1, c);
+ }
+ return (0, nil);
+}
+
+# [private]
+
+pack_revoked(r: ref RevokedCert): ref Elem
+{
+ el: list of ref Elem;
+ if(r.exts != nil) {
+ e_exts := pack_extlist(r.exts);
+ if(e_exts == nil)
+ return nil;
+ el = e_exts :: el;
+ }
+ if(r.revoc_date != 0) {
+ e_date := pack_time(r.revoc_date, UTCTime);
+ if(e_date == nil)
+ return nil;
+ el = ref Elem(
+ Tag(Universal, ASN1->UTCTime, 0),
+ ref Value.String(e_date)
+ ) :: el;
+ }
+ if(r.user_cert == nil)
+ return nil;
+ el = ref Elem(Tag(Universal, INTEGER, 0),
+ ref Value.BigInt(r.user_cert.iptobebytes())
+ ) :: el;
+ return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+}
+
+## The extensions field allows addition of new fields to the structure
+## without modification to the ASN.1 definition. An extension field
+## consists of an extension identifier, a criticality flag, and a
+## canonical encoding of a data value of an ASN.1 type associated with
+## the identified extension. For those extensions where ordering of
+## individual extensions within the SEQUENCE is significant, the
+## specification of those individual extensions shall include the rules
+## for the significance of the ordering. When an implementation
+## processing a certificate does not recognize an extension, if the
+## criticality flag is FALSE, it may ignore that extension. If the
+## criticality flag is TRUE, unrecognized extensions shall cause the
+## structure to be considered invalid, i.e. in a certificate, an
+## unrecognized critical extension would cause validation of a signature
+## using that certificate to fail.
+
+# [public]
+
+cr_exts(es: list of ref Extension): list of ref Extension
+{
+ cr: list of ref Extension;
+ l := es;
+ while(l != nil) {
+ e := hd l;
+ if(e.critical == 1)
+ cr = e :: cr;
+ l = tl l;
+ }
+ return cr;
+}
+
+# [public]
+
+noncr_exts(es: list of ref Extension): list of ref Extension
+{
+ ncr: list of ref Extension;
+ l := es;
+ while(l != nil) {
+ e := hd l;
+ if(e.critical == 0)
+ ncr = e :: ncr;
+ l = tl l;
+ }
+ return ncr;
+}
+
+# [public]
+
+parse_exts(exts: list of ref Extension): (string, list of ref ExtClass)
+{
+ ets: list of ref ExtClass;
+ l := exts;
+ while(l != nil) {
+ ext := hd l;
+ (err, et) := ExtClass.decode(ext);
+ if(err != "")
+ return (err, nil);
+ ets = et :: ets;
+ l = tl l;
+ }
+ lseq: list of ref ExtClass;
+ while(ets != nil) {
+ lseq = (hd ets) :: lseq;
+ ets = tl ets;
+ }
+ return ("", lseq);
+}
+
+# [public]
+
+ExtClass.decode(ext: ref Extension): (string, ref ExtClass)
+{
+ err: string;
+ eclass: ref ExtClass;
+
+ oid := asn1->oid_lookup(ext.oid, objIdTab);
+ case oid {
+ id_ce_authorityKeyIdentifier =>
+ (err, eclass) = decode_authorityKeyIdentifier(ext);
+ if(err == "" && ext.critical == 1) {
+ err = "authority key identifier: should be non-critical";
+ break;
+ }
+ id_ce_subjectKeyIdentifier =>
+ (err, eclass) = decode_subjectKeyIdentifier(ext);
+ if(err != "" && ext.critical != 0) {
+ err = "subject key identifier: should be non-critical";
+ break;
+ }
+ id_ce_basicConstraints =>
+ (err, eclass) = decode_basicConstraints(ext);
+ if(err == "" && ext.critical != 1) {
+ err = "basic constraints: should be critical";
+ break;
+ }
+ id_ce_keyUsage =>
+ (err, eclass) = decode_keyUsage(ext);
+ if(err == "" && ext.critical != 1) {
+ err = "key usage: should be critical";
+ break;
+ }
+ id_ce_privateKeyUsage =>
+ (err, eclass) = decode_privateKeyUsage(ext);
+ if(err == "" && ext.critical != 0) {
+ err = "private key usage: should be non-critical";
+ break;
+ }
+ id_ce_policyMapping =>
+ (err, eclass) = decode_policyMapping(ext);
+ if(err == "" && ext.critical != 0) {
+ err = "policy mapping: should be non-critical";
+ break;
+ }
+ id_ce_certificatePolicies =>
+ (err, eclass) = decode_certificatePolicies(ext);
+ # either critical or non-critical
+ id_ce_issuerAltName =>
+ n: list of ref GeneralName;
+ (err, n) = decode_alias(ext);
+ if(err == "")
+ eclass = ref ExtClass.IssuerAltName(n);
+ # either critical or non-critical
+ id_ce_subjectAltName =>
+ n: list of ref GeneralName;
+ (err, n) = decode_alias(ext);
+ if(err == "")
+ eclass = ref ExtClass.SubjectAltName(n);
+ # either critical or non-critical
+ id_ce_nameConstraints =>
+ (err, eclass) = decode_nameConstraints(ext);
+ # either critical or non-critical
+ id_ce_policyConstraints =>
+ (err, eclass) = decode_policyConstraints(ext);
+ # either critical or non-critical
+ id_ce_cRLNumber =>
+ (err, eclass) = decode_cRLNumber(ext);
+ if(err == "" && ext.critical != 0) {
+ err = "crl number: should be non-critical";
+ break;
+ }
+ id_ce_reasonCode =>
+ (err, eclass) = decode_reasonCode(ext);
+ if(err == "" && ext.critical != 0) {
+ err = "crl reason: should be non-critical";
+ break;
+ }
+ id_ce_instructionCode =>
+ (err, eclass) = decode_instructionCode(ext);
+ if(err == "" && ext.critical != 0) {
+ err = "instruction code: should be non-critical";
+ break;
+ }
+ id_ce_invalidityDate =>
+ (err, eclass) = decode_invalidityDate(ext);
+ if(err == "" && ext.critical != 0) {
+ err = "invalidity date: should be non-critical";
+ break;
+ }
+ id_ce_issuingDistributionPoint =>
+ (err, eclass) = decode_issuingDistributionPoint(ext);
+ if(err == "" && ext.critical != 1) {
+ err = "issuing distribution point: should be critical";
+ break;
+ }
+ id_ce_cRLDistributionPoint =>
+ (err, eclass) = decode_cRLDistributionPoint(ext);
+ # either critical or non-critical
+ id_ce_certificateIssuer =>
+ (err, eclass) = decode_certificateIssuer(ext);
+ if(err == "" && ext.critical != 1) {
+ err = "certificate issuer: should be critical";
+ break;
+ }
+ id_ce_deltaCRLIndicator =>
+ (err, eclass) = decode_deltaCRLIndicator(ext);
+ if(err == "" && ext.critical != 1) {
+ err = "delta crl indicator: should be critical";
+ break;
+ }
+ id_ce_subjectDirectoryAttributes =>
+ (err, eclass) = decode_subjectDirectoryAttributes(ext);
+ if(ext.critical != 0) {
+ err = "subject directory attributes should be non-critical";
+ break;
+ }
+ * =>
+ err = "unknown extension class";
+ }
+
+ return (err, eclass);
+}
+
+# [public]
+
+ExtClass.encode(ec: self ref ExtClass, critical: int): ref Extension
+{
+ ext: ref Extension;
+
+ if(critical)
+ ; # unused
+ pick c := ec {
+ AuthorityKeyIdentifier =>
+ (err, a) := encode_authorityKeyIdentifier(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_authorityKeyIdentifier], 0, a);
+ SubjectKeyIdentifier =>
+ (err, a) := encode_subjectKeyIdentifier(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_subjectKeyIdentifier], 0, a);
+ BasicConstraints =>
+ (err, a) := encode_basicConstraints(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_basicConstraints], 0, a);
+ KeyUsage =>
+ (err, a) := encode_keyUsage(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_keyUsage], 0, a);
+ PrivateKeyUsage =>
+ (err, a) := encode_privateKeyUsage(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_privateKeyUsage], 0, a);
+ PolicyMapping =>
+ (err, a) := encode_policyMapping(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_policyMapping], 0, a);
+ CertificatePolicies =>
+ (err, a) := encode_certificatePolicies(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_certificatePolicies], 0, a);
+ IssuerAltName =>
+ (err, a) := encode_alias(c.alias);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_issuerAltName], 0, a);
+ SubjectAltName =>
+ (err, a) := encode_alias(c.alias);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_subjectAltName], 0, a);
+ NameConstraints =>
+ (err, a) := encode_nameConstraints(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_nameConstraints], 0, a);
+ PolicyConstraints =>
+ (err, a) := encode_policyConstraints(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_policyConstraints], 0, a);
+ CRLNumber =>
+ (err, a) := encode_cRLNumber(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_cRLNumber], 0, a);
+ ReasonCode =>
+ (err, a) := encode_reasonCode(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_reasonCode], 0, a);
+ InstructionCode =>
+ (err, a) := encode_instructionCode(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_instructionCode], 0, a);
+ InvalidityDate =>
+ (err, a) := encode_invalidityDate(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_invalidityDate], 0, a);
+ CRLDistributionPoint =>
+ (err, a) := encode_cRLDistributionPoint(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_cRLDistributionPoint], 0, a);
+ IssuingDistributionPoint =>
+ (err, a) := encode_issuingDistributionPoint(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_issuingDistributionPoint], 0, a);
+ CertificateIssuer =>
+ (err, a) := encode_certificateIssuer(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_certificateIssuer], 0, a);
+ DeltaCRLIndicator =>
+ (err, a) := encode_deltaCRLIndicator(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_deltaCRLIndicator], 0, a);
+ SubjectDirectoryAttributes =>
+ (err, a) := encode_subjectDirectoryAttributes(c);
+ if(err == "")
+ ext = ref Extension(ref objIdTab[id_ce_subjectDirectoryAttributes], 0, a);
+ }
+ return ext;
+}
+
+# [public]
+
+ExtClass.tostring(et: self ref ExtClass): string
+{
+ s: string;
+
+ pick t := et {
+ AuthorityKeyIdentifier =>
+ s = "Authority Key Identifier: ";
+ s += "\n\tid = " + bastr(t.id);
+ s += "\n\tissuer = " + t.issuer.tostring();
+ s += "\n\tserial_number = " + bastr(t.serial_number.iptobebytes());
+ SubjectKeyIdentifier =>
+ s = "Subject Key Identifier ";
+ s += "\n\tid = " + bastr(t.id);
+ BasicConstraints =>
+ s = "Basic Constraints: ";
+ s += "\n\tdepth = " + string t.depth;
+ KeyUsage =>
+ s = "Key Usage: ";
+ s += "\n\tusage = ";
+ PrivateKeyUsage =>
+ s = "Private Key Usage: ";
+ s += "\n\tusage = ";
+ PolicyMapping =>
+ s = "Policy Mapping: ";
+ pl := t.pairs;
+ while(pl != nil) {
+ (issuer_oid, subject_oid) := hd pl;
+ s += "\n\t(" + issuer_oid.tostring() + ", " + subject_oid.tostring() + ")";
+ pl = tl pl;
+ }
+ CertificatePolicies =>
+ s = "Certificate Policies: ";
+ pl := t.policies;
+ while(pl != nil) {
+ s += (hd pl).tostring();
+ pl = tl pl;
+ }
+ IssuerAltName =>
+ s = "Issuer Alt Name: ";
+ al := t.alias;
+ while(al != nil) {
+ s += (hd al).tostring() + ",";
+ al = tl al;
+ }
+ SubjectAltName =>
+ s = "Subject Alt Name: ";
+ al := t.alias;
+ while(al != nil) {
+ s += (hd al).tostring() + ",";
+ al = tl al;
+ }
+ NameConstraints =>
+ s = "Name Constraints: ";
+ s += "\n\tpermitted = ";
+ p := t.permitted;
+ while(p != nil) {
+ s += (hd p).tostring();
+ p = tl p;
+ }
+ s += "\n\texcluded = ";
+ e := t.excluded;
+ while(e != nil) {
+ s += (hd e).tostring();
+ e = tl e;
+ }
+ PolicyConstraints =>
+ s = "Policy Constraints: ";
+ s += "\n\trequire = " + string t.require;
+ s += "\n\tinhibit = " + string t.inhibit;
+ CRLNumber =>
+ s = "CRL Number: ";
+ s += "\n\tcurrent crl number = " + string t.curr;
+ ReasonCode =>
+ s = "Reason Code: ";
+ s += "\n\tcode = ";
+ InstructionCode =>
+ s = "Instruction Code: ";
+ s += "\n\thold with oid = " + t.oid.tostring();
+ InvalidityDate =>
+ s = "Invalidity Date: ";
+ s += "\n\tdate = " + daytime->text(daytime->local(t.date));
+ CRLDistributionPoint =>
+ s = "CRL Distribution Point: ";
+ ps := t.ps;
+ while(ps != nil) {
+ s += (hd ps).tostring() + ",";
+ ps = tl ps;
+ }
+ IssuingDistributionPoint =>
+ s = "Issuing Distribution Point: ";
+ CertificateIssuer =>
+ s = "Certificate Issuer: ";
+ DeltaCRLIndicator =>
+ s = "Delta CRL Indicator: ";
+ SubjectDirectoryAttributes =>
+ s = "Subject Directory Attributes: ";
+ * =>
+ s = "Unknown Extension: ";
+ }
+
+ return s;
+}
+
+# [private]
+
+decode_authorityKeyIdentifier(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, el) := all.is_seq();
+ if(!ok)
+ break parse;
+ ak := ref ExtClass.AuthorityKeyIdentifier;
+ e := hd el;
+ (ok, e) = is_context(e, 0);
+ if(ok) {
+ (ok, ak.id) = e.is_octetstring();
+ if(!ok)
+ break parse;
+ el = tl el;
+ }
+ if(el != nil && len el != 2)
+ break parse;
+ e = hd el;
+ (ok, e) = is_context(e, 1);
+ if(!ok)
+ break parse;
+ (ok, ak.issuer) = parse_gname(e);
+ if(!ok)
+ break parse;
+ e = hd tl el;
+ (ok, e) = is_context(e, 2);
+ if(!ok)
+ break parse;
+ (ok, ak.serial_number) = parse_sernum(e);
+ if(!ok)
+ break;
+ return ("", ak);
+ }
+ return ("syntax error", nil);
+}
+
+# [private]
+
+encode_authorityKeyIdentifier(c: ref ExtClass.AuthorityKeyIdentifier): (string, array of byte)
+{
+ el: list of ref Elem;
+ if(c.serial_number != nil) {
+ (ok, e) := pack_context(
+ ref Elem(
+ Tag(Universal, INTEGER, 0),
+ ref Value.BigInt(c.serial_number.iptobebytes())
+ ),
+ 2
+ );
+ if(!ok)
+ return ("syntax error", nil);
+ el = e :: nil;
+ }
+ if(c.issuer != nil) {
+ (ok, e) := pack_gname(c.issuer);
+ if(!ok)
+ return ("authority key identifier: encoding error", nil);
+ (ok, e) = pack_context(e, 1);
+ if(!ok)
+ return ("authority key identifier: encoding error", nil);
+ el = e :: el;
+ }
+ if(c.id != nil) {
+ (ok, e) := pack_context(
+ ref Elem(
+ Tag(Universal, OCTET_STRING, 0),
+ ref Value.Octets(c.id)
+ ),
+ 0
+ );
+ if(!ok)
+ return ("authority key identifier: encoding error", nil);
+ el = e :: el;
+ }
+ return asn1->encode(ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)));
+}
+
+# [private]
+
+decode_subjectKeyIdentifier(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, id) := all.is_octetstring();
+ if(!ok)
+ break parse;
+ return ("", ref ExtClass.SubjectKeyIdentifier(id));
+
+ }
+ return ("subject key identifier: syntax error", nil);
+}
+
+# [private]
+
+encode_subjectKeyIdentifier(c: ref ExtClass.SubjectKeyIdentifier): (string, array of byte)
+{
+ if(c.id == nil)
+ return ("syntax error", nil);
+ e := ref Elem(Tag(Universal, OCTET_STRING, 0), ref Value.Octets(c.id));
+ return asn1->encode(e);
+}
+
+# [private]
+
+decode_basicConstraints(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, el) := all.is_seq();
+ if(!ok || len el != 2)
+ break parse;
+ ca: int;
+ (ok, ca) = (hd el).is_int(); # boolean
+ if(!ok || ca != 1)
+ break parse;
+ path: int;
+ (ok, path) = (hd tl el).is_int(); # integer
+ if(!ok || path < 0)
+ break parse;
+ return ("", ref ExtClass.BasicConstraints(path));
+ }
+ return ("basic constraints: syntax error", nil);
+}
+
+# [private]
+
+encode_basicConstraints(c: ref ExtClass.BasicConstraints): (string, array of byte)
+{
+ el: list of ref Elem;
+ el = ref Elem(Tag(Universal, INTEGER, 0), ref Value.Int(c.depth)) :: nil;
+ el = ref Elem(Tag(Universal, BOOLEAN, 0), ref Value.Bool(1)) :: el;
+ e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return asn1->encode(e);
+}
+
+# [private]
+
+decode_keyUsage(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ # assert bits can fit into a limbo int
+ if(len ext.value > 4)
+ break parse;
+ return ("", ref ExtClass.KeyUsage(b4int(ext.value)));
+ }
+ return ("key usage: syntax error", nil);
+}
+
+# [private]
+
+encode_keyUsage(c: ref ExtClass.KeyUsage): (string, array of byte)
+{
+ return ("", int4b(c.usage));
+}
+
+# [private]
+
+decode_privateKeyUsage(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, el) := all.is_seq();
+ if(!ok || len el < 1) # at least one exists
+ break parse;
+ v := ref Validity;
+ e := hd el;
+ (ok, e) = is_context(e, 0);
+ if(ok) {
+ (ok, v.not_before) = parse_time(e, GeneralizedTime);
+ if(!ok)
+ break parse;
+ el = tl el;
+ }
+ if(el != nil) {
+ e = hd el;
+ (ok, e) = is_context(e, 1);
+ if(!ok)
+ break parse;
+ (ok, v.not_after) = parse_time(e, GeneralizedTime);
+ if(!ok)
+ break parse;
+ }
+ return ("", ref ExtClass.PrivateKeyUsage(v));
+ }
+ return ("private key usage: syntax error", nil);
+}
+
+# [private]
+
+encode_privateKeyUsage(c: ref ExtClass.PrivateKeyUsage): (string, array of byte)
+{
+ el: list of ref Elem;
+ e: ref Elem;
+ ok := 1;
+ p := c.period;
+ if(p == nil)
+ return ("encode private key usage: imcomplete data", nil);
+ if(p.not_after > 0) {
+ t := pack_time(p.not_after, GeneralizedTime);
+ e = ref Elem(Tag(Universal, GeneralizedTime, 0), ref Value.String(t));
+ (ok, e) = pack_context(e, 1);
+ if(!ok)
+ return ("encode private key usage: illegal context", nil);
+ el = e :: nil;
+ }
+ if(p.not_before > 0) {
+ t := pack_time(p.not_before, GeneralizedTime);
+ e = ref Elem(Tag(Universal, GeneralizedTime, 0), ref Value.String(t));
+ (ok, e) = pack_context(e, 0);
+ if(!ok)
+ return ("encode private key usage: illegal context", nil);
+ el = e :: el;
+ }
+ e = ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return asn1->encode(e);
+}
+
+# [private]
+
+decode_policyMapping(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, el) := all.is_seq();
+ if(!ok)
+ break parse;
+ l_pm: list of (ref Oid, ref Oid);
+ while(el != nil) {
+ e_pm: list of ref Elem;
+ (ok, e_pm) = (hd el).is_seq();
+ if(!ok || len e_pm != 2)
+ break parse;
+ idp, sdp: ref Oid;
+ (ok, idp) = (hd e_pm).is_oid();
+ if(!ok)
+ break parse;
+ (ok, sdp) = (hd tl e_pm).is_oid();
+ if(!ok)
+ break parse;
+ l_pm = (idp, sdp) :: l_pm;
+ }
+ # reverse the order
+ l: list of (ref Oid, ref Oid);
+ while(l_pm != nil) {
+ l = (hd l_pm) :: l;
+ l_pm = tl l_pm;
+ }
+ return ("", ref ExtClass.PolicyMapping(l));
+ }
+ return ("policy mapping: syntax error", nil);
+}
+
+# [private]
+
+encode_policyMapping(c: ref ExtClass.PolicyMapping): (string, array of byte)
+{
+ el, pel: list of ref Elem;
+ if(c.pairs == nil)
+ return ("policy mapping: incomplete data", nil);
+ pl := c.pairs;
+ while(pl != nil) {
+ (a, b) := hd pl;
+ if(a == nil || b == nil)
+ return ("policy mapping: incomplete data", nil);
+ be := ref Elem(Tag(Universal, OBJECT_ID, 0), ref Value.ObjId(b));
+ ae := ref Elem(Tag(Universal, OBJECT_ID, 0), ref Value.ObjId(a));
+ pel = ref Elem(
+ Tag(Universal, SEQUENCE, 1),
+ ref Value.Seq(ae::be::nil)
+ ) :: pel;
+ pl = tl pl;
+ }
+ while(pel != nil) {
+ el = (hd pel) :: el;
+ pel = tl pel;
+ }
+ e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return asn1->encode(e);
+}
+
+# [private]
+
+decode_certificatePolicies(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, el) := all.is_seq();
+ if(!ok)
+ break parse;
+ l_pi: list of ref PolicyInfo;
+ while(el != nil) {
+ e_pi: list of ref Elem;
+ (ok, e_pi) = (hd el).is_seq();
+ if(!ok || len e_pi > 2 || len e_pi < 1)
+ break parse;
+ pi: ref PolicyInfo;
+ (ok, pi.oid) = (hd e_pi).is_oid();
+ if(!ok)
+ break parse;
+ # get optional policy qualifier info
+ e_pi = tl e_pi;
+ if(e_pi != nil) {
+ e_pq: list of ref Elem;
+ (ok, e_pq) = (hd e_pi).is_seq();
+ if(!ok || len e_pq > 2 || len e_pq < 1)
+ break parse;
+ l_pq: list of ref PolicyQualifier;
+ while(e_pq != nil) {
+ pq: ref PolicyQualifier;
+ (ok, pq.oid) = (hd e_pq).is_oid();
+ if(!ok || pq.oid == nil)
+ break parse;
+ # get optional value
+ if(tl e_pq != nil) {
+ (ok, pq.value) = (hd tl e_pq).is_octetstring();
+ if(!ok)
+ break parse;
+ }
+ l_pq = pq :: l_pq;
+ e_pq = tl e_pq;
+ }
+ # reverse the order
+ while(l_pq != nil) {
+ pi.qualifiers = (hd l_pq) :: pi.qualifiers;
+ l_pq = tl l_pq;
+ }
+ }
+ l_pi = pi :: l_pi;
+ }
+ # reverse the order
+ l: list of ref PolicyInfo;
+ while(l_pi != nil) {
+ l = (hd l_pi) :: l;
+ l_pi = tl l_pi;
+ }
+ return ("", ref ExtClass.CertificatePolicies(l));
+ }
+ return ("certificate policies: syntax error", nil);
+}
+
+# [private]
+
+encode_certificatePolicies(c: ref ExtClass.CertificatePolicies): (string, array of byte)
+{
+ el, pel: list of ref Elem;
+ pl := c.policies;
+ while(pl != nil) {
+ p := hd pl;
+ if(p.oid == nil)
+ return ("certificate policies: incomplete data", nil);
+ plseq: list of ref Elem;
+ if(p.qualifiers != nil) {
+ ql := p.qualifiers;
+ qel, qlseq: list of ref Elem;
+ while(ql != nil) {
+ pq := hd ql;
+ pqseq: list of ref Elem;
+ if(pq.oid == nil)
+ return ("certificate policies: incomplete data", nil);
+ if(pq.value != nil) {
+ pqseq = ref Elem(
+ Tag(Universal, OCTET_STRING, 0),
+ ref Value.Octets(pq.value)
+ ) :: nil;
+ }
+ pqseq = ref Elem(
+ Tag(Universal, OBJECT_ID, 0),
+ ref Value.ObjId(pq.oid)
+ ) :: pqseq;
+ qlseq = ref Elem(
+ Tag(Universal, SEQUENCE, 1),
+ ref Value.Seq(pqseq)
+ ) :: qlseq;
+ ql = tl ql;
+ }
+ while(qlseq != nil) {
+ qel = (hd qlseq) :: qel;
+ qlseq = tl qlseq;
+ }
+ plseq = ref Elem(
+ Tag(Universal, SEQUENCE, 1),
+ ref Value.Seq(qel)
+ ) :: nil;
+ }
+ plseq = ref Elem(
+ Tag(Universal, OBJECT_ID, 0),
+ ref Value.ObjId(p.oid)
+ ) :: plseq;
+ pel = ref Elem(
+ Tag(Universal, SEQUENCE, 1),
+ ref Value.Seq(plseq)
+ ) :: pel;
+ pl = tl pl;
+ }
+ while(pel != nil) {
+ el = (hd pel) :: el;
+ pel = tl pel;
+ }
+ e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return asn1->encode(e);
+}
+
+# [private]
+
+decode_alias(ext: ref Extension): (string, list of ref GeneralName)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, el) := all.is_seq();
+ if(!ok)
+ break parse;
+ l_sa: list of ref GeneralName;
+ while(el != nil) {
+ gn: ref GeneralName;
+ (ok, gn) = parse_gname(hd el);
+ if(!ok)
+ break parse;
+ l_sa = gn :: l_sa;
+ el = tl el;
+ }
+ # reverse order
+ sa: list of ref GeneralName;
+ while(l_sa != nil) {
+ sa = (hd l_sa) :: sa;
+ l_sa = tl l_sa;
+ }
+ return ("", sa);
+ }
+ return ("alias: syntax error", nil);
+}
+
+# [private]
+
+encode_alias(gl: list of ref GeneralName): (string, array of byte)
+{
+ el, gel: list of ref Elem;
+ while(gl != nil) {
+ g := hd gl;
+ (ok, e) := pack_gname(g);
+ if(!ok)
+ return ("alias: encoding error", nil);
+ gel = e :: gel;
+ gl = tl gl;
+ }
+ while(gel != nil) {
+ el = (hd gel) :: el;
+ gel = tl gel;
+ }
+ e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return asn1->encode(e);
+}
+
+# [private]
+
+decode_subjectDirectoryAttributes(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, el) := all.is_seq();
+ if(!ok)
+ break parse;
+ l_a: list of ref Attribute;
+ while(el != nil) {
+ a: ref Attribute;
+ #(ok, a) = parse_attr(hd el);
+ #if(!ok)
+ # break parse;
+ l_a = a :: l_a;
+ el = tl el;
+ }
+ # reverse order
+ as: list of ref Attribute;
+ while(l_a != nil) {
+ as = (hd l_a) :: as;
+ l_a = tl l_a;
+ }
+ return ("", ref ExtClass.SubjectDirectoryAttributes(as));
+ }
+ return ("subject directory attributes: syntax error", nil);
+}
+
+# [private]
+
+encode_subjectDirectoryAttributes(c: ref ExtClass.SubjectDirectoryAttributes)
+ : (string, array of byte)
+{
+ el, ael: list of ref Elem;
+ al := c.attrs;
+ while(al != nil) {
+ (ok, e) := pack_attr(hd al);
+ if(!ok)
+ return ("subject directory attributes: encoding error", nil);
+ ael = e :: ael;
+ al = tl al;
+ }
+ while(ael != nil) {
+ el = (hd ael) :: el;
+ ael = tl ael;
+ }
+ e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return asn1->encode(e);
+}
+
+# [private]
+
+decode_nameConstraints(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, el) := all.is_seq();
+ if(!ok || len el < 1 || len el > 2)
+ break parse;
+ nc := ref ExtClass.NameConstraints;
+ if(el != nil) {
+ (ok, nc.permitted) = parse_gsubtrees(hd el);
+ if(!ok || nc.permitted == nil)
+ break parse;
+ el = tl el;
+ }
+ if(el!= nil) {
+ (ok, nc.excluded) = parse_gsubtrees(hd el);
+ if(!ok || nc.excluded == nil)
+ break parse;
+ }
+ return ("", nc);
+ }
+ return ("name constraints: syntax error", nil);
+}
+
+# [private]
+
+encode_nameConstraints(c: ref ExtClass.NameConstraints): (string, array of byte)
+{
+ el: list of ref Elem;
+ if(c.permitted == nil && c.excluded == nil)
+ return ("name constraints: incomplete data", nil);
+ if(c.excluded != nil) {
+ (ok, e) := pack_gsubtrees(c.excluded);
+ if(!ok)
+ return ("name constraints: encoding error", nil);
+ el = e :: el;
+ }
+ if(c.permitted != nil) {
+ (ok, e) := pack_gsubtrees(c.permitted);
+ if(!ok)
+ return ("name constraints: encoding error", nil);
+ el = e :: el;
+ }
+ e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return asn1->encode(e);
+}
+
+# [private]
+
+parse_gsubtrees(e: ref Elem): (int, list of ref GSubtree)
+{
+parse:
+ for(;;) {
+ (ok, el) := e.is_seq();
+ if(!ok)
+ break parse;
+ l, lgs: list of ref GSubtree;
+ while(el != nil) {
+ gs: ref GSubtree;
+ (ok, gs) = parse_gsubtree(hd el);
+ if(!ok)
+ break parse;
+ lgs = gs :: lgs;
+ el = tl el;
+ }
+ while(lgs != nil) {
+ l = (hd lgs) :: l;
+ lgs = tl lgs;
+ }
+ return (1, l);
+ }
+ return (0, nil);
+}
+
+# [private]
+
+pack_gsubtrees(gs: list of ref GSubtree): (int, ref Elem)
+{
+ el, l: list of ref Elem;
+ while(gs != nil) {
+ (ok, e) := pack_gsubtree(hd gs);
+ if(!ok)
+ return (0, nil);
+ l = e :: l;
+ }
+ while(l != nil) {
+ el = (hd l) :: el;
+ l = tl l;
+ }
+ e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return (1, e);
+}
+
+# [private]
+
+parse_gsubtree(e: ref Elem): (int, ref GSubtree)
+{
+parse:
+ for(;;) {
+ (ok, el) := e.is_seq();
+ if(!ok || len el > 3 || len el < 2)
+ break parse;
+ gs := ref GSubtree;
+ e = hd el;
+ (ok, gs.base) = parse_gname(e);
+ if(!ok)
+ break parse;
+ el = tl el;
+ e = hd el;
+ (ok, e) = is_context(e, 0);
+ if(ok) {
+ (ok, gs.min) = e.is_int();
+ if(!ok)
+ break parse;
+ el = tl el;
+ }
+ # get optional maximum base distance
+ if(el != nil) {
+ e = hd el;
+ (ok, e) = is_context(e, 1);
+ if(!ok)
+ break parse;
+ (ok, gs.max) = e.is_int();
+ if(!ok)
+ break parse;
+ }
+ return (1, gs);
+ }
+ return (0, nil);
+}
+
+# [private]
+
+pack_gsubtree(g: ref GSubtree): (int, ref Elem)
+{
+ el: list of ref Elem;
+ ok := 1;
+ e: ref Elem;
+ if(g.base == nil)
+ return (0, nil);
+ if(g.max != 0) {
+ e = ref Elem(Tag(Universal, INTEGER, 0), ref Value.Int(g.max));
+ (ok, e) = pack_context(e, 1);
+ if(!ok)
+ return (0, nil);
+ el = e :: nil;
+ }
+ if(g.min != 0) {
+ e = ref Elem(Tag(Universal, INTEGER, 0), ref Value.Int(g.min));
+ (ok, e) = pack_context(e, 0);
+ if(!ok)
+ return (0, nil);
+ el = e :: el;
+ }
+ (ok, e) = pack_gname(g.base);
+ if(!ok)
+ return (0, nil);
+ el = e :: el;
+ e = ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return (1, e);
+}
+
+# [private]
+
+decode_policyConstraints(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, el) := all.is_seq();
+ if(!ok || len el < 1 || len el > 2)
+ break parse;
+ pc := ref ExtClass.PolicyConstraints;
+ e := hd el;
+ (ok, e) = is_context(e, 0);
+ if(ok) {
+ (ok, pc.require) = e.is_int();
+ if(!ok)
+ break parse;
+ el = tl el;
+ }
+ if(el != nil) {
+ e = hd el;
+ (ok, e) = is_context(e, 1);
+ if(!ok)
+ break parse;
+ (ok, pc.inhibit) = e.is_int();
+ if(!ok)
+ break parse;
+ }
+ return ("", pc);
+ }
+ return ("policy constraints: syntax error", nil);
+}
+
+# [private]
+
+encode_policyConstraints(c: ref ExtClass.PolicyConstraints): (string, array of byte)
+{
+ el: list of ref Elem;
+ ok := 1;
+ if(c.inhibit > 0) {
+ e := ref Elem(Tag(Universal, INTEGER, 0), ref Value.Int(c.inhibit));
+ (ok, e) = pack_context(e, 1);
+ if(!ok)
+ return ("policy constraints: encoding error", nil);
+ el = e :: nil;
+ }
+ if(c.require > 0) {
+ e := ref Elem(Tag(Universal, INTEGER, 0), ref Value.Int(c.require));
+ (ok, e) = pack_context(e, 0);
+ if(!ok)
+ return ("policy constraints: encoding error", nil);
+ el = e :: el;
+ }
+ e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return asn1->encode(e);
+}
+
+# [private]
+
+decode_cRLNumber(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, n) := all.is_int(); # TODO: should be IPint
+ if(!ok)
+ break parse;
+ return ("", ref ExtClass.CRLNumber(n));
+ }
+ return ("crl number: syntax error", nil);
+}
+
+# [private]
+
+encode_cRLNumber(c: ref ExtClass.CRLNumber): (string, array of byte)
+{
+ e := ref Elem(Tag(Universal, INTEGER, 0), ref Value.Int(c.curr));
+ return asn1->encode(e);
+}
+
+# [private]
+
+decode_reasonCode(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, un_used_bits, code) := all.is_bitstring();
+ if(!ok)
+ break parse;
+ # no harm to ignore unused bits
+ if(len code > 4)
+ break parse;
+ return ("", ref ExtClass.ReasonCode(b4int(code)));
+ }
+ return ("crl reason: syntax error", nil);
+}
+
+# [private]
+
+encode_reasonCode(c: ref ExtClass.ReasonCode): (string, array of byte)
+{
+ e := ref Elem(
+ Tag(Universal, BIT_STRING, 0),
+ ref Value.BitString(0, int4b(c.code))
+ );
+ return asn1->encode(e);
+}
+
+# [private]
+
+decode_instructionCode(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, code) := all.is_oid();
+ if(!ok)
+ break parse;
+ return ("", ref ExtClass.InstructionCode(code));
+ }
+ return ("instruction code: syntax error", nil);
+}
+
+# [private]
+
+encode_instructionCode(c: ref ExtClass.InstructionCode): (string, array of byte)
+{
+ e := ref Elem(Tag(Universal, OBJECT_ID, 0), ref Value.ObjId(c.oid));
+ return asn1->encode(e);
+}
+
+# [private]
+
+decode_invalidityDate(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, date) := all.is_time();
+ if(!ok)
+ break parse;
+ t := decode_time(date, GeneralizedTime);
+ if(t < 0)
+ break parse;
+ return ("", ref ExtClass.InvalidityDate(t));
+ }
+ return ("", nil);
+}
+
+# [private]
+
+encode_invalidityDate(c: ref ExtClass.InvalidityDate): (string, array of byte)
+{
+ e := ref Elem(
+ Tag(Universal, GeneralizedTime, 0),
+ ref Value.String(pack_time(c.date, GeneralizedTime))
+ );
+ return asn1->encode(e);
+}
+
+# [private]
+
+decode_cRLDistributionPoint(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, el) := all.is_seq();
+ if(!ok || len el < 1) # Note: at least one
+ break parse;
+ l, dpl: list of ref DistrPoint;
+ while(el != nil) {
+ dp: ref DistrPoint;
+ (ok, dp) = parse_distrpoint(hd el);
+ if(!ok)
+ break parse;
+ dpl = dp :: dpl;
+ }
+ # reverse order
+ while(dpl != nil) {
+ l = (hd dpl) :: l;
+ dpl = tl dpl;
+ }
+ return ("", ref ExtClass.CRLDistributionPoint(l));
+ }
+ return ("crl distribution point: syntax error", nil);
+}
+
+# [private]
+
+encode_cRLDistributionPoint(c: ref ExtClass.CRLDistributionPoint): (string, array of byte)
+{
+ el, l: list of ref Elem;
+ dpl := c.ps;
+ if(dpl == nil) # at lease one
+ return ("crl distribution point: incomplete data error", nil);
+ while(dpl != nil) {
+ (ok, e) := pack_distrpoint(hd dpl);
+ if(!ok)
+ return ("crl distribution point: encoding error", nil);
+ l = e :: l;
+ }
+ while(l != nil) {
+ el = (hd l) :: el;
+ l = tl l;
+ }
+ e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return asn1->encode(e);
+}
+
+# [private]
+
+parse_distrpoint(e: ref Elem): (int, ref DistrPoint)
+{
+parse:
+ for(;;) {
+ (ok, el) := e.is_seq();
+ if(!ok)
+ break parse;
+ if(!ok || len el > 3 || len el < 1)
+ break parse;
+ dp: ref DistrPoint;
+ e = hd el;
+ # get optional distribution point name
+ (ok, e) = is_context(e, 0);
+ if(ok) {
+ (ok, dp.name) = parse_dpname(e);
+ if(!ok)
+ break parse;
+ el = tl el;
+ }
+ # get optional reason flags
+ if(el != nil) {
+ e = hd el;
+ (ok, e) = is_context(e, 1);
+ if(ok) {
+ unused_bits: int;
+ reasons: array of byte;
+ (ok, unused_bits, reasons) = e.is_bitstring();
+ if(!ok)
+ break parse;
+ # no harm to ignore unused bits
+ if(len reasons > 4)
+ break parse;
+ dp.reasons = b4int(reasons);
+ }
+ el = tl el;
+ }
+ # get optional crl issuer
+ if(el != nil) {
+ e = hd el;
+ (ok, e) = is_context(e, 2);
+ if(!ok)
+ break parse;
+ (ok, dp.issuer) = parse_lgname(e);
+ if(!ok)
+ break parse;
+ el = tl el;
+ }
+ # must be no more left
+ if(el != nil)
+ break parse;
+ return (1, dp);
+ }
+ return (0, nil);
+}
+
+# [private]
+
+pack_distrpoint(dp: ref DistrPoint): (int, ref Elem)
+{
+ el: list of ref Elem;
+ if(dp.issuer != nil) {
+ (ok, e) := pack_lgname(dp.issuer);
+ if(!ok)
+ return (0, nil);
+ (ok, e) = pack_context(e, 2);
+ if(!ok)
+ return (0, nil);
+ el = e :: nil;
+ }
+ if(dp.reasons != 0) {
+ e := ref Elem(
+ Tag(Universal, BIT_STRING, 0),
+ ref Value.BitString(0, int4b(dp.reasons))
+ );
+ ok := 1;
+ (ok, e) = pack_context(e, 1);
+ if(!ok)
+ return (0, nil);
+ el = e :: el;
+ }
+ if(dp.name != nil) {
+ (ok, e) := pack_dpname(dp.name);
+ if(!ok)
+ return (0, nil);
+ (ok, e) = pack_context(e, 0);
+ if(!ok)
+ return (0, nil);
+ el = e :: el;
+ }
+ e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return (1, e);
+}
+
+# [private]
+
+parse_dpname(e: ref Elem): (int, ref DistrPointName)
+{
+parse:
+ for(;;) {
+ # parse CHOICE
+ ok := 0;
+ (ok, e) = is_context(e, 0);
+ if(ok) {
+ lg: list of ref GeneralName;
+ (ok, lg) = parse_lgname(e);
+ if(!ok)
+ break parse;
+ return (1, ref DistrPointName(lg, nil));
+ }
+ (ok, e) = is_context(e, 1);
+ if(!ok)
+ break parse;
+ n: ref Name;
+ (ok, n) = parse_name(e);
+ if(!ok)
+ break parse;
+ return (1, ref DistrPointName(nil, n.rd_names));
+ }
+ return (0, nil);
+}
+
+# [private]
+
+pack_dpname(dpn: ref DistrPointName): (int, ref Elem)
+{
+ if(dpn.full_name != nil) {
+ (ok, e) := pack_lgname(dpn.full_name);
+ if(!ok)
+ return (0, nil);
+ return pack_context(e, 0);
+ }
+ if(dpn.rdname != nil) {
+ rdn := dpn.rdname;
+ el, l: list of ref Elem;
+ while(rdn != nil) {
+ l = pack_rdname(hd rdn) :: l;
+ rdn = tl rdn;
+ }
+ while(l != nil) {
+ el = (hd l) :: el;
+ l = tl l;
+ }
+ e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return pack_context(e, 1);
+ }
+ return (0, nil);
+}
+
+# [private]
+
+decode_issuingDistributionPoint(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, el) := all.is_seq();
+ if(!ok || len el < 3 || len el > 5)
+ break parse;
+ ip := ref ExtClass.IssuingDistributionPoint;
+ ae := hd el;
+ # get optional distribution point name
+ (ok, ae) = is_context(ae, 0);
+ if(ok) {
+ #(ok, ip.name) = parse_dpname(ae);
+ if(!ok)
+ break parse;
+ el = tl el;
+ }
+ # get only contains user certs field
+ if(el != nil) {
+ ae = hd el;
+ (ok, ae) = is_context(ae, 1);
+ if(ok) {
+ (ok, ip.only_usercerts) = ae.is_int(); # boolean
+ if(!ok)
+ break parse;
+ }
+ el = tl el;
+ }
+ # get only contains ca certs field
+ if(el != nil) {
+ ae = hd el;
+ (ok, ae) = is_context(ae, 2);
+ if(ok) {
+ (ok, ip.only_cacerts) = ae.is_int(); # boolean
+ if(!ok)
+ break parse;
+ }
+ el = tl el;
+ }
+ # get optioinal only some reasons
+ if(el != nil) {
+ ae = hd el;
+ (ok, ae) = is_context(ae, 3);
+ if(ok) {
+ reasons: array of byte;
+ unused_bits: int;
+ (ok, unused_bits, reasons) = ae.is_bitstring();
+ if(!ok || len reasons > 4)
+ break parse;
+ ip.only_reasons = b4int(reasons);
+ }
+ el = tl el;
+ }
+ # get indirect crl field
+ if(el != nil) {
+ ae = hd el;
+ (ok, ae) = is_context(ae, 4);
+ if(!ok)
+ break parse;
+ (ok, ip.indirect_crl) = ae.is_int(); # boolean
+ if(!ok)
+ break parse;
+ el = tl el;
+ }
+ # must be no more left
+ if(el != nil)
+ break parse;
+ return ("", ip);
+ }
+ return ("issuing distribution point: syntax error", nil);
+}
+
+# [private]
+
+encode_issuingDistributionPoint(c: ref ExtClass.IssuingDistributionPoint)
+ : (string, array of byte)
+{
+ el: list of ref Elem;
+ ok := 1;
+ if(c.indirect_crl != 0) { # no encode for DEFAULT
+ e := ref Elem(
+ Tag(Universal, BOOLEAN, 0),
+ ref Value.Bool(c.indirect_crl)
+ );
+ (ok, e) = pack_context(e, 4);
+ if(!ok)
+ return ("issuing distribution point: encoding error", nil);
+ el = e :: el;
+ }
+ if(c.only_reasons != 0) {
+ e := ref Elem(
+ Tag(Universal, BIT_STRING, 0),
+ ref Value.BitString(0, int4b(c.only_reasons))
+ );
+ (ok, e) = pack_context(e, 3);
+ if(!ok)
+ return ("issuing distribution point: encoding error", nil);
+ el = e :: el;
+ }
+ if(c.only_cacerts != 0) {
+ e := ref Elem(
+ Tag(Universal, BOOLEAN, 0),
+ ref Value.Bool(c.only_cacerts)
+ );
+ (ok, e) = pack_context(e, 2);
+ if(!ok)
+ return ("issuing distribution point: encoding error", nil);
+ el = e :: el;
+ }
+ if(c.only_usercerts != 0) {
+ e := ref Elem(
+ Tag(Universal, BOOLEAN, 0),
+ ref Value.Bool(c.only_usercerts)
+ );
+ (ok, e) = pack_context(e, 1);
+ if(!ok)
+ return ("issuing distribution point: encoding error", nil);
+ el = e :: el;
+ }
+ if(c.name != nil) {
+ e: ref Elem;
+ (ok, e) = pack_dpname(c.name);
+ if(!ok)
+ return ("issuing distribution point: encoding error", nil);
+ (ok, e) = pack_context(e, 0);
+ if(!ok)
+ return ("issuing distribution point: encoding error", nil);
+ el = e :: el;
+ }
+
+ e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return asn1->encode(e);
+}
+
+# [private]
+
+decode_certificateIssuer(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, el) := all.is_seq();
+ if(!ok)
+ break parse;
+ gl, gnl: list of ref GeneralName;
+ while(el != nil) {
+ g: ref GeneralName;
+ (ok, g) = parse_gname(hd el);
+ if(!ok)
+ break parse;
+ gnl = g :: gnl;
+ el = tl el;
+ }
+ while(gnl != nil) {
+ gl = (hd gnl) :: gl;
+ gnl = tl gnl;
+ }
+ return ("", ref ExtClass.CertificateIssuer(gl));
+ }
+
+ return ("certificate issuer: syntax error", nil);
+}
+
+# [private]
+
+encode_certificateIssuer(c: ref ExtClass.CertificateIssuer): (string, array of byte)
+{
+ el, nel: list of ref Elem;
+ ns := c.names;
+ while(ns != nil) {
+ (ok, e) := pack_gname(hd ns);
+ if(!ok)
+ return ("certificate issuer: encoding error", nil);
+ nel = e :: nel;
+ ns = tl ns;
+ }
+ while(nel != nil) {
+ el = (hd nel) :: el;
+ nel = tl nel;
+ }
+ e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return asn1->encode(e);
+}
+
+# [private]
+
+decode_deltaCRLIndicator(ext: ref Extension): (string, ref ExtClass)
+{
+parse:
+ for(;;) {
+ (err, all) := asn1->decode(ext.value);
+ if(err != "")
+ break parse;
+ (ok, b) := all.is_bigint();
+ if(!ok)
+ break parse;
+ return ("", ref ExtClass.DeltaCRLIndicator(IPint.bebytestoip(b)));
+ }
+ return ("delta crl number: syntax error", nil);
+}
+
+# [private]
+
+encode_deltaCRLIndicator(c: ref ExtClass.DeltaCRLIndicator): (string, array of byte)
+{
+ e := ref Elem(
+ Tag(Universal, INTEGER, 0),
+ ref Value.BigInt(c.number.iptobebytes())
+ );
+ return asn1->encode(e);
+}
+
+# [public]
+
+GeneralName.tostring(gn: self ref GeneralName): string
+{
+ s: string;
+
+ pick g := gn {
+ otherName =>
+ s = "other name: " + g.str;
+ rfc822Name =>
+ s = "rfc822 name: " + g.str;
+ dNSName =>
+ s = "dns name: " + g.str;
+ x400Address =>
+ s = "x400 address: " + g.str;
+ uniformResourceIdentifier =>
+ s = "url: " + g.str;
+ iPAddress =>
+ s = "ip address: " + bastr(g.ip);
+ registeredID =>
+ s = "oid: " + g.oid.tostring();
+ ediPartyName =>
+ s = "edi party name: ";
+ s += "\n\tname assigner is " + g.nameAssigner.tostring();
+ s += "\n\tparty name is " + g.partyName.tostring();
+ directoryName =>
+ s = "directory name: " + g.dir.tostring();
+ }
+ return s;
+}
+
+# [public]
+
+PolicyInfo.tostring(pi: self ref PolicyInfo): string
+{
+ s := "oid: " + pi.oid.tostring();
+ s += "qualifiers: ";
+ ql := pi.qualifiers;
+ while(ql != nil) {
+ s += (hd ql).tostring();
+ ql = tl ql;
+ }
+ return s;
+}
+
+# [public]
+
+PolicyQualifier.tostring(pq: self ref PolicyQualifier): string
+{
+ s := "oid: " + pq.oid.tostring();
+ s += "value: " + bastr(pq.value);
+ return s;
+}
+
+# [public]
+
+GSubtree.tostring(gs: self ref GSubtree): string
+{
+ s := "base: " + gs.base.tostring();
+ s += "range: " + string gs.min + "-" + string gs.max;
+ return s;
+}
+
+# [public]
+
+DistrPoint.tostring(dp: self ref DistrPoint): string
+{
+ s := "Distribution Point: ";
+ s += "\n\tname = ";
+ d := dp.name;
+ if(d.full_name != nil) {
+ f := d.full_name;
+ while(f != nil) {
+ s += (hd f).tostring() + ",";
+ f = tl f;
+ }
+ }
+ else {
+ r := d.rdname;
+ while(r != nil) {
+ s += (hd r).tostring() + ",";
+ r = tl r;
+ }
+ }
+ s += "\n\treasons = " + string dp.reasons;
+ s += "\n\tissuer = ";
+ gl := dp.issuer;
+ while(gl != nil) {
+ s += (hd gl).tostring() + ",";
+ gl = tl gl;
+ }
+ return s;
+}
+
+# [private]
+
+is_context(e: ref Elem, num: int): (int, ref Elem)
+{
+ if(e.tag.class == ASN1->Context && e.tag.num == num) {
+ pick v := e.val {
+ Octets =>
+ (err, all) := asn1->decode(v.bytes);
+ if(err == "")
+ return (1, all);
+ }
+ }
+ return (0, nil);
+}
+
+# [private]
+
+pack_context(e: ref Elem, num: int): (int, ref Elem)
+{
+ (err, b) := asn1->encode(e);
+ if(err == "")
+ return (1, ref Elem(Tag(Context, num, 0), ref Value.Octets(b)));
+ return (0, nil);
+}
+
+# [private]
+
+parse_lgname(e: ref Elem): (int, list of ref GeneralName)
+{
+parse:
+ for(;;) {
+ (ok, el) := e.is_seq();
+ if(!ok)
+ break parse;
+ l, lg: list of ref GeneralName;
+ while(el != nil) {
+ g: ref GeneralName;
+ (ok, g) = parse_gname(hd el);
+ if(!ok)
+ break parse;
+ lg = g :: lg;
+ el = tl el;
+ }
+ while(lg != nil) {
+ l = (hd lg) :: l;
+ lg = tl lg;
+ }
+ return (1, l);
+ }
+ return (0, nil);
+}
+
+# [private]
+
+pack_lgname(lg: list of ref GeneralName): (int, ref Elem)
+{
+ el, gel: list of ref Elem;
+ while(lg != nil) {
+ (ok, e) := pack_gname(hd lg);
+ if(!ok)
+ return (0, nil);
+ gel = e :: gel;
+ lg = tl lg;
+ }
+ while(gel != nil) {
+ el = (hd gel) :: el;
+ gel = tl gel;
+ }
+ e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el));
+ return (1, e);
+}
+
+# [private]
+
+parse_gname(e: ref Elem): (int, ref GeneralName)
+{
+parse:
+ for(;;) {
+ g: ref GeneralName;
+ ok := 1;
+ case e.tag.num {
+ 0 =>
+ (ok, e) = is_context(e, 0);
+ if(!ok)
+ break parse;
+ str: string;
+ (ok, str) = e.is_string();
+ if(!ok)
+ break parse;
+ g = ref GeneralName.otherName(str);
+ 1 =>
+ (ok, e) = is_context(e, 1);
+ if(!ok)
+ break parse;
+ str: string;
+ (ok, str) = e.is_string();
+ if(!ok)
+ break parse;
+ g = ref GeneralName.rfc822Name(str);
+ 2 =>
+ (ok, e) = is_context(e, 2);
+ if(!ok)
+ break parse;
+ str: string;
+ (ok, str) = e.is_string();
+ if(!ok)
+ break parse;
+ g = ref GeneralName.dNSName(str);
+ 3 =>
+ (ok, e) = is_context(e, 3);
+ if(!ok)
+ break parse;
+ str: string;
+ (ok, str) = e.is_string();
+ if(!ok)
+ break parse;
+ g = ref GeneralName.x400Address(str);
+ 4 =>
+ (ok, e) = is_context(e, 4);
+ if(!ok)
+ break parse;
+ dir: ref Name;
+ (ok, dir) = parse_name(e);
+ if(!ok)
+ break parse;
+ g = ref GeneralName.directoryName(dir);
+ 5 =>
+ (ok, e) = is_context(e, 5);
+ if(!ok)
+ break parse;
+ el: list of ref Elem;
+ (ok, el) = e.is_seq();
+ if(!ok || len el < 1 || len el > 3)
+ break parse;
+ na, pn: ref Name;
+ (ok, e) = is_context(hd el, 0);
+ if(ok) {
+ (ok, na) = parse_name(e);
+ if(!ok)
+ break parse;
+ el = tl el;
+ }
+ if(el != nil) {
+ (ok, e) = is_context(hd el, 1);
+ if(!ok)
+ break parse;
+ (ok, pn) = parse_name(e);
+ if(!ok)
+ break parse;
+ }
+ g = ref GeneralName.ediPartyName(na, pn);
+ 6 =>
+ (ok, e) = is_context(e, 6);
+ if(!ok)
+ break parse;
+ str: string;
+ (ok, str) = e.is_string();
+ if(!ok)
+ break parse;
+ g = ref GeneralName.uniformResourceIdentifier(str);
+ 7 =>
+ (ok, e) = is_context(e, 7);
+ if(!ok)
+ break parse;
+ ip: array of byte;
+ (ok, ip) = e.is_octetstring();
+ if(!ok)
+ break parse;
+ g = ref GeneralName.iPAddress(ip);
+ 8 =>
+ (ok, e) = is_context(e, 8);
+ if(!ok)
+ break parse;
+ oid: ref Oid;
+ (ok, oid) = e.is_oid();
+ if(!ok)
+ break parse;
+ g = ref GeneralName.registeredID(oid);
+ * =>
+ break parse;
+ }
+ return (1, g);
+ }
+ return (0, nil);
+}
+
+# [private]
+
+pack_gname(gn: ref GeneralName): (int, ref Elem)
+{
+ e: ref Elem;
+ ok := 1;
+
+ pick g := gn {
+ otherName =>
+ e = ref Elem(
+ Tag(Universal, GeneralString, 0),
+ ref Value.String(g.str)
+ );
+ (ok, e) = pack_context(e, 0);
+ if(!ok)
+ return (0, nil);
+ rfc822Name =>
+ e = ref Elem(
+ Tag(Universal, IA5String, 0),
+ ref Value.String(g.str)
+ );
+ (ok, e) = pack_context(e, 1);
+ if(!ok)
+ return (0, nil);
+ dNSName =>
+ e = ref Elem(
+ Tag(Universal, IA5String, 0),
+ ref Value.String(g.str)
+ );
+ (ok, e) = pack_context(e, 2);
+ if(!ok)
+ return (0, nil);
+ x400Address =>
+ e = ref Elem(
+ Tag(Universal, GeneralString, 0),
+ ref Value.String(g.str)
+ );
+ (ok, e) = pack_context(e, 3);
+ if(!ok)
+ return (0, nil);
+ uniformResourceIdentifier =>
+ e = ref Elem(
+ Tag(Universal, GeneralString, 0),
+ ref Value.String(g.str)
+ );
+ (ok, e) = pack_context(e, 6);
+ if(!ok)
+ return (0, nil);
+ iPAddress =>
+ e = ref Elem(
+ Tag(Universal, OCTET_STRING, 0),
+ ref Value.Octets(g.ip)
+ );
+ (ok, e) = pack_context(e, 7);
+ if(!ok)
+ return (0, nil);
+
+ registeredID =>
+ e = ref Elem(
+ Tag(Universal, OBJECT_ID, 0),
+ ref Value.ObjId(g.oid)
+ );
+ (ok, e) = pack_context(e, 8);
+ if(!ok)
+ return (0, nil);
+
+ ediPartyName =>
+ el: list of ref Elem;
+ if(g.partyName != nil) {
+ e = pack_name(g.partyName);
+ (ok, e) = pack_context(e, 1);
+ if(!ok)
+ return (0, nil);
+ el = e :: nil;
+ }
+ if(g.nameAssigner != nil) {
+ e = pack_name(g.nameAssigner);
+ (ok, e) = pack_context(e, 0);
+ if(!ok)
+ return (0, nil);
+ el = e :: el;
+ }
+ e = ref Elem(
+ Tag(Universal, SEQUENCE, 1),
+ ref Value.Seq(el)
+ );
+ (ok, e) = pack_context(e, 5);
+ if(!ok)
+ return (0, nil);
+ directoryName =>
+ e = pack_name(g.dir);
+ (ok, e) = pack_context(e, 4);
+ if(!ok)
+ return (0, nil);
+ }
+ return (1, e);
+}
+
+# [private]
+# convert at most 4 bytes to int, len buf must be less than 4
+
+b4int(buf: array of byte): int
+{
+ val := 0;
+ for(i := 0; i < len buf; i++)
+ val = (val << 8) | (int buf[i]);
+ return val;
+}
+
+# [private]
+
+int4b(value: int): array of byte
+{
+ n := 4;
+ buf := array [n] of byte;
+ while(n--) {
+ buf[n] = byte value;
+ value >>= 8;
+ }
+ return buf;
+}
+
+# [private]
+
+oid_cmp(a, b: ref Oid): int
+{
+ na := len a.nums;
+ nb := len b.nums;
+ if(na != nb)
+ return 0;
+ for(i := 0; i < na; i++) {
+ if(a.nums[i] != b.nums[i])
+ return 0;
+ }
+ return 1;
+}
+
+# [private]
+# decode two bytes into an integer [0-99]
+# return -1 for an invalid encoding
+
+get2(a: string, i: int): int
+{
+ a0 := int a[i];
+ a1 := int a[i+1];
+ if(a0 < '0' || a0 > '9' || a1 < '0' || a1 > '9')
+ return -1;
+ return (a0 - '0')*10 + a1 - '0';
+}
+
+# [private]
+# encode an integer [0-99] into two bytes
+
+put2(a: array of byte, n, i: int): int
+{
+ a[i] = byte (n/10 + '0');
+ a[i+1] = byte (n%10 + '0');
+ return i+2;
+}
+
+# [private]
+
+bastr(a: array of byte) : string
+{
+ ans := "";
+ for(i := 0; i < len a; i++) {
+ if(i < len a - 1 && i%10 == 0)
+ ans += "\n\t\t";
+ ans += sys->sprint("%2x ", int a[i]);
+ }
+ return ans;
+}
+
+# [private]
+
+parse_attr(nil: ref Elem): (int, ref Attribute)
+{
+ return (0, nil);
+}
+
+# [private]
+
+pack_attr(nil: ref Attribute): (int, ref Elem)
+{
+ return (0, nil);
+}
diff --git a/appl/lib/daytime.b b/appl/lib/daytime.b
new file mode 100644
index 00000000..a650f8ca
--- /dev/null
+++ b/appl/lib/daytime.b
@@ -0,0 +1,478 @@
+implement Daytime;
+#
+# These routines convert time as follows:
+#
+# The epoch is 0000 Jan 1 1970 GMT.
+# The argument time is in microseconds since then.
+# The local(t) entry returns a reference to an ADT
+# containing
+#
+# seconds (0-59)
+# minutes (0-59)
+# hours (0-23)
+# day of month (1-31)
+# month (0-11)
+# year-1900
+# weekday (0-6, Sun is 0)
+# day of the year
+# daylight savings flag
+#
+# The routine gets the daylight savings time from the file /locale/timezone.
+#
+# text(tvec)
+# where tvec is produced by local
+# returns a string that has the time in the form
+#
+# Thu Jan 01 00:00:00 GMT 1970n0
+# 012345678901234567890123456789
+# 0 1 2
+#
+# time() just reads the time from /dev/time
+# and then calls localtime, then asctime.
+#
+# The sign bit of second times will turn on 68 years from the epoch ->2038
+#
+include "sys.m";
+include "string.m";
+include "daytime.m";
+
+S: String;
+sys: Sys;
+
+dmsize := array[] of {
+ 31, 28, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31
+};
+ldmsize := array[] of {
+ 31, 29, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31
+};
+
+Timezone: adt
+{
+ stname: string;
+ dlname: string;
+ stdiff: int;
+ dldiff: int;
+ dlpairs: array of int;
+};
+
+timezone: ref Timezone;
+
+now(): int
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+
+ fd := sys->open("/dev/time", sys->OREAD);
+ if(fd == nil)
+ return 0;
+ buf := array[128] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return 0;
+
+ t := (big string buf[0:n]) / big 1000000;
+ return int t;
+}
+
+time(): string
+{
+ t := now();
+ tm := local(t);
+ return text(tm);
+}
+
+local(tim: int): ref Tm
+{
+ ct: ref Tm;
+
+ if(timezone == nil)
+ timezone = readtimezone(nil);
+
+ t := tim + timezone.stdiff;
+ dlflag := 0;
+ for(i := 0; i+1 < len timezone.dlpairs; i += 2) {
+ if(t >= timezone.dlpairs[i] && t < timezone.dlpairs[i+1]) {
+ t = tim + timezone.dldiff;
+ dlflag++;
+ break;
+ }
+ }
+ ct = gmt(t);
+ if(dlflag) {
+ ct.zone = timezone.dlname;
+ ct.tzoff = timezone.dldiff;
+ }
+ else {
+ ct.zone = timezone.stname;
+ ct.tzoff = timezone.stdiff;
+ }
+ return ct;
+}
+
+gmt(tim: int): ref Tm
+{
+ xtime := ref Tm;
+
+ # break initial number into days
+ hms := tim % 86400;
+ day := tim / 86400;
+ if(hms < 0) {
+ hms += 86400;
+ day -= 1;
+ }
+
+ # generate hours:minutes:seconds
+ xtime.sec = hms % 60;
+ d1 := hms / 60;
+ xtime.min = d1 % 60;
+ d1 /= 60;
+ xtime.hour = d1;
+
+ # day is the day number.
+ # generate day of the week.
+ # The addend is 4 mod 7 (1/1/1970 was Thursday)
+ xtime.wday = (day + 7340036) % 7;
+
+ # year number
+ if(day >= 0)
+ for(d1 = 70; day >= dysize(d1+1900); d1++)
+ day -= dysize(d1+1900);
+ else
+ for (d1 = 70; day < 0; d1--)
+ day += dysize(d1+1900-1);
+ xtime.year = d1;
+ d0 := day;
+ xtime.yday = d0;
+
+ # generate month
+ if(dysize(d1+1900) == 366)
+ dmsz := ldmsize;
+ else
+ dmsz = dmsize;
+ for(d1 = 0; d0 >= dmsz[d1]; d1++)
+ d0 -= dmsz[d1];
+ xtime.mday = d0 + 1;
+ xtime.mon = d1;
+ xtime.zone = "GMT";
+ xtime.tzoff = 0;
+ return xtime;
+}
+
+wkday := array[] of {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+weekday := array[] of {
+ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
+};
+
+month := array[] of {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+text(t: ref Tm): string
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+
+ year := 1900+t.year;
+
+ return sys->sprint("%s %s %.2d %.2d:%.2d:%.2d %s %d",
+ wkday[t.wday],
+ month[t.mon],
+ t.mday,
+ t.hour,
+ t.min,
+ t.sec,
+ t.zone,
+ year);
+}
+
+filet(now: int, file: int): string
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+
+ t := local(file);
+ if(now - file < 6*30*24*3600)
+ return sys->sprint("%s %.2d %.2d:%.2d",
+ month[t.mon], t.mday, t.hour, t.min);
+
+ year := 1900+t.year;
+
+ return sys->sprint("%s %.2d %d", month[t.mon], t.mday, year);
+}
+
+dysize(y: int): int
+{
+ if(y%4 == 0 && (y%100 != 0 || y%400 == 0))
+ return 366;
+ return 365;
+}
+
+readtimezone(fname: string): ref Timezone
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+
+ tz := ref Timezone;
+ tz.stdiff = 0;
+ tz.stname = "GMT";
+
+ fd: ref Sys->FD;
+ if(fname == nil){
+ fd = sys->open("/env/timezone", Sys->OREAD);
+ if(fd == nil)
+ fd = sys->open("/locale/timezone", Sys->OREAD);
+ }else
+ fd = sys->open("/locale/" + fname, sys->OREAD);
+ if(fd == nil)
+ return tz;
+ buf := array[2048] of byte;
+ cnt := sys->read(fd, buf, len buf);
+ if(cnt <= 0)
+ return tz;
+
+ (n, val) := sys->tokenize(string buf[0:cnt], "\t \n\r");
+ if(n < 4)
+ return tz;
+
+ tz.stname = hd val;
+ val = tl val;
+ tz.stdiff = int hd val;
+ val = tl val;
+ tz.dlname = hd val;
+ val = tl val;
+ tz.dldiff = int hd val;
+ val = tl val;
+
+ tz.dlpairs = array[n-4] of {* => 0};
+ for(j := 0; val != nil; val = tl val)
+ tz.dlpairs[j++] = int hd val;
+ return tz;
+}
+
+SEC2MIN: con 60;
+SEC2HOUR: con 60*SEC2MIN;
+SEC2DAY: con 24*SEC2HOUR;
+
+tm2epoch(tm: ref Tm): int
+{
+ secs := 0;
+
+ #
+ # seconds per year
+ #
+ yr := tm.year + 1900;
+ if(yr < 1970)
+ for(i := yr; i < 1970; i++)
+ secs -= dysize(i) * SEC2DAY;
+ else
+ for(i = 1970; i < yr; i++)
+ secs += dysize(i) * SEC2DAY;
+ #
+ # seconds per month
+ #
+ if(dysize(yr) == 366)
+ dmsz := ldmsize;
+ else
+ dmsz = dmsize;
+ for(i = 0; i < tm.mon; i++)
+ secs += dmsz[i] * SEC2DAY;
+
+ #
+ # secs in last month
+ #
+ secs += (tm.mday-1) * SEC2DAY;
+
+ #
+ # hours, minutes, seconds
+ #
+ secs += tm.hour * SEC2HOUR;
+ secs += tm.min * SEC2MIN;
+ secs += tm.sec;
+
+ #
+ # time zone offset includes daylight savings time
+ #
+ return secs - tm.tzoff;
+}
+
+# handle three formats (we'll be a bit more tolerant)
+# Sun, 06 Nov 1994 08:49:37 TZ (rfc822+rfc1123)
+# Sunday, 06-Nov-94 08:49:37 TZ (rfc850, obsoleted by rfc1036)
+# Sun Nov 6 08:49:37 1994 (ANSI C's asctime() format, assume GMT)
+#
+# return nil on parsing error
+#
+string2tm(date: string): ref Tm
+{
+ buf: string;
+ ok: int;
+ tm := ref Tm;
+
+ if(S == nil)
+ S = load String String->PATH;
+
+ # Weekday|Wday
+ (date, buf) = dateword(date);
+ tm.wday = strlookup(wkday, buf);
+ if(tm.wday < 0)
+ tm.wday = strlookup(weekday, buf);
+ if(tm.wday < 0)
+ return nil;
+
+ # Try Mon
+ odate := date;
+ (date, buf) = dateword(date);
+ tm.mon = strlookup(month, buf);
+ if(tm.mon >= 0) {
+ # Mon was OK, so asctime() format
+ # DD
+ (date, tm.mday) = datenum(date);
+ if(tm.mday < 1 || tm.mday > 31)
+ return nil;
+
+ # HH:MM:SS
+ (ok, date) = hhmmss(date, tm);
+ if(!ok)
+ return nil;
+
+ # YY|YYYY
+ (buf, tm.year) = datenum(date);
+ if(tm.year > 1900)
+ tm.year -= 1900;
+ } else {
+ # Mon was not OK
+ date = odate;
+ # DD Mon YYYY or DD-Mon-(YY|YYYY)
+ (date, tm.mday) = datenum(date);
+ if(tm.mday < 1 || tm.mday > 31)
+ return nil;
+ (date, buf) = dateword(date);
+ tm.mon = strlookup(month, buf);
+ if(tm.mon < 0 || tm.mon >= 12)
+ return nil;
+ (date, tm.year) = datenum(date);
+ if(tm.year > 1900)
+ tm.year -= 1900;
+
+ # HH:MM:SS
+ (ok, buf) = hhmmss(date, tm);
+ if(!ok)
+ return nil;
+ }
+ (tm.zone, tm.tzoff) = tzinfo(buf);
+ if(tm.zone == "")
+ return nil;
+
+ return tm;
+}
+
+dateword(date: string): (string, string)
+{
+ notalnum: con "^A-Za-z0-9";
+
+ date = S->drop(date, notalnum);
+ (w, rest) := S->splitl(date, notalnum);
+ return (rest, w);
+}
+
+datenum(date: string): (string, int)
+{
+ notdig: con "^0-9";
+
+ date = S->drop(date, notdig);
+ (num, rest) := S->splitl(date, notdig);
+ return (rest, int num);
+}
+
+strlookup(a: array of string, s: string): int
+{
+ n := len a;
+ for(i := 0; i < n; i++) {
+ if(s == a[i])
+ return i;
+ }
+ return -1;
+}
+
+hhmmss(date: string, tm: ref Tm): (int, string)
+{
+ err := (0, "");
+
+ (date, tm.hour) = datenum(date);
+ if(tm.hour < 0 || tm.hour >= 24)
+ return err;
+ (date, tm.min) = datenum(date);
+ if(tm.min < 0 || tm.min >= 60)
+ return err;
+ (date, tm.sec) = datenum(date);
+ if(tm.sec < 0 || tm.sec >= 60)
+ return err;
+
+ return (1, date);
+}
+
+tzinfo(tz: string): (string, int)
+{
+ # strip leading and trailing whitespace
+ WS: con " \t";
+ tz = S->drop(tz, WS);
+ for(n := len tz; n > 0; n--) {
+ if(S->in(tz[n-1], WS) == 0)
+ break;
+ }
+ if(n < len tz)
+ tz = tz[:n];
+
+ # if no timezone, default to GMT
+ if(tz == nil)
+ return ("GMT", 0);
+
+ # GMT aliases
+ case tz {
+ "GMT" or
+ "UT" or
+ "UTC" or
+ "Z" =>
+ return ("GMT", 0);
+ }
+
+ # [+-]hhmm (hours and minutes offset from GMT)
+ if(len tz == 5 && (tz[0] == '+' || tz[0] == '-')) {
+ h := int tz[1:3];
+ m := int tz[3:5];
+ if(h > 23 || m > 59)
+ return ("", 0);
+ tzoff := h*SEC2HOUR + m*SEC2MIN;
+ if(tz[0] == '-')
+ tzoff = -tzoff;
+ return ("GMT", tzoff);
+ }
+
+ # try continental US timezones
+ filename: string;
+ case tz {
+ "CST" or "CDT" =>
+ filename = "CST.CDT";
+ "EST" or "EDT" =>
+ filename = "EST.EDT";
+ "MST" or "MDT" =>
+ filename = "MST.MDT";
+ "PST" or "PDT" =>
+ filename = "PST.PDT";
+ * =>
+ ; # default to local timezone
+ }
+ tzdata := readtimezone(filename);
+ if(tzdata.stname == tz)
+ return (tzdata.stname, tzdata.stdiff);
+ if(tzdata.dlname == tz)
+ return (tzdata.dlname, tzdata.dldiff);
+
+ return ("", 0);
+}
diff --git a/appl/lib/db.b b/appl/lib/db.b
new file mode 100644
index 00000000..6271b925
--- /dev/null
+++ b/appl/lib/db.b
@@ -0,0 +1,265 @@
+implement DB;
+
+include "sys.m";
+ sys : Sys;
+
+include "draw.m";
+
+include "keyring.m";
+
+include "security.m";
+
+include "db.m";
+
+RES_HEADER_SIZE: con 22;
+
+open(addr, username, password, dbname: string): (ref DB_Handle, list of string)
+{
+ (fd, err) := connect(addr, "none");
+ if(nil == fd)
+ return (nil, err :: nil);
+ return dbopen(fd, username, password, dbname);
+}
+
+connect(addr: string, alg: string): (ref Sys->FD, string)
+{
+ if (sys == nil)
+ sys = load Sys Sys->PATH;
+#
+# Should check to see if addr == /dev/sysname. If so, probably should
+# change addr to localhost, since some underlying platform code might
+# choke if handed its own address.
+#
+ (ok, c) := sys->dial(addr, nil);
+ if(ok < 0)
+ return (nil, sys->sprint("DB init: dial %s: %r\n", addr));
+ (n, addrparts) := sys->tokenize(addr, "!");
+ if(n >= 2)
+ addr = hd addrparts + "!" + hd tl addrparts;
+
+ kr := load Keyring Keyring->PATH;
+
+ user := user();
+ kd := "/usr/" + user + "/keyring/";
+ cert := kd + addr;
+ (ok, nil) = sys->stat(cert);
+ if(ok < 0){
+ cert = kd + "default";
+ # (ok, nil) = sys->stat(cert);
+ # if(ok<0)
+ # sys->fprint(stderr, "Warning: no certificate found in %s; use getauthinfo\n", kd);
+ }
+
+ ai := kr->readauthinfo(cert);
+
+ #
+ # let auth->client handle nil ai
+ # if(ai == nil){
+ # return (nil, sys->sprint("DB init: certificate for %s not found, use getauthinfo first", addr));
+ # }
+ #
+
+ au := load Auth Auth->PATH;
+ if(au == nil)
+ return (nil, sys->sprint("DB init: can't load module Auth %r"));
+
+ err := au->init();
+ if(err != nil)
+ return (nil, sys->sprint("DB init: can't initialize module Auth: %s", err));
+
+ fd: ref Sys->FD;
+
+ (fd, err) = au->client(alg, ai, c.dfd);
+ if(fd == nil)
+ return (nil, sys->sprint("DB init: authentication failed: %s", err));
+
+ return (fd, nil);
+}
+
+dbopen(fd: ref Sys->FD, username, password, dbname: string): (ref DB_Handle, list of string)
+{
+ dbh := ref DB_Handle;
+ dbh.datafd = fd;
+ dbh.lock = makelock();
+ dbh.sqlstream = -1;
+ logon := array of byte (username +"/"+ password +"/"+ dbname);
+ (mtype, strm, rc, data) := sendReq(dbh, 'I', logon);
+ if(mtype == 'h')
+ return (nil, (sys->sprint("DB: couldn't initialize %s for %s", dbname, username) :: string data :: nil));
+ dbh.sqlconn = int string data;
+
+ (mtype, strm, rc, data) = sendReq(dbh, 'O', array of byte string dbh.sqlconn);
+ if(mtype == 'h')
+ return (nil, (sys->sprint("DB: couldn't open SQL connection") :: string data :: nil));
+ dbh.sqlstream = int string data;
+ return (dbh, nil);
+}
+
+DB_Handle.SQLOpen(oldh: self ref DB_Handle): (int, ref DB_Handle)
+{
+ dbh := ref *oldh;
+ (mtype, strm, rc, data) := sendReq(dbh, 'O', array of byte string dbh.sqlconn);
+ if(mtype == 'h')
+ return (-1, nil);
+ dbh.sqlstream = int string data;
+ return (0, dbh);
+}
+
+DB_Handle.SQLClose(dbh: self ref DB_Handle): int
+{
+ (mtype, strm, rc, data) := sendReq(dbh, 'K', array[0] of byte);
+ if(mtype == 'h')
+ return -1;
+ dbh.sqlstream = -1;
+ return 0;
+}
+
+DB_Handle.SQL(dbh: self ref DB_Handle, command: string): (int, list of string)
+{
+ (mtype, strm, rc, data) := sendReq(dbh, 'W', array of byte command);
+ if(mtype == 'h')
+ return (-1, "Probable SQL format error" :: string data :: nil);
+ return (0, nil);
+}
+
+DB_Handle.columns(dbh: self ref DB_Handle): int
+{
+ (mtype, strm, rc, data) := sendReq(dbh, 'C', array[0] of byte);
+ if(mtype == 'h')
+ return 0;
+ return int string data;
+}
+
+DB_Handle.nextRow(dbh: self ref DB_Handle): int
+{
+ (mtype, strm, rc, data) := sendReq(dbh, 'N', array[0] of byte);
+ if(mtype == 'h')
+ return 0;
+ return int string data;
+}
+
+DB_Handle.read(dbh: self ref DB_Handle, columnI: int): (int, array of byte)
+{
+ (mtype, strm, rc, data) := sendReq(dbh, 'R', array of byte string columnI);
+ if(mtype == 'h')
+ return (-1, data);
+ return (len data, data);
+}
+
+DB_Handle.write(dbh: self ref DB_Handle, paramI: int, val: array of byte)
+ : int
+{
+ outbuf := array[len val + 4] of byte;
+ param := array of byte sys->sprint("%3d ", paramI);
+
+ for(i := 0; i < 4; i++)
+ outbuf[i] = param[i];
+ outbuf[4:] = val;
+ (mtype, strm, rc, data) := sendReq(dbh, 'P', outbuf);
+ if(mtype == 'h')
+ return -1;
+ return len val;
+}
+
+DB_Handle.columnTitle(handle: self ref DB_Handle, columnI: int): string
+{
+ (mtype, strm, rc, data) := sendReq(handle, 'T', array of byte string columnI);
+ if(mtype == 'h')
+ return nil;
+ return string data;
+}
+
+DB_Handle.errmsg(dbh: self ref DB_Handle): string
+{
+ (mtype, strm, rc, data) := sendReq(dbh, 'H', array[0] of byte);
+ return string data;
+}
+
+sendReq(dbh: ref DB_Handle, mtype: int, data: array of byte) : (int, int, int, array of byte)
+{
+ lock(dbh);
+ header := sys->sprint("%c1%11d %3d ", mtype, len data, dbh.sqlstream);
+ if(sys->write(dbh.datafd, array of byte header, 18) != 18) {
+ unlock(dbh);
+ return ('h', dbh.sqlstream, 0, array of byte "header write failure");
+ }
+ if(sys->write(dbh.datafd, data, len data) != len data) {
+ unlock(dbh);
+ return ('h', dbh.sqlstream, 0, array of byte "data write failure");
+ }
+ if(sys->write(dbh.datafd, array of byte "\n", 1) != 1) {
+ unlock(dbh);
+ return ('h', dbh.sqlstream, 0, array of byte "header write failure");
+ }
+ hbuf := array[RES_HEADER_SIZE+3] of byte;
+ if((n := sys->read(dbh.datafd, hbuf, RES_HEADER_SIZE)) !=
+ RES_HEADER_SIZE) {
+ # Is this an error? Try another read.
+ if(n < 0 ||
+ (m := sys->read(dbh.datafd, hbuf[n:], RES_HEADER_SIZE-n)) !=
+ RES_HEADER_SIZE-n) {
+ unlock(dbh);
+ return ('h', dbh.sqlstream, 0, array of byte "read error");
+ }
+ }
+ rheader := string hbuf[0:22];
+ rtype := rheader[0];
+ # Probably should check version in header[1]
+ datalen := int rheader[2:13];
+ rstrm := int rheader[14:17];
+ retcode := int rheader[18:21];
+
+ databuf := array[datalen] of byte;
+ # read in loop until get amount of data we want. If there is a mismatch
+ # here, we may hang with a lock on!
+
+ nbytes: int;
+
+ for(length := 0; length < datalen; length += nbytes) {
+ nbytes = sys->read(dbh.datafd, databuf[length:], datalen-length);
+ if(nbytes <= 0) {
+ break;
+ }
+ }
+ nbytes = sys->read(dbh.datafd, hbuf, 1); # The final \n
+ unlock(dbh);
+ return (rtype, rstrm, retcode, databuf);
+}
+
+makelock(): chan of int
+{
+ ch := chan of int;
+ spawn holdlock(ch);
+ return ch;
+}
+
+holdlock(c: chan of int)
+{
+ for(;;){
+ i := <-c;
+ c <-= i;
+ }
+}
+
+lock(h: ref DB_Handle)
+{
+ h.lock <-= h.sqlstream;
+}
+
+unlock(h: ref DB_Handle)
+{
+ <-h.lock;
+}
+
+user(): string
+{
+ sys = load Sys Sys->PATH;
+ fd := sys->open("/dev/user", sys->OREAD);
+ if(fd == nil)
+ return "";
+ buf := array[128] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return "";
+ return string buf[0:n];
+}
diff --git a/appl/lib/dbm.b b/appl/lib/dbm.b
new file mode 100644
index 00000000..e6c27630
--- /dev/null
+++ b/appl/lib/dbm.b
@@ -0,0 +1,463 @@
+implement Dbm;
+
+# Copyright © Caldera International Inc. 2001-2002. All rights reserved.
+# Limbo transliteration (with amendment) Copyright © 2004 Vita Nuova Holdings Limited.
+
+include "sys.m";
+ sys: Sys;
+ OREAD, OWRITE, ORDWR: import Sys;
+
+include "dbm.m";
+
+BYTESIZ: con 8; # bits
+SHORTSIZ: con 2; # bytes
+
+PBLKSIZ: con 512;
+DBLKSIZ: con 8192; # was 4096
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+Dbf.create(file: string, mode: int): ref Dbf
+{
+ pf := sys->create(file+".pag", ORDWR, mode);
+ if(pf == nil)
+ return nil;
+ df := sys->create(file+".dir", ORDWR, mode);
+ if(df == nil)
+ return nil;
+ return alloc(pf, df, ORDWR);
+}
+
+Dbf.open(file: string, flags: int): ref Dbf
+{
+ if((flags & 3) == OWRITE)
+ flags = (flags & ~3) | ORDWR;
+ pf := sys->open(file+".pag", flags);
+ if(pf == nil)
+ return nil;
+ df := sys->open(file+".dir", flags);
+ if(df == nil)
+ return nil;
+ return alloc(pf, df, flags);
+}
+
+alloc(pf: ref Sys->FD, df: ref Sys->FD, flags: int): ref Dbf
+{
+ db := ref Dbf;
+ db.pagf = pf;
+ db.dirf = df;
+ db.flags = flags & 3;
+ db.maxbno = 0;
+ db.bitno = 0;
+ db.hmask = 0;
+ db.blkno = 0;
+ db.pagbno = -1;
+ db.pagbuf = array[PBLKSIZ] of byte;
+ db.dirbno = -1;
+ db.dirbuf = array[DBLKSIZ] of byte;
+ (ok, d) := sys->fstat(db.dirf);
+ if(ok < 0)
+ d.length = big 0;
+ db.maxbno = int (d.length*big BYTESIZ - big 1);
+ return db;
+}
+
+Dbf.flush(db: self ref Dbf)
+{
+ db.pagbno = db.dirbno = -1;
+}
+
+Dbf.isrdonly(db: self ref Dbf): int
+{
+ return db.flags == OREAD;
+}
+
+Dbf.fetch(db: self ref Dbf, key: Datum): Datum
+{
+ access(db, calchash(key));
+ for(i:=0;; i+=2){
+ item := makdatum(db.pagbuf, i);
+ if(item == nil)
+ return item;
+ if(cmpdatum(key, item) == 0){
+ item = makdatum(db.pagbuf, i+1);
+ if(item == nil){
+ sys->fprint(sys->fildes(2), "dbm: items not in pairs\n");
+ raise "dbm: items not in pairs";
+ }
+ return item;
+ }
+ }
+}
+
+Dbf.delete(db: self ref Dbf, key: Datum): int
+{
+ if(db.isrdonly())
+ return -1;
+ access(db, calchash(key));
+ for(i:=0;; i+=2){
+ item := makdatum(db.pagbuf, i);
+ if(item == nil)
+ return -1;
+ if(cmpdatum(key, item) == 0){
+ delitem(db.pagbuf, i);
+ delitem(db.pagbuf, i);
+ break;
+ }
+ }
+ sys->seek(db.pagf, big db.blkno*big PBLKSIZ, 0);
+ write(db.pagf, db.pagbuf, PBLKSIZ);
+ db.pagbno = db.blkno;
+ return 0;
+}
+
+Dbf.store(db: self ref Dbf, key: Datum, dat: Datum, replace: int): int
+{
+ if(db.isrdonly())
+ return -1;
+ for(;;){
+ access(db, calchash(key));
+ for(i:=0;; i+=2){
+ item := makdatum(db.pagbuf, i);
+ if(item == nil)
+ break;
+ if(cmpdatum(key, item) == 0){
+ if(!replace)
+ return 1;
+ delitem(db.pagbuf, i);
+ delitem(db.pagbuf, i);
+ break;
+ }
+ }
+ i = additem(db.pagbuf, key);
+ if(i >= 0){
+ if(additem(db.pagbuf, dat) >= 0)
+ break;
+ delitem(db.pagbuf, i);
+ }
+ if(!split(db, key, dat))
+ return -1;
+ }
+ sys->seek(db.pagf, big db.blkno*big PBLKSIZ, 0);
+ write(db.pagf, db.pagbuf, PBLKSIZ);
+ db.pagbno = db.blkno;
+ return 0;
+}
+
+split(db: ref Dbf, key: Datum, dat: Datum): int
+{
+ if(len key+len dat+3*SHORTSIZ >= PBLKSIZ)
+ return 0;
+ ovfbuf := array[PBLKSIZ] of {* => byte 0};
+ for(i:=0;;){
+ item := makdatum(db.pagbuf, i);
+ if(item == nil)
+ break;
+ if(calchash(item) & (db.hmask+1)){
+ additem(ovfbuf, item);
+ delitem(db.pagbuf, i);
+ item = makdatum(db.pagbuf, i);
+ if(item == nil){
+ sys->fprint(sys->fildes(2), "dbm: split not paired\n");
+ raise "dbm: split not paired";
+ #break;
+ }
+ additem(ovfbuf, item);
+ delitem(db.pagbuf, i);
+ continue;
+ }
+ i += 2;
+ }
+ sys->seek(db.pagf, big db.blkno*big PBLKSIZ, 0);
+ write(db.pagf, db.pagbuf, PBLKSIZ);
+ db.pagbno = db.blkno;
+ sys->seek(db.pagf, (big db.blkno+big db.hmask+big 1)*big PBLKSIZ, 0);
+ write(db.pagf, ovfbuf, PBLKSIZ);
+ setbit(db);
+ return 1;
+}
+
+Dbf.firstkey(db: self ref Dbf): Datum
+{
+ return copy(firsthash(db, 0));
+}
+
+Dbf.nextkey(db: self ref Dbf, key: Datum): Datum
+{
+ hash := calchash(key);
+ access(db, hash);
+ item, bitem: Datum;
+ for(i:=0;; i+=2){
+ item = makdatum(db.pagbuf, i);
+ if(item == nil)
+ break;
+ if(cmpdatum(key, item) <= 0)
+ continue;
+ if(bitem == nil || cmpdatum(bitem, item) < 0)
+ bitem = item;
+ }
+ if(bitem != nil)
+ return copy(bitem);
+ hash = hashinc(db, hash);
+ if(hash == 0)
+ return copy(item);
+ return copy(firsthash(db, hash));
+}
+
+firsthash(db: ref Dbf, hash: int): Datum
+{
+ for(;;){
+ access(db, hash);
+ bitem := makdatum(db.pagbuf, 0);
+ item: Datum;
+ for(i:=2;; i+=2){
+ item = makdatum(db.pagbuf, i);
+ if(item == nil)
+ break;
+ if(cmpdatum(bitem, item) < 0)
+ bitem = item;
+ }
+ if(bitem != nil)
+ return bitem;
+ hash = hashinc(db, hash);
+ if(hash == 0)
+ return item;
+ }
+}
+
+access(db: ref Dbf, hash: int)
+{
+ for(db.hmask=0;; db.hmask=(db.hmask<<1)+1){
+ db.blkno = hash & db.hmask;
+ db.bitno = db.blkno + db.hmask;
+ if(getbit(db) == 0)
+ break;
+ }
+ if(db.blkno != db.pagbno){
+ sys->seek(db.pagf, big db.blkno * big PBLKSIZ, 0);
+ read(db.pagf, db.pagbuf, PBLKSIZ);
+ chkblk(db.pagbuf);
+ db.pagbno = db.blkno;
+ }
+}
+
+getbit(db: ref Dbf): int
+{
+ if(db.bitno > db.maxbno)
+ return 0;
+ n := db.bitno % BYTESIZ;
+ bn := db.bitno / BYTESIZ;
+ i := bn % DBLKSIZ;
+ b := bn / DBLKSIZ;
+ if(b != db.dirbno){
+ sys->seek(db.dirf, big b * big DBLKSIZ, 0);
+ read(db.dirf, db.dirbuf, DBLKSIZ);
+ db.dirbno = b;
+ }
+ if(int db.dirbuf[i] & (1<<n))
+ return 1;
+ return 0;
+}
+
+setbit(db: ref Dbf)
+{
+ if(db.bitno > db.maxbno){
+ db.maxbno = db.bitno;
+ getbit(db);
+ }
+ n := db.bitno % BYTESIZ;
+ bn := db.bitno / BYTESIZ;
+ i := bn % DBLKSIZ;
+ b := bn / DBLKSIZ;
+ db.dirbuf[i] |= byte (1<<n);
+ sys->seek(db.dirf, big b * big DBLKSIZ, 0);
+ write(db.dirf, db.dirbuf, DBLKSIZ);
+ db.dirbno = b;
+}
+
+makdatum(buf: array of byte, n: int): Datum
+{
+ ne := GETS(buf, 0);
+ if(n < 0 || n >= ne)
+ return nil;
+ t := PBLKSIZ;
+ if(n > 0)
+ t = GETS(buf, n+1-1);
+ v := GETS(buf, n+1);
+ return buf[v: t]; # size is t-v
+}
+
+cmpdatum(d1: Datum, d2: Datum): int
+{
+ n := len d1;
+ if(n != len d2)
+ return n - len d2;
+ if(n == 0)
+ return 0;
+ for(i := 0; i < len d1; i++)
+ if(d1[i] != d2[i])
+ return int d1[i] - int d2[i];
+ return 0;
+}
+
+copy(d: Datum): Datum
+{
+ if(d == nil)
+ return nil;
+ a := array[len d] of byte;
+ a[0:] = d;
+ return a;
+}
+
+# ken's
+#
+# 055,043,036,054,063,014,004,005,
+# 010,064,077,000,035,027,025,071,
+#
+
+hitab := array[16] of {
+ 61, 57, 53, 49, 45, 41, 37, 33,
+ 29, 25, 21, 17, 13, 9, 5, 1,
+};
+
+hltab := array[64] of {
+ 8r6100151277,8r6106161736,8r6452611562,8r5001724107,
+ 8r2614772546,8r4120731531,8r4665262210,8r7347467531,
+ 8r6735253126,8r6042345173,8r3072226605,8r1464164730,
+ 8r3247435524,8r7652510057,8r1546775256,8r5714532133,
+ 8r6173260402,8r7517101630,8r2431460343,8r1743245566,
+ 8r0261675137,8r2433103631,8r3421772437,8r4447707466,
+ 8r4435620103,8r3757017115,8r3641531772,8r6767633246,
+ 8r2673230344,8r0260612216,8r4133454451,8r0615531516,
+ 8r6137717526,8r2574116560,8r2304023373,8r7061702261,
+ 8r5153031405,8r5322056705,8r7401116734,8r6552375715,
+ 8r6165233473,8r5311063631,8r1212221723,8r1052267235,
+ 8r6000615237,8r1075222665,8r6330216006,8r4402355630,
+ 8r1451177262,8r2000133436,8r6025467062,8r7121076461,
+ 8r3123433522,8r1010635225,8r1716177066,8r5161746527,
+ 8r1736635071,8r6243505026,8r3637211610,8r1756474365,
+ 8r4723077174,8r3642763134,8r5750130273,8r3655541561,
+};
+
+hashinc(db: ref Dbf, hash: int): int
+{
+ hash &= db.hmask;
+ bit := db.hmask+1;
+ for(;;){
+ bit >>= 1;
+ if(bit == 0)
+ return 0;
+ if((hash&bit) == 0)
+ return hash|bit;
+ hash &= ~bit;
+ }
+}
+
+calchash(item: Datum): int
+{
+ hashl := 0;
+ hashi := 0;
+ for(i:=0; i<len item; i++){
+ f := int item[i];
+ for(j:=0; j<BYTESIZ; j+=4){
+ hashi += hitab[f&16rF];
+ hashl += hltab[hashi&16r3F];
+ f >>= 4;
+ }
+ }
+ return hashl;
+}
+
+delitem(buf: array of byte, n: int)
+{
+ ne := GETS(buf, 0);
+ if(n < 0 || n >= ne){
+ sys->fprint(sys->fildes(2), "dbm: bad delitem\n");
+ raise "dbm: bad delitem";
+ }
+ i1 := GETS(buf, n+1);
+ i2 := PBLKSIZ;
+ if(n > 0)
+ i2 = GETS(buf, n+1-1);
+ i3 := GETS(buf, ne+1-1);
+ if(i2 > i1)
+ while(i1 > i3){
+ i1--;
+ i2--;
+ buf[i2] = buf[i1];
+ buf[i1] = byte 0;
+ }
+ i2 -= i1;
+ for(i1=n+1; i1<ne; i1++)
+ PUTS(buf, i1+1-1, GETS(buf, i1+1) + i2);
+ PUTS(buf, 0, ne-1);
+ PUTS(buf, ne, 0);
+}
+
+additem(buf: array of byte, item: Datum): int
+{
+ i1 := PBLKSIZ;
+ ne := GETS(buf, 0);
+ if(ne > 0)
+ i1 = GETS(buf, ne+1-1);
+ i1 -= len item;
+ i2 := (ne+2) * SHORTSIZ;
+ if(i1 <= i2)
+ return -1;
+ PUTS(buf, ne+1, i1);
+ buf[i1:] = item;
+ PUTS(buf, 0, ne+1);
+ return ne;
+}
+
+chkblk(buf: array of byte)
+{
+ t := PBLKSIZ;
+ ne := GETS(buf, 0);
+ for(i:=0; i<ne; i++){
+ v := GETS(buf, i+1);
+ if(v > t)
+ badblk();
+ t = v;
+ }
+ if(t < (ne+1)*SHORTSIZ)
+ badblk();
+}
+
+read(fd: ref Sys->FD, buf: array of byte, n: int)
+{
+ nr := sys->read(fd, buf, n);
+ if(nr == 0){
+ for(i := 0; i < len buf; i++)
+ buf[i] = byte 0;
+ }else if(nr != n)
+ raise "dbm: read error: "+sys->sprint("%r");
+}
+
+write(fd: ref Sys->FD, buf: array of byte, n: int)
+{
+ if(sys->write(fd, buf, n) != n)
+ raise "dbm: write error: "+sys->sprint("%r");
+}
+
+badblk()
+{
+ sys->fprint(sys->fildes(2), "dbm: bad block\n");
+ raise "dbm: bad block";
+}
+
+GETS(buf: array of byte, sh: int): int
+{
+ sh *= SHORTSIZ;
+ return (int buf[sh]<<8) | int buf[sh+1];
+}
+
+PUTS(buf: array of byte, sh: int, v: int)
+{
+ sh *= SHORTSIZ;
+ buf[sh] = byte (v>>8);
+ buf[sh+1] = byte v;
+}
diff --git a/appl/lib/dbsrv.b b/appl/lib/dbsrv.b
new file mode 100644
index 00000000..859e3150
--- /dev/null
+++ b/appl/lib/dbsrv.b
@@ -0,0 +1,124 @@
+implement DBserver;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "keyring.m";
+
+include "security.m";
+
+include "db.m"; # For now.
+
+stderr: ref Sys->FD;
+
+DBserver : module
+{
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+# argv is a list of Inferno supported algorithms from Security->Auth
+
+init(nil: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ stdin := sys->fildes(0);
+ stderr = sys->fildes(2);
+ if(argv != nil)
+ argv = tl argv;
+ if(argv == nil)
+ err("no algorithm list");
+
+ kr := load Keyring Keyring->PATH;
+ if(nil == kr)
+ err(sys->sprint("can't load Keyring: %r"));
+
+ auth := load Auth Auth->PATH;
+ if(auth == nil)
+ err(sys->sprint("can't load Auth: %r"));
+
+ error := auth->init();
+ if(error != nil)
+ err(sys->sprint("Auth init failed: %s", error));
+
+ ai := kr->readauthinfo("/usr/"+user()+"/keyring/default");
+
+ (client_fd, info_or_err) := auth->server(argv, ai, stdin, 1);
+ if(client_fd == nil)
+ err(sys->sprint("can't authenticate client: %s", info_or_err));
+
+ auth = nil;
+ kr = nil;
+
+ sys->pctl(Sys->FORKNS|Sys->NEWPGRP, nil);
+
+ # run the infdb database program in the host system using /cmd
+
+ cmdfd := sys->open("/cmd/clone", sys->ORDWR);
+ if (cmdfd == nil)
+ err(sys->sprint("can't open /cmd/clone: %r"));
+
+ buf := array [20] of byte;
+ n := sys->read(cmdfd, buf, len buf);
+ if(n <= 0)
+ err(sys->sprint("can't read /cmd/clone: %r"));
+ cmddir := string buf[0:n];
+
+ if (sys->fprint(cmdfd, "exec infdb") <= 0)
+ err(sys->sprint("can't start infdb via /cmd/clone: %r"));
+
+ datafile := "/cmd/" + cmddir + "/data";
+ infdb_fd := sys->open(datafile, Sys->ORDWR);
+ if (infdb_fd == nil)
+ err(sys->sprint("can't open %s: %r", datafile));
+
+ spawn dbxfer(infdb_fd, client_fd, "client");
+
+ dbxfer(client_fd, infdb_fd, "infdb");
+ sys->fprint(infdb_fd, "X1 0 0 \n");
+}
+
+dbxfer(source, sink: ref Sys->FD, tag: string)
+{
+ buf := array [Sys->ATOMICIO] of byte;
+ while((nr := sys->read(source, buf, len buf)) > 0)
+ if(sys->write(sink, buf, nr) != nr){
+ sys->fprint(stderr, "dbsrv: write to %s failed: %r\n", tag);
+ shutdown();
+ }
+ if(nr < 0){
+ sys->fprint(stderr, "dbsrv: reading data for %s: %r\n", tag);
+ shutdown();
+ }
+}
+
+shutdown()
+{
+ pid := sys->pctl(0, nil);
+ fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);
+ if(fd == nil || sys->fprint(fd, "killgrp") < 0)
+ err(sys->sprint("can't kill group %d: %r", pid));
+}
+
+err(s: string)
+{
+ sys->fprint(stderr, "dbsrv: %s\n", s);
+ raise "fail:error";
+}
+
+user(): string
+{
+ sys = load Sys Sys->PATH;
+
+ fd := sys->open("/dev/user", sys->OREAD);
+ if(fd == nil)
+ return "";
+
+ buf := array[Sys->NAMEMAX] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return "";
+
+ return string buf[0:n];
+}
diff --git a/appl/lib/debug.b b/appl/lib/debug.b
new file mode 100644
index 00000000..09273dc7
--- /dev/null
+++ b/appl/lib/debug.b
@@ -0,0 +1,1493 @@
+implement Debug;
+
+include "sys.m";
+sys: Sys;
+sprint, FD: import sys;
+
+include "string.m";
+str: String;
+
+include "draw.m";
+
+include "debug.m";
+
+include "dis.m";
+ dism: Dis;
+
+Command: module
+{
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+Spin: adt
+{
+ spin: int;
+ pspin: int;
+};
+
+SrcState: adt
+{
+ files: array of string;
+ lastf: int;
+ lastl: int;
+ vers: int; # version number
+ # 11 => more source states
+};
+
+typenames := array[] of {
+ Terror => "error",
+ Tid => "id",
+ Tadt => "adt",
+ Tadtpick => "adtpick",
+ Tarray => "array",
+ Tbig => "big",
+ Tbyte => "byte",
+ Tchan => "chan",
+ Treal => "real",
+ Tfn => "fn",
+ Targ => "arg",
+ Tlocal => "local",
+ Tglobal => "global",
+ Tint => "int",
+ Tlist => "list",
+ Tmodule => "module",
+ Tnil => "nil",
+ Tnone => "none",
+ Tref => "ref",
+ Tstring => "string",
+ Ttuple => "tuple",
+ Tend => "end",
+ Targs => "args",
+ Tslice => "slice",
+ Tpoly => "poly",
+};
+
+tnone: ref Type;
+tnil: ref Type;
+tint: ref Type;
+tbyte: ref Type;
+tbig: ref Type;
+treal: ref Type;
+tstring: ref Type;
+tpoly: ref Type;
+
+IBY2WD: con 4;
+IBY2LG: con 8;
+H: con int 16rffffffff;
+
+ModHash: con 32;
+SymHash: con 32;
+mods:= array[ModHash] of list of ref Module;
+syms:= array[SymHash] of list of ref Sym;
+
+sblpath := array[] of
+{
+ ("/dis/", "/appl/cmd/"),
+ ("/dis/", "/appl/"),
+};
+
+init(): int
+{
+ sys = load Sys Sys->PATH;
+ str = load String String->PATH;
+ if(sys == nil || str == nil)
+ return 0;
+ tnone = ref Type(nil, Tnone, 0, "", nil, nil, nil);
+ tnil = ref Type(nil, Tnil, IBY2WD, "nil", nil, nil, nil);
+ tint = ref Type(nil, Tint, IBY2WD, "int", nil, nil, nil);
+ tbyte = ref Type(nil, Tbyte, 1, "byte", nil, nil, nil);
+ tbig = ref Type(nil, Tbig, IBY2LG, "big", nil, nil, nil);
+ treal = ref Type(nil, Treal, IBY2LG, "real", nil, nil, nil);
+ tstring = ref Type(nil, Tstring, IBY2WD, "string", nil, nil, nil);
+ tpoly = ref Type(nil, Tpoly, IBY2WD, "polymorphic", nil, nil, nil);
+ return 1;
+}
+
+prog(pid: int): (ref Prog, string)
+{
+ spid := string pid;
+ h := sys->open("/prog/"+spid+"/heap", sys->ORDWR);
+ if(h == nil)
+ return (nil, sprint("can't open heap file: %r"));
+ c := sys->open("/prog/"+spid+"/ctl", sys->OWRITE);
+ if(c == nil)
+ return (nil, sprint("can't open ctl file: %r"));
+ d := sys->open("/prog/"+spid+"/dbgctl", sys->ORDWR);
+ if(d == nil)
+ return (nil, sprint("can't open debug ctl file: %r"));
+ s := sys->open("/prog/"+spid+"/stack", sys->OREAD);
+ if(s == nil)
+ return (nil, sprint("can't open stack file: %r"));
+ return (ref Prog(pid, h, c, d, s), "");
+}
+
+startprog(dis, dir: string, ctxt: ref Draw->Context, argv: list of string): (ref Prog, string)
+{
+ c := load Command dis;
+ if(c == nil)
+ return (nil, "module not loaded");
+
+ ack := chan of int;
+ spin := ref Spin(1, 1);
+ end := chan of int;
+ spawn execer(ack, dir, c, ctxt, argv, spin, end);
+ kid := <-ack;
+
+ fd := sys->open("/prog/"+string kid+"/dbgctl", sys->ORDWR);
+ if(fd == nil){
+ spin.spin = -1;
+ <- end;
+ return (nil, sprint("can't open debug ctl file: %r"));
+ }
+ done := chan of string;
+ spawn stepper(done, fd, spin);
+
+wait: for(;;){
+ alt{
+ <-ack =>
+ sys->sleep(0);
+ err := <-done =>
+ if(err != ""){
+ <- end;
+ return(nil, err);
+ }
+ break wait;
+ }
+ }
+
+ b := array[20] of byte;
+ n := sys->read(fd, b, len b);
+ if(n <= 0){
+ <- end;
+ return(nil, sprint("%r"));
+ }
+ msg := string b[:n];
+ if(!str->prefix("new ", msg)){
+ <- end;
+ return (nil, msg);
+ }
+
+ kid = int msg[len "new ":];
+
+ # clean up the execer slave
+ b = array of byte "start";
+ sys->write(fd, b, len b);
+
+ <- end;
+ return prog(kid);
+}
+
+stepper(done: chan of string, ctl: ref FD, spin: ref Spin)
+{
+ b := array of byte "step1";
+ while(spin.pspin){
+ if(sys->write(ctl, b, len b) != len b)
+ done <-= sprint("can't start new thread: %r");
+ spin.spin = 0;
+ }
+ done <-= "";
+}
+
+execer(ack: chan of int, dir: string, c: Command, ctxt: ref Draw->Context, args: list of string, spin: ref Spin, end: chan of int)
+{
+ pid := sys->pctl(Sys->NEWPGRP|Sys->FORKNS|Sys->NEWFD, 0::1::2::nil);
+ sys->chdir(dir);
+ while(spin.spin == 1)
+ ack <-= pid;
+ if(spin.spin == -1){
+ end <-= 0;
+ exit;
+ }
+ spawn c->init(ctxt, args);
+ spin.pspin = 0;
+ end <-= 0;
+ exit;
+}
+
+# format of each line is
+# fp pc mp prog compiled path
+# fp, pc, mp, and prog are %.8lux
+# compile is or 1
+# path is a string
+Prog.stack(p: self ref Prog): (array of ref Exp, string)
+{
+ buf := array[8192] of byte;
+ sys->seek(p.stk, big 0, 0);
+ n := sys->read(p.stk, buf, len buf - 1);
+ if(n < 0)
+ return (nil, sprint("can't read stack file: %r"));
+ buf[n] = byte 0;
+
+ t := 0;
+ nf := 0;
+ for(s := 0; s < n; s = t+1){
+ t = strchr(buf, s, '\n');
+ if(buf[t] != byte '\n' || t-s < 40)
+ continue;
+ nf++;
+ }
+
+ e := array[nf] of ref Exp;
+ nf = 0;
+ for(s = 0; s < n; s = t+1){
+ t = strchr(buf, s, '\n');
+ if(buf[t] != byte '\n' || t-s < 40)
+ continue;
+ e[nf] = ref Exp("unknown fn",
+ hex(buf[s+0:s+8]),
+ hex(buf[s+9:s+17]),
+ mkmod(hex(buf[s+18:s+26]), hex(buf[s+27:s+35]), buf[36] != byte '0', string buf[s+38:t]),
+ p,
+ nil);
+ nf++;
+ }
+
+ return (e, "");
+}
+
+Prog.step(p: self ref Prog, how: int): string
+{
+ (stack, nil) := p.stack();
+ if(stack == nil)
+ return "can't find initial pc";
+ src := stack[0].srcstr();
+ stmt := ftostmt(stack[0]);
+
+ if(stack[0].m.sym == nil)
+ how = -1;
+
+ buf := array of byte("step1");
+ if(how == StepOut)
+ buf = array of byte("toret");
+ while(sys->write(p.dbgctl, buf, len buf) == len buf){
+ (stk, err) := p.stack();
+ if(err != nil)
+ return "";
+ case how{
+ StepExp =>
+ if(src != stk[0].srcstr())
+ return "";
+ StepStmt =>
+ if(stmt != ftostmt(stk[0]))
+ return "";
+ if(stk[0].offset != stack[0].offset)
+ return "";
+ StepOut =>
+ if(returned(stack, stk))
+ return "";
+ StepOver =>
+ if(stk[0].offset == stack[0].offset){
+ if(stmt != ftostmt(stk[0]))
+ return "";
+ buf = array of byte("step1");
+ break;
+ }
+ if(returned(stack, stk))
+ return "";
+ buf = array of byte("toret");
+ * =>
+ return "";
+ }
+ }
+ return sprint("%r");
+}
+
+Prog.stop(p: self ref Prog): string
+{
+ return dbgctl(p, "stop");
+}
+
+Prog.unstop(p: self ref Prog): string
+{
+ return dbgctl(p, "unstop");
+}
+
+Prog.grab(p: self ref Prog): string
+{
+ return dbgctl(p, "step0");
+}
+
+Prog.start(p: self ref Prog): string
+{
+ return dbgctl(p, "start");
+}
+
+Prog.cont(p: self ref Prog): string
+{
+ return dbgctl(p, "cont");
+}
+
+dbgctl(p: ref Prog, msg: string): string
+{
+ b := array of byte msg;
+ while(sys->write(p.dbgctl, b, len b) != len b)
+ return sprint("%r");
+ return "";
+}
+
+returned(old, new: array of ref Exp): int
+{
+ n := len old;
+ if(n > len new)
+ return 1;
+ return 0;
+}
+
+Prog.setbpt(p: self ref Prog, dis: string, pc:int): string
+{
+ b := array of byte("bpt set "+dis+" "+string pc);
+ if(sys->write(p.dbgctl, b, len b) != len b)
+ return sprint("can't set breakpoint: %r");
+ return "";
+}
+
+Prog.delbpt(p: self ref Prog, dis: string, pc:int): string
+{
+ b := array of byte("bpt del "+dis+" "+string pc);
+ if(sys->write(p.dbgctl, b, len b) != len b)
+ return sprint("can't del breakpoint: %r");
+ return "";
+}
+
+Prog.kill(p: self ref Prog): string
+{
+ b := array of byte "kill";
+ if(sys->write(p.ctl, b, len b) != len b)
+ return sprint("can't kill process: %r");
+ return "";
+}
+
+Prog.event(p: self ref Prog): string
+{
+ b := array[100] of byte;
+ n := sys->read(p.dbgctl, b, len b);
+ if(n < 0)
+ return sprint("error: %r");
+ return string b[:n];
+}
+
+ftostmt(e: ref Exp): int
+{
+ m := e.m;
+ if(!m.comp && m.sym != nil && e.pc < len m.sym.srcstmt)
+ return m.sym.srcstmt[e.pc];
+ return -1;
+}
+
+Exp.srcstr(e: self ref Exp): string
+{
+ m := e.m;
+ if(!m.comp && m.sym != nil && e.pc < len m.sym.src){
+ src := m.sym.src[e.pc];
+ ss := src.start.file+":"+string src.start.line+"."+string src.start.pos+", ";
+ if(src.stop.file != src.start.file)
+ ss += src.stop.file+":"+string src.stop.line+".";
+ else if(src.stop.line != src.start.line)
+ ss += string src.stop.line+".";
+ return ss+string src.stop.pos;
+ }
+ return sprint("Module %s PC %d", e.m.path, e.pc);
+}
+
+Exp.findsym(e: self ref Exp): string
+{
+ m := e.m;
+ if(m.comp)
+ return "compiled module";
+ if(m.sym != nil){
+ n := e.pc;
+ fns := m.sym.fns;
+ for(i := 0; i < len fns; i++){
+ if(n >= fns[i].offset && n < fns[i].stoppc){
+ e.name = fns[i].name;
+ e.id = fns[i];
+ return "";
+ }
+ }
+ return "pc out of bounds";
+ }
+ return "no symbol file";
+}
+
+Exp.src(e: self ref Exp): ref Src
+{
+ m := e.m;
+ if(e.id == nil || m.sym == nil)
+ return nil;
+ src := e.id.src;
+ if(src != nil)
+ return src;
+ if(e.id.t.kind == Tfn && !m.comp && e.pc < len m.sym.src && e.pc >= 0)
+ return m.sym.src[e.pc];
+ return nil;
+}
+
+Type.getkind(t: self ref Type, sym: ref Sym): int
+{
+ if(t == nil)
+ return -1;
+ if(t.kind == Tid)
+ return sym.adts[int t.name].getkind(sym);
+ return t.kind;
+}
+
+Type.text(t: self ref Type, sym: ref Sym): string
+{
+ if (t == nil)
+ return "no type";
+ s := typenames[t.kind];
+ case t.kind {
+ Tadt or
+ Tadtpick or
+ Tmodule =>
+ s = t.name;
+ Tid =>
+ return sym.adts[int t.name].text(sym);
+ Tarray or
+ Tlist or
+ Tchan or
+ Tslice =>
+ s += " of " + t.Of.text(sym);
+ Tref =>
+ s += " " + t.Of.text(sym);
+ Tfn =>
+ s += "(";
+ for(i := 0; i < len t.ids; i++)
+ s += t.ids[i].name + ": " + t.ids[i].t.text(sym);
+ s += "): " + t.Of.text(sym);
+ Ttuple or
+ Tlocal or
+ Tglobal or
+ Targ =>
+ if(t.kind == Ttuple)
+ s = "";
+ s += "(";
+ for (i := 0; i < len t.ids; i++) {
+ s += t.ids[i].t.text(sym);
+ if (i < len t.ids - 1)
+ s += ", ";
+ }
+ s += ")";
+ }
+ return s;
+}
+
+Exp.typename(e: self ref Exp): string
+{
+ if (e.id == nil)
+ return "no info";
+ return e.id.t.text(e.m.sym);
+}
+
+Exp.kind(e: self ref Exp): int
+{
+ if(e.id == nil)
+ return -1;
+ return e.id.t.getkind(e.m.sym);
+}
+
+EXPLISTMAX : con 32; # what's a good value for this ?
+
+Exp.expand(e: self ref Exp): array of ref Exp
+{
+ if(e.id == nil)
+ return nil;
+
+ t := e.id.t;
+ if(t.kind == Tid)
+ t = e.m.sym.adts[int t.name];
+
+ off := e.offset;
+ ids := t.ids;
+ case t.kind{
+ Tadt or Tfn or Targ or Tlocal or Ttuple =>
+ break;
+ Tadtpick =>
+ break;
+ Tglobal =>
+ ids = e.m.sym.vars;
+ off = e.m.data;
+ Tmodule =>
+ (s, err) := pdata(e.p, off, "M");
+ if(s == "nil" || err != "")
+ return nil;
+ off = hex(array of byte s);
+ Tref =>
+ (s, err) := pdata(e.p, off, "P");
+ if(s == "nil" || err != "")
+ return nil;
+ off = hex(array of byte s);
+ et := t.Of;
+ if(et.kind == Tid)
+ et = e.m.sym.adts[int et.name];
+ ids = et.ids;
+ if(et.kind == Tadtpick){
+ (s, err) = pdata(e.p, off, "W");
+ tg := int s;
+ if(tg < 0 || tg > len et.tags || err != "" )
+ return nil;
+ k := array[1 + len ids + len et.tags[tg].ids] of ref Exp;
+ k[0] = ref Exp(et.tags[tg].name, off+0, e.pc, e.m, e.p, ref Id(et.src, et.tags[tg].name, 0, 0, tint));
+ x := 1;
+ for(i := 0; i < len ids; i++){
+ id := ids[i];
+ k[i+x] = ref Exp(id.name, off+id.offset, e.pc, e.m, e.p, id);
+ }
+ x += len ids;
+ ids = et.tags[tg].ids;
+ for(i = 0; i < len ids; i++){
+ id := ids[i];
+ k[i+x] = ref Exp(id.name, off+id.offset, e.pc, e.m, e.p, id);
+ }
+ return k;
+ }
+ Tlist =>
+ (s, err) := pdata(e.p, off, "L");
+ if(err != "")
+ return nil;
+ (tloff, hdoff) := str->splitl(s, ".");
+ hdoff = hdoff[1:];
+ k := array[2] of ref Exp;
+ k[0] = ref Exp("hd", hex(array of byte hdoff), e.pc, e.m, e.p, ref Id(nil, "hd", H, H, t.Of));
+ k[1] = ref Exp("tl", hex(array of byte tloff), e.pc, e.m, e.p, ref Id(nil, "tl", H, H, t));
+ return k;
+ Tarray =>
+ (s, err) := pdata(e.p, e.offset, "A");
+ if(s == "nil")
+ return nil;
+ (sn, sa) := str->splitl(s, ".");
+ n := int sn;
+ if(sa == "" || n <= 0)
+ return nil;
+ (off, nil) = str->toint(sa[1:], 16);
+ et := t.Of;
+ if(et.kind == Tid)
+ et = e.m.sym.adts[int et.name];
+ esize := et.size;
+ if (n <= EXPLISTMAX || EXPLISTMAX == 0) {
+ k := array[n] of ref Exp;
+ for(i := 0; i < n; i++){
+ name := string i;
+ k[i] = ref Exp(name, off+i*esize, e.pc, e.m, e.p, ref Id(nil, name, H, H, et));
+ }
+ return k;
+ }
+ else {
+ # slice it
+ (p, q, r) := partition(n, EXPLISTMAX);
+ lb := 0;
+ k := array[p] of ref Exp;
+ st := ref Type(et.src, Tslice, 0, nil, et, nil, nil);
+ for (i := 0; i < p; i++){
+ ub := lb+q-1;
+ if (--r >= 0)
+ ub++;
+ name := string lb + ".." + string ub;
+ k[i] = ref Exp(name, off+lb*esize, e.pc, e.m, e.p, ref Id(nil, name, H, H, st));
+ lb = ub+1;
+ }
+ return k;
+ }
+ Tslice =>
+ (lb, ub) := bounds(e.name);
+ if (lb > ub)
+ return nil;
+ n := ub-lb+1;
+ et := t.Of;
+ if(et.kind == Tid)
+ et = e.m.sym.adts[int et.name];
+ esize := et.size;
+ if (n <= EXPLISTMAX || EXPLISTMAX == 0) {
+ k := array[n] of ref Exp;
+ for(i := 0; i < n; i++){
+ name := string (i+lb);
+ k[i] = ref Exp(name, off+i*esize, e.pc, e.m, e.p, ref Id(nil, name, H, H, et));
+ }
+ return k;
+ }
+ else {
+ # slice it again
+ (p, q, r) := partition(n, EXPLISTMAX);
+ lb0 := lb;
+ k := array[p] of ref Exp;
+ st := ref Type(et.src, Tslice, 0, nil, et, nil, nil);
+ for (i := 0; i < p; i++){
+ ub = lb+q-1;
+ if (--r >= 0)
+ ub++;
+ name := string lb + ".." + string ub;
+ k[i] = ref Exp(name, off+(lb-lb0)*esize, e.pc, e.m, e.p, ref Id(nil, name, H, H, st));
+ lb = ub+1;
+ }
+ return k;
+ }
+ Tchan =>
+ (s, err) := pdata(e.p, e.offset, "c");
+ if(s == "nil")
+ return nil;
+ (sn, sa) := str->splitl(s, ".");
+ n := int sn;
+ if(sa == "" || n <= 0)
+ return nil;
+ (off, nil) = str->toint(sa[1:], 16);
+ (nil, sa) = str->splitl(sa[1:], ".");
+ (sn, sa) = str->splitl(sa[1:], ".");
+ f := int sn;
+ sz := int sa[1:];
+ et := t.Of;
+ if(et.kind == Tid)
+ et = e.m.sym.adts[int et.name];
+ esize := et.size;
+ k := array[sz] of ref Exp;
+ for(i := 0; i < sz; i++){
+ name := string i;
+ j := (f+i)%n;
+ k[i] = ref Exp(name, off+j*esize, e.pc, e.m, e.p, ref Id(nil, name, H, H, et));
+ }
+ return k;
+ * =>
+ return nil;
+ }
+ k := array[len ids] of ref Exp;
+ for(i := 0; i < len k; i++){
+ id := ids[i];
+ k[i] = ref Exp(id.name, off+id.offset, e.pc, e.m, e.p, id);
+ }
+ return k;
+}
+
+Exp.val(e: self ref Exp): (string, int)
+{
+ if(e.id == nil)
+ return (e.m.path+" unknown fn", 0);
+ t := e.id.t;
+ if(t.kind == Tid)
+ t = e.m.sym.adts[int t.name];
+
+ w := 0;
+ s := "";
+ err := "";
+ p := e.p;
+ case t.kind{
+ Tfn =>
+ if(t.ids != nil)
+ w = 1;
+ src := e.m.sym.src[e.pc];
+ ss := src.start.file+":"+string src.start.line+"."+string src.start.pos+", ";
+ if(src.stop.file != src.start.file)
+ ss += src.stop.file+":"+string src.stop.line+".";
+ else if(src.stop.line != src.start.line)
+ ss += string src.stop.line+".";
+ return (ss+string src.stop.pos, w);
+ Targ or Tlocal or Tglobal or Tadtpick or Ttuple =>
+ return ("", 1);
+ Tadt =>
+ return ("#" + string e.offset, 1);
+ Tnil =>
+ s = "nil";
+ Tbyte =>
+ (s, err) = pdata(p, e.offset, "B");
+ Tint =>
+ (s, err) = pdata(p, e.offset, "W");
+ Tbig =>
+ (s, err) = pdata(p, e.offset, "V");
+ Treal =>
+ (s, err) = pdata(p, e.offset, "R");
+ Tarray =>
+ (s, err) = pdata(p, e.offset, "A");
+ if(s == "nil")
+ break;
+ (n, a) := str->splitl(s, ".");
+ if(a == "")
+ return ("", 0);
+ s = "["+n+"] @"+a[1:];
+ w = 1;
+ Tslice =>
+ (lb, ub) := bounds(e.name);
+ s = sys->sprint("[:%d] @ %x", ub-lb+1, e.offset);
+ w = 1;
+ Tstring =>
+ n : int;
+ (n, s, err) = pstring(p, e.offset);
+ if(err != "")
+ return ("", 0);
+ for(i := 0; i < len s; i++)
+ if(s[i] == '\n')
+ s[i] = '\u008a';
+ s = "["+string n+"] \""+s+"\"";
+ Tref or Tlist or Tmodule or Tpoly=>
+ (s, err) = pdata(p, e.offset, "P");
+ if(s == "nil")
+ break;
+ s = "@" + s;
+ w = 1;
+ Tchan =>
+ (s, err) = pdata(p, e.offset, "c");
+ if(s == "nil")
+ break;
+ (n, a) := str->splitl(s, ".");
+ if(a == "")
+ return ("", 0);
+ if(n == "0"){
+ s = "@" + a[1:];
+ w = 0;
+ }
+ else{
+ (a, nil) = str->splitl(a[1:], ".");
+ s = "["+n+"] @"+a;
+ w = 1;
+ }
+ }
+ if(err != "")
+ return ("", 0);
+ return (s, w);
+}
+
+Sym.srctopc(s: self ref Sym, src: ref Src): int
+{
+ srcs := s.src;
+ line := src.start.line;
+ pos := src.start.pos;
+ (nil, file) := str->splitr(src.start.file, "/");
+ backup := -1;
+ delta := 80;
+ for(i := 0; i < len srcs; i++){
+ ss := srcs[i];
+ if(ss.start.file != file)
+ continue;
+ if(ss.start.line <= line && ss.start.pos <= pos
+ && ss.stop.line >= line && ss.stop.pos >= pos)
+ return i;
+ d := ss.start.line - line;
+ if(d >= 0 && d < delta){
+ delta = d;
+ backup = i;
+ }
+ }
+ return backup;
+}
+
+Sym.pctosrc(s: self ref Sym, pc: int): ref Src
+{
+ if(pc < 0 || pc >= len s.src)
+ return nil;
+ return s.src[pc];
+}
+
+sym(sbl: string): (ref Sym, string)
+{
+ h := 0;
+ for(i := 0; i < len sbl; i++)
+ h = (h << 1) + sbl[i];
+ h &= SymHash - 1;
+ for(sl := syms[h]; sl != nil; sl = tl sl){
+ s := hd sl;
+ if(sbl == s.path)
+ return (s, "");
+ }
+ (sy, err) := loadsyms(sbl);
+ if(err != "")
+ return (nil, err);
+ syms[h] = sy :: syms[h];
+ return (sy, "");
+}
+
+Module.addsym(m: self ref Module, sym: ref Sym)
+{
+ m.sym = sym;
+}
+
+Module.sbl(m: self ref Module): string
+{
+ if(m.sym != nil)
+ return m.sym.path;
+ return "";
+}
+
+Module.dis(m: self ref Module): string
+{
+ return m.path;
+}
+
+findsbl(dis: string): string
+{
+ n := len dis;
+ if(n <= 4 || dis[n-4: n] != ".dis")
+ dis += ".dis";
+ if(dism == nil){
+ dism = load Dis Dis->PATH;
+ if(dism != nil)
+ dism->init();
+ }
+ if(dism != nil && (b := dism->src(dis)) != nil){
+ n = len b;
+ if(n > 2 && b[n-2: n] == ".b"){
+ sbl := b[0: n-2] + ".sbl";
+ if(sys->open(sbl, Sys->OREAD) != nil)
+ return sbl;
+ }
+ }
+ return nil;
+}
+
+Module.stdsym(m: self ref Module)
+{
+ if(m.sym != nil)
+ return;
+ if((sbl := findsbl(m.path)) != nil){
+ (m.sym, nil) = sym(sbl);
+ return;
+ }
+ sbl = m.path;
+ n := len sbl;
+ if(n > 4 && sbl[n-4:n] == ".dis")
+ sbl = sbl[:n-4]+".sbl";
+ else
+ sbl = sbl+".sbl";
+ path := sbl;
+ fd := sys->open(sbl, sys->OREAD);
+ for(i := 0; fd == nil && i < len sblpath; i++){
+ (dis, src) := sblpath[i];
+ nd := len dis;
+ if(len sbl > nd && sbl[:nd] == dis){
+ path = src + sbl[nd:];
+ fd = sys->open(path, sys->OREAD);
+ }
+ }
+ if(fd == nil)
+ return;
+ (m.sym, nil) = sym(path);
+}
+
+mkmod(data, code, comp: int, dis: string): ref Module
+{
+ h := 0;
+ for(i := 0; i < len dis; i++)
+ h = (h << 1) + dis[i];
+ h &= ModHash - 1;
+ sym : ref Sym;
+ for(ml := mods[h]; ml != nil; ml = tl ml){
+ m := hd ml;
+ if(m.path == dis && m.code == code && m.comp == comp){
+ sym = m.sym;
+ if(m.data == data)
+ return m;
+ }
+ }
+ m := ref Module(dis, code, data, comp, sym);
+ mods[h] = m :: mods[h];
+ return m;
+}
+
+pdata(p: ref Prog, a: int, fmt: string): (string, string)
+{
+ b := array of byte sprint("0x%ux.%s1", a, fmt);
+ if(sys->write(p.heap, b, len b) != len b)
+ return ("", sprint("can't write heap: %r"));
+
+ buf := array[64] of byte;
+ sys->seek(p.heap, big 0, 0);
+ n := sys->read(p.heap, buf, len buf);
+ if(n <= 1)
+ return ("", sprint("can't read heap: %r"));
+ return (string buf[:n-1], "");
+}
+
+pstring0(p: ref Prog, a: int, blen: int): (int, string, string)
+{
+ b := array of byte sprint("0x%ux.C1", a);
+ if(sys->write(p.heap, b, len b) != len b)
+ return (-1, "", sprint("can't write heap: %r"));
+
+ buf := array[blen] of byte;
+ sys->seek(p.heap, big 0, 0);
+ n := sys->read(p.heap, buf, len buf-1);
+ if(n <= 1)
+ return (-1, "", sprint("can't read heap: %r"));
+ buf[n] = byte 0;
+ m := strchr(buf, 0, '.');
+ if(buf[m++] != byte '.')
+ m = 0;
+ return (int string buf[0:m], string buf[m:n], "");
+}
+
+pstring(p: ref Prog, a: int): (int, string, string)
+{
+ m, n: int;
+ s, err: string;
+
+ m = 64;
+ for(;;){
+ (n, s, err) = pstring0(p, a, m);
+ if(err != "" || n <= len s)
+ break;
+ m *= 2;
+ }
+ return (n, s, err);
+}
+
+Prog.status(p: self ref Prog): (int, string, string, string)
+{
+ fd := sys->open(sprint("/prog/%d/status", p.id), sys->OREAD);
+ if(fd == nil)
+ return (-1, "", sprint("can't open status file: %r"), "");
+ buf := array[256] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n <= 0)
+ return (-1, "", sprint("can't read status file: %r"), "");
+ (ni, info) := sys->tokenize(string buf[:n], " \t");
+ if(ni != 6 && ni != 7)
+ return (-1, "", "can't parse status file", "");
+ info = tl info;
+ if(ni == 6)
+ return (int hd info, hd tl info, hd tl tl info, hd tl tl tl tl info);
+ return (int hd info, hd tl info, hd tl tl tl info, hd tl tl tl tl tl info);
+}
+
+loadsyms(sbl: string): (ref Sym, string)
+{
+ fd := sys->open(sbl, sys->OREAD);
+ if(fd == nil)
+ return (nil, sprint("Can't open symbol file '%s': %r", sbl));
+
+ (ok, dir) := sys->fstat(fd);
+ if(ok < 0)
+ return (nil, sprint("Can't read symbol file '%s': %r", sbl));
+ n := int dir.length;
+ buf := array[n+1] of byte;
+ if(sys->read(fd, buf, n) != n)
+ return (nil, sprint("Can't read symbol file '%s': %r", sbl));
+ fd = nil;
+ buf[n] = byte 0;
+
+ s := ref Sym;
+ s.path = sbl;
+
+ n = strchr(buf, 0, '\n');
+ vers := 0;
+ if(string buf[:n] == "limbo .sbl 1.")
+ vers = 10;
+ else if(string buf[:n] == "limbo .sbl 1.1")
+ vers = 11;
+ else if(string buf[:n] == "limbo .sbl 2.0")
+ vers = 20;
+ else if(string buf[:n] == "limbo .sbl 2.1")
+ vers = 21;
+ else
+ return (nil, "Symbol file "+sbl+" out of date");
+ o := n += 1;
+ n = strchr(buf, o, '\n');
+ if(buf[n] != byte '\n')
+ return (nil, "Corrupted symbol file "+sbl);
+ s.name = string buf[o:n++];
+ ss := ref SrcState(nil, 0, 0, vers);
+ err : string;
+ if(n >= 0){
+ err = "file";
+ n = debugfiles(ss, buf, n);
+ }
+ if(n >= 0){
+ err = "pc";
+ n = debugpc(ss, s, buf, n);
+ }
+ if(n >= 0){
+ err = "types";
+ n = debugtys(ss, s, buf, n);
+ }
+ if(n >= 0){
+ err = "fn";
+ n = debugfns(ss, s, buf, n);
+ }
+ vs: array of ref Id;
+ if(n >= 0){
+ err = "global";
+ (vs, n) = debugid(ss, buf, n);
+ }
+ if(n < 0)
+ return (nil, "Corrupted "+err+" symbol table in "+sbl);
+ s.vars = vs;
+ return (s, "");
+}
+
+#
+# parse a source location
+# format[file:][line.]pos,[file:][line.]pos' '
+#
+debugsrc(ss: ref SrcState, buf: array of byte, p: int): (ref Src, int)
+{
+ n: int;
+ src: ref Src;
+
+ (n, p) = strtoi(buf, p);
+ if(buf[p] == byte ':'){
+ ss.lastf = n;
+ (n, p) = strtoi(buf, p + 1);
+ }
+ if(buf[p] == byte '.'){
+ ss.lastl = n;
+ (n, p) = strtoi(buf, p + 1);
+ }
+ if(buf[p++] != byte ',' || ss.lastf >= len ss.files || ss.lastf < 0)
+ return (nil, -1);
+ src = ref Src;
+ src.start.file = ss.files[ss.lastf];
+ src.start.line = ss.lastl;
+ src.start.pos = n;
+
+ (n, p) = strtoi(buf, p);
+ if(buf[p] == byte ':'){
+ ss.lastf = n;
+ (n, p) = strtoi(buf, p+1);
+ }
+ if(buf[p] == byte '.'){
+ ss.lastl = n;
+ (n, p) = strtoi(buf, p + 1);
+ }
+ if(buf[p++] != byte ' ' || ss.lastf >= len ss.files || ss.lastf < 0)
+ return (nil, -1);
+ src.stop.file = ss.files[ss.lastf];
+ src.stop.line = ss.lastl;
+ src.stop.pos = n;
+ return (src, p);
+}
+
+#
+# parse the file table
+# item format: file: string
+#
+debugfiles(ss: ref SrcState, buf: array of byte, p: int): int
+{
+ n, q: int;
+
+ (n, p) = strtoi(buf, p);
+ if(buf[p++] != byte '\n')
+ return -1;
+ ss.files = array[n] of string;
+ for(i := 0; i < n; i++){
+ q = strchr(buf, p, '\n');
+ ss.files[i] = string buf[p:q];
+ p = q + 1;
+ }
+ return p;
+}
+
+#
+# parse the pc to source table
+# item format: Source stmt
+#
+debugpc(ss: ref SrcState, s: ref Sym, buf: array of byte, p: int): int
+{
+ ns: int;
+
+ (ns, p) = strtoi(buf, p);
+ if(buf[p++] != byte '\n')
+ return -1;
+ s.src = array[ns] of ref Src;
+ s.srcstmt = array[ns] of int;
+ for(i := 0; i < ns; i++){
+ (s.src[i], p) = debugsrc(ss, buf, p);
+ if(p < 0)
+ return -1;
+ (s.srcstmt[i], p) = strtoi(buf, p);
+ if(buf[p++] != byte '\n')
+ return -1;
+ }
+ return p;
+}
+
+#
+# parse the type table
+# format: linear list of types
+#
+debugtys(ss: ref SrcState, s: ref Sym, buf: array of byte, p: int): int
+{
+ na: int;
+
+ (na, p) = strtoi(buf, p);
+ if(buf[p++] != byte '\n')
+ return -1;
+ s.adts = array[na] of ref Type;
+ adts := s.adts;
+ for(i := 0; i < na; i++){
+ if(ss.vers < 20)
+ (adts[i], p) = debugadt(ss, buf, p);
+ else
+ (adts[i], p) = debugtype(ss, buf, p);
+ if(p < 0)
+ return -1;
+ }
+ return p;
+}
+
+#
+# parse the function table
+# format: pc:name:argids localids rettype
+#
+debugfns(ss: ref SrcState, s: ref Sym, buf: array of byte, p: int): int
+{
+ t: ref Type;
+ args, locals: array of ref Id;
+ nf, pc, q: int;
+
+ (nf, p) = strtoi(buf, p);
+ if(buf[p++] != byte '\n')
+ return -1;
+ s.fns = array[nf] of ref Id;
+ fns := s.fns;
+ for(i := 0; i < nf; i++){
+ (pc, p) = strtoi(buf, p);
+ if(buf[p++] != byte ':')
+ return -2;
+ q = strchr(buf, p, '\n');
+ if(buf[q] != byte '\n')
+ return -3;
+ name := string buf[p:q];
+ (args, p) = debugid(ss, buf, q + 1);
+ if(p == -1)
+ return -4;
+ (locals, p) = debugid(ss, buf, p);
+ if(p == -1)
+ return -5;
+ (t, p) = debugtype(ss, buf, p);
+ if(p == -1)
+ return -6;
+ nk := 1 + (len args != 0) + (len locals != 0);
+ kids := array[nk] of ref Id;
+ nk = 0;
+ if(len locals != 0)
+ kids[nk++] = ref Id(nil, "locals", 0, 0, ref Type(nil, Tlocal, 0, nil, nil, locals, nil));
+ if(len args != 0)
+ kids[nk++] = ref Id(nil, "args", 0, 0, ref Type(nil, Targ, 0, nil, nil, args, nil));
+ kids[nk++] = ref Id(nil, "module", 0, 0, ref Type(nil, Tglobal, 0, nil, nil, nil, nil));
+ args = nil;
+ locals = nil;
+ fns[i] = ref Id(nil, name, pc, 0, ref Type(nil, Tfn, 0, name, t, kids, nil));
+ }
+ for(i = 1; i < nf; i++)
+ fns[i-1].stoppc = fns[i].offset;
+ fns[i-1].stoppc = len s.src;
+ return p;
+}
+
+#
+# parse a list of ids
+# format: offset ':' name ':' src type '\n'
+#
+debugid(ss: ref SrcState, buf: array of byte, p: int): (array of ref Id, int)
+{
+ t: ref Type;
+ off, nd, q, qq, tq: int;
+ src: ref Src;
+
+ (nd, p) = strtoi(buf, p);
+ if(buf[p++] != byte '\n')
+ return (nil, -1);
+ d := array[nd] of ref Id;
+ for(i := 0; i < nd; i++){
+ (off, q) = strtoi(buf, p);
+ if(buf[q++] != byte ':')
+ return (nil, -1);
+ qq = strchr(buf, q, ':');
+ if(buf[qq] != byte ':')
+ return (nil, -1);
+ tq = qq + 1;
+ if(ss.vers > 10){
+ (src, tq) = debugsrc(ss, buf, tq);
+ if(tq < 0)
+ return (nil, -1);
+ }
+ (t, p) = debugtype(ss, buf, tq);
+ if(p == -1 || buf[p++] != byte '\n')
+ return (nil, -1);
+ d[i] = ref Id(src, string buf[q:qq], off, 0, t);
+ }
+ return (d, p);
+}
+
+idlist(a: array of ref Id): list of ref Id
+{
+ n := len a;
+ ids : list of ref Id = nil;
+ while(n-- > 0)
+ ids = a[n] :: ids;
+ return ids;
+}
+
+#
+# parse a type description
+#
+debugtype(ss: ref SrcState, buf: array of byte, p: int): (ref Type, int)
+{
+ t: ref Type;
+ d: array of ref Id;
+ q, k: int;
+ src: ref Src;
+
+ size := 0;
+ case int buf[p++]{
+ '@' =>
+ k = Tid;
+ 'A' =>
+ k = Tarray;
+ size = IBY2WD;
+ 'B' =>
+ return (tbig, p);
+ 'C' => k = Tchan;
+ size = IBY2WD;
+ 'L' =>
+ k = Tlist;
+ size = IBY2WD;
+ 'N' =>
+ return (tnil, p);
+ 'R' =>
+ k = Tref;
+ size = IBY2WD;
+ 'a' =>
+ k = Tadt;
+ if(ss.vers < 20)
+ size = -1;
+ 'b' =>
+ return (tbyte, p);
+ 'f' =>
+ return (treal, p);
+ 'i' =>
+ return (tint, p);
+ 'm' =>
+ k = Tmodule;
+ size = IBY2WD;
+ 'n' =>
+ return (tnone, p);
+ 'p' =>
+ k = Tadtpick;
+ 's' =>
+ return (tstring, p);
+ 't' =>
+ k = Ttuple;
+ size = -1;
+ 'F' =>
+ k = Tfn;
+ size = IBY2WD;
+ 'P' =>
+ return (tpoly, p);
+ * =>
+ k = Terror;
+ }
+
+ if(size == -1){
+ q = strchr(buf, p, '.');
+ if(buf[q] == byte '.'){
+ size = int string buf[p:q];
+ p = q+1;
+ }
+ }
+
+ case k{
+ Tid =>
+ q = strchr(buf, p, '\n');
+ if(buf[q] != byte '\n')
+ return (nil, -1);
+ t = ref Type(nil, Tid, -1, string buf[p:q], nil, nil, nil);
+ p = q + 1;
+ Tadt =>
+ if(ss.vers < 20){
+ q = strchr(buf, p, '\n');
+ if(buf[q] != byte '\n')
+ return (nil, -1);
+ t = ref Type(nil, Tid, size, string buf[p:q], nil, nil, nil);
+ p = q + 1;
+ }else
+ (t, p) = debugadt(ss, buf, p);
+ Tadtpick =>
+ (t, p) = debugadt(ss, buf, p);
+ t.kind = Tadtpick;
+ (t.tags, p) = debugtag(ss, buf, p);
+ Tmodule =>
+ q = strchr(buf, p, '\n');
+ if(buf[q] != byte '\n')
+ return (nil, -1);
+ t = ref Type(nil, k, size, string buf[p:q], nil, nil, nil);
+ p = q + 1;
+ if(ss.vers > 10){
+ (src, p) = debugsrc(ss, buf, p);
+ t.src = src;
+ }
+ if(ss.vers > 20)
+ (t.ids, p) = debugid(ss, buf, p);
+ Tref or Tarray or Tlist or Tchan => # ref, array, list, chan
+ (t, p) = debugtype(ss, buf, p);
+ t = ref Type(nil, k, size, "", t, nil, nil);
+
+ Ttuple => # tuple
+ (d, p) = debugid(ss, buf, p);
+ t = ref Type(nil, k, size, "", nil, d, nil);
+
+ Tfn => # fn
+ (d, p) = debugid(ss, buf, p);
+ (t, p) = debugtype(ss, buf, p);
+ t = ref Type(nil, k, size, "", t, d, nil);
+
+ * =>
+ p = -1;
+ }
+ return (t, p);
+}
+
+#
+# parse an adt type spec
+# format: name ' ' src size '\n' ids
+#
+debugadt(ss: ref SrcState, buf: array of byte, p: int): (ref Type, int)
+{
+ src: ref Src;
+
+ q := strchr(buf, p, ' ');
+ if(buf[q] != byte ' ')
+ return (nil, -1);
+ sq := q + 1;
+ if(ss.vers > 10){
+ (src, sq) = debugsrc(ss, buf, sq);
+ if(sq < 0)
+ return (nil, -1);
+ }
+ qq := strchr(buf, sq, '\n');
+ if(buf[qq] != byte '\n')
+ return (nil, -1);
+ (d, pp) := debugid(ss, buf, qq + 1);
+ if(pp == -1)
+ return (nil, -1);
+ t := ref Type(src, Tadt, int string buf[sq:qq], string buf[p:q], nil, d, nil);
+ return (t, pp);
+}
+
+#
+# parse a list of tags
+# format:
+# name ':' src size '\n' ids
+# or
+# name ':' src '\n'
+#
+debugtag(ss: ref SrcState, buf: array of byte, p: int): (array of ref Type, int)
+{
+ d: array of ref Id;
+ ntg, q, pp, np: int;
+ src: ref Src;
+
+ (ntg, p) = strtoi(buf, p);
+ if(buf[p++] != byte '\n')
+ return (nil, -1);
+ tg := array[ntg] of ref Type;
+ for(i := 0; i < ntg; i++){
+ pp = strchr(buf, p, ':');
+ if(buf[pp] != byte ':')
+ return (nil, -1);
+ q = pp + 1;
+ (src, q) = debugsrc(ss, buf, q);
+ if(q < 0)
+ return (nil, -1);
+ if(buf[q] == byte '\n'){
+ np = q + 1;
+ if(i <= 0)
+ return (nil, -1);
+ tg[i] = ref Type(src, Tadt, tg[i-1].size, string buf[p:pp], nil, tg[i-1].ids, nil);
+ }else{
+ np = strchr(buf, q, '\n');
+ if(buf[np] != byte '\n')
+ return (nil, -1);
+ size := int string buf[q:np];
+ (d, np) = debugid(ss, buf, np+1);
+ if(np == -1)
+ return (nil, -1);
+ tg[i] = ref Type(src, Tadt, size, string buf[p:pp], nil, d, nil);
+ }
+ p = np;
+ }
+ return (tg, p);
+}
+
+strchr(a: array of byte, p, c: int): int
+{
+ bc := byte c;
+ while((b := a[p]) != byte 0 && b != bc)
+ p++;
+ return p;
+}
+
+strtoi(a: array of byte, start: int): (int, int)
+{
+ p := start;
+ for(; c := int a[p]; p++){
+ case c{
+ ' ' or '\t' or '\n' or '\r' =>
+ continue;
+ }
+ break;
+ }
+
+ # sign
+ neg := c == '-';
+ if(neg || c == '+')
+ p++;
+
+ # digits
+ n := 0;
+ nn := 0;
+ ndig := 0;
+ over := 0;
+ for(; c = int a[p]; p++){
+ if(c < '0' || c > '9')
+ break;
+ ndig++;
+ nn = n * 10 + (c - '0');
+ if(nn < n)
+ over = 1;
+ n = nn;
+ }
+ if(ndig == 0)
+ return (0, start);
+ if(neg)
+ n = -n;
+ if(over)
+ if(neg)
+ n = 2147483647;
+ else
+ n = int -2147483648;
+ return (n, p);
+}
+
+hex(a: array of byte): int
+{
+ n := 0;
+ for(i := 0; i < len a; i++){
+ c := int a[i];
+ if(c >= '0' && c <= '9')
+ c -= '0';
+ else
+ c -= 'a' - 10;
+ n = (n << 4) + (c & 15);
+ }
+ return n;
+}
+
+partition(n : int, max : int) : (int, int, int)
+{
+ p := n/max;
+ if (n%max != 0)
+ p++;
+ if (p > max)
+ p = max;
+ q := n/p;
+ r := n-p*q;
+ return (p, q, r);
+}
+
+bounds(s : string) : (int, int)
+{
+ lb := int s;
+ for (i := 0; i < len s; i++)
+ if (s[i] == '.')
+ break;
+ if (i+1 >= len s || s[i] != '.' || s[i+1] != '.')
+ return (1, 0);
+ ub := int s[i+2:];
+ return (lb, ub);
+}
diff --git a/appl/lib/deflate.b b/appl/lib/deflate.b
new file mode 100644
index 00000000..ccfeafbe
--- /dev/null
+++ b/appl/lib/deflate.b
@@ -0,0 +1,1367 @@
+# gzip-compatible compression filter.
+
+implement Filter;
+
+include "sys.m";
+ sys: Sys;
+
+include "filter.m";
+
+GZMAGIC1: con byte 16r1f;
+GZMAGIC2: con byte 16r8b;
+
+GZDEFLATE: con byte 8;
+
+GZFTEXT: con byte 1 << 0; # file is text
+GZFHCRC: con byte 1 << 1; # crc of header included
+GZFEXTRA: con byte 1 << 2; # extra header included
+GZFNAME: con byte 1 << 3; # name of file included
+GZFCOMMENT: con byte 1 << 4; # header comment included
+GZFMASK: con (byte 1 << 5) - byte 1; # mask of specified bits
+
+GZXFAST: con byte 2; # used fast algorithm little compression
+GZXBEST: con byte 4; # used maximum compression algorithm
+
+GZOSFAT: con byte 0; # FAT file system
+GZOSAMIGA: con byte 1; # Amiga
+GZOSVMS: con byte 2; # VMS or OpenVMS
+GZOSUNIX: con byte 3; # Unix
+GZOSVMCMS: con byte 4; # VM/CMS
+GZOSATARI: con byte 5; # Atari TOS
+GZOSHPFS: con byte 6; # HPFS file system
+GZOSMAC: con byte 7; # Macintosh
+GZOSZSYS: con byte 8; # Z-System
+GZOSCPM: con byte 9; # CP/M
+GZOSTOPS20: con byte 10; # TOPS-20
+GZOSNTFS: con byte 11; # NTFS file system
+GZOSQDOS: con byte 12; # QDOS
+GZOSACORN: con byte 13; # Acorn RISCOS
+GZOSUNK: con byte 255;
+
+GZCRCPOLY: con int 16redb88320;
+GZOSINFERNO: con GZOSUNIX;
+
+
+LZstate: adt
+{
+ hist: array of byte; # [HistSize];
+ epos: int; # end of history buffer
+ pos: int; # current location in history buffer
+ eof: int;
+ hash: array of int; # [Nhash] hash chains
+ nexts: array of int; # [MaxOff]
+ me: int; # pos in hash chains
+ dot: int; # dawn of time in history
+ prevlen: int; # lazy matching state
+ prevoff: int;
+ maxchars: int; # compressor tuning
+ maxdefer: int;
+
+ crctab: array of int;
+ crc: int;
+ tot: int;
+ headers: int;
+
+ outbuf: array of byte; # current output buffer;
+ out: int; # current position in the output buffer
+ bits: int; # bit shift register
+ nbits: int;
+
+ verbose: int;
+ debug: int;
+
+ lzb: ref LZblock;
+ slop: array of byte;
+ dlitlentab: array of Huff; # [Nlitlen]
+ dofftab: array of Huff; # [Noff];
+ hlitlentab: array of Huff; # [Nlitlen];
+ dyncode: ref Dyncode;
+ hdyncode: ref Dyncode;
+ c: chan of ref Rq;
+ rc: chan of int;
+};
+
+#
+# lempel-ziv compressed block
+#
+LZblock: adt
+{
+ litlen: array of byte; # [MaxUncBlock+1];
+ off: array of int; # [MaxUncBlock+1];
+ litlencount: array of int; # [Nlitlen];
+ offcount: array of int; # [Noff];
+ entries: int; # entries in litlen & off tables
+ bytes: int; # consumed from the input
+ excost: int; # cost of encoding extra len & off bits
+};
+
+#
+# encoding of dynamic huffman trees
+#
+Dyncode: adt
+{
+ nlit: int;
+ noff: int;
+ nclen: int;
+ ncode: int;
+ codetab: array of Huff; # [Nclen];
+ codes: array of byte; # [Nlitlen+Noff];
+ codeaux: array of byte; # [Nlitlen+Noff];
+};
+
+#
+# huffman code table
+#
+Huff: adt
+{
+ bits: int; # length of the code
+ encode: int; # the code
+};
+
+DeflateBlock: con 64*1024-258-1;
+DeflateOut: con 258+10;
+
+
+DeflateUnc: con 0; # uncompressed block
+DeflateFix: con 1; # fixed huffman codes
+DeflateDyn: con 2; # dynamic huffman codes
+
+DeflateEob: con 256; # end of block code in lit/len book
+
+LenStart: con 257; # start of length codes in litlen
+Nlitlen: con 288; # number of litlen codes
+Noff: con 30; # number of offset codes
+Nclen: con 19; # number of codelen codes
+
+MaxLeaf: con Nlitlen;
+MaxHuffBits: con 15; # max bits in a huffman code
+ChainMem: con 2 * MaxHuffBits * (MaxHuffBits + 1);
+
+MaxUncBlock: con 64*1024-1; # maximum size of uncompressed block
+
+MaxOff: con 32*1024;
+MinMatch: con 3; # shortest match possible
+MaxMatch: con 258; # longest match possible
+MinMatchMaxOff: con 4096; # max profitable offset for small match;
+ # assumes 8 bits for len; 5+10 for offset
+HistSlop: con 4096; # slop for fewer calls to lzcomp
+HistSize: con MaxOff + 2*HistSlop;
+
+Hshift: con 4; # nice compromise between space & time
+Nhash: con 1<<(Hshift*MinMatch);
+Hmask: con Nhash-1;
+
+MaxOffCode: con 256; # biggest offset looked up in direct table
+
+EstLitBits: con 8;
+EstLenBits: con 4;
+EstOffBits: con 5;
+
+# conversion from len to code word
+lencode := array[MaxMatch] of int;
+
+#
+# conversion from off to code word
+# off <= MaxOffCode ? offcode[off] : bigoffcode[(off-1) >> 7]
+#
+offcode := array[MaxOffCode + 1] of int;
+bigoffcode := array[256] of int;
+
+# litlen code words LenStart-285 extra bits
+litlenbase := array[Nlitlen-LenStart] of int;
+litlenextra := array[Nlitlen-LenStart] of
+{
+ 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 1, 1, 2,
+ 2, 2, 2, 3, 3, 3, 3, 4, 4, 4,
+ 4, 5, 5, 5, 5, 0, 0, 0
+};
+
+# offset code word extra bits
+offbase := array[Noff] of int;
+offextra := array[] of
+{
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3,
+ 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 9, 10, 10, 11, 11, 12, 12, 13, 13,
+ 0, 0,
+};
+
+# order code lengths
+clenorder := array[Nclen] of
+{
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+};
+
+# static huffman tables
+litlentab : array of Huff;
+offtab : array of Huff;
+hofftab : array of Huff;
+
+# bit reversal for brain dead endian swap in huffman codes
+revtab: array of byte;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+
+ bitcount := array[MaxHuffBits] of int;
+ i, j, ci, n: int;
+
+ # byte reverse table
+ revtab = array[256] of byte;
+ for(i=0; i<256; i++){
+ revtab[i] = byte 0;
+ for(j=0; j<8; j++)
+ if(i & (1<<j))
+ revtab[i] |= byte 16r80 >> j;
+ }
+
+ litlentab = array[Nlitlen] of Huff;
+ offtab = array[Noff] of Huff;
+ hofftab = array[Noff] of { * => Huff(0, 0) };
+
+ # static Litlen bit lengths
+ for(i=0; i<144; i++)
+ litlentab[i].bits = 8;
+ for(i=144; i<256; i++)
+ litlentab[i].bits = 9;
+ for(i=256; i<280; i++)
+ litlentab[i].bits = 7;
+ for(i=280; i<Nlitlen; i++)
+ litlentab[i].bits = 8;
+
+ for(i = 0; i < 10; i++)
+ bitcount[i] = 0;
+ bitcount[8] += 144 - 0;
+ bitcount[9] += 256 - 144;
+ bitcount[7] += 280 - 256;
+ bitcount[8] += Nlitlen - 280;
+
+ hufftabinit(litlentab, Nlitlen, bitcount, 9);
+
+ # static offset bit lengths
+ for(i = 0; i < Noff; i++)
+ offtab[i].bits = 5;
+
+ for(i = 0; i < 5; i++)
+ bitcount[i] = 0;
+ bitcount[5] = Noff;
+
+ hufftabinit(offtab, Noff, bitcount, 5);
+
+ bitcount[0] = 0;
+ bitcount[1] = 0;
+ mkprecode(hofftab, bitcount, 2, MaxHuffBits);
+
+ # conversion tables for lens & offs to codes
+ ci = 0;
+ for(i = LenStart; i < 286; i++){
+ n = ci + (1 << litlenextra[i - LenStart]);
+ litlenbase[i - LenStart] = ci;
+ for(; ci < n; ci++)
+ lencode[ci] = i;
+ }
+ # patch up special case for len MaxMatch
+ lencode[MaxMatch-MinMatch] = 285;
+ litlenbase[285-LenStart] = MaxMatch-MinMatch;
+
+ ci = 1;
+ for(i = 0; i < 16; i++){
+ n = ci + (1 << offextra[i]);
+ offbase[i] = ci;
+ for(; ci < n; ci++)
+ offcode[ci] = i;
+ }
+
+ ci = (LenStart - 1) >> 7;
+ for(; i < 30; i++){
+ n = ci + (1 << (offextra[i] - 7));
+ offbase[i] = (ci << 7) + 1;
+ for(; ci < n; ci++)
+ bigoffcode[ci] = i;
+ }
+}
+
+start(param: string): chan of ref Rq
+{
+ # param contains flags:
+ # [0-9] - compression level
+ # v verbose
+ # d debug
+ lz := ref LZstate;
+ level := 6;
+ lz.verbose = lz.debug = 0;
+ lz.headers = 0;
+ lz.crc = lz.tot = 0;
+ # XXX could also put filename and modification time in param
+ for (i := 0; i < len param; i++) {
+ case param[i] {
+ '0' to '9' =>
+ level = param[i] - '0';
+ 'v' =>
+ lz.verbose = 1;
+ 'h' =>
+ lz.headers = 1;
+ 'd' =>
+ lz.debug = 1;
+ }
+ }
+
+ lz.hist = array[HistSize] of byte;
+ lz.hash = array[Nhash] of int;
+ lz.nexts = array[MaxOff] of int;
+ lz.slop = array[2*MaxMatch] of byte;
+ lz.dlitlentab = array[Nlitlen] of Huff;
+ lz.dofftab = array[Noff] of Huff;
+ lz.hlitlentab = array[Nlitlen] of Huff;
+
+ lz.lzb = ref LZblock;
+ lzb := lz.lzb;
+ lzb.litlen = array[MaxUncBlock+1] of byte;
+ lzb.off = array[MaxUncBlock+1] of int;
+ lzb.litlencount = array[Nlitlen] of int;
+ lzb.offcount = array[Noff] of int;
+
+ lz.dyncode = ref Dyncode;
+ lz.dyncode.codetab =array[Nclen] of Huff;
+ lz.dyncode.codes =array[Nlitlen+Noff] of byte;
+ lz.dyncode.codeaux = array[Nlitlen+Noff] of byte;
+ lz.hdyncode = ref Dyncode;
+ lz.hdyncode.codetab =array[Nclen] of Huff;
+ lz.hdyncode.codes =array[Nlitlen+Noff] of byte;
+ lz.hdyncode.codeaux = array[Nlitlen+Noff] of byte;
+
+ for(i = 0; i < MaxOff; i++)
+ lz.nexts[i] = 0;
+ for(i = 0; i < Nhash; i++)
+ lz.hash[i] = 0;
+ lz.pos = 0;
+ lz.epos = 0;
+ lz.prevlen = MinMatch - 1;
+ lz.prevoff = 0;
+ lz.eof = 0;
+ lz.me = 4 * MaxOff;
+ lz.dot = lz.me;
+ lz.bits = 0;
+ lz.nbits = 0;
+ if(level < 5) {
+ lz.maxchars = 1;
+ lz.maxdefer = 0;
+ } else if(level == 9) {
+ lz.maxchars = 4000;
+ lz.maxdefer = MaxMatch;
+ } else {
+ lz.maxchars = 200;
+ lz.maxdefer = MaxMatch / 4;
+ }
+ if (lz.headers)
+ lz.crctab = mkcrctab(GZCRCPOLY);
+ lz.c = chan of ref Rq;
+ lz.rc = chan of int;
+ spawn deflate(lz);
+ return lz.c;
+}
+
+# return (eof, nbytes)
+fillbuf(lz: ref LZstate, buf: array of byte): (int, int)
+{
+ n := 0;
+ while (n < len buf) {
+ lz.c <-= ref Rq.Fill(buf[n:], lz.rc);
+ nr := <-lz.rc;
+ if (nr == -1)
+ exit;
+ if (nr == 0)
+ return (1, n);
+ n += nr;
+ }
+ return (0, n);
+}
+
+deflate(lz: ref LZstate)
+{
+ lz.c <-= ref Rq.Start(sys->pctl(0, nil));
+
+ if (lz.headers)
+ header(lz);
+ buf := array[DeflateBlock] of byte;
+ out := array[DeflateBlock + DeflateOut] of byte;
+ eof := 0;
+ for (;;) {
+ nslop := lz.epos - lz.pos;
+ nbuf := 0;
+ if (!eof) {
+ (eof, nbuf) = fillbuf(lz, buf);
+ if (lz.headers) # added by Roman Joel Pacheco
+ inblock(lz, buf[0:nbuf]);
+ }
+ if(eof && nbuf == 0 && nslop == 0) {
+ if(lz.nbits) {
+ out[0] = byte lz.bits;
+ lz.nbits = 0;
+ lz.c <-= ref Rq.Result(out[0:1], lz.rc);
+ if (<-lz.rc == -1)
+ exit;
+ continue;
+ }
+ if (lz.headers)
+ footer(lz);
+ lz.c <-= ref Rq.Finished(nil);
+ exit;
+ }
+
+ lz.outbuf = out;
+
+ if(nslop > 2*MaxMatch) {
+ lz.c <-= ref Rq.Error(sys->sprint("slop too large: %d", nslop));
+ exit;
+ }
+ lz.slop[0:] = lz.hist[lz.pos:lz.epos]; # memmove(slop, lz.pos, nslop);
+
+ lzb := lz.lzb;
+ for(i := 0; i < Nlitlen; i++)
+ lzb.litlencount[i] = 0;
+ for(i = 0; i < Noff; i++)
+ lzb.offcount[i] = 0;
+ lzb.litlencount[DeflateEob]++;
+
+ lzb.bytes = 0;
+ lzb.entries = 0;
+ lzb.excost = 0;
+ lz.eof = 0;
+
+ n := 0;
+ while(n < nbuf || eof && !lz.eof){
+ if(!lz.eof) {
+ if(lz.pos >= MaxOff + HistSlop) {
+ lz.pos -= MaxOff + HistSlop;
+ lz.epos -= MaxOff + HistSlop;
+ lz.hist[:] = lz.hist[MaxOff + HistSlop: MaxOff + HistSlop + lz.epos];
+ }
+ m := HistSlop - (lz.epos - lz.pos);
+ if(lz.epos + m > HistSize) {
+ lz.c <-= ref Rq.Error("read too long");
+ exit;
+ }
+ if(m >= nbuf - n) {
+ m = nbuf - n;
+ lz.eof = eof;
+ }
+ lz.hist[lz.epos:] = buf[n:n+m];
+ n += m;
+ lz.epos += m;
+ }
+ lzcomp(lz, lzb, lz.epos - lz.pos);
+ }
+
+ lz.outbuf = out;
+ lz.out = 0;
+
+ nunc := lzb.bytes;
+ if(nunc < nslop)
+ nslop = nunc;
+
+ mkprecode(lz.dlitlentab, lzb.litlencount, Nlitlen, MaxHuffBits);
+ mkprecode(lz.dofftab, lzb.offcount, Noff, MaxHuffBits);
+
+ ndyn := huffcodes(lz.dyncode, lz.dlitlentab, lz.dofftab)
+ + bitcost(lz.dlitlentab, lzb.litlencount, Nlitlen)
+ + bitcost(lz.dofftab, lzb.offcount, Noff)
+ + lzb.excost;
+
+ litcount := array[Nlitlen] of int;
+ for(i = 0; i < Nlitlen; i++)
+ litcount[i] = 0;
+ for(i = 0; i < nslop; i++)
+ litcount[int lz.slop[i]]++;
+ for(i = 0; i < nunc-nslop; i++)
+ litcount[int buf[i]]++;
+ litcount[DeflateEob]++;
+
+ mkprecode(lz.hlitlentab, litcount, Nlitlen, MaxHuffBits);
+ nhuff := huffcodes(lz.hdyncode, lz.hlitlentab, hofftab)
+ + bitcost(lz.hlitlentab, litcount, Nlitlen);
+
+ nfix := bitcost(litlentab, lzb.litlencount, Nlitlen)
+ + bitcost(offtab, lzb.offcount, Noff)
+ + lzb.excost;
+
+ lzput(lz, lz.eof && lz.pos == lz.epos, 1);
+
+ if(lz.verbose) {
+ lz.c <-= ref Rq.Info(sys->sprint("block: %d bytes %d entries %d extra bits",
+ nunc, lzb.entries, lzb.excost));
+ lz.c <-= ref Rq.Info(sys->sprint("\tuncompressed %d fixed %d dynamic %d huffman %d",
+ (nunc + 4) * 8, nfix, ndyn, nhuff));
+ }
+
+ if((nunc + 4) * 8 < ndyn && (nunc + 4) * 8 < nfix && (nunc + 4) * 8 < nhuff) {
+ lzput(lz, DeflateUnc, 2);
+ lzflushbits(lz);
+
+ lz.outbuf[lz.out++] = byte(nunc);
+ lz.outbuf[lz.out++] = byte(nunc >> 8);
+ lz.outbuf[lz.out++] = byte(~nunc);
+ lz.outbuf[lz.out++] = byte(~nunc >> 8);
+
+ lz.outbuf[lz.out:] = lz.slop[:nslop];
+ lz.out += nslop;
+ lz.outbuf[lz.out:] = buf[:nunc - nslop];
+ lz.out += nunc - nslop;
+ } else if(ndyn < nfix && ndyn < nhuff) {
+ lzput(lz, DeflateDyn, 2);
+
+ wrdyncode(lz, lz.dyncode);
+ wrblock(lz, lzb.entries, lzb.litlen, lzb.off, lz.dlitlentab, lz.dofftab);
+ lzput(lz, lz.dlitlentab[DeflateEob].encode, lz.dlitlentab[DeflateEob].bits);
+ } else if(nhuff < nfix){
+ lzput(lz, DeflateDyn, 2);
+
+ wrdyncode(lz, lz.hdyncode);
+ for(i = 0; i < len lzb.off; i++)
+ lzb.off[i] = 0;
+
+ wrblock(lz, nslop, lz.slop, lzb.off, lz.hlitlentab, hofftab);
+ wrblock(lz, nunc-nslop, buf, lzb.off, lz.hlitlentab, hofftab);
+ lzput(lz, lz.hlitlentab[DeflateEob].encode, lz.hlitlentab[DeflateEob].bits);
+ } else {
+ lzput(lz, DeflateFix, 2);
+
+ wrblock(lz, lzb.entries, lzb.litlen, lzb.off, litlentab, offtab);
+ lzput(lz, litlentab[DeflateEob].encode, litlentab[DeflateEob].bits);
+ }
+
+ lz.c <-= ref Rq.Result(out[0:lz.out], lz.rc);
+ if (<-lz.rc == -1)
+ exit;
+ }
+}
+
+header(lz: ref LZstate)
+{
+ buf := array[20] of byte;
+ i := 0;
+ buf[i++] = byte GZMAGIC1;
+ buf[i++] = byte GZMAGIC2;
+ buf[i++] = byte GZDEFLATE;
+
+ flags := 0;
+ #if(file != nil)
+ # flags |= GZFNAME;
+ buf[i++] = byte flags;
+
+ mtime := 0;
+ buf[i++] = byte(mtime);
+ buf[i++] = byte(mtime>>8);
+ buf[i++] = byte(mtime>>16);
+ buf[i++] = byte(mtime>>24);
+
+ buf[i++] = byte 0;
+ buf[i++] = byte GZOSINFERNO;
+
+ #if((flags & GZFNAME) == GZFNAME){
+ # bout.puts(file);
+ # bout.putb(byte 0);
+ #}
+ lz.c <-= ref Rq.Result(buf[0:i], lz.rc);
+ if (<-lz.rc == -1)
+ exit;
+}
+
+footer(lz: ref LZstate)
+{
+ buf := array[8] of byte;
+ i := 0;
+ buf[i++] = byte(lz.crc);
+ buf[i++] = byte(lz.crc>>8);
+ buf[i++] = byte(lz.crc>>16);
+ buf[i++] = byte(lz.crc>>24);
+
+ buf[i++] = byte(lz.tot);
+ buf[i++] = byte(lz.tot>>8);
+ buf[i++] = byte(lz.tot>>16);
+ buf[i++] = byte(lz.tot>>24);
+ lz.c <-= ref Rq.Result(buf[0:i], lz.rc);
+ if (<-lz.rc == -1)
+ exit;
+}
+
+lzput(lz: ref LZstate, bits, nbits: int): int
+{
+ bits = (bits << lz.nbits) | lz.bits;
+ for(nbits += lz.nbits; nbits >= 8; nbits -= 8){
+ lz.outbuf[lz.out++] = byte bits;
+ bits >>= 8;
+ }
+ lz.bits = bits;
+ lz.nbits = nbits;
+ return 0;
+}
+
+lzflushbits(lz: ref LZstate): int
+{
+ if(lz.nbits & 7)
+ lzput(lz, 0, 8 - (lz.nbits & 7));
+ return 0;
+}
+
+#
+# write out a block of n samples,
+# given lz encoding and counts for huffman tables
+# todo: inline lzput
+#
+wrblock(lz: ref LZstate, n: int, litlen: array of byte, off: array of int, litlentab, offtab: array of Huff): int
+{
+ for(i := 0; i < n; i++) {
+ offset := off[i];
+ lit := int litlen[i];
+ if(lz.debug) {
+ if(offset == 0)
+ lz.c <-= ref Rq.Info(sys->sprint("\tlit %.2ux %c", lit, lit));
+ else
+ lz.c <-= ref Rq.Info(sys->sprint("\t<%d, %d>", offset, lit + MinMatch));
+ }
+ if(offset == 0)
+ lzput(lz, litlentab[lit].encode, litlentab[lit].bits);
+ else {
+ c := lencode[lit];
+ lzput(lz, litlentab[c].encode, litlentab[c].bits);
+ c -= LenStart;
+ if(litlenextra[c])
+ lzput(lz, lit - litlenbase[c], litlenextra[c]);
+
+ if(offset <= MaxOffCode)
+ c = offcode[offset];
+ else
+ c = bigoffcode[(offset - 1) >> 7];
+ lzput(lz, offtab[c].encode, offtab[c].bits);
+ if(offextra[c])
+ lzput(lz, offset - offbase[c], offextra[c]);
+ }
+ }
+
+ return n;
+}
+
+lzcomp(lz: ref LZstate, lzb: ref LZblock, max: int)
+{
+ q, s, es, t: int;
+ you, m: int;
+
+# hashcheck(lz, "start");
+
+ hist := lz.hist;
+ nexts := lz.nexts;
+ hash := lz.hash;
+ me := lz.me;
+
+ p := lz.pos;
+ ep := lz.epos;
+ if(p + max < ep)
+ ep = p + max;
+ if(lz.prevlen != MinMatch - 1)
+ p++;
+
+ #
+ # hash in the links for any hanging link positions,
+ # and calculate the hash for the current position.
+ #
+ n := MinMatch;
+ if(n > ep - p)
+ n = ep - p;
+ h := 0;
+ for(i := 0; i < n - 1; i++) {
+ m = me - ((MinMatch-1) - i);
+ if(m < lz.dot)
+ continue;
+ s = p - (me - m);
+ if(s < 0)
+ s += MaxOff + HistSlop;
+ h = hashit(s, hist);
+ for(you = hash[h]; me - you < me - m; you = nexts[you & (MaxOff-1)])
+ ;
+ if(you == m)
+ continue;
+ nexts[m & (MaxOff-1)] = hash[h];
+ hash[h] = m;
+ }
+ for(i = 0; i < n; i++)
+ h = ((h << Hshift) ^ int hist[p+i]) & Hmask;
+
+ #
+ # me must point to the index in the next/prev arrays
+ # corresponding to p's position in the history
+ #
+ entries := lzb.entries;
+ litlencount := lzb.litlencount;
+ offcount := lzb.offcount;
+ litlen := lzb.litlen;
+ off := lzb.off;
+ prevlen := lz.prevlen;
+ prevoff := lz.prevoff;
+ maxdefer := lz.maxdefer;
+ maxchars := lz.maxchars;
+ excost := 0;
+ for(;;) {
+ es = p + MaxMatch;
+ if(es > ep) {
+ if(!lz.eof || ep != lz.epos || p >= ep)
+ break;
+ es = ep;
+ }
+
+ #
+ # look for the longest, closest string which
+ # matches what we are going to send. the clever
+ # part here is looking for a string 1 longer than
+ # are previous best match.
+ #
+ runlen := prevlen;
+ m = 0;
+ chars := maxchars;
+ matchloop:
+ for(you = hash[h]; me-you <= MaxOff && chars > 0; you = nexts[you & (MaxOff-1)]) {
+ s = p + runlen;
+ if(s >= es)
+ break;
+ t = s - me + you;
+ if(t - runlen < 0)
+ t += MaxOff + HistSlop;
+ for(; s >= p; s--) {
+ if(hist[s] != hist[t]) {
+ chars -= p + runlen - s + 1;
+ continue matchloop;
+ }
+ t--;
+ }
+
+ #
+ # we have a new best match.
+ # extend it to it's maximum length
+ #
+ t += runlen + 2;
+ s += runlen + 2;
+ for(; s < es; s++) {
+ if(hist[s] != hist[t])
+ break;
+ t++;
+ }
+ runlen = s - p;
+ m = you;
+ if(s == es)
+ break;
+ if(runlen > 7)
+ chars >>= 1;
+ chars -= runlen;
+ }
+
+ #
+ # back out of small matches too far in the past
+ #
+ if(runlen == MinMatch && me - m >= MinMatchMaxOff) {
+ runlen = MinMatch - 1;
+ m = 0;
+ }
+
+ #
+ # record the encoding and increment counts for huffman trees
+ # if we get a match, defer selecting it until we check for
+ # a longer match at the next position.
+ #
+ if(prevlen >= runlen && prevlen != MinMatch - 1) {
+ #
+ # old match at least as good; use that one
+ #
+ n = prevlen - MinMatch;
+ litlen[entries] = byte n;
+ n = lencode[n];
+ litlencount[n]++;
+ excost += litlenextra[n - LenStart];
+
+ off[entries++] = prevoff;
+ if(prevoff <= MaxOffCode)
+ n = offcode[prevoff];
+ else
+ n = bigoffcode[(prevoff - 1) >> 7];
+ offcount[n]++;
+ excost += offextra[n];
+
+ runlen = prevlen - 1;
+ prevlen = MinMatch - 1;
+ } else if(runlen == MinMatch - 1) {
+ #
+ # no match; just put out the literal
+ #
+ n = int hist[p];
+ litlen[entries] = byte n;
+ litlencount[n]++;
+ off[entries++] = 0;
+ runlen = 1;
+ } else {
+ if(prevlen != MinMatch - 1) {
+ #
+ # longer match now. output previous literal,
+ # update current match, and try again
+ #
+ n = int hist[p - 1];
+ litlen[entries] = byte n;
+ litlencount[n]++;
+ off[entries++] = 0;
+ }
+
+ prevoff = me - m;
+
+ if(runlen < maxdefer) {
+ prevlen = runlen;
+ runlen = 1;
+ } else {
+ n = runlen - MinMatch;
+ litlen[entries] = byte n;
+ n = lencode[n];
+ litlencount[n]++;
+ excost += litlenextra[n - LenStart];
+
+ off[entries++] = prevoff;
+ if(prevoff <= MaxOffCode)
+ n = offcode[prevoff];
+ else
+ n = bigoffcode[(prevoff - 1) >> 7];
+ offcount[n]++;
+ excost += offextra[n];
+ prevlen = MinMatch - 1;
+ }
+ }
+
+ #
+ # update the hash for the newly matched data
+ # this is constructed so the link for the old
+ # match in this position must at the end of a chain,
+ # and will expire when this match is added, ie it will
+ # never be examined for by the match loop.
+ # add to the hash chain only if we have the real hash data.
+ #
+ for(q = p + runlen; p != q; p++) {
+ if(p + MinMatch <= ep) {
+ nexts[me & (MaxOff-1)] = hash[h];
+ hash[h] = me;
+ if(p + MinMatch < ep)
+ h = ((h << Hshift) ^ int hist[p + MinMatch]) & Hmask;
+ }
+ me++;
+ }
+ }
+
+ #
+ # we can just store away the lazy state and
+ # pick it up next time. the last block will have eof
+ # so we won't have any pending matches
+ # however, we need to correct for how much we've encoded
+ #
+ if(prevlen != MinMatch - 1)
+ p--;
+
+ lzb.excost += excost;
+ lzb.bytes += p - lz.pos;
+ lzb.entries = entries;
+
+ lz.pos = p;
+ lz.me = me;
+ lz.prevlen = prevlen;
+ lz.prevoff = prevoff;
+
+# hashcheck(lz, "stop");
+}
+
+#
+# check all the hash list invariants are really satisfied
+#
+hashcheck(lz: ref LZstate, where: string)
+{
+ s, age, a, you: int;
+
+ nexts := lz.nexts;
+ hash := lz.hash;
+ me := lz.me;
+ start := lz.pos;
+ if(lz.prevlen != MinMatch-1)
+ start++;
+ found := array [MaxOff] of byte;
+ for(i := 0; i < MaxOff; i++)
+ found[i] = byte 0;
+ for(i = 0; i < Nhash; i++) {
+ age = 0;
+ for(you = hash[i]; me-you <= MaxOff; you = nexts[you & (MaxOff-1)]) {
+ a = me - you;
+ if(a < age)
+ fatal(lz, sys->sprint("%s: out of order links age %d a %d me %d you %d",
+ where, age, a, me, you));
+
+ age = a;
+
+ s = start - a;
+ if(s < 0)
+ s += MaxOff + HistSlop;
+
+ if(hashit(s, lz.hist) != i)
+ fatal(lz, sys->sprint("%s: bad hash chain you %d me %d s %d start %d chain %d hash %d %d %d",
+ where, you, me, s, start, i, hashit(s - 1, lz.hist), hashit(s, lz.hist), hashit(s + 1, lz.hist)));
+
+ if(found[you & (MaxOff - 1)] != byte 0)
+ fatal(lz, where + ": found link again");
+ found[you & (MaxOff - 1)] = byte 1;
+ }
+ }
+
+ for(you = me - (MaxOff-1); you != me; you++)
+ found[you & (MaxOff - 1)] = byte 1;
+
+ for(i = 0; i < MaxOff; i++){
+ if(found[i] == byte 0 && nexts[i] != 0)
+ fatal(lz, sys->sprint("%s: link not found: max %d at %d", where, me & (MaxOff-1), i));
+ }
+}
+
+hashit(p: int, hist: array of byte): int
+{
+ h := 0;
+ for(ep := p + MinMatch; p < ep; p++)
+ h = ((h << Hshift) ^ int hist[p]) & Hmask;
+ return h;
+}
+
+#
+# make up the dynamic code tables, and return the number of bits
+# needed to transmit them.
+#
+huffcodes(dc: ref Dyncode, littab, offtab: array of Huff): int
+{
+ i, n, m, c, nlit, noff, ncode, nclen: int;
+
+ codetab := dc.codetab;
+ codes := dc.codes;
+ codeaux := dc.codeaux;
+
+ #
+ # trim the sizes of the tables
+ #
+ for(nlit = Nlitlen; nlit > 257 && littab[nlit-1].bits == 0; nlit--)
+ ;
+ for(noff = Noff; noff > 1 && offtab[noff-1].bits == 0; noff--)
+ ;
+
+ #
+ # make the code-length code
+ #
+ for(i = 0; i < nlit; i++)
+ codes[i] = byte littab[i].bits;
+ for(i = 0; i < noff; i++)
+ codes[i + nlit] = byte offtab[i].bits;
+
+ #
+ # run-length compress the code-length code
+ #
+ excost := 0;
+ c = 0;
+ ncode = nlit+noff;
+ for(i = 0; i < ncode; ) {
+ n = i + 1;
+ v := codes[i];
+ while(n < ncode && v == codes[n])
+ n++;
+ n -= i;
+ i += n;
+ if(v == byte 0) {
+ while(n >= 11) {
+ m = n;
+ if(m > 138)
+ m = 138;
+ codes[c] = byte 18;
+ codeaux[c++] = byte(m - 11);
+ n -= m;
+ excost += 7;
+ }
+ if(n >= 3) {
+ codes[c] = byte 17;
+ codeaux[c++] = byte(n - 3);
+ n = 0;
+ excost += 3;
+ }
+ }
+ while(n--) {
+ codes[c++] = v;
+ while(n >= 3) {
+ m = n;
+ if(m > 6)
+ m = 6;
+ codes[c] = byte 16;
+ codeaux[c++] = byte(m - 3);
+ n -= m;
+ excost += 3;
+ }
+ }
+ }
+
+ codecount := array[Nclen] of {* => 0};
+ for(i = 0; i < c; i++)
+ codecount[int codes[i]]++;
+ mkprecode(codetab, codecount, Nclen, 7);
+
+ for(nclen = Nclen; nclen > 4 && codetab[clenorder[nclen-1]].bits == 0; nclen--)
+ ;
+
+ dc.nlit = nlit;
+ dc.noff = noff;
+ dc.nclen = nclen;
+ dc.ncode = c;
+
+ return 5 + 5 + 4 + nclen * 3 + bitcost(codetab, codecount, Nclen) + excost;
+}
+
+wrdyncode(out: ref LZstate, dc: ref Dyncode)
+{
+ #
+ # write out header, then code length code lengths,
+ # and code lengths
+ #
+ lzput(out, dc.nlit-257, 5);
+ lzput(out, dc.noff-1, 5);
+ lzput(out, dc.nclen-4, 4);
+
+ codetab := dc.codetab;
+ for(i := 0; i < dc.nclen; i++)
+ lzput(out, codetab[clenorder[i]].bits, 3);
+
+ codes := dc.codes;
+ codeaux := dc.codeaux;
+ c := dc.ncode;
+ for(i = 0; i < c; i++){
+ v := int codes[i];
+ lzput(out, codetab[v].encode, codetab[v].bits);
+ if(v >= 16){
+ if(v == 16)
+ lzput(out, int codeaux[i], 2);
+ else if(v == 17)
+ lzput(out, int codeaux[i], 3);
+ else # v == 18
+ lzput(out, int codeaux[i], 7);
+ }
+ }
+}
+
+bitcost(tab: array of Huff, count: array of int, n: int): int
+{
+ tot := 0;
+ for(i := 0; i < n; i++)
+ tot += count[i] * tab[i].bits;
+ return tot;
+}
+
+hufftabinit(tab: array of Huff, n: int, bitcount: array of int, nbits: int)
+{
+ nc := array[MaxHuffBits + 1] of int;
+
+ code := 0;
+ for(bits := 1; bits <= nbits; bits++) {
+ code = (code + bitcount[bits-1]) << 1;
+ nc[bits] = code;
+ }
+
+ for(i := 0; i < n; i++) {
+ bits = tab[i].bits;
+ if(bits != 0) {
+ code = nc[bits]++ << (16 - bits);
+ tab[i].encode = int(revtab[code >> 8]) | (int(revtab[code & 16rff]) << 8);
+ }
+ }
+}
+
+Chain: adt
+{
+ count: int; # occurances of everything in the chain
+ leaf: int; # leaves to the left of chain, or leaf value
+ col: byte; # ref count for collecting unused chains
+ gen: byte; # need to generate chains for next lower level
+ up: int; # Chain up in the lists
+};
+
+Chains: adt
+{
+ lists: array of int; # [MaxHuffBits * 2]
+ chains: array of Chain; # [ChainMem]
+ nleaf: int; # number of leaves
+ free: int;
+ col: byte;
+ nlists: int;
+};
+
+Nil: con -1;
+
+#
+# fast, low space overhead algorithm for max depth huffman type codes
+#
+# J. Katajainen, A. Moffat and A. Turpin, "A fast and space-economical
+# algorithm for length-limited coding," Proc. Intl. Symp. on Algorithms
+# and Computation, Cairns, Australia, Dec. 1995, Lecture Notes in Computer
+# Science, Vol 1004, J. Staples, P. Eades, N. Katoh, and A. Moffat, eds.,
+# pp 12-21, Springer Verlag, New York, 1995.
+#
+mkprecode(tab: array of Huff, count: array of int, n, maxbits: int)
+{
+ cs := ref Chains(array[MaxHuffBits * 2] of int, array[MaxLeaf+ChainMem] of Chain, 0, 0, byte 0, 0);
+ bits: int;
+
+ for(i := 0; i < n; i++){
+ tab[i].bits = 0;
+ tab[i].encode = 0;
+ }
+
+ #
+ # set up the sorted list of leaves
+ #
+ m := 0;
+ for(i = 0; i < n; i++) {
+ if(count[i] != 0){
+ cs.chains[m].count = count[i];
+ cs.chains[m].leaf = i;
+ m++;
+ }
+ }
+ if(m < 2) {
+ if(m != 0) {
+ m = cs.chains[0].leaf;
+ tab[m].bits = 1;
+ tab[m].encode = 0;
+ }
+ return;
+ }
+ cs.nleaf = m;
+ csorts(cs.chains, 0, m);
+
+ cs.free = cs.nleaf + 2;
+ cs.col = byte 1;
+
+ #
+ # initialize chains for each list
+ #
+ c := cs.chains;
+ cl := cs.nleaf;
+ c[cl].count = cs.chains[0].count;
+ c[cl].leaf = 1;
+ c[cl].col = cs.col;
+ c[cl].up = Nil;
+ c[cl].gen = byte 0;
+ c[cl + 1] = c[cl];
+ c[cl + 1].leaf = 2;
+ c[cl + 1].count = cs.chains[1].count;
+ for(i = 0; i < maxbits; i++){
+ cs.lists[i * 2] = cl;
+ cs.lists[i * 2 + 1] = cl + 1;
+ }
+
+ cs.nlists = 2 * maxbits;
+ m = 2 * m - 2;
+ for(i = 2; i < m; i++)
+ nextchain(cs, cs.nlists - 2);
+
+ bitcount := array[MaxHuffBits + 1] of int;
+ bits = 0;
+ bitcount[0] = cs.nleaf;
+ for(cl = cs.lists[2 * maxbits - 1]; cl != Nil; cl = c[cl].up) {
+ m = c[cl].leaf;
+ for(i = 0; i < m; i++)
+ tab[cs.chains[i].leaf].bits++;
+ bitcount[bits++] -= m;
+ bitcount[bits] = m;
+ }
+
+ hufftabinit(tab, n, bitcount, bits);
+}
+
+#
+# calculate the next chain on the list
+# we can always toss out the old chain
+#
+nextchain(cs: ref Chains, clist: int)
+{
+ i, nleaf, sumc: int;
+
+ oc := cs.lists[clist + 1];
+ cs.lists[clist] = oc;
+ if(oc == Nil)
+ return;
+
+ #
+ # make sure we have all chains needed to make sumc
+ # note it is possible to generate only one of these,
+ # use twice that value for sumc, and then generate
+ # the second if that preliminary sumc would be chosen.
+ # however, this appears to be slower on current tests
+ #
+ chains := cs.chains;
+ if(chains[oc].gen != byte 0) {
+ nextchain(cs, clist - 2);
+ nextchain(cs, clist - 2);
+ chains[oc].gen = byte 0;
+ }
+
+ #
+ # pick up the chain we're going to add;
+ # collect unused chains no free ones are left
+ #
+ for(c := cs.free; ; c++) {
+ if(c >= ChainMem) {
+ cs.col++;
+ for(i = 0; i < cs.nlists; i++)
+ for(c = cs.lists[i]; c != Nil; c = chains[c].up)
+ chains[c].col = cs.col;
+ c = cs.nleaf;
+ }
+ if(chains[c].col != cs.col)
+ break;
+ }
+
+ #
+ # pick the cheapest of
+ # 1) the next package from the previous list
+ # 2) the next leaf
+ #
+ nleaf = chains[oc].leaf;
+ sumc = 0;
+ if(clist > 0 && cs.lists[clist-1] != Nil)
+ sumc = chains[cs.lists[clist-2]].count + chains[cs.lists[clist-1]].count;
+ if(sumc != 0 && (nleaf >= cs.nleaf || chains[nleaf].count > sumc)) {
+ chains[c].count = sumc;
+ chains[c].leaf = chains[oc].leaf;
+ chains[c].up = cs.lists[clist-1];
+ chains[c].gen = byte 1;
+ } else if(nleaf >= cs.nleaf) {
+ cs.lists[clist + 1] = Nil;
+ return;
+ } else {
+ chains[c].leaf = nleaf + 1;
+ chains[c].count = chains[nleaf].count;
+ chains[c].up = chains[oc].up;
+ chains[c].gen = byte 0;
+ }
+ cs.free = c + 1;
+
+ cs.lists[clist + 1] = c;
+ chains[c].col = cs.col;
+}
+
+chaincmp(chain: array of Chain, ai, bi: int): int
+{
+ ac := chain[ai].count;
+ bc := chain[bi].count;
+ if(ac < bc)
+ return -1;
+ if(ac > bc)
+ return 1;
+ ac = chain[ai].leaf;
+ bc = chain[bi].leaf;
+ if(ac > bc)
+ return -1;
+ return ac < bc;
+}
+
+pivot(chain: array of Chain, a, n: int): int
+{
+ j := n/6;
+ pi := a + j; # 1/6
+ j += j;
+ pj := pi + j; # 1/2
+ pk := pj + j; # 5/6
+ if(chaincmp(chain, pi, pj) < 0) {
+ if(chaincmp(chain, pi, pk) < 0) {
+ if(chaincmp(chain, pj, pk) < 0)
+ return pj;
+ return pk;
+ }
+ return pi;
+ }
+ if(chaincmp(chain, pj, pk) < 0) {
+ if(chaincmp(chain, pi, pk) < 0)
+ return pi;
+ return pk;
+ }
+ return pj;
+}
+
+csorts(chain: array of Chain, a, n: int)
+{
+ j, pi, pj, pn: int;
+
+ while(n > 1) {
+ if(n > 10)
+ pi = pivot(chain, a, n);
+ else
+ pi = a + (n>>1);
+
+ t := chain[pi];
+ chain[pi] = chain[a];
+ chain[a] = t;
+ pi = a;
+ pn = a + n;
+ pj = pn;
+ for(;;) {
+ do
+ pi++;
+ while(pi < pn && chaincmp(chain, pi, a) < 0);
+ do
+ pj--;
+ while(pj > a && chaincmp(chain, pj, a) > 0);
+ if(pj < pi)
+ break;
+ t = chain[pi];
+ chain[pi] = chain[pj];
+ chain[pj] = t;
+ }
+ t = chain[a];
+ chain[a] = chain[pj];
+ chain[pj] = t;
+ j = pj - a;
+
+ n = n-j-1;
+ if(j >= n) {
+ csorts(chain, a, j);
+ a += j+1;
+ } else {
+ csorts(chain, a + (j+1), n);
+ n = j;
+ }
+ }
+}
+
+mkcrctab(poly: int): array of int
+{
+ crctab := array[256] of int;
+ for(i := 0; i < 256; i++){
+ crc := i;
+ for(j := 0; j < 8; j++){
+ c := crc & 1;
+ crc = (crc >> 1) & 16r7fffffff;
+ if(c)
+ crc ^= poly;
+ }
+ crctab[i] = crc;
+ }
+ return crctab;
+}
+
+inblock(lz: ref LZstate, buf: array of byte)
+{
+ crc := lz.crc;
+ n := len buf;
+ crc ^= int 16rffffffff;
+ for(i := 0; i < n; i++)
+ crc = lz.crctab[int(byte crc ^ buf[i])] ^ ((crc >> 8) & 16r00ffffff);
+ lz.crc = crc ^ int 16rffffffff;
+ lz.tot += n;
+}
+
+fatal(lz: ref LZstate, s: string)
+{
+ lz.c <-= ref Rq.Error(s);
+ exit;
+}
diff --git a/appl/lib/devpointer.b b/appl/lib/devpointer.b
new file mode 100644
index 00000000..137f29f6
--- /dev/null
+++ b/appl/lib/devpointer.b
@@ -0,0 +1,123 @@
+implement Devpointer;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+ Pointer: import Draw;
+
+include "devpointer.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+reader(file: string, posn: chan of ref Pointer, pid: chan of (int, string))
+{
+ if(file == nil)
+ file = "/dev/pointer";
+ dfd := sys->open(file, sys->OREAD);
+ if(dfd == nil){
+ if(pid != nil){
+ pid <-= (-1, sys->sprint("cannot open %s: %r", file));
+ return;
+ }
+ }
+ if(pid != nil)
+ pid <-= (sys->pctl(0, nil), nil);
+ b:= array[Size] of byte;
+ while((n := sys->read(dfd, b, len b)) == Size)
+ posn <-= bytes2ptr(b);
+}
+
+bytes2ptr(b: array of byte): ref Pointer
+{
+ if(len b < Size || int b[0] != 'm')
+ return nil;
+ x := int string b[1:13];
+ y := int string b[13:25];
+ but := int string b[25:37];
+ msec := int string b[37:49];
+ return ref Pointer (but, (x, y), msec);
+}
+
+ptr2bytes(p: ref Pointer): array of byte
+{
+ if(p == nil)
+ return nil;
+ return sys->aprint("m%11d %11d %11d %11ud ", p.xy.x, p.xy.y, p.buttons, p.msec);
+}
+
+srv(c: chan of ref Pointer, f: ref Sys->FileIO)
+{
+ ptrq := ref Ptrqueue;
+ dummy := chan of (int, int, int, Sys->Rread);
+ sys = load Sys Sys->PATH;
+
+ for(;;){
+ r := dummy;
+ if(ptrq.nonempty())
+ r = f.read;
+ alt{
+ p := <-c =>
+ if(p == nil)
+ exit;
+ ptrq.put(p);
+ (nil, n, nil, rc) := <-r =>
+ if(rc != nil){
+ alt{
+ rc <-= (ptr2bytes(ptrq.get()), nil) =>;
+ * =>;
+ }
+ }
+ (nil, nil, nil, rc) := <-f.write =>
+ if(rc != nil)
+ rc <-= (0, "read only");
+ }
+ }
+}
+
+Ptrqueue.put(q: self ref Ptrqueue, s: ref Pointer)
+{
+ if(q.last != nil && s.buttons == q.last.buttons)
+ *q.last = *s;
+ else{
+ q.t = s :: q.t;
+ q.last = s;
+ }
+}
+
+Ptrqueue.get(q: self ref Ptrqueue): ref Pointer
+{
+ s: ref Pointer;
+ h := q.h;
+ if(h == nil){
+ for(t := q.t; t != nil; t = tl t)
+ h = hd t :: h;
+ q.t = nil;
+ }
+ if(h != nil){
+ s = hd h;
+ h = tl h;
+ if(h == nil)
+ q.last = nil;
+ }
+ q.h = h;
+ return s;
+}
+Ptrqueue.peek(q: self ref Ptrqueue): ref Pointer
+{
+ s: ref Pointer;
+ if (q.h == nil && q.t == nil)
+ return s;
+ t := q.last;
+ s = q.get();
+ q.h = s :: q.h;
+ q.last = t;
+ return s;
+}
+Ptrqueue.nonempty(q: self ref Ptrqueue): int
+{
+ return q.h != nil || q.t != nil;
+}
diff --git a/appl/lib/dhcpclient.b b/appl/lib/dhcpclient.b
new file mode 100644
index 00000000..e7c78108
--- /dev/null
+++ b/appl/lib/dhcpclient.b
@@ -0,0 +1,1039 @@
+implement Dhcpclient;
+
+#
+# DHCP and BOOTP clients
+# Copyright © 2004-2006 Vita Nuova Holdings Limited
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "ip.m";
+ ip: IP;
+ IPv4off, IPaddrlen, OUdphdrlen: import IP;
+ IPaddr: import ip;
+ get2, get4, put2, put4: import ip;
+
+include "keyring.m";
+include "security.m"; # for Random
+
+include "dhcp.m";
+
+debug := 0;
+
+#
+# format of UDP head read and written in `oldheaders' mode
+#
+Udphdrsize: con OUdphdrlen;
+Udpraddr: con 0;
+Udpladdr: con IPaddrlen;
+Udprport: con 2*IPaddrlen;
+Udplport: con 2*IPaddrlen+2;
+
+xidgen: int;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ random := load Random Random->PATH;
+ if(random != nil)
+ xidgen = random->randomint(Random->NotQuiteRandom);
+ else
+ xidgen = sys->pctl(0, nil)*sys->millisec();
+ random = nil;
+ ip = load IP IP->PATH;
+ ip->init();
+}
+
+tracing(d: int)
+{
+ debug = d;
+}
+
+Bootconf.new(): ref Bootconf
+{
+ bc := ref Bootconf;
+ bc.lease = 0;
+ bc.options = array[256] of array of byte;
+ return bc;
+}
+
+Bootconf.get(c: self ref Bootconf, n: int): array of byte
+{
+ a := c.options;
+ if(n & Ovendor){
+ a = c.vendor;
+ n &= ~Ovendor;
+ }
+ if(n < 0 || n >= len a)
+ return nil;
+ return a[n];
+}
+
+Bootconf.getint(c: self ref Bootconf, n: int): int
+{
+ a := c.get(n);
+ v := 0;
+ for(i := 0; i < len a; i++)
+ v = (v<<8) | int a[i];
+ return v;
+}
+
+Bootconf.getip(c: self ref Bootconf, n: int): string
+{
+ l := c.getips(n);
+ if(l == nil)
+ return nil;
+ return hd l;
+}
+
+Bootconf.getips(c: self ref Bootconf, n: int): list of string
+{
+ a := c.get(n);
+ rl: list of string;
+ while(len a >= 4){
+ rl = v4text(a) :: rl;
+ a = a[4:];
+ }
+ l: list of string;
+ for(; rl != nil; rl = tl rl)
+ l = hd rl :: l;
+ return l;
+}
+
+Bootconf.gets(c: self ref Bootconf, n: int): string
+{
+ a := c.get(n);
+ if(a == nil)
+ return nil;
+ for(i:=0; i<len a; i++)
+ if(a[i] == byte 0)
+ break;
+ return string a[0:i];
+}
+
+Bootconf.put(c: self ref Bootconf, n: int, a: array of byte)
+{
+ if(n < 0 || n >= len c.options)
+ return;
+ ca := array[len a] of byte;
+ ca[0:] = a;
+ c.options[n] = ca;
+}
+
+Bootconf.putint(c: self ref Bootconf, n: int, v: int)
+{
+ if(n < 0 || n >= len c.options)
+ return;
+ a := array[4] of byte;
+ put4(a, 0, v);
+ c.options[n] = a;
+}
+
+Bootconf.putips(c: self ref Bootconf, n: int, ips: list of string)
+{
+ if(n < 0 || n >= len c.options)
+ return;
+ na := len ips;
+ a := array[na*4] of byte;
+ na = 0;
+ for(; ips != nil; ips = tl ips){
+ (nil, ipa) := IPaddr.parse(hd ips);
+ a[na++:] = ipa.v4();
+ }
+ c.options[n] = a;
+}
+
+Bootconf.puts(c: self ref Bootconf, n: int, s: string)
+{
+ if(n < 0 || n >= len c.options)
+ return;
+ c.options[n] = array of byte s;
+}
+
+#
+#
+# DHCP
+#
+#
+
+# BOOTP operations
+Bootprequest, Bootpreply: con 1+iota;
+
+# DHCP operations
+NotDHCP, Discover, Offer, Request, Decline, Ack, Nak, Release, Inform: con iota;
+
+Dhcp: adt {
+ udphdr: array of byte;
+ op: int;
+ htype: int;
+ hops: int;
+ xid: int;
+ secs: int;
+ flags: int;
+ ciaddr: IPaddr;
+ yiaddr: IPaddr;
+ siaddr: IPaddr;
+ giaddr: IPaddr;
+ chaddr: array of byte;
+ sname: string;
+ file: string;
+ options: list of (int, array of byte);
+ dhcpop: int;
+};
+
+opnames := array[] of {
+ Discover => "Discover",
+ Offer => "Offer",
+ Request => "Request",
+ Decline => "Decline",
+ Ack => "Ack",
+ Nak => "Nak",
+ Release => "Release",
+ Inform => "Inform"
+};
+
+opname(op: int): string
+{
+ if(op >= 0 && op < len opnames)
+ return opnames[op];
+ return sys->sprint("OP%d", op);
+}
+
+stringget(buf: array of byte): string
+{
+ for(x := 0; x < len buf; x++)
+ if(buf[x] == byte 0)
+ break;
+ if(x == 0)
+ return nil;
+ return string buf[0 : x];
+}
+
+eqbytes(b1: array of byte, b2: array of byte): int
+{
+ l := len b1;
+ if(l != len b2)
+ return 0;
+ for(i := 0; i < l; i++)
+ if(b1[i] != b2[i])
+ return 0;
+ return 1;
+}
+
+magic := array[] of {byte 99, byte 130, byte 83, byte 99}; # RFC2132 (replacing RFC1048)
+
+dhcpsend(fd: ref Sys->FD, xid: int, dhcp: ref Dhcp)
+{
+ dhcp.xid = xid;
+ abuf := array[576+Udphdrsize] of {* => byte 0};
+ abuf[0:] = dhcp.udphdr;
+ buf := abuf[Udphdrsize:];
+ buf[0] = byte dhcp.op;
+ buf[1] = byte dhcp.htype;
+ buf[2] = byte len dhcp.chaddr;
+ buf[3] = byte dhcp.hops;
+ put4(buf, 4, xid);
+ put2(buf, 8, dhcp.secs);
+ put2(buf, 10, dhcp.flags);
+ buf[12:] = dhcp.ciaddr.v4();
+ buf[16:] = dhcp.yiaddr.v4();
+ buf[20:] = dhcp.siaddr.v4();
+ buf[24:] = dhcp.giaddr.v4();
+ buf[28:] = dhcp.chaddr;
+ buf[44:] = array of byte dhcp.sname; # [64]
+ buf[108:] = array of byte dhcp.file; # [128]
+ o := 236;
+ # RFC1542 suggests including magic and Oend as a minimum, even in BOOTP
+ buf[o:] = magic;
+ o += 4;
+ if(dhcp.dhcpop != NotDHCP){
+ buf[o++] = byte Otype;
+ buf[o++] = byte 1;
+ buf[o++] = byte dhcp.dhcpop;
+ }
+ for(ol := dhcp.options; ol != nil; ol = tl ol){
+ (opt, val) := hd ol;
+ buf[o++] = byte opt;
+ buf[o++] = byte len val;
+ if(len val > 0){
+ buf[o:] = val;
+ o += len val;
+ }
+ }
+ buf[o++] = byte Oend;
+ if(debug)
+ dumpdhcp(dhcp, "->");
+ sys->write(fd, abuf, len abuf);
+}
+
+kill(pid: int, grp: string)
+{
+ fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE);
+ if(fd != nil)
+ sys->fprint(fd, "kill%s", grp);
+}
+
+v4text(a: array of byte): string
+{
+ return sys->sprint("%ud.%ud.%ud.%ud", int a[0], int a[1], int a[2], int a[3]);
+}
+
+parseopt(a: array of byte, isdhcp: int): (int, list of (int, array of byte))
+{
+ opts: list of (int, array of byte);
+ xop := NotDHCP;
+ for(i := 0; i < len a;){
+ op := int a[i++];
+ if(op == Opad)
+ continue;
+ if(op == Oend || i >= len a)
+ break;
+ l := int a[i++];
+ if(i+l > len a)
+ break;
+ if(isdhcp && op == Otype)
+ xop = int a[i];
+ else
+ opts = (op, a[i:i+l]) :: opts;
+ i += l;
+ }
+ rl := opts;
+ opts = nil;
+ for(; rl != nil; rl = tl rl)
+ opts = hd rl :: opts;
+ return (xop, opts);
+}
+
+dhcpreader(pidc: chan of int, srv: ref DhcpIO)
+{
+ pidc <-= sys->pctl(0, nil);
+ for(;;){
+ abuf := array [576+Udphdrsize] of byte;
+ n := sys->read(srv.fd, abuf, len abuf);
+ if(n < 0){
+ if(debug)
+ sys->print("read error: %r\n");
+ sys->sleep(1000);
+ continue;
+ }
+ if(n < Udphdrsize+236){
+ if(debug)
+ sys->print("short read: %d\n", n);
+ continue;
+ }
+ buf := abuf[Udphdrsize:n];
+ n -= Udphdrsize;
+ dhcp := ref Dhcp;
+ dhcp.op = int buf[0];
+ if(dhcp.op != Bootpreply){
+ if(debug)
+ sys->print("bootp: not reply, discarded\n");
+ continue;
+ }
+ dhcp.dhcpop = NotDHCP;
+ if(n >= 240 && eqbytes(buf[236:240], magic)) # otherwise it's something we won't understand
+ (dhcp.dhcpop, dhcp.options) = parseopt(buf[240:n], 1);
+ case dhcp.dhcpop {
+ NotDHCP or Ack or Nak or Offer =>
+ ;
+ * =>
+ if(debug)
+ sys->print("dhcp: ignore dhcp op %d\n", dhcp.dhcpop);
+ continue;
+ }
+ dhcp.udphdr = abuf[0:Udphdrsize];
+ dhcp.htype = int buf[1];
+ hlen := int buf[2];
+ dhcp.hops = int buf[3];
+ dhcp.xid = get4(buf, 4);
+ dhcp.secs = get2(buf, 8);
+ dhcp.flags = get2(buf, 10);
+ dhcp.ciaddr = IPaddr.newv4(buf[12:]);
+ dhcp.yiaddr = IPaddr.newv4(buf[16:]);
+ dhcp.siaddr = IPaddr.newv4(buf[20:]);
+ dhcp.giaddr = IPaddr.newv4(buf[24:]);
+ dhcp.chaddr = buf[28 : 28 + hlen];
+ dhcp.sname = stringget(buf[44 : 108]);
+ dhcp.file = stringget(buf[108 : 236]);
+ srv.dc <-= dhcp;
+ }
+}
+
+timeoutstart(msecs: int): (int, chan of int)
+{
+ tc := chan of int;
+ spawn timeoutproc(tc, msecs);
+ return (<-tc, tc);
+}
+
+timeoutproc(c: chan of int, msecs: int)
+{
+ c <-= sys->pctl(0, nil);
+ sys->sleep(msecs);
+ c <-= 1;
+}
+
+hex(b: int): int
+{
+ if(b >= '0' && b <= '9')
+ return b-'0';
+ if(b >= 'A' && b <= 'F')
+ return b-'A' + 10;
+ if(b >= 'a' && b <= 'f')
+ return b-'a' + 10;
+ return -1;
+}
+
+gethaddr(device: string): (int, string, array of byte)
+{
+ fd := sys->open(device, Sys->OREAD);
+ if(fd == nil)
+ return (-1, sys->sprint("%r"), nil);
+ buf := array [100] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return (-1, sys->sprint("%r"), nil);
+ if(n == 0)
+ return (-1, "empty address file", nil);
+ addr := array [n/2] of byte;
+ for(i := 0; i < len addr; i++){
+ u := hex(int buf[2*i]);
+ l := hex(int buf[2*i+1]);
+ if(u < 0 || l < 0)
+ return (-1, "bad address syntax", nil);
+ addr[i] = byte ((u<<4)|l);
+ }
+ return (1, nil, addr);
+}
+
+newrequest(dest: IPaddr, bootfile: string, htype: int, haddr: array of byte, ipaddr: IPaddr, options: array of array of byte): ref Dhcp
+{
+ dhcp := ref Dhcp;
+ dhcp.op = Bootprequest;
+ hdr := array[Udphdrsize] of {* => byte 0};
+ hdr[Udpraddr:] = dest.v6();
+ put2(hdr, Udprport, 67);
+ dhcp.udphdr = hdr;
+ dhcp.htype = htype;
+ dhcp.chaddr = haddr;
+ dhcp.hops = 0;
+ dhcp.secs = 0;
+ dhcp.flags = 0;
+ dhcp.xid = 0;
+ dhcp.ciaddr = ipaddr;
+ dhcp.yiaddr = ip->v4noaddr;
+ dhcp.siaddr = ip->v4noaddr;
+ dhcp.giaddr = ip->v4noaddr;
+ dhcp.file = bootfile;
+ dhcp.dhcpop = NotDHCP;
+ if(options != nil){
+ for(i := 0; i < len options; i++)
+ if(options[i] != nil)
+ dhcp.options = (i, options[i]) :: dhcp.options;
+ }
+ clientid := array[len haddr + 1] of byte;
+ clientid[0] = byte htype;
+ clientid[1:] = haddr;
+ dhcp.options = (Oclientid, clientid) :: dhcp.options;
+ dhcp.options = (Ovendorclass, array of byte "plan9_386") :: dhcp.options; # 386 will do because type doesn't matter
+ return dhcp;
+}
+
+udpannounce(net: string): (ref Sys->FD, string)
+{
+ if(net == nil)
+ net = "/net";
+ (ok, conn) := sys->announce(net+"/udp!*!68");
+ if(ok < 0)
+ return (nil, sys->sprint("can't announce dhcp port: %r"));
+ if(sys->fprint(conn.cfd, "headers") < 0)
+ return (nil, sys->sprint("can't set headers mode on dhcp port: %r"));
+ sys->fprint(conn.cfd, "oldheaders");
+ conn.dfd = sys->open(conn.dir+"/data", Sys->ORDWR);
+ if(conn.dfd == nil)
+ return (nil, sys->sprint("can't open %s: %r", conn.dir+"/data"));
+ return (conn.dfd, nil);
+}
+
+ifcnoaddr(fd: ref Sys->FD, s: string)
+{
+ if(fd != nil && sys->fprint(fd, "%s %s %s", s, (ip->noaddr).text(), (ip->noaddr).text()) < 0){
+ if(debug)
+ sys->print("dhcp: ctl %s: %r\n", s);
+ }
+}
+
+setup(net: string, device: string, init: ref Bootconf): (ref Dhcp, ref DhcpIO, string)
+{
+ (htype, err, mac) := gethaddr(device);
+ if(htype < 0)
+ return (nil, nil, sys->sprint("can't get hardware MAC address: %s", err));
+ ciaddr := ip->v4noaddr;
+ if(init != nil && init.ip != nil){
+ valid: int;
+ (valid, ciaddr) = IPaddr.parse(init.ip);
+ if(valid < 0)
+ return (nil, nil, sys->sprint("invalid ip address: %s", init.ip));
+ }
+ (dfd, err2) := udpannounce(net);
+ if(err2 != nil)
+ return (nil, nil, err);
+ bootfile: string;
+ options: array of array of byte;
+ if(init != nil){
+ bootfile = init.bootf;
+ options = init.options;
+ }
+ return (newrequest(ip->v4bcast, bootfile, htype, mac, ciaddr, options), DhcpIO.new(dfd), nil);
+}
+
+#
+# BOOTP (RFC951) is used by Inferno only during net boots, to get initial IP address and TFTP address and parameters
+#
+bootp(net: string, ctlifc: ref Sys->FD, device: string, init: ref Bootconf): (ref Bootconf, string)
+{
+ (req, srv, err) := setup(net, device, init);
+ if(err != nil)
+ return (nil, err);
+ ifcnoaddr(ctlifc, "add");
+ rdhcp := exchange(srv, ++xidgen, req, 1<<NotDHCP);
+ srv.rstop();
+ ifcnoaddr(ctlifc, "remove");
+ if(rdhcp == nil)
+ return (nil, "no response to BOOTP request");
+ return (fillbootconf(init, rdhcp), nil);
+}
+
+defparams := array[] of {
+ byte Omask, byte Orouter, byte Odnsserver, byte Ohostname, byte Odomainname, byte Ontpserver,
+};
+
+#
+# DHCP (RFC2131)
+#
+dhcp(net: string, ctlifc: ref Sys->FD, device: string, init: ref Bootconf, needparam: array of int): (ref Bootconf, ref Lease, string)
+{
+ (req, srv, err) := setup(net, device, init);
+ if(err != nil)
+ return (nil, nil, err);
+ params := defparams;
+ if(needparam != nil){
+ n := len defparams;
+ params = array[n+len needparam] of byte;
+ params[0:] = defparams;
+ for(i := 0; i < len needparam; i++)
+ params[n+i] = byte needparam[i];
+ }
+ initopt := (Oparams, params) :: req.options; # RFC2131 requires parameters to be repeated each time
+ lease := ref Lease(0, chan[1] of (ref Bootconf, string));
+ spawn dhcp1(srv, lease, net, ctlifc, req, init, initopt);
+ bc: ref Bootconf;
+ (bc, err) = <-lease.configs;
+ return (bc, lease, err);
+}
+
+dhcp1(srv: ref DhcpIO, lease: ref Lease, net: string, ctlifc: ref Sys->FD, req: ref Dhcp, init: ref Bootconf, initopt: list of (int, array of byte))
+{
+ cfd := -1;
+ if(ctlifc != nil)
+ cfd = ctlifc.fd;
+ lease.pid = sys->pctl(Sys->NEWPGRP|Sys->NEWFD, 1 :: srv.fd.fd :: cfd :: nil);
+ if(ctlifc != nil)
+ ctlifc = sys->fildes(ctlifc.fd);
+ srv.fd = sys->fildes(srv.fd.fd);
+ rep: ref Dhcp;
+ ifcnoaddr(ctlifc, "add");
+ if(req.ciaddr.isvalid())
+ rep = reacquire(srv, req, initopt, req.ciaddr);
+ if(rep == nil)
+ rep = askround(srv, req, initopt);
+ srv.rstop();
+ ifcnoaddr(ctlifc, "remove");
+ if(rep == nil){
+ lease.pid = 0;
+ lease.configs <-= (nil, "no response");
+ exit;
+ }
+ for(;;){
+ conf := fillbootconf(init, rep);
+ applycfg(net, ctlifc, conf);
+ if(conf.lease == 0){
+ srv.rstop();
+ lease.pid = 0;
+ flush(lease.configs);
+ lease.configs <-= (conf, nil);
+ exit;
+ }
+ flush(lease.configs);
+ lease.configs <-= (conf, nil);
+ req.ciaddr = rep.yiaddr;
+ while((rep = tenancy(srv, req, conf.lease)) != nil){
+ if(rep.dhcpop == Nak || !rep.ciaddr.eq(req.ciaddr))
+ break;
+ req.udphdr[Udpraddr:] = rep.udphdr[Udpraddr:Udpraddr+IPaddrlen];
+ conf = fillbootconf(init, rep);
+ }
+ removecfg(net, ctlifc, conf);
+ ifcnoaddr(ctlifc, "add");
+ while((rep = askround(srv, req, initopt)) == nil){
+ flush(lease.configs);
+ lease.configs <-= (nil, "no response");
+ srv.rstop();
+ sys->sleep(60*1000);
+ }
+ ifcnoaddr(ctlifc, "remove");
+ }
+}
+
+reacquire(srv: ref DhcpIO, req: ref Dhcp, initopt: list of (int, array of byte), addr: IPaddr): ref Dhcp
+{
+ # INIT-REBOOT: know an address; try requesting it (once)
+ # TO DO: could use Inform when our address is static but we need a few service parameters
+ req.ciaddr = ip->v4noaddr;
+ rep := request(srv, ++xidgen, req, (Oipaddr, addr.v4()) :: initopt);
+ if(rep != nil && rep.dhcpop == Ack && addr.eq(rep.yiaddr)){
+ if(debug)
+ sys->print("req: server accepted\n");
+ req.udphdr[Udpraddr:] = rep.udphdr[Udpraddr:Udpraddr+IPaddrlen];
+ return rep;
+ }
+ if(debug)
+ sys->print("req: cannot reclaim\n");
+ return nil;
+}
+
+askround(srv: ref DhcpIO, req: ref Dhcp, initopt: list of (int, array of byte)): ref Dhcp
+{
+ # INIT
+ req.ciaddr = ip->v4noaddr;
+ req.udphdr[Udpraddr:] = (ip->v4bcast).v6();
+ for(retries := 0; retries < 5; retries++){
+ # SELECTING
+ req.dhcpop = Discover;
+ req.options = initopt;
+ rep := exchange(srv, ++xidgen, req, 1<<Offer);
+ if(rep == nil)
+ break;
+ #
+ # could wait a little while and accumulate offers, but is it sensible?
+ # we do sometimes see arguments between DHCP servers that could
+ # only be resolved by user choice
+ #
+ if(!rep.yiaddr.isvalid())
+ continue; # server has no idea either
+ serverid := getopt(rep.options, Oserverid, 4);
+ if(serverid == nil)
+ continue; # broken server
+ # REQUESTING
+ options := (Oserverid, serverid) :: (Oipaddr, rep.yiaddr.v4()) :: initopt;
+ lease := getlease(rep);
+ if(lease != nil)
+ options = (Olease, lease) :: options;
+ rep = request(srv, rep.xid, req, options);
+ if(rep != nil){
+ # could probe with ARP here, and if found, Decline
+ if(debug)
+ sys->print("req: server accepted\n");
+ req.udphdr[Udpraddr:] = rep.udphdr[Udpraddr:Udpraddr+IPaddrlen];
+ return rep;
+ }
+ }
+ return nil;
+}
+
+request(srv: ref DhcpIO, xid: int, req: ref Dhcp, options: list of (int, array of byte)): ref Dhcp
+{
+ req.dhcpop = Request; # Selecting
+ req.options = options;
+ rep := exchange(srv, xid, req, (1<<Ack)|(1<<Nak));
+ if(rep == nil || rep.dhcpop == Nak)
+ return nil;
+ return rep;
+}
+
+# renew
+# direct to server from T1 to T2 [RENEW]
+# Request must not include
+# requested IP address, server identifier
+# Request must include
+# ciaddr set to client's address
+# Request might include
+# lease time
+# similar, but broadcast, from T2 to T3 [REBIND]
+# at T3, unbind, restart Discover
+
+tenancy(srv: ref DhcpIO, req: ref Dhcp, leasesec: int): ref Dhcp
+{
+ # configure address...
+ t3 := big leasesec * big 1000; # lease expires; restart
+ t2 := (big 3 * t3)/big 4; # broadcast renewal request at ¾time
+ t1 := t2/big 2; # renew lease with original server at ½time
+ srv.rstop();
+ thebigsleep(t1);
+ # RENEW
+ rep := renewing(srv, req, t1, t2);
+ if(rep != nil)
+ return rep;
+ # REBIND
+ req.udphdr[Udpraddr:] = (ip->v4bcast).v6(); # now try broadcast
+ return renewing(srv, req, t2, t3);
+}
+
+renewing(srv: ref DhcpIO, req: ref Dhcp, a: big, b: big): ref Dhcp
+{
+ Minute: con big(60*1000);
+ while(a < b){
+ rep := exchange(srv, req.xid, req, (1<<Ack)|(1<<Nak));
+ if(rep != nil)
+ return rep;
+ delta := (b-a)/big 2;
+ if(delta < Minute)
+ delta = Minute;
+ thebigsleep(delta);
+ a += delta;
+ }
+ return nil;
+}
+
+thebigsleep(msec: big)
+{
+ Day: con big (24*3600*1000); # 1 day in msec
+ while(msec > big 0){
+ n := msec;
+ if(n > Day)
+ n = Day;
+ sys->sleep(int n);
+ msec -= n;
+ }
+}
+
+getlease(m: ref Dhcp): array of byte
+{
+ lease := getopt(m.options, Olease, 4);
+ if(lease == nil)
+ return nil;
+ if(get4(lease, 0) == 0){
+ lease = array[4] of byte;
+ put4(lease, 0, 15*60);
+ }
+ return lease;
+}
+
+fillbootconf(init: ref Bootconf, pkt: ref Dhcp): ref Bootconf
+{
+ bc := ref Bootconf;
+ if(init != nil)
+ *bc = *init;
+ if(bc.options == nil)
+ bc.options = array[256] of array of byte;
+ for(l := pkt.options; l != nil; l = tl l){
+ (c, v) := hd l;
+ if(bc.options[c] == nil)
+ bc.options[c] = v; # give priority to first occurring
+ }
+ if((a := bc.get(Ovendorinfo)) != nil){
+ if(bc.vendor == nil)
+ bc.vendor = array[256] of array of byte;
+ for(l = parseopt(a, 0).t1; l != nil; l = tl l){
+ (c, v) := hd l;
+ if(bc.vendor[c] == nil)
+ bc.vendor[c] = v;
+ }
+ }
+ if(pkt.yiaddr.isvalid()){
+ bc.ip = pkt.yiaddr.text();
+ bc.ipmask = bc.getip(Omask);
+ if(bc.ipmask == nil)
+ bc.ipmask = pkt.yiaddr.classmask().masktext();
+ }
+ bc.bootf = pkt.file;
+ bc.dhcpip = IPaddr.newv6(pkt.udphdr[Udpraddr:]).text();
+ bc.siaddr = pkt.siaddr.text();
+ bc.lease = bc.getint(Olease);
+ if(bc.lease == Infinite)
+ bc.lease = 0;
+ else if(debug > 1)
+ bc.lease = 2*60; # shorten time, for testing
+ bc.dom = bc.gets(Odomainname);
+ s := bc.gets(Ohostname);
+ for(i:=0; i<len s; i++)
+ if(s[i] == '.'){
+ if(bc.dom == nil)
+ bc.dom = s[i+1:];
+ s = s[0:i];
+ break;
+ }
+ bc.sys = s;
+ bc.ipgw = bc.getip(Orouter);
+ bc.bootip = bc.getip(Otftpserver);
+ bc.serverid = bc.getip(Oserverid);
+ return bc;
+}
+
+Lease.release(l: self ref Lease)
+{
+ # could send a Release message
+ # should unconfigure
+ if(l.pid){
+ kill(l.pid, "grp");
+ l.pid = 0;
+ }
+}
+
+flush(c: chan of (ref Bootconf, string))
+{
+ alt{
+ <-c => ;
+ * => ;
+ }
+}
+
+DhcpIO: adt {
+ fd: ref Sys->FD;
+ pid: int;
+ dc: chan of ref Dhcp;
+ new: fn(fd: ref Sys->FD): ref DhcpIO;
+ rstart: fn(io: self ref DhcpIO);
+ rstop: fn(io: self ref DhcpIO);
+};
+
+DhcpIO.new(fd: ref Sys->FD): ref DhcpIO
+{
+ return ref DhcpIO(fd, 0, chan of ref Dhcp);
+}
+
+DhcpIO.rstart(io: self ref DhcpIO)
+{
+ if(io.pid == 0){
+ pids := chan of int;
+ spawn dhcpreader(pids, io);
+ io.pid = <-pids;
+ }
+}
+
+DhcpIO.rstop(io: self ref DhcpIO)
+{
+ if(io.pid != 0){
+ kill(io.pid, "");
+ io.pid = 0;
+ }
+}
+
+getopt(options: list of (int, array of byte), op: int, minlen: int): array of byte
+{
+ for(; options != nil; options = tl options){
+ (opt, val) := hd options;
+ if(opt == op && len val >= minlen)
+ return val;
+ }
+ return nil;
+}
+
+exchange(srv: ref DhcpIO, xid: int, req: ref Dhcp, accept: int): ref Dhcp
+{
+ srv.rstart();
+ nsec := 3;
+ for(count := 0; count < 5; count++) {
+ (tpid, tc) := timeoutstart(nsec*1000);
+ dhcpsend(srv.fd, xid, req);
+ Wait:
+ for(;;){
+ alt {
+ <-tc=>
+ break Wait;
+ rep := <-srv.dc=>
+ if(debug)
+ dumpdhcp(rep, "<-");
+ if(rep.op == Bootpreply &&
+ rep.xid == req.xid &&
+ rep.ciaddr.eq(req.ciaddr) &&
+ eqbytes(rep.chaddr, req.chaddr)){
+ if((accept & (1<<rep.dhcpop)) == 0){
+ if(debug)
+ sys->print("req: unexpected reply %s to %s\n", opname(rep.dhcpop), opname(req.dhcpop));
+ continue;
+ }
+ kill(tpid, "");
+ return rep;
+ }
+ if(debug)
+ sys->print("req: mismatch\n");
+ }
+ }
+ req.secs += nsec;
+ nsec++;
+ }
+ return nil;
+}
+
+applycfg(net: string, ctlfd: ref Sys->FD, bc: ref Bootconf): string
+{
+ # write addresses to /net/...
+ # local address, mask[or default], remote address [mtu]
+ if(net == nil)
+ net = "/net";
+ if(bc.ip == nil)
+ return "invalid address";
+ if(ctlfd != nil){
+ if(sys->fprint(ctlfd, "add %s %s", bc.ip, bc.ipmask) < 0) # TO DO: [raddr [mtu]]
+ return sys->sprint("add interface: %r");
+ # could use "mtu n" request to set/change mtu
+ }
+ # if primary:
+ # add default route if gateway valid
+ # put ndb entries ip=, ipmask=, ipgw=; sys= dom=; fs=; auth=; dns=; ntp=; other options from bc.options
+ if(bc.ipgw != nil){
+ fd := sys->open(net+"/iproute", Sys->OWRITE);
+ if(fd != nil)
+ sys->fprint(fd, "add 0 0 %s", bc.ipgw);
+ }
+ s := sys->sprint("ip=%s ipmask=%s", bc.ip, bc.ipmask);
+ if(bc.ipgw != nil)
+ s += sys->sprint(" ipgw=%s", bc.ipgw);
+ s += "\n";
+ if(bc.sys != nil)
+ s += sys->sprint(" sys=%s\n", bc.sys);
+ if(bc.dom != nil)
+ s += sys->sprint(" dom=%s.%s\n", bc.sys, bc.dom);
+ if((addr := bc.getip(OP9auth)) != nil)
+ s += sys->sprint(" auth=%s\n", addr); # TO DO: several addresses
+ if((addr = bc.getip(OP9fs)) != nil)
+ s += sys->sprint(" fs=%s\n", addr);
+ if((addr = bc.getip(Odnsserver)) != nil)
+ s += sys->sprint(" dns=%s\n", addr);
+ fd := sys->open(net+"/ndb", Sys->OWRITE | Sys->OTRUNC);
+ if(fd != nil){
+ a := array of byte s;
+ sys->write(fd, a, len a);
+ }
+ return nil;
+}
+
+removecfg(nil: string, ctlfd: ref Sys->FD, bc: ref Bootconf): string
+{
+ # remove localaddr, localmask[or default]
+ if(ctlfd != nil){
+ if(sys->fprint(ctlfd, "remove %s %s", bc.ip, bc.ipmask) < 0)
+ return sys->sprint("remove address: %r");
+ }
+ bc.ip = nil;
+ bc.ipgw = nil;
+ bc.ipmask = nil;
+ # remote address?
+ # clear net+"/ndb"?
+ return nil;
+}
+
+#
+# the following is just for debugging
+#
+
+dumpdhcp(m: ref Dhcp, dir: string)
+{
+ s := "";
+ sys->print("%s %s/%ud: ", dir, IPaddr.newv6(m.udphdr[Udpraddr:]).text(), get2(m.udphdr, Udprport));
+ if(m.dhcpop != NotDHCP)
+ s = " "+opname(m.dhcpop);
+ sys->print("op %d%s htype %d hops %d xid %ud\n", m.op, s, m.htype, m.hops, m.xid);
+ sys->print("\tsecs %d flags 0x%.4ux\n", m.secs, m.flags);
+ sys->print("\tciaddr %s\n", m.ciaddr.text());
+ sys->print("\tyiaddr %s\n", m.yiaddr.text());
+ sys->print("\tsiaddr %s\n", m.siaddr.text());
+ sys->print("\tgiaddr %s\n", m.giaddr.text());
+ sys->print("\tchaddr ");
+ for(x := 0; x < len m.chaddr; x++)
+ sys->print("%2.2ux", int m.chaddr[x]);
+ sys->print("\n");
+ if(m.sname != nil)
+ sys->print("\tsname %s\n", m.sname);
+ if(m.file != nil)
+ sys->print("\tfile %s\n", m.file);
+ if(m.options != nil){
+ sys->print("\t");
+ printopts(m.options, opts);
+ sys->print("\n");
+ }
+}
+
+Optbytes, Optaddr, Optmask, Optint, Optstr, Optopts, Opthex: con iota;
+
+Opt: adt
+{
+ code: int;
+ name: string;
+ otype: int;
+};
+
+opts: array of Opt = array[] of {
+ (Omask, "ipmask", Optmask),
+ (Orouter, "ipgw", Optaddr),
+ (Odnsserver, "dns", Optaddr),
+ (Ohostname, "hostname", Optstr),
+ (Odomainname, "domain", Optstr),
+ (Ontpserver, "ntp", Optaddr),
+ (Oipaddr, "requestedip", Optaddr),
+ (Olease, "lease", Optint),
+ (Oserverid, "serverid", Optaddr),
+ (Otype, "dhcpop", Optint),
+ (Ovendorclass, "vendorclass", Optstr),
+ (Ovendorinfo, "vendorinfo", Optopts),
+ (Onetbiosns, "wins", Optaddr),
+ (Opop3server, "pop3", Optaddr),
+ (Osmtpserver, "smtp", Optaddr),
+ (Owwwserver, "www", Optaddr),
+ (Oparams, "params", Optbytes),
+ (Otftpserver, "tftp", Optaddr),
+ (Oclientid, "clientid", Opthex),
+};
+
+p9opts: array of Opt = array[] of {
+ (OP9fs, "fs", Optaddr),
+ (OP9auth, "auth", Optaddr),
+};
+
+lookopt(optab: array of Opt, code: int): (int, string, int)
+{
+ for(i:=0; i<len optab; i++)
+ if(opts[i].code == code)
+ return opts[i];
+ return (-1, nil, 0);
+}
+
+printopts(options: list of (int, array of byte), opts: array of Opt)
+{
+ for(; options != nil; options = tl options){
+ (code, val) := hd options;
+ sys->print("(%d %d", code, len val);
+ (nil, name, otype) := lookopt(opts, code);
+ if(name == nil){
+ for(v := 0; v < len val; v++)
+ sys->print(" %d", int val[v]);
+ }else{
+ sys->print(" %s", name);
+ case otype {
+ Optbytes =>
+ for(v := 0; v < len val; v++)
+ sys->print(" %d", int val[v]);
+ Opthex =>
+ for(v := 0; v < len val; v++)
+ sys->print(" %#.2ux", int val[v]);
+ Optaddr or Optmask =>
+ while(len val >= 4){
+ sys->print(" %s", v4text(val));
+ val = val[4:];
+ }
+ Optstr =>
+ sys->print(" \"%s\"", string val);
+ Optint =>
+ n := 0;
+ for(v := 0; v < len val; v++)
+ n = (n<<8) | int val[v];
+ sys->print(" %d", n);
+ Optopts =>
+ printopts(parseopt(val, 0).t1, p9opts);
+ }
+ }
+ sys->print(")");
+ }
+}
diff --git a/appl/lib/dialog.b b/appl/lib/dialog.b
new file mode 100644
index 00000000..76409b06
--- /dev/null
+++ b/appl/lib/dialog.b
@@ -0,0 +1,190 @@
+implement Dialog;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+ draw: Draw;
+ Screen, Rect, Point: import draw;
+
+include "tk.m";
+ tk: Tk;
+ Toplevel: import tk;
+
+include "tkclient.m";
+ tkclient: Tkclient;
+
+include "dialog.m";
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+ tk = load Tk Tk->PATH;
+ tkclient = load Tkclient Tkclient->PATH;
+ tkclient->init();
+ return nil;
+}
+
+STEP: con 20;
+
+#
+# find upper left corner for subsidiary child window (always at constant
+# position relative to parent)
+#
+localgeom(im: ref Draw->Image): string
+{
+ if (im == nil)
+ return nil;
+
+ return sys->sprint("-x %d -y %d", im.r.min.x+STEP, im.r.min.y+STEP);
+}
+
+centre(t: ref Toplevel)
+{
+ org: Point;
+ org.x = t.image.screen.image.r.dx() / 2 - t.image.r.dx() / 2;
+ org.y = t.image.screen.image.r.dy() / 3 - t.image.r.dy() / 2;
+ if (org.y < 0)
+ org.y = 0;
+ cmd(t, ". configure -x " + string org.x + " -y " + string org.y);
+}
+
+tkcmds(top: ref Tk->Toplevel, a: array of string)
+{
+ n := len a;
+ for(i := 0; i < n; i++)
+ tk->cmd(top, a[i]);
+}
+
+dialog_config := array[] of {
+ "label .top.ico",
+ "label .top.msg",
+ "frame .top -relief raised -bd 1",
+ "frame .bot -relief raised -bd 1",
+ "pack .top.ico -side left -padx 10 -pady 10",
+ "pack .top.msg -side left -expand 1 -fill both -padx 10 -pady 10",
+ "pack .Wm_t .top .bot -side top -fill both",
+ "focus ."
+};
+
+prompt(ctxt: ref Draw->Context,
+ parent: ref Draw->Image,
+ ico: string,
+ title:string,
+ msg: string,
+ dflt: int,
+ labs : list of string): int
+{
+ where := localgeom(parent);
+
+ (t, tc) := tkclient->toplevel(ctxt, where, title, Tkclient->Popup);
+
+ d := chan of string;
+ tk->namechan(t, d, "d");
+
+ tkcmds(t, dialog_config);
+ cmd(t, ".top.msg configure -text '" + msg);
+ if (ico != nil)
+ cmd(t, ".top.ico configure -bitmap " + ico);
+
+ n := len labs;
+ for(i := 0; i < n; i++) {
+ cmd(t, "button .bot.button" +
+ string(i) + " -command {send d " +
+ string(i) + "} -text '" + hd labs);
+
+ if(i == dflt) {
+ cmd(t, "frame .bot.default -relief sunken -bd 1");
+ cmd(t, "pack .bot.default -side left -expand 1 -padx 10 -pady 8");
+ cmd(t, "pack .bot.button" + string i +
+ " -in .bot.default -side left -padx 10 -pady 8 -ipadx 8 -ipady 4");
+ }
+ else
+ cmd(t, "pack .bot.button" + string i +
+ " -side left -expand 1 -padx 10 -pady 10 -ipadx 8 -ipady 4");
+ labs = tl labs;
+ }
+
+ if(dflt >= 0)
+ cmd(t, "bind . <Key-\n> {send d " + string dflt + "}");
+
+ e := cmd(t, "variable lasterror");
+ if(e != "") {
+ sys->fprint(sys->fildes(2), "Dialog error: %s\n", e);
+ return dflt;
+ }
+ tkclient->onscreen(t, nil);
+ tkclient->startinput(t, "kbd" :: "ptr" :: nil);
+ cmd(t, "update");
+
+ for(;;) alt {
+ c := <-t.ctxt.kbd =>
+ tk->keyboard(t, c);
+ p := <-t.ctxt.ptr =>
+ tk->pointer(t, *p);
+ c := <-t.ctxt.ctl or
+ c = <-t.wreq =>
+ tkclient->wmctl(t, c);
+ ans := <-d =>
+ return int ans;
+ tcs := <-tc =>
+ if(tcs == "exit")
+ return dflt;
+ tkclient->wmctl(t, tcs);
+ }
+
+}
+
+getstring_config := array[] of {
+ "label .lab",
+ "entry .ent -relief sunken -bd 2 -width 200",
+ "pack .lab .ent -side left",
+ "bind .ent <Key-\n> {send f 1}",
+ "focus .ent"
+};
+
+getstring(ctxt: ref Draw->Context, parent: ref Draw->Image, msg: string): string
+{
+ where := localgeom(parent);
+ (t, wmctl) := tkclient->toplevel(ctxt, where + " -borderwidth 2 -relief raised", nil, Tkclient->Popup);
+ f := chan of string;
+ tk->namechan(t, f, "f");
+
+ tkcmds(t, getstring_config);
+ cmd(t, ".lab configure -text '" + msg + ": ");
+ tkclient->onscreen(t, nil);
+ tkclient->startinput(t, "kbd" :: "ptr" :: nil);
+
+ e := tk->cmd(t, "variable lasterror");
+ if(e != "") {
+ sys->print("getstring error: %s\n", e);
+ return "";
+ }
+ cmd(t, "update");
+
+ for(;;)alt{
+ c := <-t.ctxt.kbd =>
+ tk->keyboard(t, c);
+ p := <-t.ctxt.ptr =>
+ tk->pointer(t, *p);
+ c := <-t.ctxt.ctl or
+ c = <-wmctl =>
+ if(c == "exit")
+ return nil;
+ tkclient->wmctl(t, c);
+ <-f =>
+ return tk->cmd(t, ".ent get");
+ }
+}
+Showtk: con 0;
+
+cmd(top: ref Tk->Toplevel, s: string): string
+{
+ if (Showtk)
+ sys->print("%s\n", s);
+ e := tk->cmd(top, s);
+ if (e != nil && e[0] == '!')
+ sys->fprint(sys->fildes(2), "Dialog: tk error %s on '%s'\n", e, s);
+ return e;
+}
diff --git a/appl/lib/dict.b b/appl/lib/dict.b
new file mode 100644
index 00000000..4df241af
--- /dev/null
+++ b/appl/lib/dict.b
@@ -0,0 +1,57 @@
+implement Dictionary;
+
+#
+# This is intended to be a simple dictionary of string tuples
+# It is not intended for large data sets or efficient deletion of keys
+#
+
+include "dict.m";
+
+Dict.add( d: self ref Dict, e: (string, string) )
+{
+ if (d.entries == nil)
+ d.entries = e::nil;
+ else
+ d.entries = e::d.entries;
+}
+
+Dict.delete( d: self ref Dict, k: string )
+{
+ key : string;
+ newlist : list of (string, string);
+ temp := d.entries;
+
+ while (temp != nil) {
+ (key,nil) = hd temp;
+ if (key != k)
+ newlist = (hd temp)::newlist;
+ temp = tl temp;
+ }
+ d.entries = newlist;
+}
+
+Dict.lookup( d: self ref Dict, k: string ) :string
+{
+ key, value :string;
+ temp := d.entries;
+ while (temp != nil) {
+ (key,value) = hd temp;
+ if (key == k)
+ return value;
+ temp = tl temp;
+ }
+ return nil;
+}
+
+Dict.keys( d: self ref Dict ) :list of string
+{
+ key: string;
+ keylist : list of string;
+ temp := d.entries;
+ while (temp != nil) {
+ (key, nil) = hd temp;
+ keylist = key::keylist;
+ temp = tl temp;
+ }
+ return keylist;
+}
diff --git a/appl/lib/dis.b b/appl/lib/dis.b
new file mode 100644
index 00000000..f64fd162
--- /dev/null
+++ b/appl/lib/dis.b
@@ -0,0 +1,609 @@
+implement Dis;
+
+#
+# Derived by Vita Nuova Limited 1998 from /appl/wm/rt.b, which is
+# Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+ sprint: import sys;
+
+include "math.m";
+ math: Math;
+
+include "dis.m";
+
+disptr: int;
+disobj: array of byte;
+
+optab := array[] of {
+ "nop",
+ "alt",
+ "nbalt",
+ "goto",
+ "call",
+ "frame",
+ "spawn",
+ "runt",
+ "load",
+ "mcall",
+ "mspawn",
+ "mframe",
+ "ret",
+ "jmp",
+ "case",
+ "exit",
+ "new",
+ "newa",
+ "newcb",
+ "newcw",
+ "newcf",
+ "newcp",
+ "newcm",
+ "newcmp",
+ "send",
+ "recv",
+ "consb",
+ "consw",
+ "consp",
+ "consf",
+ "consm",
+ "consmp",
+ "headb",
+ "headw",
+ "headp",
+ "headf",
+ "headm",
+ "headmp",
+ "tail",
+ "lea",
+ "indx",
+ "movp",
+ "movm",
+ "movmp",
+ "movb",
+ "movw",
+ "movf",
+ "cvtbw",
+ "cvtwb",
+ "cvtfw",
+ "cvtwf",
+ "cvtca",
+ "cvtac",
+ "cvtwc",
+ "cvtcw",
+ "cvtfc",
+ "cvtcf",
+ "addb",
+ "addw",
+ "addf",
+ "subb",
+ "subw",
+ "subf",
+ "mulb",
+ "mulw",
+ "mulf",
+ "divb",
+ "divw",
+ "divf",
+ "modw",
+ "modb",
+ "andb",
+ "andw",
+ "orb",
+ "orw",
+ "xorb",
+ "xorw",
+ "shlb",
+ "shlw",
+ "shrb",
+ "shrw",
+ "insc",
+ "indc",
+ "addc",
+ "lenc",
+ "lena",
+ "lenl",
+ "beqb",
+ "bneb",
+ "bltb",
+ "bleb",
+ "bgtb",
+ "bgeb",
+ "beqw",
+ "bnew",
+ "bltw",
+ "blew",
+ "bgtw",
+ "bgew",
+ "beqf",
+ "bnef",
+ "bltf",
+ "blef",
+ "bgtf",
+ "bgef",
+ "beqc",
+ "bnec",
+ "bltc",
+ "blec",
+ "bgtc",
+ "bgec",
+ "slicea",
+ "slicela",
+ "slicec",
+ "indw",
+ "indf",
+ "indb",
+ "negf",
+ "movl",
+ "addl",
+ "subl",
+ "divl",
+ "modl",
+ "mull",
+ "andl",
+ "orl",
+ "xorl",
+ "shll",
+ "shrl",
+ "bnel",
+ "bltl",
+ "blel",
+ "bgtl",
+ "bgel",
+ "beql",
+ "cvtlf",
+ "cvtfl",
+ "cvtlw",
+ "cvtwl",
+ "cvtlc",
+ "cvtcl",
+ "headl",
+ "consl",
+ "newcl",
+ "casec",
+ "indl",
+ "movpc",
+ "tcmp",
+ "mnewz",
+ "cvtrf",
+ "cvtfr",
+ "cvtws",
+ "cvtsw",
+ "lsrw",
+ "lsrl",
+ "eclr",
+ "newz",
+ "newaz",
+ "raise",
+ "casel",
+ "mulx",
+ "divx",
+ "cvtxx",
+ "mulx0",
+ "divx0",
+ "cvtxx0",
+ "mulx1",
+ "divx1",
+ "cvtxx1",
+ "cvtfx",
+ "cvtxf",
+ "expw",
+ "expl",
+ "expf",
+ "self",
+};
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ math = load Math Math->PATH; # optional
+}
+
+loadobj(disfile: string): (ref Mod, string)
+{
+ fd := sys->open(disfile, sys->OREAD);
+ if(fd == nil)
+ return (nil, "open failed: "+sprint("%r"));
+
+ (ok, d) := sys->fstat(fd);
+ if(ok < 0)
+ return (nil, "stat failed: "+sprint("%r"));
+
+ objlen := int d.length;
+ disobj = array[objlen] of byte;
+
+ if(sys->read(fd, disobj, objlen) != objlen){
+ disobj = nil;
+ return (nil, "read failed: "+sprint("%r"));
+ }
+
+ disptr = 0;
+ m := ref Mod;
+ m.magic = operand();
+ if(m.magic == SMAGIC) {
+ n := operand();
+ m.sign = disobj[disptr:disptr+n];
+ disptr += n;
+ m.magic = operand();
+ }
+ if(m.magic != XMAGIC){
+ disobj = nil;
+ return (nil, "bad magic number");
+ }
+
+ m.rt = operand();
+ m.ssize = operand();
+ m.isize = operand();
+ m.dsize = operand();
+ m.tsize = operand();
+ m.lsize = operand();
+ m.entry = operand();
+ m.entryt = operand();
+
+ m.inst = array[m.isize] of ref Inst;
+ for(i := 0; i < m.isize; i++) {
+ o := ref Inst;
+ o.op = int disobj[disptr++];
+ o.addr = int disobj[disptr++];
+ case o.addr & ARM {
+ AXIMM or
+ AXINF or
+ AXINM =>
+ o.mid = operand();
+ }
+
+ case (o.addr>>3) & 7 {
+ AFP or
+ AMP or
+ AIMM =>
+ o.src = operand();
+ AIND|AFP or
+ AIND|AMP =>
+ o.src = operand()<<16;
+ o.src |= operand();
+ }
+
+ case o.addr & 7 {
+ AFP or
+ AMP or
+ AIMM =>
+ o.dst = operand();
+ AIND|AFP or
+ AIND|AMP =>
+ o.dst = operand()<<16;
+ o.dst |= operand();
+ }
+ m.inst[i] = o;
+ }
+
+ m.types = array[m.tsize] of ref Type;
+ for(i = 0; i < m.tsize; i++) {
+ h := ref Type;
+ id := operand();
+ h.size = operand();
+ h.np = operand();
+ h.map = disobj[disptr:disptr+h.np];
+ disptr += h.np;
+ m.types[i] = h;
+ }
+
+ for(;;) {
+ op := int disobj[disptr++];
+ if(op == 0)
+ break;
+
+ n := op & (DMAX-1);
+ if(n == 0)
+ n = operand();
+
+ offset := operand();
+
+ dat: ref Data;
+ case op>>4 {
+ DEFB =>
+ dat = ref Data.Bytes(op, n, offset, disobj[disptr:disptr+n]);
+ disptr += n;
+ DEFW =>
+ words := array[n] of int;
+ for(i = 0; i < n; i++)
+ words[i] = getw();
+ dat = ref Data.Words(op, n, offset, words);
+ DEFS =>
+ dat = ref Data.String(op, n, offset, string disobj[disptr:disptr+n]);
+ disptr += n;
+ DEFF =>
+ if(math != nil){
+ reals := array[n] of real;
+ for(i = 0; i < n; i++)
+ reals[i] = math->bits64real(getl());
+ dat = ref Data.Reals(op, n, offset, nil);
+ } else {
+ disptr += 8*n; # skip it
+ dat = ref Data.Reals(op, n, offset, nil);
+ }
+ break;
+ DEFA =>
+ typex := getw();
+ length := getw();
+ dat = ref Data.Array(op, n, offset, typex, length);
+ DIND =>
+ dat = ref Data.Aindex(op, n, offset, getw());
+ DAPOP =>
+ dat = ref Data.Arestore(op, n, offset);
+ DEFL =>
+ bigs := array[n] of big;
+ for(i = 0; i < n; i++)
+ bigs[i] = getl();
+ dat = ref Data.Bigs(op, n, offset, bigs);
+ * =>
+ dat = ref Data.Zero(op, n, offset);
+ }
+ m.data = dat :: m.data;
+ }
+
+ m.data = revdat(m.data);
+
+ m.name = gets();
+
+ m.links = array[m.lsize] of ref Link;
+ for(i = 0; i < m.lsize; i++) {
+ l := ref Link;
+ l.pc = operand();
+ l.desc = operand();
+ l.sig = getw();
+ l.name = gets();
+
+ m.links[i] = l;
+ }
+
+ if(m.rt & Dis->HASLDT0)
+ raise "obsolete dis";
+
+ if(m.rt & Dis->HASLDT){
+ nl := operand();
+ imps := array[nl] of array of ref Import;
+ for(i = 0; i < nl; i++){
+ n := operand();
+ imps[i] = array[n] of ref Import;
+ for(j := 0; j < n; j++){
+ imps[i][j] = im := ref Import;
+ im.sig = getw();
+ im.name = gets();
+ }
+ }
+ disptr++;
+ m.imports = imps;
+ }
+
+ if(m.rt & Dis->HASEXCEPT){
+ nh := operand(); # number of handlers
+ hs := array[nh] of ref Handler;
+ for(i = 0; i < nh; i++){
+ h := hs[i] = ref Handler;
+ h.eoff = operand();
+ h.pc1 = operand();
+ h.pc2 = operand();
+ t := operand();
+ if(t >= 0)
+ h.t = m.types[t];
+ n := operand();
+ h.ne = n>>16;
+ n &= 16rffff; # number of cases
+ h.etab = array[n+1] of ref Except;
+ for(j := 0; j < n; j++){
+ e := h.etab[j] = ref Except;
+ k := disptr;
+ while(int disobj[disptr++]) # pattern
+ ;
+ e.s = string disobj[k: disptr-1];
+ e.pc = operand();
+ }
+ e := h.etab[j] = ref Except;
+ e.pc = operand(); # * pc
+ }
+ disptr++; # 0 byte
+ m.handlers = hs;
+ }
+
+ m.srcpath = gets();
+
+ disobj = nil;
+ return (m, nil);
+}
+
+operand(): int
+{
+ if(disptr >= len disobj)
+ return -1;
+
+ b := int disobj[disptr++];
+
+ case b & 16rC0 {
+ 16r00 =>
+ return b;
+ 16r40 =>
+ return b | ~16r7F;
+ 16r80 =>
+ if(disptr >= len disobj)
+ return -1;
+ if(b & 16r20)
+ b |= ~16r3F;
+ else
+ b &= 16r3F;
+ return (b<<8) | int disobj[disptr++];
+ 16rC0 =>
+ if(disptr+2 >= len disobj)
+ return -1;
+ if(b & 16r20)
+ b |= ~16r3F;
+ else
+ b &= 16r3F;
+ b = b<<24 |
+ (int disobj[disptr]<<16) |
+ (int disobj[disptr+1]<<8)|
+ int disobj[disptr+2];
+ disptr += 3;
+ return b;
+ }
+ return 0;
+}
+
+get4(a: array of byte, i: int): int
+{
+ return (int a[i+0] << 24) | (int a[i+1] << 16) | (int a[i+2] << 8) | int a[i+3];
+}
+
+getw(): int
+{
+ if(disptr+3 >= len disobj)
+ return -1;
+ i := (int disobj[disptr+0]<<24) |
+ (int disobj[disptr+1]<<16) |
+ (int disobj[disptr+2]<<8) |
+ int disobj[disptr+3];
+
+ disptr += 4;
+ return i;
+}
+
+getl(): big
+{
+ if(disptr+7 >= len disobj)
+ return big -1;
+ i := (big disobj[disptr+0]<<56) |
+ (big disobj[disptr+1]<<48) |
+ (big disobj[disptr+2]<<40) |
+ (big disobj[disptr+3]<<32) |
+ (big disobj[disptr+4]<<24) |
+ (big disobj[disptr+5]<<16) |
+ (big disobj[disptr+6]<<8) |
+ big disobj[disptr+7];
+
+ disptr += 8;
+ return i;
+}
+
+gets(): string
+{
+ s := disptr;
+ while(disptr < len disobj && disobj[disptr] != byte 0)
+ disptr++;
+
+ v := string disobj[s:disptr];
+ disptr++;
+ return v;
+}
+
+revdat(d: list of ref Data): list of ref Data
+{
+ t: list of ref Data;
+
+ while(d != nil) {
+ t = hd d :: t;
+ d = tl d;
+ }
+ return t;
+}
+
+op2s(op: int): string
+{
+ if(op < 0 || op >= len optab)
+ return sys->sprint("OP%d", op);
+ return optab[op];
+}
+
+inst2s(o: ref Inst): string
+{
+ fi := 0;
+ si := 0;
+ s := sprint("%-10s", optab[o.op]);
+ src := "";
+ dst := "";
+ mid := "";
+ case (o.addr>>3) & 7 {
+ AFP =>
+ src = sprint("%d(fp)", o.src);
+ AMP =>
+ src = sprint("%d(mp)", o.src);
+ AIMM =>
+ src = sprint("$%d", o.src);
+ AIND|AFP =>
+ fi = (o.src>>16) & 16rFFFF;
+ si = o.src & 16rFFFF;
+ src = sprint("%d(%d(fp))", si, fi);
+ AIND|AMP =>
+ fi = (o.src>>16) & 16rFFFF;
+ si = o.src & 16rFFFF;
+ src = sprint("%d(%d(mp))", si, fi);
+ }
+
+ case o.addr & ARM {
+ AXIMM =>
+ mid = sprint("$%d", o.mid);
+ AXINF =>
+ mid = sprint("%d(fp)", o.mid);
+ AXINM =>
+ mid = sprint("%d(mp)", o.mid);
+ }
+
+ case o.addr & 7 {
+ AFP =>
+ dst = sprint("%d(fp)", o.dst);
+ AMP =>
+ dst = sprint("%d(mp)", o.dst);
+ AIMM =>
+ dst = sprint("$%d", o.dst);
+ AIND|AFP =>
+ fi = (o.dst>>16) & 16rFFFF;
+ si = o.dst & 16rFFFF;
+ dst = sprint("%d(%d(fp))", si, fi);
+ AIND|AMP =>
+ fi = (o.dst>>16) & 16rFFFF;
+ si = o.dst & 16rFFFF;
+ dst = sprint("%d(%d(mp))", si, fi);
+ }
+ if(mid == "") {
+ if(src == "")
+ s += sprint("%s", dst);
+ else if(dst == "")
+ s += sprint("%s", src);
+ else
+ s += sprint("%s, %s", src, dst);
+ }
+ else
+ s += sprint("%s, %s, %s", src, mid, dst);
+
+ return s;
+}
+
+getsb(fd: ref Sys->FD, o: int): (string, int)
+{
+ b := array[1] of byte;
+ buf := array[8192] of byte;
+ p := len buf;
+ for( ; ; o++){
+ sys->seek(fd, big -o, Sys->SEEKEND);
+ if(sys->read(fd, b, 1) != 1)
+ return (nil, 0);
+ if(b[0] == byte 0){
+ if(p < len buf)
+ break;
+ }
+ else if(p > 0)
+ buf[--p] = b[0];
+ }
+ return (string buf[p: ], o);
+}
+
+src(disf: string): string
+{
+ fd := sys->open(disf, sys->OREAD);
+ if(fd == nil)
+ return nil;
+ (s, nil) := getsb(fd, 1);
+ if(s != nil && s[0] == '/')
+ return s;
+ return nil;
+}
diff --git a/appl/lib/diskblocks.b b/appl/lib/diskblocks.b
new file mode 100644
index 00000000..a98505e7
--- /dev/null
+++ b/appl/lib/diskblocks.b
@@ -0,0 +1,122 @@
+implement Diskblocks;
+
+#
+# adapted from Acme's disk.b
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "diskblocks.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+tempfile(): ref Sys->FD
+{
+ user := "inferno";
+ fd := sys->open("/dev/user", Sys->OREAD);
+ if(fd != nil){
+ b := array[Sys->NAMEMAX] of byte;
+ n := sys->read(fd, b, len b);
+ if(n > 0)
+ user = string b[0:n];
+ }
+ fd = nil;
+ buf := sys->sprint("/tmp/X%d.%.4sblks", sys->pctl(0, nil), user);
+ for(i:='A'; i<='Z'; i++){
+ buf[5] = i;
+ if(sys->stat(buf).t0 == 0)
+ continue;
+ fd = sys->create(buf, Sys->ORDWR|Sys->ORCLOSE|Sys->OEXCL, 8r600);
+ if(fd != nil)
+ return fd;
+ }
+ return nil;
+}
+
+Disk.init(fd: ref Sys->FD, gran: int, maxblock: int): ref Disk
+{
+ d := ref Disk;
+ if(gran == 0 || maxblock%gran != 0)
+ return nil;
+ d.maxblock = maxblock;
+ d.gran = gran;
+ d.free = array[maxblock/gran+1] of list of ref Block;
+ d.addr = big 0;
+ d.fd = fd;
+ d.lock = chan[1] of int;
+ return d;
+}
+
+ntosize(d: ref Disk, n: int): (int, int)
+{
+ if (n > d.maxblock)
+ return (-1, -1);
+ size := n;
+ if((size % d.gran) != 0)
+ size += d.gran - size%d.gran;
+ # last bucket holds blocks of exactly d.maxblock
+ return (size, size/d.gran);
+}
+
+Disk.new(d: self ref Disk, n: int): ref Block
+{
+ (size, i) := ntosize(d, n);
+ if(i < 0){
+ sys->werrstr("illegal Disk allocation");
+ return nil;
+ }
+ b: ref Block;
+ d.lock <-= 1;
+ if(d.free[i] != nil){
+ b = hd d.free[i];
+ d.free[i] = tl d.free[i];
+ }else{
+ b = ref Block(d.addr, 0);
+ d.addr += big size;
+ }
+ <-d.lock;
+ b.n = n;
+ return b;
+}
+
+Disk.release(d: self ref Disk, b: ref Block)
+{
+ (nil, i) := ntosize(d, b.n);
+ d.lock <-= 1;
+ d.free[i] = b :: d.free[i];
+ <-d.lock;
+}
+
+Disk.write(d: self ref Disk, b: ref Block, a: array of byte, n: int): ref Block
+{
+ if(b != nil){
+ (size, nil) := ntosize(d, b.n);
+ (nsize, nil) := ntosize(d, n);
+ if(size != nsize){
+ d.release(b);
+ b = d.new(n);
+ }
+ }else
+ b = d.new(n);
+ if(b == nil)
+ return nil;
+ if(sys->pwrite(d.fd, a, n, b.addr) != n){
+ sys->werrstr(sys->sprint("Disk write error: %r"));
+ return nil;
+ }
+ b.n = n;
+ return b;
+}
+
+Disk.read(d: self ref Disk, b: ref Block, a: array of byte, n: int): int
+{
+ if(b == nil || n > b.n){
+ sys->werrstr("read request bigger than block");
+ return -1;
+ }
+ return sys->pread(d.fd, a, n, b.addr);
+}
diff --git a/appl/lib/disks.b b/appl/lib/disks.b
new file mode 100644
index 00000000..ac718af1
--- /dev/null
+++ b/appl/lib/disks.b
@@ -0,0 +1,377 @@
+implement Disks;
+
+# adapted from /sys/src/libdisk on Plan 9: subject to Lucent Public License 1.02
+
+include "sys.m";
+ sys: Sys;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "disks.m";
+
+scsiverbose := 0;
+
+Codefile: con "/lib/scsicodes";
+
+Code: adt {
+ v: int; # (asc<<8) | ascq
+ s: string;
+};
+codes: array of Code;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+}
+
+#
+# Discover the disk geometry by various sleazeful means.
+#
+# First, if there is a partition table in sector 0,
+# see if all the partitions have the same end head
+# and sector; if so, we'll assume that that's the
+# right count.
+#
+# If that fails, we'll try looking at the geometry that the ATA
+# driver supplied, if any, and translate that as a
+# BIOS might.
+#
+# If that too fails, which should only happen on a SCSI
+# disk with no currently defined partitions, we'll try
+# various common (h, s) pairs used by BIOSes when faking
+# the geometries.
+#
+
+# table entry:
+ Oactive, # active flag
+ Ostarth, # starting head
+ Ostarts, # starting sector
+ Ostartc, # starting cylinder
+ Otype, # partition type
+ Oendh, # ending head
+ Oends, # ending sector
+ Oendc: con iota; # ending cylinder
+ Oxlba: con Oendc+1; # starting LBA from start of disc or partition [4]
+ Oxsize: con Oxlba+4; # size in sectors[4]
+
+# Table: entry[NTentry][TentrySize] magic[2]
+Omagic: con NTentry*TentrySize;
+
+partitiongeometry(d: ref Disk): int
+{
+ buf := array[512] of byte;
+ t := buf[Toffset:];
+
+ #
+ # look for an MBR first in the /dev/sdXX/data partition, otherwise
+ # attempt to fall back on the current partition.
+ #
+ rawfd := sys->open(d.prefix+"data", Sys->OREAD);
+ if(rawfd != nil
+ && sys->seek(rawfd, big 0, 0) == big 0
+ && readn(rawfd, buf, 512) == 512
+ && int t[Omagic] == Magic0
+ && int t[Omagic+1] == Magic1) {
+ rawfd = nil;
+ }else{
+ rawfd = nil;
+ if(sys->seek(d.fd, big 0, 0) < big 0
+ || readn(d.fd, buf, 512) != 512
+ || int t[Omagic] != Magic0
+ || int t[Omagic+1] != Magic1)
+ return -1;
+ }
+
+ h := s := -1;
+ for(i:=0; i<NTentry*TentrySize; i += TentrySize) {
+ if(t[i+Otype] == byte 0)
+ continue;
+
+ t[i+Oends] &= byte 63;
+ if(h == -1) {
+ h = int t[i+Oendh];
+ s = int t[i+Oends];
+ } else {
+ #
+ # Only accept the partition info if every
+ # partition is consistent.
+ #
+ if(h != int t[i+Oendh] || s != int t[i+Oends])
+ return -1;
+ }
+ }
+
+ if(h == -1)
+ return -1;
+
+ d.h = h+1; # heads count from 0
+ d.s = s; # sectors count from 1
+ d.c = int (d.secs / big (d.h*d.s));
+ d.chssrc = "part";
+ return 0;
+}
+
+#
+# If there is ATA geometry, use it, perhaps massaged
+#
+drivergeometry(d: ref Disk): int
+{
+ if(d.c == 0 || d.h == 0 || d.s == 0)
+ return -1;
+
+ d.chssrc = "disk";
+ if(d.c < 1024)
+ return 0;
+
+ case d.h {
+ 15 =>
+ d.h = 255;
+ d.c /= 17;
+
+ * =>
+ for(m := 2; m*d.h < 256; m *= 2) {
+ if(d.c/m < 1024) {
+ d.c /= m;
+ d.h *= m;
+ return 0;
+ }
+ }
+
+ # set to 255, 63 and be done with it
+ d.h = 255;
+ d.s = 63;
+ d.c = int (d.secs / big(d.h * d.s));
+ }
+ return 0;
+}
+
+#
+# There's no ATA geometry and no partitions.
+# Our guess is as good as anyone's.
+#
+Guess: adt {
+ h: int;
+ s: int;
+};
+guess: array of Guess = array[] of {
+ (64, 32),
+ (64, 63),
+ (128, 63),
+ (255, 63),
+};
+
+guessgeometry(d: ref Disk)
+{
+ d.chssrc = "guess";
+ c := 1024;
+ for(i:=0; i<len guess; i++)
+ if(big(c*guess[i].h*guess[i].s) >= d.secs) {
+ d.h = guess[i].h;
+ d.s = guess[i].s;
+ d.c = int(d.secs / big(d.h * d.s));
+ return;
+ }
+
+ # use maximum values
+ d.h = 255;
+ d.s = 63;
+ d.c = int(d.secs / big(d.h * d.s));
+}
+
+findgeometry(disk: ref Disk)
+{
+ disk.h = disk.s = disk.c = 0;
+ if(partitiongeometry(disk) < 0 &&
+ drivergeometry(disk) < 0)
+ guessgeometry(disk);
+}
+
+openfile(d: ref Disk): ref Disk
+{
+ (ok, db) := sys->fstat(d.fd);
+ if(ok < 0)
+ return nil;
+
+ d.secsize = 512;
+ d.size = db.length;
+ d.secs = d.size / big d.secsize;
+ d.offset = big 0;
+
+ findgeometry(d);
+ return mkwidth(d);
+}
+
+opensd(d: ref Disk): ref Disk
+{
+ b := bufio->fopen(d.ctlfd, Bufio->OREAD);
+ while((p := b.gets('\n')) != nil){
+ p = p[0:len p - 1];
+ (nf, f) := sys->tokenize(p, " \t"); # might need str->unquote
+ if(nf >= 3 && hd f == "geometry") {
+ d.secsize = int hd tl tl f;
+ if(nf >= 6) {
+ d.c = int hd tl tl tl f;
+ d.h = int hd tl tl tl tl f;
+ d.s = int hd tl tl tl tl tl f;
+ }
+ }
+ if(nf >= 4 && hd f == "part" && hd tl f == d.part) {
+ d.offset = big hd tl tl f;
+ d.secs = big hd tl tl tl f - d.offset;
+ }
+ }
+
+
+ d.size = d.secs * big d.secsize;
+ if(d.size <= big 0) {
+ d.part = "";
+ d.dtype = "file";
+ return openfile(d);
+ }
+
+ findgeometry(d);
+ return mkwidth(d);
+}
+
+Disk.open(name: string, mode: int, noctl: int): ref Disk
+{
+ d := ref Disk;
+ d.rdonly = mode == Sys->OREAD;
+ d.fd = sys->open(name, Sys->OREAD);
+ if(d.fd == nil)
+ return nil;
+
+ if(mode != Sys->OREAD){
+ d.wfd = sys->open(name, Sys->OWRITE);
+ if(d.wfd == nil)
+ d.rdonly = 1;
+ }
+
+ if(noctl)
+ return openfile(d);
+
+ # check for floppy(3) disk
+ if(len name >= 7) {
+ q := name[len name-7:];
+ if(q[0] == 'f' && q[1] == 'd' && isdigit(q[2]) && q[3:] == "disk") {
+ if((d.ctlfd = sys->open(name[0:len name-4]+"ctl", Sys->ORDWR)) != nil) {
+ d.prefix = name[0:len name-4]; # fdN (unlike Plan 9)
+ d.dtype = "floppy";
+ return openfile(d);
+ }
+ }
+ }
+
+ # attempt to find sd(3) disk or partition
+ d.prefix = name;
+ for(i := len name; --i >= 0;)
+ if(name[i] == '/'){
+ d.prefix = name[0:i+1];
+ break;
+ }
+
+ if((d.ctlfd = sys->open(d.prefix+"ctl", Sys->ORDWR)) != nil) {
+ d.dtype = "sd";
+ d.part = name[len d.prefix:];
+ return opensd(d);
+ }
+
+ # assume we have just a normal file
+ d.dtype = "file";
+ return openfile(d);
+}
+
+mkwidth(d: ref Disk): ref Disk
+{
+ d.width = len sys->sprint("%bd", d.size);
+ return d;
+}
+
+isdigit(c: int): int
+{
+ return c >= '0' && c <= '9';
+}
+
+putchs(d: ref Disk, p: array of byte, lba: big)
+{
+ s := int (lba % big d.s);
+ h := int (lba / big d.s % big d.h);
+ c := int (lba / (big d.s * big d.h));
+
+ if(c >= 1024) {
+ c = 1023;
+ h = d.h - 1;
+ s = d.s - 1;
+ }
+
+ p[0] = byte h;
+ p[1] = byte (((s+1) & 16r3F) | ((c>>2) & 16rC0));
+ p[2] = byte c;
+}
+
+PCpart.bytes(p: self PCpart, d: ref Disk): array of byte
+{
+ a := array[TentrySize] of byte;
+ a[Oactive] = byte p.active;
+ a[Otype] = byte p.ptype;
+ putchs(d, a[Ostarth:], p.base+p.offset);
+ putchs(d, a[Oendh:], p.base+p.offset+p.size-big 1);
+ putle32(a[Oxlba:], p.offset);
+ putle32(a[Oxsize:], p.size);
+ return a;
+}
+
+PCpart.extract(a: array of byte, nil: ref Disk): PCpart
+{
+ p: PCpart;
+ p.active = int a[Oactive];
+ p.ptype = int a[Otype];
+ p.base = big 0;
+ p.offset = getle32(a[Oxlba:]);
+ p.size = getle32(a[Oxsize:]);
+ return p;
+}
+
+getle32(p: array of byte): big
+{
+ return (big p[3]<<24) | (big p[2]<<16) | (big p[1] << 8) | big p[0];
+}
+
+putle32(p: array of byte, i: big)
+{
+ p[0] = byte i;
+ p[1] = byte (i>>8);
+ p[2] = byte (i>>16);
+ p[3] = byte (i>>24);
+}
+
+Disk.readn(d: self ref Disk, buf: array of byte, nb: int): int
+{
+ return readn(d.fd, buf, nb);
+}
+
+chstext(p: array of byte): string
+{
+ h := int p[0];
+ c := int p[2];
+ c |= (int p[1]&16rC0)<<2;
+ s := (int p[1] & 16r3F);
+ return sys->sprint("%d/%d/%d", c, h, s);
+}
+
+readn(fd: ref Sys->FD, buf: array of byte, nb: int): int
+{
+ for(nr := 0; nr < nb;){
+ n := sys->read(fd, buf[nr:], nb-nr);
+ if(n <= 0){
+ if(nr == 0)
+ return n;
+ break;
+ }
+ nr += n;
+ }
+ return nr;
+}
diff --git a/appl/lib/dividers.b b/appl/lib/dividers.b
new file mode 100644
index 00000000..aaf3b08e
--- /dev/null
+++ b/appl/lib/dividers.b
@@ -0,0 +1,242 @@
+implement Dividers;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Point, Rect: import draw;
+include "tk.m";
+ tk: Tk;
+include "dividers.m";
+
+Lay: adt {
+ d: int;
+ x: fn(l: self Lay, p: Point): int;
+ y: fn(l: self Lay, p: Point): int;
+ mkr: fn(l: self Lay, r: Rect): Rect;
+ mkpt: fn(l: self Lay, p: Point): Point;
+};
+
+DIVHEIGHT: con 6;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+ tk = load Tk Tk->PATH;
+}
+
+# dir is direction in which to stack widgets (NS or EW)
+Divider.new(win: ref Tk->Toplevel, w: string, wl: list of string, dir: int): (ref Divider, chan of string)
+{
+ lay := Lay(dir);
+ n := len wl;
+ d := ref Divider(win, w, nil, dir, array[n] of {* => ref DWidget}, (0, 0));
+ p := Point(0, 0);
+ for (i := 0; wl != nil; (wl, i) = (tl wl, i+1)) {
+ sz := lay.mkpt(wsize(win, hd wl));
+ *d.widgets[i] = (hd wl, (p, p.add(sz)), sz);
+ if (sz.x > d.canvsize.x)
+ d.canvsize.x = sz.x;
+ p.y += sz.y + DIVHEIGHT;
+ }
+ d.canvsize.y = p.y - DIVHEIGHT;
+ cmd(win, "canvas " + d.w + " -width " + string lay.x(d.canvsize) +
+ " -height " + string lay.y(d.canvsize));
+ ech := chan of string;
+ echname := "dw" + d.w;
+ tk->namechan(win, ech, echname);
+ for (i = 0; i < n; i++) {
+ dw := d.widgets[i];
+ dw.r.max.x = d.canvsize.x + dw.r.min.x;
+ sz := dxy(dw.r);
+ cmd(win, d.w + " create window " + p2s(lay.mkpt(dw.r.min)) +
+ " -window " + dw.w +
+ " -tags w" + string i + " -anchor nw" +
+ " -width " + string lay.x(sz) +
+ " -height " + string lay.y(sz));
+ cmd(win, "pack propagate " + dw.w + " 0");
+ if (i < n - 1) {
+ r := lay.mkr(((dw.r.min.x, dw.r.max.y),
+ (dw.r.max.x, dw.r.max.y + DIVHEIGHT)));
+ cmd(win, d.w + " create rectangle " + r2s(r) +
+ " -fill red" +
+ " -tags d" + string i);
+ cmd(win, d.w + " bind d" + string i + " <Button-1>" +
+ " {send " + echname + " but " + string i + " %x %y}");
+ cmd(win, d.w + " bind d" + string i + " <Motion-Button-1> {}");
+ cmd(win, d.w + " bind d" + string i + " <ButtonRelease-1>" +
+ " {send " + echname + " up x %x %y}");
+ }
+ }
+ cmd(win, d.w + " create rectangle -2 -2 -1 -1 -tags grab");
+ cmd(win, d.w + " bind grab <Button-1> {send " + echname + " drag x %x %y}");
+ cmd(win, d.w + " bind grab <ButtonRelease-1> {send " + echname + " up x %x %y}");
+ cmd(win, "bind " + d.w + " <Configure> {send " + echname + " config x x x}");
+ return (d, ech);
+}
+
+Divider.event(d: self ref Divider, e: string)
+{
+ (n, toks) := sys->tokenize(e, " ");
+ if (n != 4) {
+ sys->print("dividers: invalid event %s\n", e);
+ return;
+ }
+ lay := Lay(d.dir);
+ p := lay.mkpt((int hd tl tl toks, int hd tl tl tl toks));
+ t := hd toks;
+ if (t == "but" && d.state != nil)
+ t = "drag";
+ case t {
+ "but" =>
+ if (d.state != nil) {
+ sys->print("dividers: event '%s' received in drag mode\n", e);
+ return;
+ }
+ div := int hd tl toks;
+ d.state = ref DState;
+ d.state.dragdiv = div;
+ d.state.dy = p.y - d.widgets[div].r.max.y;
+ d.state.maxy = d.widgets[div+1].r.max.y - DIVHEIGHT;
+ d.state.miny = d.widgets[div].r.min.y;
+ cmd(d.win, d.w + " itemconfigure d" + string div + " -fill orange");
+ cmd(d.win, d.w + " raise d" + string div);
+ cmd(d.win, d.w + " coords grab -10000 -10000 10000 10000");
+ cmd(d.win, "grab set " + d.w);
+ cmd(d.win, "update");
+ "drag" =>
+ if (d.state == nil) {
+ sys->print("dividers: event '%s' received in non-drag mode\n", e);
+ return;
+ }
+ div := d.state.dragdiv;
+ ypos := p.y - d.state.dy;
+ if (ypos > d.state.maxy)
+ ypos = d.state.maxy;
+ else if (ypos < d.state.miny)
+ ypos = d.state.miny;
+ r := Rect((0, ypos), (d.canvsize.x, ypos + DIVHEIGHT));
+ cmd(d.win, d.w + " coords d" + string div + " " + r2s(lay.mkr(r)));
+ d.widgets[div].r.max.y = ypos;
+ d.widgets[div+1].r.min.y = ypos + DIVHEIGHT;
+ relayout(d);
+ cmd(d.win, "update");
+ "up" =>
+ if (d.state == nil) {
+ sys->print("dividers: event '%s' received in non-drag mode\n", e);
+ return;
+ }
+ div := d.state.dragdiv;
+ cmd(d.win, d.w + " itemconfigure d" + string div + " -fill red");
+ cmd(d.win, d.w + " coords grab -2 -2 -1 -1");
+ cmd(d.win, "grab release " + d.w);
+ cmd(d.win, "update");
+ d.state = nil;
+ "config" =>
+ resize(d);
+ cmd(d.win, "update");
+ }
+}
+
+# lay out widgets according to rectangles that have been already specified.
+relayout(d: ref Divider)
+{
+ lay := Lay(d.dir);
+ for (i := 0; i < len d.widgets; i++) {
+ dw := d.widgets[i];
+ sz := dxy(dw.r);
+ szs := " -width " + string lay.x(sz) + " -height " + string lay.y(sz);
+ cmd(d.win, d.w + " coords w" + string i + " " + p2s(lay.mkpt(dw.r.min)));
+ cmd(d.win, d.w + " itemconfigure w" + string i + szs);
+ cmd(d.win, dw.w + " configure" + szs);
+ if (i < len d.widgets - 1) {
+ r := lay.mkr(((dw.r.min.x, dw.r.max.y),
+ (dw.r.max.x, dw.r.max.y + DIVHEIGHT)));
+ cmd(d.win, d.w + " coords d" + string i + " " + r2s(r));
+ }
+ }
+}
+
+# resize based on current actual size of canvas;
+# sections resize proportionate to their previously occupied space.
+# strange things will happen if we're resizing in the middle of a drag...
+resize(d: ref Divider)
+{
+ lay := Lay(d.dir);
+ sz := lay.mkpt((int cmd(d.win, d.w + " cget -actwidth"),
+ int cmd(d.win, d.w + " cget -actheight")));
+
+ wspace := (len d.widgets - 1) * DIVHEIGHT;
+ y := 0;
+ for (i := 0; i < len d.widgets; i++) {
+ dw := d.widgets[i];
+ prop := real dw.r.dy() / real (d.canvsize.y - wspace);
+ dw.r = ((0, y), (sz.x, y + int (prop * real (sz.y - wspace))));
+ y = dw.r.max.y + DIVHEIGHT;
+ }
+ y -= DIVHEIGHT;
+ # compensate for rounding errors
+ d.widgets[i - 1].r.max.y -= y - sz.y;
+ d.canvsize = sz;
+ relayout(d);
+}
+
+wsize(win: ref Tk->Toplevel, w: string): Point
+{
+ bw := int cmd(win, w + " cget -borderwidth");
+ return Point(int cmd(win, w + " cget -width") + bw*2,
+ int cmd(win, w + " cget -height") + bw*2);
+}
+
+dxy(r: Rect): Point
+{
+ return r.max.sub(r.min);
+}
+
+p2s(p: Point): string
+{
+ return string p.x + " " + string p.y;
+}
+
+r2s(r: Rect): string
+{
+ return string r.min.x + " " + string r.min.y + " " +
+ string r.max.x + " " + string r.max.y;
+}
+
+Lay.x(l: self Lay, p: Point): int
+{
+ if (l.d == NS)
+ return p.x;
+ return p.y;
+}
+
+Lay.y(l: self Lay, p: Point): int
+{
+ if (l.d == NS)
+ return p.y;
+ return p.x;
+}
+
+Lay.mkr(l: self Lay, r: Rect): Rect
+{
+ if (l.d == NS)
+ return r;
+ return ((r.min.y, r.min.x), (r.max.y, r.max.x));
+}
+
+Lay.mkpt(l: self Lay, p: Point): Point
+{
+ if (l.d == NS)
+ return p;
+ return (p.y, p.x);
+}
+
+cmd(top: ref Tk->Toplevel, s: string): string
+{
+ e := tk->cmd(top, s);
+ if (e != nil && e[0] == '!')
+ sys->print("dividers: tk error %s on '%s'\n", e, s);
+ return e;
+}
diff --git a/appl/lib/ecmascript/builtin.b b/appl/lib/ecmascript/builtin.b
new file mode 100644
index 00000000..c8374f35
--- /dev/null
+++ b/appl/lib/ecmascript/builtin.b
@@ -0,0 +1,1480 @@
+#
+# utility functions
+#
+biinst(o: ref Obj, bi: Builtin, p: ref Obj, h: ESHostobj): ref Obj
+{
+ bo := mkobj(p, "Function");
+ bo.call = mkcall(nil, bi.params);
+ bo.val = strval(bi.val);
+ bo.host = h;
+ varinstant(bo, DontEnum|DontDelete|ReadOnly, "length", ref RefVal(numval(real bi.length)));
+ varinstant(o, DontEnum, bi.name, ref RefVal(objval(bo)));
+ return bo;
+}
+
+biminst(o: ref Obj, bis: array of Builtin, p: ref Obj, h: ESHostobj)
+{
+ for(i := 0; i < len bis; i++)
+ biinst(o, bis[i], p, h);
+}
+
+biarg(args: array of ref Val, i: int): ref Val
+{
+ if(i < len args)
+ return args[i];
+ return undefined;
+}
+
+#
+# interface to builtin objects
+#
+get(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val
+{
+ return esget(ex, o, property, 1);
+}
+
+put(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string, val: ref Ecmascript->Val)
+{
+ return esput(ex, o, property, val, 1);
+}
+
+canput(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val
+{
+ return escanput(ex, o, property, 1);
+}
+
+hasproperty(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val
+{
+ return eshasproperty(ex, o, property, 1);
+}
+
+delete(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string)
+{
+ return esdelete(ex, o, property, 1);
+}
+
+defaultval(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, tyhint: int): ref Ecmascript->Val
+{
+ return esdefaultval(ex, o, tyhint, 1);
+}
+
+call(ex: ref Ecmascript->Exec, f, this: ref Ecmascript->Obj, args: array of ref Ecmascript->Val, eval: int): ref Ecmascript->Ref
+{
+ x, y: real;
+ v: ref Val;
+
+ if(this == nil)
+ this = ex.global;
+ if(f.host != me)
+ return escall(ex, f, this, args, eval);
+ case f.val.str{
+ "eval" =>
+ v = ceval(ex, f, this, args);
+ "parseInt" =>
+ v = cparseInt(ex, f, this, args);
+ "parseFloat" =>
+ v = cparseFloat(ex, f, this, args);
+ "escape" =>
+ v = cescape(ex, f, this, args);
+ "unescape" =>
+ v = cunescape(ex, f, this, args);
+ "isNaN" =>
+ v = cisNaN(ex, f, this, args);
+ "isFinite" =>
+ v = cisFinite(ex, f, this, args);
+ "decodeURI" =>
+ v = cdecodeuri(ex, f, this, args);
+ "encodeURI" =>
+ v = cencodeuri(ex, f, this, args);
+ "decodeURIComponent" =>
+ v = cdecodeuric(ex, f, this, args);
+ "encodeURIComponent" =>
+ v = cencodeuric(ex, f, this, args);
+ "Object" =>
+ v = cobj(ex, f, this, args);
+ "Object.prototype.toString" or
+ "Object.prototype.toLocaleString" =>
+ v = cobjprototoString(ex, f, this, args);
+ "Object.prototype.valueOf" =>
+ v = cobjprotovalueOf(ex, f, this, args);
+ "Object.prototype.hasOwnProperty" =>
+ v = cobjprotohasownprop(ex, f, this, args);
+ "Object.prototype.isPrototypeOf" =>
+ v = cobjprotoisprotoof(ex, f, this, args);
+ "Object.prototype.propertyisEnumerable" =>
+ v = cobjprotopropisenum(ex, f, this, args);
+ "Function" =>
+ v = objval(nfunc(ex, f, args));
+ "Function.Prototype" =>
+ v = undefined;
+ "Function.prototype.toString" =>
+ v = cfuncprototoString(ex, f, this, args);
+ "Function.prototype.apply" =>
+ v = cfuncprotoapply(ex, f, this, args);
+ "Function.prototype.call" =>
+ v = cfuncprotocall(ex, f, this, args);
+ "Error" =>
+ v = objval(nerr(ex, f, args, ex.errproto));
+ "Error.prototype.toString" =>
+ v = cerrprototoString(ex, f, this, args);
+ "EvalError" =>
+ v = objval(nerr(ex, f, args, ex.evlerrproto));
+ "EvalError.prototype.toString" =>
+ v = cerrprototoString(ex, f, this, args);
+ "RangeError" =>
+ v = objval(nerr(ex, f, args, ex.ranerrproto));
+ "RangeError.prototype.toString" =>
+ v = cerrprototoString(ex, f, this, args);
+ "ReferenceError" =>
+ v = objval(nerr(ex, f, args, ex.referrproto));
+ "ReferenceError.prototype.toString" =>
+ v = cerrprototoString(ex, f, this, args);
+ "SyntaxError" =>
+ v = objval(nerr(ex, f, args, ex.synerrproto));
+ "SyntaxError.prototype.toString" =>
+ v = cerrprototoString(ex, f, this, args);
+ "TypeError" =>
+ v = objval(nerr(ex, f, args, ex.typerrproto));
+ "TypeError.prototype.toString" =>
+ v = cerrprototoString(ex, f, this, args);
+ "URIError" =>
+ v = objval(nerr(ex, f, args, ex.urierrproto));
+ "URIError.prototype.toString" =>
+ v = cerrprototoString(ex, f, this, args);
+ "InternalError" =>
+ v = objval(nerr(ex, f, args, ex.interrproto));
+ "InternalError.prototype.toString" =>
+ v = cerrprototoString(ex, f, this, args);
+ "Array" =>
+ v = objval(narray(ex, f, args));
+ "Array.prototype.toString" or "Array.prototype.toLocaleString" =>
+ v = carrayprototoString(ex, f, this, args);
+ "Array.prototype.concat" =>
+ v = carrayprotoconcat(ex, f, this, args);
+ "Array.prototype.join" =>
+ v = carrayprotojoin(ex, f, this, args);
+ "Array.prototype.pop" =>
+ v = carrayprotopop(ex, f, this, args);
+ "Array.prototype.push" =>
+ v = carrayprotopush(ex, f, this, args);
+ "Array.prototype.reverse" =>
+ v = carrayprotoreverse(ex, f, this, args);
+ "Array.prototype.shift" =>
+ v = carrayprotoshift(ex, f, this, args);
+ "Array.prototype.slice" =>
+ v = carrayprotoslice(ex, f, this, args);
+ "Array.prototype.splice" =>
+ v = carrayprotosplice(ex, f, this, args);
+ "Array.prototype.sort" =>
+ v = carrayprotosort(ex, f, this, args);
+ "Array.prototype.unshift" =>
+ v = carrayprotounshift(ex, f, this, args);
+ "String" =>
+ v = cstr(ex, f, this, args);
+ "String.fromCharCode" =>
+ v = cstrfromCharCode(ex, f, this, args);
+ "String.prototype.toString" =>
+ v = cstrprototoString(ex, f, this, args);
+ "String.prototype.valueOf" =>
+ v = cstrprototoString(ex, f, this, args);
+ "String.prototype.charAt" =>
+ v = cstrprotocharAt(ex, f, this, args);
+ "String.prototype.charCodeAt" =>
+ v = cstrprotocharCodeAt(ex, f, this, args);
+ "String.prototype.concat" =>
+ v = cstrprotoconcat(ex, f, this, args);
+ "String.prototype.indexOf" =>
+ v = cstrprotoindexOf(ex, f, this, args);
+ "String.prototype.lastIndexOf" =>
+ v = cstrprotolastindexOf(ex, f, this, args);
+ "String.prototype.localeCompare" =>
+ v = cstrprotocmp(ex, f, this, args);
+ "String.prototype.slice" =>
+ v = cstrprotoslice(ex, f, this, args);
+ "String.prototype.split" =>
+ v = cstrprotosplit(ex, f, this, args);
+ "String.prototype.substr" =>
+ v = cstrprotosubstr(ex, f, this, args);
+ "String.prototype.substring" =>
+ v = cstrprotosubstring(ex, f, this, args);
+ "String.prototype.toLowerCase" or "String.prototype.toLocaleLowerCase" =>
+ v = cstrprototoLowerCase(ex, f, this, args);
+ "String.prototype.toUpperCase" or "String.prototype.toLocaleUpperCase" =>
+ v = cstrprototoUpperCase(ex, f, this, args);
+ "String.prototype.match" =>
+ v = cstrprotomatch(ex, f, this, args);
+ "String.prototype.replace" =>
+ v = cstrprotoreplace(ex, f, this, args);
+ "String.prototype.search" =>
+ v = cstrprotosearch(ex, f, this, args);
+# JavaScript 1.0
+ "String.prototype.anchor" or
+ "String.prototype.big" or
+ "String.prototype.blink" or
+ "String.prototype.bold" or
+ "String.prototype.fixed" or
+ "String.prototype.fontcolor" or
+ "String.prototype.fontsize" or
+ "String.prototype.italics" or
+ "String.prototype.link" or
+ "String.prototype.small" or
+ "String.prototype.strike" or
+ "String.prototype.sub" or
+ "String.prototype.sup" =>
+ s := toString(ex, objval(this));
+ arg := toString(ex, biarg(args, 0));
+ tag, endtag: string;
+ case f.val.str{
+ "String.prototype.anchor" =>
+ tag = "<A NAME=\"" + arg + "\">";
+ endtag = "</A>";
+ "String.prototype.big" =>
+ tag = "<BIG>";
+ endtag = "</BIG>";
+ "String.prototype.blink" =>
+ tag = "<BLINK>";
+ endtag = "</BLINK>";
+ "String.prototype.bold" =>
+ tag = "<B>";
+ endtag = "</B>";
+ "String.prototype.fixed" =>
+ tag = "<TT>";
+ endtag = "</TT>";
+ "String.prototype.fontcolor" =>
+ tag = "<FONT COLOR=\"" + arg + "\">";
+ endtag = "</FONT>";
+ "String.prototype.fontsize" =>
+ tag = "<FONT SIZE=\"" + arg + "\">";
+ endtag = "</FONT>";
+ "String.prototype.italics" =>
+ tag = "<I>";
+ endtag = "</I>";
+ "String.prototype.link" =>
+ tag = "<A HREF=\"" + arg + "\">";
+ endtag = "</A>";
+ "String.prototype.small" =>
+ tag = "<SMALL>";
+ endtag = "</SMALL>";
+ "String.prototype.strike" =>
+ tag = "<STRIKE>";
+ endtag = "</STRIKE>";
+ "String.prototype.sub" =>
+ tag = "<SUB>";
+ endtag = "</SUB>";
+ "String.prototype.sup" =>
+ tag = "<SUP>";
+ endtag = "</SUP>";
+ }
+ v = strval(tag + s + endtag);
+ "Boolean" =>
+ v = cbool(ex, f, this, args);
+ "Boolean.prototype.toString" =>
+ v = cboolprototoString(ex, f, this, args);
+ "Boolean.prototype.valueOf" =>
+ v = cboolprotovalueOf(ex, f, this, args);
+ "Number" =>
+ v = cnum(ex, f, this, args);
+ "Number.prototype.toString" or "Number.prototype.toLocaleString" =>
+ v = cnumprototoString(ex, f, this, args);
+ "Number.prototype.valueOf" =>
+ v = cnumprotovalueOf(ex, f, this, args);
+ "Number.prototype.toFixed" =>
+ v = cnumprotofix(ex, f, this, args);
+ "Number.prototype.toExponential" =>
+ v = cnumprotoexp(ex, f, this, args);
+ "Number.prototype.toPrecision" =>
+ v = cnumprotoprec(ex, f, this, args);
+ "RegExp" =>
+ v = cregexp(ex, f, this, args);
+ "RegExp.prototype.exec" =>
+ v = cregexpprotoexec(ex, f, this, args);
+ "RegExp.prototype.test" =>
+ v = cregexpprototest(ex, f, this, args);
+ "RegExp.prototype.toString" =>
+ v = cregexpprototoString(ex, f, this, args);
+ "Math.abs" or
+ "Math.acos" or
+ "Math.asin" or
+ "Math.atan" or
+ "Math.ceil" or
+ "Math.cos" or
+ "Math.exp" or
+ "Math.floor" or
+ "Math.log" or
+ "Math.round" or
+ "Math.sin" or
+ "Math.sqrt" or
+ "Math.tan" =>
+ x = toNumber(ex, biarg(args, 0));
+ case f.val.str{
+ "Math.abs" =>
+ if(x < 0.)
+ x = -x;
+ else if(x == 0.)
+ x = 0.;
+ "Math.acos" => x = math->acos(x);
+ "Math.asin" => x = math->asin(x);
+ "Math.atan" => x = math->atan(x);
+ "Math.ceil" => x = math->ceil(x);
+ "Math.cos" => x = math->cos(x);
+ "Math.exp" => x = math->exp(x);
+ "Math.floor" => x = math->floor(x);
+ "Math.log" => x = math->log(x);
+ "Math.round" => if((x == .0 && copysign(1., x) == -1.)
+ || (x < .0 && x >= -0.5))
+ x = -0.;
+ else
+ x = math->floor(x+.5);
+ "Math.sin" => x = math->sin(x);
+ "Math.sqrt" => x = math->sqrt(x);
+ "Math.tan" => x = math->tan(x);
+ }
+ v = numval(x);
+ "Math.random" =>
+# range := big 16r7fffffffffffffff;
+ range := big 1000000000;
+ v = numval(real bigrand(range)/ real range);
+ "Math.atan2" or
+ "Math.max" or
+ "Math.min" or
+ "Math.pow" =>
+ x = toNumber(ex, biarg(args, 0));
+ y = toNumber(ex, biarg(args, 1));
+ case f.val.str{
+ "Math.atan2" =>
+ x = math->atan2(x, y);
+ "Math.max" =>
+ if(x > y)
+ ;
+ else if(x < y)
+ x = y;
+ else if(x == y){
+ if(x == 0. && copysign(1., x) == -1. && copysign(1., y) == 1.)
+ x = y;
+ }else
+ x = Math->NaN;
+ "Math.min" =>
+ if(x < y)
+ ;
+ else if(x > y)
+ x = y;
+ else if(x == y){
+ if(x == 0. && copysign(1., x) == 1. && copysign(1., y) == -1.)
+ x = y;
+ }else
+ x = Math->NaN;
+ "Math.pow" =>
+ x = math->pow(x, y);
+ }
+ v = numval(x);
+ "Date" =>
+ v = cdate(ex, f, this, args);
+ "Date.parse" =>
+ v = cdateparse(ex, f, this, args);
+ "Date.UTC" =>
+ v = cdateUTC(ex, f, this, args);
+ "Date.prototype.toString" or
+ "Date.prototype.toLocaleString" =>
+ v = cdateprototoString(ex, f, this, args);
+ "Date.prototype.toDateString" or
+ "Date.prototype.toLocaleDateString" =>
+ v = cdateprototoDateString(ex, f, this, args);
+ "Date.prototype.toTimeString" or
+ "Date.prototype.toLocaleTimeString" =>
+ v = cdateprototoTimeString(ex, f, this, args);
+ "Date.prototype.valueOf" or
+ "Date.prototype.getTime" =>
+ v = cdateprotovalueOf(ex, f, this, args);
+ "Date.prototype.getYear" or
+ "Date.prototype.getFullYear" or
+ "Date.prototype.getMonth" or
+ "Date.prototype.getDate" or
+ "Date.prototype.getDay" or
+ "Date.prototype.getHours" or
+ "Date.prototype.getMinutes" or
+ "Date.prototype.getSeconds" =>
+ v = cdateprotoget(ex, f, this, args, !UTC);
+ "Date.prototype.getUTCFullYear" or
+ "Date.prototype.getUTCMonth" or
+ "Date.prototype.getUTCDate" or
+ "Date.prototype.getUTCDay" or
+ "Date.prototype.getUTCHours" or
+ "Date.prototype.getUTCMinutes" or
+ "Date.prototype.getUTCSeconds" =>
+ v = cdateprotoget(ex, f, this, args, UTC);
+ "Date.prototype.getMilliseconds" or
+ "Date.prototype.getUTCMilliseconds" =>
+ v = cdateprotogetMilliseconds(ex, f, this, args);
+ "Date.prototype.getTimezoneOffset" =>
+ v = cdateprotogetTimezoneOffset(ex, f, this, args);
+ "Date.prototype.setTime" =>
+ v = cdateprotosetTime(ex, f, this, args);
+ "Date.prototype.setMilliseconds" =>
+ v = cdateprotosetMilliseconds(ex, f, this, args, !UTC);
+ "Date.prototype.setUTCMilliseconds" =>
+ v = cdateprotosetMilliseconds(ex, f, this, args, UTC);
+ "Date.prototype.setSeconds" =>
+ v = cdateprotosetSeconds(ex, f, this, args, !UTC);
+ "Date.prototype.setUTCSeconds" =>
+ v = cdateprotosetSeconds(ex, f, this, args, UTC);
+ "Date.prototype.setMinutes" =>
+ v = cdateprotosetMinutes(ex, f, this, args, !UTC);
+ "Date.prototype.setUTCMinutes" =>
+ v = cdateprotosetMinutes(ex, f, this, args, UTC);
+ "Date.prototype.setHours" =>
+ v = cdateprotosetHours(ex, f, this, args, !UTC);
+ "Date.prototype.setUTCHours" =>
+ v = cdateprotosetHours(ex, f, this, args, UTC);
+ "Date.prototype.setDate" =>
+ v = cdateprotosetDate(ex, f, this, args, !UTC);
+ "Date.prototype.setUTCDate" =>
+ v = cdateprotosetDate(ex, f, this, args, UTC);
+ "Date.prototype.setMonth" =>
+ v = cdateprotosetMonth(ex, f, this, args, !UTC);
+ "Date.prototype.setUTCMonth" =>
+ v = cdateprotosetMonth(ex, f, this, args, UTC);
+ "Date.prototype.setFullYear" =>
+ v = cdateprotosetFullYear(ex, f, this, args, !UTC);
+ "Date.prototype.setUTCFullYear" =>
+ v = cdateprotosetFullYear(ex, f, this, args, UTC);
+ "Date.prototype.setYear" =>
+ v = cdateprotosetYear(ex, f, this, args);
+ "Date.prototype.toUTCString" or
+ "Date.prototype.toGMTString" =>
+ v = cdateprototoUTCString(ex, f, this, args);
+ * =>
+ v = nil;
+ }
+ if(v == nil)
+ runtime(ex, ReferenceError, "unknown function "+f.val.str+" in builtin call");
+ return valref(v);
+}
+
+rsalt := big 12345678;
+
+randinit(seed: big)
+{
+ rsalt = big seed;
+ bigrand(big 1);
+ bigrand(big 1);
+}
+
+RANDMASK: con (big 1<<63)-(big 1);
+
+bigrand(modulus: big): big
+{
+ rsalt = rsalt * big 1103515245 + big 12345;
+ if(modulus <= big 0)
+ return big 0;
+ return ((rsalt&RANDMASK)>>10) % modulus;
+}
+
+construct(ex: ref Ecmascript->Exec, f: ref Ecmascript->Obj, args: array of ref Ecmascript->Val): ref Ecmascript->Obj
+{
+ if(f.host != me)
+ runtime(ex, TypeError, "ecmascript builtin called incorrectly");
+ case f.val.str{
+ "Object" =>
+ return nobj(ex, f, args);
+ "Function" =>
+ return nfunc(ex, f, args);
+ "Array" =>
+ return narray(ex, f, args);
+ "Error" =>
+ return nerr(ex, f, args, ex.errproto);
+ "EvalError" =>
+ return nerr(ex, f, args, ex.evlerrproto);
+ "RangeError" =>
+ return nerr(ex, f, args, ex.ranerrproto);
+ "ReferenceError" =>
+ return nerr(ex, f, args, ex.referrproto);
+ "SyntaxError" =>
+ return nerr(ex, f, args, ex.synerrproto);
+ "TypeError" =>
+ return nerr(ex, f, args, ex.typerrproto);
+ "URIError" =>
+ return nerr(ex, f, args, ex.urierrproto);
+ "InternalError" =>
+ return nerr(ex, f, args, ex.interrproto);
+ "String" or
+ "Boolean" or
+ "Number" =>
+ return coerceToObj(ex, call(ex, f, nil, args, 0).val).obj;
+ "Date" =>
+ return ndate(ex, f, args);
+ "RegExp" =>
+ return nregexp(ex, f, args);
+ }
+ runtime(ex, ReferenceError, "unknown constructor "+f.val.str+" in builtin construct");
+ return nil;
+}
+
+ceval(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ if(len args < 1)
+ return undefined;
+ vs := coerceToVal(args[0]);
+ if(!isstr(vs))
+ return args[0];
+ (k, v, nil) := eval(ex, vs.str);
+ if(k != CNormal || v == nil)
+ v = undefined;
+ return v;
+}
+
+cparseInt(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ sv := biarg(args, 0);
+ s := toString(ex, sv);
+ neg := 0;
+ i := 0;
+ if(len s > i){
+ if(s[i] == '-'){
+ neg = 1;
+ i++;
+ }else if(s[i] == '+')
+ i++;
+ }
+ rv := biarg(args, 1);
+ if(rv == undefined)
+ r := big 0;
+ else
+ r = big toInt32(ex, rv);
+ if(r == big 0){
+ if(len s > i && s[i] == '0'){
+ r = big 8;
+ if(len s >= i+2 && (s[i+1] == 'x' || s[i+1] == 'X'))
+ r = big 16;
+ }else
+ r = big 10;
+ }else if(r < big 0 || r > big 36)
+ return numval(Math->NaN);
+ if(r == big 16 && len s >= i+2 && s[i] == '0' && (s[i+1] == 'x' || s[i+1] == 'X'))
+ i += 2;
+ ok := 0;
+ n := big 0;
+ for(; i < len s; i++) {
+ c := s[i];
+ v := r;
+ case c {
+ 'a' to 'z' =>
+ v = big(c - 'a' + 10);
+ 'A' to 'Z' =>
+ v = big(c - 'A' + 10);
+ '0' to '9' =>
+ v = big(c - '0');
+ }
+ if(v >= r)
+ break;
+ ok = 1;
+ n = n * r + v;
+ }
+ if(!ok)
+ return numval(Math->NaN);
+ if(neg)
+ n = -n;
+ return numval(real n);
+}
+
+cparseFloat(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := toString(ex, biarg(args, 0));
+ (nil, r) := parsenum(ex, s, 0, ParseReal|ParseTrim);
+ return numval(r);
+}
+
+cescape(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := toString(ex, biarg(args, 0));
+ t := "";
+ for(i := 0; i < len s; i++){
+ c := s[i];
+ case c{
+ 'A' to 'Z' or
+ 'a' to 'z' or
+ '0' to '9' or
+ '@' or '*' or '_' or '+' or '-' or '.' or '/' =>
+ t[len t] = s[i];
+ * =>
+ e := "";
+ do{
+ d := c & 16rf;
+ e = "0123456789abcdef"[d:d+1] + e;
+ c >>= 4;
+ }while(c);
+ if(len e & 1)
+ e = "0" + e;
+ if(len e == 4)
+ e = "u" + e;
+ t += "%" + e;
+ }
+ }
+ return strval(t);
+}
+
+cunescape(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := toString(ex, biarg(args, 0));
+ t := "";
+ for(i := 0; i < len s; i++){
+ c := s[i];
+ if(c == '%'){
+ if(i + 5 < len s && s[i+1] == 'u'){
+ (v, e) := str->toint(s[i+2:i+6], 16);
+ if(e == ""){
+ c = v;
+ i += 5;
+ }
+ }else if(i + 2 < len s){
+ (v, e) := str->toint(s[i+1:i+3], 16);
+ if(e == ""){
+ c = v;
+ i += 2;
+ }
+ }
+ }
+ t[len t] = c;
+ }
+ return strval(t);
+}
+
+cisNaN(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ if(math->isnan(toNumber(ex, biarg(args, 0))))
+ return true;
+ return false;
+}
+
+cisFinite(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ r := toNumber(ex, biarg(args, 0));
+ if(math->isnan(r) || r == +Infinity || r == -Infinity)
+ return false;
+ return true;
+}
+
+cobj(ex: ref Exec, f, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ o: ref Obj;
+
+ v := biarg(args, 0);
+
+ if(isnull(v) || isundefined(v))
+ o = nobj(ex, f, args);
+ else
+ o = toObject(ex, v);
+ return objval(o);
+}
+
+nobj(ex: ref Exec, nil: ref Ecmascript->Obj, args: array of ref Val): ref Ecmascript->Obj
+{
+ o: ref Obj;
+
+ v := biarg(args, 0);
+
+ case v.ty{
+ TNull or TUndef =>
+ o = mkobj(ex.objproto, "Object");
+ TBool =>
+ o = mkobj(ex.boolproto, "Boolean");
+ o.val = v;
+ TStr =>
+ o = mkobj(ex.strproto, "String");
+ o.val = v;
+ varinstant(o, DontEnum|DontDelete|ReadOnly, "length", ref RefVal(numval(real len v.str)));
+ TNum =>
+ o = mkobj(ex.numproto, "Number");
+ o.val = v;
+ TObj =>
+ o = v.obj;
+ TRegExp =>
+ o = mkobj(ex.regexpproto, "RegExp");
+ o.val = v;
+ varinstant(o, DontEnum|DontDelete|ReadOnly, "length", ref RefVal(numval(real len v.rev.p)));
+ varinstant(o, DontEnum|DontDelete|ReadOnly, "source", ref RefVal(strval(v.rev.p)));
+ varinstant(o, DontEnum|DontDelete|ReadOnly, "global", ref RefVal(strhas(v.rev.f, 'g')));
+ varinstant(o, DontEnum|DontDelete|ReadOnly, "ignoreCase", ref RefVal(strhas(v.rev.f, 'i')));
+ varinstant(o, DontEnum|DontDelete|ReadOnly, "multiline", ref RefVal(strhas(v.rev.f, 'm')));
+ varinstant(o, DontEnum|DontDelete, "lastIndex", ref RefVal(numval(real v.rev.i)));
+
+ * =>
+ runtime(ex, ReferenceError, "unknown type in Object constructor");
+ }
+ return o;
+}
+
+cobjprototoString(nil: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ return strval("[object " + this.class + "]");
+}
+
+cobjprotovalueOf(nil: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ return objval(this);
+}
+
+cobjprotohasownprop(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ o := this;
+ s := toString(ex, biarg(args, 0));
+ p := o.prototype;
+ o.prototype = nil;
+ v := eshasproperty(ex, o, s, 0);
+ o.prototype = p;
+ return v;
+}
+
+cobjprotoisprotoof(nil: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ o := this;
+ v := biarg(args, 0);
+ if(!isobj(v))
+ return false;
+ for(p := v.obj.prototype; p != nil; p = p.prototype)
+ if(p == o)
+ return true;
+ return false;
+}
+
+cobjprotopropisenum(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ return eshasenumprop(this, toString(ex, biarg(args, 0)));
+}
+
+nfunc(ex: ref Exec, nil: ref Ecmascript->Obj, args: array of ref Val): ref Ecmascript->Obj
+{
+ params := "";
+ body := "";
+ sep := "";
+ for(i := 0; i < len args - 1; i++){
+ params += sep + toString(ex, args[i]);
+ sep = ",";
+ }
+ if(i < len args)
+ body = toString(ex, args[i]);
+
+ p := mkparser(ex, "function anonymous("+params+"){"+body+"}");
+ fundecl(ex, p, 0);
+ if(p.errors)
+ runtime(ex, SyntaxError, ex.error);
+ if(p.code.vars[0].name != "anonymous")
+ runtime(ex, SyntaxError, "parse failure");
+ return p.code.vars[0].val.val.obj;
+}
+
+cfuncprototoString(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ if(this.call == nil)
+ runtime(ex, TypeError, "Function.prototype.toString called for a non-Function object");
+ return strval(funcprint(ex, this));
+}
+
+nerr(ex: ref Exec, f: ref Ecmascript->Obj, args: array of ref Val, proto: ref Obj): ref Ecmascript->Obj
+{
+ msg := biarg(args, 0);
+ if(msg == undefined)
+ s := "";
+ else
+ s = toString(ex, msg);
+ o := mkobj(proto, f.val.str);
+ varinstant(o, DontEnum|DontDelete|ReadOnly, "length", ref RefVal(numval(real 1)));
+ varinstant(o, DontEnum|DontDelete, "name", ref RefVal(strval(f.val.str)));
+ varinstant(o, DontEnum|DontDelete, "message", ref RefVal(strval(s)));
+ return o;
+}
+
+cfuncprotoapply(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ ar: ref Obj;
+
+ if(this.call == nil || !isfuncobj(this))
+ runtime(ex, TypeError, "Function.prototype.apply called for a non-Function object");
+ v := biarg(args, 0);
+ if(v == null || v == undefined)
+ th := ex.global;
+ else
+ th = coerceToObj(ex, v).obj;
+ v = biarg(args, 1);
+ if(v == null || v == undefined)
+ l := 0;
+ else{
+ if(!isobj(v))
+ runtime(ex, TypeError, "Function.prototype.apply non-array argument");
+ ar = v.obj;
+ v = esget(ex, ar, "length", 0);
+ if(v == undefined)
+ runtime(ex, TypeError, "Function.prototype.apply non-array argument");
+ l = int toUint32(ex, v);
+ }
+ args = array[l] of ref Val;
+ for(i := 0; i < l; i++)
+ args[i] = esget(ex, ar, string i, 0);
+ return getValue(ex, escall(ex, this, th, args, 0));
+}
+
+cfuncprotocall(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ if(this.call == nil || !isfuncobj(this))
+ runtime(ex, TypeError, "Function.prototype.call called for a non-Function object");
+ v := biarg(args, 0);
+ if(v == null || v == undefined)
+ th := ex.global;
+ else
+ th = coerceToObj(ex, v).obj;
+ return getValue(ex, escall(ex, this, th, args[1: ], 0));
+}
+
+cerrprototoString(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ return esget(ex, this, "message", 0);
+}
+
+narray(ex: ref Exec, nil: ref Ecmascript->Obj, args: array of ref Val): ref Ecmascript->Obj
+{
+ o := mkobj(ex.arrayproto, "Array");
+ length := big len args;
+ if(length == big 1 && isnum(coerceToVal(args[0]))){
+ length = toUint32(ex, args[0]);
+ varinstant(o, DontEnum|DontDelete, "length", ref RefVal(numval(real length)));
+ }else{
+ varinstant(o, DontEnum|DontDelete, "length", ref RefVal(numval(real length)));
+ for(i := 0; i < len args; i++)
+ esput(ex, o, string i, args[i], 0);
+ }
+
+ return o;
+}
+
+carrayprototoString(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ return carrayprotojoin(ex, nil, this, nil);
+}
+
+carrayprotoconcat(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ v: ref Val;
+ e: ref Obj;
+
+ a := narray(ex, nil, nil);
+ n := 0;
+ nargs := len args;
+ for(i := -1; i < nargs; i++){
+ if(i < 0){
+ e = this;
+ v = objval(e);
+ }
+ else{
+ v = biarg(args, i);
+ if(isobj(v))
+ e = v.obj;
+ else
+ e = nil;
+ }
+ if(e != nil && isarray(e)){
+ leng := int toUint32(ex, esget(ex, e, "length", 0));
+ for(k := 0; k < leng; k++){
+ av := esget(ex, e, string k, 0);
+ if(v != undefined)
+ esput(ex, a, string n, av, 0);
+ n++;
+ }
+ }
+ else{
+ esput(ex, a, string n, v, 0);
+ n++;
+ }
+ }
+ esput(ex, a, "length", numval(real n), 0);
+ return objval(a);
+}
+
+carrayprotojoin(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ length := toUint32(ex, esget(ex, this, "length", 0));
+ sepv := biarg(args, 0);
+ sep := ",";
+ if(sepv != undefined)
+ sep = toString(ex, sepv);
+ s := "";
+ ss := "";
+ for(i := big 0; i < length; i++){
+ tv := esget(ex, this, string i, 0);
+ t := "";
+ if(tv != undefined && !isnull(tv))
+ t = toString(ex, tv);
+ s += ss + t;
+ ss = sep;
+ }
+ return strval(s);
+}
+
+carrayprotoreverse(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ length := toUint32(ex, esget(ex, this, "length", 0));
+ mid := length / big 2;
+ for(i := big 0; i < mid; i++){
+ i1 := string i;
+ v1 := esget(ex, this, i1, 0);
+ i2 := string(length - i - big 1);
+ v2 := esget(ex, this, i2, 0);
+ if(v2 == undefined)
+ esdelete(ex, this, i1, 0);
+ else
+ esput(ex, this, i1, v2, 0);
+ if(v1 == undefined)
+ esdelete(ex, this, i2, 0);
+ else
+ esput(ex, this, i2, v1, 0);
+ }
+ return objval(this);
+}
+
+carrayprotopop(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ leng := toUint32(ex, esget(ex, this, "length", 0));
+ if(leng == big 0){
+ esput(ex, this, "length", numval(0.), 0);
+ return undefined;
+ }
+ ind := string (leng-big 1);
+ v := esget(ex, this, ind, 0);
+ esdelete(ex, this, ind, 0);
+ esput(ex, this, "length", numval(real (leng-big 1)), 0);
+ return v;
+}
+
+carrayprotopush(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ leng := toUint32(ex, esget(ex, this, "length", 0));
+ nargs := len args;
+ for(i := 0; i < nargs; i++)
+ esput(ex, this, string (leng+big i), biarg(args, i), 0);
+ nv := numval(real (leng+big nargs));
+ esput(ex, this, "length", nv, 0);
+ return nv;
+}
+
+carrayprotoshift(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ leng := int toUint32(ex, esget(ex, this, "length", 0));
+ if(leng == 0){
+ esput(ex, this, "length", numval(0.), 0);
+ return undefined;
+ }
+ v0 := esget(ex, this, "0", 0);
+ for(k := 1; k < leng; k++){
+ v := esget(ex, this, string k, 0);
+ if(v == undefined)
+ esdelete(ex, this, string (k-1), 0);
+ else
+ esput(ex, this, string (k-1), v, 0);
+ }
+ esdelete(ex, this, string (leng-1), 0);
+ esput(ex, this, "length", numval(real (leng-1)), 0);
+ return v0;
+}
+
+carrayprotounshift(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ leng := int toUint32(ex, esget(ex, this, "length", 0));
+ nargs := len args;
+ for(i := leng-1; i >= 0; i--){
+ v := esget(ex, this, string i, 0);
+ if(v == undefined)
+ esdelete(ex, this, string (i+nargs), 0);
+ else
+ esput(ex, this, string (i+nargs), v, 0);
+ }
+ for(i = 0; i < nargs; i++)
+ esput(ex, this, string i, biarg(args, i), 0);
+ nv := numval(real (leng+nargs));
+ esput(ex, this, "length", nv, 0);
+ return nv;
+}
+
+carrayprotoslice(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ a := narray(ex, nil, nil);
+ leng := int toUint32(ex, esget(ex, this, "length", 0));
+ start := toInt32(ex, biarg(args, 0));
+ if(start < 0) start += leng;
+ if(start < 0) start = 0;
+ if(start > leng) start = leng;
+ if(biarg(args, 1) == undefined)
+ end := leng;
+ else
+ end = toInt32(ex, biarg(args, 1));
+ if(end < 0) end += leng;
+ if(end < 0) end = 0;
+ if(end > leng) end = leng;
+ n := 0;
+ for(k := start; k < end; k++){
+ v := esget(ex, this, string k, 0);
+ if(v != undefined)
+ esput(ex, a, string n, v, 0);
+ n++;
+ }
+ esput(ex, a, "length", numval(real n), 0);
+ return objval(a);
+}
+
+carrayprotosplice(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ a := narray(ex, nil, nil);
+ leng := int toUint32(ex, esget(ex, this, "length", 0));
+ start := toInt32(ex, biarg(args, 0));
+ if(start < 0) start += leng;
+ if(start < 0) start = 0;
+ if(start > leng) start = leng;
+ delc := toInt32(ex, biarg(args, 1));
+ if(delc < 0) delc = 0;
+ if(start+delc > leng) delc = leng-start;
+ for(k := 0; k < delc; k++){
+ v := esget(ex, this, string (k+start), 0);
+ if(v != undefined)
+ esput(ex, a, string k, v, 0);
+ }
+ esput(ex, a, "length", numval(real delc), 0);
+ nargs := len args - 2;
+ if(nargs < delc){
+ for(k = start; k < leng-delc; k++){
+ v := esget(ex, this, string (k+delc), 0);
+ if(v == undefined)
+ esdelete(ex, this, string (k+nargs), 0);
+ else
+ esput(ex, this, string (k+nargs), v, 0);
+ }
+ for(k = leng; k > leng-delc+nargs; k--)
+ esdelete(ex, this, string (k-1), 0);
+ }
+ else if(nargs > delc){
+ for(k = leng-delc; k > start; k--){
+ v := esget(ex, this, string (k+delc-1), 0);
+ if(v == undefined)
+ esdelete(ex, this, string (k+nargs-1), 0);
+ else
+ esput(ex, this, string (k+nargs-1), v, 0);
+ }
+ }
+ for(k = start; k < start+nargs; k++)
+ esput(ex, this, string k, biarg(args, k-start+2), 0);
+ esput(ex, this, "length", numval(real (leng-delc+nargs)), 0);
+ return objval(a);
+}
+
+carrayprotosort(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ length := toUint32(ex, esget(ex, this, "length", 0));
+ cmp := biarg(args, 0);
+ if(cmp == undefined)
+ cmp = nil;
+ else if(!isobj(cmp) || cmp.obj.call == nil)
+ runtime(ex, TypeError, "Array.prototype.sort argument is not a function");
+
+ #
+ # shell sort
+ #
+ for(m := (length+big 3)/big 5; m > big 0; m = (m+big 1)/big 3){
+ for(i := length-m; i-- != big 0;){
+ v1, v2 : ref Val = nil;
+ ji := big -1;
+ for(j := i+m; j < length; j += m){
+ if(v1 == nil)
+ v1 = esget(ex, this, string(j-m), 0);
+ v2 = esget(ex, this, string(j), 0);
+ cr : real;
+ if(v1 == undefined && v2 == undefined)
+ cr = 0.;
+ else if(v1 == undefined)
+ cr = 1.;
+ else if(v2 == undefined)
+ cr = -1.;
+ else if(cmp == nil){
+ s1 := toString(ex, v1);
+ s2 := toString(ex, v2);
+ if(s1 < s2)
+ cr = -1.;
+ else if(s1 > s2)
+ cr = 1.;
+ else
+ cr = 0.;
+ }else{
+ #
+ # this value not specified by docs
+ #
+ cr = toNumber(ex, getValue(ex, escall(ex, cmp.obj, this, array[] of {v1, v2}, 0)));
+ }
+ if(cr <= 0.)
+ break;
+ if(v2 == undefined)
+ esdelete(ex, this, string(j-m), 0);
+ else
+ esput(ex, this, string(j-m), v2, 0);
+ ji = j;
+ }
+ if(ji != big -1){
+ if(v1 == undefined)
+ esdelete(ex, this, string(ji), 0);
+ else
+ esput(ex, this, string(ji), v1, 0);
+ }
+ }
+ }
+ return objval(this);
+}
+
+cstr(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := "";
+ if(len args > 0)
+ s = toString(ex, biarg(args, 0));
+ return strval(s);
+}
+
+cstrfromCharCode(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := "";
+ for(i := 0; i < len args; i++)
+ s[i] = toUint16(ex, args[i]);
+ return strval(s);
+}
+
+cstrprototoString(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ if(!isstrobj(this))
+ runtime(ex, TypeError, "String.prototype.toString called on non-String object");
+ return this.val;
+}
+
+cstrprotocharAt(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := toString(ex, objval(this));
+ rpos := toInteger(ex, biarg(args, 0));
+ if(rpos < 0. || rpos >= real len s)
+ s = "";
+ else{
+ pos := int rpos;
+ s = s[pos: pos+1];
+ }
+ return strval(s);
+}
+
+cstrprotocharCodeAt(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := toString(ex, objval(this));
+ rpos := toInteger(ex, biarg(args, 0));
+ if(rpos < 0. || rpos >= real len s)
+ c := Math->NaN;
+ else
+ c = real s[int rpos];
+ return numval(c);
+}
+
+cstrprotoindexOf(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := toString(ex, objval(this));
+ t := toString(ex, biarg(args, 0));
+ rpos := toInteger(ex, biarg(args, 1));
+ if(rpos < 0.)
+ rpos = 0.;
+ else if(rpos > real len s)
+ rpos = real len s;
+ lent := len t;
+ stop := len s - lent;
+ for(i := int rpos; i <= stop; i++)
+ if(s[i:i+lent] == t)
+ break;
+ if(i > stop)
+ i = -1;
+ return numval(real i);
+}
+
+cstrprotolastindexOf(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := toString(ex, objval(this));
+ t := toString(ex, biarg(args, 0));
+ v := biarg(args, 1);
+ rpos := toNumber(ex, v);
+ if(math->isnan(rpos))
+ rpos = Math->Infinity;
+ else
+ rpos = toInteger(ex, v);
+ if(rpos < 0.)
+ rpos = 0.;
+ else if(rpos > real len s)
+ rpos = real len s;
+ lent := len t;
+ i := len s - lent;
+ if(i > int rpos)
+ i = int rpos;
+ for(; i >= 0; i--)
+ if(s[i:i+lent] == t)
+ break;
+ return numval(real i);
+}
+
+cstrprotosplit(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := toString(ex, objval(this));
+ a := narray(ex, nil, nil);
+ tv := biarg(args, 0);
+ ai := 0;
+ if(tv == undefined)
+ esput(ex, a, string ai, strval(s), 0);
+ else{
+ t := toString(ex, tv);
+ lent := len t;
+ stop := len s - lent;
+ pos := 0;
+ if(lent == 0){
+ for(; pos < stop; pos++)
+ esput(ex, a, string ai++, strval(s[pos:pos+1]), 0);
+ }else{
+ for(k := pos; k <= stop; k++){
+ if(s[k:k+lent] == t){
+ esput(ex, a, string ai++, strval(s[pos:k]), 0);
+ pos = k + lent;
+ k = pos - 1;
+ }
+ }
+ esput(ex, a, string ai, strval(s[pos:k]), 0);
+ }
+ }
+ return objval(a);
+}
+
+cstrprotosubstring(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := toString(ex, objval(this));
+ rstart := toInteger(ex, biarg(args, 0));
+ lens := real len s;
+ rend := lens;
+ if(len args >= 2)
+ rend = toInteger(ex, biarg(args, 1));
+ if(rstart < 0.)
+ rstart = 0.;
+ else if(rstart > lens)
+ rstart = lens;
+ if(rend < 0.)
+ rend = 0.;
+ else if(rend > lens)
+ rend = lens;
+ if(rstart > rend){
+ lens = rstart;
+ rstart = rend;
+ rend = lens;
+ }
+ return strval(s[int rstart: int rend]);
+}
+
+cstrprotosubstr(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := toString(ex, objval(this));
+ ls := len s;
+ start := toInt32(ex, biarg(args, 0));
+ if(biarg(args, 1) == undefined)
+ leng := ls;
+ else
+ leng = toInt32(ex, biarg(args, 1));
+ if(start < 0)
+ start += ls;
+ if(start < 0)
+ start = 0;
+ if(leng < 0)
+ leng = 0;
+ if(start+leng > ls)
+ leng = ls-start;
+ if(leng <= 0)
+ s = "";
+ else
+ s = s[start: start+leng];
+ return strval(s);
+}
+
+cstrprotoslice(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := toString(ex, objval(this));
+ ls := len s;
+ start := toInt32(ex, biarg(args, 0));
+ if(biarg(args, 1) == undefined)
+ end := ls;
+ else
+ end = toInt32(ex, biarg(args, 1));
+ if(start < 0)
+ start += ls;
+ if(start < 0)
+ start = 0;
+ if(start > ls)
+ start = ls;
+ if(end < 0)
+ end += ls;
+ if(end < 0)
+ end = 0;
+ if(end > ls)
+ end = ls;
+ leng := end-start;
+ if(leng < 0)
+ leng = 0;
+ return strval(s[start: start+leng]);
+}
+
+cstrprotocmp(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := toString(ex, objval(this));
+ t := toString(ex, biarg(args, 0));
+ r := 0;
+ if(s < t)
+ r = -1;
+ else if(s > t)
+ r = 1;
+ return numval(real r);
+}
+
+cstrprotoconcat(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := toString(ex, objval(this));
+ n := len args;
+ for(i := 0; i < n; i++)
+ s += toString(ex, biarg(args, i));
+ return strval(s);
+}
+
+# this doesn't use unicode tolower
+cstrprototoLowerCase(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ s := toString(ex, objval(this));
+ for(i := 0; i < len s; i++)
+ s[i] = tolower(s[i]);
+ return strval(s);
+}
+
+#this doesn't use unicode toupper
+cstrprototoUpperCase(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ s := toString(ex, objval(this));
+ for(i := 0; i < len s; i++)
+ s[i] = toupper(s[i]);
+ return strval(s);
+}
+
+cbool(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ return toBoolean(ex, biarg(args, 0));
+}
+
+tolower(c: int): int
+{
+ if(c >= 'A' && c <= 'Z')
+ return c - 'A' + 'a';
+ return c;
+}
+
+toupper(c: int): int
+{
+ if(c >= 'a' && c <= 'a')
+ return c - 'a' + 'A';
+ return c;
+}
+
+cboolprototoString(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ if(!isboolobj(this))
+ runtime(ex, TypeError, "Boolean.prototype.toString called on non-Boolean object");
+ return strval(toString(ex, this.val));
+}
+
+cboolprotovalueOf(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ if(!isboolobj(this))
+ runtime(ex, TypeError, "Boolean.prototype.valueOf called on non-Boolean object");
+ return this.val;
+}
+
+cnum(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ r := 0.;
+ if(len args > 0)
+ r = toNumber(ex, biarg(args, 0));
+ return numval(r);
+}
+
+cnumprototoString(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ if(!isnumobj(this))
+ runtime(ex, TypeError, "Number.prototype.toString called on non-Number object");
+ return this.val;
+}
+
+cnumprotovalueOf(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ if(!isnumobj(this))
+ runtime(ex, TypeError, "Number.prototype.valueOf called on non-Number object");
+ return strval(toString(ex, this.val));
+}
+
+cnumprotofix(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ if(!isnumobj(this))
+ runtime(ex, TypeError, "Number.prototype.toFixed called on non-Number object");
+ v := biarg(args, 0);
+ if(v == undefined)
+ f := 0;
+ else
+ f = toInt32(ex, v);
+ if(f < 0 || f > 20)
+ runtime(ex, RangeError, "fraction digits out of range");
+ x := toNumber(ex, this.val);
+ if(isnan(x) || x == Infinity || x == -Infinity)
+ s := toString(ex, this.val);
+ else
+ s = sys->sprint("%.*f", f, x);
+ return strval(s);
+}
+
+cnumprotoexp(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ if(!isnumobj(this))
+ runtime(ex, TypeError, "Number.prototype.toExponential called on non-Number object");
+ v := biarg(args, 0);
+ if(v == undefined)
+ f := 6;
+ else
+ f = toInt32(ex, v);
+ if(f < 0 || f > 20)
+ runtime(ex, RangeError, "fraction digits out of range");
+ x := toNumber(ex, this.val);
+ if(isnan(x) || x == Infinity || x == -Infinity)
+ s := toString(ex, this.val);
+ else
+ s = sys->sprint("%.*e", f, x);
+ return strval(s);
+}
+
+cnumprotoprec(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ if(!isnumobj(this))
+ runtime(ex, TypeError, "Number.prototype.toPrecision called on non-Number object");
+ v := biarg(args, 0);
+ if(v == undefined)
+ return strval(toString(ex, this.val));
+ p := toInt32(ex, v);
+ if(p < 1 || p > 21)
+ runtime(ex, RangeError, "fraction digits out of range");
+ x := toNumber(ex, this.val);
+ if(isnan(x) || x == Infinity || x == -Infinity)
+ s := toString(ex, this.val);
+ else{
+ y := x;
+ if(y < 0.0)
+ y = -y;
+ er := math->log10(y);
+ (e, ef) := math->modf(er);
+ if(ef < 0.0)
+ e--;
+ if(e < -6 || e >=p)
+ s = sys->sprint("%.*e", p-1, x);
+ else
+ s = sys->sprint("%.*f", p-1, x);
+ }
+ return strval(s);
+}
diff --git a/appl/lib/ecmascript/date.b b/appl/lib/ecmascript/date.b
new file mode 100644
index 00000000..e84d00b6
--- /dev/null
+++ b/appl/lib/ecmascript/date.b
@@ -0,0 +1,495 @@
+# the Date object is founded on the Daytime module
+
+UTC: con 1;
+msPerSec: con big 1000;
+
+# based on Daytime->Tm with big fields
+bigTm: adt {
+ ms: big;
+ sec: big;
+ min: big;
+ hour: big;
+ mday: big;
+ mon: big;
+ year: big;
+ tzoff: int;
+};
+
+isfinite(r: real): int
+{
+ if(math->isnan(r) || r == +Infinity || r == -Infinity)
+ return 0;
+ return 1;
+}
+
+time2Tm(t: real, utc: int): ref Daytime->Tm
+{
+ secs := int(big t / msPerSec);
+ if(big t % msPerSec < big 0) # t<0?
+ secs -= 1;
+ if(utc)
+ tm := daytime->gmt(secs);
+ else
+ tm = daytime->local(secs);
+ return tm;
+}
+
+time2bigTm(t: real, utc: int): ref bigTm
+{
+ tm := time2Tm(t, utc);
+ btm := ref bigTm;
+ btm.ms = big t % msPerSec;
+ if(btm.ms < big 0)
+ btm.ms += msPerSec;
+ btm.sec = big tm.sec;
+ btm.min = big tm.min;
+ btm.hour = big tm.hour;
+ btm.mday = big tm.mday;
+ btm.mon = big tm.mon;
+ btm.year = big tm.year;
+ btm.tzoff = tm.tzoff;
+ return btm;
+}
+
+bigTm2time(btm: ref bigTm): real
+{
+ # normalize
+ if(btm.mon / big 12 != big 0){
+ btm.year += btm.mon / big 12;
+ btm.mon %= big 12;
+ }
+ if(btm.ms / msPerSec != big 0){
+ btm.sec += btm.ms / msPerSec;
+ btm.ms %= msPerSec;
+ }
+ if(btm.sec / big 60 != big 0){
+ btm.min += btm.sec / big 60;
+ btm.sec %= big 60;
+ }
+ if(btm.min / big 60 != big 0){
+ btm.hour += btm.hour / big 60;
+ btm.min %= big 60;
+ }
+ if(btm.hour / big 24 != big 0){
+ btm.mday += btm.mday / big 24;
+ btm.hour %= big 24;
+ }
+
+ tm := ref Tm;
+ tm.sec = int btm.sec;
+ tm.min = int btm.min;
+ tm.hour = int btm.hour;
+ tm.mday = int btm.mday;
+ tm.mon = int btm.mon;
+ tm.year = int btm.year;
+ tm.tzoff = btm.tzoff;
+ secs := daytime->tm2epoch(tm);
+ # check for out-of-band times
+ if(secs == daytime->tm2epoch(daytime->gmt(secs)))
+ r := real(big secs * msPerSec + btm.ms);
+ else
+ r = Math->NaN;
+ return r;
+}
+
+str2time(s: string): real
+{
+ tm := daytime->string2tm(s);
+ if(tm == nil)
+ r := Math->NaN;
+ else
+ r = real (big daytime->tm2epoch(tm) * msPerSec);
+ return r;
+}
+
+cdate(nil: ref Exec, nil, nil: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ return strval(daytime->time());
+}
+
+# process arguments of Date() [called as constructor] and Date.UTC()
+datectorargs(ex: ref Exec, args: array of ref Val): (int, ref bigTm)
+{
+ x := array[7] of { * => big 0 };
+ ok := 1;
+ for(i := 0; i < 7 && i < len args; i++){
+ if(!isfinite(toNumber(ex, biarg(args, i))))
+ ok = 0;
+ else
+ x[i] = big toInteger(ex, biarg(args, i));
+ }
+ btm := ref bigTm;
+ yr := x[0];
+ if(yr >= big 0 && yr <= big 99)
+ btm.year = yr;
+ else
+ btm.year = yr - big 1900;
+ btm.mon = x[1];
+ btm.mday = x[2];
+ btm.hour = x[3];
+ btm.min = x[4];
+ btm.sec = x[5];
+ btm.ms = x[6];
+ return (ok, btm);
+}
+
+ndate(ex: ref Exec, nil: ref Ecmascript->Obj, args: array of ref Val): ref Ecmascript->Obj
+{
+ o := mkobj(ex.dateproto, "Date");
+ r := Math->NaN;
+ case len args{
+ 0 =>
+ r = real(big daytime->now() * msPerSec);
+ 1 =>
+ v := toPrimitive(ex, biarg(args, 0), NoHint);
+ if(isstr(v))
+ r = str2time(v.str);
+ else if(isfinite(toNumber(ex, v))){
+ t := big toInteger(ex, v);
+ secs := t / msPerSec;
+ if(big t % msPerSec < big 0)
+ secs -= big 1;
+ if(secs == big int secs)
+ r = real t;
+ }
+ * =>
+ (ok, btm) := datectorargs(ex, args);
+ if(ok){
+ tm := daytime->local(daytime->now());
+ btm.tzoff = tm.tzoff;
+ r = bigTm2time(btm);
+ }
+ }
+ o.val = numval(r);
+ return o;
+}
+
+cdateparse(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ s := toString(ex, biarg(args, 0));
+ return numval(str2time(s));
+}
+
+cdateUTC(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ r := Math->NaN;
+ if(len args == 0)
+ r = real(big daytime->now() * msPerSec);
+ else{
+ (ok, btm) := datectorargs(ex, args);
+ if(ok){
+ btm.tzoff = 0;
+ r = bigTm2time(btm);
+ }
+ }
+ return numval(r);
+}
+
+datecheck(ex: ref Exec, o: ref Ecmascript->Obj, f: string)
+{
+ if(!isdateobj(o))
+ runtime(ex, TypeError, "Date.prototype." + f + " called on non-Date object");
+}
+
+cdateprototoString(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ secs := 0;
+ t := this.val.num;
+ if(!math->isnan(t)){
+ secs = int(big t / msPerSec);
+ if(big t % msPerSec < big 0)
+ secs -= 1;
+ }
+ return strval(daytime->text(daytime->local(secs)));
+}
+
+cdateprototoDateString(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ secs := 0;
+ t := this.val.num;
+ if(!math->isnan(t)){
+ secs = int(big t / msPerSec);
+ if(big t % msPerSec < big 0)
+ secs -= 1;
+ }
+ s := daytime->text(daytime->local(secs));
+ (n, ls) := sys->tokenize(s, " ");
+ if(n < 3)
+ return strval("");
+ return strval(hd ls + " " + hd tl ls + " " + hd tl tl ls);
+}
+
+cdateprototoTimeString(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ secs := 0;
+ t := this.val.num;
+ if(!math->isnan(t)){
+ secs = int(big t / msPerSec);
+ if(big t % msPerSec < big 0)
+ secs -= 1;
+ }
+ s := daytime->text(daytime->local(secs));
+ (n, ls) := sys->tokenize(s, " ");
+ if(n < 4)
+ return strval("");
+ return strval(hd tl tl tl ls);
+}
+
+cdateprotovalueOf(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ return this.val;
+}
+
+cdateprotoget(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val, utc: int): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ t := this.val.num;
+ if(!math->isnan(t)){
+ tm := time2Tm(t, utc);
+ case f.val.str{
+ "Date.prototype.getYear" =>
+ if (tm.year < 0 || tm.year > 99)
+ t = real(tm.year + 1900);
+ else
+ t = real tm.year;
+ "Date.prototype.getFullYear" or
+ "Date.prototype.getUTCFullYear" =>
+ t = real(tm.year + 1900);
+ "Date.prototype.getMonth" or
+ "Date.prototype.getUTCMonth" =>
+ t = real tm.mon;
+ "Date.prototype.getDate" or
+ "Date.prototype.getUTCDate" =>
+ t = real tm.mday;
+ "Date.prototype.getDay" or
+ "Date.prototype.getUTCDay" =>
+ t = real tm.wday;
+ "Date.prototype.getHours" or
+ "Date.prototype.getUTCHours" =>
+ t = real tm.hour;
+ "Date.prototype.getMinutes" or
+ "Date.prototype.getUTCMinutes" =>
+ t = real tm.min;
+ "Date.prototype.getSeconds" or
+ "Date.prototype.getUTCSeconds" =>
+ t = real tm.sec;
+ }
+ }
+ return numval(t);
+}
+
+cdateprotogetMilliseconds(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ t := this.val.num;
+ if(!math->isnan(t)){
+ ms := big t % msPerSec;
+ if(ms < big 0)
+ ms += msPerSec;
+ t = real ms;
+ }
+ return numval(t);
+}
+
+cdateprotogetTimezoneOffset(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ t := this.val.num;
+ if(!math->isnan(t)){
+ tm := time2Tm(t, !UTC);
+ t = real(tm.tzoff / 60);
+ }
+ return numval(t);
+}
+
+# process arguments of Date.prototype.set*() functions
+dateprotosetargs(ex: ref Exec, this: ref Ecmascript->Obj, args: array of ref Val, n: int): (int, big, big, big, big)
+{
+ x := array[4] of { * => big 0 };
+ ok := 1;
+ if(this != nil && !isfinite(this.val.num))
+ ok = 0;
+ for(i := 0; i < n && i < len args; i++){
+ if(!isfinite(toNumber(ex, biarg(args, i))))
+ ok = 0;
+ else
+ x[i] = big toInteger(ex, biarg(args, i));
+ }
+ return (ok, x[0], x[1], x[2], x[3]);
+}
+
+cdateprotosetTime(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ r := Math->NaN;
+ (ok, t, nil, nil, nil) := dateprotosetargs(ex, nil, args, 1);
+ if(ok){
+ secs := t / msPerSec;
+ if(big t % msPerSec < big 0)
+ secs -= big 1;
+ if(secs == big int secs)
+ r = real t;
+ }
+ this.val.num = r;
+ return numval(r);
+}
+
+cdateprotosetMilliseconds(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ r := Math->NaN;
+ (ok, ms, nil, nil, nil) := dateprotosetargs(ex, this, args, 1);
+ if(ok){
+ btm := time2bigTm(this.val.num, utc);
+ btm.ms = ms;
+ r = bigTm2time(btm);
+ }
+ this.val.num = r;
+ return numval(r);
+}
+
+cdateprotosetSeconds(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ r := Math->NaN;
+ (ok, sec, ms, nil, nil) := dateprotosetargs(ex, this, args, 2);
+ if(ok){
+ btm := time2bigTm(this.val.num, utc);
+ btm.sec = sec;
+ if(len args > 1)
+ btm.ms = ms;
+ r = bigTm2time(btm);
+ }
+ this.val.num = r;
+ return numval(r);
+}
+
+cdateprotosetMinutes(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ r := Math->NaN;
+ (ok, min, sec, ms, nil) := dateprotosetargs(ex, this, args, 3);
+ if(ok){
+ btm := time2bigTm(this.val.num, utc);
+ btm.min = min;
+ if(len args > 1){
+ btm.sec = sec;
+ if(len args > 2)
+ btm.ms = ms;
+ }
+ r = bigTm2time(btm);
+ }
+ this.val.num = r;
+ return numval(r);
+}
+
+cdateprotosetHours(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ r := Math->NaN;
+ (ok, hour, min, sec, ms) := dateprotosetargs(ex, this, args, 4);
+ if(ok){
+ btm := time2bigTm(this.val.num, utc);
+ btm.hour = hour;
+ if(len args > 1){
+ btm.min = min;
+ if(len args > 2){
+ btm.sec = sec;
+ if(len args > 3)
+ btm.ms = ms;
+ }
+ }
+ r = bigTm2time(btm);
+ }
+ this.val.num = r;
+ return numval(r);
+}
+
+cdateprotosetDate(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ r := Math->NaN;
+ (ok, day, nil, nil, nil) := dateprotosetargs(ex, this, args, 1);
+ if(ok){
+ btm := time2bigTm(this.val.num, utc);
+ btm.mday = day;
+ r = bigTm2time(btm);
+ }
+ this.val.num = r;
+ return numval(r);
+}
+
+cdateprotosetMonth(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ r := Math->NaN;
+ (ok, mon, day, nil, nil) := dateprotosetargs(ex, this, args, 2);
+ if(ok){
+ btm := time2bigTm(this.val.num, utc);
+ btm.mon = mon;
+ if(len args > 1)
+ btm.mday = day;
+ r = bigTm2time(btm);
+ }
+ this.val.num = r;
+ return numval(r);
+}
+
+cdateprotosetFullYear(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ r := Math->NaN;
+ (ok, year, mon, day, nil) := dateprotosetargs(ex, nil, args, 3);
+ if(ok){
+ t := this.val.num;
+ if(!isfinite(t))
+ t = 0.;
+ btm := time2bigTm(t, utc);
+ btm.year = (year - big 1900);
+ if(len args > 1){
+ btm.mon = mon;
+ if(len args > 2)
+ btm.mday = day;
+ }
+ r = bigTm2time(btm);
+ }
+ this.val.num = r;
+ return numval(r);
+}
+
+cdateprotosetYear(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ r := Math->NaN;
+ (ok, year, nil, nil, nil) := dateprotosetargs(ex, nil, args, 1);
+ if(ok){
+ t := this.val.num;
+ if(!isfinite(t))
+ t = 0.;
+ btm := time2bigTm(t, !UTC);
+ if(year >= big 0 && year <= big 99)
+ btm.year = year;
+ else
+ btm.year = year - big 1900;
+ r = bigTm2time(btm);
+ }
+ this.val.num = r;
+ return numval(r);
+}
+
+cdateprototoUTCString(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ datecheck(ex, this, f.val.str);
+ secs := 0;
+ t := this.val.num;
+ if(!math->isnan(t)){
+ secs = int(big t / msPerSec);
+ if(big t % msPerSec < big 0)
+ secs -= 1;
+ }
+ return strval(daytime->text(daytime->gmt(secs)));
+}
diff --git a/appl/lib/ecmascript/ecmascript.b b/appl/lib/ecmascript/ecmascript.b
new file mode 100644
index 00000000..ccbce016
--- /dev/null
+++ b/appl/lib/ecmascript/ecmascript.b
@@ -0,0 +1,2626 @@
+implement Ecmascript;
+
+include "sys.m";
+include "math.m";
+include "string.m";
+include "daytime.m";
+include "ecmascript.m";
+
+include "pprint.b";
+include "obj.b";
+include "exec.b";
+include "date.b";
+include "builtin.b";
+include "regexp.b";
+include "uri.b";
+
+FF: con '\u000c';
+LS: con '\u2028';
+PS: con '\u2029';
+
+islt(c: int): int
+{
+ return c == '\n' || c == '\r' || c == LS || c == PS;
+}
+
+me: ESHostobj;
+
+sys: Sys;
+print, sprint: import sys;
+stdout: ref Sys->FD;
+
+math: Math;
+ isnan, floor, copysign, fabs, fmod, NaN, Infinity: import math;
+
+str: String;
+
+daytime: Daytime;
+ Tm: import daytime;
+
+labrec: adt{
+ s: string; # name
+ k: int; # kind
+};
+
+HashSize: con 1024;
+
+Parser: adt
+{
+ ex: ref Exec;
+
+ code: ref Code;
+
+ inloop: int; # parser state
+ incase: int;
+ infunc: int;
+ lastnl: int; # parser state for inserting ;
+ notin: int; # don't allow `in' in expression
+
+ token: int; # lexical token
+ token1: int; # lexical token
+ id: int; # associated value
+ lineno: int;
+
+ src: string; # lexical input state
+ esrc: int;
+ srci: int;
+
+ errors: int;
+ labs: list of ref labrec;
+};
+
+Keywd: adt
+{
+ name: string;
+ token: int;
+};
+
+#
+# lexical tokens and ops
+#
+ Lbase: con 128;
+
+ Leos,
+
+ Landas,
+ Loras,
+ Lxoras,
+ Llshas,
+ Lrshas,
+ Lrshuas,
+ Laddas,
+ Lsubas,
+ Lmulas,
+ Ldivas,
+ Lmodas,
+ Loror,
+ Landand,
+ Leq,
+ Lneq,
+ Lleq,
+ Lgeq,
+ Llsh,
+ Lrsh,
+ Lrshu,
+ Linc,
+ Ldec,
+ Lnum,
+ Lid,
+ Lstr,
+ Lthis,
+ Ltypeof,
+ Ldelete,
+ Lvoid,
+ Lwhile,
+ Lfor,
+ Lbreak,
+ Lcontinue,
+ Lwith,
+ Lreturn,
+ Lfunction,
+ Lvar,
+ Lif,
+ Lelse,
+ Lin,
+ Lnew,
+ Lcase,
+ Ldefault,
+ Lswitch,
+ Ldo,
+ Linstanceof,
+ Lcatch,
+ Lfinally,
+ Lthrow,
+ Ltry,
+ Lregexp,
+ Lseq,
+ Lsne,
+ Lprint,
+
+ Lpostinc, # ops that aren't lexical tokens
+ Lpostdec,
+ Lpresub,
+ Lpreadd,
+ Lcall,
+ Lnewcall,
+ Lgetval,
+ Las,
+ Lasop,
+ Lforin,
+ Lforvar,
+ Lforvarin,
+ Larrinit,
+ Lobjinit,
+ Lnoval,
+ Llabel,
+ Lbreaklab,
+ Lcontinuelab,
+
+ #
+ # reserved words
+ #
+ Labstract,
+ Lboolean,
+ Lbyte,
+ Lchar,
+ Lclass,
+ Lconst,
+ Ldebugger,
+ Ldouble,
+ Lenum,
+ Lexport,
+ Lextends,
+ Lfinal,
+ Lfloat,
+ Lgoto,
+ Limplements,
+ Limport,
+ Lint,
+ Linterface,
+ Llong,
+ Lnative,
+ Lpackage,
+ Lprivate,
+ Lprotected,
+ Lpublic,
+ Lshort,
+ Lstatic,
+ Lsuper,
+ Lsynchronized,
+ Lthrows,
+ Ltransient,
+ Lvolatile: con Lbase + iota;
+
+
+#
+# internals
+#
+
+Mlower, Mupper, Munder, Mdigit, Msign, Mexp, Mhex, Moct: con byte 1 << iota;
+Malpha: con Mupper|Mlower|Munder;
+map := array[256] of
+{
+ '_' or '$' => Munder,
+ '-' or '+' => Msign,
+ 'a' to 'd' or 'f' => Mlower | Mhex,
+ 'e' => Mlower | Mhex | Mexp,
+ 'g' to 'z' => Mlower,
+ 'A' to 'D' or 'F' => Mupper | Mhex,
+ 'E' => Mupper | Mhex | Mexp,
+ 'G' to 'Z' => Mupper,
+ '0' to '7' => Mdigit | Mhex | Moct,
+ '8' to '9' => Mdigit | Mhex,
+ * => byte 0
+};
+
+maxerr: int;
+toterrors: int;
+fabort: int;
+
+escmap := array[] of
+{
+ '\'' => byte '\'',
+ '"' => byte '"',
+ '\\' => byte '\\',
+ 'b' => byte '\b',
+ 'f' => byte FF,
+ 'n' => byte '\n',
+ 'r' => byte '\r',
+ 't' => byte '\t',
+ 'v' => byte FF,
+
+ * => byte 255
+};
+
+#
+# must be sorted
+#
+keywords := array [] of
+{
+ Keywd("abstract", Labstract),
+ Keywd("boolean", Lboolean),
+ Keywd("byte", Lbyte),
+ Keywd("break", Lbreak),
+ Keywd("case", Lcase),
+ Keywd("catch", Lcatch),
+ Keywd("char", Lchar),
+ Keywd("class", Lclass),
+ Keywd("const", Lconst),
+ Keywd("continue", Lcontinue),
+ Keywd("debugger", Ldebugger),
+ Keywd("default", Ldefault),
+ Keywd("delete", Ldelete),
+ Keywd("do", Ldo),
+ Keywd("double", Ldouble),
+ Keywd("else", Lelse),
+ Keywd("enum", Lenum),
+ Keywd("export", Lexport),
+ Keywd("extends ", Lextends),
+ Keywd("final", Lfinal),
+ Keywd("finally", Lfinally),
+ Keywd("float", Lfloat),
+ Keywd("for", Lfor),
+ Keywd("function", Lfunction),
+ Keywd("goto", Lgoto),
+ Keywd("if", Lif),
+ Keywd("implements", Limplements),
+ Keywd("import", Limport),
+ Keywd("in", Lin),
+ Keywd("instanceof", Linstanceof),
+ Keywd("int", Lint),
+ Keywd("interface", Linterface),
+ Keywd("long", Llong),
+ Keywd("native", Lnative),
+ Keywd("new", Lnew),
+ Keywd("package", Lpackage),
+ # Keywd("print", Lprint),
+ Keywd("private", Lprivate),
+ Keywd("protected", Lprotected),
+ Keywd("public", Lpublic),
+ Keywd("return", Lreturn),
+ Keywd("short", Lshort),
+ Keywd("static", Lstatic),
+ Keywd("super", Lsuper),
+ Keywd("switch", Lswitch),
+ Keywd("synchronized", Lsynchronized),
+ Keywd("this", Lthis),
+ Keywd("throw", Lthrow),
+ Keywd("throws", Lthrows),
+ Keywd("transient", Ltransient),
+ Keywd("try", Ltry),
+ Keywd("typeof", Ltypeof),
+ Keywd("var", Lvar),
+ Keywd("void", Lvoid),
+ Keywd("volatile", Lvolatile),
+ Keywd("while", Lwhile),
+ Keywd("with", Lwith),
+};
+
+debug = array[256] of {* => 0};
+
+glbuiltins := array[] of
+{
+ Builtin("eval", "eval", array[] of {"src"}, 1),
+ Builtin("parseInt", "parseInt", array[] of {"string", "radix"}, 2),
+ Builtin("parseFloat", "parseFloat", array[] of {"string"}, 1),
+ Builtin("escape", "escape", array[] of {"string"}, 1),
+ Builtin("unescape", "unescape", array[] of {"string"}, 1),
+ Builtin("isNaN", "isNaN", array[] of {"number"}, 1),
+ Builtin("isFinite", "isFinite", array[] of {"number"}, 1),
+ Builtin("decodeURI", "decodeURI", array[] of {"string"}, 1),
+ Builtin("decodeURIComponent", "decodeURIComponent", array[] of {"string"}, 1),
+ Builtin("encodeURI", "encodeURI", array[] of {"string"}, 1),
+ Builtin("encodeURIComponent", "encodeURIComponent", array[] of {"string"}, 1),
+};
+
+biobj := Builtin("Object", "Object", array[] of {"value"}, 1);
+biobjproto := array[] of
+{
+ Builtin("toString", "Object.prototype.toString", nil, 0),
+ Builtin("toLocaleString", "Object.prototype.toLocaleString", nil, 0),
+ Builtin("valueOf", "Object.prototype.valueOf", nil, 0),
+ Builtin("hasOwnProperty", "Object.prototype.hasOwnProperty", array[] of {"V"}, 1),
+ Builtin("isPrototypeOf", "Object.prototype.isPrototypeOf", array[] of {"V"}, 1),
+ Builtin("propertyisEnumerable", "Object.prototype.propertyisEnumerable", array[] of {"V"}, 1),
+};
+
+bifunc := Builtin("Function", "Function", array[] of {"body"}, 1);
+bifuncproto := array[] of
+{
+ Builtin("toString", "Function.prototype.toString", nil, 0),
+ Builtin("apply", "Function.prototype.apply", array[] of {"this", "array"}, 2),
+ Builtin("call", "Function.prototype.call", array[] of {"this", "arg"}, 1),
+};
+
+bierr := Builtin("Error", "Error", array[] of {"message"}, 1);
+bierrproto := array[] of
+{
+ Builtin("toString", "Error.prototype.toString", nil , 0),
+};
+
+biarray := Builtin("Array", "Array", array[] of {"length"}, 1);
+biarrayproto := array[] of
+{
+ Builtin("toString", "Array.prototype.toString", nil, 0),
+ Builtin("toLocaleString", "Array.prototype.toLocaleString", nil, 0),
+ Builtin("concat", "Array.prototype.concat", array[] of {"item"}, 1),
+ Builtin("join", "Array.prototype.join", array[] of {"separator"}, 1),
+ Builtin("pop", "Array.prototype.pop", nil, 0),
+ Builtin("push", "Array.prototype.push", array[] of {"item"} , 1),
+ Builtin("reverse", "Array.prototype.reverse", nil, 0),
+ Builtin("shift", "Array.prototype.shift", nil, 0),
+ Builtin("slice", "Array.prototype.slice", array[] of {"start", "end"}, 2),
+ Builtin("splice", "Array.prototype.splice", array[] of {"start", "delcnt", "item"}, 2),
+ Builtin("sort", "Array.prototype.sort", array[] of {"comparefunc"}, 1),
+ Builtin("unshift", "Array.prototype.unshift", array[] of {"item"}, 1),
+};
+
+bistr := Builtin("String", "String", array[] of {"value"}, 1);
+bistrproto := array[] of
+{
+ Builtin("toString", "String.prototype.toString", nil, 0),
+ Builtin("valueOf", "String.prototype.valueOf", nil, 0),
+ Builtin("charAt", "String.prototype.charAt", array[] of {"pos"}, 1),
+ Builtin("charCodeAt", "String.prototype.charCodeAt", array[] of {"pos"}, 1),
+ Builtin("concat", "String.prototype.concat", array[] of {"string"}, 1),
+ Builtin("indexOf", "String.prototype.indexOf", array[] of {"string", "pos"}, 2),
+ Builtin("lastIndexOf", "String.prototype.lastIndexOf", array[] of {"string", "pos"}, 2),
+ Builtin("localeCompare", "String.prototype.localeCompare", array[] of {"that"}, 1),
+ Builtin("slice", "String.prototype.slice", array[] of {"start", "end"}, 2),
+ Builtin("split", "String.prototype.split", array[] of {"separator"}, 2),
+ Builtin("substr", "String.prototype.substr", array[] of {"start", "length"}, 2),
+ Builtin("substring", "String.prototype.substring", array[] of {"start", "end"}, 2),
+ Builtin("toLowerCase", "String.prototype.toLowerCase", nil, 0),
+ Builtin("toUpperCase", "String.prototype.toUpperCase", nil, 0),
+ Builtin("toLocaleLowerCase", "String.prototype.toLocaleLowerCase", nil, 0),
+ Builtin("toLocaleUpperCase", "String.prototype.toLocaleUpperCase", nil, 0),
+# JavaScript 1.0
+ Builtin("anchor", "String.prototype.anchor", array[] of {"name"}, 1),
+ Builtin("big", "String.prototype.big", nil, 0),
+ Builtin("blink", "String.prototype.blink", nil, 0),
+ Builtin("bold", "String.prototype.bold", nil, 0),
+ Builtin("fixed", "String.prototype.fixed", nil, 0),
+ Builtin("fontcolor", "String.prototype.fontcolor", array[] of {"color"}, 1),
+ Builtin("fontsize", "String.prototype.fontsize", array[] of {"size"}, 1),
+ Builtin("italics", "String.prototype.italics", nil, 0),
+ Builtin("link", "String.prototype.link", array[] of {"href"}, 1),
+ Builtin("small", "String.prototype.small", nil, 0),
+ Builtin("strike", "String.prototype.strike", nil, 0),
+ Builtin("sub", "String.prototype.sub", nil, 0),
+ Builtin("sup", "String.prototype.sup", nil, 0),
+ Builtin("match", "String.prototype.match", array[] of {"regexp"}, 1),
+ Builtin("replace", "String.prototype.replace", array[] of {"searchval", "replaceval"}, 2),
+ Builtin("search", "String.prototype.search", array[] of {"regexp"}, 1),
+};
+bistrctor := Builtin("fromCharCode", "String.fromCharCode", array[] of {"characters"}, 1);
+
+bibool := Builtin("Boolean", "Boolean", array[] of {"value"}, 1);
+biboolproto := array[] of
+{
+ Builtin("toString", "Boolean.prototype.toString", nil, 0),
+ Builtin("valueOf", "Boolean.prototype.valueOf", nil, 0),
+};
+
+binum := Builtin("Number", "Number", array[] of {"value"}, 1);
+binumproto := array[] of
+{
+ Builtin("toString", "Number.prototype.toString", nil, 0),
+ Builtin("toLocaleString", "Number.prototype.toLocaleString", nil, 0),
+ Builtin("valueOf", "Number.prototype.valueOf", nil, 0),
+ Builtin("toFixed", "Number.prototype.toFixed", array[] of {"digits"}, 1),
+ Builtin("toExponential", "Number.prototype.toExponential", array[] of {"digits"}, 1),
+ Builtin("toPrecision", "Number.prototype.toPrecision", array[] of {"digits"}, 1),
+};
+
+biregexp := Builtin("RegExp", "RegExp", array[] of {"pattern", "flags"}, 2);
+biregexpproto := array[] of
+{
+ Builtin("exec", "RegExp.prototype.exec", array[] of {"string"}, 1),
+ Builtin("test", "RegExp.prototype.test", array[] of {"string"}, 1),
+ Builtin("toString", "RegExp.prototype.toString", nil, 0),
+};
+
+bidate := Builtin("Date", "Date", array[] of {"value"}, 1);
+bidateproto := array[] of
+{
+ Builtin("toString", "Date.prototype.toString", nil, 0),
+ Builtin("toDateString", "Date.prototype.toDateString", nil, 0),
+ Builtin("toTimeString", "Date.prototype.toTimeString", nil, 0),
+ Builtin("toLocaleString", "Date.prototype.toLocalString", nil, 0),
+ Builtin("toLocaleDateString", "Date.prototype.toLocaleDateString", nil, 0),
+ Builtin("toLocaleTimeString", "Date.prototype.toLocaleTimeString", nil, 0),
+ Builtin("valueOf", "Date.prototype.valueOf", nil, 0),
+ Builtin("getTime", "Date.prototype.getTime", nil, 0),
+ Builtin("getYear", "Date.prototype.getYear", nil, 0),
+ Builtin("getFullYear", "Date.prototype.getFullYear", nil, 0),
+ Builtin("getUTCFullYear", "Date.prototype.getUTCFullYear", nil, 0),
+ Builtin("getMonth", "Date.prototype.getMonth", nil, 0),
+ Builtin("getUTCMonth", "Date.prototype.getUTCMonth", nil, 0),
+ Builtin("getDate", "Date.prototype.getDate", nil, 0),
+ Builtin("getUTCDate", "Date.prototype.getUTCDate", nil, 0),
+ Builtin("getDay", "Date.prototype.getDay", nil, 0),
+ Builtin("getUTCDay", "Date.prototype.getUTCDay", nil, 0),
+ Builtin("getHours", "Date.prototype.getHours", nil, 0),
+ Builtin("getUTCHours", "Date.prototype.getUTCHours", nil, 0),
+ Builtin("getMinutes", "Date.prototype.getMinutes", nil, 0),
+ Builtin("getUTCMinutes", "Date.prototype.getUTCMinutes", nil, 0),
+ Builtin("getSeconds", "Date.prototype.getSeconds", nil, 0),
+ Builtin("getUTCSeconds", "Date.prototype.getUTCSeconds", nil, 0),
+ Builtin("getMilliseconds", "Date.prototype.getMilliseconds", nil, 0),
+ Builtin("getUTCMilliseconds", "Date.prototype.getUTCMilliseconds", nil, 0),
+ Builtin("getTimezoneOffset", "Date.prototype.getTimezoneOffset", nil, 0),
+ Builtin("setTime", "Date.prototype.setTime", array[] of {"time"}, 1),
+ Builtin("setMilliseconds", "Date.prototype.setMilliseconds", array[] of {"ms"}, 1),
+ Builtin("setUTCMilliseconds", "Date.prototype.setUTCMilliseconds", array[] of {"ms"}, 1),
+ Builtin("setSeconds", "Date.prototype.setSeconds", array[] of {"sec", "ms"}, 2),
+ Builtin("setUTCSeconds", "Date.prototype.setUTCSeconds", array[] of {"sec", "ms"}, 2),
+ Builtin("setMinutes", "Date.prototype.setMinutes", array[] of {"min", "sec", "ms"}, 3),
+ Builtin("setUTCMinutes", "Date.prototype.setUTCMinutes", array[] of {"min", "sec", "ms"}, 3),
+ Builtin("setHours", "Date.prototype.setHours", array[] of {"hour", "min", "sec", "ms"}, 4),
+ Builtin("setUTCHours", "Date.prototype.setUTCHours", array[] of {"hour", "min", "sec", "ms"}, 4),
+ Builtin("setDate", "Date.prototype.setDate", array[] of {"date"}, 1),
+ Builtin("setUTCDate", "Date.prototype.setUTCDate", array[] of {"date"}, 1),
+ Builtin("setMonth", "Date.prototype.setMonth", array[] of {"mon", "date"}, 2),
+ Builtin("setUTCMonth", "Date.prototype.setUTCMonth", array[] of {"mon", "date"}, 2),
+ Builtin("setFullYear", "Date.prototype.setFullYear", array[] of {"year", "mon", "date"}, 3),
+ Builtin("setUTCFullYear", "Date.prototype.setUTCFullYear", array[] of {"year", "mon", "date"}, 3),
+ Builtin("setYear", "Date.prototype.setYear", array[] of {"year"}, 1),
+ Builtin("toLocaleString", "Date.prototype.toLocaleString", nil, 0),
+ Builtin("toUTCString", "Date.prototype.toUTCString", nil, 0),
+ Builtin("toGMTString", "Date.prototype.toGMTString", nil, 0),
+};
+bidatector := array[] of
+{
+ Builtin("parse", "Date.parse", array[] of {"string"}, 1),
+ Builtin("UTC", "Date.UTC", array[] of {"year", "month", "date", "hours", "minutes", "seconds", "ms"}, 7),
+};
+
+bimath := array[] of
+{
+ Builtin("abs", "Math.abs", array[] of {"x"}, 1),
+ Builtin("acos", "Math.acos", array[] of {"x"}, 1),
+ Builtin("asin", "Math.asin", array[] of {"x"}, 1),
+ Builtin("atan", "Math.atan", array[] of {"x"}, 1),
+ Builtin("atan2", "Math.atan2", array[] of {"y", "x"}, 2),
+ Builtin("ceil", "Math.ceil", array[] of {"x"}, 1),
+ Builtin("cos", "Math.cos", array[] of {"x"}, 1),
+ Builtin("exp", "Math.exp", array[] of {"x"}, 1),
+ Builtin("floor", "Math.floor", array[] of {"x"}, 1),
+ Builtin("log", "Math.log", array[] of {"x"}, 1),
+ Builtin("max", "Math.max", array[] of {"x", "y"}, 2),
+ Builtin("min", "Math.min", array[] of {"x", "y"}, 2),
+ Builtin("pow", "Math.pow", array[] of {"x", "y"}, 2),
+ Builtin("random", "Math.random", nil, 0),
+ Builtin("round", "Math.round", array[] of {"x"}, 1),
+ Builtin("sin", "Math.sin", array[] of {"x"}, 1),
+ Builtin("sqrt", "Math.sqrt", array[] of {"x"}, 1),
+ Builtin("tan", "Math.tan", array[] of {"x"}, 1),
+};
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ math = load Math Math->PATH;
+ if(math == nil)
+ return sys->sprint("can't load module %s: %r", Math->PATH);
+
+ str = load String String->PATH;
+ if(str == nil)
+ return sys->sprint("can't load module %s: %r", String->PATH);
+
+ daytime = load Daytime Daytime->PATH;
+ if(daytime == nil)
+ return sys->sprint("can't load module %s: %r", Daytime->PATH);
+
+ me = load ESHostobj SELF;
+ if(me == nil)
+ return "can't load builtin functions";
+
+ randinit(big sys->millisec());
+ stdout = sys->fildes(1);
+ #
+ # maximum number of syntax errors reported
+ #
+ maxerr = 1;
+
+ undefined = ref Val(TUndef, 0., nil, nil, nil);
+ null = ref Val(TNull, 0., nil, nil, nil);
+ true = ref Val(TBool, 1., nil, nil, nil);
+ false = ref Val(TBool, 0., nil, nil, nil);
+ return "";
+}
+
+mkcall(ex : ref Exec, p: array of string): ref Call
+{
+ return ref Call(p, nil, ex);
+}
+
+mkbiobj(ex: ref Exec, meth: Builtin, proto: ref Obj): ref Obj
+{
+ o := biinst(ex.global, meth, ex.funcproto, me);
+ o.construct = o.call;
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "prototype", objval(proto));
+ valinstant(proto, DontEnum, "constructor", objval(o));
+ return o;
+}
+
+mkexec(go: ref Obj): ref Exec
+{
+ o: ref Obj;
+ if(go == nil)
+ go = mkobj(nil, "global");
+ ex := ref Exec;
+ ex.this = go;
+ ex.scopechain = go :: nil;
+ ex.stack = array[4] of ref Ref;
+ ex.sp = 0;
+ ex.global = go;
+
+ #
+ # builtin object prototypes
+ #
+ ex.objproto = mkobj(nil, "Object");
+ ex.funcproto = mkobj(ex.objproto, "Function");
+ ex.arrayproto = mkobj(ex.objproto, "Array");
+ ex.strproto = mkobj(ex.objproto, "String");
+ ex.numproto = mkobj(ex.objproto, "Number");
+ ex.boolproto = mkobj(ex.objproto, "Boolean");
+ ex.dateproto = mkobj(ex.objproto, "Date");
+ ex.regexpproto = mkobj(ex.objproto, "RegExp");
+ ex.errproto = mkobj(ex.objproto, "Error");
+
+ biminst(ex.objproto, biobjproto, ex.funcproto, me);
+
+ biminst(ex.funcproto, bifuncproto, ex.funcproto, me);
+ ex.funcproto.call = mkcall(ex, nil);
+ ex.funcproto.val = strval("Function.prototype");
+ valinstant(ex.funcproto, DontEnum|DontDelete|ReadOnly, "length", numval(real 0));
+
+ biminst(ex.arrayproto, biarrayproto, ex.funcproto, me);
+ valinstant(ex.arrayproto, DontEnum|DontDelete, "length", numval(real 0));
+
+ biminst(ex.errproto, bierrproto, ex.funcproto, me);
+ ex.errproto.val = strval("");
+ valinstant(ex.errproto, DontEnum|DontDelete, "length", numval(real 0));
+ valinstant(ex.errproto, DontEnum|DontDelete, "name", strval(""));
+ valinstant(ex.errproto, DontEnum|DontDelete, "message", strval("Error"));
+
+ biminst(ex.strproto, bistrproto, ex.funcproto, me);
+ ex.strproto.val = strval("");
+ valinstant(ex.strproto, DontEnum|DontDelete|ReadOnly, "length", numval(real 0));
+
+ biminst(ex.boolproto, biboolproto, ex.funcproto, me);
+ ex.boolproto.val = false;
+
+ biminst(ex.numproto, binumproto, ex.funcproto, me);
+ ex.numproto.val = numval(real +0);
+
+ biminst(ex.regexpproto, biregexpproto, ex.funcproto, me);
+ ex.regexpproto.val = strval("");
+ valinstant(ex.regexpproto, DontEnum|DontDelete|ReadOnly, "length", numval(real 2));
+ valinstant(ex.regexpproto, DontEnum|DontDelete|ReadOnly, "source", strval(""));
+ valinstant(ex.regexpproto, DontEnum|DontDelete|ReadOnly, "global", false);
+ valinstant(ex.regexpproto, DontEnum|DontDelete|ReadOnly, "ignoreCase", false);
+ valinstant(ex.regexpproto, DontEnum|DontDelete|ReadOnly, "multiline", false);
+ valinstant(ex.regexpproto, DontEnum|DontDelete, "lastIndex", numval(real 0));
+
+ biminst(ex.dateproto, bidateproto, ex.funcproto, me);
+ ex.dateproto.val = numval(Math->NaN);
+ valinstant(ex.dateproto, DontEnum|DontDelete|ReadOnly, "length", numval(real 7));
+
+ #
+ # simple builtin functions and values
+ #
+ valinstant(go, DontEnum, "NaN", numval(Math->NaN));
+ valinstant(go, DontEnum, "Infinity", numval(Math->Infinity));
+
+ biminst(go, glbuiltins, ex.funcproto, me);
+
+ #
+ # builtin objects, and cross-link them to their prototypes
+ #
+ mkbiobj(ex, biobj, ex.objproto);
+ mkbiobj(ex, bifunc, ex.funcproto);
+ mkbiobj(ex, biarray, ex.arrayproto);
+ o = mkbiobj(ex, bistr, ex.strproto);
+ biinst(o, bistrctor, ex.funcproto, me);
+ mkbiobj(ex, bibool, ex.boolproto);
+ o = mkbiobj(ex, binum, ex.numproto);
+ mkbiobj(ex, biregexp, ex.regexpproto);
+ mkbiobj(ex, bierr, ex.errproto);
+
+ math->FPcontrol(0, Math->INVAL|Math->ZDIV|Math->OVFL|Math->UNFL|Math->INEX);
+
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "MAX_VALUE", numval(math->nextafter(Math->Infinity, 0.)));
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "MIN_VALUE", numval(math->nextafter(0., 1.)));
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "NaN", numval(Math->NaN));
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "NEGATIVE_INFINITY", numval(-Math->Infinity));
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "POSITIVE_INFINITY", numval(+Math->Infinity));
+ o = mkbiobj(ex, bidate, ex.dateproto);
+ biminst(o, bidatector, ex.funcproto, me);
+
+ #
+ # the math object is a little different
+ #
+ o = mkobj(ex.objproto, "Object");
+ valinstant(go, DontEnum, "Math", objval(o));
+ biminst(o, bimath, ex.funcproto, me);
+
+ #
+ # these are computed so they are consistent with numbers ecma might calculate
+ #
+ mathe := math->exp(1.);
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "E", numval(mathe));
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "LN10", numval(math->log(10.)));
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "LN2", numval(math->log(2.)));
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "LOG2E", numval(1./math->log(2.)));
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "LOG10E", numval(1./math->log(10.)));
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "PI", numval(Math->Pi));
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "SQRT1_2", numval(math->sqrt(1./2.)));
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "SQRT2", numval(math->sqrt(2.)));
+
+ (EvalError, ex.evlerrproto) = mkerr(ex, "EvalError");
+ (RangeError, ex.ranerrproto) = mkerr(ex, "RangeError");
+ (ReferenceError, ex.referrproto) = mkerr(ex, "ReferenceError");
+ (SyntaxError, ex.synerrproto) = mkerr(ex, "SyntaxError");
+ (TypeError, ex.typerrproto) = mkerr(ex, "TypeError");
+ (URIError, ex.urierrproto) = mkerr(ex, "URIError");
+ (InternalError, ex.interrproto) = mkerr(ex, "InternalError");
+
+ return ex;
+}
+
+mkerr(ex: ref Exec, e: string): (ref Obj, ref Obj)
+{
+ errproto := mkobj(ex.objproto, e);
+ biminst(errproto, array[] of { Builtin("toString", e+".prototype.toString", nil, 0) }, ex.funcproto, me);
+ errproto.val = strval("");
+ valinstant(errproto, DontEnum|DontDelete, "length", numval(real 0));
+ valinstant(errproto, DontEnum|DontDelete, "name", strval(e));
+ valinstant(errproto, DontEnum|DontDelete, "message", strval(e));
+ eo := mkbiobj(ex, Builtin(e, e, array[] of {"message"}, 1), errproto);
+ # return (eo, errproto);
+ return (nerr(ex, eo, array[] of {strval(e)}, errproto), errproto);
+}
+
+mkparser(ex: ref Exec, src: string): ref Parser
+{
+ p := ref Parser;
+ p.ex = ex;
+ p.src = src;
+ p.esrc = len src;
+ p.srci = 0;
+ p.errors = 0;
+ p.lineno = 1;
+ p.token = -1;
+ p.token1 = -1;
+ p.lastnl = 0;
+ p.inloop = 0;
+ p.incase = 0;
+ p.infunc = 0;
+ p.notin = 0;
+ p.code = mkcode();
+ return p;
+}
+
+eval(ex: ref Exec, src: string): Completion
+{
+ {
+ p := mkparser(ex, src);
+
+ if(debug['t'])
+ parset := sys->millisec();
+
+ prog(ex, p);
+
+ toterrors += p.errors;
+
+ if(p.errors)
+ runtime(ex, SyntaxError, ex.error);
+ if(debug['p']){
+ s := array of byte pprint(ex, p.code, "");
+ if(len s)
+ sys->write(stdout, s, len s);
+ }
+
+ if(debug['t'])
+ xect := sys->millisec();
+
+ globalinstant(hd ex.scopechain, p.code.vars);
+ c := exec(ex, p.code);
+
+ if(debug['t'])
+ print("parse time %d exec time %d\n", xect - parset, sys->millisec() - xect);
+
+ return c;
+ }exception{
+ "throw" =>
+ return (CThrow, ex.errval, nil);
+ }
+}
+
+#prog : selems
+# ;
+#
+#selems : selem
+# | selems selem
+# ;
+#selem : stmt
+# | fundecl
+# ;
+prog(ex: ref Exec, p: ref Parser)
+{
+ while(look(p) != Leos)
+ if(look(p) == Lfunction)
+ fundecl(ex, p, 0);
+ else
+ stmt(p);
+}
+
+#fundecl : Lfunction Lid '(' zplist ')' '{' stmts '}'
+# ;
+#zplist :
+# | plist
+# ;
+#
+#plist : Lid
+# | plist ',' Lid
+# ;
+fundecl(ex: ref Exec, p: ref Parser, expr: int): ref Obj
+{
+ jp: ref Prop;
+
+ c := p.code;
+ labs := p.labs;
+ p.labs = nil;
+ mustbe(p, Lfunction);
+ if(!expr || look(p) == Lid){
+ mustbe(p, Lid);
+ jp = codevar(p, expr);
+ }
+ p.code = mkcode();
+ mustbe(p, '(');
+ if(look(p) != ')'){
+ for(;;){
+ mustbe(p, Lid);
+ codevar(p, 1);
+ if(look(p) == ')')
+ break;
+ mustbe(p, ',');
+ }
+ }
+ params := p.code.vars;
+ p.code.vars = nil;
+ mustbe(p, ')');
+ mustbe(p, '{');
+ p.infunc++;
+ stmts(p);
+ p.infunc--;
+ mustbe(p, '}');
+
+ #
+ # override any existing value,
+ # as per sec. 10.1.3 Variable instantiation
+ #
+ sparams := array[len params] of string;
+ for(i := 0; i < len sparams; i++)
+ sparams[i] = params[i].name;
+
+ #
+ # construct a function object;
+ # see section 15.3.21
+ o := mkobj(ex.funcproto, "Function");
+ o.call = ref Call(sparams, p.code, ex);
+ o.construct = o.call;
+ if(jp != nil)
+ o.val = strval(jp.name);
+ else
+ o.val = strval("");
+ valinstant(o, DontDelete|DontEnum|ReadOnly, "length", numval(real len sparams));
+ po := nobj(ex, nil, nil);
+ valinstant(o, DontEnum, "prototype", objval(po));
+ valinstant(po, DontEnum, "constructor", objval(o));
+ valinstant(o, DontDelete|DontEnum|ReadOnly, "arguments", null);
+ if(jp != nil)
+ jp.val.val = objval(o);
+
+ if(debug['p']){
+ s := array of byte (funcprint(ex, o) + "\n");
+ sys->write(stdout, s, len s);
+ }
+
+ p.code = c;
+ p.labs = labs;
+ if(expr && jp != nil)
+ popvar(p);
+ return o;
+}
+
+#
+# install a variable for the id just lexed
+#
+codevar(p: ref Parser, forcenew: int): ref Prop
+{
+ name := p.code.strs[p.id];
+ vs := p.code.vars;
+ i : int;
+ if(!forcenew){
+ for(i = 0; i < len vs; i++)
+ if(vs[i].name == name)
+ return vs[i];
+ }else{
+ i = len vs;
+ }
+ vs = array[i+1] of ref Prop;
+ vs[:] = p.code.vars;
+ p.code.vars = vs;
+ vs[i] = ref Prop(0, name, ref RefVal(undefined));
+ return vs[i];
+}
+
+popvar(p: ref Parser)
+{
+ vs := p.code.vars;
+ p.code.vars = vs[0: len vs - 1];
+}
+
+#stmts :
+# | stmts stmt
+# ;
+stmts(p: ref Parser)
+{
+ while((op := look(p)) != '}' && op != Leos)
+ stmt(p);
+}
+
+#stmt : '{' stmts '}'
+# | Lvar varlist ';'
+# | exp ';'
+# | ';'
+# | Lif '(' exp ')' stmt
+# | Lif '(' exp ')' stmt Lelse stmt
+# | Lwhile '(' exp ')' stmt
+# | Ldo stmt Lwhile '(' exp ')'
+# | Lfor '(' zexp-notin ';' zexp ';' zexp ')' stmt
+# | Lfor '(' Lvar varlist-notin ';' zexp ';' zexp ')' stmt
+# | Lfor '(' lhsexp Lin exp ')' stmt
+# | Lfor '(' Lvar Lid [init] Lin exp ')' stmt
+# | Lcontinue ';'
+# | Lbreak ';'
+# | Lreturn zexp ';' # no line term after return
+# | Lwith '(' exp ')' stmt
+# | Lswitch '(' exp ')' '{' caseblk '}'
+# | Lthrow exp ';'
+# | Ltry block Lcatch '(' Lid ')' block
+# | Ltry block finally block
+# | Ltry block Lcatch '(' Lid ')' block finally block
+# ;
+stmt(p: ref Parser)
+{
+ pc: int;
+
+ seenlabs := 0;
+ while(look(p) == Lid && look2(p) == ':'){
+ pushlab(p, p.code.strs[p.id]);
+ emitconst(p, Llabel, p.id);
+ lex(p);
+ lex(p);
+ seenlabs++;
+ }
+
+ op := look(p);
+ if(seenlabs)
+ setkindlab(p, op, seenlabs);
+ case op{
+ ';' =>
+ lexemit(p);
+ '{' =>
+ if(seenlabs == 0){
+ lex(p);
+ stmts(p);
+ }
+ else{
+ lexemit(p);
+ pc = epatch(p);
+ stmts(p);
+ patch(p, pc);
+ }
+ mustbe(p, '}');
+ Lvar =>
+ lexemit(p);
+ pc = epatch(p);
+ varlist(p);
+ semi(p);
+ patch(p, pc);
+ * =>
+ exp(p);
+ semi(p);
+ emit(p, ';');
+ Lif =>
+ lexemit(p);
+ pc = epatch(p);
+ mustbe(p, '(');
+ exp(p);
+ mustbe(p, ')');
+ patch(p, pc);
+ pc = epatch(p);
+ stmt(p);
+ patch(p, pc);
+ pc = epatch(p);
+ if(look(p) == Lelse){
+ lex(p);
+ stmt(p);
+ }
+ patch(p, pc);
+ Lwhile or
+ Lwith =>
+ lexemit(p);
+ pc = epatch(p);
+ mustbe(p, '(');
+ exp(p);
+ mustbe(p, ')');
+ patch(p, pc);
+ if(op == Lwhile)
+ p.inloop++;
+ pc = epatch(p);
+ stmt(p);
+ patch(p, pc);
+ if(op == Lwhile)
+ p.inloop--;
+ Ldo =>
+ p.inloop++;
+ lexemit(p);
+ pc = epatch(p);
+ stmt(p);
+ patch(p, pc);
+ mustbe(p, Lwhile);
+ mustbe(p, '(');
+ pc = epatch(p);
+ exp(p);
+ patch(p, pc);
+ mustbe(p, ')');
+ mustbe(p, ';');
+ p.inloop--;
+ Lfor =>
+ fpc := p.code.npc;
+ lexemit(p);
+ mustbe(p, '(');
+ p.notin++;
+ if(look(p) == Lvar){
+ lex(p);
+ pc = epatch(p);
+ varlist(p);
+ patch(p, pc);
+ p.notin--;
+ if(look(p) == Lin){
+ check1var(p);
+ p.code.ops[fpc] = byte Lforvarin;
+ lex(p);
+ pc = epatch(p);
+ exp(p);
+ patch(p, pc);
+ }else{
+ p.code.ops[fpc] = byte Lforvar;
+ mustbe(p, ';');
+ pc = epatch(p);
+ zexp(p);
+ patch(p, pc);
+ mustbe(p, ';');
+ pc = epatch(p);
+ zexp(p);
+ patch(p, pc);
+ }
+ }else{
+ pc = epatch(p);
+ lhspc := p.code.npc;
+ zexp(p);
+ patch(p, pc);
+ p.notin--;
+ if(look(p) == Lin){
+ p.code.ops[fpc] = byte Lforin;
+ checklhsexp(p, lhspc);
+ lex(p);
+ pc = epatch(p);
+ exp(p);
+ patch(p, pc);
+ }else{
+ mustbe(p, ';');
+ pc = epatch(p);
+ zexp(p);
+ patch(p, pc);
+ mustbe(p, ';');
+ pc = epatch(p);
+ zexp(p);
+ patch(p, pc);
+ }
+ }
+ mustbe(p, ')');
+ p.inloop++;
+ pc = epatch(p);
+ stmt(p);
+ patch(p, pc);
+ p.inloop--;
+ Lcontinue or
+ Lbreak =>
+ lex(p);
+ lab := 0;
+ if(look(p) == Lid){
+ if((lr := findlab(p, p.code.strs[p.id])) == nil)
+ error(p, "missing label");
+ if(op == Lcontinue && !itstmt(lr.k))
+ error(p, "continue label not on iteration statement");
+ if(op == Lbreak)
+ nop := Lbreaklab;
+ else
+ nop = Lcontinuelab;
+ if(!inlocallabs(p, lr, seenlabs)) # otherwise noop
+ emitconst(p, nop, p.id);
+ lex(p);
+ lab = 1;
+ }
+ else
+ emit(p, op);
+ semi(p);
+ if(op == Lbreak && !lab && !p.inloop && !p.incase)
+ error(p, "break not in a do or for or while or case");
+ if(op == Lcontinue && !p.inloop)
+ error(p, "continue not in a do or for or while");
+ Lreturn =>
+ lexemit(p);
+ nextop := look(p);
+ if(nextop != ';' && nextop != '}' && !p.lastnl)
+ exp(p);
+ semi(p);
+ emit(p, ';');
+ if(!p.infunc)
+ error(p, tokname(op)+" not in a function");
+ Lswitch =>
+ lexemit(p);
+ mustbe(p, '(');
+ pc = epatch(p);
+ exp(p);
+ patch(p, pc);
+ mustbe(p, ')');
+ mustbe(p, '{');
+ pc = epatch(p);
+ caseblk(p);
+ patch(p, pc);
+ mustbe(p, '}');
+ Lthrow =>
+ lexemit(p);
+ nextop := look(p);
+ if(!p.lastnl)
+ exp(p);
+ mustbe(p, ';');
+ emit(p, ';');
+ Lprint =>
+ lexemit(p);
+ nextop := look(p);
+ if(!p.lastnl)
+ exp(p);
+ mustbe(p, ';');
+ emit(p, ';');
+ Ltry =>
+ lexemit(p);
+ pc = epatch(p);
+ block(p);
+ patch(p, pc);
+ pc = epatch(p);
+ if(look(p) == Lcatch){
+ lex(p);
+ mustbe(p, '(');
+ mustbe(p, Lid);
+ emitconst(p, Lid, p.id);
+ mustbe(p, ')');
+ block(p);
+ }
+ patch(p, pc);
+ pc = epatch(p);
+ if(look(p) == Lfinally){
+ lex(p);
+ block(p);
+ }
+ patch(p, pc);
+ }
+ while(--seenlabs >= 0)
+ poplab(p);
+}
+
+block(p : ref Parser)
+{
+ mustbe(p, '{');
+ stmts(p);
+ mustbe(p, '}');
+}
+
+caseblk(p : ref Parser)
+{
+ pc, defaultpc, clausepc : int;
+ gotdef := 0;
+ p.incase++;
+
+ defaultpc = epatch(p);
+ while((op := look(p)) != '}' && op != Leos) {
+ if (op != Lcase && op != Ldefault) {
+ err := "expected " + tokname(Lcase)
+ + " or " + tokname(Ldefault)
+ + " found " + tokname(op);
+ error(p, err);
+ }
+ if (op == Ldefault) {
+ if (gotdef)
+ error(p, "default case already defined");
+ gotdef = 1;
+
+ patch(p, defaultpc);
+ }
+ lex(p);
+ clausepc = epatch(p);
+ if (op == Lcase) {
+ pc = epatch(p);
+ exp(p);
+ patch(p, pc);
+ }
+ mustbe(p, ':');
+ casestmts(p);
+ patch(p, clausepc);
+ }
+ clausepc = epatch(p);
+ patch(p, clausepc);
+ if (!gotdef)
+ patch(p, defaultpc);
+ p.incase--;
+}
+
+casestmts(p : ref Parser)
+{
+ while((op := look(p)) != '}' && op != Lcase && op != Ldefault && op != Leos)
+ stmt(p);
+}
+
+semi(p: ref Parser)
+{
+ op := look(p);
+ if(op == ';'){
+ lex(p);
+ return;
+ }
+ if(op == '}' || op == Leos || p.lastnl)
+ return;
+ mustbe(p, ';');
+}
+
+#varlist : vardecl
+# | varlist ',' vardecl
+# ;
+#
+#vardecl : Lid init
+# ;
+#
+#init :
+# | '=' asexp
+# ;
+varlist(p: ref Parser)
+{
+ #
+ # these declaration aren't supposed
+ # to override current definitions
+ #
+ mustbe(p, Lid);
+ codevar(p, 0);
+ emitconst(p, Lid, p.id);
+ if(look(p) == '='){
+ lex(p);
+ asexp(p);
+ emit(p, '=');
+ }
+ if(look(p) != ',')
+ return;
+ emit(p, Lgetval);
+ lex(p);
+ varlist(p);
+ emit(p, ',');
+}
+
+#
+# check that only 1 id is declared in the var list
+#
+check1var(p: ref Parser)
+{
+ if(p.code.ops[p.code.npc-1] == byte ',')
+ error(p, "only one identifier allowed");
+}
+
+#zexp :
+# | exp
+# ;
+zexp(p: ref Parser)
+{
+ op := look(p);
+ if(op == ';' || op == ')')
+ return;
+ exp(p);
+}
+
+#exp : asexp
+# | exp ',' asexp
+# ;
+exp(p: ref Parser)
+{
+ asexp(p);
+ while(look(p) == ','){
+ lex(p);
+ emit(p, Lgetval);
+ asexp(p);
+ emit(p, ',');
+ }
+}
+
+#asexp : condexp
+# | lhsexp asop asexp
+# ;
+#
+#asop : '=' | Lmulas | Ldivas | Lmodas | Laddas | Lsubas
+# | Llshas | Lrshas | Lrshuas | Landas | Lxoras | Loras
+# ;
+asops := array[] of { '=', Lmulas, Ldivas, Lmodas, Laddas, Lsubas,
+ Llshas, Lrshas, Lrshuas, Landas, Lxoras, Loras };
+asbaseops := array[] of { '=', '*', '/', '%', '+', '-',
+ Llsh, Lrsh, Lrshu, '&', '^', '|' };
+asexp(p: ref Parser)
+{
+ lhspc := p.code.npc;
+ condexp(p);
+ i := inops(look(p), asops);
+ if(i >= 0){
+ op := lex(p);
+ checklhsexp(p, lhspc);
+ if(op != '=')
+ emit(p, Lasop);
+ asexp(p);
+ emit(p, asbaseops[i]);
+ if(op != '=')
+ emit(p, Las);
+ }
+}
+
+#condexp : ororexp
+# | ororexp '?' asexp ':' asexp
+# ;
+condexp(p: ref Parser)
+{
+ ororexp(p);
+ if(look(p) == '?'){
+ lexemit(p);
+ pc := epatch(p);
+ asexp(p);
+ mustbe(p, ':');
+ patch(p, pc);
+ pc = epatch(p);
+ asexp(p);
+ patch(p, pc);
+ }
+}
+
+#ororexp : andandexp
+# | ororexp op andandexp
+# ;
+ororexp(p: ref Parser)
+{
+ andandexp(p);
+ while(look(p) == Loror){
+ lexemit(p);
+ pc := epatch(p);
+ andandexp(p);
+ patch(p, pc);
+ }
+}
+
+#andandexp : laexp
+# | andandexp op laexp
+# ;
+andandexp(p: ref Parser)
+{
+ laexp(p, 0);
+ while(look(p) == Landand){
+ lexemit(p);
+ pc := epatch(p);
+ laexp(p, 0);
+ patch(p, pc);
+ }
+}
+
+#laexp : unexp
+# | laexp op laexp
+# ;
+prectab := array[] of
+{
+ array[] of { '|' },
+ array[] of { '^' },
+ array[] of { '&' },
+ array[] of { Leq, Lneq, Lseq, Lsne },
+ array[] of { '<', '>', Lleq, Lgeq, Lin, Linstanceof },
+ array[] of { Llsh, Lrsh, Lrshu },
+ array[] of { '+', '-' },
+ array[] of { '*', '/', '%' },
+};
+laexp(p: ref Parser, prec: int)
+{
+ unexp(p);
+ for(pr := len prectab - 1; pr >= prec; pr--){
+ while(inops(look(p), prectab[pr]) >= 0){
+ emit(p, Lgetval);
+ op := lex(p);
+ laexp(p, pr + 1);
+ emit(p, op);
+ }
+ }
+}
+
+#unexp : postexp
+# | Ldelete unexp
+# | Lvoid unexp
+# | Ltypeof unexp
+# | Linc unexp
+# | Ldec unexp
+# | '+' unexp
+# | '-' unexp
+# | '~' unexp
+# | '!' unexp
+# ;
+preops := array[] of { Ldelete, Lvoid, Ltypeof, Linc, Ldec, '+', '-', '~', '!' };
+unexp(p: ref Parser)
+{
+ if(inops(look(p), preops) >= 0){
+ op := lex(p);
+ unexp(p);
+ if(op == '-')
+ op = Lpresub;
+ else if(op == '+')
+ op = Lpreadd;
+ emit(p, op);
+ return;
+ }
+ postexp(p);
+}
+
+#postexp : lhsexp
+# | lhsexp Linc # no line terminators before Linc or Ldec
+# | lhsexp Ldec
+# ;
+postexp(p: ref Parser)
+{
+ lhsexp(p, 0);
+ if(p.lastnl)
+ return;
+ op := look(p);
+ if(op == Linc || op == Ldec){
+ if(op == Linc)
+ op = Lpostinc;
+ else
+ op = Lpostdec;
+ lex(p);
+ emit(p, op);
+ }
+}
+
+#
+# verify that the last expression is actually a lhsexp
+#
+checklhsexp(p: ref Parser, pc: int)
+{
+
+ case int p.code.ops[p.code.npc-1]{
+ Lthis or
+ ')' or
+ '.' or
+ '[' or
+ Lcall or
+ Lnew or
+ Lnewcall =>
+ return;
+ }
+
+ case int p.code.ops[pc]{
+ Lid or
+ Lnum or
+ Lstr or
+ Lregexp =>
+ npc := pc + 1;
+ (npc, nil) = getconst(p.code.ops, npc);
+ if(npc == p.code.npc)
+ return;
+ }
+
+ (nil, e) := pexp(mkpprint(p.ex, p.code), pc, p.code.npc);
+ error(p, "only left-hand-side expressions allowed: "+e);
+}
+
+#lhsexp : newexp
+# | callexp
+# ;
+#callexp: memexp args
+# | callexp args
+# | callexp '[' exp ']'
+# | callexp '.' Lid
+# ;
+#newexp : memexp
+# | Lnew newexp
+# ;
+#memexp : primexp
+# | Lfunction id(opt) '(' zplist ')' '{' stmts '}'
+# | memexp '[' exp ']'
+# | memexp '.' Lid
+# | Lnew memexp args
+# ;
+lhsexp(p: ref Parser, hasnew: int): int
+{
+ a: int;
+ if(look(p) == Lnew){
+ lex(p);
+ hasnew = lhsexp(p, hasnew + 1);
+ if(hasnew){
+ emit(p, Lnew);
+ hasnew--;
+ }
+ return hasnew;
+ }
+ if(look(p) == Lfunction){
+ o := fundecl(p.ex, p, 1);
+ emitconst(p, Lfunction, fexplook(p, o));
+ return 0;
+ }
+ primexp(p);
+ for(;;){
+ op := look(p);
+ if(op == '('){
+ op = Lcall;
+ if(hasnew){
+ hasnew--;
+ #
+ # stupid different order of evaluation
+ #
+ emit(p, Lgetval);
+ op = Lnewcall;
+ }
+ a = args(p);
+ emitconst(p, op, a);
+ }else if(op == '['){
+ emit(p, Lgetval);
+ lex(p);
+ exp(p);
+ mustbe(p, ']');
+ emit(p, '[');
+ }else if(op == '.'){
+ lex(p);
+ mustbe(p, Lid);
+ emitconst(p, Lid, p.id);
+ emit(p, '.');
+ }else
+ return hasnew;
+ }
+}
+
+#primexp : Lthis
+# | Lid
+# | Lnum
+# | Lstr
+# | Lregexp
+# | '(' exp ')'
+# | '[' array initializer ']'
+# | '{' propandval '}'
+# ;
+primexp(p: ref Parser)
+{
+ case t := lex(p){
+ Lthis =>
+ emit(p, t);
+ Lid or
+ Lnum or
+ Lstr =>
+ emitconst(p, t, p.id);
+ '/' =>
+ lexregexp(p);
+ emitconst(p, Lregexp, p.id);
+ '(' =>
+ emit(p, '(');
+ exp(p);
+ mustbe(p, ')');
+ emit(p, ')');
+ '[' =>
+ a := 0;
+ if(look(p) == ']')
+ lex(p);
+ else{
+ for(;;){
+ if(look(p) == ']'){
+ lex(p);
+ break;
+ }
+ if(look(p) == ',')
+ emit(p, Lnoval);
+ else
+ asexp(p);
+ emit(p, Lgetval);
+ a++;
+ if(look(p) == ']'){
+ lex(p);
+ break;
+ }
+ mustbe(p, ',');
+ }
+ }
+ emitconst(p, Larrinit, a);
+ '{' =>
+ a := 0;
+ if(look(p) == '}')
+ lex(p);
+ else{
+ for(;;){
+ case(tt := lex(p)){
+ Lid =>
+ emitconst(p, Lstr, p.id);
+ Lnum or
+ Lstr =>
+ emitconst(p, tt, p.id);
+ * =>
+ error(p, "expected identifier, number or string");
+ }
+ mustbe(p, ':');
+ asexp(p);
+ emit(p, Lgetval);
+ a++;
+ if(look(p) == '}'){
+ lex(p);
+ break;
+ }
+ mustbe(p, ',');
+ }
+ }
+ emitconst(p, Lobjinit, a);
+ * =>
+ error(p, "expected an expression");
+ }
+}
+
+#args : '(' ')'
+# | '(' arglist ')'
+# ;
+#
+#arglist : asexp
+# | arglist ',' asexp
+# ;
+args(p: ref Parser): int
+{
+ mustbe(p, '(');
+ if(look(p) == ')'){
+ lex(p);
+ return 0;
+ }
+ a := 0;
+ for(;;){
+ asexp(p);
+ emit(p, Lgetval);
+ a++;
+ if(look(p) == ')'){
+ lex(p);
+ return a;
+ }
+ mustbe(p, ',');
+ }
+}
+
+inops(tok: int, ops: array of int): int
+{
+ for(i := 0; i < len ops; i++)
+ if(tok == ops[i])
+ return i;
+ return -1;
+}
+
+mustbe(p: ref Parser, t: int)
+{
+ tt := lex(p);
+ if(tt != t)
+ error(p, "expected "+tokname(t)+" found "+tokname(tt));
+}
+
+toknames := array[] of
+{
+ Leos-Lbase => "end of input",
+ Landas-Lbase => "&=",
+ Loras-Lbase => "|=",
+ Lxoras-Lbase => "^=",
+ Llshas-Lbase => "<<=",
+ Lrshas-Lbase => ">>=",
+ Lrshuas-Lbase => ">>>=",
+ Laddas-Lbase => "+=",
+ Lsubas-Lbase => "-=",
+ Lmulas-Lbase => "*=",
+ Ldivas-Lbase => "/=",
+ Lmodas-Lbase => "%=",
+ Loror-Lbase => "||",
+ Landand-Lbase => "&&",
+ Leq-Lbase => "==",
+ Lneq-Lbase => "!=",
+ Lleq-Lbase => "<=",
+ Lgeq-Lbase => ">=",
+ Llsh-Lbase => "<<",
+ Lrsh-Lbase => ">>",
+ Lrshu-Lbase => ">>>",
+ Linc-Lbase => "++",
+ Ldec-Lbase => "--",
+ Lnum-Lbase => "a number",
+ Lid-Lbase => "an identifier",
+ Lstr-Lbase => "a string",
+ Lthis-Lbase => "this",
+ Ltypeof-Lbase => "typeof",
+ Ldelete-Lbase => "delete",
+ Lvoid-Lbase => "void",
+ Lwhile-Lbase => "while",
+ Lfor-Lbase => "for",
+ Lbreak-Lbase => "break",
+ Lcontinue-Lbase => "continue",
+ Lwith-Lbase => "with",
+ Lreturn-Lbase => "return",
+ Lfunction-Lbase => "function",
+ Lvar-Lbase => "var",
+ Lif-Lbase => "if",
+ Lelse-Lbase => "else",
+ Lin-Lbase => "in",
+ Lnew-Lbase => "new",
+
+ Lpreadd-Lbase => "+",
+ Lpresub-Lbase => "-",
+ Lpostinc-Lbase => "++",
+ Lpostdec-Lbase => "--",
+ Lcall-Lbase => "call",
+ Lnewcall-Lbase => "newcall",
+ Lgetval-Lbase => "[[GetValue]]",
+ Las-Lbase => "[[as]]",
+ Lasop-Lbase => "[[asop]]",
+ Lforin-Lbase => "forin",
+ Lforvar-Lbase => "forvar",
+ Lforvarin-Lbase => "forvarin",
+ Lcase-Lbase => "case",
+ Labstract-Lbase => "abstract",
+ Lboolean-Lbase => "boolean",
+ Lbyte-Lbase => "byte",
+ Lcatch-Lbase => "catch",
+ Lchar-Lbase => "char",
+ Lclass-Lbase => "class",
+ Lconst-Lbase => "const",
+ Ldebugger-Lbase => "debugger",
+ Ldefault-Lbase => "default",
+ Ldo-Lbase => "do",
+ Ldouble-Lbase => "double",
+ Lenum-Lbase => "enum",
+ Lexport-Lbase => "export",
+ Lextends-Lbase => "extends",
+ Lfinal-Lbase => "final",
+ Lfinally-Lbase => "finally",
+ Lfloat-Lbase => "float",
+ Lgoto-Lbase => "goto",
+ Limplements-Lbase => "implements",
+ Limport-Lbase => "import",
+ Linstanceof-Lbase => "instanceof",
+ Lint-Lbase => "int",
+ Linterface-Lbase => "interface",
+ Llong-Lbase => "long",
+ Lnative-Lbase => "native",
+ Lpackage-Lbase => "package",
+ Lprint-Lbase => "print",
+ Lprivate-Lbase => "private",
+ Lprotected-Lbase => "protected",
+ Lpublic-Lbase => "public",
+ Lregexp-Lbase => "regexp",
+ Lseq-Lbase => "===",
+ Lsne-Lbase => "!==",
+ Lshort-Lbase => "short",
+ Lstatic-Lbase => "static",
+ Lsuper-Lbase => "super",
+ Lswitch-Lbase => "switch",
+ Lsynchronized-Lbase => "synchronized",
+ Lthrow-Lbase => "throw",
+ Lthrows-Lbase => "throws",
+ Ltransient-Lbase => "transient",
+ Ltry-Lbase=> "try",
+ Lvolatile-Lbase => "volatile",
+ Larrinit-Lbase => "arrayinit",
+ Lobjinit-Lbase => "objinit",
+ Lnoval-Lbase => "novalue",
+ Llabel-Lbase => "label",
+ Lbreaklab-Lbase => "break",
+ Lcontinuelab-Lbase => "continue",
+};
+
+tokname(t: int): string
+{
+ if(t < Lbase){
+ s := "";
+ s[0] = t;
+ return s;
+ }
+ if(t-Lbase >= len toknames || toknames[t-Lbase] == "")
+ return sprint("<%d>", t);
+ return toknames[t-Lbase];
+}
+
+lexemit(p: ref Parser)
+{
+ emit(p, lex(p));
+ if(debug['s'])
+ sys->print("%d: %s\n", p.code.npc-1, tokname(int p.code.ops[p.code.npc-1]));
+}
+
+emit(p: ref Parser, t: int)
+{
+ if(t > 255)
+ fatal(p.ex, sprint("emit too big: %d\n", t));
+ if(p.code.npc >= len p.code.ops){
+ ops := array[2 * len p.code.ops] of byte;
+ ops[:] = p.code.ops;
+ p.code.ops = ops;
+ }
+ p.code.ops[p.code.npc++] = byte t;
+}
+
+emitconst(p: ref Parser, op, c: int)
+{
+ emit(p, op);
+ if(c < 0)
+ fatal(p.ex, "emit negative constant");
+ if(c >= 255){
+ if(c >= 65536)
+ fatal(p.ex, "constant too large");
+ emit(p, 255);
+ emit(p, c & 16rff);
+ c >>= 8;
+ }
+ emit(p, c);
+}
+
+epatch(p: ref Parser): int
+{
+ pc := p.code.npc;
+ emit(p, 0);
+ emit(p, 0);
+ return pc;
+}
+
+patch(p: ref Parser, pc: int)
+{
+ val := p.code.npc - pc;
+ if(val >= 65536)
+ fatal(p.ex, "patch constant too large");
+ p.code.ops[pc] = byte val;
+ p.code.ops[pc+1] = byte(val >> 8);
+}
+
+getconst(ops: array of byte, pc: int): (int, int)
+{
+ c := int ops[pc++];
+ if(c == 255){
+ c = int ops[pc] + (int ops[pc+1] << 8);
+ pc += 2;
+ }
+ return (pc, c);
+}
+
+getjmp(ops: array of byte, pc: int): (int, int)
+{
+ c := int ops[pc] + (int ops[pc+1] << 8) + pc;
+ pc += 2;
+ return (pc, c);
+}
+
+mkcode(): ref Code
+{
+ return ref Code(array[16] of byte, 0, nil, nil, nil, nil, nil);
+}
+
+look(p: ref Parser): int
+{
+ if(p.token == -1)
+ p.token = lex(p);
+ if(p.notin && p.token == Lin)
+ return ~Lin;
+ return p.token;
+}
+
+look2(p: ref Parser): int
+{
+ look(p);
+ if(p.token1 == -1){
+ # fool lex()
+ t := p.token;
+ p.token = -1;
+ p.token1 = lex(p);
+ p.token = t;
+ }
+ return p.token1;
+}
+
+lex(p: ref Parser): int
+{
+ t := lex0(p);
+ if(0)
+ sys->print("tok=%d %s\n", t, tokname(t));
+ return t;
+}
+
+lex0(p: ref Parser): int
+{
+ t := p.token;
+ if(t != -1){
+ p.token = p.token1;
+ p.token1 = -1;
+ return t;
+ }
+
+ p.lastnl = 0;
+ while(p.srci < p.esrc){
+ c := p.src[p.srci++];
+ case c{
+ '\r' or LS or PS =>
+ p.lastnl = 1;
+ '\n' =>
+ p.lineno++;
+ p.lastnl = 1;
+ ' ' or
+ '\t' or
+ '\v' or
+ FF or # form feed
+ '\u00a0' => # no-break space
+ ;
+ '"' or
+ '\''=>
+ return lexstring(p, c);
+ '(' or
+ ')' or
+ '[' or
+ ']' or
+ '{' or
+ '}' or
+ ',' or
+ ';' or
+ '~' or
+ '?' or
+ ':' =>
+ return c;
+ '.' =>
+ if(p.srci < p.esrc && (map[p.src[p.srci]] & Mdigit) != byte 0){
+ p.srci--;
+ return lexnum(p);
+ }
+ return '.';
+ '^' =>
+ if(p.srci < p.esrc && p.src[p.srci] == '='){
+ p.srci++;
+ return Lxoras;
+ }
+ return '^';
+ '*' =>
+ if(p.srci < p.esrc && p.src[p.srci] == '='){
+ p.srci++;
+ return Lmulas;
+ }
+ return '*';
+ '%' =>
+ if(p.srci < p.esrc && p.src[p.srci] == '='){
+ p.srci++;
+ return Lmodas;
+ }
+ return '%';
+ '=' =>
+ if(p.srci < p.esrc && p.src[p.srci] == '='){
+ p.srci++;
+ if(p.srci < p.esrc && p.src[p.srci] == '='){
+ p.srci++;
+ return Lseq;
+ }
+ return Leq;
+ }
+ return '=';
+ '!' =>
+ if(p.srci < p.esrc && p.src[p.srci] == '='){
+ p.srci++;
+ if(p.srci < p.esrc && p.src[p.srci] == '='){
+ p.srci++;
+ return Lsne;
+ }
+ return Lneq;
+ }
+ return '!';
+ '+' =>
+ if(p.srci < p.esrc){
+ c = p.src[p.srci];
+ if(c == '='){
+ p.srci++;
+ return Laddas;
+ }
+ if(c == '+'){
+ p.srci++;
+ return Linc;
+ }
+ }
+ return '+';
+ '-' =>
+ if(p.srci < p.esrc){
+ c = p.src[p.srci];
+ if(c == '='){
+ p.srci++;
+ return Lsubas;
+ }
+ if(c == '-'){
+ p.srci++;
+ return Ldec;
+ }
+ }
+ return '-';
+ '|' =>
+ if(p.srci < p.esrc){
+ c = p.src[p.srci];
+ if(c == '='){
+ p.srci++;
+ return Loras;
+ }
+ if(c == '|'){
+ p.srci++;
+ return Loror;
+ }
+ }
+ return '|';
+ '&' =>
+ if(p.srci < p.esrc){
+ c = p.src[p.srci];
+ if(c == '='){
+ p.srci++;
+ return Landas;
+ }
+ if(c == '&'){
+ p.srci++;
+ return Landand;
+ }
+ }
+ return '&';
+ '/' =>
+ if(p.srci < p.esrc){
+ c = p.src[p.srci];
+ if(c == '='){
+ p.srci++;
+ return Ldivas;
+ }
+ if(c == '/'){
+ p.srci++;
+ if(lexcom(p) < 0)
+ return Leos;
+ break;
+ }
+ if(c == '*'){
+ p.srci++;
+ if(lexmcom(p) < 0)
+ return Leos;
+ break;
+ }
+ }
+ return '/';
+ '>' =>
+ if(p.srci < p.esrc){
+ c = p.src[p.srci];
+ if(c == '='){
+ p.srci++;
+ return Lgeq;
+ }
+ if(c == '>'){
+ p.srci++;
+ if (p.srci < p.esrc) {
+ c = p.src[p.srci];
+ if(c == '='){
+ p.srci++;
+ return Lrshas;
+ }
+ if(c == '>'){
+ p.srci++;
+ c = p.src[p.srci];
+ if(c == '='){
+ p.srci++;
+ return Lrshuas;
+ }
+ return Lrshu;
+ }
+ }
+ return Lrsh;
+ }
+ }
+ return '>';
+ '<' =>
+ if(p.srci < p.esrc){
+ c = p.src[p.srci];
+ case c {
+ '=' =>
+ p.srci++;
+ return Lleq;
+ '<' =>
+ p.srci++;
+ if (p.srci < p.esrc) {
+ c = p.src[p.srci];
+ if(c == '='){
+ p.srci++;
+ return Llshas;
+ }
+ }
+ return Llsh;
+ '!' =>
+ # HTML comment - consume to end of line or end of comment
+ # No way of having the HTML parser do this
+ if (p.srci+2 >= p.esrc)
+ return Leos;
+
+ if (p.src[p.srci+1] != '-' || p.src[p.srci+2] != '-')
+ # don't treat as a comment, let the parser report syntax error
+ return '<';
+ # consume "!--"
+ p.srci += 3;
+ if(lexhtmlcom(p) < 0)
+ return Leos;
+ continue;
+ }
+ }
+ return '<';
+ '0' to '9' =>
+ p.srci--;
+ return lexnum(p);
+ '\\' =>
+ return lexid(p);
+ * =>
+ if((map[c] & Malpha) != byte 0)
+ return lexid(p);
+ s := "";
+ s[0] = c;
+ error(p, "unknown character '"+s+"'");
+ }
+ }
+ return Leos;
+}
+
+#
+# single line comment
+#
+lexcom(p: ref Parser): int
+{
+ while(p.srci < p.esrc){
+ c := p.src[p.srci];
+ if(islt(c))
+ return 0;
+ p.srci++;
+ }
+ return -1;
+}
+
+#
+# multi-line comment
+#
+lexmcom(p: ref Parser): int
+{
+ star := 0;
+ while(p.srci < p.esrc){
+ c := p.src[p.srci++];
+ if(c == '/' && star)
+ return 0;
+ star = c == '*';
+ }
+ return -1;
+}
+
+# HTML comment
+# consume to end of line or end of comment (-->), whichever we see first.
+# [not strict HTML comment semantics because of
+# the way in which HTML comments are used in JavaScript]
+#
+lexhtmlcom(p: ref Parser): int
+{
+ nmin := 0;
+ for (;p.srci < p.esrc;) {
+ c := p.src[p.srci++];
+ if (c == '-') {
+ nmin++;
+ continue;
+ }
+ if (c == '>' && nmin >= 2)
+ return 0;
+ if (islt(c))
+ return 0;
+ nmin = 0;
+ }
+ return -1;
+}
+
+lexid(p: ref Parser): int
+{
+ p.srci--;
+ id := "";
+ ch := "Z";
+ while(p.srci < p.esrc){
+ c := p.src[p.srci];
+ if(c == '\\'){
+ p.srci++;
+ c = uniescchar(p);
+ if(c == -1)
+ error(p, "malformed unicode escape sequence in identifier");
+ else
+ ;
+ }
+ else{
+ if(c >= 0 && c < 256 && (map[c] & (Malpha|Mdigit)) == byte 0)
+ # if(c >= 256 || (map[c] & (Malpha|Mdigit)) == byte 0)
+ break;
+ p.srci++;
+ }
+ ch[0] = c;
+ id += ch;
+ }
+ # id := p.src[srci:p.srci];
+ t := keywdlook(id);
+ if(t != -1)
+ return t;
+ p.id = strlook(p, id);
+ return Lid;
+}
+
+ParseReal, ParseHex, ParseOct, ParseTrim, ParseEmpty: con 1 << iota;
+
+#
+# parse a numeric identifier
+# format [0-9]+(r[0-9A-Za-z]+)?
+# or ([0-9]+(\.[0-9]*)?|\.[0-9]+)([eE][+-]?[0-9]+)?
+#
+lexnum(p: ref Parser): int
+{
+ v: real;
+ (p.srci, v) = parsenum(p.ex, p.src, p.srci, ParseReal|ParseHex|ParseOct);
+ p.id = numlook(p, v);
+ return Lnum;
+}
+
+parsenum(ex: ref Exec, s: string, si, how: int): (int, real)
+{
+ Inf: con "Infinity";
+
+ osi := si;
+ lens := len s;
+ if (how & ParseTrim) {
+ while(si < lens && iswhite(s[si]))
+ si++;
+ }
+ if(si >= lens) {
+ if (how & ParseEmpty)
+ return (si, 0.);
+ return (osi, Math->NaN);
+ }
+ c := s[si];
+ neg := 0;
+ if(c == '+')
+ si++;
+ else if(c == '-'){
+ si++;
+ neg = 1;
+ }
+ v := 0.;
+ if((how & ParseReal) && si + len Inf <= lens && s[si:si+len Inf] == Inf){
+ si += len Inf;
+ v = Math->Infinity;
+ }else{
+ nsi := si;
+ (si, v) = parsenumval(ex, s, si, how);
+ if(si == nsi)
+ return (osi, Math->NaN);
+ }
+ if(neg)
+ v = -v;
+ if (how & ParseTrim) {
+ while(si < lens && iswhite(s[si]))
+ si++;
+ }
+ return (si, v);
+}
+
+#
+# parse a bunch of difference subsets of numbers
+#
+parsenumval(ex: ref Exec, s: string, si, how: int): (int, real)
+{
+ Int, Oct, Hex, FracSeen, Frac, ExpSeen, ExpSignSeen, Exp: con iota;
+
+ lens := len s;
+ if(si >= lens)
+ return (si, Math->NaN);
+ ssi := si;
+ c := s[si];
+ state := Int;
+ if(c == '.' && (how & ParseReal)){
+ state = FracSeen;
+ si++;
+ }else if(c == '0'){
+ if(si+1 >= lens)
+ return (si+1, 0.);
+ c = s[si+1];
+ if(c == '.' && (how & ParseReal)){
+ state = Frac;
+ si += 2;
+ }else if((c == 'x' || c == 'X') && (how & ParseHex)){
+ state = Hex;
+ ssi += 2;
+ si += 2;
+ }else if(how & ParseOct)
+ state = Oct;
+ }
+
+done: while(si < lens){
+ c = s[si];
+ case state{
+ Int =>
+ if((map[c] & Mdigit) != byte 0)
+ break;
+ if((map[c] & Mexp) != byte 0 && (how & ParseReal))
+ state = ExpSeen;
+ else if(c == '.' && (how & ParseReal))
+ state = Frac;
+ else
+ break done;
+ Hex =>
+ if((map[c] & Mhex) == byte 0)
+ break done;
+ Oct =>
+ if((map[c] & Moct) == byte 0)
+ break done;
+ FracSeen or
+ Frac =>
+ if((map[c] & Mdigit) != byte 0)
+ state = Frac;
+ else if((map[c] & Mexp) != byte 0)
+ state = ExpSeen;
+ else
+ break done;
+ ExpSeen =>
+ if((map[c] & Msign) != byte 0)
+ state = ExpSignSeen;
+ else if((map[c] & Mdigit) != byte 0)
+ state = Exp;
+ else
+ break done;
+ ExpSignSeen or
+ Exp =>
+ if((map[c] & Mdigit) != byte 0)
+ state = Exp;
+ else
+ break done;
+ }
+ si++;
+ }
+
+ esi := si;
+ if(state == FracSeen)
+ return (si - 1, Math->NaN);
+ if(state == ExpSeen){
+ state = Frac;
+ esi--;
+ }else if(state == ExpSignSeen){
+ state = Frac;
+ esi -= 2;
+ }
+ buf := s[ssi:esi];
+ v: real;
+ case state{
+ * =>
+ # only if the above lexing code is wrong
+ fatal(ex, "bad parse of numerical constant '"+buf+"'");
+ v = 0.;
+ Oct =>
+ v = strtoi(ex, buf, 8);
+ Hex =>
+ v = strtoi(ex, buf, 16);
+ Int or
+ Frac or
+ Exp =>
+ v = real buf;
+ }
+ return (si, v);
+}
+
+#
+# called only from parsenumval
+# can never fatal error if that routine works correctly
+#
+strtoi(ex: ref Exec, t: string, base: int): real
+{
+ if(len t == 0)
+ return Math->NaN;
+
+ v := 0.;
+ for(i := 0; i < len t; i++){
+ c := t[i];
+ if(c >= '0' && c <= '9')
+ c -= '0';
+ else if(c >= 'a' && c <= 'z')
+ c -= 'a' - 10;
+ else
+ c -= 'A' - 10;
+ if(c >= base){
+ fatal(ex, "digit '"+t[i:i+1]+"' is not radix "+string base);
+ return Math->NaN;
+ }
+ v = v * real base + real c;
+ }
+ return v;
+}
+
+lexstring(p: ref Parser, end: int): int
+{
+ s := "";
+ i := 0;
+ for(;;){
+ if(p.srci >= p.esrc){
+ error(p, "end of file in string constant");
+ break;
+ }
+ c := p.src[p.srci];
+ if(islt(c)){
+ error(p, "newline in string constant");
+ break;
+ }
+ p.srci++;
+ if(c == end)
+ break;
+ if(c == '\\'){
+ c = escchar(p);
+ if(c == Leos)
+ continue;
+ }
+ s[i++] = c;
+ }
+ p.id = strlook(p, s);
+ return Lstr;
+}
+
+lexregexp(p: ref Parser): int
+{
+ c := esc := 0;
+ s := "";
+ i := 0;
+ s[i++] = '/';
+ for(;;){
+ if(p.srci >= p.esrc){
+ error(p, "end of file in regexp constant");
+ break;
+ }
+ c = p.src[p.srci];
+ if(islt(c)){
+ error(p, "newline in regexp constant");
+ break;
+ }
+ p.srci++;
+ s[i++] = c;
+ if(!esc && c == '/')
+ break;
+ esc = !esc && c == '\\';
+ }
+ if(esc)
+ error(p, "missing escaped character");
+ if(i == 2)
+ error(p, "missing regexp");
+ while(p.srci < p.esrc){
+ c = p.src[p.srci];
+ if(c >= 256 || (map[c] & (Malpha|Mdigit)) == byte 0)
+ break;
+ p.srci++;
+ s[i++] = c;
+ }
+ p.id = strlook(p, s);
+ return Lregexp;
+}
+
+uniescchar(p: ref Parser): int
+{
+ if(p.srci >= p.esrc)
+ return -1;
+ c := p.src[p.srci++];
+ if(c != 'u')
+ return -1;
+ v := 0;
+ for(i := 0; i < 4; i++){
+ if(p.srci >= p.esrc || (map[c = p.src[p.srci]] & (Mdigit|Mhex)) == byte 0)
+ return -1;
+ p.srci++;
+ if((map[c] & Mdigit) != byte 0)
+ c -= '0';
+ else if((map[c] & Mlower) != byte 0)
+ c = c - 'a' + 10;
+ else if((map[c] & Mupper) != byte 0)
+ c = c - 'A' + 10;
+ v = v * 16 + c;
+ }
+ return v;
+}
+
+escchar(p: ref Parser): int
+{
+ v: int;
+ if(p.srci >= p.esrc)
+ return Leos;
+ c := p.src[p.srci++];
+ if(c == 'u' || c == 'x'){
+ d := 2;
+ if(c == 'u')
+ d = 4;
+ v = 0;
+ for(i := 0; i < d; i++){
+ if(p.srci >= p.esrc || (map[c = p.src[p.srci]] & (Mdigit|Mhex)) == byte 0){
+ error(p, "malformed hex escape sequence");
+ break;
+ }
+ p.srci++;
+ if((map[c] & Mdigit) != byte 0)
+ c -= '0';
+ else if((map[c] & Mlower) != byte 0)
+ c = c - 'a' + 10;
+ else if((map[c] & Mupper) != byte 0)
+ c = c - 'A' + 10;
+ v = v * 16 + c;
+ }
+ return v;
+ }
+ if(c >= '0' && c <= '7'){
+ v = c - '0';
+ if(p.srci < p.esrc && (c = p.src[p.srci]) >= '0' && c <= '7'){
+ p.srci++;
+ v = v * 8 + c - '0';
+ if(v <= 8r37 && p.srci < p.esrc && (c = p.src[p.srci]) >= '0' && c <= '7'){
+ p.srci++;
+ v = v * 8 + c - '0';
+ }
+ }
+ return v;
+ }
+
+ if(c < len escmap && (v = int escmap[c]) < 255)
+ return v;
+ return c;
+}
+
+keywdlook(s: string): int
+{
+ m: int;
+ l := 1;
+ r := len keywords - 1;
+ while(l <= r){
+ m = (r + l) >> 1;
+ if(keywords[m].name <= s)
+ l = m + 1;
+ else
+ r = m - 1;
+ }
+ m = l - 1;
+ if(keywords[m].name == s)
+ return keywords[m].token;
+ return -1;
+}
+
+strlook(p: ref Parser, s: string): int
+{
+ for(i := 0; i < len p.code.strs; i++)
+ if(p.code.strs[i] == s)
+ return i;
+ strs := array[i + 1] of string;
+ strs[:] = p.code.strs;
+ strs[i] = s;
+ p.code.strs = strs;
+ return i;
+}
+
+numlook(p: ref Parser, r: real): int
+{
+ for(i := 0; i < len p.code.nums; i++)
+ if(p.code.nums[i] == r)
+ return i;
+ nums := array[i + 1] of real;
+ nums[:] = p.code.nums;
+ nums[i] = r;
+ p.code.nums = nums;
+ return i;
+}
+
+fexplook(p: ref Parser, o: ref Obj): int
+{
+ i := len p.code.fexps;
+ fexps := array[i+1] of ref Obj;
+ fexps[:] = p.code.fexps;
+ fexps[i] = o;
+ p.code.fexps = fexps;
+ return i;
+}
+
+iswhite(c: int): int
+{
+ if(islt(c))
+ return 1;
+ case c {
+ ' ' or
+ '\t' or
+ '\v' or
+ FF or # form feed
+ '\u00a0' => # no-break space
+ return 1;
+ }
+ return 0;
+}
+
+error(p: ref Parser, s: string)
+{
+ p.errors++;
+ p.ex.error += sys->sprint("%d: syntax error: %s\n", p.lineno, s);
+ if(p.errors >= maxerr)
+ runtime(p.ex, SyntaxError, p.ex.error);
+}
+
+fatal(ex: ref Exec, msg: string)
+{
+ if(debug['f']){
+ print("fatal ecmascript error: %s\n", msg);
+ if(""[5] == -1); # abort
+ }
+ runtime(ex, InternalError, "unrecoverable internal ecmascript error: "+ msg);
+}
+
+# scanb(p: ref Parser, s: string): int
+# {
+# n := len s;
+# for(i := p.srci; i+n > p.esrc || p.src[i: i+n] != s; --i)
+# ;
+# return i;
+# }
+
+setkindlab(p: ref Parser, op: int, n: int)
+{
+ l := p.labs;
+ for(i := 0; i < n; i++){
+ (hd l).k = op;
+ l = tl l;
+ }
+}
+
+inlocallabs(p: ref Parser, lr: ref labrec, n: int): int
+{
+ l := p.labs;
+ for(i := 0; i < n; i++){
+ if(hd l == lr)
+ return 1;
+ l = tl l;
+ }
+ return 0;
+}
+
+findlab(p: ref Parser, s: string): ref labrec
+{
+ for(l := p.labs; l != nil; l = tl l)
+ if((hd l).s == s)
+ return hd l;
+ return nil;
+}
+
+pushlab(p: ref Parser, s: string)
+{
+ if(findlab(p, s) != nil)
+ error(p, "duplicate labels");
+ p.labs = ref labrec(s, 0) :: p.labs;
+}
+
+poplab(p: ref Parser)
+{
+ p.labs = tl p.labs;
+}
+
+itstmt(k: int): int
+{
+ return k == Lwhile || k == Ldo || k == Lfor;
+}
diff --git a/appl/lib/ecmascript/exec.b b/appl/lib/ecmascript/exec.b
new file mode 100644
index 00000000..d182df82
--- /dev/null
+++ b/appl/lib/ecmascript/exec.b
@@ -0,0 +1,863 @@
+exec(ex: ref Exec, code: ref Code): Completion
+{
+ ssp := ex.sp;
+
+ r := estmt(ex, code, 0, code.npc);
+
+ if(r.kind == CThrow)
+ ex.sp = ssp;
+
+ if(ssp != ex.sp)
+ runtime(ex, InternalError, "internal error: exec stack not balanced");
+
+ if(r.lab != nil)
+ runtime(ex, InternalError, "internal error: label out of stack");
+ return r;
+}
+
+estmt(ex: ref Exec, code: ref Code, pc, epc: int): Completion
+{
+ e: ref Ref;
+ ev: ref Val;
+ k, apc, pc2, apc2, pc3, apc3, c: int;
+ lab: string;
+ labs: list of string;
+
+ osp := ex.sp;
+
+{
+ v : ref Val = nil;
+ k1 := CNormal;
+ while(pc < epc){
+ v1 : ref Val = nil;
+
+ labs = nil;
+ op := int code.ops[pc++];
+ while(op == Llabel){
+ (pc, c) = getconst(code.ops, pc);
+ labs = code.strs[c] :: labs;
+ op = int code.ops[pc++];
+ }
+ if(debug['e'] > 1)
+ print("estmt(pc %d, sp %d) %s\n", pc-1, ex.sp, tokname(op));
+ case op {
+ Lbreak =>
+ return (CBreak, v, nil);
+ Lcontinue =>
+ return (CContinue, v, nil);
+ Lbreaklab =>
+ (pc, c) = getconst(code.ops, pc);
+ return (CBreak, v, code.strs[c]);
+ Lcontinuelab =>
+ (pc, c) = getconst(code.ops, pc);
+ return (CContinue, v, code.strs[c]);
+ Lreturn =>
+ (pc, v) = eexpval(ex, code, pc, code.npc);
+ return (CReturn, v, nil);
+ '{' =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (k1, v1, lab) = estmt(ex, code, pc, apc);
+ pc = apc;
+ Lif =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, ev) = eexpval(ex, code, pc, apc);
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc2, apc2) = getjmp(code.ops, apc);
+ if(toBoolean(ex, ev) != false)
+ (k1, v1, lab) = estmt(ex, code, pc, apc);
+ else if(pc2 != apc2)
+ (k1, v1, lab) = estmt(ex, code, pc2, apc2);
+ pc = apc2;
+ Lwhile =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc2, apc2) = getjmp(code.ops, apc);
+ for(;;){
+ (nil, ev) = eexpval(ex, code, pc, apc);
+ if(toBoolean(ex, ev) == false)
+ break;
+ (k, v1, lab) = estmt(ex, code, pc2, apc2);
+ if(v1 != nil)
+ v = v1;
+ if(k == CBreak || k == CContinue){
+ if(initlabs(lab, labs)){
+ if(k == CBreak)
+ break;
+ else
+ continue;
+ }
+ else
+ return (k, v1, lab);
+ }
+ if(k == CReturn || k == CThrow)
+ return (k, v1, nil);
+ }
+ pc = apc2;
+ Ldo =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc2, apc2) = getjmp(code.ops, apc);
+ for(;;){
+ (k, v1, lab) = estmt(ex, code, pc, apc);
+ if(v1 != nil)
+ v = v1;
+ if(k == CBreak || k == CContinue){
+ if(initlabs(lab, labs)){
+ if(k == CBreak)
+ break;
+ else
+ continue;
+ }
+ else
+ return (k, v1, lab);
+ }
+ if(k == CReturn || k == CThrow)
+ return (k, v1, nil);
+ (nil, ev) = eexpval(ex, code, pc2, apc2);
+ if(toBoolean(ex, ev) == false)
+ break;
+ }
+ pc = apc2;
+ Lfor or
+ Lforvar =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, nil) = eexpval(ex, code, pc, apc);
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc2, apc2) = getjmp(code.ops, apc);
+ (pc3, apc3) = getjmp(code.ops, apc2);
+ for(;;){
+ (nil, e) = eexp(ex, code, pc, apc);
+ if(e != nil && toBoolean(ex, getValue(ex, e)) == false)
+ break;
+ (k, v1, lab) = estmt(ex, code, pc3, apc3);
+ if(v1 != nil)
+ v = v1;
+ if(k == CBreak || k == CContinue){
+ if(initlabs(lab, labs)){
+ if(k == CBreak)
+ break;
+ else
+ continue;
+ }
+ else
+ return (k, v1, lab);
+ }
+ if(k == CReturn || k == CThrow)
+ return (k, v1, nil);
+ eexpval(ex, code, pc2, apc2);
+ }
+ pc = apc3;
+ Lforin or
+ Lforvarin =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc2, apc2) = getjmp(code.ops, apc);
+ (pc3, apc3) = getjmp(code.ops, apc2);
+ if(op == Lforvarin){
+ (nil, nil) = eexp(ex, code, pc, apc);
+ # during for only evaluate the id, not the initializer
+ apc = pc + 1;
+ }
+ (nil, ev) = eexpval(ex, code, pc2, apc2);
+ bo := toObject(ex, ev);
+
+ #
+ # note this won't enumerate host properties
+ #
+ enum:
+ for(o := bo; o != nil; o = o.prototype){
+ if(o.host != nil && o.host != me)
+ continue;
+ for(i := 0; i < len o.props; i++){
+ if(o.props[i] == nil
+ || (o.props[i].attr & DontEnum)
+ || propshadowed(bo, o, o.props[i].name))
+ continue;
+ (nil, e) = eexp(ex, code, pc, apc);
+ putValue(ex, e, strval(o.props[i].name));
+ (k, v1, lab) = estmt(ex, code, pc3, apc3);
+ if(v1 != nil)
+ v = v1;
+ if(k == CBreak || k == CContinue){
+ if(initlabs(lab, labs)){
+ if(k == CBreak)
+ break enum;
+ else
+ continue enum;
+ }
+ else
+ return (k, v1, lab);
+ }
+ if(k == CReturn || k == CThrow)
+ return (k, v1, nil);
+ }
+ }
+ pc = apc3;
+ Lwith =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, ev) = eexpval(ex, code, pc, apc);
+ pushscope(ex, toObject(ex, ev));
+ (pc, apc) = getjmp(code.ops, pc);
+ (k1, v1, lab) = estmt(ex, code, pc, apc);
+ popscope(ex);
+ pc = apc;
+ ';' =>
+ ;
+ Lvar =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, nil) = eexp(ex, code, pc, apc);
+ Lswitch =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, ev) = eexpval(ex, code, pc, apc);
+ (pc, apc) = getjmp(code.ops, pc);
+ (k1, v1, lab) = ecaseblk(ex, code, ev, pc, apc, labs);
+ pc = apc;
+ Lthrow =>
+ (pc, v) = eexpval(ex, code, pc, code.npc);
+ ex.error = toString(ex, v);
+ return (CThrow, v, nil);
+ Lprint =>
+ (pc, v1) = eexpval(ex, code, pc, code.npc);
+ print("%s\n", toString(ex, v1));
+ Ltry =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (k1, v1, lab) = estmt(ex, code, pc, apc);
+ (kc, vc) := (k1, v1);
+ (pc, apc) = getjmp(code.ops, apc);
+ if(pc != apc){
+ (pc, c) = getconst(code.ops, ++pc);
+ if(k1 == CThrow){
+ o := mkobj(ex.objproto, "Object");
+ valinstant(o, DontDelete, code.strs[c], v1);
+ pushscope(ex, o);
+ (k1, v1, lab) = estmt(ex, code, pc, apc);
+ popscope(ex);
+ if(k1 != CNormal)
+ (kc, vc) = (k1, v1);
+ }
+ }
+ (pc, apc) = getjmp(code.ops, apc);
+ if(pc != apc){
+ (k, v, lab) = estmt(ex, code, pc, apc);
+ if(k == CNormal)
+ (k1, v1) = (kc, vc);
+ else
+ (k1, v1) = (k, v);
+ }
+ pc = apc;
+ * =>
+ (pc, e) = eexp(ex, code, pc-1, code.npc);
+ if(e != nil)
+ v1 = getValue(ex, e);
+ if(debug['v'])
+ print("%s\n", toString(ex, v1));
+ }
+
+ if(v1 != nil)
+ v = v1;
+ if(k1 == CBreak && lab != nil && inlabs(lab, labs))
+ (k1, lab) = (CNormal, nil);
+ if(k1 != CNormal)
+ return (k1, v, lab);
+ }
+ return (CNormal, v, nil);
+}
+exception{
+ "throw" =>
+ ex.sp = osp;
+ return (CThrow, ex.errval, nil);
+}
+}
+
+ecaseblk(ex : ref Exec, code : ref Code, sv : ref Val, pc, epc : int, labs: list of string) : Completion
+{ defpc, nextpc, clausepc, apc : int;
+ ev : ref Val;
+ lab: string;
+
+ k := CNormal;
+ v := undefined;
+ matched := 0;
+
+ (pc, defpc) = getjmp(code.ops, pc);
+ clausepc = pc;
+ (pc, nextpc) = getjmp(code.ops, pc);
+ for (; pc <= epc; (clausepc, (pc, nextpc)) = (nextpc, getjmp(code.ops, nextpc))) {
+ if (nextpc == epc) {
+ if (matched || defpc == epc)
+ break;
+ # do the default
+ matched = 1;
+ nextpc = defpc;
+ continue;
+ }
+ if (!matched && clausepc == defpc)
+ # skip default case - still scanning guards
+ continue;
+ if (clausepc != defpc) {
+ # only case clauses have guard exprs
+ (pc, apc) = getjmp(code.ops, pc);
+ if (matched)
+ pc = apc;
+ else {
+ (pc, ev) = eexpval(ex, code, pc, apc);
+ if (identical(sv, ev))
+ matched = 1;
+ else
+ continue;
+ }
+ }
+ (k, v, lab) = estmt(ex, code, pc, nextpc);
+ if(k == CBreak && initlabs(lab, labs))
+ return (CNormal, v, nil);
+ if(k == CBreak || k == CContinue || k == CReturn || k == CThrow)
+ return (k, v, lab);
+ }
+ return (k, v, lab);
+}
+
+identical(v1, v2 : ref Val) : int
+{
+ if (v1.ty != v2.ty)
+ return 0;
+ ret := 0;
+ case v1.ty{
+ TUndef or
+ TNull =>
+ ret = 1;
+ TNum =>
+ if(v1.num == v2.num)
+ ret = 1;
+ TBool =>
+ if(v1 == v2)
+ ret = 1;
+ TStr =>
+ if(v1.str == v2.str)
+ ret = 1;
+ TObj =>
+ if(v1.obj == v2.obj)
+ ret = 1;
+ TRegExp =>
+ if(v1.rev == v2.rev)
+ ret = 1;
+ }
+ return ret;
+}
+
+eexpval(ex: ref Exec, code: ref Code, pc, epc: int): (int, ref Val)
+{
+ e: ref Ref;
+
+ (pc, e) = eexp(ex, code, pc, epc);
+ if(e == nil)
+ v := undefined;
+ else
+ v = getValue(ex, e);
+ return (pc, v);
+}
+
+eexp(ex: ref Exec, code: ref Code, pc, epc: int): (int, ref Ref)
+{
+ o, th: ref Obj;
+ a1: ref Ref;
+ v, v1, v2: ref Val;
+ s: string;
+ r1, r2: real;
+ c, apc, i1, i2: int;
+
+ savesp := ex.sp;
+out: while(pc < epc){
+ op := int code.ops[pc++];
+ if(debug['e'] > 1){
+ case op{
+ Lid or
+ Lstr or
+ Lregexp =>
+ (nil, c) = getconst(code.ops, pc);
+ print("eexp(pc %d, sp %d) %s '%s'\n", pc-1, ex.sp, tokname(op), code.strs[c]);
+ Lnum =>
+ (nil, c) = getconst(code.ops, pc);
+ print("eexp(pc %d, sp %d) %s '%g'\n", pc-1, ex.sp, tokname(op), code.nums[c]);
+ * =>
+ print("eexp(pc %d, sp %d) %s\n", pc-1, ex.sp, tokname(op));
+ }
+ }
+ case op{
+ Lthis =>
+ v1 = objval(ex.this);
+ Lnum =>
+ (pc, c) = getconst(code.ops, pc);
+ v1 = numval(code.nums[c]);
+ Lstr =>
+ (pc, c) = getconst(code.ops, pc);
+ v1 = strval(code.strs[c]);
+ Lregexp =>
+ (pc, c) = getconst(code.ops, pc);
+ (p, f) := rsplit(code.strs[c]);
+ o = nregexp(ex, nil, array[] of { strval(p), strval(f) });
+ v1 = objval(o);
+ # v1 = regexpval(p, f, 0);
+ Lid =>
+ (pc, c) = getconst(code.ops, pc);
+ epush(ex, esprimid(ex, code.strs[c]));
+ continue;
+ Lnoval =>
+ v1 = undefined;
+ '.' =>
+ a1 = epop(ex);
+ v1 = epopval(ex);
+ epush(ex, ref Ref(1, nil, toObject(ex, v1), a1.name));
+ continue;
+ '[' =>
+ v2 = epopval(ex);
+ v1 = epopval(ex);
+ epush(ex, ref Ref(1, nil, toObject(ex, v1), toString(ex, v2)));
+ continue;
+ Lpostinc or
+ Lpostdec =>
+ a1 = epop(ex);
+ r1 = toNumber(ex, getValue(ex, a1));
+ v1 = numval(r1);
+ if(op == Lpostinc)
+ r1++;
+ else
+ r1--;
+ putValue(ex, a1, numval(r1));
+ Linc or
+ Ldec or
+ Lpreadd or
+ Lpresub =>
+ a1 = epop(ex);
+ r1 = toNumber(ex, getValue(ex, a1));
+ case op{
+ Linc =>
+ r1++;
+ Ldec =>
+ r1--;
+ Lpresub =>
+ r1 = -r1;
+ }
+ v1 = numval(r1);
+ if(op == Linc || op == Ldec)
+ putValue(ex, a1, v1);
+ '~' =>
+ v = epopval(ex);
+ i1 = toInt32(ex, v);
+ i1 = ~i1;
+ v1 = numval(real i1);
+ '!' =>
+ v = epopval(ex);
+ v1 = toBoolean(ex, v);
+ if(v1 == true)
+ v1 = false;
+ else
+ v1 = true;
+ Ltypeof =>
+ a1 = epop(ex);
+ if(a1.isref && getBase(ex, a1) == nil)
+ s = "undefined";
+ else case (v1 = getValue(ex, a1)).ty{
+ TUndef =>
+ s = "undefined";
+ TNull =>
+ s = "object";
+ TBool =>
+ s = "boolean";
+ TNum =>
+ s = "number";
+ TStr =>
+ s = "string";
+ TObj =>
+ if(v1.obj.call != nil)
+ s = "function";
+ else
+ s = "object";
+ TRegExp =>
+ s = "regexp";
+ }
+ v1 = strval(s);
+ Ldelete =>
+ a1 = epop(ex);
+ o = getBase(ex, a1);
+ s = getPropertyName(ex, a1);
+ if(o != nil)
+ esdelete(ex, o, s, 0);
+ v1 = undefined;
+ Lvoid =>
+ epopval(ex);
+ v = undefined;
+ '*' or
+ '/' or
+ '%' or
+ '-' =>
+ v2 = epopval(ex);
+ a1 = epop(ex);
+ r1 = toNumber(ex, getValue(ex, a1));
+ r2 = toNumber(ex, v2);
+ case op{
+ '*' =>
+ r1 = r1 * r2;
+ '/' =>
+ r1 = r1 / r2;
+ '%' =>
+ r1 = fmod(r1, r2);
+ '-' =>
+ r1 = r1 - r2;
+ }
+ v1 = numval(r1);
+ '+' =>
+ v2 = epopval(ex);
+ a1 = epop(ex);
+ v1 = toPrimitive(ex, getValue(ex, a1), NoHint);
+ v2 = toPrimitive(ex, v2, NoHint);
+ if(v1.ty == TStr || v2.ty == TStr)
+ v1 = strval(toString(ex, v1)+toString(ex, v2));
+ else
+ v1 = numval(toNumber(ex, v1)+toNumber(ex, v2));
+ Llsh or
+ Lrsh or
+ Lrshu or
+ '&' or
+ '^' or
+ '|' =>
+ v2 = epopval(ex);
+ a1 = epop(ex);
+ i1 = toInt32(ex, getValue(ex, a1));
+ i2 = toInt32(ex, v2);
+ case op{
+ Llsh =>
+ i1 <<= i2 & 16r1f;
+ Lrsh =>
+ i1 >>= i2 & 16r1f;
+ Lrshu =>
+ i1 = int (((big i1) & 16rffffffff) >> (i2 & 16r1f));
+ '&' =>
+ i1 &= i2;
+ '|' =>
+ i1 |= i2;
+ '^' =>
+ i1 ^= i2;
+ }
+ v1 = numval(real i1);
+ '=' or
+ Las =>
+ v1 = epopval(ex);
+ a1 = epop(ex);
+ putValue(ex, a1, v1);
+ '<' or
+ '>' or
+ Lleq or
+ Lgeq =>
+ v2 = epopval(ex);
+ v1 = epopval(ex);
+ if(op == '>' || op == Lleq){
+ v = v1;
+ v1 = v2;
+ v2 = v;
+ }
+ v1 = toPrimitive(ex, v1, TNum);
+ v2 = toPrimitive(ex, v2, TNum);
+ if(v1.ty == TStr && v2.ty == TStr){
+ if(v1.str < v2.str)
+ v1 = true;
+ else
+ v1 = false;
+ }else{
+ r1 = toNumber(ex, v1);
+ r2 = toNumber(ex, v2);
+ if(isnan(r1) || isnan(r2))
+ v1 = undefined;
+ else if(r1 < r2)
+ v1 = true;
+ else
+ v1 = false;
+ }
+ if(op == Lgeq || op == Lleq){
+ if(v1 == false)
+ v1 = true;
+ else
+ v1 = false;
+ }
+ Lin =>
+ v2 = epopval(ex);
+ v1 = epopval(ex);
+ if(v2.ty != TObj)
+ runtime(ex, TypeError, "rhs of 'in' not an object");
+ s = toString(ex, v1);
+ v1 = eshasproperty(ex, v2.obj, s, 0);
+ Linstanceof =>
+ v2 = epopval(ex);
+ v1 = epopval(ex);
+ if(v2.ty != TObj)
+ runtime(ex, TypeError, "rhs of 'instanceof' not an object");
+ if(!isfuncobj(v2.obj))
+ runtime(ex, TypeError, "rhs of 'instanceof' not a function");
+ if(v1.ty != TObj)
+ v1 = false;
+ else{
+ v2 = esget(ex, v2.obj, "prototype", 0);
+ if(v2.ty != TObj)
+ runtime(ex, TypeError, "prototype value not an object");
+ o = v2.obj;
+ for(p := v1.obj.prototype; p != nil; p = p.prototype){
+ if(p == o){
+ v1 = true;
+ break;
+ }
+ }
+ if(p == nil)
+ v1 = false;
+ }
+ Leq or
+ Lneq or
+ Lseq or
+ Lsne =>
+ strict := op == Lseq || op == Lsne;
+ v2 = epopval(ex);
+ v1 = epopval(ex);
+ v = false;
+ while(v1.ty != v2.ty){
+ if(strict)
+ break;
+ if(isnull(v1) && v2 == undefined
+ || v1 == undefined && isnull(v2))
+ v1 = v2;
+ else if(v1.ty == TNum && v2.ty == TStr)
+ v2 = numval(toNumber(ex, v2));
+ else if(v1.ty == TStr && v2.ty == TNum)
+ v1 = numval(toNumber(ex, v1));
+ else if(v1.ty == TBool)
+ v1 = numval(toNumber(ex, v1));
+ else if(v2.ty == TBool)
+ v2 = numval(toNumber(ex, v2));
+ else if(v2.ty == TObj && (v1.ty == TStr || v1.ty == TNum))
+ v2 = toPrimitive(ex, v2, NoHint);
+ else if(v1.ty == TObj && (v2.ty == TStr || v2.ty == TNum))
+ v1 = toPrimitive(ex, v1, NoHint);
+ else{
+ v1 = true;
+ v2 = false;
+ }
+ }
+ if(v1.ty != v2.ty)
+ v = false;
+ else{
+ case v1.ty{
+ TUndef or
+ TNull =>
+ v = true;
+ TNum =>
+ if(v1.num == v2.num)
+ v = true;
+ TBool =>
+ if(v1 == v2)
+ v = true;
+ TStr =>
+ if(v1.str == v2.str)
+ v = true;
+ TObj =>
+ if(v1.obj == v2.obj)
+ v = true;
+ TRegExp =>
+ if(v1.rev.p == v2.rev.p && v1.rev.f == v2.rev.f)
+ v = true;
+ }
+ }
+ if(op == Lneq || op == Lsne){
+ if(v == false)
+ v = true;
+ else
+ v = false;
+ }
+ v1 = v;
+ Landand =>
+ v1 = epopval(ex);
+ (pc, apc) = getjmp(code.ops, pc);
+ if(toBoolean(ex, v1) != false){
+ (pc, a1) = eexp(ex, code, pc, apc);
+ v1 = getValue(ex, a1);
+ }
+ pc = apc;
+ Loror =>
+ v1 = epopval(ex);
+ (pc, apc) = getjmp(code.ops, pc);
+ if(toBoolean(ex, v1) != true){
+ (pc, a1) = eexp(ex, code, pc, apc);
+ v1 = getValue(ex, a1);
+ }
+ pc = apc;
+ '?' =>
+ v1 = epopval(ex);
+ (pc, apc) = getjmp(code.ops, pc);
+ v1 = toBoolean(ex, v1);
+ if(v1 == true)
+ (pc, a1) = eexp(ex, code, pc, apc);
+ pc = apc;
+ (pc, apc) = getjmp(code.ops, pc);
+ if(v1 != true)
+ (pc, a1) = eexp(ex, code, pc, apc);
+ pc = apc;
+ v1 = getValue(ex, a1);
+ Lasop =>
+ a1 = epop(ex);
+ epush(ex, a1);
+ v1 = getValue(ex, a1);
+ Lgetval =>
+ v1 = epopval(ex);
+ ',' =>
+ v1 = epopval(ex);
+ epop(ex);
+ # a1's value already gotten by Lgetval
+ '(' or
+ ')' =>
+ continue;
+ Larrinit =>
+ o = narray(ex, nil, nil);
+ (pc, c) = getconst(code.ops, pc);
+ esput(ex, o, "length", numval(real c), 0);
+ c = ex.sp-c;
+ for(sp := c; sp < ex.sp; sp++){
+ v = getValue(ex, ex.stack[sp]);
+ if(v != undefined)
+ esput(ex, o, string (sp-c), v, 0);
+ }
+ ex.sp = c;
+ v1 = objval(o);
+ Lobjinit =>
+ o = nobj(ex, nil, nil);
+ (pc, c) = getconst(code.ops, pc);
+ c = ex.sp-2*c;
+ for(sp := c; sp < ex.sp; sp += 2){
+ v = getValue(ex, ex.stack[sp]);
+ if(isnum(v) || isstr(v))
+ p := toString(ex, v);
+ else
+ p = ex.stack[sp].name;
+ v = getValue(ex, ex.stack[sp+1]);
+ esput(ex, o, p, v, 0);
+ }
+ ex.sp = c;
+ v1 = objval(o);
+ Lcall or
+ Lnewcall =>
+ (pc, c) = getconst(code.ops, pc);
+ args := array[c] of ref Val;
+ c = ex.sp - c;
+ for(sp := c; sp < ex.sp; sp++)
+ args[sp-c] = getValue(ex, ex.stack[sp]);
+ ex.sp = c;
+ a1 = epop(ex);
+ v = getValue(ex, a1);
+ o = getobj(v);
+ if(op == Lcall){
+ if(o == nil || o.call == nil)
+ runtime(ex, TypeError, "can only call function objects ("+a1.name+")");
+ th = nil;
+ if(a1.isref){
+ th = getBase(ex, a1);
+ if(th != nil && isactobj(th))
+ th = nil;
+ }
+
+ # have to execute functions in the same context as they
+ # were defined, but need to use current stack.
+ if (o.call.ex == nil)
+ a1 = escall(ex, v.obj, th, args, 0);
+ else {
+ fnex := ref *o.call.ex;
+ fnex.stack = ex.stack;
+ fnex.sp = ex.sp;
+ fnex.scopechain = fnex.global :: nil;
+ # drop ref to stack to avoid array duplication should stack grow
+ ex.stack = nil;
+ osp := ex.sp;
+ # can get an exception here that corrupts ex etc.
+#aardvark:=99;
+#test:=99;
+# zebra:=99;
+ {
+ a1 = escall(fnex, v.obj, th, args, 0);
+ }
+ exception e{
+ "throw" =>
+ # copy up error so as it gets reported properly
+ ex.error = fnex.error;
+ ex.errval = fnex.errval;
+ ex.stack = fnex.stack;
+ ex.sp = osp;
+# raise e;
+ raise "throw";
+ }
+ # restore stack, sp is OK as escall() ensures that stack is balanced
+ ex.stack = fnex.stack;
+ }
+ }else{
+ if(o == nil || o.construct == nil)
+ runtime(ex, TypeError, "new must be given a constructor object");
+ a1 = valref(objval(esconstruct(ex, o, args)));
+ }
+ epush(ex, a1);
+ args = nil;
+ continue;
+ Lnew =>
+ v = epopval(ex);
+ o = getobj(v);
+ if(o == nil || o.construct == nil)
+ runtime(ex, TypeError, "new must be given a constructor object");
+ v1 = objval(esconstruct(ex, o, nil));
+ Lfunction =>
+ (pc, c) = getconst(code.ops, pc);
+ v1 = objval(code.fexps[c]);
+ ';' =>
+ break out;
+ * =>
+ fatal(ex, sprint("eexp: unknown op %s\n", tokname(op)));
+ }
+ epushval(ex, v1);
+ }
+
+ if(savesp == ex.sp)
+ return (pc, nil);
+
+ if(savesp != ex.sp-1)
+ print("unbalanced stack in eexp: %d %d\n", savesp, ex.sp);
+ return (pc, epop(ex));
+}
+
+epushval(ex: ref Exec, v: ref Val)
+{
+ epush(ex, valref(v));
+}
+
+epush(ex: ref Exec, r: ref Ref)
+{
+ if(ex.sp >= len ex.stack){
+ st := array[2 * len ex.stack] of ref Ref;
+ st[:] = ex.stack;
+ ex.stack = st;
+ }
+ ex.stack[ex.sp++] = r;
+}
+
+epop(ex: ref Exec): ref Ref
+{
+ if(ex.sp == 0)
+ fatal(ex, "popping too far off the estack\n");
+ return ex.stack[--ex.sp];
+}
+
+epopval(ex: ref Exec): ref Val
+{
+ if(ex.sp == 0)
+ fatal(ex, "popping too far off the estack\n");
+ return getValue(ex, ex.stack[--ex.sp]);
+}
+
+inlabs(lab: string, labs: list of string): int
+{
+ for(l := labs; l != nil; l = tl l)
+ if(hd l == lab)
+ return 1;
+ return 0;
+}
+
+initlabs(lab: string, labs: list of string): int
+{
+ return lab == nil || inlabs(lab, labs);
+}
diff --git a/appl/lib/ecmascript/mkfile b/appl/lib/ecmascript/mkfile
new file mode 100644
index 00000000..afcd2d52
--- /dev/null
+++ b/appl/lib/ecmascript/mkfile
@@ -0,0 +1,23 @@
+<../../../mkconfig
+
+TARG= ecmascript.dis\
+
+MODULES=\
+ builtin.b\
+ date.b\
+ exec.b\
+ obj.b\
+ pprint.b\
+ regexp.b\
+ uri.b\
+
+SYSMODULES= \
+ sys.m\
+ math.m\
+ string.m\
+ daytime.m\
+ ecmascript.m\
+
+DISBIN=$ROOT/dis/lib
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/lib/ecmascript/obj.b b/appl/lib/ecmascript/obj.b
new file mode 100644
index 00000000..ad3df676
--- /dev/null
+++ b/appl/lib/ecmascript/obj.b
@@ -0,0 +1,836 @@
+#
+# want to use the value in a context which
+# prefers an object, so coerce schizo vals
+# to object versions
+#
+coerceToObj(ex: ref Exec, v: ref Val): ref Val
+{
+ o: ref Obj;
+
+ case v.ty{
+ TBool =>
+ o = mkobj(ex.boolproto, "Boolean");
+ o.val = v;
+ TStr =>
+ o = mkobj(ex.strproto, "String");
+ o.val = v;
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "length", numval(real len v.str));
+ TNum =>
+ o = mkobj(ex.numproto, "Number");
+ o.val = v;
+ TRegExp =>
+ o = mkobj(ex.regexpproto, "RegExp");
+ o.val = v;
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "length", numval(real len v.rev.p));
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "source", strval(v.rev.p));
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "global", strhas(v.rev.f, 'g'));
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "ignoreCase", strhas(v.rev.f, 'i'));
+ valinstant(o, DontEnum|DontDelete|ReadOnly, "multiline", strhas(v.rev.f, 'm'));
+ valinstant(o, DontEnum|DontDelete, "lastIndex", numval(real v.rev.i));
+ * =>
+ return v;
+ }
+ return objval(o);
+}
+
+coerceToVal(v: ref Val): ref Val
+{
+ if(v.ty != TObj)
+ return v;
+ o := v.obj;
+ if(o.host != nil && o.host != me
+ || o.class != "String"
+ || o.class != "Number"
+ || o.class != "Boolean")
+ return v;
+ return o.val;
+}
+
+isstrobj(o: ref Obj): int
+{
+ return (o.host == nil || o.host == me) && o.class == "String";
+}
+
+isnumobj(o: ref Obj): int
+{
+ return (o.host == nil || o.host == me) && o.class == "Number";
+}
+
+isboolobj(o: ref Obj): int
+{
+ return (o.host == nil || o.host == me) && o.class == "Boolean";
+}
+
+isdateobj(o: ref Obj): int
+{
+ return (o.host == nil || o.host == me) && o.class == "Date";
+}
+
+isregexpobj(o: ref Obj): int
+{
+ return (o.host == nil || o.host == me) && o.class == "RegExp";
+}
+
+isfuncobj(o: ref Obj): int
+{
+ return (o.host == nil || o.host == me) && o.class == "Function";
+}
+
+isarray(o: ref Obj): int
+{
+# return (o.host == nil || o.host == me) && o.class == "Array";
+ # relax the host test
+ # so that hosts can intercept Array operations and defer
+ # unhandled ops to the builtin
+ return o.class == "Array";
+}
+
+iserr(o: ref Obj): int
+{
+ return (o.host == nil || o.host == me) && o.class == "Error";
+}
+
+isactobj(o: ref Obj): int
+{
+ return o.host == nil && o.class == "Activation";
+}
+
+isnull(v: ref Val): int
+{
+ return v == null || v == nil;
+}
+
+isundefined(v: ref Val): int
+{
+ return v == undefined;
+}
+
+isstr(v: ref Val): int
+{
+ return v.ty == TStr;
+}
+
+isnum(v: ref Val): int
+{
+ return v.ty == TNum;
+}
+
+isbool(v: ref Val): int
+{
+ return v.ty == TBool;
+}
+
+isobj(v: ref Val): int
+{
+ return v.ty == TObj;
+}
+
+isregexp(v: ref Val): int
+{
+ return v.ty == TRegExp || v.ty == TObj && isregexpobj(v.obj);
+}
+
+#
+# retrieve the object field if it's valid
+#
+getobj(v: ref Val): ref Obj
+{
+ if(v.ty == TObj)
+ return v.obj;
+ return nil;
+}
+
+isprimval(v: ref Val): int
+{
+ return v.ty != TObj;
+}
+
+pushscope(ex: ref Exec, o: ref Obj)
+{
+ ex.scopechain = o :: ex.scopechain;
+}
+
+popscope(ex: ref Exec)
+{
+ ex.scopechain = tl ex.scopechain;
+}
+
+runtime(ex: ref Exec, o: ref Obj, s: string)
+{
+ ex.error = s;
+ if(o == nil)
+ ex.errval = undefined;
+ else
+ ex.errval = objval(o);
+ if(debug['r']){
+ print("ecmascript runtime error: %s\n", s);
+ if(""[5] == -1); # abort
+ }
+ raise "throw";
+ exit; # never reached
+}
+
+mkobj(proto: ref Obj, class: string): ref Obj
+{
+ if(class == nil)
+ class = "Object";
+ return ref Obj(nil, proto, nil, nil, nil, class, nil, nil);
+}
+
+valcheck(ex: ref Exec, v: ref Val, hint: int)
+{
+ if(v == nil
+ || v.ty < 0
+ || v.ty >= NoHint
+ || v.ty == TBool && v != true && v != false
+ || v.ty == TObj && v.obj == nil
+ || hint != NoHint && v.ty != hint)
+ runtime(ex, RangeError, "bad value generated by host object");
+}
+
+# builtin methods for properties
+esget(ex: ref Exec, o: ref Obj, prop: string, force: int): ref Val
+{
+ for( ; o != nil; o = o.prototype){
+ if(!force && o.host != nil && o.host != me){
+ v := o.host->get(ex, o, prop);
+ valcheck(ex, v, NoHint);
+ return v;
+ }
+
+ for(i := 0; i < len o.props; i++)
+ if(o.props[i] != nil && o.props[i].name == prop)
+ return o.props[i].val.val;
+ force = 0;
+ }
+ return undefined;
+}
+
+esputind(o: ref Obj, prop: string): int
+{
+ empty := -1;
+ props := o.props;
+ for(i := 0; i < len props; i++){
+ if(props[i] == nil)
+ empty = i;
+ else if(props[i].name == prop)
+ return i;
+ }
+ if(empty != -1)
+ return empty;
+
+ props = array[i+1] of ref Prop;
+ props[:] = o.props;
+ o.props = props;
+ return i;
+}
+
+esput(ex: ref Exec, o: ref Obj, prop: string, v: ref Val, force: int)
+{
+ ai: big;
+
+ if(!force && o.host != nil && o.host != me)
+ return o.host->put(ex, o, prop, v);
+
+ if(escanput(ex, o, prop, 0) != true)
+ return;
+
+ #
+ # should this test for prototype == ex.arrayproto?
+ # hard to say, but 15.4.5 "Properties of Array Instances" implies not
+ #
+ if(isarray(o))
+ al := toUint32(ex, esget(ex, o, "length", 1));
+
+ i := esputind(o, prop);
+ props := o.props;
+ if(props[i] != nil)
+ props[i].val.val = v;
+ else
+ props[i] = ref Prop(0, prop, ref RefVal(v));
+ if(!isarray(o))
+ return;
+
+ if(prop == "length"){
+ nl := toUint32(ex, v);
+ for(ai = nl; ai < al; ai++)
+ esdelete(ex, o, string ai, 1);
+ props[i].val.val = numval(real nl);
+ }else{
+ ai = big prop;
+ if(prop != string ai || ai < big 0 || ai >= 16rffffffff)
+ return;
+ i = esputind(o, "length");
+ if(props[i] == nil)
+ fatal(ex, "bogus array esput");
+ else if(toUint32(ex, props[i].val.val) <= ai)
+ props[i].val.val = numval(real(ai+big 1));
+ }
+}
+
+escanput(ex: ref Exec, o: ref Obj, prop: string, force: int): ref Val
+{
+ for( ; o != nil; o = o.prototype){
+ if(!force && o.host != nil && o.host != me){
+ v := o.host->canput(ex, o, prop);
+ valcheck(ex, v, TBool);
+ return v;
+ }
+
+ for(i := 0; i < len o.props; i++){
+ if(o.props[i] != nil && o.props[i].name == prop){
+ if(o.props[i].attr & ReadOnly)
+ return false;
+ else
+ return true;
+ }
+ }
+
+ force = 0;
+ }
+ return true;
+}
+
+eshasproperty(ex: ref Exec, o: ref Obj, prop: string, force: int): ref Val
+{
+ for(; o != nil; o = o.prototype){
+ if(!force && o.host != nil && o.host != me){
+ v := o.host->hasproperty(ex, o, prop);
+ valcheck(ex, v, TBool);
+ return v;
+ }
+ for(i := 0; i < len o.props; i++)
+ if(o.props[i] != nil && o.props[i].name == prop)
+ return true;
+ }
+ return false;
+}
+
+eshasenumprop(o: ref Obj, prop: string): ref Val
+{
+ for(i := 0; i < len o.props; i++)
+ if(o.props[i] != nil && o.props[i].name == prop){
+ if(o.props[i].attr & DontEnum)
+ return false;
+ return true;
+ }
+ return false;
+}
+
+propshadowed(start, end: ref Obj, prop: string): int
+{
+ for(o := start; o != end; o = o.prototype){
+ if(o.host != nil && o.host != me)
+ return 0;
+ for(i := 0; i < len o.props; i++)
+ if(o.props[i] != nil && o.props[i].name == prop)
+ return 1;
+ }
+ return 0;
+}
+
+esdelete(ex: ref Exec, o: ref Obj, prop: string, force: int)
+{
+ if(!force && o.host != nil && o.host != me)
+ return o.host->delete(ex, o, prop);
+
+ for(i := 0; i < len o.props; i++){
+ if(o.props[i] != nil && o.props[i].name == prop){
+ if(!(o.props[i].attr & DontDelete))
+ o.props[i] = nil;
+ return;
+ }
+ }
+}
+
+esdeforder := array[] of {"valueOf", "toString"};
+esdefaultval(ex: ref Exec, o: ref Obj, ty: int, force: int): ref Val
+{
+ v: ref Val;
+
+ if(!force && o.host != nil && o.host != me){
+ v = o.host->defaultval(ex, o, ty);
+ valcheck(ex, v, NoHint);
+ if(!isprimval(v))
+ runtime(ex, TypeError, "host object returned an object to [[DefaultValue]]");
+ return v;
+ }
+
+ hintstr := 0;
+ if(ty == TStr || ty == NoHint && isdateobj(o))
+ hintstr = 1;
+
+ for(i := 0; i < 2; i++){
+ v = esget(ex, o, esdeforder[hintstr ^ i], 0);
+ if(v != undefined && v.ty == TObj && v.obj.call != nil){
+ r := escall(ex, v.obj, o, nil, 0);
+ v = nil;
+ if(!r.isref)
+ v = r.val;
+ if(v != nil && isprimval(v))
+ return v;
+ }
+ }
+ runtime(ex, TypeError, "no default value");
+ return nil;
+}
+
+esprimid(ex: ref Exec, s: string): ref Ref
+{
+ for(sc := ex.scopechain; sc != nil; sc = tl sc){
+ o := hd sc;
+ if(eshasproperty(ex, o, s, 0) == true)
+ return ref Ref(1, nil, o, s);
+ }
+
+ #
+ # the right place to add literals?
+ #
+ case s{
+ "null" =>
+ return ref Ref(0, null, nil, "null");
+ "true" =>
+ return ref Ref(0, true, nil, "true");
+ "false" =>
+ return ref Ref(0, false, nil, "false");
+ }
+ return ref Ref(1, nil, nil, s);
+}
+
+bivar(ex: ref Exec, sc: list of ref Obj, s: string): ref Val
+{
+ for(; sc != nil; sc = tl sc){
+ o := hd sc;
+ if(eshasproperty(ex, o, s, 0) == true)
+ return esget(ex, o, s, 0);
+ }
+ return nil;
+}
+
+esconstruct(ex: ref Exec, func: ref Obj, args: array of ref Val): ref Obj
+{
+ o: ref Obj;
+
+ if(func.construct == nil)
+ runtime(ex, TypeError, "new must be applied to a constructor object");
+ if(func.host != nil)
+ o = func.host->construct(ex, func, args);
+ else{
+ o = getobj(esget(ex, func, "prototype", 0));
+ if(o == nil)
+ o = ex.objproto;
+ this := mkobj(o, "Object");
+ o = getobj(getValue(ex, escall(ex, func, this, args, 0)));
+
+ # Divergence from ECMA-262
+ #
+ # observed that not all script-defined constructors return an object,
+ # the value of 'this' is assumed to be the value of the constructor
+ if (o == nil)
+ o = this;
+ }
+ if(o == nil)
+ runtime(ex, TypeError, func.val.str+" failed to generate an object");
+ return o;
+}
+
+escall(ex: ref Exec, func, this: ref Obj, args: array of ref Val, eval: int): ref Ref
+{
+ if(func.call == nil)
+ runtime(ex, TypeError, "can only call function objects");
+ if(this == nil)
+ this = ex.global;
+
+ r: ref Ref = nil;
+ if(func.host != nil){
+ r = func.host->call(ex, func, this, args, 0);
+ if(r.isref && r.name == nil)
+ runtime(ex, ReferenceError, "host call returned a bad reference");
+ else if(!r.isref)
+ valcheck(ex, r.val, NoHint);
+ return r;
+ }
+
+ argobj := mkobj(ex.objproto, "Object");
+ actobj := mkobj(nil, "Activation");
+
+ oargs: ref RefVal = nil;
+ props := func.props;
+ empty := -1;
+ i := 0;
+ for(i = 0; i < len props; i++){
+ if(props[i] == nil)
+ empty = i;
+ else if(props[i].name == "arguments"){
+ oargs = props[i].val;
+ empty = i;
+ break;
+ }
+ }
+ if(i == len func.props){
+ if(empty == -1){
+ props = array[i+1] of ref Prop;
+ props[:] = func.props;
+ func.props = props;
+ empty = i;
+ }
+ props[empty] = ref Prop(DontDelete|DontEnum|ReadOnly, "arguments", nil);
+ }
+ props[empty].val = ref RefVal(objval(argobj));
+
+ #
+ #see section 10.1.3 page 33
+ # if multiple params share the same name, the last one takes effect
+ # vars don't override params of the same name, or earlier parms defs
+ #
+ actobj.props = array[] of {ref Prop(DontDelete, "arguments", ref RefVal(objval(argobj)))};
+
+ argobj.props = array[len args + 2] of {
+ ref Prop(DontEnum, "callee", ref RefVal(objval(func))),
+ ref Prop(DontEnum, "length", ref RefVal(numval(real len args))),
+ };
+
+ #
+ # instantiate the arguments by name in the activation object
+ # and by number in the arguments object, aliased to the same RefVal.
+ #
+ params := func.call.params;
+ for(i = 0; i < len args; i++){
+ rjv := ref RefVal(args[i]);
+ argobj.props[i+2] = ref Prop(DontEnum, string i, rjv);
+ if(i < len params)
+ fvarinstant(actobj, 1, DontDelete, params[i], rjv);
+ }
+ for(; i < len params; i++)
+ fvarinstant(actobj, 1, DontDelete, params[i], ref RefVal(undefined));
+
+ #
+ # instantiate the local variables defined within the function
+ #
+ vars := func.call.code.vars;
+ for(i = 0; i < len vars; i++)
+ valinstant(actobj, DontDelete, vars[i].name, undefined);
+
+ # NOTE: the treatment of scopechain here is wrong if nested functions are
+ # permitted. ECMA-262 currently does not support nested functions (so we
+ # are ok for now) - but other flavours of Javascript do.
+ # Difficulties are introduced by multiple execution contexts.
+ # e.g. in web browsers, one frame can ref a func in
+ # another frame (each frame has a distinct execution context), but the func
+ # ids must bind as if in original lexical context
+
+ osc := ex.scopechain;
+ ex.this = this;
+ ex.scopechain = actobj :: osc;
+ (k, v, nil) := exec(ex, func.call.code);
+ ex.scopechain = osc;
+
+ #
+ # i can find nothing in the docs which defines
+ # the value of a function call
+ # this seems like a reasonable definition
+ #
+ if (k == CThrow)
+ raise "throw";
+ if(!eval && k != CReturn || v == nil)
+ v = undefined;
+ r = valref(v);
+
+ props = func.props;
+ for(i = 0; i < len props; i++){
+ if(props[i] != nil && props[i].name == "arguments"){
+ if(oargs == nil)
+ props[i] = nil;
+ else
+ props[i].val = oargs;
+ break;
+ }
+ }
+
+ return r;
+}
+
+#
+# routines for instantiating variables
+#
+fvarinstant(o: ref Obj, force, attr: int, s: string, v: ref RefVal)
+{
+ props := o.props;
+ empty := -1;
+ for(i := 0; i < len props; i++){
+ if(props[i] == nil)
+ empty = i;
+ else if(props[i].name == s){
+ if(force){
+ props[i].attr = attr;
+ props[i].val = v;
+ }
+ return;
+ }
+ }
+ if(empty == -1){
+ props = array[i+1] of ref Prop;
+ props[:] = o.props;
+ o.props = props;
+ empty = i;
+ }
+ props[empty] = ref Prop(attr, s, v);
+}
+
+varinstant(o: ref Obj, attr: int, s: string, v: ref RefVal)
+{
+ fvarinstant(o, 0, attr, s, v);
+}
+
+valinstant(o: ref Obj, attr: int, s: string, v: ref Val)
+{
+ fvarinstant(o, 0, attr, s, ref RefVal(v));
+}
+
+#
+# instantiate global or val variables
+# note that only function variables are forced to be redefined;
+# all other variables have a undefined val.val field
+#
+globalinstant(o: ref Obj, vars: array of ref Prop)
+{
+ for(i := 0; i < len vars; i++){
+ force := vars[i].val.val != undefined;
+ fvarinstant(o, force, 0, vars[i].name, vars[i].val);
+ }
+}
+
+numval(r: real): ref Val
+{
+ return ref Val(TNum, r, nil, nil, nil);
+}
+
+strval(s: string): ref Val
+{
+ return ref Val(TStr, 0., s, nil, nil);
+}
+
+objval(o: ref Obj): ref Val
+{
+ return ref Val(TObj, 0., nil, o, nil);
+}
+
+regexpval(p: string, f: string, i: int): ref Val
+{
+ return ref Val(TRegExp, 0., nil, nil, ref REval(p, f, i));
+}
+
+#
+# operations on refereneces
+# note the substitution of nil for an object
+# version of null, implied in the discussion of
+# Reference Types, since there isn't a null object
+#
+valref(v: ref Val): ref Ref
+{
+ return ref Ref(0, v, nil, nil);
+}
+
+getBase(ex: ref Exec, r: ref Ref): ref Obj
+{
+ if(!r.isref)
+ runtime(ex, ReferenceError, "not a reference");
+ return r.base;
+}
+
+getPropertyName(ex: ref Exec, r: ref Ref): string
+{
+ if(!r.isref)
+ runtime(ex, ReferenceError, "not a reference");
+ return r.name;
+}
+
+getValue(ex: ref Exec, r: ref Ref): ref Val
+{
+ if(!r.isref)
+ return r.val;
+ b := r.base;
+ if(b == nil)
+ runtime(ex, ReferenceError, "reference " + r.name + " is null");
+ return esget(ex, b, r.name, 0);
+}
+
+putValue(ex: ref Exec, r: ref Ref, v: ref Val)
+{
+ if(!r.isref)
+ runtime(ex, ReferenceError, "not a reference: " + r.name);
+ b := r.base;
+ if(b == nil)
+ b = ex.global;
+ esput(ex, b, r.name, v, 0);
+}
+
+#
+# conversion routines defined by the abstract machine
+# see section 9.
+# note that string, boolean, and number objects are
+# not automaically coerced to values, and vice versa.
+#
+toPrimitive(ex: ref Exec, v: ref Val, ty: int): ref Val
+{
+ if(v.ty != TObj)
+ return v;
+ v = esdefaultval(ex, v.obj, ty, 0);
+ if(v.ty == TObj)
+ runtime(ex, TypeError, "toPrimitive returned an object");
+ return v;
+}
+
+toBoolean(ex: ref Exec, v: ref Val): ref Val
+{
+ case v.ty{
+ TUndef or
+ TNull =>
+ return false;
+ TBool =>
+ return v;
+ TNum =>
+ if(isnan(v.num))
+ return false;
+ if(v.num == 0.)
+ return false;
+ TStr =>
+ if(v.str == "")
+ return false;
+ TObj =>
+ break;
+ TRegExp =>
+ break;
+ * =>
+ runtime(ex, TypeError, "unknown type in toBoolean");
+ }
+ return true;
+}
+
+toNumber(ex: ref Exec, v: ref Val): real
+{
+ case v.ty{
+ TUndef =>
+ return NaN;
+ TNull =>
+ return 0.;
+ TBool =>
+ if(v == false)
+ return 0.;
+ return 1.;
+ TNum =>
+ return v.num;
+ TStr =>
+ (si, r) := parsenum(ex, v.str, 0, ParseReal|ParseHex|ParseTrim|ParseEmpty);
+ if(si != len v.str)
+ r = Math->NaN;
+ return r;
+ TObj =>
+ return toNumber(ex, toPrimitive(ex, v, TNum));
+ TRegExp =>
+ return NaN;
+ * =>
+ runtime(ex, TypeError, "unknown type in toNumber");
+ return 0.;
+ }
+}
+
+toInteger(ex: ref Exec, v: ref Val): real
+{
+ r := toNumber(ex, v);
+ if(isnan(r))
+ return 0.;
+ if(r == 0. || r == +Infinity || r == -Infinity)
+ return r;
+ return copysign(floor(fabs(r)), r);
+}
+
+#
+# toInt32 == toUint32, except for numbers > 2^31
+#
+toInt32(ex: ref Exec, v: ref Val): int
+{
+ r := toNumber(ex, v);
+ if(isnan(r) || r == 0. || r == +Infinity || r == -Infinity)
+ return 0;
+ r = copysign(floor(fabs(r)), r);
+ # need to convert to big since it might be unsigned
+ return int big fmod(r, 4294967296.);
+}
+
+toUint32(ex: ref Exec, v: ref Val): big
+{
+ r := toNumber(ex, v);
+ if(isnan(r) || r == 0. || r == +Infinity || r == -Infinity)
+ return big 0;
+ r = copysign(floor(fabs(r)), r);
+ # need to convert to big since it might be unsigned
+ b := big fmod(r, 4294967296.);
+ if(b < big 0)
+ fatal(ex, "uint32 < 0");
+ return b;
+}
+
+toUint16(ex: ref Exec, v: ref Val): int
+{
+ return toInt32(ex, v) & 16rffff;
+}
+
+toString(ex: ref Exec, v: ref Val): string
+{
+ case v.ty{
+ TUndef =>
+ return "undefined";
+ TNull =>
+ return "null";
+ TBool =>
+ if(v == false)
+ return "false";
+ return "true";
+ TNum =>
+ r := v.num;
+ if(isnan(r))
+ return "NaN";
+ if(r == 0.)
+ return "0";
+ if(r == Infinity)
+ return "Infinity";
+ if(r == -Infinity)
+ return "-Infinity";
+ # this is wrong, but right is too hard
+ if(r < 1000000000000000000000. && r >= 1./(1000000.)){
+ return string r;
+ }
+ return string r;
+ TStr =>
+ return v.str;
+ TObj =>
+ return toString(ex, toPrimitive(ex, v, TStr));
+ TRegExp =>
+ return "/" + v.rev.p + "/" + v.rev.f;
+ * =>
+ runtime(ex, TypeError, "unknown type in ToString");
+ return "";
+ }
+}
+
+toObject(ex: ref Exec, v: ref Val): ref Obj
+{
+ case v.ty{
+ TUndef =>
+ runtime(ex, TypeError, "can't convert undefined to an object");
+ TNull =>
+ runtime(ex, TypeError, "can't convert null to an object");
+ TBool or
+ TStr or
+ TNum or
+ TRegExp =>
+ return coerceToObj(ex, v).obj;
+ TObj =>
+ return v.obj;
+ * =>
+ runtime(ex, TypeError, "unknown type in toObject");
+ return nil;
+ }
+ return nil;
+}
diff --git a/appl/lib/ecmascript/pprint.b b/appl/lib/ecmascript/pprint.b
new file mode 100644
index 00000000..01fd0640
--- /dev/null
+++ b/appl/lib/ecmascript/pprint.b
@@ -0,0 +1,378 @@
+PPrint: adt
+{
+ ex: ref Exec;
+ code: ref Code;
+ stack: array of string;
+ sp: int;
+};
+
+mkpprint(ex: ref Exec, code: ref Code): ref PPrint
+{
+ return ref PPrint(ex, code, array[4] of string, 0);
+}
+
+funcprint(ex: ref Exec, func: ref Ecmascript->Obj): string
+{
+ params := func.call.params;
+ (nil, name) := str->splitr(func.val.str, ".");
+ s := "function " + name + "(";
+ sep := "";
+ for(i := 0; i < len params; i++){
+ s += sep + params[i];
+ sep = ", ";
+ }
+ s += "){";
+ if(func.host != nil)
+ s += "[host code]";
+ else
+ s += "\n" + pprint(ex, func.call.code, " ");
+ s += "}";
+ return s;
+}
+
+pprint(ex: ref Exec, code: ref Code, indent: string): string
+{
+ pp := ref PPrint(ex, code, array[4] of string, 0);
+#for(i:=0; i < code.npc; i++) sys->print("%d: %d\n", i, int code.ops[i]);
+ s := pstmt(pp, 0, code.npc, indent);
+
+ if(pp.sp != 0)
+ fatal(ex, "pprint stack not balanced");
+
+ return s;
+}
+
+pstmt(pp: ref PPrint, pc, epc: int, indent: string): string
+{
+ e, e1, e2: string;
+ c, apc: int;
+
+ code := pp.code;
+ s := "";
+ while(pc < epc){
+ op := int code.ops[pc++];
+ while(op == Llabel){
+ (pc, c) = getconst(code.ops, pc);
+ s += code.strs[c] + ":\n";
+ op = int code.ops[pc++];
+ }
+ s += indent;
+ case op{
+ Lbreak or
+ Lcontinue or
+ Lreturn =>
+ s += tokname(op);
+ if(op == Lreturn){
+ (pc, e) = pexp(pp, pc, code.npc);
+ s += " " + e;
+ }
+ s += ";\n";
+ Lbreaklab or
+ Lcontinuelab =>
+ s += tokname(op);
+ (pc, c) = getconst(code.ops, pc);
+ s += " " + code.strs[c] + ";\n";
+ '{' =>
+ (pc, apc) = getjmp(code.ops, pc);
+ s += "{\n" + pstmt(pp, pc, apc, indent+" ") + indent + "}\n";
+ pc = apc;
+ Lif or
+ Lwith or
+ Lwhile =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, e) = pexp(pp, pc, apc);
+ (pc, apc) = getjmp(code.ops, pc);
+ s += tokname(op) + "(" + e + "){\n";
+ s += pstmt(pp, pc, apc, indent+" ");
+ if(op == Lif){
+ (pc, apc) = getjmp(code.ops, apc);
+ if(pc != apc)
+ s += indent + "}else{\n";
+ s += pstmt(pp, pc, apc, indent+" ");
+ }
+ s += indent + "}\n";
+ pc = apc;
+ Ldo =>
+ (pc, apc) = getjmp(code.ops, pc);
+ e = pstmt(pp, pc, apc, indent+" ");
+ (pc, apc) = getjmp(code.ops, apc);
+ (pc, e1) = pexp(pp, pc, apc);
+ s += "do{\n" + e + indent + "}(while(" + e1 + ");\n";
+ pc = apc;
+ Lfor or
+ Lforvar or
+ Lforin or
+ Lforvarin =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, e) = pexp(pp, pc, apc);
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, e1) = pexp(pp, pc, apc);
+ s += "for(";
+ if(op == Lforvar || op == Lforvarin)
+ s += "var ";
+ s += e;
+ if(op == Lfor || op == Lforvar){
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, e2) = pexp(pp, pc, apc);
+ s += "; " + e1 + "; " + e2;
+ }else
+ s += " in " + e1;
+ s += "){\n";
+ (pc, apc) = getjmp(code.ops, pc);
+ s += pstmt(pp, pc, apc, indent+" ");
+ s += indent + "}\n";
+ pc = apc;
+ ';' =>
+ s += ";\n";
+ Lvar =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, e) = pexp(pp, pc, apc);
+ s += "var " + e + ";\n";
+ Lswitch =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, e) = pexp(pp, pc, apc);
+ s += "switch (" + e + ") {\n";
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, e) = pcaseblk(pp, pc, apc, indent);
+ s += e + indent + "}\n";
+ pc = apc;
+ Lthrow =>
+ (pc, e) = pexp(pp, pc, code.npc);
+ s += "throw " + e + "\n";
+ Ltry =>
+ s += "try\n";
+ (pc, apc) = getjmp(code.ops, pc);
+ s += pstmt(pp, pc, apc, indent+" ");
+ (pc, apc) = getjmp(code.ops, apc);
+ if(pc != apc){
+ (pc, c) = getconst(code.ops, ++pc);
+ s += "catch(" + code.strs[c] + ")\n";
+ s += pstmt(pp, pc, apc, indent+" ");
+ }
+ (pc, apc) = getjmp(code.ops, apc);
+ if(pc != apc){
+ s += "finally\n";
+ s += pstmt(pp, pc, apc, indent+" ");
+ }
+ pc = apc;
+ * =>
+ (pc, e) = pexp(pp, pc-1, code.npc);
+ s += e + ";\n";
+ }
+ }
+ return s;
+}
+
+pexp(pp: ref PPrint, pc, epc: int): (int, string)
+{
+ c, apc: int;
+ s, f, a, a1, a2: string;
+
+ code := pp.code;
+ savesp := pp.sp;
+out: while(pc < epc){
+ case op := int code.ops[pc++]{
+ Lthis =>
+ s = "this";
+ Lid or
+ Lnum or
+ Lstr or
+ Lregexp =>
+ (pc, c) = getconst(code.ops, pc);
+ if(op == Lnum)
+ s = string code.nums[c];
+ else{
+ s = code.strs[c];
+ if(op == Lstr)
+ s = "\""+escstr(code.strs[c])+"\"";
+ }
+ '*' or
+ '/' or
+ '%' or
+ '+' or
+ '-' or
+ Llsh or
+ Lrsh or
+ Lrshu or
+ '<' or
+ '>' or
+ Lleq or
+ Lgeq or
+ Lin or
+ Linstanceof or
+ Leq or
+ Lneq or
+ Lseq or
+ Lsne or
+ '&' or
+ '^' or
+ '|' or
+ '=' or
+ '.' or
+ ',' or
+ '[' =>
+ a2 = ppop(pp);
+ a1 = ppop(pp);
+ s = tokname(op);
+ if(a1[0] == '='){
+ s += "=";
+ a1 = a1[1:];
+ }
+ if(op == '[')
+ s = a1 + "[" + a2 + "]";
+ else{
+ if(op != '.'){
+ if(op != ',')
+ s = " " + s;
+ s = s + " ";
+ }
+ s = a1 + s + a2;
+ }
+ Ltypeof or
+ Ldelete or
+ Lvoid or
+ Lnew or
+ Linc or
+ Ldec or
+ Lpreadd or
+ Lpresub or
+ '~' or
+ '!' or
+ Lpostinc or
+ Lpostdec =>
+ a = ppop(pp);
+ s = tokname(op);
+ if(op == Lpostinc || op == Lpostdec)
+ s = a + s;
+ else{
+ if(op == Ltypeof || op == Ldelete || op == Lvoid || op == Lnew)
+ s += " ";
+ s += a;
+ }
+ '(' =>
+ s = "(";
+ ')' =>
+ s = ppop(pp);
+ if(ppop(pp) != "(")
+ fatal(pp.ex, "unbalanced () in pexp");
+ s = "(" + s + ")";
+ Lgetval or
+ Las =>
+ continue;
+ Lasop =>
+ s = "=" + ppop(pp);
+ Lcall or
+ Lnewcall =>
+ (pc, c) = getconst(code.ops, pc);
+ a = "";
+ sep := "";
+ for(sp := pp.sp-c; sp < pp.sp; sp++){
+ a += sep + pp.stack[sp];
+ sep = ", ";
+ }
+ pp.sp -= c;
+ f = ppop(pp);
+ if(op == Lnewcall)
+ f = "new " + f;
+ s = f + "(" + a + ")";
+ ';' =>
+ break out;
+ Landand or
+ Loror or
+ '?' =>
+ s = ppop(pp);
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, a1) = pexp(pp, pc, apc);
+ s += " " + tokname(op) + " " + a1;
+ if(op == '?'){
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, a2) = pexp(pp, pc, apc);
+ s += " : "+ a2;
+ }
+ * =>
+ fatal(pp.ex, "pexp: unknown op " + tokname(op));
+ }
+ ppush(pp, s);
+ }
+
+ if(savesp == pp.sp)
+ return (pc, "");
+
+ if(savesp != pp.sp-1)
+ fatal(pp.ex, "unbalanced stack in pexp");
+ return (pc, ppop(pp));
+}
+
+pcaseblk(pp: ref PPrint, pc, epc: int, indent: string): (int, string)
+{
+ code := pp.code;
+ defpc, clausepc, nextpc, apc: int;
+ s, a: string;
+
+ (pc, defpc) = getjmp(code.ops, pc);
+ clausepc = pc;
+ (pc, nextpc) = getjmp(code.ops, pc);
+ for (; pc < epc; (clausepc, (pc, nextpc)) = (nextpc, getjmp(code.ops, nextpc))) {
+ if (clausepc == defpc) {
+ s += indent + "default:\n";
+ } else {
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, a) = pexp(pp, pc, apc);
+ s += indent + "case " + a + ":\n";
+ }
+ s += pstmt(pp, pc, nextpc, indent+"\t");
+ }
+ return (epc, s);
+}
+
+ppush(pp: ref PPrint, s: string)
+{
+ if(pp.sp >= len pp.stack){
+ st := array[2 * len pp.stack] of string;
+ st[:] = pp.stack;
+ pp.stack = st;
+ }
+ pp.stack[pp.sp++] = s;
+}
+
+ppop(pp: ref PPrint): string
+{
+ if(pp.sp == 0)
+ fatal(pp.ex, "popping too far off the pstack");
+ return pp.stack[--pp.sp];
+}
+
+unescmap := array[128] of
+{
+ '\'' => byte '\'',
+ '"' => byte '"',
+ '\\' => byte '\\',
+ '\b' => byte 'b',
+ '\u000c' => byte 'f',
+ '\n' => byte 'n',
+ '\r' => byte 'r',
+ '\t' => byte 't',
+
+ * => byte 0
+};
+
+escstr(s: string): string
+{
+ n := len s;
+ sb := "";
+ for(i := 0; i < n; i++){
+ c := s[i];
+ if(c < 128 && (e := int unescmap[c])){
+ sb[len sb] = '\\';
+ sb[len sb] = e;
+ }else if(c > 128 || c < 32){
+ sb += "\\u0000";
+ for(j := 1; j <= 4; j++){
+ sb[len sb - j] = "0123456789abcdef"[c & 16rf];
+ c >>= 4;
+ }
+ }else
+ sb[len sb] = c;
+ }
+ return sb;
+}
diff --git a/appl/lib/ecmascript/regexp.b b/appl/lib/ecmascript/regexp.b
new file mode 100644
index 00000000..945e0cc5
--- /dev/null
+++ b/appl/lib/ecmascript/regexp.b
@@ -0,0 +1,1286 @@
+strhas(s: string, c: int): ref Val
+{
+ for(i := 0; i < len s; i++)
+ if(s[i] == c)
+ return true;
+ return false;
+}
+
+rsplit(r: string): (string, string)
+{
+ esc := 0;
+ i := 1; # skip '/'
+ for(;;){
+ c := r[i++];
+ if(!esc && c == '/')
+ break;
+ esc = !esc && c == '\\';
+ }
+ return (r[1: i-1], r[i: ]);
+}
+
+badflags(f: string): int
+{
+ g := i := m := 0;
+ for(j := 0; j < len f; j++){
+ case(f[j]){
+ 'g' =>
+ g++;
+ 'i' =>
+ i++;
+ 'm' =>
+ m++;
+ * =>
+ return 1;
+ }
+ }
+ return g > 1 || i > 1 || m > 1;
+}
+
+regexpvals(ex: ref Exec, v: ref Val, o: ref Ecmascript->Obj): (string, string, int)
+{
+ if(v != nil){
+ if(v.ty == TRegExp)
+ return (v.rev.p, v.rev.f, v.rev.i);
+ o = v.obj;
+ }
+ p := toString(ex, esget(ex, o, "source", 0));
+ f := "";
+ if(toBoolean(ex, esget(ex, o, "global", 0)) == true)
+ f += "g";
+ if(toBoolean(ex, esget(ex, o, "ignoreCase", 0)) == true)
+ f += "i";
+ if(toBoolean(ex, esget(ex, o, "multiline", 0)) == true)
+ f += "m";
+ i := toInt32(ex, esget(ex, o, "lastIndex", 0));
+ return (p, f, i);
+}
+
+nregexp(ex: ref Exec, nil: ref Ecmascript->Obj, args: array of ref Val): ref Ecmascript->Obj
+{
+ pat := biarg(args, 0);
+ flags := biarg(args, 1);
+ (p, f) := ("", "");
+ if(isregexp(pat)){
+ if(flags == undefined)
+ (p, f, nil) = regexpvals(ex, pat, nil);
+ else
+ runtime(ex, TypeError, "flags defined");
+ }
+ else{
+ if(pat == undefined)
+ p = "";
+ else
+ p = toString(ex, pat);
+ if(flags == undefined)
+ f = "";
+ else
+ f = toString(ex, flags);
+ }
+ o := nobj(ex, nil, array[] of { regexpval(p, f, 0) });
+ if(badflags(f))
+ runtime(ex, SyntaxError, "bad regexp flags");
+ regex = ex;
+ (re, err) := compile(p, 1);
+ if(re == nil || err != nil)
+ runtime(ex, SyntaxError, "bad regexp pattern");
+ o.re = re;
+ return o;
+}
+
+cregexp(ex: ref Exec, f, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ pat := biarg(args, 0);
+ flags := biarg(args, 1);
+ if(isregexp(pat) && flags == undefined)
+ return pat;
+ return objval(nregexp(ex, f, args));
+}
+
+cregexpprotoexec(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ m: array of (int, int);
+
+ regexpcheck(ex, this, f);
+ s := toString(ex, biarg(args, 0));
+ l := len s;
+ i := toInt32(ex, esget(ex, this, "lastIndex", 0));
+ e := 0;
+ glob := esget(ex, this, "global", 0);
+ multiline := esget(ex, this, "multiline", 0);
+ ignorecase := esget(ex, this, "ignoreCase", 0);
+ if(glob == false)
+ i = 0;
+ for(;;){
+ if(i < 0 || i >= l){
+ esput(ex, this, "lastIndex", numval(real 0), 0);
+ return null;
+ }
+ regex = ex;
+ m = executese(this.re, s, (i, len s), i == 0, 1, multiline == true, ignorecase == true);
+ if(m != nil)
+ break;
+ i++;
+ i = -1; # no need to loop with executese
+ }
+ (i, e) = m[0];
+ if(glob == true)
+ esput(ex, this, "lastIndex", numval(real e), 0);
+ n := len m;
+ av := array[n] of ref Val;
+ for(j := 0; j < n; j++){
+ (a, b) := m[j];
+ if(a < 0)
+ av[j] = undefined;
+ else
+ av[j] = strval(s[a: b]);
+ }
+ a := narray(ex, nil, av);
+ esput(ex, a, "index", numval(real i), 0);
+ esput(ex, a, "input", strval(s), 0);
+ return objval(a);
+}
+
+cregexpprototest(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ regexpcheck(ex, this, f);
+ v := cregexpprotoexec(ex, f, this, args);
+ if(!isnull(v))
+ return true;
+ return false;
+}
+
+cregexpprototoString(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
+{
+ regexpcheck(ex, this, f);
+ (p, fl, nil) := regexpvals(ex, nil, this);
+ return strval("/" + p + "/" + fl);
+}
+
+regexpcheck(ex: ref Exec, o: ref Ecmascript->Obj, f: ref Obj)
+{
+ if(f == nil)
+ s := "exec";
+ else
+ s = f.val.str;
+ if(!isregexpobj(o))
+ runtime(ex, TypeError, "RegExp.prototype." + s + " called on non-RegExp object");
+}
+
+cstrprotomatch(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ v := biarg(args, 0);
+ if(!isregexp(v))
+ re := nregexp(ex, nil, args);
+ else if(v.ty == TObj)
+ re = v.obj;
+ else
+ re = nobj(ex, nil, args);
+ s := toString(ex, this.val);
+ glob := esget(ex, re, "global", 0);
+ av := array[1] of ref Val;
+ av[0] = strval(s);
+ if(glob == false)
+ return cregexpprotoexec(ex, nil, re, av);
+ li := 0;
+ esput(ex, re, "lastIndex", numval(real li), 0);
+ ms: list of ref Val;
+ for(;;){
+ v = cregexpprotoexec(ex, nil, re, av);
+ if(isnull(v))
+ break;
+ ms = esget(ex, v.obj, "0", 0) :: ms;
+ ni := int toUint32(ex, esget(ex, re, "lastIndex", 0));
+ if(ni == li)
+ esput(ex, re, "lastIndex", numval(real ++li), 0);
+ else
+ li = ni;
+ }
+ n := len ms;
+ av = array[n] of ref Val;
+ for(j := n-1; j >= 0; j--){
+ av[j] = hd ms;
+ ms = tl ms;
+ }
+ return objval(narray(ex, nil, av));
+}
+
+cstrprotoreplace(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ re: ref Ecmascript->Obj;
+
+ v := biarg(args, 0);
+ rege := isregexp(v);
+ if(!rege){
+ if(args == nil)
+ re = nregexp(ex, nil, args);
+ else
+ re = nregexp(ex, nil, args[0:1]);
+ }
+ else if(v.ty == TObj)
+ re = v.obj;
+ else
+ re = nobj(ex, nil, args);
+ s := toString(ex, this.val);
+ if(rege)
+ glob := esget(ex, re, "global", 0);
+ else
+ glob = false;
+ av := array[1] of ref Val;
+ av[0] = strval(s);
+ ms: list of ref Val;
+ li := 0;
+ if(glob == true)
+ esput(ex, re, "lastIndex", numval(real li), 0);
+ for(;;){
+ v = cregexpprotoexec(ex, nil, re, av);
+ if(!isnull(v))
+ ms = v :: ms;
+ if(isnull(v) || glob == false)
+ break;
+ ni := int toUint32(ex, esget(ex, re, "lastIndex", 0));
+ if(ni == li)
+ esput(ex, re, "lastIndex", numval(real ++li), 0);
+ else
+ li = ni;
+ }
+ if(ms == nil)
+ return strval(s);
+ ms = rev(ms);
+ if(rege)
+ lcp := int toUint32(ex, esget(ex, (hd ms).obj, "length", 0))-1;
+ else
+ lcp = 0;
+ v = biarg(args, 1);
+ if(isobj(v) && isfuncobj(v.obj)){
+ ns := s;
+ n := len ms;
+ args = array[lcp+3] of ref Val;
+ o := inc := 0;
+ for(i := 0; i < n; i++){
+ a := (hd ms).obj;
+ ms = tl ms;
+ for(j := 0; j <= lcp; j++)
+ args[j] = esget(ex, a, string j, 0);
+ ss := toString(ex, args[0]);
+ o = offset(ss, s, o);
+ args[lcp+1] = numval(real o);
+ args[lcp+2] = strval(s);
+ rs := toString(ex, getValue(ex, escall(ex, v.obj, nil, args, 0)));
+ ns = repl(ns, o+inc, o+inc+len ss, rs);
+ o += len ss;
+ inc += len rs - len ss;
+ }
+ return strval(ns);
+ }
+ else{
+ ps := toString(ex, v);
+ lps := len ps;
+ ns := s;
+ n := len ms;
+ o := inc := 0;
+ for(i := 0; i < n; i++){
+ a := (hd ms).obj;
+ ms = tl ms;
+ ss := toString(ex, esget(ex, a, "0", 0));
+ o = offset(ss, s, o);
+ rs := "";
+ for(j := 0; j < lps; j++){
+ if(ps[j] == '$' && j < lps-1){
+ j++;
+ case(c := ps[j]){
+ '$' =>
+ rs += "$";
+ '&' =>
+ rs += ss;
+ '`' =>
+ rs += s[0: o];
+ ''' =>
+ rs += s[o+len ss: ];
+ '0' to '9' =>
+ if(j < lps-1 && isdigit(ps[j+1]))
+ c = 10*(c-'0')+ps[++j]-'0';
+ else
+ c = c-'0';
+ if(c >= 1 && c <= lcp)
+ rs += toString(ex, esget(ex, a, string c, 0));
+ }
+ }
+ else
+ rs += ps[j: j+1];
+ }
+ ns = repl(ns, o+inc, o+inc+len ss, rs);
+ o += len ss;
+ inc += len rs - len ss;
+ }
+ return strval(ns);
+ }
+}
+
+cstrprotosearch(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ v := biarg(args, 0);
+ if(!isregexp(v))
+ re := nregexp(ex, nil, args);
+ else if(v.ty == TObj)
+ re = v.obj;
+ else
+ re = nobj(ex, nil, args);
+ s := toString(ex, this.val);
+ glob := esget(ex, re, "global", 0);
+ esput(ex, re, "global", false, 0);
+ av := array[1] of ref Val;
+ av[0] = strval(s);
+ v = cregexpprotoexec(ex, nil, re, av);
+ if(isnull(v))
+ r := -1;
+ else{
+ ss := toString(ex, esget(ex, v.obj, "0", 0));
+ r = offset(ss, s, 0);
+ }
+ esput(ex, re, "global", glob, 0);
+ return numval(real r);
+}
+
+offset(ss: string, s: string, m: int): int
+{
+ nn := len ss;
+ n := len s;
+ for(i := m; i <= n-nn; i++){
+ if(s[i: i+nn] == ss)
+ return i;
+ }
+ return -1;
+}
+
+repl(s: string, a: int, b: int, ns: string): string
+{
+ return s[0: a] + ns + s[b: ];
+}
+
+rev(ls: list of ref Val): list of ref Val
+{
+ ns: list of ref Val;
+
+ for( ; ls != nil; ls = tl ls)
+ ns = hd ls :: ns;
+ return ns;
+}
+
+#########################################################################
+# regex.b originally
+
+# normally imported identifiers
+
+# internal identifiers, not normally imported
+
+ALT, CAT, DOT, SET, HAT, DOL, NUL, PCLO, CLO, OPT, LPN, RPN, LPN0, RPN0, LPN1, RPN1, LPN2, RPN2, BEET, BEEF, MNCLO, LCP, IDLE: con (1<<16)+iota;
+
+# syntax
+
+# RE ALT regular expression
+# NUL
+# ALT CAT alternation
+# CAT | ALT
+#
+# CAT DUP catenation
+# DUP CAT
+#
+# DUP PRIM possibly duplicated primary
+# PCLO
+# CLO
+# OPT
+#
+# PCLO PRIM + 1 or more
+# CLO PRIM * 0 or more
+# OPT PRIM ? 0 or 1
+#
+# PRIM ( RE )
+# ()
+# DOT any character
+# CHAR a single character
+# ESC escape sequence
+# [ SET ] character set
+# NUL null string
+# HAT beginning of string
+# DOL end of string
+#
+
+regex: ref Exec;
+
+NIL : con -1; # a refRex constant
+NONE: con -2; # ditto, for an un-set value
+BAD: con 1<<16; # a non-character
+HUGE: con (1<<31) - 1;
+
+# the data structures of re.m would like to be ref-linked, but are
+# circular (see fn walk), thus instead of pointers we use indexes
+# into an array (arena) of nodes of the syntax tree of a regular expression.
+# from a storage-allocation standpoint, this replaces many small
+# allocations of one size with one big one of variable size.
+
+ReStr: adt {
+ s : string;
+ i : int; # cursor postion
+ n : int; # number of chars left; -1 on error
+ peek : fn(s: self ref ReStr): int;
+ next : fn(s: self ref ReStr): int;
+ unput: fn(s: self ref ReStr);
+};
+
+ReStr.peek(s: self ref ReStr): int
+{
+ if(s.n <= 0)
+ return BAD;
+ return s.s[s.i];
+}
+
+ReStr.next(s: self ref ReStr): int
+{
+ if(s.n <= 0)
+ syntax("bad regular expression");
+ s.n--;
+ return s.s[s.i++];
+}
+
+ReStr.unput(s: self ref ReStr)
+{
+ s.n++;
+ s.i--;
+}
+
+newRe(kind: int, left, right: refRex, set: ref Set, ar: ref Arena, pno: int, greedy: int): refRex
+{
+ ar.rex[ar.ptr] = Rex(kind, left, right, set, pno, greedy, nil);
+ return ar.ptr++;
+}
+
+# parse a regex by recursive descent to get a syntax tree
+
+re(s: ref ReStr, ar: ref Arena): refRex
+{
+ left := cat(s, ar);
+ if(left==NIL || s.peek()!='|')
+ return left;
+ s.next();
+ right := re(s, ar);
+ if(right == NIL)
+ return NIL;
+ return newRe(ALT, left, right, nil, ar, 0, 0);
+}
+
+cat(s: ref ReStr, ar: ref Arena): refRex
+{
+ left := dup(s, ar);
+ if(left == NIL)
+ return left;
+ right := cat(s, ar);
+ if(right == NIL)
+ return left;
+ return newRe(CAT, left, right, nil, ar, 0, 0);
+}
+
+dup(s: ref ReStr, ar: ref Arena): refRex
+{
+ n1, n2: int;
+
+ case s.peek() {
+ BAD or ')' or ']' or '|' or '?' or '*' or '+' =>
+ return NIL;
+ }
+ prim: refRex;
+ case kind:=s.next() {
+ '(' => if(ar.pno < 0) {
+ if(s.peek() == ')') {
+ s.next();
+ prim = newRe(NUL, NONE, NONE, nil, ar, 0, 0);
+ } else {
+ prim = re(s, ar);
+ if(prim==NIL || s.next()!=')')
+ syntax("( with no )");
+ }
+ } else {
+ pno := ++ar.pno;
+ lp := newRe(LPN, NONE, NONE, nil, ar, pno, 0);
+ rp := newRe(RPN, NONE, NONE, nil, ar, pno, 0);
+ if(s.peek() == ')') {
+ s.next();
+ prim = newRe(CAT, lp, rp, nil, ar, 0, 0);
+ } else {
+ if(s.peek() == '?'){
+ s.next();
+ case s.next(){
+ ':' => ar.rex[lp].kind = LPN0;
+ ar.rex[rp].kind = RPN0;
+ '=' => ar.rex[lp].kind = LPN1;
+ ar.rex[rp].kind = RPN1;
+ '!' => ar.rex[lp].kind = LPN2;
+ ar.rex[rp].kind = RPN2;
+ * => syntax("bad char after ?");
+ }
+ }
+ prim = re(s, ar);
+ if(prim==NIL || s.next()!=')')
+ syntax("( with no )");
+ else {
+ prim = newRe(CAT, prim, rp, nil, ar, 0, 0);
+ prim = newRe(CAT, lp, prim, nil, ar, 0, 0);
+ }
+ }
+ }
+ '[' => prim = newRe(SET, NONE, NONE, newSet(s), ar, 0, 0);
+ * => case kind {
+ '.' => kind = DOT;
+ '^' => kind = HAT;
+ '$' => kind = DOL;
+ }
+ (c, set, op) := esc(s, kind, 0);
+ if(set != nil)
+ prim = newRe(SET, NONE, NONE, set, ar, 0, 0);
+ else if(op == LCP){
+ if(c > ar.pno)
+ syntax("\num too big");
+ prim = newRe(LCP, NONE, NONE, nil, ar, 0, 0);
+ ar.rex[prim].ns = ref Nstate(c, c);
+ }
+ else
+ prim = newRe(c, NONE, NONE, nil, ar, 0, 0);
+ }
+ case s.peek() {
+ '*' => kind = CLO;
+ '+' => kind = PCLO;
+ '?' => kind = OPT;
+ '{' => s.next();
+ (n1, n2) = drange(s);
+ kind = MNCLO;
+ if(s.peek() != '}')
+ syntax("{ with no }");
+ * => return prim;
+ }
+ s.next();
+ greedy := 1;
+ if(s.peek() == '?'){
+ # non-greedy op
+ greedy = 0;
+ s.next();
+ }
+ prim = newRe(kind, prim, NONE, nil, ar, 0, greedy);
+ if(kind == MNCLO)
+ ns := ar.rex[prim].ns = ref Nstate(n1, n2);
+ return prim;
+}
+
+esc(s: ref ReStr, char: int, inset: int): (int, ref Set, int)
+{
+ set: ref Set;
+
+ op := 0;
+ if(char == '\\') {
+ char = s.next();
+ case char {
+ 'b' =>
+ if(inset)
+ char = '\b';
+ else
+ char = BEET;
+ 'B' => if(inset)
+ syntax("\\B in set");
+ else
+ char = BEEF;
+ 'f' => char = '\u000c';
+ 'n' => char = '\n';
+ 'r' => char = '\r';
+ 't' => char = '\t';
+ 'v' => char = '\v';
+ '0' to '9' =>
+ s.unput();
+ char = digits(s);
+ if(char == 0)
+ char = '\0';
+ else if(inset)
+ syntax("\num in set");
+ else
+ op = LCP;
+ 'x' => char = hexdigits(s, 2);
+ 'u' => char = hexdigits(s, 4);
+ 'c' => char = s.next()%32;
+ 'd' or 'D' =>
+ set = newset('0', '9');
+ if(char == 'D')
+ set.neg = 1;
+ 's' or 'S' =>
+ set = newset(' ', ' ');
+ addsets(set, "\t\v\u000c\u00a0\n\r\u2028\u2029");
+ if(char == 'S')
+ set.neg = 1;
+ 'w' or 'W' =>
+ set = newset('0', '9');
+ addset(set, 'a', 'z');
+ addset(set, 'A', 'Z');
+ addset(set, '_', '_');
+ if(char == 'W')
+ set.neg = 1;
+ * =>
+ ;
+ }
+ }
+ if(char == -1){
+ if(inset)
+ syntax("bad set");
+ else
+ syntax("bad character");
+ }
+ return (char, set, op);
+}
+
+isdigit(c: int): int
+{
+ return c >= '0' && c <= '9';
+}
+
+islower(c: int): int
+{
+ return c >= 'a' && c <= 'z';
+}
+
+isupper(c: int): int
+{
+ return c >= 'A' && c <= 'Z';
+}
+
+isalpha(c: int): int
+{
+ return islower(c) || isupper(c);
+}
+
+hexdigit(c: int): int
+{
+ if(isdigit(c))
+ return c-'0';
+ if('a' <= c && c <= 'f')
+ return c-'a'+10;
+ if('A' <= c && c <= 'F')
+ return c-'A'+10;
+ return -1;
+}
+
+digits(s: ref ReStr): int
+{
+ n := 0;
+ while(isdigit(s.peek()))
+ n = 10*n + s.next() -'0';
+ return n;
+}
+
+hexdigits(s: ref ReStr, n: int): int
+{
+ x := 0;
+ for(i := 0; i < n; i++){
+ v := hexdigit(s.next());
+ if(v < 0)
+ return -1;
+ x = 16*x+v;
+ }
+ return x;
+}
+
+drange(s: ref ReStr): (int, int)
+{
+ n1 := n2 := -1;
+ if(isdigit(s.peek()))
+ n1 = digits(s);
+ if(s.peek() == ','){
+ s.next();
+ if(isdigit(s.peek()))
+ n2 = digits(s);
+ else
+ n2 = HUGE;
+ }
+ else
+ n2 = n1;
+ if(n1 < 0 || n1 > n2)
+ syntax("bad number range");
+ return (n1, n2);
+}
+
+# walk the tree adjusting pointers to refer to
+# next state of the finite state machine
+
+walk(r: refRex, succ: refRex, ar: ref Arena)
+{
+ if(r==NONE)
+ return;
+ rex := ar.rex[r];
+ case rex.kind {
+ ALT => walk(rex.left, succ, ar);
+ walk(rex.right, succ, ar);
+ return;
+ CAT => walk(rex.left, rex.right, ar);
+ walk(rex.right, succ, ar);
+ ar.rex[r] = ar.rex[rex.left]; # optimization
+ return;
+ CLO or PCLO =>
+ end := newRe(OPT, r, succ, nil, ar, 0, rex.greedy); # here's the circularity
+ walk(rex.left, end, ar);
+ OPT => walk(rex.left, succ, ar);
+ MNCLO =>
+ ar.ptr++;
+ walk(rex.left, r, ar);
+ LCP =>
+ ar.rex[r].left = newRe(IDLE, NONE, succ, nil, ar, 0, 0);
+ }
+ ar.rex[r].right = succ;
+}
+
+prtree(r: refRex, ar: ref Arena, done: list of int, ind: string): list of int
+{
+ sys->print("%s", ind);
+ if(r==NIL){
+ sys->print("NIL\n");
+ return done;
+ }
+ if(r==NONE){
+ sys->print("NONE\n");
+ return done;
+ }
+ printed := 0;
+ for(li := done; li != nil; li = tl li){
+ if(hd li == r){
+ printed = 1;
+ break;
+ }
+ }
+ rex := ar.rex[r];
+ op := "";
+ z := "Z";
+ case rex.kind{
+ ALT => op = "|";
+ CAT => op = "and";
+ DOT => op = ".";
+ SET => op = "[]";
+ HAT => op = "^";
+ DOL => op = "$";
+ NUL => op = "NUL";
+ PCLO => op = "+";
+ CLO => op = "*";
+ OPT => op = "?";
+ LPN => op = "(";
+ RPN => op = ")";
+ LPN0 => op = "?:";
+ RPN0 => op = ":?";
+ LPN1 => op = "?=";
+ RPN1 => op = "=?";
+ LPN2 => op = "?!";
+ RPN2 => op = "!?";
+ BEET => op = "\\b";
+ BEEF => op = "\\B";
+ MNCLO => op = "{}";
+ LCP => op = "n";
+ IDLE => op = "i";
+ * => z[0] = rex.kind; op = z;
+ }
+ if(printed){
+ sys->print("node %d (%d)\n", r, r);
+ return done;
+ }
+ else{
+ if(rex.ns != nil)
+ sys->print("%s [%d-%d] (%d)\n", op, rex.ns.m, rex.ns.n, r);
+ else
+ sys->print("%s (%d)\n", op, r);
+ done = r :: done;
+ ind += " ";
+ done = prtree(rex.left, ar, done, ind);
+ done = prtree(rex.right, ar, done, ind);
+ return done;
+ }
+}
+
+compile(e: string, flag: int): (Re, string)
+{
+ if(e == nil)
+ return (nil, "missing expression");
+ s := ref ReStr(e, 0, len e);
+ ar := ref Arena(array[2*s.n] of Rex, 0, 0, (flag&1)-1);
+ start := ar.start = re(s, ar);
+ if(start==NIL || s.n!=0)
+ syntax("invalid regular expression");
+ walk(start, NIL, ar);
+ # prtree(start, ar, nil, "");
+ if(ar.pno < 0)
+ ar.pno = 0;
+ return (ar, nil);
+}
+
+# todo: queue for epsilon and advancing transitions
+
+Num: adt{
+ ns: ref Nstate;
+ m: int;
+ n: int;
+};
+Gaz: adt {
+ pno: int;
+ beg: int;
+ end: int;
+};
+Trace: adt {
+ cre: refRex; # cursor in Re
+ trans: int; # 0 epsilon transition, 1 advancing transition
+ beg: int; # where this trace began;
+ end: int; # where this trace ended if success (-1 by default)
+ gaz: list of Gaz;
+ ns: list of ref Num;
+};
+Queue: adt {
+ ptr: int;
+ q: array of Trace;
+};
+
+execute(re: Re, s: string): array of (int, int)
+{
+ return executese(re, s, (-1,-1), 1, 1, 1, 0);
+}
+
+executese(re: Re, s: string, range: (int, int), bol: int, eol: int, multiline: int, ignorecase: int): array of (int,int)
+{
+ if(re==nil)
+ return nil;
+ (s0, s1) := range;
+ if(s0 < 0)
+ s0 = 0;
+ if(s1 < 0)
+ s1 = len s;
+ match := 0;
+ todo := ref Queue(0, array[2*re.ptr] of Trace);
+ for(i:=s0; i<=s1; i++) {
+ if(!match) # no leftmost match yet
+ todo.q[todo.ptr++] = Trace(re.start, 0, i, -1, nil, nil);
+ for(k:=0; k<todo.ptr; k++) {
+ q := todo.q[k];
+ if(q.trans)
+ continue;
+ rex := re.rex[q.cre];
+ next0 := next1 := next2 := NONE;
+ case rex.kind {
+ NUL =>
+ next1 = rex.right;
+ DOT =>
+ if(i<len s && !islt(s[i]))
+ next2 = rex.right;
+ HAT =>
+ if(i == s0 && bol)
+ next1 = rex.right;
+ else if(multiline && i > 0 && islt(s[i-1]))
+ next1 = rex.right;
+ DOL =>
+ if(i == s1 && eol)
+ next1 = rex.right;
+ else if(multiline && i < s1 && islt(s[i]))
+ next1 = rex.right;
+ SET =>
+ if(i<len s && member(s[i], rex.set, ignorecase))
+ next2 = rex.right;
+ CAT or
+ PCLO =>
+ next1 = rex.left;
+ ALT or
+ CLO or
+ OPT =>
+ if(rex.kind == ALT || rex.greedy){
+ next0 = rex.left;
+ next1 = rex.right;
+ }
+ else{
+ next0 = rex.right;
+ next1 = rex.left;
+ }
+ LPN =>
+ next1 = rex.right;
+ q.gaz = Gaz(rex.pno,i,-1)::q.gaz;
+ RPN =>
+ next1 = rex.right;
+ for(r:=q.gaz; ; r=tl r) {
+ (pno,beg1,end1) := hd r;
+ if(rex.pno==pno && end1==-1) {
+ q.gaz = Gaz(pno,beg1,i)::q.gaz;
+ break;
+ }
+ }
+ LPN0 or RPN0 or RPN1 or RPN2 =>
+ next1 = rex.right;
+ LPN1 =>
+ (rpn, nxt, nre) := storetree(q.cre, re);
+ m := executese(nre, s, (i, -1), bol, eol, multiline, ignorecase);
+ if(m != nil && m[0].t0 == i){
+ next1 = nxt;
+ for(j := 1; j < len m; j++)
+ if(m[j].t0 >= 0)
+ q.gaz = Gaz(j, m[j].t0, m[j].t1)::q.gaz;
+ }
+ restoretree(LPN1, rpn, nxt, nre);
+ LPN2 =>
+ (rpn, nxt, nre) := storetree(q.cre, re);
+ m := executese(nre, s, (i, -1), bol, eol, multiline, ignorecase);
+ if(m == nil || m[0].t0 != i)
+ next1 = nxt;
+ restoretree(LPN2, rpn, nxt, nre);
+ MNCLO =>
+ num: ref Num;
+
+ (q.ns, num) = nextn(q.cre, q.ns, rex.ns.m, rex.ns.n, re);
+ if(num.m > 0)
+ next1 = rex.left;
+ else if(num.n > 0){
+ if(rex.greedy){
+ next0 = rex.left;
+ next1 = rex.right;
+ }
+ else{
+ next0 = rex.right;
+ next1 = rex.left;
+ }
+ }
+ else{
+ next1 = rex.right;
+ (num.m, num.n) = (-1, -1);
+ }
+ LCP =>
+ pno := rex.ns.m;
+ (beg1, end1) := lcpar(q.gaz, pno);
+ l := end1-beg1;
+ if(beg1 < 0) # undefined so succeeds
+ next1 = rex.right;
+ else if(i+l <= s1 && eqstr(s[beg1: end1], s[i: i+l], ignorecase)){
+ (q.ns, nil) = nextn(rex.left, q.ns, l, l, re);
+ next1 = rex.left; # idle
+ }
+ IDLE =>
+ num: ref Num;
+
+ (q.ns, num) = nextn(q.cre, q.ns, -1, -1, re);
+ if(num.m >= 0)
+ next2 = q.cre;
+ else{
+ next1 = rex.right;
+ (num.m, num.n) = (-1, -1);
+ }
+ BEET =>
+ if(iswordc(s, i-1) != iswordc(s, i))
+ next1 = rex.right;
+ BEEF =>
+ if(iswordc(s, i-1) == iswordc(s, i))
+ next1 = rex.right;
+ * =>
+ if(i<len s && (rex.kind==s[i] || (ignorecase && eqcase(rex.kind, s[i]))))
+ next2 = rex.right;
+ }
+ l := k;
+ if(next0 != NONE) {
+ if(next0 != NIL)
+ (k, l) = insert(next0, 0, q.beg, -1, q.gaz, q.ns, todo, k, l);
+ else{
+ match = 1;
+ (k, l) = insert(NIL, 2, q.beg, i, q.gaz, nil, todo, k, l);
+ }
+ }
+ if(next1 != NONE) {
+ if(next1 != NIL)
+ (k, l) = insert(next1, 0, q.beg, -1, q.gaz, q.ns, todo, k, l);
+ else{
+ match = 1;
+ (k, l) = insert(NIL, 2, q.beg, i, q.gaz, nil, todo, k, l);
+ }
+ }
+ if(next2 != NONE) {
+ if(next2 != NIL)
+ (k, l) = insert(next2, 1, q.beg, -1, q.gaz, q.ns, todo, k, l);
+ else{
+ match = 1;
+ (k, l) = insert(NIL, 2, q.beg, i+1, q.gaz, nil, todo, k, l);
+ }
+ }
+ }
+ if(!atoe(todo) && match)
+ break;
+ }
+ if(todo.ptr == 0)
+ return nil;
+ if(todo.ptr > 1)
+ rfatal(sys->sprint("todo.ptr = %d", todo.ptr));
+ if(todo.q[0].trans != 2)
+ rfatal(sys->sprint("trans = %d", todo.q[0].trans));
+ if(todo.q[0].cre != NIL)
+ rfatal(sys->sprint("cre = %d", todo.q[0].cre));
+ beg := todo.q[0].beg;
+ end := todo.q[0].end;
+ gaz := todo.q[0].gaz;
+ if(beg == -1)
+ return nil;
+ result := array[re.pno+1] of { 0 => (beg,end), * => (-1,-1) };
+ for( ; gaz!=nil; gaz=tl gaz) {
+ (pno, beg1, end1) := hd gaz;
+ (rbeg, nil) := result[pno];
+ if(rbeg==-1 && (beg1|end1)!=-1)
+ result[pno] = (beg1,end1);
+ }
+ return result;
+}
+
+better(newbeg, newend, oldbeg, oldend: int): int
+{
+ return oldbeg==-1 || newbeg<oldbeg ||
+ newbeg==oldbeg && newend>oldend;
+}
+
+insert(next: refRex, trans: int, tbeg: int, tend: int, tgaz: list of Gaz, tns: list of ref Num, todo: ref Queue, k: int, l: int): (int, int)
+{
+# sys->print("insert %d eps=%d beg=%d end=%d (k, l) = (%d %d) => ", next, trans, tbeg, tend, k, l);
+ for(j:=0; j<todo.ptr; j++){
+ if(todo.q[j].trans == trans){
+ if(todo.q[j].cre == next){
+ if(better(todo.q[j].beg, todo.q[j].end, tbeg, tend))
+ return (k, l);
+ else if(better(tbeg, tend, todo.q[j].beg, todo.q[j].end))
+ break;
+ else if(j < k)
+ return (k, l);
+ else
+ break;
+ }
+ }
+ }
+ if(j < k){
+ k--;
+ l--;
+ }
+ if(j < todo.ptr){
+ todo.q[j: ] = todo.q[j+1: todo.ptr];
+ todo.ptr--;
+ }
+ todo.q[l+2: ] = todo.q[l+1: todo.ptr];
+ todo.ptr++;
+ todo.q[l+1] = Trace(next, trans, tbeg, tend, tgaz, tns);
+# for(j=0; j < todo.ptr; j++) sys->print("%d(%d) ", todo.q[j].cre, todo.q[j].trans); sys->print("\n");
+ return (k, l+1);
+}
+
+# remove epsilon transitions and move advancing transitions to epsilon ones
+atoe(todo: ref Queue): int
+{
+ n := 0;
+ for(j := 0; j < todo.ptr; j++){
+ if(todo.q[j].trans){
+ if(todo.q[j].trans == 1){
+ todo.q[j].trans = 0;
+ n++;
+ }
+ }
+ else{
+ todo.q[j: ] = todo.q[j+1: todo.ptr];
+ todo.ptr--;
+ j--;
+ }
+ }
+ return n;
+}
+
+nextn(re: int, ln: list of ref Num, m: int, n: int, ar: ref Arena): (list of ref Num, ref Num)
+{
+ num: ref Num;
+
+ ns := ar.rex[re].ns;
+ for(l := ln; l != nil; l = tl l){
+ if((hd l).ns == ns){
+ num = hd l;
+ break;
+ }
+ }
+ if(num == nil)
+ ln = (num = ref Num(ns, -1, -1)) :: ln;
+ if(num.m == -1 && num.n == -1)
+ (num.m, num.n) = (m, n);
+ else
+ (nil, nil) = (--num.m, --num.n);
+ return (ln, num);
+}
+
+ASCII : con 128;
+WORD : con 32;
+
+mem(c: int, set: ref Set): int
+{
+ return (set.ascii[c/WORD]>>c%WORD)&1;
+}
+
+member(char: int, set: ref Set, ignorecase: int): int
+{
+ if(set.subset != nil){
+ for(l := set.subset; l != nil; l = tl l)
+ if(member(char, hd l, ignorecase))
+ return !set.neg;
+ }
+ if(char < 128){
+ if(ignorecase)
+ return (mem(tolower(char), set) || mem(toupper(char), set))^set.neg;
+ else
+ return ((set.ascii[char/WORD]>>char%WORD)&1)^set.neg;
+ }
+ for(l:=set.unicode; l!=nil; l=tl l) {
+ (beg, end) := hd l;
+ if(char>=beg && char<=end)
+ return !set.neg;
+ }
+ return set.neg;
+}
+
+newSet(s: ref ReStr): ref Set
+{
+ op: int;
+ set0: ref Set;
+
+ set := ref Set(0, array[ASCII/WORD] of {* => 0}, nil, nil);
+ if(s.peek() == '^') {
+ set.neg = 1;
+ s.next();
+ }
+ while(s.n > 0) {
+ char1 := s.next();
+ if(char1 == ']')
+ return set;
+ (char1, set0, op) = esc(s, char1, 1);
+ if(set0 != nil)
+ mergeset(set, set0);
+ char2 := char1;
+ if(s.peek() == '-') {
+ if(set0 != nil)
+ syntax("set in range");
+ s.next();
+ char2 = s.next();
+ if(char2 == ']')
+ break;
+ (char2, set0, op) = esc(s, char2, 1);
+ if(set0 != nil)
+ syntax("set in range");
+ if(char2 < char1)
+ break;
+ }
+ addset(set, char1, char2);
+ }
+ syntax("bad set");
+ return nil;
+}
+
+addset(set: ref Set, c1: int, c2: int)
+{
+ for(c := c1; c <= c2; c++){
+ if(c < ASCII)
+ set.ascii[c/WORD] |= 1<<c%WORD;
+ else{
+ set.unicode = (c, c2) :: set.unicode;
+ break;
+ }
+ }
+}
+
+addsets(set: ref Set, s: string)
+{
+ for(i := 0; i < len s; i++)
+ addset(set, s[i], s[i]);
+}
+
+mergeset(set: ref Set, set0: ref Set)
+{
+ if(!set0.neg){
+ for(i := 0; i < ASCII/WORD; i++)
+ set.ascii[i] |= set0.ascii[i];
+ for(l := set0.unicode; l != nil; l = tl l)
+ set.unicode = hd l :: set.unicode;
+ }
+ else
+ set.subset = set0 :: set.subset;
+}
+
+newset(c1: int, c2: int): ref Set
+{
+ set := ref Set(0, array[ASCII/WORD] of {* => 0}, nil, nil);
+ addset(set, c1, c2);
+ return set;
+}
+
+storetree(lpn: int, re: ref Arena): (int, int, ref Arena)
+{
+ rpn: int;
+
+ rex := re.rex[lpn];
+ k := rex.kind;
+ l := 1;
+ for(;;){
+ rpn = rex.right;
+ rex = re.rex[rpn];
+ if(rex.kind == k)
+ l++;
+ else if(rex.kind == k+1 && --l == 0)
+ break;
+ }
+ re.rex[lpn].kind = LPN;
+ re.rex[rpn].kind = RPN;
+ nxt := re.rex[rpn].right;
+ re.rex[rpn].right = NIL;
+ nre := ref *re;
+ nre.start = lpn;
+ return (rpn, nxt, nre);
+}
+
+restoretree(lop: int, rpn: int, nxt: int, re: ref Arena)
+{
+ lpn := re.start;
+ re.rex[lpn].kind = lop;
+ re.rex[rpn].kind = lop+1;
+ re.rex[rpn].right = nxt;
+}
+
+iswordc(s: string, i: int): int
+{
+ if(i < 0 || i >= len s)
+ return 0;
+ c := s[i];
+ return isdigit(c) || isalpha(c) || c == '_';
+}
+
+lcpar(gaz: list of Gaz, pno: int): (int, int)
+{
+ for(r := gaz; r != nil; r = tl r) {
+ (pno1, beg1, end1) := hd r;
+ if(pno == pno1)
+ return (beg1, end1);
+ }
+ return (-1, -1);
+}
+
+eqstr(s: string, t: string, ic: int): int
+{
+ if(!ic)
+ return s == t;
+ if(len s != len t)
+ return 0;
+ for(i := 0; i < len s; i++)
+ if(!eqcase(s[i], t[i]))
+ return 0;
+ return 1;
+}
+
+eqcase(c1: int, c2: int): int
+{
+ return toupper(c1) == toupper(c2);
+}
+
+syntax(s: string)
+{
+ runtime(regex, SyntaxError, s);
+}
+
+rfatal(s: string)
+{
+ runtime(regex, InternalError, s);
+}
diff --git a/appl/lib/ecmascript/uri.b b/appl/lib/ecmascript/uri.b
new file mode 100644
index 00000000..b571f286
--- /dev/null
+++ b/appl/lib/ecmascript/uri.b
@@ -0,0 +1,140 @@
+tohex(c: int): int
+{
+ if(c > 9)
+ return c-10+'A';
+ return c+'0';
+}
+
+fromhex(ex: ref Exec, c1: int, c2: int): int
+{
+ c1 = hexdigit(c1);
+ c2 = hexdigit(c2);
+ if(c1 < 0 || c2 < 0)
+ runtime(ex, URIError, "bad hex digit");
+ return 16*c1+c2;
+}
+
+isres(c: int): int
+{
+ return c == ';' || c == '/' || c == '?' || c == ':' || c == '@' || c == '&' || c == '=' || c == '+' || c == '$' || c == ',' || c == '#'; # add '#' here for convenience
+}
+
+isunesc(c: int): int
+{
+ return isalpha(c) || isdigit(c) || c == '-' || c == '_' || c == '.' || c == '!' || c == '~' || c == '*' || c == ''' || c == '(' || c == ')';
+}
+
+encode(ex: ref Exec, s: string, flag: int): string
+{
+ m := len s;
+ r := "";
+ n := len r;
+ for(k := 0; k < m; k++){
+ c := s[k];
+ if(isunesc(c) || (flag && isres(c)))
+ r[n++] = c;
+ else{
+ if(c >= 16rdc00 && c <= 16rdfff)
+ runtime(ex, URIError, "char out of range");
+ if(c < 16rd800 || c > 16rdbff)
+ ;
+ else{
+ if(++k == m)
+ runtime(ex, URIError, "char missing");
+ if(s[k] < 16rdc00 || s[k] > 16rdfff)
+ runtime(ex, URIError, "char out of range");
+ c = (c-16rd800)*16r400 + (s[k]-16rdc00) + 16r10000;
+ }
+ s1 := "Z";
+ s1[0] = c;
+ o := array of byte s1;
+ for(j := 0; j < len o; j++){
+ r += sys->sprint("%%%c%c", tohex(int o[j]/16), tohex(int o[j]%16));
+ n += 3;
+ }
+ }
+ }
+ return r;
+}
+
+decode(ex: ref Exec, s: string, flag: int): string
+{
+ m := len s;
+ r := "";
+ n := len r;
+ for(k := 0; k < m; k++){
+ c := s[k];
+ if(c != '%')
+ r[n++] = c;
+ else{
+ start := k;
+ if(k+2 >= m)
+ runtime(ex, URIError, "char missing");
+ c = fromhex(ex, s[k+1], s[k+2]);
+ k += 2;
+ if((c&16r80 == 0)){
+ if(flag && isres(c)){
+ r += s[start: k+1];
+ n += k+1-start;
+ }
+ else
+ r[n++] = c;
+ }
+ else{
+ for(i := 1; ((c<<i)&16r80) == 0; i++)
+ ;
+ if(i == 1 || i > 4)
+ runtime(ex, URIError, "bad hex number");
+ o := array[i] of byte;
+ o[0] = byte c;
+ if(k+3*(n-1) >= m)
+ runtime(ex, URIError, "char missing");
+ for(j := 1; j < i; j++){
+ if(s[++k] != '%')
+ runtime(ex, URIError, "% missing");
+ c = fromhex(ex, s[k+1], s[k+2]);
+ k += 2;
+ if((c&16rc0) != 2)
+ runtime(ex, URIError, "bad hex number");
+ o[j] = byte c;
+ }
+ (c, nil, nil) = sys->byte2char(o, 0);
+ if(c < 16r10000){
+ if(flag && isres(c)){
+ r += s[start: k+1];
+ n += k+1-start;
+ }
+ else
+ r[n++] = c;
+ }
+ else if(c > 16r10ffff)
+ runtime(ex, URIError, "bad byte sequence");
+ else{
+ r[n++] = ((c-16r10000)&16r3ff)+16rdc00;
+ r[n++] = (((c-16r10000)>>10)&16r3ff)+16rd800;
+ }
+ }
+ }
+ }
+ return r;
+}
+
+cdecodeuri(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ return strval(decode(ex, toString(ex, biarg(args, 0)), 1));
+}
+
+cdecodeuric(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ return strval(decode(ex, toString(ex, biarg(args, 0)), 0));
+}
+
+cencodeuri(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ return strval(encode(ex, toString(ex, biarg(args, 0)), 1));
+}
+
+cencodeuric(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
+{
+ return strval(encode(ex, toString(ex, biarg(args, 0)), 0));
+}
diff --git a/appl/lib/encoding/base16.b b/appl/lib/encoding/base16.b
new file mode 100644
index 00000000..6c91c200
--- /dev/null
+++ b/appl/lib/encoding/base16.b
@@ -0,0 +1,43 @@
+implement Encoding;
+
+include "encoding.m";
+
+hex: con "0123456789ABCDEF";
+
+enc(a: array of byte): string
+{
+ o: string;
+ for(i := 0; i < len a; i++){
+ n := int a[i];
+ o[len o] = hex[n>>4];
+ o[len o] = hex[n & 16rF];
+ }
+ return o;
+}
+
+dec(s: string): array of byte
+{
+ a := array[(len s+1)/2] of byte; # upper bound
+ o := 0;
+ j := 0;
+ n := 0;
+ for(i := 0; i < len s; i++){
+ c := s[i];
+ n <<= 4;
+ case c {
+ '0' to '9' =>
+ n |= c-'0';
+ 'A' to 'F' =>
+ n |= c-'A'+10;
+ 'a' to 'f' =>
+ n |= c-'a'+10;
+ * =>
+ continue;
+ }
+ if(++j == 2){
+ a[o++] = byte n;
+ j = n = 0;
+ }
+ }
+ return a[0:o];
+}
diff --git a/appl/lib/encoding/base32.b b/appl/lib/encoding/base32.b
new file mode 100644
index 00000000..d745bcc0
--- /dev/null
+++ b/appl/lib/encoding/base32.b
@@ -0,0 +1,60 @@
+implement Encoding;
+
+include "encoding.m";
+
+b32: con "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+
+enc(a: array of byte): string
+{
+ if(len a == 0)
+ return "========";
+ out := "";
+ nbit := len a * 8;
+ for(bit := 0; bit < nbit; bit += 5){
+ b := bit >> 3;
+ r := bit & 7;
+ v := int a[b] << r;
+ if(r > 3){
+ if(b+1 < len a)
+ v |= int (a[b+1] >> (8-r));
+ }
+ out[len out] = b32[(v>>3) & 16r1F];
+ }
+ while(len out & 7)
+ out[len out] = '='; # RFC3548 says pad: we pad.
+ return out;
+}
+
+Naughty: con 255;
+
+t32d := array[256] of {
+ 'a' => byte 0, 'b' => byte 1, 'c' => byte 2, 'd' => byte 3, 'e' => byte 4, 'f' => byte 5, 'g' => byte 6, 'h' => byte 7,
+ 'i' => byte 8, 'j' => byte 9, 'k' => byte 10, 'l' => byte 11, 'm' => byte 12, 'n' => byte 13, 'o' => byte 14, 'p' => byte 15,
+ 'q' => byte 16, 'r' => byte 17, 's' => byte 18, 't' => byte 19, 'u' => byte 20, 'v' => byte 21, 'w' => byte 22, 'x' => byte 23,
+ 'y' => byte 24, 'z' => byte 25,
+ 'A' => byte 0, 'B' => byte 1, 'C' => byte 2, 'D' => byte 3, 'E' => byte 4, 'F' => byte 5, 'G' => byte 6, 'H' => byte 7,
+ 'I' => byte 8, 'J' => byte 9, 'K' => byte 10, 'L' => byte 11, 'M' => byte 12, 'N' => byte 13, 'O' => byte 14, 'P' => byte 15,
+ 'Q' => byte 16, 'R' => byte 17, 'S' => byte 18, 'T' => byte 19, 'U' => byte 20, 'V' => byte 21, 'W' => byte 22, 'X' => byte 23,
+ 'Y' => byte 24, 'Z' => byte 25,
+ '2' => byte 26, '3' => byte 27, '4' => byte 28, '5' => byte 29, '6' => byte 30, '7' => byte 31,
+ * => byte Naughty
+};
+
+dec(s: string): array of byte
+{
+ a := array[(8*len s + 4)/5] of byte;
+ o := 0;
+ v := 0;
+ j := 0;
+ for(i := 0; i < len s; i++){
+ if((c := s[i]) > 16rFF || (c = int t32d[c]) == Naughty)
+ continue;
+ v <<= 5;
+ v |= c;
+ if((j += 5) >= 8){
+ a[o++] = byte (v>>(j-8));
+ j -= 8;
+ }
+ }
+ return a[0:o];
+}
diff --git a/appl/lib/encoding/base32a.b b/appl/lib/encoding/base32a.b
new file mode 100644
index 00000000..8d44a344
--- /dev/null
+++ b/appl/lib/encoding/base32a.b
@@ -0,0 +1,57 @@
+implement Encoding;
+
+include "encoding.m";
+
+b32: con "23456789abcdefghijkmnpqrstuvwxyz";
+
+enc(a: array of byte): string
+{
+ if(len a == 0)
+ return "========";
+ out := "";
+ nbit := len a * 8;
+ for(bit := 0; bit < nbit; bit += 5){
+ b := bit >> 3;
+ r := bit & 7;
+ v := int a[b] << r;
+ if(r > 3){
+ if(b+1 < len a)
+ v |= int (a[b+1] >> (8-r));
+ }
+ out[len out] = b32[(v>>3) & 16r1F];
+ }
+ # RFC3548 says pad with =; this follows alternative tradition (a)
+ return out;
+}
+
+INVAL: con 255;
+
+t32d := array[256] of {
+ '2' => byte 0, '3' => byte 1, '4' => byte 2, '5' => byte 3, '6' => byte 4, '7' => byte 5, '8' => byte 6, '9' => byte 7,
+ 'a' => byte 8, 'b' => byte 9, 'c' => byte 10, 'd' => byte 11, 'e' => byte 12, 'f' => byte 13, 'g' => byte 14, 'h' => byte 15,
+ 'i' => byte 16, 'j' => byte 17, 'k' => byte 18, 'm' => byte 19, 'n' => byte 20, 'p' => byte 21, 'q' => byte 22, 'r' => byte 23,
+ 's' => byte 24, 't' => byte 25, 'u' => byte 26, 'v' => byte 27, 'w' => byte 28, 'x' => byte 29, 'y' => byte 30, 'z' => byte 31,
+ 'A' => byte 8, 'B' => byte 9, 'C' => byte 10, 'D' => byte 11, 'E' => byte 12, 'F' => byte 13, 'G' => byte 14, 'H' => byte 15,
+ 'I' => byte 16, 'J' => byte 17, 'K' => byte 18, 'M' => byte 19, 'N' => byte 20, 'P' => byte 21, 'Q' => byte 22, 'R' => byte 23,
+ 'S' => byte 24, 'T' => byte 25, 'U' => byte 26, 'V' => byte 27, 'W' => byte 28, 'X' => byte 29, 'Y' => byte 30, 'Z' => byte 31,
+ * => byte INVAL
+};
+
+dec(s: string): array of byte
+{
+ a := array[(8*len s + 4)/5] of byte;
+ o := 0;
+ v := 0;
+ j := 0;
+ for(i := 0; i < len s; i++){
+ if((c := s[i]) > 16rFF || (c = int t32d[c]) == INVAL)
+ continue;
+ v <<= 5;
+ v |= c;
+ if((j += 5) >= 8){
+ a[o++] = byte (v>>(j-8));
+ j -= 8;
+ }
+ }
+ return a[0:o];
+}
diff --git a/appl/lib/encoding/base64.b b/appl/lib/encoding/base64.b
new file mode 100644
index 00000000..43a59f81
--- /dev/null
+++ b/appl/lib/encoding/base64.b
@@ -0,0 +1,92 @@
+implement Encoding;
+
+include "encoding.m";
+
+enc(a: array of byte) : string
+{
+ n := len a;
+ if(n == 0)
+ return "";
+ out := "";
+ j := 0;
+ i := 0;
+ while(i < n) {
+ x := int a[i++] << 16;
+ if(i < n)
+ x |= (int a[i++]&255) << 8;
+ if(i < n)
+ x |= (int a[i++]&255);
+ out[j++] = c64(x>>18);
+ out[j++] = c64(x>>12);
+ out[j++] = c64(x>> 6);
+ out[j++] = c64(x);
+ }
+ nmod3 := n % 3;
+ if(nmod3 != 0) {
+ out[j-1] = '=';
+ if(nmod3 == 1)
+ out[j-2] = '=';
+ }
+ return out;
+}
+
+c64(c: int) : int
+{
+ v: con "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ return v[c&63];
+}
+
+INVAL: con byte 255;
+
+t64d := array[256] of {
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, byte 62,INVAL,INVAL,INVAL, byte 63,
+ byte 52, byte 53, byte 54, byte 55, byte 56, byte 57, byte 58, byte 59, byte 60, byte 61,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL, byte 0, byte 1, byte 2, byte 3, byte 4, byte 5, byte 6, byte 7, byte 8, byte 9, byte 10, byte 11, byte 12, byte 13, byte 14,
+ byte 15, byte 16, byte 17, byte 18, byte 19, byte 20, byte 21, byte 22, byte 23, byte 24, byte 25,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL, byte 26, byte 27, byte 28, byte 29, byte 30, byte 31, byte 32, byte 33, byte 34, byte 35, byte 36, byte 37, byte 38, byte 39, byte 40,
+ byte 41, byte 42, byte 43, byte 44, byte 45, byte 46, byte 47, byte 48, byte 49, byte 50, byte 51,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL
+};
+
+dec(s: string): array of byte
+{
+ b24 := 0;
+ i := 0;
+ out := array[(3*len s+3)/4] of byte; # upper bound, especially if s contains white space
+ o := 0;
+ for(n := 0; n < len s; n++){
+ if((c := s[n]) > 16rFF || (c = int t64d[c]) == int INVAL)
+ continue;
+ case i++ {
+ 0 =>
+ b24 = c<<18;
+ 1 =>
+ b24 |= c<<12;
+ 2 =>
+ b24 |= c<<6;
+ 3 =>
+ b24 |= c;
+ out[o++] = byte (b24>>16);
+ out[o++] = byte (b24>>8);
+ out[o++] = byte b24;
+ i = 0;
+ }
+ }
+ case i {
+ 2 =>
+ out[o++] = byte (b24>>16);
+ 3 =>
+ out[o++] = byte (b24>>16);
+ out[o++] = byte (b24>>8);
+ }
+ return out[0:o];
+}
diff --git a/appl/lib/encoding/mkfile b/appl/lib/encoding/mkfile
new file mode 100644
index 00000000..1d8c60d3
--- /dev/null
+++ b/appl/lib/encoding/mkfile
@@ -0,0 +1,16 @@
+<../../../mkconfig
+
+TARG=\
+ base16.dis\
+ base32.dis\
+ base32a.dis\
+ base64.dis\
+
+MODULES=\
+
+SYSMODULES= \
+ encoding.m\
+
+DISBIN=$ROOT/dis/lib/encoding
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/lib/env.b b/appl/lib/env.b
new file mode 100644
index 00000000..b2fa064c
--- /dev/null
+++ b/appl/lib/env.b
@@ -0,0 +1,91 @@
+implement Env;
+
+#
+# Copyright © 2000 Vita Nuova Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "bufio.m";
+include "readdir.m";
+include "env.m";
+
+ENVDIR : con "/env/";
+
+setenv(var: string, val: string): int
+{
+ init();
+ if (var == nil || !nameok(var)) {
+ sys->werrstr("bad variable name");
+ return -1;
+ }
+ if (val == nil) {
+ sys->remove(ENVDIR+var);
+ return 0;
+ }
+ fd := sys->create(ENVDIR+var, Sys->OWRITE, 8r600);
+ if (fd == nil)
+ return -1;
+ valb := array of byte val;
+ if (sys->write(fd, valb, len valb) != len valb)
+ return -1;
+ return 0;
+}
+
+getenv(var: string): string
+{
+ init();
+ if (var == nil || !nameok(var))
+ return nil;
+ fd := sys->open(ENVDIR+var, Sys->OREAD);
+ if (fd == nil)
+ return nil;
+ (ok, stat) := sys->fstat(fd);
+ if (ok == -1)
+ return nil;
+ buf := array[int stat.length] of byte;
+ n := sys->read(fd, buf, len buf);
+ if (n < 0)
+ return nil;
+ return string buf[0:n];
+}
+
+getall(): list of (string, string)
+{
+ readdir := load Readdir Readdir->PATH;
+ if (readdir == nil)
+ return nil;
+ (a, n) := readdir->init(ENVDIR,
+ Readdir->NONE | Readdir->COMPACT | Readdir->DESCENDING);
+ vl: list of (string, string);
+ for (i := 0; i < len a; i++)
+ vl = (a[i].name, getenv(a[i].name)) :: vl;
+ return vl;
+}
+
+# clone the current environment
+clone(): int
+{
+ init();
+ return sys->pctl(sys->FORKENV, nil);
+}
+
+new(): int
+{
+ init();
+ return sys->pctl(sys->NEWENV, nil);
+}
+
+init()
+{
+ if (sys == nil)
+ sys = load Sys Sys->PATH;
+}
+
+nameok(var: string): int
+{
+ for(i:=0; i<len var; i++)
+ if (var[i] == '/') return 0;
+ return 1;
+}
diff --git a/appl/lib/ether.b b/appl/lib/ether.b
new file mode 100644
index 00000000..98010499
--- /dev/null
+++ b/appl/lib/ether.b
@@ -0,0 +1,83 @@
+implement Ether;
+
+include "sys.m";
+ sys: Sys;
+
+include "ether.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+parse(s: string): array of byte
+{
+ a := array[Eaddrlen] of byte;
+ for(i := 0; i < len a; i++){
+ n: int;
+ (n, s) = hex(s);
+ if(n < 0){
+ sys->werrstr("invalid ether address");
+ return nil;
+ }
+ a[i] = byte n;
+ if(s != nil && s[0] == ':')
+ s = s[1:];
+ }
+ return a;
+}
+
+hex(s: string): (int, string)
+{
+ n := 0;
+ for(i := 0; i < len s && i < 2; i++){
+ if((c := s[i]) >= '0' && c <= '9')
+ c -= '0';
+ else if(c >= 'a' && c <= 'f')
+ c += 10 - 'a';
+ else if(c >= 'A' && c <= 'F')
+ c += 10 - 'A';
+ else if(c == ':')
+ break;
+ else
+ return (-1, s);
+ n = (n<<4) | c;
+ }
+ if(i == 0)
+ return (-1, s);
+ return (n, s[i:]);
+}
+
+text(a: array of byte): string
+{
+ if(len a < Eaddrlen)
+ return "<invalid>";
+ return sys->sprint("%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux",
+ int a[0], int a[1], int a[2], int a[3], int a[4], int a[5]);
+}
+
+addressof(dev: string): array of byte
+{
+ if(dev != nil && dev[0] != '/')
+ dev = "/net/"+dev;
+ fd := sys->open(dev+"/addr", Sys->OREAD);
+ if(fd == nil)
+ return nil;
+ buf := array[64] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n <= 0)
+ return nil;
+ if(n > 0 && buf[n-1] == byte '\n')
+ n--;
+ return parse(string buf[0:n]);
+}
+
+eqaddr(a: array of byte, b: array of byte): int
+{
+ if(len a != len b)
+ return 0;
+ for(i := 0; i < len a; i++)
+ if(a[i] != b[i])
+ return 0;
+ return 1;
+}
diff --git a/appl/lib/exception.b b/appl/lib/exception.b
new file mode 100644
index 00000000..f31fc6b4
--- /dev/null
+++ b/appl/lib/exception.b
@@ -0,0 +1,59 @@
+implement Exception;
+
+include "sys.m";
+ sys: Sys;
+include "exception.m";
+
+getexc(pid: int): (int, string, string)
+{
+ loadsys();
+ if(pid < 0)
+ pid = sys->pctl(0, nil);
+ f := "/prog/"+string pid+"/exception";
+ if((fd := sys->open(f, Sys->OREAD)) == nil)
+ return (0, nil, nil);
+ b := array[8192] of byte;
+ if((n := sys->read(fd, b, len b)) < 0)
+ return (0, nil, nil);
+ s := string b[0: n];
+ if(s == nil)
+ return (0, nil, nil);
+ (m, l) := sys->tokenize(s, " ");
+ if(m < 3)
+ return (0, nil, nil);
+ pc := int hd l; l = tl l;
+ mod := hd l; l = tl l;
+ exc := hd l; l = tl l;
+ for( ; l != nil; l = tl l)
+ exc += " " + hd l;
+ return (pc, mod, exc);
+}
+
+setexcmode(mode: int): int
+{
+ loadsys();
+ pid := sys->pctl(0, nil);
+ f := "/prog/" + string pid + "/ctl";
+ if(mode == NOTIFYLEADER)
+ return write(f, "exceptions notifyleader");
+ else if(mode == PROPAGATE)
+ return write(f, "exceptions propagate");
+ else
+ return -1;
+}
+
+loadsys()
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+}
+
+write(f: string, s: string): int
+{
+ if((fd := sys->open(f, Sys->OWRITE)) == nil)
+ return -1;
+ b := array of byte s;
+ if((n := sys->write(fd, b, len b)) != len b)
+ return -1;
+ return 0;
+}
diff --git a/appl/lib/factotum.b b/appl/lib/factotum.b
new file mode 100644
index 00000000..a2cec879
--- /dev/null
+++ b/appl/lib/factotum.b
@@ -0,0 +1,308 @@
+implement Factotum;
+
+#
+# client interface to factotum
+#
+# this is a near transliteration of Plan 9 code, subject to the Lucent Public License 1.02
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "string.m";
+
+include "factotum.m";
+
+debug := 0;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+setdebug(i: int)
+{
+ debug = i;
+}
+
+getaia(a: array of byte, n: int): (int, array of byte)
+{
+ if(len a - n < 2)
+ return (-1, nil);
+ c := (int a[n+1]<<8) | int a[n+0];
+ n += 2;
+ if(len a - n < c)
+ return (-1, nil);
+ b := array[c] of byte; # could avoid copy if known not to alias
+ b[0:] = a[n: n+c];
+ return (n+c, b);
+}
+
+getais(a: array of byte, n: int): (int, string)
+{
+ (n, a) = getaia(a, n);
+ return (n, string a);
+}
+
+Authinfo.unpack(a: array of byte): (int, ref Authinfo)
+{
+ ai := ref Authinfo;
+ n: int;
+ (n, ai.cuid) = getais(a, 0);
+ (n, ai.suid) = getais(a, n);
+ (n, ai.cap) = getais(a, n);
+ (n, ai.secret) = getaia(a, n);
+ if(n < 0)
+ return (-1, nil);
+ return (n, ai);
+}
+
+mount(fd: ref Sys->FD, mnt: string, flags: int, aname: string, keyspec: string): (int, ref Authinfo)
+{
+ ai: ref Authinfo;
+ afd := sys->fauth(fd, aname);
+ if(afd != nil){
+ ai = proxy(afd, sys->open("/mnt/factotum/rpc", Sys->ORDWR), "proto=p9any role=client "+keyspec);
+ if(debug && ai == nil){
+ sys->print("proxy failed: %r\n");
+ return (-1, nil);
+ }
+ }
+ return (sys->mount(fd, afd, mnt, flags, aname), ai);
+}
+
+dump(a: array of byte): string
+{
+ s := sys->sprint("[%d]", len a);
+ for(i := 0; i < len a; i++){
+ c := int a[i];
+ if(c >= ' ' && c <= 16r7E)
+ s += sys->sprint("%c", c);
+ else
+ s += sys->sprint("\\x%.2ux", c);
+ }
+ return s;
+}
+
+verbof(buf: array of byte): (string, array of byte)
+{
+ n := len buf;
+ for(i:=0; i<n && buf[i] != byte ' '; i++)
+ ;
+ s := string buf[0:i];
+ if(i < n)
+ i++;
+ buf = buf[i:];
+ case s {
+ "ok" or "error" or "done" or "phase" or
+ "protocol" or "needkey" or "toosmall" or "internal" =>
+ return (s, buf);
+ * =>
+ sys->werrstr(sys->sprint("malformed rpc response: %q", s));
+ return ("rpc failure", buf);
+ }
+}
+
+dorpc(fd: ref Sys->FD, verb: string, val: array of byte): (string, array of byte)
+{
+ (o, a) := rpc(fd, verb, val);
+ if(o != "needkey" && o != "badkey")
+ return (o, a);
+ return ("no key", a); # don't know how to get key
+}
+
+rpc(afd: ref Sys->FD, verb: string, a: array of byte): (string, array of byte)
+{
+ va := array of byte verb;
+ l := len va;
+ na := len a;
+ if(na+l+1 > AuthRpcMax){
+ sys->werrstr("rpc too big");
+ return ("toobig", nil);
+ }
+ buf := array[na+l+1] of byte;
+ buf[0:] = va;
+ buf[l] = byte ' ';
+ buf[l+1:] = a;
+ if(debug)
+ sys->print("rpc: ->%s %s\n", verb, dump(a));
+ if((n:=sys->write(afd, buf, len buf)) != len buf){
+ if(n >= 0)
+ sys->werrstr("rpc short write");
+ return ("rpc failure", nil);
+ }
+ buf = array[AuthRpcMax] of byte;
+ if((n=sys->read(afd, buf, len buf)) < 0){
+ if(debug)
+ sys->print("<- (readerr) %r\n");
+ return ("rpc failure", nil);
+ }
+ if(n < len buf)
+ buf[n] = byte 0;
+ buf = buf[0:n];
+
+ #
+ # Set error string for good default behavior.
+ #
+ s: string;
+ (t, r) := verbof(buf);
+ if(debug)
+ sys->print("<- %s %#q\n", t, dump(r));
+ case t {
+ "ok" or
+ "rpc failure" =>
+ ; # don't touch
+ "error" =>
+ if(len r == 0)
+ s = "unspecified rpc error";
+ else
+ s = sys->sprint("%s", string r);
+ "needkey" =>
+ s = sys->sprint("needkey %s", string r);
+ "badkey" =>
+ (nf, flds) := sys->tokenize(string r, "\n");
+ if(nf < 2)
+ s = sys->sprint("badkey %q", string r);
+ else
+ s = sys->sprint("badkey %q", hd tl flds);
+ break;
+ "phase" =>
+ s = sys->sprint("phase error: %q", string r);
+ * =>
+ s = sys->sprint("unknown rpc type %q (bug in rpc.c)", t);
+ }
+ if(s != nil)
+ sys->werrstr(s);
+ return (t, r);
+}
+
+Authinfo.read(fd: ref Sys->FD): ref Authinfo
+{
+ (o, a) := rpc(fd, "authinfo", nil);
+ if(o != "ok")
+ return nil;
+ (n, ai) := Authinfo.unpack(a);
+ if(n <= 0)
+ sys->werrstr("bad auth info from factotum");
+ return ai;
+}
+
+proxy(fd: ref Sys->FD, afd: ref Sys->FD, params: string): ref Authinfo
+{
+ readc := chan of (array of byte, chan of (int, string));
+ writec := chan of (array of byte, chan of (int, string));
+ donec := chan of (ref Authinfo, string);
+ spawn genproxy(readc, writec, donec, afd, params);
+ for(;;)alt{
+ (buf, reply) := <-readc =>
+ n := sys->read(fd, buf, len buf);
+ if(n == -1)
+ reply <-= (-1, sys->sprint("%r"));
+ else
+ reply <-= (n, nil);
+ (buf, reply) := <-writec =>
+ n := sys->write(fd, buf, len buf);
+ if(n == -1)
+ reply <-= (-1, sys->sprint("%r"));
+ else
+ reply <-= (n, nil);
+ (authinfo, err) := <-donec =>
+ if(authinfo == nil)
+ sys->werrstr(err);
+ return authinfo;
+ }
+}
+
+#
+# do what factotum says
+#
+genproxy(
+ readc: chan of (array of byte, chan of (int, string)),
+ writec: chan of (array of byte, chan of (int, string)),
+ donec: chan of (ref Authinfo, string),
+ afd: ref Sys->FD,
+ params: string)
+{
+ if(afd == nil){
+ donec <-= (nil, "no authentication fd");
+ return;
+ }
+
+ pa := array of byte params;
+ (o, a) := dorpc(afd, "start", pa);
+ if(o != "ok"){
+ donec <-= (nil, sys->sprint("proxy start: %r"));
+ return;
+ }
+
+ ai: ref Authinfo;
+ err: string;
+done:
+ for(;;){
+ (o, a) = dorpc(afd, "read", nil);
+ case o {
+ "done" =>
+ if(len a > 0 && a[0] == byte 'h' && string a == "haveai")
+ ai = Authinfo.read(afd);
+ else
+ ai = ref Authinfo; # auth succeeded but empty authinfo
+ break done;
+ "ok" =>
+ writec <-= (a[0:len a], reply := chan of (int, string));
+ (n, e) := <-reply;
+ if(n != len a){
+ err = "proxy write fd: "+e;
+ break done;
+ }
+ "phase" =>
+ buf := array[AuthRpcMax] of {* => byte 0};
+ n := 0;
+ for(;;){
+ (o, a) = dorpc(afd, "write", buf[0:n]);
+ if(o != "toosmall")
+ break;
+ c := int string a;
+ if(c > AuthRpcMax)
+ break;
+ readc <-= (buf[n:c], reply := chan of (int, string));
+ (m, e) := <-reply;
+ if(m <= 0){
+ err = e;
+ if(m == 0)
+ err = sys->sprint("proxy short read");
+ break done;
+ }
+ n += m;
+ }
+ if(o != "ok"){
+ err = sys->sprint("proxy rpc write: %r");
+ break done;
+ }
+ * =>
+ err = sys->sprint("proxy rpc: %r");
+ break done;
+ }
+ }
+ donec <-= (ai, err);
+}
+
+getuserpasswd(keyspec: string): (string, string)
+{
+ str := load String String->PATH;
+ if(str == nil)
+ return (nil, nil);
+ fd := sys->open("/mnt/factotum/rpc", Sys->ORDWR);
+ if(fd == nil)
+ return (nil, nil);
+ if(((o, a) := dorpc(fd, "start", array of byte keyspec)).t0 != "ok" ||
+ ((o, a) = dorpc(fd, "read", nil)).t0 != "ok"){
+ sys->werrstr("factotum: "+o);
+ return (nil, nil);
+ }
+ flds := str->unquoted(string a);
+ if(len flds != 2){
+ sys->werrstr("odd response from factotum");
+ return (nil, nil);
+ }
+ return (hd flds, hd tl flds);
+}
diff --git a/appl/lib/filepat.b b/appl/lib/filepat.b
new file mode 100644
index 00000000..adbf0e0a
--- /dev/null
+++ b/appl/lib/filepat.b
@@ -0,0 +1,169 @@
+implement Filepat;
+
+include "sys.m";
+ sys: Sys;
+
+include "readdir.m";
+ rdir: Readdir;
+
+include "filepat.m";
+
+expand(pat: string): list of string
+{
+ if(sys == nil){
+ sys = load Sys Sys->PATH;
+ }
+ if(rdir == nil){
+ rdir = load Readdir Readdir->PATH;
+ }
+ (nil, elem) := sys->tokenize(pat, "/");
+ if(elem == nil)
+ return filepat1(pat, nil, 0);
+
+ files: list of string;
+ if(pat[0] == '/')
+ files = "/" :: nil;
+
+ while(elem != nil){
+ files = filepat1(hd elem, files, tl elem!=nil);
+ if(files == nil)
+ break;
+ elem = tl elem;
+ }
+ return files;
+}
+
+filepat1(pat: string, files: list of string, mustbedir: int): list of string
+{
+ if(files == nil)
+ return filepatdir(pat, "", nil, mustbedir);
+
+ # reverse list; will rebuild in forward order
+ r: list of string;
+ while(files != nil){
+ r = hd files :: r;
+ files = tl files;
+ }
+ files = r;
+
+ nfiles: list of string = nil;
+ while(files != nil){
+ nfiles = filepatdir(pat, hd files, nfiles, mustbedir);
+ files = tl files;
+ }
+ return nfiles;
+}
+
+filepatdir(pat: string, dir: string, files: list of string, mustbedir: int): list of string
+{
+ if(pat=="." || pat=="..") {
+ if(dir=="/" || dir=="")
+ files = (dir + pat) :: files;
+ else
+ files = (dir + "/" + pat) :: files;
+ return files;
+ }
+ dirname := dir;
+ if(dir == "")
+ dirname = ".";
+ # sort into descending order means resulting list will ascend
+ (d, n) := rdir->init(dirname, rdir->NAME|rdir->DESCENDING|rdir->COMPACT);
+ if(d == nil)
+ return files;
+
+ # suppress duplicates
+ for(i:=1; i<n; i++)
+ if(d[i-1].name == d[i].name){
+ d[i-1:] = d[i:];
+ n--;
+ i--;
+ }
+
+ for(i=0; i<n; i++){
+ if(match(pat, d[i].name) && (mustbedir==0 || (d[i].mode&Sys->DMDIR))){
+ if(dir=="/" || dir=="")
+ files = (dir + d[i].name) :: files;
+ else
+ files = (dir + "/" + d[i].name) :: files;
+ }
+ }
+ return files;
+}
+
+match(pat, name: string): int
+{
+ n := 0;
+ p := 0;
+ while(p < len pat){
+ r := pat[p++];
+ case r{
+ '*' =>
+ pat = pat[p:];
+ if(len pat==0)
+ return 1;
+ for(; n<=len name; n++)
+ if(match(pat, name[n:]))
+ return 1;
+ return 0;
+ '[' =>
+ if(n == len name)
+ return 0;
+ s := name[n++];
+ matched := 0;
+ invert := 0;
+ first := 1;
+ esc: int;
+ while(p < len pat){
+ (p, r, esc) = char(pat, p);
+ if(first && !esc && r=='^'){
+ invert = 1;
+ first = 0;
+ continue;
+ }
+ first = 0;
+ if(!esc && r==']')
+ break;
+ lo, hi: int;
+ (p, lo, hi) = range(pat, p-1);
+ if(lo<=s && s<=hi)
+ matched = 1;
+ }
+ if(!(!esc && r==']') || invert==matched)
+ return 0;
+ '?' =>
+ if(n==len name)
+ return 0;
+ n++;
+ '\\' =>
+ if(n==len name || p==len pat || pat[p++]!=name[n++])
+ return 0;
+ * =>
+ if(n==len name || r!=name[n++])
+ return 0;
+ }
+ }
+ return n == len name;
+}
+
+# return character or range (a-z)
+range(pat: string, p: int): (int, int, int)
+{
+ (q, lo, nil) := char(pat, p);
+ (q1, hi, esc) := char(pat, q);
+ if(!esc && hi=='-'){
+ (q1, hi, nil) = char(pat, q1);
+ return (q1, lo, hi);
+ }
+ return (q, lo, lo);
+}
+
+# return possibly backslash-escaped next character
+char(pat: string, p: int): (int, int, int)
+{
+ if(p == len pat)
+ return (p, 0, -1);
+ r := pat[p++];
+ if(p==len pat || r!='\\')
+ return (p, r, 0);
+ return (p+1, pat[p], 1);
+}
diff --git a/appl/lib/format.b b/appl/lib/format.b
new file mode 100644
index 00000000..5bd5d918
--- /dev/null
+++ b/appl/lib/format.b
@@ -0,0 +1,147 @@
+implement Format;
+include "sys.m";
+ sys: Sys;
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "sexprs.m";
+ sexprs: Sexprs;
+ Sexp: import sexprs;
+include "format.m";
+
+# possible addition?
+# se2spec(se: list of ref Sexp): (array of Fmtspec, string)
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ sexprs = load Sexprs Sexprs->PATH;
+ sexprs->init();
+ bufio = load Bufio Bufio->PATH;
+}
+
+spec2se(spec: array of Fmtspec): list of ref Sexp
+{
+ l: list of ref Sexp;
+ for(i := len spec - 1; i >= 0; i--){
+ if((sp := spec[i]).fields != nil)
+ l = ref Sexp.List(ref Sexp.String(sp.name, nil) :: spec2se(sp.fields)) :: l;
+ else if(sp.name != nil)
+ l = ref Sexp.String(sp.name, nil) :: l;
+ }
+ return l;
+}
+
+spec2fmt(specs: array of Fmtspec): array of Fmt
+{
+ if(specs == nil)
+ return nil;
+ f := array[len specs] of Fmt;
+ for(i := 0; i < len specs; i++){
+ if(specs[i].name == nil)
+ f[i].kind = -1;
+ else
+ f[i] = (i, spec2fmt(specs[i].fields));
+ }
+ return f;
+}
+
+
+se2fmt(spec: array of Fmtspec, se: ref Sexp): (array of Fmt, string)
+{
+ if(!se.islist())
+ return (nil, "format must be a list");
+ return ses2fmt(spec, se.els());
+}
+
+ses2fmt(spec: array of Fmtspec, els: list of ref Sexp): (array of Fmt, string)
+{
+ a := array[len els] of Fmt;
+ for(i := 0; els != nil; els = tl els){
+ name := (hd els).op();
+ for(j := 0; j < len spec; j++)
+ if(spec[j].name == name)
+ break;
+ if(j == len spec)
+ return (nil, sys->sprint("format name %#q not found", name));
+ sp := spec[j];
+ if((hd els).islist() == 0)
+ a[i++] = Fmt(j, spec2fmt(sp.fields));
+ else if(sp.fields == nil)
+ return (nil, sys->sprint("unexpected list %#q", name));
+ else{
+ (f, err) := ses2fmt(sp.fields, (hd els).args());
+ if(f == nil)
+ return (nil, err);
+ a[i++] = Fmt(j, f);
+ }
+ }
+ return (a, nil);
+}
+
+rec2val(spec: array of Fmtspec, se: ref Sexprs->Sexp): (array of Fmtval, string)
+{
+ if(se.islist() == 0)
+ return (nil, "expected list of fields; got "+se.text());
+ els := se.els();
+ if(len els > len spec)
+ return (nil, sys->sprint("too many fields found, expected %d, got %s", len spec, se.text()));
+ a := array[len spec] of Fmtval;
+ err: string;
+ for(i := 0; i < len spec; i++){
+ f := spec[i];
+ if(f.name == nil)
+ continue;
+ if(els == nil)
+ return (nil, sys->sprint("too few fields found, expected %d, got %s", len spec, se.text()));
+ el := hd els;
+ if(f.fields == nil)
+ a[i].val = el;
+ else{
+ if(el.islist() == 0)
+ return (nil, "expected list of elements; got "+el.text());
+ vl := el.els();
+ a[i].recs = recs := array[len vl] of array of Fmtval;
+ for(j := 0; vl != nil; vl = tl vl){
+ (recs[j++], err) = rec2val(spec[i].fields, hd vl);
+ if(err != nil)
+ return (nil, err);
+ }
+ }
+ els = tl els;
+ }
+ return (a, nil);
+}
+
+Fmtval.text(v: self Fmtval): string
+{
+ return v.val.astext();
+}
+
+Fmtfile.new(spec: array of Fmtspec): Fmtfile
+{
+ return (spec, (ref Sexp.List(spec2se(spec))).pack());
+}
+
+Fmtfile.open(f: self Fmtfile, name: string): ref Bufio->Iobuf
+{
+ fd := sys->open(name, Sys->ORDWR);
+ if(fd == nil){
+ sys->werrstr(sys->sprint("open failed: %r"));
+ return nil;
+ }
+ if(sys->write(fd, f.descr, len f.descr) == -1){
+ sys->werrstr(sys->sprint("format write failed: %r"));
+ return nil;
+ }
+ sys->seek(fd, big 0, Sys->SEEKSTART);
+ return bufio->fopen(fd, Sys->OREAD);
+}
+
+Fmtfile.read(f: self Fmtfile, iob: ref Iobuf): (array of Fmtval, string)
+{
+ (se, err) := Sexp.read(iob);
+ if(se == nil)
+ return (nil, err);
+ return rec2val(f.spec, se);
+}
diff --git a/appl/lib/fsfilter.b b/appl/lib/fsfilter.b
new file mode 100644
index 00000000..ea4041e4
--- /dev/null
+++ b/appl/lib/fsfilter.b
@@ -0,0 +1,67 @@
+implement Fsfilter;
+
+#
+# Copyright © 2004 Vita Nuova Holdings Limited
+#
+
+include "sys.m";
+include "draw.m";
+include "sh.m";
+include "fslib.m";
+ Fschan, Next, Quit, Skip, Down: import Fslib;
+
+filter[T](t: T, src, dst: Fschan)
+ for{
+ T =>
+ query: fn(t: self T, d: ref Sys->Dir, name: string, depth: int): int;
+ }
+{
+ names: list of string;
+ name: string;
+ indent := 0;
+ myreply := chan of int;
+loop:
+ for(;;){
+ (d, reply) := <-src;
+ if(d.dir != nil){
+ p := name;
+ if(indent > 0){
+ if(p != nil && p[len p - 1] != '/')
+ p[len p] = '/';
+ }
+ if(t.query(d.dir, p + d.dir.name, indent) == 0 && indent > 0){
+ reply <-= Next;
+ continue;
+ }
+ }
+ dst <-= (d, myreply);
+ case reply <-= <-myreply {
+ Quit =>
+ break loop;
+ Next =>
+ if(d.dir == nil && d.data == nil){
+ if(--indent == 0)
+ break loop;
+ (name, names) = (hd names, tl names);
+ }
+ Skip =>
+ if(--indent == 0)
+ break loop;
+ (name, names) = (hd names, tl names);
+ Down =>
+ if(d.dir != nil){
+ names = name :: names;
+ if(d.dir.mode & Sys->DMDIR){
+ if(indent == 0)
+ name = d.dir.name;
+ else{
+ if(name[len name - 1] != '/')
+ name[len name] = '/';
+ name += d.dir.name;
+ }
+ }
+ indent++;
+ }
+ }
+ }
+}
diff --git a/appl/lib/fslib.b b/appl/lib/fslib.b
new file mode 100644
index 00000000..20dd6990
--- /dev/null
+++ b/appl/lib/fslib.b
@@ -0,0 +1,400 @@
+implement Fslib;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited
+#
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "fslib.m";
+
+# Fsdata stream conventions:
+#
+# Fsdata: adt {
+# dir: ref Sys->Dir;
+# data: array of byte;
+# };
+# Fschan: type chan of (Fsdata, chan of int);
+# c: Fschan;
+#
+# a stream of values sent on c represent the contents of a directory
+# hierarchy. after each value has been received, the associated reply
+# channel must be used to prompt the sender how next to proceed.
+#
+# the first item sent on an fsdata channel represents the root directory
+# (it must be a directory), and its name holds the full path of the
+# hierarchy that's being transferred. the items that follow represent
+# the contents of the root directory.
+#
+# the set of valid sequences of values can be described by a yacc-style
+# grammar, where the terminal tokens describe data values (Fsdata adts)
+# passed down the channel. this grammar describes the case where the
+# entire fs tree is traversed in its entirety:
+#
+# dir: DIR dircontents NIL
+# | DIR NIL
+# dircontents: entry
+# | dircontents entry
+# entry: FILE filecontents NIL
+# | FILE NIL
+# | dir
+# filecontents: DATA
+# | filecontents DATA
+#
+# the tests for the various terminal token types, given a token (of type
+# Fsdata) t:
+#
+# FILE t.dir != nil && (t.dir.mode & Sys->DMDIR) == 0
+# DIR t.dir != nil && (t.dir.mode & Sys->DMDIR)
+# DATA t.data != nil
+# NIL t.data == nil && t.dir == nil
+#
+# when a token is received, there are four possible replies:
+# Quit
+# terminate the stream immediately. no more tokens will
+# be on the channel.
+#
+# Down
+# descend one level in the hierarchy, if possible. the next tokens
+# will represent the contents of the current entry.
+#
+# Next
+# get the next entry in a directory, or the next data
+# block in a file, or travel one up the hierarchy if
+# it's the last entry or data block in that directory or file.
+#
+# Skip
+# skip to the end of a directory or file's contents.
+# if we're already at the end, this is a no-op (same as Next)
+#
+# grammar including replies is different. a token is the tuple (t, reply),
+# where reply is the value that was sent over the reply channel. Quit
+# always causes the grammar to terminate, so it is omitted for clarity.
+# thus there are 12 possible tokens (DIR_DOWN, DIR_NEXT, DIR_SKIP, FILE_DOWN, etc...)
+#
+# dir: DIR_DOWN dircontents NIL_NEXT
+# | DIR_DOWN dircontents NIL_SKIP
+# | DIR_DOWN dircontents NIL_DOWN
+# | DIR_NEXT
+# dircontents:
+# | FILE_SKIP
+# | DIR_SKIP
+# | file dircontents
+# | dir dircontents
+# file: FILE_DOWN filecontents NIL_NEXT
+# | FILE_DOWN filecontents NIL_SKIP
+# | FILE_DOWN filecontents NIL_DOWN
+# | FILE_NEXT
+# filecontents:
+# | data
+# | data DATA_SKIP
+# data: DATA_NEXT
+# | data DATA_NEXT
+#
+# both the producer and consumer of fs data on the channel must between
+# them conform to the second grammar. if a stream of fs data
+# is sent with no reply channel, the stream must conform to the first grammar.
+
+valuec := array[] of {
+ tagof(Value.V) => 'v',
+ tagof(Value.X) => 'x',
+ tagof(Value.P) => 'p',
+ tagof(Value.S) => 's',
+ tagof(Value.C) => 'c',
+ tagof(Value.T) => 't',
+ tagof(Value.M) => 'm',
+};
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+# copy the contents (not the entry itself) of a directory from src to dst.
+copy(src, dst: Fschan): int
+{
+ indent := 1;
+ myreply := chan of int;
+ for(;;){
+ (d, reply) := <-src;
+ dst <-= (d, myreply);
+ r := <-myreply;
+ case reply <-= r {
+ Quit =>
+ return Quit;
+ Next =>
+ if(d.dir == nil && d.data == nil)
+ if(--indent == 0)
+ return Next;
+ Skip =>
+ if(--indent == 0)
+ return Next;
+ Down =>
+ if(d.dir != nil || d.data != nil)
+ indent++;
+ }
+ }
+}
+
+Report.new(): ref Report
+{
+ r := ref Report(chan of string, chan of (string, chan of string), chan of int);
+ spawn reportproc(r.startc, r.enablec, r.reportc);
+ return r;
+}
+
+Report.start(r: self ref Report, name: string): chan of string
+{
+ if(r == nil)
+ return nil;
+ errorc := chan of string;
+ r.startc <-= (name, errorc);
+ return errorc;
+}
+
+Report.enable(r: self ref Report)
+{
+ r.enablec <-= 0;
+}
+
+reportproc(startc: chan of (string, chan of string), startreports: chan of int, errorc: chan of string)
+{
+ realc := array[2] of chan of string;
+ p := array[len realc] of string;
+ a := array[0] of chan of string;;
+
+ n := 0;
+ for(;;) alt{
+ (prefix, c) := <-startc =>
+ if(n == len realc){
+ realc = (array[n * 2] of chan of string)[0:] = realc;
+ p = (array[n * 2] of string)[0:] = p;
+ }
+ realc[n] = c;
+ p[n] = prefix;
+ n++;
+ <-startreports =>
+ if(n == 0){
+ errorc <-= nil;
+ exit;
+ }
+ a = realc;
+ (x, report) := <-a =>
+ if(report == nil){
+# errorc <-= "exit " + p[x];
+ --n;
+ if(n != x){
+ a[x] = a[n];
+ a[n] = nil;
+ p[x] = p[n];
+ p[n] = nil;
+ }
+ if(n == 0){
+ errorc <-= nil;
+ exit;
+ }
+ }else if(a == realc)
+ errorc <-= p[x] + ": " + report;
+ }
+}
+
+type2s(c: int): string
+{
+ case c{
+ 'a' =>
+ return "any";
+ 'x' =>
+ return "fs";
+ 's' =>
+ return "string";
+ 'v' =>
+ return "void";
+ 'p' =>
+ return "gate";
+ 'c' =>
+ return "command";
+ 't' =>
+ return "entries";
+ 'm' =>
+ return "selector";
+ * =>
+ return sys->sprint("unknowntype('%c')", c);
+ }
+}
+
+typeerror(tc: int, v: ref Value): string
+{
+ sys->fprint(sys->fildes(2), "fs: bad type conversion, expected %s, was actually %s\n", type2s(tc), type2s(valuec[tagof v]));
+ return "type conversion error";
+}
+
+Value.t(v: self ref Value): ref Value.T
+{
+ pick xv :=v {T => return xv;}
+ raise typeerror('t', v);
+}
+Value.c(v: self ref Value): ref Value.C
+{
+ pick xv :=v {C => return xv;}
+ raise typeerror('c', v);
+}
+Value.s(v: self ref Value): ref Value.S
+{
+ pick xv :=v {S => return xv;}
+ raise typeerror('s', v);
+}
+Value.p(v: self ref Value): ref Value.P
+{
+ pick xv :=v {P => return xv;}
+ raise typeerror('p', v);
+}
+Value.x(v: self ref Value): ref Value.X
+{
+ pick xv :=v {X => return xv;}
+ raise typeerror('x', v);
+}
+Value.v(v: self ref Value): ref Value.V
+{
+ pick xv :=v {V => return xv;}
+ raise typeerror('v', v);
+}
+Value.m(v: self ref Value): ref Value.M
+{
+ pick xv :=v {M => return xv;}
+ raise typeerror('m', v);
+}
+
+Value.typec(v: self ref Value): int
+{
+ return valuec[tagof v];
+}
+
+Value.discard(v: self ref Value)
+{
+ if(v == nil)
+ return;
+ pick xv := v {
+ X =>
+ (<-xv.i).t1 <-= Quit;
+ P =>
+ xv.i <-= (Nilentry, nil);
+ M =>
+ xv.i <-= (nil, nil, nil);
+ V =>
+ xv.i <-= 0;
+ T =>
+ xv.i.sync <-= 0;
+ }
+}
+
+sendnulldir(c: Fschan): int
+{
+ reply := chan of int;
+ c <-= ((ref Sys->nulldir, nil), reply);
+ if((r := <-reply) == Down){
+ c <-= ((nil, nil), reply);
+ if(<-reply != Quit)
+ return Quit;
+ return Next;
+ }
+ return r;
+}
+
+quit(errorc: chan of string)
+{
+ if(errorc != nil)
+ errorc <-= nil;
+ exit;
+}
+
+report(errorc: chan of string, err: string)
+{
+ if(errorc != nil)
+ errorc <-= err;
+}
+
+# true if a module with type sig t1 is compatible with a caller that expects t0
+typecompat(t0, t1: string): int
+{
+ (rt0, at0, ot0) := splittype(t0);
+ (rt1, at1, ot1) := splittype(t1);
+ if((rt0 != rt1 && rt0 != 'a') || at0 != at1) # XXX could do better for repeated args.
+ return 0;
+ for(i := 1; i < len ot0; i++){
+ for(j := i; j < len ot0; j++)
+ if(ot0[j] == '-')
+ break;
+ (ok, t) := opttypes(ot0[i], ot1);
+ if(ok == -1 || ot0[i:j] != t)
+ return 0;
+ i = j + 1;
+ }
+ return 1;
+}
+
+splittype(t: string): (int, string, string)
+{
+ if(t == nil)
+ return (-1, nil, nil);
+ for(i := 1; i < len t; i++)
+ if(t[i] == '-')
+ break;
+ return (t[0], t[1:i], t[i:]);
+}
+
+opttypes(opt: int, opts: string): (int, string)
+{
+ for(i := 1; i < len opts; i++){
+ if(opts[i] == opt && opts[i-1] == '-'){
+ for(j := i+1; j < len opts; j++)
+ if(opts[j] == '-')
+ break;
+ return (0, opts[i+1:j]);
+ }
+ }
+ return (-1, nil);
+}
+
+cmdusage(s, t: string): string
+{
+ if(s == nil)
+ return nil;
+ for(oi := 0; oi < len t; oi++)
+ if(t[oi] == '-')
+ break;
+ if(oi < len t){
+ single, multi: string;
+ for(i := oi; i < len t - 1;){
+ for(j := i + 1; j < len t; j++)
+ if(t[j] == '-')
+ break;
+
+ optargs := t[i+2:j];
+ if(optargs == nil)
+ single[len single] = t[i+1];
+ else{
+ multi += sys->sprint(" [-%c", t[i+1]);
+ for (k := 0; k < len optargs; k++)
+ multi += " " + type2s(optargs[k]);
+ multi += "]";
+ }
+ i = j;
+ }
+ if(single != nil)
+ s += " [-" + single + "]";
+ s += multi;
+ }
+ multi := 0;
+ if(oi > 2 && t[oi - 1] == '*'){
+ multi = 1;
+ oi -= 2;
+ }
+ for(k := 1; k < oi; k++)
+ s += " " + type2s(t[k]);
+ if(multi)
+ s += " [" + type2s(t[k]) + "...]";
+ s += " -> " + type2s(t[0]);
+ return s;
+}
diff --git a/appl/lib/fsproto.b b/appl/lib/fsproto.b
new file mode 100644
index 00000000..62a57512
--- /dev/null
+++ b/appl/lib/fsproto.b
@@ -0,0 +1,385 @@
+implement FSproto;
+
+include "sys.m";
+ sys: Sys;
+ Dir: import Sys;
+include "draw.m";
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "string.m";
+ str: String;
+include "readdir.m";
+ readdir: Readdir;
+include "fsproto.m";
+
+File: adt {
+ new: string;
+ elem: string;
+ old: string;
+ uid: string;
+ gid: string;
+ mode: int;
+};
+
+Proto: adt {
+ b: ref Iobuf;
+ doquote: int;
+ indent: int;
+ lineno: int;
+ newfile: string;
+ oldfile: string;
+ oldroot: string;
+ ec: chan of Direntry;
+ wc: chan of (string, string);
+
+ walk: fn(w: self ref Proto, f: ref File, level: int);
+ entry: fn(w: self ref Proto, old: string, new: string, d: ref Sys->Dir);
+ warn: fn(w: self ref Proto, s: string);
+ fatal: fn(w: self ref Proto, s: string);
+};
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ if(bufio == nil)
+ return sys->sprint("%r");
+ str = load String String->PATH;
+ if(str == nil)
+ return sys->sprint("%r");
+ readdir = load Readdir Readdir->PATH;
+ if(readdir == nil)
+ return sys->sprint("%r");
+ return nil;
+}
+
+readprotofile(proto: string, root: string, entries: chan of Direntry, warnings: chan of (string, string)): string
+{
+ b := bufio->open(proto, Sys->OREAD);
+ if(b == nil)
+ return sys->sprint("%r");
+ rdproto(b, root, entries, warnings);
+ return nil;
+}
+
+readprotostring(proto: string, root: string, entries: chan of Direntry, warnings: chan of (string, string))
+{
+ rdproto(bufio->sopen(proto), root, entries, warnings);
+}
+
+rdproto(b: ref Iobuf, root: string, entries: chan of Direntry, warnings: chan of (string, string)): string
+{
+ w := ref Proto;
+ w.b = b;
+ w.doquote = 1;
+ w.ec = entries;
+ w.wc = warnings;
+ w.oldroot = root;
+ w.lineno = 0;
+ w.indent = 0;
+ file := ref File;
+ file.mode = 0;
+ spawn walker(w, file);
+ return nil;
+}
+
+walker(w: ref Proto, file: ref File)
+{
+ w.walk(file, -1);
+ w.entry(nil, nil, nil);
+}
+
+Proto.entry(w: self ref Proto, old: string, new: string, d: ref Sys->Dir)
+{
+ if(w.ec != nil)
+ w.ec <-= (old, new, d);
+}
+
+Proto.warn(w: self ref Proto, s: string)
+{
+ if(w.wc != nil)
+ w.wc <-= (w.oldfile, s);
+ else
+ sys->fprint(sys->fildes(2), "warning: %s\n", s);
+}
+
+Proto.fatal(w: self ref Proto, s: string)
+{
+ if(w.wc != nil)
+ w.wc <-= (w.oldfile, s);
+ else
+ sys->fprint(sys->fildes(2), "fatal error: %s\n", s);
+ w.ec <-= (nil, nil, nil);
+ exit;
+}
+
+Proto.walk(w: self ref Proto, me: ref File, level: int)
+{
+ (child, fp) := getfile(w, me);
+ if(child == nil)
+ return;
+ if(child.elem == "+" || child.elem == "*" || child.elem == "%"){
+ rec := child.elem[0] == '+';
+ filesonly := child.elem[0] == '%';
+ child.new = me.new;
+ setnames(w, child);
+ mktree(w, child, rec, filesonly);
+ (child, fp) = getfile(w, me);
+ }
+ while(child != nil && w.indent > level){
+ if(mkfile(w, child))
+ w.walk(child, w.indent);
+ (child, fp) = getfile(w, me);
+ }
+ if(child != nil){
+ w.b.seek(big fp, 0);
+ w.lineno--;
+ }
+}
+
+mktree(w: ref Proto, me: ref File, rec: int, filesonly: int)
+{
+ fd := sys->open(w.oldfile, Sys->OREAD);
+ if(fd == nil){
+ w.warn(sys->sprint("can't open %s: %r", w.oldfile));
+ return;
+ }
+ child := ref *me;
+ (d, n) := readdir->init(w.oldfile, Readdir->NAME|Readdir->COMPACT);
+ for(i := 0; i < n; i++) {
+ if(filesonly && (d[i].mode & Sys->DMDIR))
+ continue;
+ child.new = mkpath(me.new, d[i].name);
+ if(me.old != nil)
+ child.old = mkpath(me.old, d[i].name);
+ child.elem = d[i].name;
+ setnames(w, child);
+ if(copyfile(w, child, d[i]) && rec)
+ mktree(w, child, rec, filesonly);
+ }
+}
+
+mkfile(w: ref Proto, f: ref File): int
+{
+ (i, dir) := sys->stat(w.oldfile);
+ if(i < 0){
+ w.warn(sys->sprint("can't stat file %s: %r", w.oldfile));
+ skipdir(w);
+ return 0;
+ }
+ return copyfile(w, f, ref dir);
+}
+
+copyfile(w: ref Proto, f: ref File, d: ref Dir): int
+{
+ d.name = f.elem;
+ if(f.mode != ~0){
+ if((d.mode&Sys->DMDIR) != (f.mode&Sys->DMDIR))
+ w.warn(sys->sprint("inconsistent mode for %s", f.new));
+ else
+ d.mode = f.mode;
+ }
+ w.entry(w.oldfile, w.newfile, d);
+ return (d.mode & Sys->DMDIR) != 0;
+}
+
+setnames(w: ref Proto, f: ref File)
+{
+ w.newfile = f.new;
+ if(f.old != nil){
+ if(f.old[0] == '/')
+ w.oldfile = mkpath(w.oldroot, f.old);
+ else
+ w.oldfile = f.old;
+ }else
+ w.oldfile = mkpath(w.oldroot, f.new);
+}
+
+#
+# skip all files in the proto that
+# could be in the current dir
+#
+skipdir(w: ref Proto)
+{
+ if(w.indent < 0)
+ return;
+ b := w.b;
+ level := w.indent;
+ for(;;){
+ w.indent = 0;
+ fp := b.offset();
+ p := b.gets('\n');
+ if(p != nil && p[len p - 1] != '\n')
+ p += "\n";
+ w.lineno++;
+ if(p == nil){
+ w.indent = -1;
+ return;
+ }
+ for(j := 0; (c := p[j++]) != '\n';)
+ if(c == ' ')
+ w.indent++;
+ else if(c == '\t')
+ w.indent += 8;
+ else
+ break;
+ if(w.indent <= level){
+ b.seek(fp, 0);
+ w.lineno--;
+ return;
+ }
+ }
+}
+
+getfile(w: ref Proto, old: ref File): (ref File, int)
+{
+ p, elem: string;
+ c: int;
+
+ if(w.indent < 0)
+ return (nil, 0);
+ b := w.b;
+ fp := int b.offset();
+ do {
+ w.indent = 0;
+ p = b.gets('\n');
+ if(p != nil && p[len p - 1] != '\n')
+ p += "\n";
+ w.lineno++;
+ if(p == nil){
+ w.indent = -1;
+ return (nil, 0);
+ }
+ for(; (c = p[0]) != '\n'; p = p[1:])
+ if(c == ' ')
+ w.indent++;
+ else if(c == '\t')
+ w.indent += 8;
+ else
+ break;
+ } while(c == '\n' || c == '#');
+ (elem, p) = getname(w, p);
+ if(p == nil)
+ return (nil, 0);
+ f := ref File;
+ f.new = mkpath(old.new, elem);
+ (nil, f.elem) = str->splitr(f.new, "/");
+ if(f.elem == nil)
+ w.fatal(sys->sprint("can't find file name component of %s", f.new));
+ (f.mode, p) = getmode(w, p);
+ if(p == nil)
+ return (nil, 0);
+ (f.uid, p) = getname(w, p);
+ if(p == nil)
+ return (nil, 0);
+ if(f.uid == nil)
+ f.uid = "-";
+ (f.gid, p) = getname(w, p);
+ if(p == nil)
+ return (nil, 0);
+ if(f.gid == nil)
+ f.gid = "-";
+ f.old = getpath(p);
+ if(f.old == "-")
+ f.old = nil;
+ if(f.old == nil && old.old != nil)
+ f.old = mkpath(old.old, elem);
+ setnames(w, f);
+ return (f, fp);
+}
+
+getpath(p: string): string
+{
+ for(; (c := p[0]) == ' ' || c == '\t'; p = p[1:])
+ ;
+ for(n := 0; (c = p[n]) != '\n' && c != ' ' && c != '\t'; n++)
+ ;
+ return p[0:n];
+}
+
+getname(w: ref Proto, p: string): (string, string)
+{
+ for(; (c := p[0]) == ' ' || c == '\t'; p = p[1:])
+ ;
+ i := 0;
+ s := "";
+ quoted := 0;
+ for(; (c = p[0]) != '\n' && (c != ' ' && c != '\t' || quoted); p = p[1:]){
+ if(quoted && c == '\'' && p[1] == '\'')
+ p = p[1:];
+ else if(c == '\'' && w.doquote){
+ quoted = !quoted;
+ continue;
+ }
+ s[i++] = c;
+ }
+ if(len s > 0 && s[0] == '$'){
+ s = getenv(s[1:]);
+ if(s == nil)
+ w.warn(sys->sprint("can't read environment variable %s", s));
+ }
+ return (s, p);
+}
+
+getenv(s: string): string
+{
+ if(s == "user")
+ return readfile("/dev/user"); # more accurate?
+ return readfile("/env/"+s);
+}
+
+readfile(f: string): string
+{
+ fd := sys->open(f, Sys->OREAD);
+ if(fd != nil){
+ a := array[256] of byte;
+ n := sys->read(fd, a, len a);
+ if(n > 0)
+ return string a[0:n];
+ }
+ return nil;
+}
+
+getmode(w: ref Proto, p: string): (int, string)
+{
+ s: string;
+
+ (s, p) = getname(w, p);
+ if(s == nil || s == "-")
+ return (~0, p);
+ m := 0;
+ if(s[0] == 'd'){
+ m |= Sys->DMDIR;
+ s = s[1:];
+ }
+ if(s[0] == 'a'){
+ m |= Sys->DMAPPEND;
+ s = s[1:];
+ }
+ if(s[0] == 'l'){
+ m |= Sys->DMEXCL;
+ s = s[1:];
+ }
+ for(i:=0; i<len s || i < 3; i++)
+ if(i >= len s || !(s[i]>='0' && s[i]<='7')){
+ w.warn(sys->sprint("bad mode specification %s", s));
+ return (~0, p);
+ }
+ (v, nil) := str->toint(s, 8);
+ return (m|v, p);
+}
+
+mkpath(prefix, elem: string): string
+{
+ slash1 := slash2 := 0;
+ if(len prefix > 0)
+ slash1 = prefix[len prefix - 1] == '/';
+ if(len elem > 0)
+ slash2 = elem[0] == '/';
+ if(slash1 && slash2)
+ return prefix+elem[1:];
+ if(!slash1 && !slash2)
+ return prefix+"/"+elem;
+ return prefix+elem;
+}
diff --git a/appl/lib/gamer.b b/appl/lib/gamer.b
new file mode 100644
index 00000000..bb38ddbe
--- /dev/null
+++ b/appl/lib/gamer.b
@@ -0,0 +1,147 @@
+implement Gamer;
+
+include "sys.m";
+include "gamer.m";
+
+sys: Sys;
+FD, Connection: import Sys;
+
+devsysname: con "/dev/sysname";
+
+addr: string;
+stderr: ref FD;
+
+getaddr() : int
+{
+ f := sys->open(devsysname, sys->OREAD);
+ if (f == nil) {
+ sys->fprint(stderr, "open %s failed: %r\n", devsysname);
+ return -1;
+ }
+ buff := array[64] of byte;
+ n := sys->read(f, buff, len buff);
+ if (n < 0) {
+ sys->fprint(stderr, "read %s failed: %r\n", devsysname);
+ return -1;
+ }
+ addr = "tcp!" + string buff[0:n] + "!gamed";
+ return 0;
+}
+
+Join(game: string) : Game
+{
+ g: Game;
+
+ g.player = -1;
+ if(sys == nil) {
+ sys = load Sys Sys->PATH;
+ stderr = sys->fildes(2);
+
+ if (getaddr() < 0)
+ return g;
+ }
+
+ (ok, c) := sys->dial(addr, nil);
+ if (ok < 0) {
+ sys->fprint(stderr, "dial %s failed: %r\n", addr);
+ return g;
+ }
+
+ s := "join " + game;
+ b := array of byte s;
+ if (sys->write(c.dfd, b, len b) < 0) {
+ sys->fprint(stderr, "write %s failed: %r\n", addr);
+ return g;
+ }
+
+ buff := array[64] of byte;
+ n := sys->read(c.dfd, buff, len buff);
+ if (n < 0) {
+ sys->fprint(stderr, "read %s failed: %r\n", addr);
+ return g;
+ }
+ if (n == 0) {
+ sys->fprint(stderr, "eof on read %s\n", addr);
+ return g;
+ }
+ s = string buff[0:n];
+ if (s == "error") {
+ sys->fprint(stderr, "%s returns error\n", addr);
+ return g;
+ }
+ c.dfd = nil;
+ (t, l) := sys->tokenize(s, " \t\n");
+ if (t != 3) {
+ sys->fprint(stderr, "%s returns bad response\n", addr);
+ return g;
+ }
+ g.opponent = hd tl l;
+ player := int hd tl tl l;
+ s = "local " + s;
+
+ (ok, c) = sys->dial(addr, nil);
+ if (ok < 0) {
+ sys->fprint(stderr, "dial %s failed: %r\n", addr);
+ return g;
+ }
+ b = array of byte s;
+ if (sys->write(c.dfd, b, len b) < 0) {
+ sys->fprint(stderr, "write %s failed: %r\n", addr);
+ return g;
+ }
+ n = sys->read(c.dfd, buff, len buff);
+ if (n < 0) {
+ sys->fprint(stderr, "read %s failed: %r\n", addr);
+ return g;
+ }
+ g.wf = c.dfd;
+ if (n == 0) {
+ sys->fprint(stderr, "eof on read %s\n", addr);
+ return g;
+ }
+ s = string buff[0:n];
+ if (s == "error") {
+ sys->fprint(stderr, "%s returns error\n", addr);
+ return g;
+ }
+ g.rf = sys->open(s, sys->OREAD);
+ if (g.rf == nil) {
+ sys->fprint(stderr, "pipe open %s failed: %r\n", s);
+ return g;
+ }
+ g.player = player;
+ return g;
+}
+
+Game.In(g: self Game) : int
+{
+ buff := array[1] of byte;
+
+ if ((rd := sys->read(g.rf, buff, 1)) == 1)
+ return int buff[0];
+
+ if (rd < 0)
+ sys->fprint(stderr, "gamed read failed: %r\n");
+
+ g.rf = nil;
+ return -1;
+}
+
+Game.Out(g: self Game, i: int)
+{
+ buff := array[1] of byte;
+
+ buff[0] = byte i;
+ if (sys->write(g.wf, buff, 1) != 1) {
+ sys->fprint(stderr, "gamed write failed: %r\n");
+ g.wf = nil;
+ return;
+ }
+}
+
+Game.Exit(g: self Game)
+{
+ g.Out(255);
+ g.rf = nil;
+ g.wf = nil;
+}
diff --git a/appl/lib/hash.b b/appl/lib/hash.b
new file mode 100644
index 00000000..a039e2dc
--- /dev/null
+++ b/appl/lib/hash.b
@@ -0,0 +1,80 @@
+# ehg@research.bell-labs.com 14Dec1996
+implement Hash;
+
+include "hash.m";
+
+# from Aho Hopcroft Ullman
+fun1(s:string, n:int):int
+{
+ h := 0;
+ m := len s;
+ for(i:=0; i<m; i++){
+ h = 65599*h+s[i];
+ }
+ return (h & 16r7fffffff) % n;
+}
+
+# from Limbo compiler
+fun2(s:string, n:int):int
+{
+ h := 0;
+ m := len s;
+ for(i := 0; i < m; i++){
+ c := s[i];
+ d := c;
+ c ^= c << 6;
+ h += (c << 11) ^ (c >> 1);
+ h ^= (d << 14) + (d << 7) + (d << 4) + d;
+ }
+ return (h & 16r7fffffff) % n;
+}
+
+new(size: int):ref HashTable
+{
+ return ref HashTable(array[size] of list of HashNode);
+}
+
+HashTable.find(h: self ref HashTable, key: string): ref HashVal
+{
+ j := fun1(key,len h.a);
+ for(q := h.a[j]; q!=nil; q = tl q){
+ if((hd q).key==key)
+ return (hd q).val;
+ }
+ return nil;
+}
+
+HashTable.insert(h: self ref HashTable, key: string, val: HashVal)
+{
+ j := fun1(key,len h.a);
+ for(q := h.a[j]; q!=nil; q = tl q){
+ if((hd q).key==key){
+ p := (hd q).val;
+ p.i = val.i;
+ p.r = val.r;
+ p.s = val.s;
+ return;
+ }
+ }
+ h.a[j] = HashNode(key,ref HashVal(val.i,val.r,val.s)) :: h.a[j];
+}
+
+HashTable.delete(h:self ref HashTable, key:string)
+{
+ j := fun1(key,len h.a);
+ dl:list of HashNode; dl = nil;
+ for(q := h.a[j]; q!=nil; q = tl q){
+ if((hd q).key!=key)
+ dl = (hd q) :: dl;
+ }
+ h.a[j] = dl;
+}
+
+HashTable.all(h:self ref HashTable): list of HashNode
+{
+ dl:list of HashNode; dl = nil;
+ for(j:=0; j<len h.a; j++)
+ for(q:=h.a[j]; q!=nil; q = tl q)
+ dl = (hd q) :: dl;
+ return dl;
+}
diff --git a/appl/lib/html.b b/appl/lib/html.b
new file mode 100644
index 00000000..8df723fd
--- /dev/null
+++ b/appl/lib/html.b
@@ -0,0 +1,664 @@
+implement HTML;
+
+include "sys.m";
+include "html.m";
+include "strinttab.m";
+
+sys: Sys;
+T: StringIntTab;
+
+Stringtab: adt
+{
+ name: string;
+ val: int;
+};
+
+chartab:= array[] of { T->StringInt
+ ("AElig", 'Æ'),
+ ("Aacute", 'Á'),
+ ("Acirc", 'Â'),
+ ("Agrave", 'À'),
+ ("Aring", 'Å'),
+ ("Atilde", 'Ã'),
+ ("Auml", 'Ä'),
+ ("Ccedil", 'Ç'),
+ ("ETH", 'Ð'),
+ ("Eacute", 'É'),
+ ("Ecirc", 'Ê'),
+ ("Egrave", 'È'),
+ ("Euml", 'Ë'),
+ ("Iacute", 'Í'),
+ ("Icirc", 'Î'),
+ ("Igrave", 'Ì'),
+ ("Iuml", 'Ï'),
+ ("Ntilde", 'Ñ'),
+ ("Oacute", 'Ó'),
+ ("Ocirc", 'Ô'),
+ ("Ograve", 'Ò'),
+ ("Oslash", 'Ø'),
+ ("Otilde", 'Õ'),
+ ("Ouml", 'Ö'),
+ ("THORN", 'Þ'),
+ ("Uacute", 'Ú'),
+ ("Ucirc", 'Û'),
+ ("Ugrave", 'Ù'),
+ ("Uuml", 'Ü'),
+ ("Yacute", 'Ý'),
+ ("aacute", 'á'),
+ ("acirc", 'â'),
+ ("acute", '´'),
+ ("aelig", 'æ'),
+ ("agrave", 'à'),
+ ("alpha", 'α'),
+ ("amp", '&'),
+ ("aring", 'å'),
+ ("atilde", 'ã'),
+ ("auml", 'ä'),
+ ("beta", 'β'),
+ ("brvbar", '¦'),
+ ("ccedil", 'ç'),
+ ("cdots", '⋯'),
+ ("cedil", '¸'),
+ ("cent", '¢'),
+ ("chi", 'χ'),
+ ("copy", '©'),
+ ("curren", '¤'),
+ ("ddots", '⋱'),
+ ("deg", '°'),
+ ("delta", 'δ'),
+ ("divide", '÷'),
+ ("eacute", 'é'),
+ ("ecirc", 'ê'),
+ ("egrave", 'è'),
+ ("emdash", '—'),
+ ("emsp", ' '),
+ ("endash", '–'),
+ ("ensp", ' '),
+ ("epsilon", 'ε'),
+ ("eta", 'η'),
+ ("eth", 'ð'),
+ ("euml", 'ë'),
+ ("frac12", '½'),
+ ("frac14", '¼'),
+ ("frac34", '¾'),
+ ("gamma", 'γ'),
+ ("gt", '>'),
+ ("iacute", 'í'),
+ ("icirc", 'î'),
+ ("iexcl", '¡'),
+ ("igrave", 'ì'),
+ ("iota", 'ι'),
+ ("iquest", '¿'),
+ ("iuml", 'ï'),
+ ("kappa", 'κ'),
+ ("lambda", 'λ'),
+ ("laquo", '«'),
+ ("ldots", '…'),
+ ("lt", '<'),
+ ("macr", '¯'),
+ ("micro", 'µ'),
+ ("middot", '·'),
+ ("mu", 'μ'),
+ ("nbsp", ' '),
+ ("not", '¬'),
+ ("ntilde", 'ñ'),
+ ("nu", 'ν'),
+ ("oacute", 'ó'),
+ ("ocirc", 'ô'),
+ ("ograve", 'ò'),
+ ("omega", 'ω'),
+ ("omicron", 'ο'),
+ ("ordf", 'ª'),
+ ("ordm", 'º'),
+ ("oslash", 'ø'),
+ ("otilde", 'õ'),
+ ("ouml", 'ö'),
+ ("para", '¶'),
+ ("phi", 'φ'),
+ ("pi", 'π'),
+ ("plusmn", '±'),
+ ("pound", '£'),
+ ("psi", 'ψ'),
+ ("quad", ' '),
+ ("quot", '"'),
+ ("raquo", '»'),
+ ("reg", '®'),
+ ("rho", 'ρ'),
+ ("sect", '§'),
+ ("shy", '­'),
+ ("sigma", 'σ'),
+ ("sp", ' '),
+ ("sup1", '¹'),
+ ("sup2", '²'),
+ ("sup3", '³'),
+ ("szlig", 'ß'),
+ ("tau", 'τ'),
+ ("theta", 'θ'),
+ ("thinsp", ' '),
+ ("thorn", 'þ'),
+ ("times", '×'),
+ ("trade", '™'),
+ ("uacute", 'ú'),
+ ("ucirc", 'û'),
+ ("ugrave", 'ù'),
+ ("uml", '¨'),
+ ("upsilon", 'υ'),
+ ("uuml", 'ü'),
+ ("varepsilon", '∈'),
+ ("varphi", 'ϕ'),
+ ("varpi", 'ϖ'),
+ ("varrho", 'ϱ'),
+ ("vdots", '⋮'),
+ ("vsigma", 'ς'),
+ ("vtheta", 'ϑ'),
+ ("xi", 'ξ'),
+ ("yacute", 'ý'),
+ ("yen", '¥'),
+ ("yuml", 'ÿ'),
+ ("zeta", 'ζ'),
+};
+
+htmlstringtab := array[] of { T->StringInt
+ ("a", Ta),
+ ("address", Taddress),
+ ("applet", Tapplet),
+ ("area", Tarea),
+ ("att_footer", Tatt_footer),
+ ("b", Tb),
+ ("base", Tbase),
+ ("basefont", Tbasefont),
+ ("big", Tbig),
+ ("blink", Tblink),
+ ("blockquote", Tblockquote),
+ ("body", Tbody),
+ ("bq", Tbq),
+ ("br", Tbr),
+ ("caption", Tcaption),
+ ("center", Tcenter),
+ ("cite", Tcite),
+ ("code", Tcode),
+ ("col", Tcol),
+ ("colgroup", Tcolgroup),
+ ("dd", Tdd),
+ ("dfn", Tdfn),
+ ("dir", Tdir),
+ ("div", Tdiv),
+ ("dl", Tdl),
+ ("dt", Tdt),
+ ("em", Tem),
+ ("font", Tfont),
+ ("form", Tform),
+ ("frame", Tframe),
+ ("frameset", Tframeset),
+ ("h1", Th1),
+ ("h2", Th2),
+ ("h3", Th3),
+ ("h4", Th4),
+ ("h5", Th5),
+ ("h6", Th6),
+ ("head", Thead),
+ ("hr", Thr),
+ ("html", Thtml),
+ ("i", Ti),
+ ("img", Timg),
+ ("input", Tinput),
+ ("isindex", Tisindex),
+ ("item", Titem),
+ ("kbd", Tkbd),
+ ("li", Tli),
+ ("link", Tlink),
+ ("map", Tmap),
+ ("menu", Tmenu),
+ ("meta", Tmeta),
+ ("nobr", Tnobr),
+ ("noframes", Tnoframes),
+ ("ol", Tol),
+ ("option", Toption),
+ ("p", Tp),
+ ("param", Tparam),
+ ("pre", Tpre),
+ ("q", Tq),
+ ("samp", Tsamp),
+ ("script", Tscript),
+ ("select", Tselect),
+ ("small", Tsmall),
+ ("strike", Tstrike),
+ ("strong", Tstrong),
+ ("style", Tstyle),
+ ("sub", Tsub),
+ ("sup", Tsup),
+ ("t", Tt),
+ ("table", Ttable),
+ ("tbody", Ttbody),
+ ("td", Ttd),
+ ("textarea", Ttextarea),
+ ("textflow", Ttextflow),
+ ("tfoot", Ttfoot),
+ ("th", Tth),
+ ("thead", Tthead),
+ ("title", Ttitle),
+ ("tr", Ttr),
+ ("tt", Ttt),
+ ("u", Tu),
+ ("ul", Tul),
+ ("var", Tvar)
+};
+
+W, D, L, U, N: con byte (1<<iota);
+NCTYPE: con 256;
+
+ctype := array[NCTYPE] of {
+ '0'=>D, '1'=>D, '2'=>D, '3'=>D, '4'=>D,
+ '5'=>D, '6'=>D, '7'=>D, '8'=>D, '9'=>D,
+ 'A'=>U, 'B'=>U, 'C'=>U, 'D'=>U, 'E'=>U, 'F'=>U,
+ 'G'=>U, 'H'=>U, 'I'=>U, 'J'=>U, 'K'=>U, 'L'=>U,
+ 'M'=>U, 'N'=>U, 'O'=>U, 'P'=>U, 'Q'=>U, 'R'=>U,
+ 'S'=>U, 'T'=>U, 'U'=>U, 'V'=>U, 'W'=>U, 'X'=>U,
+ 'Y'=>U, 'Z'=>U,
+ 'a'=>L, 'b'=>L, 'c'=>L, 'd'=>L, 'e'=>L, 'f'=>L,
+ 'g'=>L, 'h'=>L, 'i'=>L, 'j'=>L, 'k'=>L, 'l'=>L,
+ 'm'=>L, 'n'=>L, 'o'=>L, 'p'=>L, 'q'=>L, 'r'=>L,
+ 's'=>L, 't'=>L, 'u'=>L, 'v'=>L, 'w'=>L, 'x'=>L,
+ 'y'=>L, 'z'=>L,
+ '.'=>N, '-'=>N,
+ ' '=>W, '\n'=>W, '\t'=>W, '\r'=>W,
+ * => byte 0
+};
+
+lex(b: array of byte, charset: int, keepwh: int): array of ref Lex
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ if(T == nil)
+ T = load StringIntTab StringIntTab->PATH;
+ if(T == nil) {
+ sys->print("HTML->lex: couldn't %s\n", StringIntTab->PATH);
+ return nil;
+ }
+
+ a: array of ref Lex;
+ ai := 0;
+ i := 0;
+ nb := len b;
+ for(;;){
+ Whitespace:
+ for(;;){
+ # ignore nulls
+ while(i<nb && (int b[i] == 0))
+ i++;
+ # skip white space
+ if(!keepwh) {
+ while(i<nb) {
+ c := int b[i];
+ if(!(int (ctype[c]&W)) && c != ' ')
+ break;
+ i++;
+ }
+ }
+ # skip comments
+ if(i<nb-4 && int b[i]=='<' && int b[i+1]=='!'
+ && int b[i+2]=='-' && int b[i+3]=='-') {
+ i += 4;
+ while(i<nb-3){
+ if(int b[i]=='-' && int b[i+1]=='-' && int b[i+2]=='>'){
+ i += 3;
+ continue Whitespace;
+ }
+ i++;
+ }
+ continue Whitespace;
+ }
+ break;
+ }
+ if(i == nb)
+ break;
+ if(ai == len a){
+ na := array[len a + 500] of ref Lex;
+ if(a != nil)
+ na[0:] = a;
+ a = na;
+ }
+ if(int b[i] == '<'){
+ lx : ref Lex;
+ (lx, i) = gettag(b, i, charset);
+ a[ai++] = lx;
+ }
+ else {
+ s: string;
+ (s, i) = getdata(b, i, keepwh, charset);
+ a[ai++] = ref Lex (Data, s, nil);
+ }
+ }
+ return a[0:ai];
+}
+
+getdata(b: array of byte, i: int, keepnls, charset: int): (string, int)
+{
+ s:= "";
+ j:= 0;
+ c: int;
+ nb := len b;
+
+loop:
+ while(i < nb){
+ oldi := i;
+ case charset{
+ Latin1 =>
+ c = int b[i++];
+ UTF8 =>
+ j: int;
+ (c, j, nil) = sys->byte2char(b, i);
+ i += j;
+ }
+ case c {
+ 0 or 16r1a =>
+ continue loop;
+ '<' =>
+ i = oldi;
+ break loop;
+ '&' =>
+ (c, i) = ampersand(b, i);
+ '\n' =>
+ if(!keepnls)
+ c = ' ';
+ '\r' =>
+ if(oldi > 0 && int b[oldi-1] == '\n')
+ continue loop;
+ if(keepnls)
+ c = '\n';
+ else
+ c = ' ';
+ }
+ s[j++] = c;
+ }
+ return (s, i);
+}
+
+gettag(b: array of byte, i, charset: int): (ref Lex, int)
+{
+ rbra := 0;
+ nb := len b;
+ ans := ref Lex(Notfound, "", nil);
+ al: list of Attr;
+ if(++i == nb)
+ return (ans, i);
+ istart := i;
+ c := int b[i];
+ if(c == '/') {
+ rbra = RBRA;
+ if(++i == nb)
+ return (ans, i);
+ c = int b[i];
+ }
+ if(c>=NCTYPE || !int (ctype[c]&(L|U))) {
+ while(i < nb) {
+ c = int b[i++];
+ if(c == '>')
+ break;
+ }
+ ans.text = string b[istart:i];
+ return (ans, i);
+ }
+ namstart := i;
+ while(c<NCTYPE && int (ctype[c]&(L|U|D|N))) {
+ if(++i == nb) {
+ ans.text = string b[istart:i];
+ return (ans, i);
+ }
+ c = int b[i];
+ }
+ name := lowercase(b, namstart, i);
+ (fnd, tag) := T->lookup(htmlstringtab, name);
+ if(fnd)
+ ans.tag = tag+rbra;
+ else
+ ans.text = name;
+attrloop:
+ while(i < nb){
+ # look for "ws name" or "ws name ws = ws val" (ws=whitespace)
+ # skip whitespace
+ while(c<NCTYPE && int (ctype[c]&W)) {
+ if(++i == nb)
+ break attrloop;
+ c = int b[i];
+ }
+ if(c == '>') {
+ i++;
+ break;
+ }
+ if(c == '<')
+ break; # error: unclosed tag
+ if(c>=NCTYPE || !int (ctype[c]&(L|U))) {
+ # error, not the start of a name
+ # skip to end of tag
+ while(i < nb) {
+ c = int b[i++];
+ if(c == '>')
+ break;
+ }
+ break attrloop;
+ }
+ # gather name
+ namstart = i;
+ while(c<NCTYPE && int (ctype[c]&(L|U|D|N))) {
+ if(++i == nb)
+ break attrloop;
+ c = int b[i];
+ }
+ name = lowercase(b, namstart, i);
+ # skip whitespace
+ while(c<NCTYPE && int (ctype[c]&W)) {
+ if(++i == nb)
+ break attrloop;
+ c = int b[i];
+ }
+ if(c != '=') {
+ # no value for this attr
+ al = (name, "") :: al;
+ continue attrloop;
+ }
+ # skip whitespace
+ if(++i == nb)
+ break attrloop;
+ c = int b[i];
+ while(c<NCTYPE && int (ctype[c]&W)) {
+ if(++i == nb)
+ break attrloop;
+ c = int b[i];
+ }
+ # gather value
+ quote := 0;
+ if(c == '\'' || c == '"') {
+ quote = c;
+ i++;
+ }
+ val := "";
+ nv := 0;
+ valloop:
+ while(i < nb) {
+ case charset{
+ Latin1 =>
+ c = int b[i++];
+ UTF8 =>
+ j: int;
+ (c, j, nil) = sys->byte2char(b, i);
+ i += j;
+ }
+ if(c == '>') {
+ if(quote) {
+ # c might be part of string (though not good style)
+ # but if line ends before close quote, assume
+ # there was an unmatched quote
+ for(k := i; k < nb; k++) {
+ c = int b[k];
+ if(c == quote) {
+ val[nv++] = '>';
+ continue valloop;
+ }
+ if(c == '\n') {
+ i--;
+ break valloop;
+ }
+ }
+ }
+ i--;
+ break valloop;
+ }
+ if(quote) {
+ if(c == quote)
+ break valloop;
+ if(c == '\n')
+ continue valloop;
+ if(c == '\t' || c == '\r')
+ c = ' ';
+ }
+ else {
+ if(c<NCTYPE && int (ctype[c]&W))
+ break valloop;
+ }
+ if(c == '&')
+ (c, i) = ampersand(b, i);
+ val[nv++] = c;
+ }
+ al = (name, val) :: al;
+ if(i < nb)
+ c = int b[i];
+ }
+ ans.attr = al;
+ return (ans, i);
+}
+
+ampersand(b: array of byte, i: int): (int, int)
+{
+ starti := i;
+ c := 0;
+ nb := len b;
+ if(i >= nb)
+ return ('?', i);
+ fnd := 0;
+ ans := 0;
+ if(int b[i] == '#'){
+ i++;
+ while(i<nb){
+ d := int b[i];
+ if(!(int (ctype[d]&D)))
+ break;
+ c = c*10 + d-'0';
+ i++;
+ }
+ if(0<c && c<256) {
+ if(c==160)
+ c = ' '; # non-breaking space
+ ans = c;
+ fnd = 1;
+ }
+ }
+ else {
+ s := "";
+ k := 0;
+ c = int b[i];
+ if(int (ctype[c]&(L|U))) {
+ while(i<nb) {
+ c = int b[i];
+ if(!(int (ctype[c]&(L|U|D|N))))
+ break;
+ s[k++] = c;
+ i++;
+ }
+ }
+ (fnd, ans) = T->lookup(chartab, s);
+ }
+ if(!fnd)
+ return ('&', starti);
+ if(i<nb && (int b[i]==';' || int b[i]=='\n'))
+ i++;
+ return (ans, i);
+}
+
+lowercase(b: array of byte, istart, iend: int): string
+{
+ l := "";
+ j := 0;
+ for(i:=istart; i<iend; i++) {
+ c := int b[i];
+ if(c < NCTYPE && int (ctype[c]&U))
+ l[j] = c-'A'+'a';
+ else
+ l[j] = c;
+ j++;
+ }
+ return l;
+}
+
+uppercase(s: string): string
+{
+ l := "";
+
+ for(i:=0; i<len s; i++) {
+ c := s[i];
+ if(c < NCTYPE && int (ctype[c]&L))
+ l[i] = c+'A'-'a';
+ else
+ l[i] = c;
+ }
+ return l;
+}
+
+attrvalue(attr: list of Attr, name: string): (int, string)
+{
+ while(attr != nil){
+ a := hd attr;
+ if(a.name == name)
+ return (1, a.value);
+ attr = tl attr;
+ }
+ return (0, "");
+}
+
+globalattr(html: array of ref Lex, tag: int, attr: string): (int, string)
+{
+ for(i:=0; i<len html; i++)
+ if(html[i].tag == tag)
+ return attrvalue(html[i].attr, attr);
+ return (0, "");
+}
+
+isbreak(h: array of ref Lex, i: int): int
+{
+ for(; i<len h; i++){
+ case h[i].tag{
+ Th1 or Th2 or Th3 or Th4 or Th5 or Th6 or
+ Tbr or Tp or Tbody or Taddress or Tblockquote or
+ Tul or Tdl or Tdir or Tmenu or Tol or Tpre or Thr or Tform =>
+ return 1;
+ Data =>
+ return 0;
+ }
+ }
+ return 0;
+}
+
+# for debugging
+lex2string(l: ref Lex): string
+{
+ ans := "";
+ tag := l.tag;
+ if(tag == HTML->Data)
+ ans = "'" + l.text + "'";
+ else {
+ ans = "<";
+ if(tag >= RBRA) {
+ tag -= RBRA;
+ ans = ans + "/";
+ }
+ tname := T->revlookup(htmlstringtab, tag);
+ if(tname != nil)
+ ans = ans + uppercase(tname);
+ for(al := l.attr; al != nil; al = tl al) {
+ a := hd al;
+ ans = ans + " " + a.name + "='" + a.value + "'";
+ }
+ ans = ans + ">";
+ }
+ return ans;
+}
diff --git a/appl/lib/ida/NOTICE b/appl/lib/ida/NOTICE
new file mode 100644
index 00000000..fd652105
--- /dev/null
+++ b/appl/lib/ida/NOTICE
@@ -0,0 +1,33 @@
+The IDA software was originally based on a version included with MIT's sfsnet software
+which contains the copyright notice further down.
+This Limbo version and interface is
+ Copyright © 2006 C H Forsyth, Vita Nuova Holdings Limited
+(forsyth@vitanuova.com wrt bugs etc)
+This version was revised with reference to Rabin's paper, but is similar
+enough at the core that I'll include the copyright too.
+The same terms and conditions apply.
+
+Copyright (c) 2000 Frans Kaashoek, Frank Dabek,
+ Massachusetts Institute of Technology
+
+Copyright (c) 2001 Frans Kaashoek, Frank Dabek, Joshua Cates,
+ Massachusetts Institute of Technology
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/appl/lib/ida/ida.b b/appl/lib/ida/ida.b
new file mode 100644
index 00000000..c1430688
--- /dev/null
+++ b/appl/lib/ida/ida.b
@@ -0,0 +1,228 @@
+implement Ida;
+
+#
+# M Rabin, ``Efficient Dispersal of Information for Security,
+# Load Balancing, and Fault Tolerance'', JACM 36(2), April 1989, pp. 335-348
+# the scheme used below is that suggested at the top of page 340
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "rand.m";
+ rand: Rand;
+
+include "ida.m";
+
+invtab: array of int;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ rand = load Rand Rand->PATH;
+ rand->init(sys->pctl(0, nil)^(sys->millisec()<<8));
+ # the table is in a separate module so that
+ # the copy in the module initialisation section is discarded
+ # after unloading, preventing twice the space being used
+ idatab := load Idatab Idatab->PATH;
+ invtab = idatab->init(); # the big fella
+ idatab = nil;
+}
+
+Field: con 65537;
+Fmax: con Field-1;
+
+div(a, b: int): int
+{
+ return mul(a, invtab[b]);
+}
+
+mul(a, b: int): int
+{
+ if(a == Fmax && b == Fmax) # avoid overflow
+ return 1;
+ return int((big(a*b) & 16rFFFFFFFF) % big Field);
+}
+
+sub(a, b: int): int
+{
+ return ((a-b)+Field)%Field;
+}
+
+add(a, b: int): int
+{
+ return (a + b)%Field;
+}
+
+#
+# return a fragment representing the encoded version of data
+#
+fragment(data: array of byte, m: int): ref Frag
+{
+ nb := len data;
+ nw := (nb+1)/2;
+ a := array[m] of {* => rand->rand(Fmax)+1}; # no zero elements
+ f := array[(nw + m - 1)/m] of int;
+ o := 0;
+ i := 0;
+ for(k := 0; k < len f; k++){
+ c := 0;
+ for(j := 0; j < m && i < nb; j++){
+ b := int data[i++] << 8;
+ if(i < nb)
+ b |= int data[i++];
+ c = add(c, mul(b, a[j]));
+ }
+ f[o++] = c;
+ }
+ return ref Frag(nb, m, a, f, nil);
+}
+
+#
+# return the data encoded by the given set of fragments
+#
+reconstruct(frags: array of ref Frag): (array of byte, string)
+{
+ if(len frags < 1 || len frags < (m := frags[0].m))
+ return (nil, "too few fragments");
+ fraglen := len frags[0].enc;
+
+ a := array[m] of array of int;
+ for(j := 0; j < len a; j++){
+ a[j] = frags[j].a;
+ if(len a[j] != m)
+ return (nil, "inconsistent encoding matrix");
+ if(len frags[j].enc != fraglen)
+ return (nil, "inconsistent fragments");
+ }
+ ainv := minvert(a);
+ out := array[fraglen*2*m] of byte;
+ o := 0;
+ for(k := 0; k < fraglen; k++){
+ for(i := 0; i < m; i++){
+ row := ainv[i];
+ b := 0;
+ for(j = 0; j < m; j++)
+ b = add(b, mul(frags[j].enc[k], row[j]));
+ if((b>>16) != 0)
+ return (nil, "corrupt output");
+ out[o++] = byte (b>>8);
+ out[o++] = byte b;
+ }
+ }
+ if(frags[0].dlen < len out)
+ out = out[0: frags[0].dlen];
+ return (out, nil);
+}
+
+#
+# Rabin's paper gives a way of building an encoding matrix that can then
+# be inverted in O(m^2) operations, compared to O(m^3) for the following,
+# but m is small enough it doesn't seem worth the added complication,
+# and it's only done once per set
+#
+minvert(a: array of array of int): array of array of int
+{
+ m := len a; # it's square
+ out := array[m] of {* => array[m*2] of {* => 0}};
+ for(r := 0; r < m; r++){
+ out[r][0:] = a[r];
+ out[r][m+r] = 1; # identity matrix
+ }
+ for(r = 0; r < m; r++){
+ x := out[r][r]; # by construction, cannot be zero, unless later corrupted
+ for(c := 0; c < 2*m; c++)
+ out[r][c] = div(out[r][c], x);
+ for(r1 := 0; r1 < m; r1++)
+ if(r1 != r){
+ y := div(out[r1][r], out[r][r]);
+ for(c = 0; c < 2*m; c++)
+ out[r1][c] = sub(out[r1][c], mul(y, out[r][c]));
+ }
+ }
+ for(r = 0; r < m; r++)
+ out[r] = out[r][m:];
+ return out;
+}
+
+Val: adt {
+ v: int;
+ n: int;
+};
+
+addval(vl: list of ref Val, v: int): list of ref Val
+{
+ for(l := vl; l != nil; l = tl l)
+ if((hd l).v == v){
+ (hd l).n++;
+ return vl;
+ }
+ return ref Val(v, 1) :: vl;
+}
+
+mostly(vl: list of ref Val): ref Val
+{
+ if(len vl == 1)
+ return hd vl;
+ v: ref Val;
+ for(; vl != nil; vl = tl vl)
+ if(v == nil || (hd vl).n > v.n)
+ v = hd vl;
+ return v;
+}
+
+#
+# return a consistent set of Frags: all parameters agree with the majority,
+# and obviously bad fragments have been discarded
+#
+# in the absence of error, they should all have the same value, so lists are fine;
+# could separately return the discarded ones, out of interest
+#
+consistent(frags: array of ref Frag): array of ref Frag
+{
+ t := array[len frags] of ref Frag;
+ t[0:] = frags;
+ frags = t;
+ ds: list of ref Val; # data size
+ ms: list of ref Val;
+ fls: list of ref Val;
+ for(i := 0; i < len frags; i++){
+ f := frags[i];
+ if(f != nil){
+ ds = addval(ds, f.dlen);
+ ms = addval(ms, f.m);
+ fls = addval(fls, len f.enc);
+ }
+ }
+ dv := mostly(ds);
+ mv := mostly(ms);
+ flv := mostly(fls);
+ if(mv == nil || flv == nil || dv == nil)
+ return nil;
+ for(i = 0; i < len frags; i++){
+ f := frags[i];
+ if(f == nil || f.m != mv.v || f.m != len f.a || len f.enc != flv.v || f.dlen != dv.v || badfrag(f)){ # inconsistent: drop it
+ if(i+1 < len frags)
+ frags[i:] = frags[i+1:];
+ frags = frags[0:len frags-1];
+ }
+ }
+ if(len frags == 0)
+ return nil;
+ return frags;
+}
+
+badfrag(f: ref Frag): int
+{
+ for(i := 0; i < len f.a; i++){
+ v := f.a[i];
+ if(v <= 0 || v >= Field)
+ return 1;
+ }
+ for(i = 0; i < len f.a; i++){
+ v := f.enc[i];
+ if(v == 0 || v >= Field)
+ return 1;
+ }
+ return 0;
+}
diff --git a/appl/lib/ida/idatab.b b/appl/lib/ida/idatab.b
new file mode 100644
index 00000000..53da0b75
--- /dev/null
+++ b/appl/lib/ida/idatab.b
@@ -0,0 +1,65549 @@
+implement Idatab;
+
+include "ida.m";
+
+init(): array of int
+{
+ return invtab;
+}
+
+invtab := array[] of {
+0,
+1,
+32769,
+21846,
+49153,
+26215,
+10923,
+18725,
+57345,
+7282,
+45876,
+5958,
+38230,
+15124,
+42131,
+30584,
+61441,
+30841,
+3641,
+10348,
+22938,
+49933,
+2979,
+45591,
+19115,
+5243,
+7562,
+24273,
+53834,
+22599,
+15292,
+21141,
+63489,
+1986,
+48189,
+3745,
+34589,
+19484,
+5174,
+26887,
+11469,
+44757,
+57735,
+25910,
+34258,
+53886,
+55564,
+58565,
+42326,
+2675,
+35390,
+32126,
+3781,
+51935,
+44905,
+14299,
+26917,
+25295,
+44068,
+5554,
+7646,
+56942,
+43339,
+38490,
+64513,
+42347,
+993,
+5869,
+56863,
+15197,
+34641,
+48922,
+50063,
+38604,
+9742,
+45439,
+2587,
+19576,
+46212,
+9955,
+38503,
+8091,
+55147,
+61589,
+61636,
+32383,
+12955,
+7533,
+17129,
+45655,
+26943,
+11523,
+27782,
+7047,
+62051,
+15177,
+21163,
+41214,
+34106,
+662,
+17695,
+38284,
+16063,
+45176,
+34659,
+23094,
+58736,
+1225,
+55221,
+21044,
+39918,
+50186,
+46227,
+22039,
+45416,
+35333,
+22034,
+30808,
+2777,
+51218,
+3823,
+42247,
+28471,
+14919,
+54438,
+14156,
+19245,
+39219,
+65025,
+52328,
+53942,
+23013,
+33265,
+38928,
+35703,
+17962,
+61200,
+44967,
+40367,
+22160,
+50089,
+63213,
+24461,
+60954,
+57800,
+43842,
+19302,
+44583,
+4871,
+5718,
+55488,
+21701,
+34062,
+54400,
+9788,
+30443,
+23106,
+12523,
+37746,
+61003,
+52020,
+6513,
+36814,
+29753,
+60342,
+26612,
+63563,
+6279,
+30818,
+11246,
+48960,
+52123,
+39246,
+28412,
+36535,
+749,
+41333,
+23697,
+55596,
+25629,
+46240,
+4345,
+38530,
+62672,
+13891,
+43219,
+36292,
+50467,
+63794,
+12830,
+40357,
+62792,
+43350,
+33957,
+20607,
+57807,
+17053,
+52230,
+331,
+988,
+41616,
+23802,
+19142,
+59403,
+40800,
+61381,
+22588,
+48757,
+50098,
+48604,
+11547,
+31992,
+29368,
+38153,
+33381,
+5182,
+60379,
+21745,
+10522,
+12868,
+19959,
+12455,
+25093,
+34091,
+55882,
+36992,
+43788,
+24829,
+22708,
+60958,
+50435,
+28371,
+11017,
+25596,
+15404,
+11713,
+34157,
+25164,
+25609,
+44971,
+34680,
+61186,
+53892,
+2697,
+47004,
+535,
+40228,
+796,
+27219,
+64221,
+7078,
+57965,
+42391,
+45850,
+52378,
+32640,
+65281,
+32641,
+26164,
+58958,
+26971,
+2511,
+44275,
+35385,
+49401,
+10387,
+19464,
+37064,
+50620,
+30454,
+8981,
+64086,
+30600,
+3841,
+55252,
+42182,
+52952,
+11120,
+11080,
+2349,
+57813,
+41981,
+64375,
+20379,
+44999,
+5059,
+30477,
+53206,
+28900,
+28800,
+21921,
+13738,
+9651,
+57261,
+55060,
+40433,
+35204,
+43912,
+2859,
+3507,
+27744,
+59876,
+43619,
+34607,
+17031,
+63818,
+27200,
+17505,
+4894,
+58750,
+47990,
+28870,
+11553,
+52974,
+39030,
+7698,
+18873,
+11991,
+63270,
+31844,
+26010,
+22254,
+36025,
+43015,
+18407,
+60899,
+47645,
+50706,
+30171,
+64541,
+13306,
+198,
+64550,
+60420,
+35908,
+40496,
+15409,
+27615,
+5623,
+29192,
+24480,
+61501,
+58830,
+19107,
+19623,
+55469,
+14206,
+15676,
+51036,
+22722,
+33143,
+32115,
+53435,
+59039,
+44617,
+62214,
+27798,
+60764,
+45583,
+16795,
+23120,
+59183,
+34941,
+35928,
+19265,
+47043,
+31336,
+7143,
+39714,
+4973,
+54378,
+44869,
+18146,
+50778,
+58002,
+48410,
+31897,
+11821,
+6415,
+24036,
+52947,
+13073,
+31396,
+40212,
+21675,
+30130,
+49747,
+61134,
+43072,
+13815,
+61672,
+41233,
+41295,
+7671,
+26115,
+1991,
+32934,
+10235,
+494,
+12976,
+20808,
+8662,
+11901,
+46998,
+9571,
+27833,
+62470,
+19645,
+20400,
+24997,
+63459,
+14989,
+11294,
+56968,
+57147,
+51640,
+25049,
+51078,
+24302,
+24244,
+38542,
+49036,
+15996,
+21071,
+14684,
+19584,
+51845,
+17497,
+49459,
+20318,
+2591,
+55045,
+62958,
+29363,
+43641,
+14614,
+5261,
+40342,
+6434,
+16123,
+42748,
+14861,
+38996,
+53406,
+45315,
+9131,
+49814,
+1906,
+27941,
+46270,
+18496,
+57690,
+21894,
+50925,
+45183,
+15412,
+11354,
+53204,
+30479,
+39979,
+57986,
+35825,
+46954,
+20383,
+38277,
+53839,
+12798,
+16279,
+7702,
+57013,
+38625,
+26020,
+49847,
+32145,
+12582,
+55465,
+45573,
+42180,
+55254,
+5336,
+17340,
+21664,
+30593,
+2171,
+26946,
+47565,
+34117,
+12919,
+23502,
+53609,
+33036,
+48452,
+20114,
+55301,
+398,
+52562,
+46378,
+53801,
+64879,
+26136,
+3539,
+2093,
+61751,
+37003,
+53964,
+46979,
+22925,
+47440,
+26189,
+10043,
+16320,
+52327,
+65409,
+39220,
+49089,
+35250,
+13082,
+11282,
+29479,
+53162,
+46254,
+30064,
+34024,
+51377,
+54906,
+43941,
+50461,
+47505,
+57469,
+33326,
+37962,
+7899,
+9732,
+38732,
+18532,
+245,
+25310,
+8543,
+15227,
+12159,
+37259,
+14658,
+32043,
+23294,
+15300,
+43531,
+34689,
+47086,
+27626,
+64582,
+21091,
+18436,
+26476,
+20147,
+5560,
+36252,
+5540,
+6589,
+33943,
+17117,
+61675,
+38668,
+53759,
+6868,
+64956,
+43730,
+42958,
+47968,
+55268,
+53328,
+35298,
+6657,
+48007,
+64622,
+26603,
+20174,
+14450,
+28282,
+14400,
+11319,
+43729,
+64973,
+6869,
+28553,
+37594,
+19269,
+61399,
+21213,
+27530,
+4562,
+52985,
+17410,
+17602,
+40560,
+21956,
+23351,
+34198,
+22175,
+34522,
+6127,
+13872,
+30424,
+29938,
+7934,
+54578,
+60879,
+50072,
+3455,
+41284,
+19801,
+31909,
+4505,
+13600,
+20634,
+41521,
+42306,
+2447,
+62138,
+29375,
+43409,
+23995,
+38098,
+14435,
+25247,
+38545,
+29046,
+26487,
+38047,
+19515,
+62828,
+3849,
+62525,
+42205,
+10664,
+38764,
+47166,
+31635,
+20371,
+15922,
+56409,
+13005,
+40590,
+11127,
+10702,
+50781,
+23573,
+54276,
+52774,
+41972,
+60084,
+63218,
+29094,
+56591,
+39543,
+25353,
+17710,
+47854,
+26135,
+65039,
+53802,
+6653,
+31430,
+99,
+47843,
+32275,
+20893,
+30210,
+3832,
+17954,
+55055,
+20248,
+40924,
+40473,
+24832,
+46576,
+56022,
+35580,
+15392,
+14596,
+33975,
+12240,
+30122,
+63519,
+63426,
+29415,
+61423,
+42322,
+42165,
+42580,
+3995,
+60503,
+3035,
+7103,
+9457,
+7838,
+4432,
+25518,
+10343,
+11361,
+8532,
+49340,
+10097,
+48826,
+28620,
+59486,
+25750,
+62288,
+24194,
+55077,
+34571,
+31107,
+8388,
+13899,
+52209,
+30382,
+51513,
+55560,
+36836,
+41166,
+21238,
+11560,
+34541,
+62360,
+42241,
+50239,
+61198,
+17964,
+20824,
+42401,
+899,
+56290,
+63206,
+15668,
+21190,
+36340,
+22024,
+19857,
+36281,
+35255,
+60216,
+27189,
+22111,
+55203,
+62538,
+9073,
+14251,
+25389,
+21407,
+29001,
+175,
+24205,
+49480,
+48717,
+63013,
+38679,
+30555,
+35976,
+53936,
+12018,
+37129,
+59242,
+57614,
+39305,
+40456,
+15698,
+10880,
+20106,
+45799,
+43606,
+6477,
+15065,
+32726,
+57642,
+63587,
+30567,
+19196,
+21536,
+63344,
+39676,
+60994,
+30836,
+34237,
+53385,
+837,
+53416,
+15612,
+36604,
+34892,
+45826,
+11795,
+33764,
+59323,
+16467,
+34628,
+37886,
+25308,
+247,
+38319,
+6488,
+3445,
+10404,
+56046,
+4331,
+33299,
+38719,
+14410,
+23499,
+31997,
+37554,
+8182,
+46685,
+6384,
+31235,
+21362,
+42591,
+19058,
+10200,
+4813,
+45267,
+23126,
+64498,
+26582,
+40263,
+16006,
+5647,
+57752,
+28484,
+53650,
+61342,
+1423,
+25820,
+47398,
+45293,
+54129,
+25539,
+40578,
+12151,
+783,
+12122,
+61397,
+19271,
+50497,
+24518,
+57685,
+7998,
+28464,
+43304,
+43485,
+7342,
+6793,
+9792,
+6546,
+58691,
+59621,
+41517,
+23532,
+57498,
+47413,
+10159,
+54398,
+34064,
+39581,
+60291,
+16707,
+31479,
+58112,
+47450,
+9600,
+54589,
+905,
+7307,
+25658,
+35399,
+26425,
+20171,
+39472,
+3217,
+20102,
+40830,
+19087,
+21374,
+27896,
+40199,
+47724,
+19498,
+57169,
+26703,
+14925,
+55426,
+33690,
+37334,
+36483,
+24907,
+9981,
+953,
+44448,
+46739,
+1169,
+23135,
+729,
+9248,
+3055,
+28845,
+63650,
+10947,
+869,
+58231,
+57661,
+55360,
+55227,
+7706,
+46473,
+5677,
+5599,
+26602,
+64964,
+48008,
+12650,
+52758,
+45284,
+28993,
+5835,
+50681,
+49135,
+23477,
+47966,
+42960,
+41429,
+51907,
+21587,
+59688,
+24286,
+6399,
+31469,
+40908,
+62523,
+3851,
+30915,
+61275,
+17658,
+52081,
+35032,
+13010,
+21892,
+57692,
+2566,
+48841,
+19931,
+6291,
+43300,
+60501,
+3997,
+55555,
+894,
+21090,
+64988,
+27627,
+54306,
+2668,
+53236,
+8670,
+45078,
+10832,
+7418,
+48065,
+59221,
+33854,
+23043,
+13473,
+36184,
+56551,
+17886,
+49827,
+31253,
+39228,
+63991,
+11751,
+49572,
+59573,
+33940,
+16518,
+16902,
+24226,
+53603,
+10057,
+10446,
+60419,
+65205,
+199,
+23922,
+26281,
+54030,
+23189,
+66,
+59669,
+13305,
+65208,
+30172,
+13068,
+20140,
+34538,
+36795,
+33815,
+42733,
+63644,
+57190,
+51270,
+19980,
+26982,
+64173,
+56258,
+9205,
+44231,
+55962,
+23720,
+24988,
+45863,
+53422,
+37790,
+17558,
+8160,
+55010,
+58932,
+42346,
+65473,
+38491,
+19610,
+20931,
+57313,
+6369,
+17625,
+56447,
+6541,
+45933,
+5641,
+62181,
+47508,
+18770,
+26581,
+64717,
+23127,
+27071,
+15032,
+47629,
+17012,
+49043,
+58457,
+7574,
+27453,
+17993,
+54739,
+13282,
+57999,
+10705,
+56521,
+58828,
+61503,
+21825,
+16663,
+63371,
+18981,
+33170,
+36718,
+46178,
+4866,
+20738,
+19366,
+51410,
+9266,
+60142,
+32891,
+63946,
+12655,
+41350,
+37040,
+53466,
+40382,
+27444,
+38848,
+60192,
+51398,
+59535,
+7329,
+63419,
+48790,
+4349,
+11647,
+1447,
+7650,
+11976,
+54534,
+5106,
+50113,
+47249,
+23543,
+15681,
+13813,
+43074,
+32291,
+1789,
+43314,
+2381,
+9218,
+52287,
+13238,
+2491,
+42842,
+45349,
+2780,
+8037,
+18126,
+27312,
+2770,
+36802,
+36063,
+33033,
+49740,
+13788,
+41327,
+16926,
+63606,
+62380,
+19334,
+26670,
+59648,
+59828,
+3434,
+57105,
+32478,
+22639,
+21865,
+25786,
+21479,
+63686,
+23984,
+44288,
+27634,
+8012,
+26664,
+58172,
+17649,
+60425,
+36097,
+48049,
+56772,
+25299,
+32311,
+11199,
+46070,
+13404,
+10087,
+8427,
+7225,
+5343,
+14141,
+31889,
+7200,
+53925,
+38428,
+58186,
+54633,
+20378,
+65255,
+41982,
+36203,
+31334,
+47045,
+4605,
+18797,
+897,
+42403,
+63858,
+63468,
+35590,
+43375,
+15450,
+13765,
+23943,
+2281,
+2557,
+59261,
+15427,
+8705,
+10969,
+8801,
+44355,
+20280,
+27827,
+10978,
+30922,
+44444,
+47103,
+17099,
+51254,
+43856,
+35209,
+17261,
+26171,
+35832,
+7871,
+6936,
+48457,
+15212,
+24733,
+14969,
+38452,
+3967,
+10588,
+27289,
+15666,
+63208,
+50871,
+25036,
+59918,
+34496,
+52969,
+20642,
+38019,
+42669,
+47849,
+48723,
+28394,
+35021,
+46460,
+6800,
+107,
+10317,
+30178,
+53529,
+11305,
+21153,
+30985,
+33992,
+26842,
+31069,
+26374,
+47456,
+30040,
+54473,
+40835,
+44766,
+40822,
+19049,
+37751,
+39986,
+39059,
+45392,
+32532,
+52041,
+24924,
+14523,
+17026,
+46012,
+41111,
+51792,
+11593,
+42526,
+29927,
+31414,
+61737,
+34693,
+63666,
+64031,
+38191,
+53871,
+9170,
+5332,
+28708,
+19382,
+50715,
+23583,
+50068,
+48586,
+45150,
+42954,
+6528,
+7961,
+40441,
+60973,
+12554,
+39271,
+27678,
+20295,
+1890,
+38332,
+32743,
+5351,
+50464,
+58159,
+58724,
+44555,
+16600,
+27138,
+40194,
+26387,
+24899,
+20986,
+54471,
+30042,
+53479,
+31609,
+9420,
+14547,
+23539,
+61064,
+26717,
+52540,
+32593,
+45445,
+16572,
+8855,
+35293,
+23927,
+53358,
+45836,
+7077,
+65288,
+27220,
+26901,
+30309,
+36095,
+60427,
+15715,
+48645,
+32818,
+54507,
+56690,
+19508,
+48906,
+17802,
+43215,
+45546,
+15105,
+5064,
+1916,
+46735,
+8977,
+46420,
+60296,
+18599,
+10124,
+22481,
+20462,
+46847,
+53005,
+45413,
+12416,
+37269,
+23288,
+37068,
+28011,
+4851,
+17790,
+19230,
+7696,
+39032,
+7298,
+42983,
+49756,
+16975,
+6120,
+26340,
+15061,
+56257,
+64528,
+26983,
+31713,
+19081,
+47476,
+35952,
+63480,
+61426,
+21161,
+15179,
+53851,
+60866,
+21290,
+35172,
+34766,
+63636,
+63020,
+15708,
+34286,
+55633,
+36320,
+2224,
+37497,
+26177,
+3919,
+28640,
+2216,
+45466,
+12759,
+18866,
+37940,
+39792,
+38449,
+27397,
+4266,
+19722,
+24670,
+27272,
+37817,
+56615,
+24413,
+34611,
+14310,
+40850,
+29743,
+23024,
+12875,
+7478,
+31144,
+30519,
+12097,
+43398,
+60307,
+21784,
+50054,
+10715,
+48322,
+27211,
+4194,
+829,
+39718,
+40334,
+58873,
+13732,
+15191,
+43248,
+58525,
+14060,
+27780,
+11525,
+18418,
+36856,
+20583,
+45470,
+10619,
+22544,
+5780,
+8050,
+50039,
+29067,
+31180,
+5760,
+53889,
+1087,
+57888,
+44415,
+30599,
+65266,
+8982,
+40098,
+10412,
+15855,
+53969,
+63198,
+33218,
+59428,
+28145,
+26152,
+31603,
+63118,
+7834,
+37667,
+10595,
+61561,
+18170,
+62191,
+11012,
+43127,
+42697,
+59842,
+50909,
+21194,
+50396,
+42020,
+30108,
+62125,
+46363,
+59563,
+43824,
+39552,
+60370,
+61212,
+31269,
+57119,
+37305,
+31206,
+39894,
+61625,
+45463,
+28752,
+43472,
+53131,
+47269,
+8712,
+32856,
+18144,
+44871,
+55713,
+24740,
+44389,
+57127,
+38190,
+64275,
+63667,
+52108,
+34180,
+48046,
+24983,
+17988,
+48947,
+26968,
+59351,
+6009,
+10066,
+51333,
+59281,
+29621,
+37659,
+28807,
+15018,
+52421,
+25871,
+20228,
+47039,
+7849,
+37762,
+5440,
+6421,
+10053,
+39288,
+55668,
+3501,
+21803,
+42469,
+36007,
+34919,
+40301,
+51545,
+16363,
+32365,
+28821,
+11750,
+64562,
+39229,
+48052,
+38036,
+9598,
+47452,
+10768,
+32663,
+31672,
+5774,
+19838,
+61412,
+30497,
+28880,
+15418,
+23595,
+49887,
+31867,
+59461,
+49917,
+33187,
+8281,
+26708,
+60817,
+7806,
+55817,
+18302,
+23415,
+17446,
+14647,
+22913,
+41184,
+38666,
+61677,
+16882,
+15835,
+62430,
+49101,
+41002,
+28613,
+17314,
+54800,
+18943,
+3547,
+12654,
+64466,
+32892,
+2633,
+51928,
+45691,
+3244,
+14199,
+34491,
+56602,
+5202,
+44742,
+28023,
+46158,
+34934,
+43773,
+49418,
+48123,
+52128,
+25335,
+7205,
+46539,
+44518,
+25841,
+48767,
+8603,
+18777,
+4053,
+4091,
+22183,
+56111,
+45039,
+3192,
+4886,
+48386,
+51502,
+10681,
+34561,
+54064,
+51456,
+9529,
+13019,
+5100,
+47718,
+35175,
+36356,
+55402,
+36792,
+11563,
+54141,
+32249,
+37541,
+13291,
+16474,
+52900,
+39123,
+8003,
+43373,
+35592,
+56396,
+28876,
+38306,
+14242,
+27991,
+26825,
+13147,
+30671,
+11114,
+33480,
+50407,
+12910,
+55594,
+23699,
+46936,
+55415,
+12084,
+59833,
+21387,
+45538,
+23364,
+20289,
+24042,
+38844,
+62599,
+33160,
+34314,
+6061,
+49397,
+63467,
+64366,
+42404,
+50644,
+58017,
+34735,
+12259,
+5523,
+61611,
+55553,
+3999,
+24135,
+14232,
+47244,
+21652,
+43201,
+54511,
+58268,
+3671,
+19580,
+36165,
+58748,
+4896,
+37835,
+3273,
+47142,
+62114,
+38515,
+62579,
+17776,
+53527,
+30180,
+11766,
+11491,
+28749,
+2219,
+56475,
+56251,
+37848,
+2252,
+27199,
+65232,
+17032,
+9901,
+52559,
+44731,
+62914,
+50416,
+41122,
+30928,
+48508,
+28201,
+29056,
+31273,
+23725,
+62436,
+4800,
+29350,
+60063,
+3773,
+33221,
+15753,
+36422,
+5910,
+12829,
+65349,
+50468,
+56974,
+45981,
+61523,
+42854,
+53209,
+19736,
+21933,
+34377,
+59817,
+10051,
+6423,
+20415,
+36368,
+42312,
+32191,
+10687,
+7071,
+13948,
+42192,
+52868,
+51130,
+23862,
+45212,
+9749,
+13263,
+61353,
+6550,
+46120,
+27649,
+40231,
+51765,
+27713,
+38024,
+16845,
+13520,
+18667,
+3459,
+51010,
+41829,
+45222,
+51475,
+37759,
+57542,
+33245,
+1099,
+22224,
+51083,
+56138,
+56326,
+33353,
+3359,
+44336,
+23888,
+33133,
+4663,
+4624,
+29730,
+34296,
+31987,
+47191,
+24944,
+31825,
+57159,
+38242,
+46336,
+33203,
+14222,
+61884,
+17785,
+61599,
+20293,
+27680,
+14680,
+60382,
+30913,
+3853,
+44843,
+56005,
+21606,
+35607,
+22516,
+35568,
+28446,
+13301,
+40347,
+32482,
+13315,
+24004,
+45193,
+6325,
+14536,
+26379,
+54192,
+22642,
+6878,
+47265,
+5055,
+35686,
+33213,
+58109,
+14102,
+57336,
+45773,
+44507,
+32609,
+23983,
+64404,
+21480,
+5093,
+53483,
+48296,
+58722,
+58161,
+43562,
+24854,
+29844,
+42224,
+12143,
+34545,
+35968,
+23263,
+48503,
+55638,
+20454,
+52107,
+64030,
+64276,
+34694,
+61898,
+48226,
+9682,
+63406,
+10370,
+8829,
+8336,
+58809,
+34528,
+17516,
+13713,
+6505,
+28579,
+10946,
+64634,
+28846,
+45241,
+1283,
+27206,
+57189,
+64533,
+42734,
+31022,
+35914,
+27880,
+21650,
+47246,
+63019,
+64158,
+34767,
+62162,
+60546,
+15722,
+447,
+37597,
+10545,
+36871,
+32494,
+28636,
+46582,
+47928,
+27153,
+60472,
+1334,
+18803,
+26618,
+57204,
+4335,
+20572,
+22539,
+13530,
+5416,
+6026,
+3709,
+41390,
+56801,
+25413,
+62379,
+64417,
+16927,
+27632,
+44290,
+51549,
+39505,
+35729,
+18092,
+10951,
+61044,
+39437,
+8943,
+52180,
+57682,
+2763,
+48395,
+20028,
+19614,
+30566,
+64764,
+57643,
+38644,
+9698,
+24786,
+21354,
+62555,
+5827,
+16970,
+13181,
+8259,
+10260,
+8451,
+62165,
+12113,
+27749,
+59570,
+43447,
+37797,
+61909,
+5223,
+52403,
+62978,
+6278,
+65371,
+26613,
+32868,
+17934,
+11961,
+39607,
+45909,
+40361,
+27015,
+54168,
+44363,
+2047,
+33,
+17349,
+62603,
+59639,
+39421,
+395,
+32604,
+35876,
+15086,
+28810,
+6534,
+45912,
+10070,
+19966,
+17269,
+23123,
+51166,
+4548,
+49676,
+54162,
+54135,
+62043,
+31822,
+54413,
+28595,
+22454,
+25635,
+35487,
+9990,
+22507,
+13491,
+63425,
+64855,
+30123,
+28129,
+40989,
+37371,
+41564,
+54884,
+18674,
+27981,
+57357,
+11860,
+21738,
+12494,
+48822,
+55700,
+27659,
+26711,
+3929,
+18895,
+11325,
+8779,
+61455,
+4080,
+61459,
+27505,
+53732,
+29466,
+57429,
+21173,
+1985,
+65505,
+21142,
+52014,
+26905,
+9805,
+51523,
+43234,
+42320,
+61425,
+64167,
+35953,
+44625,
+41581,
+14055,
+60992,
+39678,
+36039,
+24501,
+55735,
+45023,
+35589,
+64365,
+63859,
+49398,
+23754,
+44703,
+9385,
+10328,
+46059,
+14988,
+65127,
+24998,
+44332,
+47460,
+46304,
+17336,
+7516,
+23323,
+56583,
+57969,
+8506,
+15247,
+57290,
+47139,
+61997,
+501,
+3787,
+44171,
+46495,
+2844,
+41765,
+23386,
+60138,
+29540,
+6641,
+47057,
+61768,
+36022,
+38121,
+36641,
+61029,
+9540,
+29414,
+64854,
+63520,
+13492,
+43681,
+30429,
+41100,
+48789,
+64454,
+7330,
+42259,
+51756,
+16585,
+3087,
+18359,
+43239,
+23089,
+55215,
+2433,
+13760,
+10369,
+63661,
+9683,
+2796,
+25705,
+55929,
+4633,
+46799,
+30071,
+17403,
+49214,
+5265,
+31973,
+7095,
+39096,
+17171,
+20675,
+56349,
+18520,
+30588,
+26733,
+55970,
+20191,
+57440,
+13722,
+11009,
+19424,
+28925,
+30096,
+2307,
+25699,
+60624,
+62536,
+55205,
+36433,
+18980,
+64478,
+16664,
+24395,
+35926,
+34943,
+483,
+38592,
+54016,
+33492,
+42245,
+3825,
+41574,
+5988,
+8241,
+27267,
+28787,
+2553,
+12549,
+57825,
+60498,
+56393,
+43991,
+44540,
+53711,
+40609,
+35655,
+39675,
+64760,
+21537,
+16332,
+48914,
+15989,
+33663,
+28909,
+21657,
+20605,
+33959,
+14577,
+4609,
+29187,
+58912,
+24053,
+6619,
+45867,
+34014,
+55785,
+21421,
+19131,
+55443,
+36896,
+1390,
+20072,
+36787,
+1713,
+9063,
+41193,
+13656,
+29216,
+1385,
+28041,
+18401,
+43760,
+50800,
+20846,
+49285,
+41684,
+24870,
+23274,
+6894,
+26596,
+53432,
+47080,
+8463,
+55263,
+31803,
+50827,
+31190,
+25011,
+9667,
+9254,
+13335,
+21904,
+29824,
+42516,
+29914,
+27688,
+1717,
+38339,
+61321,
+11538,
+16239,
+47592,
+44088,
+42850,
+43701,
+6145,
+12893,
+60440,
+43508,
+10185,
+31843,
+65219,
+11992,
+56352,
+22144,
+61670,
+13817,
+28112,
+4006,
+55512,
+13332,
+34222,
+29086,
+30166,
+41593,
+1178,
+62981,
+62896,
+50817,
+49963,
+56793,
+12924,
+28386,
+35331,
+45418,
+50061,
+48924,
+54819,
+38368,
+47318,
+23035,
+56949,
+6702,
+40166,
+37812,
+37112,
+36982,
+27945,
+36381,
+7165,
+35440,
+2159,
+39839,
+37693,
+48713,
+25693,
+3600,
+54600,
+59731,
+17184,
+19214,
+37478,
+29093,
+64887,
+60085,
+26740,
+10189,
+24460,
+65396,
+50090,
+20991,
+27178,
+50870,
+64327,
+15667,
+64806,
+56291,
+45845,
+35071,
+42578,
+42167,
+42177,
+33217,
+64080,
+53970,
+35750,
+31929,
+33258,
+31734,
+24510,
+17795,
+62242,
+54456,
+279,
+7725,
+56979,
+39651,
+45706,
+44740,
+5204,
+33909,
+5283,
+34047,
+3945,
+62399,
+55322,
+40482,
+16724,
+37121,
+6429,
+38253,
+47623,
+37169,
+53254,
+54946,
+52214,
+10140,
+41620,
+46682,
+11093,
+5489,
+32865,
+15461,
+55234,
+22222,
+1101,
+56320,
+58249,
+41318,
+8436,
+25627,
+55598,
+21928,
+44304,
+50373,
+12773,
+41399,
+19746,
+45854,
+27282,
+17916,
+22994,
+36704,
+34913,
+3468,
+12092,
+56997,
+18682,
+7606,
+43655,
+45135,
+40542,
+40253,
+54791,
+19226,
+25470,
+34752,
+33108,
+5294,
+26649,
+46413,
+28525,
+7833,
+64074,
+31604,
+54357,
+58204,
+60452,
+12518,
+9513,
+29959,
+24573,
+17248,
+58414,
+59253,
+19006,
+10321,
+2128,
+51778,
+41906,
+54103,
+15759,
+56693,
+50812,
+57130,
+26392,
+14197,
+3246,
+50279,
+50044,
+23230,
+616,
+3400,
+19348,
+32822,
+23450,
+37927,
+15015,
+15089,
+62120,
+59533,
+51400,
+38421,
+45095,
+43345,
+11451,
+48261,
+52552,
+16996,
+37275,
+13421,
+14000,
+48303,
+27181,
+13187,
+27159,
+23728,
+52419,
+15020,
+62942,
+60005,
+28178,
+53186,
+55068,
+22383,
+39729,
+20411,
+35078,
+42293,
+36975,
+51644,
+22320,
+19993,
+42603,
+52298,
+1105,
+22696,
+37645,
+16266,
+31442,
+58789,
+14173,
+12462,
+18043,
+40030,
+15775,
+8513,
+9138,
+23006,
+13526,
+53324,
+20861,
+25896,
+17736,
+38565,
+261,
+21263,
+39849,
+47732,
+20508,
+15707,
+64157,
+63637,
+47247,
+50115,
+58258,
+31833,
+38678,
+64784,
+48718,
+51864,
+36542,
+59704,
+41074,
+4585,
+5360,
+2666,
+54308,
+14354,
+9488,
+9691,
+50425,
+58126,
+48088,
+44560,
+14495,
+25034,
+50873,
+24293,
+15116,
+22575,
+24110,
+21477,
+25788,
+3264,
+44522,
+36749,
+2182,
+52989,
+62895,
+63255,
+1179,
+6277,
+63565,
+52404,
+9059,
+13839,
+61906,
+42916,
+7844,
+945,
+16697,
+19166,
+57195,
+49140,
+37650,
+35444,
+29470,
+25232,
+7050,
+61848,
+61824,
+29362,
+65105,
+55046,
+8354,
+8300,
+56885,
+13569,
+54686,
+20097,
+76,
+45962,
+5569,
+45218,
+430,
+10493,
+45747,
+60004,
+63062,
+15021,
+53222,
+59508,
+32302,
+48573,
+3200,
+4710,
+5816,
+40042,
+45335,
+44538,
+43993,
+30532,
+38860,
+46127,
+7254,
+26270,
+52244,
+49065,
+62705,
+55491,
+23991,
+8286,
+30654,
+37196,
+27305,
+50415,
+63813,
+44732,
+35003,
+26679,
+26120,
+22918,
+21962,
+36307,
+50392,
+32644,
+1593,
+13610,
+9501,
+46219,
+28208,
+47923,
+29776,
+50816,
+63254,
+62982,
+52990,
+40626,
+32880,
+57091,
+4828,
+16409,
+15908,
+60022,
+29394,
+28345,
+31521,
+9754,
+40902,
+24453,
+9373,
+8901,
+24524,
+54376,
+4975,
+22773,
+9155,
+40321,
+60176,
+2532,
+11230,
+958,
+12302,
+56136,
+51085,
+37257,
+12161,
+23210,
+49,
+30148,
+32438,
+42068,
+3327,
+5062,
+15107,
+44009,
+48194,
+10231,
+14816,
+56192,
+26927,
+59271,
+34438,
+55475,
+44081,
+6208,
+44681,
+51403,
+41754,
+11644,
+243,
+18534,
+59078,
+46774,
+57724,
+35194,
+22864,
+8895,
+16039,
+9615,
+30868,
+3848,
+64908,
+19516,
+14964,
+3649,
+54280,
+54260,
+43981,
+24878,
+23904,
+41256,
+41048,
+3060,
+41066,
+13170,
+11336,
+40299,
+34921,
+60897,
+18409,
+32264,
+39961,
+46260,
+56178,
+48625,
+15491,
+42309,
+35632,
+23738,
+62065,
+17976,
+23712,
+31740,
+19104,
+30713,
+52874,
+43349,
+65346,
+40358,
+6537,
+59694,
+59267,
+30433,
+47908,
+10645,
+5880,
+17586,
+29902,
+17383,
+58786,
+31818,
+28956,
+31510,
+41017,
+7854,
+1945,
+17143,
+56459,
+60585,
+25959,
+18160,
+38224,
+1112,
+28736,
+51517,
+9879,
+45857,
+59680,
+34728,
+118,
+14320,
+20187,
+1108,
+57501,
+22733,
+16084,
+39148,
+40640,
+9433,
+50887,
+18970,
+14569,
+19896,
+26088,
+51993,
+29941,
+46467,
+55853,
+2133,
+39833,
+9861,
+54181,
+12335,
+22532,
+13636,
+49030,
+51677,
+20841,
+61076,
+57412,
+44975,
+3453,
+50074,
+10305,
+7155,
+48879,
+20425,
+14481,
+47640,
+5886,
+11512,
+44846,
+39206,
+6714,
+3739,
+33523,
+15572,
+8746,
+48028,
+26799,
+38817,
+29143,
+21699,
+55490,
+62922,
+49066,
+10892,
+22701,
+25027,
+19936,
+38126,
+54341,
+24161,
+50335,
+46374,
+19041,
+2097,
+23773,
+33183,
+36279,
+19859,
+58981,
+20167,
+9103,
+62205,
+23178,
+6866,
+53761,
+40364,
+53861,
+21624,
+298,
+62031,
+54450,
+7030,
+3571,
+13890,
+65354,
+38531,
+23522,
+9209,
+32186,
+18428,
+18102,
+43060,
+16082,
+22735,
+43357,
+38078,
+39591,
+11272,
+19372,
+2890,
+19950,
+4025,
+15026,
+57788,
+58086,
+47302,
+3110,
+15590,
+46164,
+2880,
+45588,
+59713,
+56838,
+33312,
+41586,
+28944,
+17193,
+54976,
+12705,
+48068,
+7681,
+32633,
+60909,
+4491,
+26102,
+20049,
+33907,
+5206,
+18226,
+40696,
+10784,
+59753,
+5962,
+31599,
+18818,
+16609,
+7549,
+29714,
+54109,
+46841,
+52995,
+13076,
+39237,
+48570,
+43176,
+31559,
+57051,
+3917,
+26179,
+51602,
+16524,
+38066,
+59638,
+63549,
+17350,
+9085,
+33159,
+63864,
+38845,
+5506,
+24802,
+54332,
+5634,
+54117,
+53342,
+29921,
+25730,
+58223,
+61559,
+10597,
+52323,
+25198,
+50157,
+21010,
+3482,
+15054,
+17775,
+63831,
+38516,
+55950,
+21735,
+62550,
+14377,
+21912,
+8112,
+19776,
+7974,
+30185,
+44589,
+30606,
+49059,
+48403,
+18010,
+61328,
+30885,
+51421,
+19813,
+15603,
+22,
+19947,
+5826,
+63581,
+21355,
+55500,
+4435,
+14376,
+62575,
+21736,
+11862,
+59334,
+31903,
+56403,
+47297,
+4356,
+53655,
+16428,
+28559,
+9072,
+64794,
+55204,
+63375,
+60625,
+12265,
+12370,
+45407,
+54963,
+56296,
+61332,
+36090,
+19095,
+42204,
+64906,
+3850,
+64602,
+40909,
+26054,
+17226,
+17090,
+23879,
+24023,
+6660,
+45260,
+9604,
+8994,
+51498,
+57242,
+21391,
+13484,
+52382,
+62444,
+32282,
+35773,
+24914,
+5033,
+691,
+58435,
+37721,
+62409,
+18654,
+47579,
+3793,
+51598,
+47769,
+47172,
+30175,
+7509,
+60590,
+58979,
+19861,
+45704,
+39653,
+10114,
+17903,
+56288,
+901,
+36693,
+49544,
+18881,
+24488,
+2720,
+24472,
+35979,
+62028,
+37795,
+43449,
+19644,
+65131,
+27834,
+35961,
+34519,
+17606,
+43670,
+39753,
+54003,
+34676,
+50772,
+46517,
+50228,
+60088,
+52919,
+6977,
+58541,
+56375,
+40950,
+25535,
+48951,
+2123,
+47179,
+16188,
+5875,
+33606,
+32281,
+62507,
+52383,
+48004,
+24026,
+56755,
+19018,
+15311,
+4799,
+63804,
+23726,
+27161,
+5384,
+20727,
+49100,
+63955,
+15836,
+18234,
+2887,
+49948,
+9919,
+43558,
+30706,
+57479,
+48017,
+43418,
+14440,
+22420,
+7709,
+49158,
+44566,
+52715,
+57712,
+32024,
+48702,
+18653,
+62499,
+37722,
+57727,
+45631,
+49362,
+60161,
+36909,
+60039,
+13354,
+55321,
+63177,
+3946,
+3903,
+46216,
+60677,
+6610,
+9151,
+25673,
+44476,
+49689,
+8723,
+41957,
+40092,
+27600,
+44225,
+26273,
+20592,
+12505,
+19333,
+64416,
+63607,
+25414,
+8441,
+36884,
+40686,
+36467,
+31215,
+41455,
+57319,
+7905,
+20501,
+11333,
+47075,
+7275,
+8657,
+27925,
+27400,
+35648,
+42240,
+64815,
+34542,
+14534,
+6327,
+17162,
+32233,
+54748,
+16446,
+30289,
+34085,
+46536,
+25964,
+59084,
+55614,
+20497,
+1622,
+60652,
+39868,
+50604,
+50014,
+43705,
+28301,
+16963,
+2601,
+60828,
+22371,
+11581,
+46780,
+8118,
+23079,
+41893,
+17467,
+57858,
+54655,
+45433,
+24709,
+43161,
+56830,
+54570,
+26064,
+876,
+45436,
+35629,
+36371,
+51925,
+56038,
+40953,
+22259,
+17822,
+45689,
+51930,
+57152,
+28009,
+37070,
+9148,
+42157,
+56719,
+34795,
+49877,
+34814,
+20064,
+43860,
+35409,
+60824,
+49724,
+55288,
+19845,
+1596,
+51339,
+2443,
+15259,
+24193,
+64831,
+25751,
+36508,
+38109,
+8381,
+50049,
+45141,
+27032,
+57448,
+25728,
+29923,
+37533,
+22328,
+39278,
+39748,
+2550,
+21016,
+23859,
+3992,
+50356,
+11688,
+18178,
+39210,
+27701,
+1702,
+18396,
+3542,
+38550,
+43698,
+59839,
+59441,
+48893,
+52114,
+51539,
+36232,
+39414,
+5227,
+8237,
+8394,
+26450,
+53940,
+52330,
+14358,
+36770,
+8319,
+54455,
+63190,
+17796,
+22442,
+28198,
+19528,
+14438,
+43420,
+19153,
+44485,
+7121,
+6286,
+46764,
+27606,
+46181,
+17429,
+39342,
+23317,
+48104,
+58376,
+5557,
+22676,
+16740,
+10610,
+57972,
+39571,
+6455,
+36962,
+27797,
+65182,
+44618,
+17286,
+23468,
+2679,
+60476,
+35062,
+6042,
+23177,
+62685,
+9104,
+43462,
+53196,
+22769,
+35901,
+11682,
+34113,
+42913,
+37800,
+12021,
+55382,
+19422,
+11011,
+64068,
+18171,
+16580,
+57044,
+17157,
+4596,
+35799,
+38212,
+57467,
+47507,
+64502,
+5642,
+32183,
+1795,
+21202,
+39330,
+25322,
+42639,
+61777,
+57396,
+50136,
+49907,
+38898,
+8890,
+35530,
+12112,
+63574,
+8452,
+60545,
+63634,
+34768,
+25423,
+44836,
+26048,
+7116,
+19035,
+23622,
+61740,
+10826,
+55508,
+54369,
+29392,
+60024,
+41461,
+29134,
+12717,
+34604,
+30441,
+9790,
+6795,
+50851,
+45955,
+29374,
+64920,
+2448,
+46190,
+51686,
+52653,
+34405,
+58454,
+23571,
+50783,
+31057,
+11208,
+52026,
+46362,
+64058,
+30109,
+8888,
+38900,
+59532,
+63082,
+15090,
+53135,
+5883,
+38372,
+38514,
+63833,
+47143,
+25392,
+33878,
+48689,
+61006,
+37862,
+60894,
+13371,
+18924,
+5708,
+1126,
+8433,
+46368,
+35009,
+32616,
+3678,
+8516,
+3733,
+37719,
+58437,
+59048,
+799,
+55134,
+4468,
+31457,
+54611,
+25208,
+26625,
+20561,
+2809,
+15464,
+607,
+24254,
+39053,
+46869,
+1781,
+14528,
+18008,
+48405,
+53690,
+44631,
+54321,
+31218,
+30623,
+2400,
+53446,
+14675,
+17975,
+62800,
+23739,
+34655,
+53316,
+49379,
+41241,
+40645,
+54498,
+18211,
+44526,
+2955,
+50484,
+39183,
+15176,
+65443,
+7048,
+25234,
+13994,
+28487,
+50725,
+55759,
+31821,
+63530,
+54136,
+21427,
+57797,
+59373,
+47556,
+9868,
+1535,
+43735,
+23966,
+49957,
+54449,
+62677,
+299,
+37794,
+62474,
+35980,
+26226,
+42976,
+21286,
+18184,
+5724,
+21156,
+36132,
+48864,
+33709,
+38112,
+24011,
+36304,
+9971,
+6974,
+5150,
+21096,
+40006,
+26434,
+53633,
+25565,
+7981,
+11931,
+43815,
+22606,
+44161,
+37643,
+22698,
+39400,
+500,
+63445,
+47140,
+3275,
+26988,
+23060,
+33129,
+46593,
+1589,
+52884,
+25502,
+58651,
+59465,
+46625,
+33700,
+19012,
+14785,
+41191,
+9065,
+6760,
+11730,
+42102,
+52967,
+34498,
+29522,
+25505,
+49764,
+53683,
+32153,
+22611,
+10577,
+58506,
+2863,
+51648,
+15701,
+28771,
+23410,
+49391,
+44925,
+33318,
+60776,
+11112,
+30673,
+58310,
+60196,
+28069,
+33582,
+28163,
+5737,
+49445,
+54252,
+34448,
+52415,
+22168,
+26959,
+11944,
+17355,
+49335,
+46315,
+35100,
+39843,
+2312,
+10938,
+14865,
+37998,
+17148,
+33123,
+48762,
+49984,
+56364,
+30090,
+12472,
+43286,
+48681,
+54853,
+61348,
+34663,
+19121,
+57782,
+23168,
+29699,
+49370,
+25375,
+7111,
+12174,
+30942,
+25347,
+41661,
+5222,
+63568,
+37798,
+42915,
+62974,
+13840,
+16957,
+7340,
+43487,
+30191,
+36922,
+48225,
+63664,
+34695,
+18,
+55190,
+23027,
+60771,
+39502,
+10803,
+51107,
+50572,
+2712,
+11258,
+12673,
+17784,
+63725,
+14223,
+38856,
+39419,
+59641,
+52942,
+47590,
+16241,
+16702,
+39426,
+53156,
+12002,
+4238,
+55365,
+43179,
+35931,
+59178,
+7268,
+1696,
+45958,
+50317,
+27096,
+43727,
+11321,
+32920,
+3439,
+57022,
+56401,
+31905,
+35296,
+53330,
+17843,
+33471,
+49375,
+25614,
+61823,
+62961,
+7051,
+54990,
+28668,
+32174,
+55655,
+38258,
+55022,
+40985,
+49073,
+30793,
+44760,
+23038,
+32202,
+51573,
+10740,
+30637,
+35315,
+32300,
+59510,
+1926,
+24148,
+31859,
+29361,
+62960,
+61849,
+25615,
+21781,
+57303,
+12427,
+33165,
+14922,
+13826,
+21112,
+35453,
+38840,
+29065,
+50041,
+29278,
+17984,
+56275,
+44400,
+24258,
+57020,
+3441,
+27819,
+56711,
+10227,
+48859,
+58822,
+2822,
+32015,
+10844,
+32138,
+57938,
+17347,
+35,
+30949,
+8308,
+24113,
+49734,
+4841,
+5591,
+31703,
+27521,
+5185,
+9896,
+37183,
+12298,
+4168,
+57395,
+62173,
+42640,
+17264,
+25654,
+8758,
+25710,
+39625,
+31281,
+36021,
+63433,
+47058,
+53667,
+5473,
+1737,
+32317,
+6111,
+14423,
+20579,
+55389,
+10180,
+33410,
+52,
+13603,
+21222,
+61363,
+37002,
+65035,
+2094,
+21367,
+34576,
+15511,
+55977,
+17957,
+3041,
+13940,
+46748,
+10825,
+62154,
+23623,
+34692,
+64278,
+31415,
+32079,
+4808,
+50152,
+50845,
+31081,
+16905,
+30273,
+9446,
+7861,
+47274,
+32992,
+38535,
+51567,
+34306,
+38041,
+7383,
+51204,
+15050,
+16247,
+43051,
+14318,
+120,
+23291,
+2176,
+23964,
+43737,
+46345,
+16089,
+30236,
+35326,
+667,
+47584,
+42170,
+48482,
+13309,
+47876,
+28602,
+43771,
+34936,
+273,
+10286,
+9226,
+44038,
+53725,
+6765,
+34668,
+2708,
+630,
+3013,
+936,
+34623,
+1820,
+20695,
+32760,
+61169,
+30721,
+45475,
+16881,
+63958,
+38667,
+64977,
+17118,
+41232,
+65147,
+13816,
+63266,
+22145,
+56373,
+58543,
+21603,
+52521,
+27379,
+50633,
+30054,
+9046,
+46334,
+38244,
+13398,
+30522,
+5809,
+52487,
+17283,
+37240,
+51991,
+26090,
+20435,
+28841,
+49039,
+34150,
+18157,
+56966,
+11296,
+10014,
+51521,
+9807,
+39672,
+15283,
+38391,
+32382,
+65453,
+61590,
+3140,
+19322,
+59277,
+4849,
+28013,
+12393,
+29692,
+10677,
+45462,
+64046,
+39895,
+35682,
+52597,
+8485,
+2928,
+39359,
+1388,
+36898,
+32710,
+5130,
+43786,
+36994,
+55552,
+63851,
+5524,
+38825,
+2035,
+46643,
+36528,
+29785,
+33610,
+54492,
+59408,
+51667,
+20292,
+63723,
+17786,
+35380,
+19706,
+58970,
+18117,
+31489,
+2359,
+3139,
+61635,
+65454,
+55148,
+46075,
+30919,
+16434,
+24537,
+8967,
+24972,
+38749,
+10103,
+52572,
+12432,
+55723,
+13071,
+52949,
+41988,
+46276,
+20033,
+27084,
+1206,
+54950,
+16215,
+33792,
+23254,
+32785,
+58048,
+41443,
+18169,
+64070,
+10596,
+62588,
+58224,
+52479,
+50194,
+32966,
+45068,
+16302,
+46986,
+17938,
+5934,
+7543,
+30033,
+14405,
+41677,
+3267,
+15182,
+22956,
+689,
+5035,
+951,
+9983,
+1688,
+41403,
+52826,
+44330,
+25000,
+25583,
+37424,
+2274,
+10026,
+24838,
+25829,
+27081,
+37319,
+59836,
+42853,
+63790,
+45982,
+15911,
+49891,
+59975,
+29287,
+47066,
+11132,
+11227,
+51185,
+45586,
+2882,
+50512,
+25681,
+4995,
+59307,
+44022,
+25623,
+39514,
+21824,
+64481,
+58829,
+65196,
+24481,
+47830,
+41490,
+46833,
+12423,
+53263,
+22386,
+51454,
+54066,
+20782,
+12356,
+27442,
+40384,
+9337,
+37177,
+46759,
+1617,
+61447,
+40599,
+5930,
+59560,
+10869,
+6410,
+6247,
+36682,
+24411,
+56617,
+27850,
+56702,
+46598,
+56094,
+46124,
+54488,
+34733,
+58019,
+42216,
+51506,
+38431,
+16919,
+37158,
+27504,
+63496,
+4081,
+2040,
+4079,
+63498,
+8780,
+46521,
+14070,
+26866,
+19435,
+14733,
+40598,
+61483,
+1618,
+43355,
+22737,
+33761,
+30840,
+65521,
+30585,
+10571,
+19522,
+26007,
+28206,
+46221,
+16532,
+37671,
+57746,
+58530,
+11984,
+21617,
+59205,
+21160,
+64166,
+63481,
+42321,
+64852,
+29416,
+50745,
+58103,
+55081,
+48751,
+53559,
+11005,
+39796,
+28739,
+30496,
+63980,
+19839,
+54548,
+50788,
+39173,
+45019,
+11724,
+60636,
+16412,
+55280,
+10730,
+50563,
+21212,
+64951,
+19270,
+64698,
+12123,
+24699,
+5236,
+11877,
+13392,
+55120,
+42780,
+37461,
+40390,
+5164,
+43244,
+55798,
+22661,
+7494,
+22587,
+65332,
+40801,
+12499,
+57532,
+22166,
+52417,
+23730,
+40081,
+23152,
+50148,
+8668,
+53238,
+3758,
+8143,
+44430,
+60399,
+61060,
+37001,
+61753,
+21223,
+4253,
+5162,
+40392,
+49980,
+28645,
+9452,
+56338,
+6549,
+63767,
+13264,
+33019,
+57069,
+34662,
+61923,
+54854,
+30978,
+56016,
+38325,
+1422,
+64709,
+53651,
+6574,
+11693,
+38598,
+30069,
+46801,
+14770,
+52782,
+36089,
+62529,
+56297,
+41796,
+30884,
+62563,
+18011,
+44511,
+51829,
+45361,
+51089,
+11537,
+63283,
+38340,
+4770,
+39782,
+14707,
+57308,
+32427,
+60245,
+31760,
+45015,
+6746,
+51366,
+54609,
+31459,
+47983,
+36912,
+20550,
+46184,
+57163,
+8697,
+32227,
+53534,
+3665,
+10173,
+53898,
+55137,
+25878,
+59683,
+41061,
+14466,
+34312,
+33162,
+41948,
+9856,
+54388,
+29107,
+44313,
+4176,
+60376,
+27524,
+33985,
+47263,
+6880,
+48518,
+37953,
+17657,
+64599,
+30916,
+37610,
+38139,
+1398,
+45816,
+45621,
+22122,
+60733,
+14915,
+35085,
+35859,
+56168,
+57136,
+47804,
+10006,
+41470,
+60835,
+24607,
+26423,
+35401,
+51689,
+48755,
+22590,
+36316,
+14416,
+19548,
+33968,
+41354,
+31035,
+43106,
+48378,
+60943,
+23987,
+9260,
+21139,
+15294,
+47533,
+46135,
+33538,
+27985,
+45387,
+42864,
+34131,
+28720,
+35089,
+6861,
+26084,
+38273,
+58848,
+9712,
+25452,
+47231,
+55771,
+15048,
+51206,
+33922,
+55689,
+45618,
+47655,
+30312,
+24529,
+31268,
+64052,
+60371,
+22295,
+50985,
+9689,
+9490,
+802,
+32239,
+43651,
+8332,
+1920,
+44966,
+65401,
+17963,
+64812,
+50240,
+22208,
+33010,
+35945,
+19296,
+181,
+27008,
+14805,
+16746,
+1085,
+53891,
+65296,
+34681,
+43601,
+20787,
+18239,
+2994,
+11883,
+36889,
+13366,
+46402,
+31694,
+47162,
+42029,
+34045,
+5285,
+39043,
+30720,
+61681,
+32761,
+30249,
+21066,
+60965,
+5650,
+54764,
+60324,
+22270,
+41655,
+59624,
+7528,
+53073,
+47496,
+50596,
+30563,
+52606,
+56450,
+32380,
+38393,
+43537,
+42885,
+8166,
+59343,
+24457,
+28375,
+40763,
+56247,
+49600,
+36845,
+47223,
+25893,
+43597,
+42366,
+43071,
+65150,
+49748,
+31794,
+40057,
+42576,
+35073,
+59071,
+47362,
+30577,
+29456,
+58067,
+44795,
+48867,
+36078,
+27657,
+55702,
+41793,
+17007,
+5307,
+60661,
+39014,
+43479,
+50756,
+42334,
+32391,
+60490,
+19340,
+18448,
+57698,
+695,
+40020,
+10036,
+2985,
+51162,
+42554,
+33625,
+18573,
+37300,
+58424,
+53365,
+41700,
+6828,
+6738,
+14608,
+19807,
+33461,
+13184,
+46789,
+16491,
+41969,
+12451,
+21880,
+20404,
+25400,
+10293,
+10423,
+21635,
+57411,
+62731,
+20842,
+52923,
+12435,
+28211,
+11637,
+10402,
+3447,
+34081,
+13298,
+52198,
+26716,
+64233,
+23540,
+21997,
+37000,
+61365,
+60400,
+9584,
+48670,
+34487,
+58182,
+16461,
+15595,
+39556,
+45274,
+15161,
+37602,
+38236,
+4627,
+2904,
+39436,
+63597,
+10952,
+39468,
+14912,
+6048,
+21258,
+32062,
+14957,
+48341,
+13844,
+18571,
+33627,
+611,
+51938,
+9539,
+63429,
+36642,
+5769,
+36495,
+40888,
+58218,
+23796,
+12730,
+22044,
+27509,
+21425,
+54138,
+54619,
+43068,
+35841,
+7752,
+39215,
+39496,
+30220,
+33239,
+21754,
+26264,
+37861,
+62109,
+48690,
+52019,
+65378,
+37747,
+5996,
+37037,
+28176,
+60007,
+11072,
+47208,
+30835,
+64758,
+39677,
+63475,
+14056,
+14370,
+2003,
+15862,
+27756,
+25201,
+6666,
+18511,
+17111,
+22402,
+14543,
+41606,
+15083,
+35737,
+53565,
+38006,
+589,
+12553,
+64259,
+40442,
+31448,
+59553,
+58177,
+5006,
+57750,
+5649,
+61165,
+21067,
+6462,
+52315,
+14193,
+19989,
+50434,
+65308,
+22709,
+59371,
+57799,
+65394,
+24462,
+2530,
+60178,
+34433,
+19184,
+29748,
+23659,
+55159,
+44286,
+23986,
+61243,
+48379,
+3351,
+29739,
+20083,
+13096,
+18906,
+19842,
+18556,
+42614,
+18491,
+1167,
+46741,
+11480,
+50959,
+2204,
+36351,
+36002,
+17720,
+7686,
+33848,
+9827,
+52688,
+55331,
+51615,
+34010,
+57125,
+44391,
+45615,
+60873,
+1800,
+35808,
+27300,
+4490,
+62634,
+32634,
+8592,
+58079,
+9607,
+2136,
+18739,
+47608,
+47315,
+47644,
+65212,
+18408,
+62811,
+34922,
+13370,
+62107,
+37863,
+56747,
+12230,
+56370,
+32698,
+44321,
+25045,
+32691,
+43264,
+37663,
+13589,
+31072,
+25435,
+50071,
+64932,
+54579,
+40602,
+42379,
+32403,
+1799,
+60914,
+45616,
+55691,
+51705,
+50304,
+56722,
+21289,
+64162,
+53852,
+29634,
+53857,
+25612,
+49377,
+53318,
+32040,
+31711,
+26985,
+21841,
+17875,
+7865,
+48733,
+6183,
+16629,
+47737,
+15867,
+32468,
+12255,
+39705,
+41666,
+44883,
+31121,
+16639,
+27228,
+43296,
+32908,
+47113,
+36631,
+24606,
+61258,
+41471,
+52594,
+59436,
+22853,
+42118,
+22370,
+62336,
+2602,
+59722,
+49723,
+62297,
+35410,
+56808,
+49792,
+29302,
+34741,
+7805,
+63968,
+26709,
+27661,
+7618,
+20241,
+26728,
+8362,
+43964,
+51329,
+49863,
+35983,
+13728,
+51895,
+4791,
+56580,
+30201,
+51353,
+20559,
+26627,
+49011,
+27473,
+10962,
+26107,
+27124,
+5070,
+8660,
+20810,
+16153,
+23341,
+16367,
+38315,
+38638,
+35513,
+14216,
+49201,
+53229,
+40499,
+5621,
+27617,
+36590,
+11111,
+61958,
+33319,
+26501,
+28160,
+39501,
+61893,
+23028,
+20659,
+27196,
+4218,
+25756,
+45582,
+65180,
+27799,
+26105,
+10964,
+52586,
+22152,
+44569,
+57955,
+55873,
+39155,
+40380,
+53468,
+37076,
+9873,
+47438,
+22927,
+19761,
+13641,
+4733,
+8958,
+43468,
+11497,
+36862,
+18352,
+40713,
+50225,
+3100,
+1734,
+36188,
+6046,
+14914,
+61267,
+22123,
+9341,
+33457,
+3803,
+15386,
+54596,
+8909,
+55336,
+817,
+20271,
+14591,
+52895,
+46231,
+60164,
+28097,
+9613,
+16041,
+12735,
+28658,
+17376,
+38059,
+16554,
+8445,
+2647,
+49129,
+46093,
+39816,
+55975,
+15513,
+47031,
+17716,
+36685,
+9796,
+32037,
+52305,
+15802,
+3750,
+59947,
+11516,
+29102,
+46559,
+30226,
+46675,
+6259,
+3906,
+37525,
+1351,
+47748,
+21337,
+45055,
+29792,
+8624,
+29240,
+29207,
+6609,
+62395,
+46217,
+9503,
+15013,
+37929,
+19358,
+1064,
+44800,
+25889,
+45320,
+20953,
+148,
+59820,
+10813,
+40648,
+39013,
+61115,
+5308,
+25406,
+10840,
+28565,
+33366,
+13196,
+56907,
+39867,
+62344,
+1623,
+17152,
+57908,
+58553,
+25022,
+5467,
+11615,
+48031,
+308,
+6788,
+1700,
+27703,
+9674,
+15906,
+16411,
+61405,
+11725,
+28030,
+51732,
+55810,
+40276,
+57617,
+40313,
+40732,
+31060,
+12264,
+62535,
+63376,
+25700,
+23828,
+51979,
+18047,
+55316,
+32109,
+54441,
+13451,
+38494,
+56205,
+56899,
+28304,
+26276,
+52262,
+8498,
+27337,
+51406,
+22714,
+39479,
+6563,
+7000,
+13041,
+56920,
+36200,
+46359,
+28038,
+39362,
+58149,
+46348,
+59332,
+11864,
+57214,
+58978,
+62490,
+7510,
+40906,
+31471,
+25958,
+62771,
+56460,
+14089,
+10806,
+26593,
+17042,
+27534,
+31176,
+43960,
+40992,
+52633,
+33026,
+42974,
+26228,
+17539,
+18657,
+53915,
+43854,
+51256,
+47396,
+25822,
+369,
+11160,
+2661,
+42765,
+38648,
+54070,
+48465,
+26149,
+26499,
+33321,
+40377,
+11348,
+58464,
+51591,
+42237,
+8133,
+37491,
+15721,
+63633,
+62163,
+8453,
+39855,
+4028,
+6231,
+8761,
+41790,
+53502,
+20015,
+7129,
+40656,
+8554,
+37025,
+7359,
+4569,
+7788,
+11503,
+23250,
+6763,
+53727,
+26662,
+8014,
+43199,
+21654,
+12948,
+16799,
+8868,
+42712,
+52051,
+13157,
+32899,
+60305,
+43400,
+11438,
+52693,
+52500,
+23866,
+44647,
+10254,
+60157,
+40622,
+3034,
+64847,
+3996,
+64587,
+43301,
+56392,
+63352,
+57826,
+16381,
+29129,
+38727,
+48685,
+25942,
+19339,
+61109,
+32392,
+33424,
+24359,
+53574,
+25932,
+49584,
+18271,
+1841,
+29852,
+6104,
+20537,
+285,
+35061,
+62209,
+2680,
+50431,
+1333,
+63622,
+27154,
+8045,
+7177,
+38412,
+4744,
+56878,
+37614,
+15748,
+57981,
+18105,
+29063,
+38842,
+24044,
+36246,
+22280,
+38992,
+40016,
+23337,
+12517,
+63114,
+58205,
+23280,
+44915,
+49787,
+7558,
+50218,
+44056,
+1853,
+12055,
+16619,
+43507,
+63274,
+12894,
+52517,
+1632,
+17820,
+22261,
+56303,
+51143,
+11002,
+1091,
+15425,
+59263,
+15714,
+64216,
+36096,
+64396,
+17650,
+33358,
+34684,
+35907,
+65204,
+64551,
+10447,
+26202,
+27771,
+37298,
+18575,
+39688,
+33913,
+30953,
+10060,
+21458,
+32826,
+3922,
+21752,
+33241,
+25676,
+41117,
+30401,
+9583,
+61059,
+61366,
+44431,
+24570,
+10670,
+18825,
+7684,
+17722,
+40596,
+14735,
+44693,
+12616,
+58562,
+3525,
+44442,
+30924,
+51229,
+30912,
+63719,
+14681,
+21744,
+65321,
+5183,
+27523,
+61283,
+4177,
+25146,
+4150,
+22294,
+61211,
+64053,
+39553,
+36756,
+27343,
+14499,
+42817,
+46052,
+38,
+38651,
+22981,
+31914,
+35553,
+26764,
+22609,
+32155,
+215,
+5159,
+38015,
+3754,
+55642,
+31246,
+30002,
+56355,
+31531,
+39302,
+40279,
+32270,
+26611,
+65373,
+29754,
+58122,
+16151,
+20812,
+57055,
+8934,
+1600,
+20796,
+2355,
+31629,
+2908,
+47312,
+20021,
+26786,
+55436,
+44949,
+22269,
+61162,
+54765,
+48848,
+15266,
+5251,
+19430,
+43436,
+55832,
+23875,
+3627,
+1970,
+13135,
+22916,
+26122,
+3285,
+57301,
+21783,
+64121,
+43399,
+60514,
+32900,
+44764,
+40837,
+4143,
+53661,
+15327,
+48987,
+18598,
+64199,
+46421,
+25,
+57976,
+16706,
+64675,
+39582,
+22366,
+18229,
+50270,
+5217,
+46108,
+7311,
+13060,
+55086,
+11459,
+16431,
+10981,
+19939,
+50922,
+436,
+25196,
+52325,
+16322,
+2141,
+33565,
+39693,
+6805,
+6443,
+37519,
+55814,
+55878,
+57334,
+14104,
+24873,
+56730,
+32576,
+14888,
+10838,
+25408,
+34388,
+31627,
+2357,
+31491,
+4365,
+26495,
+59525,
+20313,
+14064,
+16440,
+31759,
+61314,
+32428,
+2414,
+38889,
+40973,
+55738,
+7954,
+26430,
+30011,
+58762,
+14697,
+16548,
+46941,
+6634,
+48529,
+4421,
+4877,
+40132,
+20451,
+28357,
+44995,
+24029,
+37455,
+22343,
+37219,
+37212,
+12262,
+31062,
+27188,
+64798,
+35256,
+53779,
+44155,
+17255,
+37346,
+17593,
+52929,
+22973,
+30088,
+56366,
+1266,
+36830,
+5615,
+10282,
+479,
+48198,
+6151,
+40152,
+28068,
+61954,
+58311,
+1153,
+51397,
+64458,
+38849,
+14463,
+11605,
+13943,
+32793,
+1286,
+15074,
+18034,
+16219,
+39004,
+21034,
+30964,
+34432,
+60951,
+2531,
+62872,
+40322,
+11487,
+54773,
+8270,
+24097,
+22212,
+37884,
+34630,
+7408,
+13349,
+28096,
+60719,
+46232,
+36908,
+62404,
+49363,
+17219,
+40621,
+60506,
+10255,
+54809,
+38375,
+3104,
+44811,
+55109,
+11302,
+58470,
+54677,
+20877,
+51654,
+5822,
+8701,
+32890,
+64468,
+9267,
+23400,
+29539,
+63437,
+23387,
+52068,
+28862,
+9910,
+17597,
+11907,
+11432,
+49780,
+37216,
+32508,
+40788,
+55896,
+37576,
+6768,
+15434,
+52006,
+1924,
+59512,
+32454,
+10558,
+9758,
+19053,
+7482,
+28859,
+34593,
+40192,
+27140,
+37291,
+27130,
+30831,
+54759,
+25317,
+12439,
+48432,
+11952,
+52719,
+20628,
+33775,
+20524,
+27774,
+1530,
+59117,
+20533,
+47151,
+6585,
+28610,
+5668,
+52278,
+52918,
+62458,
+50229,
+26739,
+63217,
+64888,
+41973,
+27236,
+16132,
+32150,
+52749,
+56497,
+23130,
+13105,
+28089,
+58639,
+57081,
+48772,
+40514,
+4891,
+53923,
+7202,
+17816,
+30774,
+11869,
+3772,
+63801,
+29351,
+8988,
+15771,
+11856,
+56219,
+15870,
+9482,
+9552,
+52126,
+48125,
+47937,
+26437,
+13449,
+54443,
+2376,
+32673,
+25661,
+20179,
+26284,
+36037,
+39680,
+29847,
+13353,
+62402,
+36910,
+47985,
+10210,
+23954,
+51484,
+38091,
+26691,
+2940,
+40736,
+8793,
+14347,
+14951,
+18064,
+41460,
+62149,
+29393,
+62887,
+15909,
+45984,
+14478,
+36420,
+15755,
+28323,
+53277,
+1685,
+3927,
+26713,
+33741,
+25482,
+41340,
+11071,
+60998,
+28177,
+63061,
+62943,
+45748,
+48392,
+9080,
+36562,
+19112,
+29284,
+556,
+58949,
+14368,
+14058,
+58527,
+54037,
+37708,
+54561,
+55697,
+28310,
+29840,
+51759,
+17364,
+21468,
+59,
+57892,
+7160,
+3314,
+42862,
+45389,
+554,
+29286,
+61519,
+49892,
+44135,
+59790,
+8042,
+16811,
+19574,
+2589,
+20320,
+19387,
+37485,
+26577,
+58212,
+19714,
+9485,
+51976,
+40053,
+24199,
+9948,
+49050,
+13044,
+57766,
+58765,
+15865,
+47739,
+43097,
+56002,
+11515,
+60695,
+3751,
+33835,
+19217,
+52685,
+29600,
+37699,
+21510,
+59859,
+913,
+38936,
+18546,
+11266,
+49606,
+6818,
+12476,
+24515,
+17369,
+58607,
+13063,
+43189,
+40308,
+30538,
+43271,
+28706,
+5334,
+55256,
+12286,
+34495,
+64324,
+25037,
+4757,
+37921,
+338,
+36346,
+25073,
+57208,
+11577,
+42981,
+7300,
+40009,
+47148,
+23820,
+11204,
+2943,
+11421,
+5756,
+46470,
+22423,
+43025,
+19603,
+1034,
+3357,
+33355,
+34638,
+43850,
+49530,
+824,
+7786,
+4571,
+4373,
+10774,
+24014,
+23233,
+46168,
+31372,
+52177,
+25997,
+47340,
+36735,
+43618,
+65236,
+27745,
+47981,
+31461,
+38952,
+24533,
+36926,
+5446,
+13260,
+44119,
+49485,
+45282,
+52760,
+9968,
+21965,
+19063,
+912,
+59939,
+21511,
+44849,
+48475,
+57936,
+32140,
+23187,
+54032,
+42289,
+42731,
+33817,
+18766,
+44655,
+31139,
+49360,
+45633,
+50908,
+64064,
+42698,
+59440,
+62259,
+43699,
+42852,
+61525,
+37320,
+21386,
+63871,
+12085,
+11589,
+46612,
+3433,
+64412,
+59649,
+54027,
+20182,
+24400,
+59699,
+48932,
+10812,
+60665,
+149,
+10050,
+63784,
+34378,
+27225,
+47352,
+3515,
+44382,
+34554,
+34136,
+6945,
+20900,
+32677,
+8691,
+52034,
+31391,
+11761,
+41562,
+37373,
+3587,
+16093,
+15976,
+9214,
+45788,
+9051,
+50428,
+21530,
+42003,
+8041,
+59972,
+44136,
+57352,
+54447,
+49959,
+19039,
+46376,
+52564,
+54115,
+5636,
+19068,
+9686,
+34356,
+1445,
+11649,
+9975,
+28703,
+34781,
+14176,
+7513,
+59389,
+28894,
+4510,
+29043,
+45232,
+23651,
+33864,
+1555,
+45700,
+7795,
+25128,
+23082,
+42992,
+1440,
+57488,
+22794,
+5961,
+62625,
+10785,
+28419,
+8471,
+16656,
+45114,
+20793,
+37516,
+14472,
+43318,
+41365,
+37852,
+27488,
+46024,
+39121,
+52902,
+24034,
+6417,
+36609,
+37653,
+49085,
+17183,
+63223,
+54601,
+35014,
+3881,
+13051,
+55601,
+42793,
+18625,
+49722,
+60826,
+2603,
+25496,
+9113,
+44735,
+20348,
+13882,
+5392,
+56837,
+62645,
+45589,
+2981,
+1957,
+48568,
+39239,
+9409,
+35820,
+41073,
+63009,
+36543,
+921,
+14857,
+48931,
+59823,
+24401,
+56189,
+6676,
+59266,
+62789,
+6538,
+52609,
+52387,
+53880,
+24285,
+64607,
+21588,
+45196,
+48548,
+41060,
+61294,
+25879,
+34727,
+62762,
+45858,
+46924,
+25801,
+49122,
+8262,
+9479,
+19033,
+7118,
+29819,
+13304,
+64543,
+67,
+8675,
+23788,
+37311,
+8771,
+49348,
+3090,
+31932,
+48085,
+52191,
+54891,
+2753,
+47952,
+12401,
+3420,
+27166,
+17896,
+2817,
+54026,
+59827,
+64413,
+26671,
+43721,
+47729,
+59020,
+12865,
+52941,
+61880,
+39420,
+63548,
+62604,
+38067,
+36328,
+58930,
+55012,
+12599,
+55515,
+57847,
+42482,
+10505,
+29114,
+1741,
+52709,
+7527,
+61159,
+41656,
+41516,
+64684,
+58692,
+19258,
+45784,
+27975,
+33737,
+43636,
+23723,
+31275,
+8871,
+39957,
+58405,
+10956,
+24937,
+4056,
+5978,
+9888,
+47598,
+3987,
+57995,
+47861,
+35048,
+55063,
+38755,
+15303,
+59547,
+57298,
+57145,
+56970,
+58614,
+9005,
+23484,
+30664,
+18056,
+48211,
+37080,
+58479,
+30341,
+42675,
+22528,
+40570,
+19660,
+11,
+27308,
+42742,
+5783,
+2913,
+33939,
+64559,
+49573,
+43446,
+63571,
+27750,
+51033,
+34986,
+45967,
+7188,
+43823,
+64056,
+46364,
+10868,
+61480,
+5931,
+55650,
+29667,
+51862,
+48720,
+58176,
+60970,
+31449,
+56417,
+23962,
+2178,
+57297,
+59596,
+15304,
+8214,
+22921,
+47048,
+27789,
+4536,
+28501,
+32397,
+44223,
+27602,
+7328,
+64456,
+51399,
+63081,
+62121,
+38901,
+16627,
+6185,
+1516,
+55472,
+20312,
+60250,
+26496,
+28148,
+18054,
+30666,
+40028,
+18045,
+51981,
+42316,
+20681,
+21102,
+21327,
+32453,
+60120,
+1925,
+61829,
+32301,
+62939,
+53223,
+32687,
+13027,
+51176,
+8613,
+15225,
+8545,
+14840,
+44708,
+11829,
+44780,
+30474,
+3330,
+42361,
+22630,
+29348,
+4802,
+50624,
+4497,
+44280,
+25749,
+64833,
+28621,
+53194,
+43464,
+36389,
+6742,
+10041,
+26191,
+30470,
+31222,
+1676,
+16141,
+13663,
+50655,
+25091,
+12457,
+10759,
+35285,
+57546,
+33114,
+46624,
+61986,
+58652,
+51629,
+49916,
+63973,
+31868,
+9327,
+35081,
+56558,
+19119,
+34665,
+37579,
+25799,
+46926,
+56653,
+7246,
+23586,
+26133,
+47856,
+20637,
+36523,
+16274,
+30295,
+48892,
+62258,
+59840,
+42699,
+52911,
+22852,
+60832,
+52595,
+35684,
+5057,
+45001,
+41720,
+27054,
+28144,
+64078,
+33219,
+3775,
+51115,
+32999,
+24772,
+9292,
+42209,
+20485,
+12244,
+48561,
+1360,
+39198,
+12236,
+42332,
+50758,
+52451,
+31014,
+599,
+51666,
+61602,
+54493,
+10636,
+9822,
+40799,
+65334,
+19143,
+13917,
+57453,
+50749,
+30959,
+50028,
+44353,
+8803,
+11234,
+21835,
+2261,
+52645,
+28893,
+59770,
+7514,
+17338,
+5338,
+25386,
+30814,
+56027,
+6197,
+25114,
+53477,
+30044,
+39020,
+59228,
+10297,
+36257,
+47555,
+62039,
+57798,
+60956,
+22710,
+20475,
+46542,
+45536,
+21389,
+57244,
+27561,
+33830,
+44597,
+56358,
+14875,
+8094,
+47994,
+35706,
+34905,
+16803,
+4685,
+48909,
+6008,
+64022,
+26969,
+58960,
+13226,
+24002,
+13317,
+12013,
+24456,
+61146,
+8167,
+9509,
+6155,
+40424,
+49647,
+35168,
+58699,
+31902,
+62547,
+11863,
+60594,
+46349,
+21455,
+2692,
+20857,
+43132,
+37406,
+24550,
+16466,
+64746,
+33765,
+7918,
+25226,
+9117,
+14901,
+34212,
+38747,
+24974,
+51625,
+37728,
+47134,
+21779,
+25617,
+15353,
+44021,
+61508,
+4996,
+56777,
+39829,
+21709,
+58936,
+7220,
+52024,
+11210,
+27406,
+36623,
+10081,
+24579,
+15820,
+22283,
+44307,
+59126,
+4060,
+28856,
+49470,
+16012,
+48238,
+24351,
+26873,
+42095,
+29620,
+64018,
+51334,
+18861,
+4848,
+61632,
+19323,
+55584,
+54152,
+24681,
+34437,
+62849,
+26928,
+51223,
+30432,
+62788,
+59695,
+6677,
+15713,
+60429,
+15426,
+64357,
+2558,
+1973,
+167,
+34720,
+12521,
+23108,
+19005,
+63107,
+58415,
+3305,
+18774,
+37344,
+17257,
+45605,
+948,
+22238,
+18829,
+57613,
+64777,
+37130,
+29641,
+53747,
+21048,
+20046,
+27802,
+13800,
+53538,
+54881,
+38948,
+45905,
+56611,
+10296,
+59377,
+39021,
+16418,
+42435,
+53339,
+32208,
+33853,
+64572,
+48066,
+12707,
+32950,
+36989,
+55905,
+18442,
+43947,
+20343,
+1834,
+51002,
+3180,
+48376,
+43108,
+53496,
+21159,
+61428,
+21618,
+36721,
+18849,
+43019,
+29971,
+38435,
+26343,
+56306,
+37683,
+36406,
+26812,
+37097,
+10143,
+46731,
+8529,
+13700,
+45687,
+17824,
+16263,
+21120,
+34940,
+65176,
+23121,
+17271,
+24289,
+7267,
+61868,
+35932,
+44859,
+8581,
+17252,
+48885,
+9030,
+27374,
+33453,
+8223,
+1029,
+47913,
+43225,
+49811,
+18243,
+23268,
+14413,
+12982,
+6639,
+29542,
+40140,
+27807,
+18405,
+43017,
+18851,
+811,
+34303,
+30326,
+48278,
+19934,
+25029,
+25302,
+47612,
+25007,
+43066,
+54621,
+41833,
+46919,
+48264,
+41250,
+932,
+34069,
+53906,
+30414,
+18700,
+43954,
+18643,
+38559,
+39760,
+23390,
+54667,
+4059,
+59291,
+44308,
+50259,
+53715,
+378,
+41502,
+5801,
+28929,
+20532,
+60096,
+1531,
+55485,
+1755,
+45123,
+19656,
+54349,
+40605,
+28415,
+2365,
+27285,
+25905,
+13032,
+25194,
+438,
+49415,
+22718,
+19487,
+50583,
+19084,
+50954,
+18783,
+58731,
+5269,
+28019,
+51067,
+53245,
+10196,
+43898,
+55334,
+8911,
+52838,
+55613,
+62348,
+25965,
+3320,
+28576,
+31718,
+46773,
+62838,
+18535,
+44469,
+4574,
+13223,
+53847,
+47361,
+61128,
+35074,
+50166,
+23909,
+57707,
+53333,
+17407,
+7715,
+10032,
+44302,
+21930,
+769,
+50473,
+48078,
+30412,
+53908,
+24862,
+20208,
+27644,
+37109,
+42691,
+27217,
+798,
+62093,
+58438,
+12752,
+33990,
+30987,
+40398,
+50018,
+44865,
+44616,
+65184,
+53436,
+45644,
+49246,
+18254,
+49896,
+51823,
+1884,
+36959,
+33821,
+57793,
+46477,
+55339,
+21641,
+13516,
+161,
+28724,
+44030,
+12864,
+59644,
+47730,
+39851,
+51535,
+17236,
+11164,
+21561,
+19639,
+57773,
+19874,
+22582,
+1275,
+57577,
+10508,
+13858,
+44698,
+36726,
+1996,
+19626,
+25178,
+2747,
+5844,
+12929,
+9089,
+1032,
+19605,
+53287,
+46619,
+55744,
+851,
+6847,
+9198,
+4183,
+1771,
+19418,
+19275,
+9361,
+21849,
+20166,
+62688,
+19860,
+62489,
+60591,
+57215,
+58355,
+26057,
+4933,
+58538,
+23779,
+18116,
+61595,
+19707,
+19704,
+35382,
+21972,
+36887,
+11885,
+4197,
+53845,
+13225,
+59349,
+26970,
+65278,
+26165,
+38410,
+7179,
+48314,
+18385,
+5444,
+36928,
+14367,
+59996,
+557,
+31595,
+49021,
+8898,
+11173,
+11221,
+45384,
+14099,
+31482,
+9764,
+50491,
+7219,
+59302,
+21710,
+28714,
+42345,
+64515,
+55011,
+59635,
+36329,
+4859,
+3143,
+56387,
+23382,
+12378,
+13803,
+9729,
+55859,
+41103,
+41483,
+2208,
+19671,
+51709,
+44427,
+29033,
+24052,
+63331,
+29188,
+15289,
+35547,
+40297,
+11338,
+46037,
+8370,
+18595,
+5305,
+17009,
+28986,
+16994,
+52554,
+6377,
+35996,
+2102,
+18481,
+34273,
+46667,
+55990,
+32591,
+52542,
+22309,
+8940,
+8643,
+50382,
+11734,
+660,
+34108,
+35324,
+30238,
+571,
+17531,
+41513,
+3021,
+20278,
+44357,
+13731,
+64111,
+40335,
+4552,
+47027,
+21731,
+53430,
+26598,
+28215,
+44153,
+53781,
+50719,
+9347,
+5841,
+6272,
+49825,
+17888,
+54225,
+58278,
+18900,
+8718,
+38779,
+9284,
+27691,
+50640,
+9711,
+61226,
+38274,
+20716,
+32034,
+57586,
+41854,
+43042,
+8290,
+49713,
+28522,
+7758,
+41347,
+8587,
+2298,
+25372,
+50668,
+30711,
+19106,
+65195,
+61502,
+64482,
+56522,
+37539,
+32251,
+26330,
+2821,
+61799,
+48860,
+18421,
+33666,
+17762,
+10601,
+36157,
+19665,
+38788,
+12661,
+22190,
+54088,
+34527,
+63657,
+8337,
+28698,
+14342,
+25068,
+38964,
+57722,
+46776,
+19449,
+58708,
+4445,
+50930,
+17765,
+29147,
+6056,
+55497,
+31787,
+20521,
+4226,
+14172,
+63041,
+31443,
+31817,
+62780,
+17384,
+31075,
+45480,
+27185,
+22418,
+14442,
+13024,
+56471,
+3558,
+53808,
+42286,
+5010,
+11811,
+3846,
+30870,
+27960,
+5413,
+50104,
+27754,
+15864,
+59953,
+57767,
+14696,
+60236,
+30012,
+36129,
+53499,
+55705,
+14567,
+18972,
+39127,
+23894,
+17302,
+21704,
+47989,
+65228,
+4895,
+63838,
+36166,
+22978,
+58194,
+849,
+55746,
+3395,
+14687,
+21280,
+32460,
+19076,
+1224,
+65431,
+23095,
+48765,
+25843,
+5268,
+59095,
+18784,
+49971,
+21820,
+29227,
+32552,
+44554,
+64248,
+58160,
+63681,
+48297,
+15930,
+5604,
+53062,
+26013,
+56073,
+23181,
+53588,
+32029,
+18504,
+47823,
+23836,
+4444,
+58800,
+19450,
+33253,
+29766,
+37262,
+31541,
+30031,
+7545,
+31901,
+59336,
+35169,
+35710,
+29746,
+19186,
+44084,
+19257,
+59620,
+64685,
+6547,
+56340,
+42811,
+12696,
+49026,
+16939,
+14211,
+57113,
+38500,
+30503,
+52529,
+18931,
+47956,
+30447,
+4308,
+39454,
+35237,
+9462,
+42358,
+2854,
+11777,
+563,
+582,
+36985,
+17102,
+23184,
+16687,
+50273,
+38388,
+16308,
+42894,
+1839,
+18273,
+4258,
+17020,
+34635,
+17653,
+51628,
+59464,
+61987,
+25503,
+29524,
+54435,
+33168,
+18983,
+27567,
+42262,
+2234,
+38942,
+48497,
+57080,
+60074,
+28090,
+12604,
+40779,
+46081,
+56062,
+43049,
+16249,
+34173,
+44928,
+7732,
+9315,
+33072,
+25936,
+12127,
+7442,
+52295,
+24234,
+56203,
+38496,
+33659,
+47417,
+7264,
+44411,
+9004,
+59592,
+56971,
+39767,
+26845,
+34410,
+55084,
+13062,
+59929,
+17370,
+15609,
+30410,
+48080,
+57665,
+1200,
+17081,
+26723,
+18200,
+40106,
+11642,
+41756,
+47423,
+31400,
+5728,
+44638,
+8415,
+50096,
+48759,
+26658,
+56184,
+57458,
+20746,
+53389,
+35308,
+53091,
+21629,
+27249,
+16170,
+41874,
+56301,
+22263,
+30759,
+34246,
+29988,
+25242,
+33046,
+52360,
+29999,
+7588,
+42325,
+65490,
+55565,
+3524,
+60388,
+12617,
+3081,
+6997,
+41760,
+47012,
+30905,
+58131,
+25021,
+60648,
+57909,
+48679,
+43288,
+31765,
+42788,
+27068,
+56500,
+43482,
+21602,
+61667,
+56374,
+62455,
+6978,
+23778,
+58973,
+4934,
+52497,
+33536,
+46137,
+54636,
+57884,
+11983,
+61431,
+57747,
+54036,
+59993,
+14059,
+64107,
+43249,
+32918,
+11323,
+18897,
+21360,
+31237,
+48945,
+17990,
+34192,
+13113,
+55608,
+21488,
+33137,
+10643,
+47910,
+9092,
+11086,
+2862,
+61967,
+10578,
+41458,
+18066,
+8170,
+24432,
+24056,
+49623,
+16759,
+19056,
+42593,
+44774,
+21551,
+18152,
+43927,
+37754,
+93,
+3487,
+40304,
+2575,
+3690,
+10548,
+18993,
+20003,
+14603,
+13217,
+30340,
+59585,
+37081,
+45551,
+26406,
+36759,
+18530,
+38734,
+45426,
+54676,
+60149,
+11303,
+53531,
+54849,
+1761,
+51590,
+60552,
+11349,
+21787,
+19700,
+1315,
+250,
+7573,
+64491,
+49044,
+23570,
+62132,
+34406,
+43679,
+13494,
+40615,
+11530,
+49266,
+49333,
+17357,
+56065,
+21473,
+33563,
+2143,
+26442,
+25561,
+12751,
+59047,
+62094,
+37720,
+62501,
+692,
+56081,
+31657,
+16850,
+39597,
+9506,
+18069,
+40161,
+3622,
+53364,
+61096,
+37301,
+39488,
+3380,
+46503,
+5865,
+35719,
+21051,
+3304,
+59252,
+63108,
+17249,
+47389,
+14761,
+51762,
+45521,
+5001,
+24882,
+10955,
+59610,
+39958,
+48845,
+55176,
+44074,
+40257,
+38057,
+17378,
+29253,
+22173,
+34200,
+367,
+25824,
+21757,
+40619,
+17221,
+47154,
+41262,
+11705,
+45008,
+57464,
+32599,
+55231,
+2812,
+16659,
+53001,
+30388,
+7644,
+5556,
+62224,
+48105,
+31666,
+29155,
+2305,
+30098,
+14768,
+46803,
+45992,
+16791,
+13462,
+46850,
+47949,
+35637,
+33586,
+57491,
+5068,
+27126,
+6582,
+17224,
+26056,
+58976,
+57216,
+11084,
+9094,
+46248,
+19569,
+5972,
+21715,
+41446,
+51356,
+57436,
+37438,
+55926,
+43831,
+17550,
+55329,
+52690,
+33647,
+1156,
+11613,
+5469,
+47722,
+40201,
+1610,
+18999,
+39575,
+8574,
+28073,
+49330,
+53313,
+24381,
+46193,
+24992,
+58243,
+28182,
+54876,
+15045,
+6600,
+6236,
+13514,
+21643,
+51564,
+57109,
+1152,
+60195,
+61955,
+30674,
+48602,
+50100,
+53374,
+42329,
+8490,
+28891,
+52647,
+11584,
+44935,
+47618,
+11036,
+24685,
+35796,
+45456,
+29398,
+36324,
+8883,
+6087,
+41952,
+15471,
+10849,
+45442,
+31354,
+53599,
+19409,
+2611,
+39268,
+31784,
+21358,
+18899,
+58856,
+54226,
+15687,
+31487,
+18119,
+6920,
+21127,
+41247,
+6358,
+3670,
+63842,
+54512,
+20731,
+47864,
+46695,
+18461,
+3171,
+56881,
+19363,
+31832,
+63016,
+50116,
+8191,
+9,
+19662,
+27595,
+25747,
+44282,
+41317,
+63154,
+56321,
+19751,
+37564,
+38170,
+28181,
+58322,
+24993,
+25286,
+26504,
+1356,
+22555,
+5629,
+25529,
+39105,
+35528,
+8892,
+57660,
+64631,
+870,
+39880,
+29814,
+19428,
+5253,
+52478,
+61558,
+62589,
+25731,
+26471,
+38783,
+23795,
+61024,
+40889,
+22156,
+8351,
+30643,
+19713,
+59963,
+26578,
+37934,
+6001,
+1082,
+2119,
+23279,
+60451,
+63115,
+54358,
+38527,
+50734,
+24180,
+29589,
+48579,
+3634,
+22051,
+848,
+58745,
+22979,
+38653,
+57927,
+28295,
+13548,
+32443,
+54632,
+64378,
+38429,
+51508,
+16460,
+61055,
+34488,
+58072,
+28511,
+5005,
+60969,
+59554,
+48721,
+47851,
+17648,
+64398,
+26665,
+49880,
+41690,
+21985,
+49504,
+38979,
+57456,
+56186,
+12807,
+43561,
+63680,
+58723,
+64249,
+50465,
+36294,
+27552,
+27495,
+3817,
+14334,
+8404,
+16087,
+46347,
+60596,
+39363,
+19129,
+21423,
+27511,
+48694,
+53261,
+12425,
+57305,
+34350,
+48165,
+49930,
+22380,
+48358,
+11519,
+32047,
+16101,
+25020,
+58555,
+30906,
+5370,
+52189,
+48087,
+62999,
+50426,
+9053,
+16150,
+60340,
+29755,
+54704,
+963,
+17473,
+12074,
+20886,
+48698,
+15636,
+47449,
+64672,
+31480,
+14101,
+63693,
+33214,
+45576,
+53084,
+43659,
+55080,
+61420,
+50746,
+38982,
+18356,
+49351,
+43336,
+7461,
+53409,
+6913,
+13243,
+10556,
+32456,
+50495,
+19273,
+19420,
+55384,
+47301,
+62652,
+57789,
+35024,
+14639,
+12325,
+8992,
+9606,
+60906,
+8593,
+22200,
+7440,
+12129,
+20224,
+28510,
+58180,
+34489,
+14201,
+46678,
+44794,
+61124,
+29457,
+37882,
+22214,
+57198,
+36841,
+29411,
+52661,
+1411,
+34394,
+48776,
+46483,
+5422,
+36679,
+16069,
+54172,
+28969,
+57265,
+41442,
+61564,
+32786,
+26570,
+48243,
+42875,
+4154,
+42951,
+44825,
+27860,
+24867,
+26463,
+35189,
+42506,
+35564,
+27104,
+48620,
+24250,
+46529,
+55218,
+35361,
+3046,
+4948,
+24632,
+51360,
+5766,
+6149,
+48200,
+2084,
+42215,
+61466,
+34734,
+63855,
+50645,
+21320,
+52339,
+8632,
+7638,
+12827,
+5912,
+4379,
+12465,
+12855,
+18562,
+52581,
+87,
+48409,
+65163,
+50779,
+10704,
+64485,
+13283,
+23529,
+47860,
+59602,
+3988,
+35505,
+6836,
+33637,
+34032,
+48927,
+2917,
+35824,
+65077,
+39980,
+55411,
+43058,
+18104,
+60463,
+15749,
+5090,
+15320,
+16705,
+60293,
+26,
+41265,
+39570,
+62219,
+10611,
+8505,
+63450,
+56584,
+18501,
+42390,
+65286,
+7079,
+1047,
+38085,
+43452,
+23466,
+17288,
+12279,
+40524,
+55872,
+60757,
+44570,
+41747,
+28228,
+34289,
+35537,
+6970,
+23213,
+23374,
+7986,
+38181,
+45478,
+31077,
+38071,
+44580,
+28632,
+17346,
+61794,
+32139,
+59855,
+48476,
+25920,
+48808,
+46854,
+2404,
+21883,
+25076,
+28294,
+58191,
+38654,
+48309,
+48449,
+41221,
+16111,
+47905,
+37875,
+4723,
+45297,
+36699,
+25489,
+23637,
+48523,
+16496,
+31389,
+52036,
+48678,
+58552,
+60649,
+17153,
+21797,
+51789,
+10432,
+36460,
+48730,
+25602,
+56904,
+7525,
+52711,
+40892,
+51728,
+54294,
+35148,
+7159,
+59982,
+60,
+8596,
+44414,
+64089,
+1088,
+53562,
+11982,
+58532,
+54637,
+27499,
+55941,
+50764,
+40813,
+22573,
+15118,
+52226,
+17663,
+23750,
+33102,
+20965,
+23792,
+18814,
+21085,
+24962,
+24241,
+393,
+39423,
+15323,
+23938,
+26915,
+14301,
+43034,
+54654,
+62328,
+17468,
+2901,
+32905,
+46711,
+5143,
+47816,
+4613,
+31690,
+22019,
+42481,
+59631,
+55516,
+36151,
+9618,
+17334,
+46306,
+1354,
+26506,
+315,
+46665,
+34275,
+49257,
+468,
+8525,
+50080,
+10309,
+910,
+19065,
+43116,
+3120,
+16380,
+60497,
+63353,
+12550,
+48129,
+6473,
+55506,
+10828,
+41209,
+31669,
+31979,
+16832,
+52102,
+41980,
+65257,
+2350,
+8559,
+20466,
+20616,
+17052,
+65342,
+20608,
+6908,
+56223,
+31633,
+47168,
+43841,
+65393,
+60955,
+59372,
+62040,
+21428,
+43570,
+46476,
+59029,
+33822,
+46458,
+35023,
+58085,
+62653,
+15027,
+29695,
+4523,
+26323,
+23167,
+61920,
+19122,
+37014,
+6699,
+24191,
+15261,
+21871,
+35673,
+19873,
+59012,
+19640,
+41410,
+23702,
+18620,
+14695,
+58764,
+59954,
+13045,
+21989,
+42986,
+40477,
+47189,
+31989,
+57288,
+15249,
+17075,
+27239,
+41847,
+51921,
+28483,
+64712,
+5648,
+60967,
+5007,
+54035,
+58529,
+61432,
+37672,
+39080,
+19836,
+5776,
+40410,
+32613,
+51964,
+56764,
+16191,
+25909,
+65495,
+44758,
+30795,
+4719,
+1570,
+9721,
+9661,
+45630,
+62407,
+37723,
+35193,
+62836,
+46775,
+58803,
+38965,
+17296,
+14846,
+30057,
+38107,
+36510,
+22731,
+57503,
+32023,
+62413,
+52716,
+34799,
+17841,
+53332,
+59067,
+23910,
+37011,
+2418,
+1464,
+27871,
+52448,
+56079,
+694,
+61106,
+18449,
+37913,
+16355,
+22620,
+2565,
+64593,
+21893,
+65086,
+18497,
+1528,
+27776,
+7997,
+64694,
+24519,
+2762,
+63593,
+52181,
+10218,
+33786,
+42888,
+56090,
+3810,
+18264,
+52793,
+47661,
+4683,
+16805,
+39937,
+27246,
+21271,
+29704,
+1199,
+58602,
+48081,
+10146,
+55359,
+64630,
+58232,
+8893,
+22866,
+17690,
+52398,
+9853,
+39407,
+29485,
+47559,
+41827,
+51012,
+48513,
+54970,
+33948,
+20431,
+34338,
+38643,
+63586,
+64765,
+32727,
+16183,
+27574,
+531,
+55806,
+9680,
+48228,
+21959,
+8217,
+3167,
+45037,
+56113,
+37252,
+49594,
+12486,
+53094,
+52143,
+10419,
+37820,
+36035,
+26286,
+31771,
+6216,
+40312,
+60630,
+40277,
+39304,
+64776,
+59243,
+18830,
+20994,
+44711,
+23138,
+39509,
+42785,
+9098,
+13542,
+35598,
+603,
+10960,
+27475,
+32624,
+40876,
+47219,
+16896,
+23455,
+11627,
+40677,
+49161,
+53139,
+29024,
+48994,
+53490,
+43433,
+41853,
+58844,
+32035,
+9798,
+5298,
+39108,
+31294,
+51461,
+29112,
+10507,
+59008,
+1276,
+25097,
+31678,
+16483,
+13634,
+22534,
+24308,
+8151,
+46959,
+23493,
+24970,
+8969,
+45760,
+2967,
+35353,
+36540,
+51866,
+47785,
+19565,
+39971,
+3531,
+53607,
+23504,
+34402,
+42162,
+7591,
+27357,
+11478,
+46743,
+33113,
+59468,
+35286,
+44473,
+33244,
+63750,
+37760,
+7851,
+844,
+37074,
+53470,
+25589,
+26413,
+1646,
+22165,
+61378,
+12500,
+47792,
+45560,
+26349,
+18712,
+37902,
+1137,
+38874,
+5013,
+22339,
+12419,
+43972,
+45683,
+47745,
+46309,
+40069,
+51428,
+42771,
+29918,
+21862,
+54195,
+40880,
+31895,
+48412,
+22991,
+54504,
+40724,
+32022,
+57714,
+22732,
+62756,
+1109,
+47412,
+64681,
+23533,
+5746,
+5566,
+48727,
+38382,
+5067,
+58361,
+33587,
+22793,
+59756,
+1441,
+15499,
+25256,
+33269,
+45609,
+48630,
+35266,
+48016,
+62422,
+30707,
+22011,
+52667,
+45580,
+25758,
+19757,
+31383,
+10912,
+33325,
+65009,
+47506,
+62183,
+38213,
+32598,
+58385,
+45009,
+14828,
+23915,
+18857,
+20745,
+58585,
+56185,
+58165,
+38980,
+50748,
+59400,
+13918,
+11193,
+54783,
+25727,
+62280,
+27033,
+81,
+10391,
+50661,
+6178,
+17544,
+13721,
+63384,
+20192,
+17066,
+37437,
+58345,
+51357,
+41087,
+56148,
+48330,
+33577,
+21172,
+63492,
+29467,
+53068,
+43624,
+2965,
+45762,
+29780,
+27192,
+38203,
+18756,
+3205,
+42459,
+35892,
+32135,
+18341,
+35435,
+44974,
+62730,
+61077,
+21636,
+13925,
+17390,
+28351,
+29887,
+23299,
+4988,
+28047,
+33127,
+23062,
+12186,
+27244,
+39939,
+50135,
+62172,
+61778,
+4169,
+21108,
+36506,
+25753,
+50832,
+51984,
+15007,
+41228,
+7968,
+18579,
+34967,
+13752,
+41926,
+31748,
+57374,
+34809,
+47978,
+1020,
+10528,
+34808,
+57380,
+31749,
+22651,
+4390,
+6195,
+56029,
+47470,
+7035,
+41106,
+13433,
+23559,
+42486,
+33486,
+40135,
+55246,
+20299,
+11859,
+63510,
+27982,
+809,
+18853,
+54446,
+59788,
+44137,
+35166,
+49649,
+23593,
+15420,
+7281,
+65529,
+18726,
+48061,
+37043,
+38054,
+18485,
+9761,
+48855,
+45772,
+63691,
+14103,
+60264,
+55879,
+33723,
+8266,
+16522,
+51604,
+12622,
+28873,
+42534,
+29265,
+50232,
+5992,
+42617,
+43577,
+7904,
+62371,
+41456,
+10580,
+30357,
+32083,
+6368,
+64509,
+20932,
+53929,
+18792,
+32426,
+61316,
+14708,
+34349,
+58141,
+12426,
+61820,
+21782,
+60309,
+3286,
+57144,
+59595,
+59548,
+2179,
+38271,
+26086,
+19898,
+37494,
+47138,
+63447,
+15248,
+57759,
+31990,
+11549,
+42688,
+37815,
+27274,
+33876,
+25394,
+32074,
+52355,
+1960,
+55278,
+16414,
+5862,
+56059,
+30318,
+31813,
+8206,
+49016,
+27640,
+10763,
+5365,
+41441,
+58050,
+28970,
+10606,
+55059,
+65244,
+9652,
+9635,
+35456,
+32349,
+1567,
+38830,
+35518,
+45118,
+41545,
+2618,
+34884,
+38707,
+22494,
+6696,
+15825,
+27560,
+59365,
+21390,
+62511,
+51499,
+32417,
+20195,
+57182,
+2582,
+8653,
+21622,
+53863,
+27899,
+50441,
+44099,
+34587,
+3747,
+41425,
+44062,
+31976,
+32666,
+53270,
+53169,
+35602,
+39018,
+30046,
+28766,
+3293,
+11083,
+58354,
+58977,
+60592,
+11865,
+41739,
+52809,
+22777,
+11576,
+59911,
+25074,
+21885,
+4334,
+63618,
+26619,
+56707,
+1879,
+6729,
+36840,
+58063,
+22215,
+49139,
+62968,
+19167,
+30530,
+43995,
+51269,
+64532,
+63645,
+27207,
+43380,
+7322,
+34895,
+10490,
+2581,
+57238,
+20196,
+45861,
+24990,
+46195,
+47091,
+38808,
+4726,
+21574,
+28169,
+51842,
+36043,
+26702,
+64652,
+19499,
+6632,
+46943,
+49278,
+8696,
+61303,
+46185,
+17331,
+38241,
+63730,
+31826,
+27427,
+3253,
+15489,
+48627,
+28008,
+62309,
+51931,
+34429,
+711,
+51639,
+65123,
+56969,
+59594,
+57299,
+3287,
+39088,
+38615,
+39906,
+19299,
+39639,
+47803,
+61262,
+56169,
+51202,
+7385,
+49451,
+26391,
+63097,
+50813,
+38189,
+64033,
+44390,
+60917,
+34011,
+20898,
+6947,
+15442,
+37304,
+64050,
+31270,
+41774,
+40983,
+55024,
+38499,
+58683,
+14212,
+55449,
+1151,
+58313,
+51565,
+38537,
+32477,
+64410,
+3435,
+19170,
+24218,
+2385,
+39911,
+19891,
+16293,
+40122,
+3160,
+28654,
+37585,
+48982,
+4827,
+62891,
+32881,
+15880,
+10875,
+55276,
+1962,
+3373,
+4993,
+25683,
+48771,
+60073,
+58640,
+48498,
+54210,
+56760,
+54538,
+18456,
+2238,
+10275,
+34855,
+23092,
+34661,
+61350,
+33020,
+37117,
+5787,
+48882,
+44158,
+26767,
+24761,
+34601,
+44717,
+37855,
+50859,
+26949,
+8933,
+60337,
+20813,
+12939,
+3916,
+62610,
+31560,
+53299,
+23207,
+7233,
+36647,
+17156,
+62188,
+16581,
+24857,
+20974,
+13274,
+4928,
+38201,
+27194,
+20661,
+47322,
+47486,
+54925,
+7567,
+2088,
+50291,
+30188,
+10367,
+13762,
+28491,
+49761,
+2502,
+56400,
+61858,
+3440,
+61805,
+24259,
+42881,
+51745,
+9914,
+41597,
+38624,
+65068,
+7703,
+15458,
+26616,
+18805,
+6347,
+51838,
+54175,
+699,
+16198,
+22908,
+29770,
+55579,
+35037,
+11061,
+18681,
+63135,
+12093,
+40226,
+537,
+50311,
+6036,
+50698,
+41506,
+28084,
+36866,
+28568,
+13742,
+23902,
+24880,
+5003,
+28513,
+20735,
+39650,
+63186,
+7726,
+45072,
+25362,
+45980,
+63792,
+50469,
+39766,
+58613,
+59593,
+57146,
+65124,
+11295,
+61645,
+18158,
+25961,
+7208,
+37465,
+9774,
+21945,
+16984,
+56347,
+20677,
+6362,
+48286,
+18150,
+21553,
+52880,
+24189,
+6701,
+63240,
+23036,
+44762,
+32902,
+4630,
+7459,
+43338,
+65476,
+7647,
+21124,
+56535,
+33443,
+55836,
+51532,
+16769,
+1615,
+46761,
+28195,
+55462,
+54420,
+21432,
+32346,
+49834,
+36768,
+14360,
+6034,
+50313,
+35161,
+36199,
+60601,
+13042,
+49052,
+51905,
+41431,
+29424,
+35744,
+4856,
+36298,
+12726,
+42762,
+56384,
+39866,
+60654,
+13197,
+7524,
+57900,
+25603,
+19293,
+16961,
+28303,
+60613,
+56206,
+22809,
+26098,
+56596,
+6650,
+15156,
+31855,
+45033,
+50553,
+15634,
+48700,
+32026,
+13568,
+62954,
+8301,
+43916,
+19362,
+58261,
+3172,
+37613,
+60466,
+4745,
+44728,
+401,
+53637,
+48888,
+38702,
+54594,
+15388,
+4166,
+12300,
+960,
+20460,
+22483,
+15196,
+65469,
+5870,
+41750,
+41647,
+32406,
+18946,
+25120,
+36011,
+11104,
+44192,
+16505,
+13862,
+50741,
+33919,
+9648,
+14278,
+32859,
+5731,
+13504,
+14671,
+40171,
+16258,
+8373,
+4235,
+33311,
+62644,
+59714,
+5393,
+32648,
+24406,
+50109,
+1182,
+54569,
+62323,
+43162,
+20303,
+41888,
+18267,
+1497,
+32682,
+38710,
+52392,
+51213,
+46636,
+6683,
+26759,
+23201,
+33148,
+15847,
+3148,
+23581,
+50717,
+53783,
+27921,
+49791,
+60822,
+35411,
+12025,
+52290,
+47286,
+15360,
+25412,
+63609,
+41391,
+49149,
+23833,
+47893,
+15985,
+10533,
+12923,
+63251,
+49964,
+2825,
+17510,
+27382,
+32158,
+30162,
+24362,
+11135,
+55751,
+53596,
+17494,
+29812,
+39882,
+3764,
+39828,
+59305,
+4997,
+23748,
+17665,
+25298,
+64393,
+48050,
+39231,
+26303,
+13079,
+28225,
+5873,
+16190,
+57738,
+51965,
+14392,
+54537,
+57077,
+54211,
+2038,
+4083,
+19017,
+62440,
+24027,
+44997,
+20381,
+46956,
+30572,
+53150,
+12229,
+60892,
+37864,
+24800,
+5508,
+51191,
+32653,
+56380,
+25218,
+45715,
+41725,
+54567,
+1184,
+21183,
+6142,
+54304,
+27629,
+32575,
+60261,
+24874,
+38791,
+15897,
+11053,
+52797,
+18182,
+21288,
+60868,
+50305,
+34794,
+62304,
+42158,
+23681,
+22497,
+48057,
+30113,
+14728,
+10226,
+61802,
+27820,
+55166,
+1878,
+57202,
+26620,
+18039,
+14943,
+46597,
+61472,
+27851,
+41464,
+53665,
+47060,
+41272,
+52268,
+35422,
+50811,
+63099,
+15760,
+19507,
+64211,
+54508,
+47466,
+25378,
+53290,
+21167,
+17441,
+48964,
+1310,
+30245,
+51885,
+9670,
+16719,
+9224,
+10288,
+28849,
+46622,
+33116,
+44852,
+20010,
+48737,
+5018,
+22826,
+34261,
+5925,
+25581,
+25002,
+21277,
+10353,
+49581,
+56530,
+42055,
+55292,
+18650,
+13438,
+29212,
+7245,
+59451,
+46927,
+20850,
+35427,
+3414,
+26638,
+3369,
+30008,
+7304,
+7878,
+42672,
+2704,
+49499,
+16515,
+6592,
+54365,
+56163,
+2658,
+41014,
+14163,
+53753,
+40014,
+38994,
+14863,
+10940,
+4811,
+10202,
+6450,
+12700,
+16353,
+37915,
+47688,
+37980,
+29994,
+43586,
+27849,
+61474,
+24412,
+64134,
+37818,
+10421,
+10295,
+59230,
+45906,
+38986,
+30306,
+46874,
+28450,
+38587,
+8481,
+5201,
+63938,
+34492,
+21853,
+49809,
+43227,
+6649,
+56895,
+26099,
+1942,
+13358,
+39542,
+64885,
+29095,
+11770,
+28964,
+43767,
+45307,
+18500,
+57968,
+63451,
+23324,
+30200,
+60803,
+4792,
+22070,
+24335,
+42704,
+50012,
+50606,
+29091,
+37480,
+40999,
+3954,
+40566,
+7972,
+19778,
+39968,
+22637,
+32480,
+40349,
+39994,
+18801,
+1336,
+19118,
+59457,
+35082,
+270,
+1452,
+25440,
+19718,
+17885,
+64567,
+36185,
+5476,
+49767,
+19734,
+53211,
+7456,
+55932,
+3024,
+14040,
+10629,
+21598,
+16031,
+26560,
+40247,
+33442,
+56939,
+21125,
+6922,
+5946,
+42054,
+56660,
+49582,
+25934,
+33074,
+24270,
+25969,
+34001,
+37538,
+58827,
+64483,
+10706,
+18321,
+12295,
+35653,
+40611,
+51016,
+27114,
+20444,
+44311,
+29109,
+12030,
+11898,
+16651,
+6365,
+38164,
+11022,
+14068,
+46523,
+50754,
+43481,
+58546,
+27069,
+23129,
+60078,
+52750,
+21534,
+19198,
+50689,
+35482,
+3876,
+19204,
+52376,
+45852,
+19748,
+5742,
+15110,
+7413,
+49388,
+55274,
+10877,
+51651,
+13132,
+2561,
+51699,
+56250,
+63823,
+2220,
+24345,
+3557,
+58778,
+13025,
+32689,
+25047,
+51642,
+36977,
+2998,
+744,
+51287,
+17166,
+14088,
+60584,
+62772,
+17144,
+5536,
+28976,
+23604,
+20874,
+48186,
+2936,
+32379,
+61152,
+52607,
+6540,
+64506,
+17626,
+7028,
+54452,
+7185,
+19290,
+33770,
+22751,
+7931,
+51996,
+13878,
+34774,
+45369,
+2851,
+3333,
+22076,
+42024,
+29433,
+41324,
+46068,
+11201,
+56265,
+40040,
+5818,
+20803,
+30536,
+40310,
+6218,
+50637,
+23961,
+59551,
+31450,
+19003,
+23110,
+33063,
+30718,
+39045,
+13004,
+64898,
+15923,
+20221,
+445,
+15724,
+47296,
+62545,
+31904,
+61857,
+57023,
+2503,
+42532,
+28875,
+63888,
+35593,
+43990,
+63351,
+60499,
+43302,
+28466,
+3231,
+23381,
+58926,
+3144,
+39865,
+56909,
+42763,
+2663,
+25217,
+56741,
+32654,
+49659,
+44123,
+40949,
+62454,
+58542,
+61668,
+22146,
+32697,
+60890,
+12231,
+11665,
+1265,
+60206,
+30089,
+61929,
+49985,
+29122,
+9592,
+51854,
+14874,
+59361,
+44598,
+31530,
+60348,
+30003,
+22143,
+63268,
+11993,
+18519,
+63390,
+20676,
+56958,
+16985,
+34444,
+54651,
+47638,
+14483,
+42810,
+58689,
+6548,
+61355,
+9453,
+43556,
+9921,
+45042,
+9278,
+1011,
+21307,
+11987,
+42014,
+2868,
+33352,
+63744,
+56139,
+49560,
+5740,
+19750,
+58248,
+63155,
+1102,
+13251,
+50944,
+12644,
+18001,
+48817,
+8860,
+55250,
+3843,
+21500,
+16924,
+41329,
+37682,
+59197,
+26344,
+51142,
+60434,
+22262,
+58576,
+41875,
+17005,
+41795,
+61331,
+62530,
+54964,
+46017,
+55576,
+45844,
+63205,
+64807,
+900,
+62483,
+17904,
+46296,
+13650,
+55869,
+2245,
+52203,
+31317,
+15363,
+16317,
+41549,
+4296,
+44399,
+61808,
+17985,
+37572,
+14126,
+1068,
+5396,
+42138,
+19140,
+23804,
+40039,
+56426,
+11202,
+23822,
+35874,
+32606,
+20494,
+9204,
+64527,
+64174,
+15062,
+17461,
+26757,
+6685,
+37847,
+63822,
+56476,
+51700,
+49599,
+61142,
+40764,
+6115,
+23329,
+28185,
+44293,
+16349,
+48795,
+54929,
+10480,
+45291,
+47400,
+49114,
+21268,
+21632,
+47767,
+51600,
+26181,
+39563,
+52745,
+15536,
+34771,
+45486,
+31632,
+57804,
+6909,
+32466,
+15869,
+60058,
+11857,
+20301,
+43164,
+53958,
+22558,
+48970,
+17760,
+33668,
+6078,
+30457,
+23246,
+22808,
+56898,
+60614,
+38495,
+58621,
+24235,
+25152,
+4050,
+28361,
+36365,
+43413,
+4806,
+32081,
+30359,
+26926,
+62851,
+14817,
+6675,
+59697,
+24402,
+12806,
+58164,
+57457,
+58586,
+26659,
+39070,
+16020,
+29319,
+48624,
+62806,
+46261,
+6553,
+43689,
+16386,
+41706,
+25487,
+36701,
+51201,
+57135,
+61263,
+35860,
+53522,
+41083,
+2657,
+56637,
+54366,
+40702,
+46904,
+16234,
+23164,
+38896,
+49909,
+52621,
+11378,
+20833,
+2074,
+55210,
+12574,
+48329,
+57433,
+41088,
+29164,
+13614,
+37056,
+21648,
+27882,
+16454,
+49559,
+56325,
+63745,
+51084,
+62867,
+12303,
+52845,
+30629,
+20013,
+53504,
+15334,
+26297,
+5830,
+29718,
+40286,
+44195,
+50171,
+21059,
+15850,
+11185,
+23161,
+31168,
+33927,
+1301,
+50991,
+29861,
+37251,
+57630,
+45038,
+63917,
+22184,
+17705,
+24049,
+28404,
+55843,
+24896,
+2786,
+14651,
+52249,
+50139,
+41557,
+36671,
+46096,
+31984,
+16563,
+46123,
+61470,
+46599,
+35263,
+3809,
+57677,
+42889,
+22819,
+13364,
+36891,
+4181,
+9200,
+21982,
+31656,
+58433,
+693,
+57700,
+52449,
+50760,
+30299,
+6864,
+23180,
+58716,
+26014,
+35164,
+44139,
+28290,
+46649,
+47869,
+21472,
+58445,
+17358,
+43048,
+58634,
+46082,
+30317,
+57274,
+5863,
+46505,
+49666,
+5481,
+55986,
+45822,
+5576,
+13562,
+51182,
+2535,
+55847,
+4330,
+64736,
+10405,
+55140,
+40845,
+35664,
+44439,
+25533,
+40952,
+62315,
+51926,
+2635,
+19319,
+4862,
+50525,
+25939,
+7108,
+47469,
+57369,
+6196,
+59383,
+30815,
+53018,
+2425,
+35579,
+64862,
+46577,
+51303,
+18295,
+31154,
+38324,
+61345,
+30979,
+24314,
+49428,
+55574,
+46019,
+50575,
+14080,
+1630,
+52519,
+21605,
+63715,
+44844,
+11514,
+59949,
+43098,
+48536,
+13598,
+4507,
+2109,
+36124,
+12878,
+49412,
+22791,
+33589,
+32590,
+58892,
+46668,
+40095,
+45821,
+56054,
+5482,
+13412,
+26293,
+31942,
+11076,
+39890,
+55053,
+17956,
+61746,
+15512,
+60705,
+39817,
+52346,
+22693,
+20190,
+63386,
+26734,
+49538,
+18538,
+404,
+37705,
+28678,
+23719,
+64524,
+44232,
+42106,
+42649,
+42063,
+39589,
+38080,
+35135,
+5136,
+4479,
+16868,
+21734,
+62577,
+38517,
+55661,
+18431,
+36414,
+9176,
+13684,
+53125,
+50763,
+57881,
+27500,
+1550,
+18086,
+867,
+10949,
+18094,
+20276,
+3023,
+56544,
+7457,
+4632,
+63402,
+25706,
+43830,
+58343,
+37439,
+4820,
+49497,
+2706,
+34670,
+29385,
+7693,
+48204,
+27298,
+35810,
+37223,
+52638,
+27668,
+27420,
+33177,
+19286,
+42904,
+11836,
+40064,
+18441,
+59216,
+36990,
+55884,
+8278,
+30082,
+26699,
+46817,
+14387,
+37575,
+60126,
+40789,
+19254,
+39136,
+18190,
+14329,
+31617,
+8688,
+51260,
+51798,
+292,
+8277,
+55903,
+36991,
+65313,
+34092,
+33722,
+57333,
+60265,
+55815,
+7808,
+19908,
+39154,
+60756,
+57956,
+40525,
+2244,
+56284,
+13651,
+8858,
+48819,
+51111,
+37833,
+4898,
+49632,
+48787,
+41102,
+58921,
+9730,
+7901,
+17310,
+1875,
+2132,
+62742,
+46468,
+5758,
+31182,
+14551,
+4329,
+56048,
+2536,
+15113,
+24895,
+56106,
+28405,
+35898,
+26892,
+1953,
+40752,
+51531,
+56937,
+33444,
+27078,
+23874,
+60317,
+43437,
+46653,
+55296,
+13102,
+14896,
+6688,
+4312,
+40086,
+14620,
+10436,
+47372,
+11803,
+36073,
+18301,
+63966,
+7807,
+55877,
+60266,
+37520,
+13660,
+40275,
+60632,
+51733,
+6615,
+9679,
+57637,
+532,
+26806,
+22400,
+17113,
+45713,
+25220,
+22660,
+61385,
+43245,
+26932,
+74,
+20099,
+29910,
+32328,
+38175,
+43456,
+20324,
+1768,
+52275,
+21420,
+63326,
+34015,
+2654,
+24636,
+12703,
+54978,
+5420,
+46485,
+47051,
+8198,
+16683,
+34054,
+6598,
+15047,
+61222,
+47232,
+52702,
+35670,
+31172,
+40995,
+33580,
+28071,
+8576,
+43593,
+28954,
+31820,
+62045,
+50726,
+12511,
+33529,
+35502,
+51134,
+38576,
+53595,
+56784,
+11136,
+154,
+35095,
+3394,
+58743,
+850,
+58992,
+46620,
+28851,
+4837,
+33501,
+7953,
+60240,
+40974,
+45022,
+63471,
+24502,
+38631,
+2052,
+14015,
+3896,
+25866,
+13229,
+27905,
+27321,
+20138,
+13070,
+61577,
+12433,
+52925,
+29020,
+20366,
+22413,
+15530,
+54900,
+6132,
+24739,
+64036,
+44872,
+31688,
+4615,
+12850,
+35939,
+11914,
+14566,
+58758,
+53500,
+41792,
+61119,
+27658,
+63505,
+48823,
+28309,
+59989,
+54562,
+39494,
+39217,
+19247,
+51704,
+60871,
+45617,
+61218,
+33923,
+14152,
+41181,
+13138,
+7882,
+26131,
+23588,
+4249,
+11150,
+46437,
+17968,
+25703,
+2798,
+11357,
+22014,
+52508,
+14363,
+36050,
+17980,
+3500,
+64003,
+39289,
+32566,
+28460,
+4786,
+18100,
+18430,
+55948,
+38518,
+14019,
+2773,
+19681,
+38257,
+61843,
+32175,
+23174,
+42909,
+29666,
+59558,
+5932,
+17940,
+28607,
+51172,
+29489,
+39721,
+31245,
+60351,
+3755,
+28355,
+20453,
+63670,
+48504,
+1721,
+12979,
+36319,
+64154,
+34287,
+28230,
+34333,
+39813,
+36674,
+5403,
+47941,
+46065,
+13791,
+8521,
+23941,
+13767,
+39724,
+15588,
+3112,
+21980,
+9202,
+20496,
+62347,
+59085,
+52839,
+16513,
+49501,
+21487,
+58514,
+13114,
+39668,
+41538,
+29618,
+42097,
+42792,
+59726,
+13052,
+21927,
+63150,
+25628,
+65359,
+23698,
+63876,
+12911,
+51250,
+32953,
+41337,
+5580,
+16488,
+34099,
+47228,
+54151,
+59275,
+19324,
+79,
+27035,
+35036,
+57001,
+29771,
+45843,
+56293,
+46018,
+56012,
+49429,
+33390,
+52957,
+12776,
+5674,
+43573,
+29232,
+3523,
+58564,
+65491,
+53887,
+5762,
+36835,
+64821,
+51514,
+39799,
+40629,
+893,
+64585,
+3998,
+63850,
+61612,
+36995,
+33533,
+52696,
+30049,
+2014,
+43031,
+35884,
+51417,
+37149,
+30208,
+20895,
+45870,
+26751,
+46726,
+42776,
+27549,
+36333,
+43628,
+20328,
+17732,
+4277,
+24068,
+51281,
+14929,
+36448,
+11967,
+35053,
+54240,
+3894,
+14017,
+38520,
+11371,
+11625,
+23457,
+36150,
+57846,
+59632,
+12600,
+13331,
+63262,
+4007,
+40700,
+54368,
+62152,
+10827,
+57821,
+6474,
+21236,
+41168,
+25516,
+4434,
+62553,
+21356,
+31786,
+58794,
+6057,
+39347,
+509,
+49218,
+23990,
+62921,
+62706,
+21700,
+65387,
+5719,
+1754,
+59115,
+1532,
+26250,
+43813,
+11933,
+984,
+55092,
+34583,
+5127,
+44080,
+62847,
+34439,
+20311,
+59527,
+1517,
+14205,
+65192,
+19624,
+1998,
+45572,
+65062,
+12583,
+54419,
+56931,
+28196,
+22444,
+31676,
+25099,
+28913,
+6241,
+40959,
+13110,
+47333,
+12685,
+52132,
+1150,
+57111,
+14213,
+12971,
+11424,
+42438,
+36895,
+63323,
+19132,
+16196,
+701,
+16712,
+37230,
+44948,
+60327,
+26787,
+3957,
+12966,
+47520,
+24792,
+24809,
+41904,
+51780,
+33689,
+64649,
+14926,
+25883,
+3052,
+47635,
+43037,
+24778,
+32911,
+24663,
+50299,
+12083,
+63873,
+46937,
+1340,
+43057,
+57984,
+39981,
+33435,
+37605,
+31811,
+30320,
+13577,
+33619,
+36791,
+63901,
+36357,
+52374,
+19206,
+13322,
+2372,
+23918,
+28439,
+6345,
+18807,
+17455,
+7874,
+10179,
+61759,
+20580,
+41821,
+44748,
+47300,
+58088,
+19421,
+62194,
+12022,
+37411,
+18123,
+858,
+11140,
+31643,
+19496,
+47726,
+20008,
+44854,
+44437,
+35666,
+39027,
+19025,
+31557,
+43178,
+61871,
+4239,
+11640,
+40108,
+55226,
+64629,
+57662,
+10147,
+3779,
+32128,
+25109,
+19636,
+22028,
+2265,
+33695,
+34483,
+38796,
+2322,
+41078,
+37164,
+54522,
+20369,
+31637,
+12291,
+6447,
+21640,
+59027,
+46478,
+816,
+60725,
+8910,
+59088,
+43899,
+51614,
+60920,
+52689,
+58340,
+17551,
+5501,
+41584,
+33314,
+36577,
+40481,
+63176,
+62400,
+13355,
+7857,
+31752,
+32108,
+60619,
+18048,
+30560,
+32198,
+50808,
+8825,
+3736,
+16679,
+21662,
+17342,
+2684,
+50722,
+15454,
+32602,
+397,
+65044,
+20115,
+37992,
+31882,
+13101,
+55829,
+46654,
+41051,
+18649,
+56658,
+42056,
+18554,
+19844,
+62295,
+49725,
+42873,
+48245,
+20889,
+5030,
+5381,
+10729,
+61403,
+16413,
+57277,
+1961,
+57087,
+10876,
+56482,
+49389,
+23412,
+12838,
+36774,
+53327,
+64969,
+47969,
+54913,
+37560,
+31802,
+63298,
+8464,
+30683,
+16287,
+54984,
+28366,
+12285,
+59921,
+5335,
+65059,
+42181,
+65263,
+3842,
+56312,
+8861,
+36689,
+20298,
+57360,
+40136,
+4457,
+55115,
+8925,
+6308,
+6161,
+29281,
+45594,
+34531,
+51063,
+22221,
+63158,
+15462,
+2811,
+58383,
+32600,
+15456,
+7705,
+64628,
+55361,
+40109,
+42145,
+10872,
+21043,
+65429,
+1226,
+35360,
+58030,
+46530,
+2432,
+63410,
+23090,
+34857,
+31115,
+12573,
+56151,
+2075,
+19479,
+11147,
+36432,
+63374,
+62537,
+64795,
+22112,
+52545,
+31193,
+18378,
+52815,
+46440,
+23335,
+40018,
+697,
+54177,
+12873,
+23026,
+61895,
+19,
+42600,
+52094,
+50848,
+44259,
+8875,
+15957,
+22987,
+50545,
+24767,
+13382,
+22782,
+44073,
+58402,
+48846,
+54767,
+32876,
+22048,
+35348,
+8509,
+51776,
+2130,
+1877,
+56709,
+27821,
+13906,
+15623,
+15173,
+15001,
+44285,
+60946,
+23660,
+48534,
+43100,
+19651,
+49694,
+52908,
+16105,
+16135,
+265,
+46074,
+61588,
+65455,
+8092,
+14877,
+43870,
+29061,
+18107,
+40844,
+56044,
+10406,
+25877,
+61296,
+53899,
+4467,
+62091,
+800,
+9492,
+10398,
+39661,
+33946,
+54972,
+48583,
+25438,
+1454,
+49683,
+23656,
+29682,
+42779,
+61391,
+13393,
+7913,
+27718,
+8924,
+55243,
+4458,
+43903,
+17772,
+30581,
+11301,
+60151,
+44812,
+24424,
+13747,
+7633,
+29078,
+35394,
+50916,
+9715,
+18166,
+21718,
+27768,
+27916,
+54157,
+44706,
+14842,
+34582,
+55479,
+985,
+5119,
+39336,
+16828,
+11458,
+60282,
+13061,
+58609,
+34411,
+48750,
+61419,
+58104,
+43660,
+34570,
+64829,
+24195,
+54468,
+22875,
+30257,
+26315,
+16450,
+48356,
+22382,
+63058,
+53187,
+41290,
+34840,
+38754,
+59599,
+35049,
+40432,
+65243,
+57262,
+10607,
+9299,
+20247,
+64868,
+17955,
+55979,
+39891,
+32781,
+16992,
+28988,
+30641,
+8353,
+62957,
+65106,
+2592,
+19791,
+30892,
+11183,
+15852,
+41883,
+22447,
+25135,
+33377,
+35377,
+44203,
+23054,
+5908,
+36424,
+7959,
+6530,
+51680,
+27543,
+33657,
+38498,
+57115,
+40984,
+61841,
+38259,
+10712,
+42738,
+30510,
+25461,
+43791,
+218,
+52670,
+12598,
+59634,
+58931,
+64516,
+8161,
+30730,
+33839,
+51348,
+49551,
+8742,
+52615,
+33543,
+36171,
+27970,
+35990,
+28941,
+51528,
+27319,
+27907,
+15615,
+27939,
+1908,
+28667,
+61846,
+7052,
+46545,
+45205,
+12044,
+28365,
+55259,
+16288,
+52293,
+7444,
+33082,
+5419,
+55780,
+12704,
+62639,
+17194,
+51695,
+48582,
+55128,
+33947,
+57648,
+48514,
+47015,
+34951,
+4098,
+46016,
+56295,
+62531,
+45408,
+42925,
+3569,
+7032,
+24080,
+8220,
+35181,
+48648,
+24492,
+30657,
+49080,
+16214,
+61569,
+1207,
+38249,
+52213,
+63167,
+53255,
+24506,
+27869,
+1466,
+3977,
+2950,
+13215,
+14605,
+47774,
+6720,
+29381,
+49998,
+40117,
+36566,
+8274,
+10479,
+56239,
+48796,
+3317,
+7566,
+57033,
+47487,
+34979,
+53047,
+35207,
+43858,
+20066,
+1438,
+42994,
+45742,
+46947,
+37559,
+55266,
+47970,
+44783,
+17095,
+51496,
+8996,
+43940,
+65013,
+51378,
+36556,
+18606,
+11043,
+6131,
+55716,
+15531,
+44833,
+13594,
+44221,
+32399,
+7026,
+17628,
+2752,
+59658,
+52192,
+54846,
+32230,
+41396,
+52960,
+18673,
+63513,
+41565,
+38947,
+59233,
+53539,
+44255,
+33079,
+15044,
+58320,
+28183,
+23331,
+633,
+26774,
+18415,
+44531,
+35576,
+40966,
+5141,
+46713,
+33008,
+22210,
+24099,
+50508,
+35844,
+3910,
+20076,
+14514,
+14034,
+1626,
+30977,
+61347,
+61924,
+48682,
+33345,
+1760,
+58467,
+53532,
+32229,
+54889,
+52193,
+11406,
+40000,
+16588,
+38571,
+27956,
+39740,
+46603,
+49165,
+54409,
+643,
+14757,
+7537,
+1053,
+9017,
+47217,
+40878,
+54197,
+19502,
+27277,
+10517,
+22800,
+15482,
+1419,
+17216,
+38367,
+63244,
+48925,
+34034,
+22459,
+31436,
+18311,
+20161,
+29649,
+38512,
+38374,
+60155,
+10256,
+4135,
+14975,
+44817,
+44190,
+11106,
+50592,
+18942,
+63950,
+17315,
+13963,
+3704,
+34901,
+39443,
+32070,
+14048,
+19225,
+63128,
+40254,
+23116,
+41911,
+18454,
+54540,
+31202,
+25726,
+57450,
+11194,
+41378,
+51244,
+53079,
+6067,
+30253,
+23758,
+37896,
+8269,
+60173,
+11488,
+51956,
+18084,
+1552,
+32875,
+55174,
+48847,
+60323,
+61163,
+5651,
+41524,
+29235,
+25316,
+60107,
+30832,
+43207,
+26261,
+25827,
+24840,
+2911,
+5785,
+37119,
+16726,
+16445,
+62354,
+32234,
+37284,
+37402,
+27258,
+11700,
+52891,
+47538,
+13281,
+64487,
+17994,
+44462,
+13986,
+26034,
+3646,
+14431,
+51447,
+4955,
+38945,
+41567,
+12666,
+38722,
+16604,
+5716,
+4873,
+24890,
+11041,
+18608,
+44689,
+16254,
+45129,
+20394,
+38507,
+27948,
+28775,
+18788,
+3796,
+3384,
+10030,
+7717,
+24329,
+26003,
+20458,
+962,
+58120,
+29756,
+42655,
+16227,
+50648,
+5279,
+40130,
+4879,
+36973,
+42295,
+33521,
+3741,
+33400,
+47198,
+38602,
+50065,
+7249,
+20096,
+62952,
+13570,
+51147,
+51414,
+13204,
+13565,
+53591,
+48184,
+20876,
+60148,
+58471,
+45427,
+30304,
+38988,
+37018,
+24216,
+19172,
+5976,
+4058,
+59128,
+23391,
+10314,
+44495,
+49656,
+8449,
+10262,
+9056,
+13887,
+49838,
+765,
+45432,
+62327,
+57859,
+43035,
+47637,
+56344,
+34445,
+36061,
+36804,
+14305,
+16470,
+2834,
+42837,
+26139,
+27863,
+26459,
+13953,
+31229,
+27498,
+57883,
+58533,
+46138,
+20377,
+64377,
+58187,
+32444,
+40012,
+53755,
+23688,
+13618,
+34153,
+8066,
+32213,
+16075,
+41832,
+59143,
+43067,
+61017,
+54139,
+11565,
+29789,
+39321,
+6,
+46813,
+25207,
+62088,
+31458,
+61309,
+51367,
+24386,
+16050,
+20257,
+13967,
+35214,
+35013,
+59730,
+63224,
+3601,
+50673,
+8908,
+60727,
+15387,
+56871,
+38703,
+36957,
+1886,
+904,
+64669,
+9601,
+47444,
+1939,
+4494,
+26070,
+40654,
+7131,
+5928,
+40601,
+60878,
+64933,
+7935,
+38063,
+4741,
+39431,
+4776,
+12952,
+26063,
+62322,
+56831,
+1183,
+56737,
+41726,
+45987,
+31571,
+39493,
+55696,
+59990,
+37709,
+1188,
+34616,
+49105,
+5258,
+45599,
+27413,
+42858,
+42520,
+13142,
+49706,
+50787,
+61410,
+19840,
+18908,
+47692,
+32068,
+39445,
+45104,
+31201,
+54786,
+18455,
+57076,
+56761,
+14393,
+5105,
+64447,
+11977,
+4121,
+25742,
+19726,
+51814,
+2155,
+46114,
+3345,
+1470,
+22411,
+20368,
+55345,
+37165,
+232,
+39942,
+38618,
+40244,
+27372,
+9032,
+51470,
+20730,
+58267,
+63843,
+43202,
+47465,
+56689,
+64212,
+32819,
+40723,
+57506,
+22992,
+17918,
+7239,
+40853,
+18210,
+62058,
+40646,
+10815,
+46930,
+10635,
+59407,
+61603,
+33611,
+19969,
+34732,
+61468,
+46125,
+38862,
+49639,
+8811,
+12741,
+11157,
+20670,
+20923,
+38304,
+28878,
+30499,
+8538,
+46857,
+40834,
+64299,
+30041,
+64240,
+20987,
+22874,
+55075,
+24196,
+5529,
+4540,
+18330,
+18281,
+33594,
+9556,
+25648,
+14642,
+54416,
+278,
+63189,
+62243,
+8320,
+7184,
+56444,
+7029,
+62676,
+62032,
+49958,
+59787,
+57353,
+18854,
+2375,
+60049,
+13450,
+60617,
+32110,
+14155,
+65413,
+14920,
+33167,
+58648,
+29525,
+8682,
+21346,
+10734,
+14946,
+32798,
+17191,
+28946,
+4760,
+3580,
+34865,
+1657,
+32058,
+21431,
+56930,
+55463,
+12584,
+277,
+54458,
+14643,
+28594,
+63528,
+31823,
+24946,
+642,
+54836,
+49166,
+29895,
+18470,
+4021,
+54311,
+41174,
+8752,
+9787,
+65384,
+34063,
+64678,
+10160,
+33895,
+42462,
+34877,
+51511,
+30384,
+46057,
+10330,
+29106,
+61287,
+9857,
+19101,
+37511,
+28711,
+25988,
+47659,
+52795,
+11055,
+44868,
+65167,
+4974,
+62877,
+24525,
+48300,
+6522,
+43977,
+28883,
+29391,
+62151,
+55509,
+40701,
+56162,
+56638,
+6593,
+54317,
+31534,
+28001,
+24152,
+38526,
+58203,
+63116,
+31605,
+34644,
+10496,
+49686,
+9416,
+42377,
+40604,
+59111,
+19657,
+14800,
+20936,
+51618,
+8086,
+10755,
+24160,
+62698,
+38127,
+33225,
+1147,
+19468,
+9110,
+9273,
+41716,
+5633,
+62595,
+24803,
+34479,
+3409,
+13512,
+6238,
+38132,
+45026,
+35460,
+41453,
+31217,
+62072,
+44632,
+39300,
+31533,
+54363,
+6594,
+20154,
+47306,
+15269,
+41173,
+54404,
+4022,
+14353,
+63004,
+2667,
+64580,
+27628,
+56733,
+6143,
+43703,
+50016,
+40400,
+32162,
+36206,
+45287,
+24615,
+35147,
+57895,
+51729,
+34718,
+169,
+16578,
+18173,
+34962,
+45305,
+43769,
+28604,
+45555,
+38557,
+18645,
+54259,
+62824,
+3650,
+52865,
+52773,
+64891,
+23574,
+41069,
+11910,
+46990,
+5602,
+15932,
+34240,
+24391,
+38479,
+25945,
+2878,
+46166,
+23235,
+15415,
+43980,
+62823,
+54281,
+18646,
+42570,
+52454,
+517,
+36059,
+34447,
+61948,
+49446,
+35303,
+17319,
+30397,
+21925,
+13054,
+24765,
+50547,
+412,
+8570,
+3893,
+55524,
+35054,
+23404,
+34955,
+10427,
+5387,
+7068,
+12007,
+1229,
+44385,
+42990,
+23084,
+28257,
+15686,
+58277,
+58857,
+17889,
+45767,
+12088,
+23670,
+38574,
+51136,
+579,
+21809,
+3676,
+32618,
+7015,
+46641,
+2037,
+56759,
+57078,
+48499,
+29017,
+19476,
+50551,
+45035,
+3169,
+18463,
+52366,
+2723,
+25239,
+6630,
+19501,
+54828,
+40879,
+57511,
+21863,
+22641,
+63700,
+26380,
+25159,
+4984,
+7074,
+43751,
+15485,
+42300,
+50124,
+456,
+12334,
+62738,
+9862,
+43524,
+12872,
+55193,
+698,
+57006,
+51839,
+28968,
+58052,
+16070,
+33371,
+44362,
+63554,
+27016,
+10017,
+53913,
+18659,
+54134,
+63532,
+49677,
+12915,
+9383,
+44705,
+55096,
+27917,
+48338,
+49491,
+24680,
+59274,
+55585,
+47229,
+25454,
+42387,
+32032,
+20718,
+21349,
+40284,
+29720,
+32248,
+63898,
+11564,
+54618,
+61018,
+21426,
+62042,
+63531,
+54163,
+18660,
+13343,
+10693,
+25538,
+64704,
+45294,
+38811,
+26000,
+38563,
+17738,
+23306,
+38794,
+34485,
+48672,
+32206,
+53341,
+62593,
+5635,
+59782,
+52565,
+10091,
+23100,
+12200,
+46840,
+62618,
+29715,
+24466,
+53629,
+5406,
+15758,
+63101,
+41907,
+32843,
+22136,
+5025,
+12845,
+31892,
+43867,
+17189,
+32800,
+46381,
+33281,
+23676,
+22964,
+34526,
+58811,
+22191,
+2461,
+17277,
+37435,
+17068,
+36227,
+36241,
+48708,
+10450,
+5256,
+49107,
+36980,
+37114,
+36196,
+26017,
+50286,
+48464,
+60559,
+38649,
+40,
+20781,
+61492,
+51455,
+63909,
+34562,
+22571,
+40815,
+38179,
+7988,
+18795,
+4607,
+14579,
+22894,
+39369,
+37294,
+50712,
+25214,
+5363,
+10765,
+13582,
+53770,
+1711,
+36789,
+33621,
+29986,
+34248,
+22068,
+4794,
+28676,
+37707,
+59992,
+58528,
+57748,
+5008,
+42288,
+59852,
+23188,
+64546,
+26282,
+20181,
+59826,
+59650,
+2818,
+20692,
+9534,
+5589,
+4843,
+36436,
+17178,
+7402,
+33491,
+63364,
+38593,
+91,
+37756,
+1433,
+47120,
+21008,
+50159,
+24921,
+7088,
+16272,
+36525,
+34675,
+62463,
+39754,
+14447,
+4215,
+2255,
+49299,
+47290,
+11556,
+22616,
+17419,
+44594,
+20646,
+16932,
+210,
+33546,
+8251,
+22850,
+52913,
+36666,
+312,
+12564,
+18246,
+11541,
+42922,
+21496,
+44298,
+720,
+30997,
+28744,
+1638,
+11397,
+10920,
+35749,
+63197,
+64081,
+15856,
+38161,
+32086,
+46978,
+65033,
+37004,
+42759,
+8328,
+5627,
+22557,
+56215,
+43165,
+3203,
+18758,
+12889,
+7236,
+20603,
+21659,
+19768,
+53451,
+5706,
+18926,
+23900,
+13744,
+1255,
+23012,
+65407,
+52329,
+62248,
+26451,
+29675,
+12017,
+64780,
+35977,
+24474,
+51073,
+5348,
+51595,
+18791,
+57311,
+20933,
+41360,
+38427,
+64380,
+7201,
+60069,
+4892,
+17507,
+38740,
+34709,
+30972,
+39294,
+43853,
+60569,
+18658,
+54165,
+10018,
+42081,
+7942,
+24861,
+59056,
+30413,
+59136,
+34070,
+40405,
+12748,
+18543,
+37325,
+4466,
+55136,
+61297,
+10174,
+25430,
+6941,
+23782,
+2696,
+65295,
+61187,
+1086,
+64091,
+5761,
+55563,
+65492,
+34259,
+22828,
+33747,
+39176,
+24284,
+59690,
+52388,
+34868,
+37473,
+50503,
+17910,
+22859,
+53305,
+9169,
+64273,
+38192,
+51040,
+40505,
+33229,
+32805,
+40197,
+27898,
+57234,
+21623,
+62680,
+40365,
+44969,
+25611,
+60863,
+29635,
+3338,
+31425,
+29633,
+60865,
+64163,
+15180,
+3269,
+47360,
+59073,
+13224,
+58962,
+4198,
+26940,
+12816,
+44911,
+12797,
+65072,
+38278,
+10794,
+12647,
+22598,
+65509,
+24274,
+7150,
+20530,
+28931,
+30647,
+44738,
+45708,
+37030,
+50132,
+235,
+31381,
+19759,
+22929,
+40395,
+23462,
+53368,
+45669,
+53029,
+24561,
+20517,
+4131,
+4902,
+37508,
+31743,
+42285,
+58776,
+3559,
+23436,
+47678,
+15154,
+6652,
+64878,
+65040,
+46379,
+32802,
+35144,
+37106,
+47781,
+11894,
+15169,
+51424,
+39378,
+37154,
+15599,
+24674,
+36715,
+1545,
+976,
+15966,
+27920,
+56811,
+50718,
+58864,
+44154,
+60214,
+35257,
+34145,
+5734,
+23976,
+32749,
+38969,
+35356,
+1710,
+54047,
+13583,
+36441,
+8948,
+36574,
+34177,
+41383,
+27013,
+40363,
+62682,
+6867,
+64975,
+38669,
+46104,
+23687,
+54629,
+40013,
+56633,
+14164,
+29510,
+22524,
+39201,
+21047,
+59239,
+29642,
+30940,
+12176,
+19710,
+789,
+31774,
+34699,
+31302,
+33049,
+51802,
+38658,
+18164,
+9717,
+29465,
+63494,
+27506,
+39804,
+39068,
+26661,
+60526,
+6764,
+61692,
+44039,
+21241,
+42667,
+38021,
+49620,
+14557,
+34030,
+33639,
+377,
+59123,
+50260,
+36532,
+40608,
+63348,
+44541,
+20828,
+6039,
+20758,
+47063,
+32342,
+13093,
+29346,
+22632,
+9629,
+25474,
+22892,
+14581,
+46756,
+20545,
+49637,
+38864,
+21818,
+49973,
+44630,
+62074,
+48406,
+19884,
+37204,
+15534,
+52747,
+32152,
+61971,
+49765,
+5478,
+9319,
+45237,
+8179,
+2028,
+43800,
+2989,
+6204,
+4944,
+8324,
+23799,
+37101,
+34762,
+5472,
+61766,
+47059,
+56699,
+41465,
+17524,
+15326,
+60300,
+4144,
+52146,
+21522,
+40420,
+16427,
+62542,
+4357,
+28649,
+6573,
+61341,
+64710,
+28485,
+13996,
+29307,
+23286,
+37271,
+17755,
+11742,
+50369,
+15332,
+53506,
+9028,
+48887,
+56874,
+402,
+18540,
+25564,
+62008,
+26435,
+47939,
+5405,
+54106,
+24467,
+11264,
+18548,
+20285,
+29597,
+9830,
+50972,
+32774,
+35290,
+13654,
+41195,
+21371,
+16097,
+35660,
+43968,
+34225,
+27902,
+49738,
+33035,
+65048,
+23503,
+57555,
+3532,
+21723,
+10056,
+64554,
+24227,
+13875,
+19408,
+58285,
+31355,
+17493,
+56783,
+55752,
+38577,
+3594,
+48183,
+54680,
+13566,
+32028,
+58714,
+23182,
+17104,
+5434,
+12819,
+30740,
+15662,
+35734,
+35879,
+27825,
+20282,
+47602,
+1978,
+25931,
+60486,
+24360,
+30164,
+29088,
+10011,
+30485,
+15348,
+48493,
+38005,
+60977,
+35738,
+11981,
+57886,
+1089,
+11004,
+61417,
+48752,
+29798,
+11974,
+7652,
+7006,
+4107,
+43921,
+44229,
+9207,
+23524,
+27436,
+46663,
+317,
+2268,
+9186,
+47019,
+20090,
+48967,
+44254,
+54880,
+59234,
+13801,
+12380,
+3664,
+61300,
+32228,
+54848,
+58468,
+11304,
+64309,
+30179,
+63829,
+17777,
+52219,
+6192,
+41082,
+56166,
+35861,
+11600,
+758,
+28409,
+27736,
+3342,
+10156,
+28127,
+30125,
+8731,
+13248,
+52301,
+14074,
+36427,
+9027,
+53640,
+15333,
+56131,
+20014,
+60538,
+41791,
+55704,
+58759,
+36130,
+21158,
+59207,
+43109,
+20331,
+10551,
+37173,
+43432,
+57589,
+48995,
+21115,
+30060,
+38476,
+33731,
+48295,
+63683,
+5094,
+48919,
+31608,
+64238,
+30043,
+59380,
+25115,
+49112,
+47402,
+39282,
+19729,
+25588,
+57537,
+37075,
+60753,
+40381,
+64462,
+37041,
+48063,
+7420,
+44652,
+22354,
+41991,
+38683,
+36949,
+22390,
+30406,
+15237,
+10121,
+1665,
+5705,
+53949,
+19769,
+11315,
+41868,
+14674,
+62068,
+2401,
+8541,
+25312,
+52137,
+35017,
+1414,
+22140,
+50966,
+45643,
+59038,
+65185,
+32116,
+47079,
+63301,
+26597,
+58868,
+21732,
+16870,
+50963,
+30006,
+3371,
+1964,
+37789,
+64520,
+45864,
+13830,
+15235,
+30408,
+15611,
+64753,
+838,
+4141,
+40839,
+15010,
+39600,
+6912,
+58096,
+7462,
+45314,
+65094,
+38997,
+28585,
+38148,
+18022,
+50411,
+23408,
+28773,
+27950,
+16557,
+47810,
+23312,
+1862,
+30993,
+51005,
+29326,
+35307,
+58583,
+20747,
+24958,
+836,
+64755,
+34238,
+15934,
+30016,
+37432,
+19046,
+50309,
+539,
+28279,
+2673,
+42328,
+58306,
+50101,
+13533,
+51558,
+21395,
+45668,
+53818,
+23463,
+41699,
+61095,
+58425,
+3623,
+34596,
+11793,
+45828,
+45835,
+64224,
+23928,
+17502,
+43087,
+34073,
+51030,
+42474,
+8137,
+38294,
+47916,
+40718,
+24446,
+38690,
+31129,
+21860,
+29920,
+62592,
+54118,
+32207,
+59224,
+42436,
+11426,
+18698,
+30416,
+17406,
+59066,
+57708,
+17842,
+61854,
+35297,
+64968,
+55269,
+36775,
+20860,
+63031,
+13527,
+26864,
+14072,
+52303,
+32039,
+60860,
+49378,
+62062,
+34656,
+24380,
+58326,
+49331,
+49268,
+46793,
+12386,
+8789,
+4646,
+9168,
+53873,
+22860,
+43011,
+26338,
+6122,
+23206,
+57049,
+31561,
+680,
+35416,
+19599,
+45051,
+6118,
+16977,
+21166,
+56686,
+25379,
+46618,
+58994,
+19606,
+15507,
+21186,
+33068,
+4690,
+25833,
+40583,
+30801,
+1684,
+60015,
+28324,
+5318,
+34476,
+4911,
+3003,
+53168,
+57224,
+32667,
+47605,
+42340,
+45995,
+39727,
+22385,
+61495,
+12424,
+58143,
+48695,
+48248,
+7579,
+25014,
+24505,
+54945,
+63168,
+37170,
+10280,
+5617,
+31043,
+43686,
+45373,
+33899,
+10195,
+59091,
+51068,
+47215,
+9019,
+29885,
+28353,
+3757,
+61370,
+8669,
+64578,
+2669,
+9402,
+12693,
+28505,
+15407,
+40498,
+60782,
+49202,
+35867,
+31781,
+12557,
+32686,
+59507,
+62940,
+15022,
+48904,
+19510,
+32835,
+29614,
+47686,
+37917,
+39887,
+50897,
+7455,
+56546,
+19735,
+63788,
+42855,
+28899,
+65250,
+30478,
+65080,
+11355,
+2800,
+43006,
+23496,
+23271,
+14107,
+22768,
+62202,
+43463,
+59484,
+28622,
+31651,
+46549,
+36216,
+16915,
+41289,
+55067,
+63059,
+28179,
+38172,
+40206,
+44754,
+4047,
+38096,
+23997,
+35921,
+17853,
+42585,
+50221,
+25514,
+41170,
+49806,
+35111,
+35601,
+57223,
+53271,
+3004,
+20131,
+32011,
+32358,
+46253,
+65018,
+29480,
+42154,
+6613,
+51735,
+12001,
+61874,
+39427,
+29982,
+38775,
+18743,
+12228,
+56749,
+30573,
+20234,
+36852,
+36135,
+37523,
+3908,
+35846,
+24121,
+20212,
+29023,
+57592,
+49162,
+17584,
+5882,
+62118,
+15091,
+15951,
+47268,
+64042,
+43473,
+38700,
+48890,
+30297,
+50762,
+55943,
+13685,
+43496,
+20123,
+1346,
+28269,
+43197,
+8016,
+21566,
+36454,
+18703,
+4041,
+12275,
+7395,
+8233,
+3718,
+32373,
+23591,
+49651,
+12964,
+3959,
+9815,
+12613,
+4464,
+37327,
+17616,
+40219,
+5432,
+17106,
+14260,
+52142,
+57626,
+12487,
+21628,
+58581,
+35309,
+18864,
+12761,
+23567,
+4453,
+43658,
+58106,
+45577,
+221,
+40445,
+6066,
+54779,
+51245,
+30754,
+51363,
+2498,
+47495,
+61157,
+7529,
+52683,
+19219,
+43623,
+57427,
+29468,
+35446,
+3610,
+22252,
+26012,
+58718,
+5605,
+41023,
+13703,
+24300,
+51080,
+15338,
+37809,
+39318,
+45058,
+15942,
+7910,
+12444,
+43910,
+35206,
+54922,
+34980,
+29563,
+43798,
+2030,
+16716,
+14428,
+14967,
+24735,
+4158,
+8006,
+17746,
+24119,
+35848,
+44944,
+3155,
+46205,
+24560,
+53816,
+45670,
+14810,
+9780,
+32009,
+20133,
+25667,
+14935,
+42199,
+5084,
+2424,
+56025,
+30816,
+6281,
+42430,
+157,
+27792,
+28920,
+27076,
+33446,
+45109,
+29120,
+49987,
+45412,
+64193,
+46848,
+13464,
+30387,
+58380,
+16660,
+15216,
+30278,
+31394,
+13075,
+62616,
+46842,
+36107,
+29762,
+40625,
+62894,
+62983,
+2183,
+7713,
+17409,
+64947,
+4563,
+1279,
+26267,
+33755,
+12312,
+32852,
+43046,
+17360,
+19023,
+39029,
+65224,
+11554,
+47292,
+42271,
+20641,
+64322,
+34497,
+61976,
+42103,
+34421,
+10326,
+9387,
+17209,
+18672,
+54886,
+41397,
+12775,
+55571,
+33391,
+474,
+10073,
+11119,
+65261,
+42183,
+41987,
+61575,
+13072,
+65157,
+24037,
+18565,
+19391,
+47589,
+61879,
+59642,
+12866,
+10524,
+5904,
+10023,
+52207,
+13901,
+37446,
+6900,
+24759,
+26769,
+22972,
+60209,
+17594,
+19474,
+29019,
+55721,
+12434,
+61074,
+20843,
+5148,
+6976,
+62457,
+60089,
+52279,
+13932,
+8209,
+36665,
+53986,
+22851,
+59438,
+42700,
+16104,
+55153,
+49695,
+46085,
+32286,
+14837,
+24033,
+59738,
+39122,
+63893,
+16475,
+36966,
+51263,
+46230,
+60721,
+14592,
+9221,
+47537,
+54742,
+11701,
+42940,
+17528,
+917,
+12780,
+25501,
+61989,
+1590,
+1072,
+24188,
+56952,
+21554,
+40661,
+26748,
+6724,
+43348,
+62794,
+30714,
+23969,
+10809,
+26816,
+51129,
+63773,
+42193,
+52772,
+54278,
+3651,
+47754,
+15005,
+51986,
+30119,
+45940,
+36746,
+28153,
+17122,
+51610,
+48149,
+18203,
+10085,
+13406,
+32332,
+51317,
+17700,
+37840,
+30628,
+56134,
+12304,
+37033,
+22725,
+6850,
+16512,
+55612,
+59086,
+8912,
+49185,
+40900,
+9756,
+10560,
+2899,
+17470,
+6318,
+32588,
+33591,
+44329,
+61536,
+41404,
+12795,
+44913,
+23282,
+36402,
+3391,
+30934,
+20822,
+17966,
+46439,
+55198,
+18379,
+37059,
+29238,
+8626,
+22776,
+57211,
+41740,
+4515,
+43494,
+13687,
+23141,
+49495,
+4822,
+36880,
+23674,
+33283,
+18181,
+56725,
+11054,
+54381,
+47660,
+57674,
+18265,
+41890,
+25131,
+11634,
+46995,
+39975,
+7098,
+6491,
+31548,
+36088,
+61334,
+14771,
+24726,
+20070,
+1392,
+46672,
+12449,
+41971,
+64890,
+54277,
+52866,
+42194,
+49172,
+33174,
+22814,
+49920,
+37600,
+15163,
+2391,
+24139,
+12579,
+9967,
+59864,
+45283,
+64619,
+12651,
+40037,
+23806,
+49577,
+45272,
+39558,
+21533,
+56496,
+60079,
+32151,
+53685,
+15535,
+56228,
+39564,
+24132,
+12713,
+20625,
+11697,
+466,
+49259,
+49803,
+15272,
+26953,
+52576,
+15207,
+41134,
+9350,
+7374,
+21977,
+34833,
+42090,
+43028,
+52048,
+48663,
+19880,
+38596,
+11695,
+20627,
+60102,
+11953,
+34798,
+57711,
+62414,
+44567,
+22154,
+40891,
+57898,
+7526,
+59626,
+1742,
+189,
+25181,
+20751,
+25269,
+35669,
+55769,
+47233,
+42124,
+10266,
+28764,
+30048,
+55549,
+33534,
+52499,
+60511,
+11439,
+33646,
+58339,
+55330,
+60921,
+9828,
+29599,
+59943,
+19218,
+53071,
+7530,
+46976,
+32088,
+33951,
+35829,
+46411,
+26651,
+45721,
+21506,
+6516,
+5894,
+12597,
+55014,
+219,
+45579,
+57476,
+22012,
+11359,
+10345,
+42512,
+1410,
+58060,
+29412,
+9542,
+16126,
+25477,
+23679,
+42160,
+34404,
+62134,
+51687,
+35403,
+19447,
+46778,
+11583,
+58302,
+28892,
+59391,
+2262,
+5098,
+13021,
+21949,
+37690,
+27667,
+55914,
+37224,
+32832,
+26419,
+33025,
+60575,
+40993,
+31174,
+27536,
+45751,
+15129,
+1660,
+9944,
+14288,
+13013,
+15859,
+11377,
+56155,
+49910,
+31419,
+487,
+42036,
+33542,
+55003,
+8743,
+2287,
+37152,
+39380,
+52386,
+59692,
+6539,
+56449,
+61153,
+30564,
+19616,
+17537,
+26230,
+25083,
+34230,
+44723,
+8484,
+61622,
+35683,
+59435,
+60833,
+41472,
+40436,
+36626,
+43882,
+5016,
+48739,
+22151,
+60760,
+10965,
+39475,
+33153,
+86,
+58005,
+18563,
+24039,
+51670,
+15206,
+52734,
+26954,
+15885,
+12431,
+61579,
+10104,
+18018,
+13822,
+30022,
+51323,
+10090,
+54114,
+59783,
+46377,
+65042,
+399,
+44730,
+63815,
+9902,
+29219,
+51123,
+6376,
+58899,
+16995,
+63074,
+48262,
+46921,
+20199,
+43064,
+25009,
+31192,
+55201,
+22113,
+22308,
+58890,
+32592,
+64231,
+26718,
+36396,
+22822,
+16877,
+24623,
+26491,
+9127,
+640,
+24948,
+18930,
+58680,
+30504,
+942,
+43646,
+51248,
+12913,
+49679,
+27378,
+61665,
+21604,
+56007,
+1631,
+60438,
+12895,
+43589,
+51094,
+6758,
+9067,
+32849,
+6032,
+14362,
+55673,
+22015,
+39631,
+6432,
+40344,
+29822,
+21906,
+23865,
+60510,
+52694,
+33535,
+58536,
+4935,
+8618,
+16486,
+5582,
+7772,
+43549,
+44053,
+42588,
+17282,
+61655,
+5810,
+9937,
+43611,
+11291,
+40773,
+33406,
+50193,
+61557,
+58225,
+5254,
+10452,
+6929,
+5609,
+22349,
+49771,
+18363,
+35364,
+998,
+45398,
+9813,
+3961,
+12589,
+381,
+34142,
+12541,
+2922,
+26301,
+39233,
+8769,
+37313,
+30286,
+516,
+54256,
+42571,
+31013,
+59412,
+50759,
+56078,
+57701,
+27872,
+51950,
+33194,
+11833,
+36192,
+45453,
+4599,
+46632,
+34860,
+39010,
+33654,
+10240,
+9709,
+50642,
+42406,
+5461,
+37449,
+32766,
+43693,
+24577,
+10083,
+18205,
+31344,
+7022,
+9930,
+25870,
+64013,
+15019,
+63064,
+23729,
+61376,
+22167,
+61946,
+34449,
+45797,
+20108,
+35235,
+39456,
+29269,
+13957,
+44658,
+13885,
+9058,
+62977,
+63566,
+5224,
+42622,
+24355,
+9852,
+57656,
+17691,
+23016,
+10986,
+15832,
+51212,
+56822,
+38711,
+1655,
+34867,
+53879,
+59691,
+52610,
+39381,
+48003,
+62443,
+62508,
+13485,
+5021,
+32639,
+65283,
+45851,
+56489,
+19205,
+55400,
+36358,
+13426,
+24157,
+14295,
+41961,
+24470,
+2722,
+54202,
+18464,
+19781,
+39952,
+28115,
+29998,
+58569,
+33047,
+31304,
+48566,
+1959,
+57279,
+32075,
+4449,
+18749,
+38355,
+2470,
+38379,
+36463,
+22692,
+55973,
+39818,
+50684,
+15741,
+32170,
+4882,
+8631,
+58014,
+21321,
+36378,
+38510,
+29651,
+14122,
+10855,
+51973,
+14357,
+62247,
+53941,
+65408,
+65026,
+16321,
+60274,
+25197,
+62586,
+10598,
+50933,
+7056,
+35198,
+38641,
+34340,
+14192,
+60962,
+6463,
+11691,
+6576,
+6189,
+41536,
+39670,
+9809,
+37633,
+15801,
+60698,
+32038,
+53320,
+14073,
+53510,
+13249,
+1104,
+63047,
+42604,
+24233,
+58623,
+7443,
+54982,
+16289,
+47285,
+56805,
+12026,
+13237,
+64434,
+9219,
+14594,
+15394,
+40413,
+45201,
+50542,
+13931,
+52917,
+60090,
+5669,
+21419,
+55787,
+1769,
+4185,
+32519,
+42066,
+32440,
+35421,
+56696,
+41273,
+29724,
+14493,
+44562,
+8497,
+60610,
+26277,
+36710,
+35957,
+50795,
+17998,
+10797,
+1051,
+7539,
+42009,
+14982,
+49905,
+50138,
+56102,
+14652,
+27995,
+1642,
+49064,
+62924,
+26271,
+44227,
+43923,
+31455,
+4470,
+13340,
+37090,
+1828,
+25191,
+35717,
+5867,
+995,
+330,
+65340,
+17054,
+3836,
+17662,
+57876,
+15119,
+24417,
+33054,
+1831,
+41534,
+6191,
+53525,
+17778,
+34279,
+46330,
+10139,
+63166,
+54947,
+38250,
+39634,
+30381,
+64824,
+13900,
+52936,
+10024,
+2276,
+31316,
+56282,
+2246,
+43634,
+33739,
+26715,
+61066,
+13299,
+28448,
+46876,
+11405,
+54845,
+54890,
+59659,
+48086,
+58128,
+5371,
+37442,
+16734,
+35689,
+5497,
+3136,
+10217,
+57681,
+63594,
+8944,
+25996,
+59881,
+31373,
+29139,
+42717,
+9450,
+28647,
+4359,
+19136,
+52158,
+30614,
+4642,
+3431,
+46614,
+47919,
+25320,
+39332,
+37624,
+50903,
+30613,
+52169,
+19137,
+40769,
+10358,
+42756,
+16017,
+19595,
+28793,
+21330,
+20927,
+17641,
+21521,
+53659,
+4145,
+10418,
+57625,
+53095,
+14261,
+27292,
+3879,
+35016,
+53442,
+25313,
+37062,
+19466,
+1149,
+55451,
+12686,
+33206,
+25334,
+63929,
+48124,
+60054,
+9553,
+39245,
+65366,
+48961,
+30751,
+43649,
+32241,
+21411,
+28261,
+2466,
+51538,
+62256,
+48894,
+29178,
+13165,
+41381,
+34179,
+64029,
+63668,
+20455,
+24430,
+8172,
+41979,
+57815,
+16833,
+46886,
+8881,
+36326,
+38069,
+31079,
+50847,
+55187,
+42601,
+19995,
+19394,
+49751,
+39099,
+5487,
+11095,
+4921,
+27044,
+45929,
+50032,
+35031,
+64597,
+17659,
+36937,
+14949,
+14349,
+48745,
+7171,
+18688,
+12534,
+35151,
+19482,
+34591,
+28861,
+60136,
+23388,
+39762,
+42493,
+968,
+29354,
+47673,
+34991,
+25924,
+25465,
+21199,
+41651,
+36733,
+47342,
+44145,
+3028,
+13156,
+60517,
+42713,
+48662,
+52725,
+43029,
+2016,
+2113,
+21857,
+7086,
+24923,
+64289,
+32533,
+48490,
+19313,
+48677,
+57911,
+31390,
+59805,
+8692,
+50867,
+48306,
+50350,
+22740,
+28036,
+46361,
+62127,
+11209,
+59300,
+7221,
+43895,
+6512,
+65377,
+61004,
+48691,
+1779,
+46871,
+26904,
+63487,
+21143,
+42530,
+2505,
+12214,
+38674,
+42997,
+1923,
+60122,
+15435,
+12165,
+13980,
+19281,
+35475,
+27063,
+25052,
+19406,
+13877,
+56438,
+7932,
+29940,
+62745,
+26089,
+61652,
+37241,
+7348,
+33095,
+30118,
+52861,
+15006,
+57389,
+50833,
+42315,
+59518,
+18046,
+60621,
+23829,
+40052,
+59960,
+9486,
+14356,
+52332,
+10856,
+11947,
+33510,
+8651,
+2584,
+10852,
+14391,
+56763,
+57739,
+32614,
+35011,
+35216,
+10132,
+31919,
+39161,
+18083,
+54771,
+11489,
+11768,
+29097,
+44103,
+33193,
+52446,
+27873,
+4654,
+34466,
+39165,
+40112,
+20703,
+10640,
+21317,
+16230,
+17000,
+9538,
+61031,
+612,
+44904,
+65484,
+3782,
+44316,
+34428,
+57151,
+62310,
+45690,
+63943,
+2634,
+56037,
+62316,
+36372,
+9392,
+28482,
+57754,
+41848,
+10910,
+31385,
+47382,
+30293,
+16276,
+15736,
+22277,
+33199,
+32124,
+35392,
+29080,
+21586,
+64609,
+41430,
+56917,
+49053,
+7965,
+43004,
+2802,
+16508,
+26531,
+44505,
+45775,
+4790,
+60805,
+13729,
+44359,
+34207,
+26794,
+27133,
+48783,
+19240,
+9252,
+9669,
+56680,
+30246,
+11918,
+24343,
+2222,
+36322,
+29400,
+28016,
+9725,
+25263,
+49395,
+6063,
+14883,
+29313,
+18631,
+49305,
+48539,
+28264,
+47784,
+57560,
+36541,
+63011,
+48719,
+59556,
+29668,
+33758,
+50353,
+42583,
+17855,
+46045,
+14873,
+56360,
+9593,
+12413,
+22042,
+12732,
+42397,
+26030,
+29810,
+17496,
+65111,
+19585,
+36042,
+57172,
+28170,
+28967,
+54174,
+57007,
+6348,
+19851,
+24513,
+12478,
+41238,
+43143,
+39874,
+45360,
+61325,
+44512,
+19250,
+19913,
+48020,
+1883,
+59033,
+49897,
+42234,
+32747,
+23978,
+28868,
+47992,
+8096,
+2154,
+54529,
+19727,
+39284,
+50387,
+29553,
+4731,
+13643,
+21179,
+6663,
+1427,
+50347,
+38657,
+53737,
+33050,
+43615,
+291,
+55887,
+51261,
+36968,
+8551,
+41636,
+11592,
+64283,
+41112,
+10431,
+57905,
+21798,
+19194,
+30569,
+8154,
+23612,
+21447,
+22689,
+33688,
+55428,
+41905,
+63103,
+2129,
+55169,
+8510,
+37047,
+50086,
+1176,
+41595,
+9916,
+25814,
+34294,
+29732,
+27712,
+63762,
+40232,
+45520,
+58410,
+14762,
+17363,
+59986,
+29841,
+16584,
+63416,
+42260,
+27569,
+46552,
+37972,
+21131,
+15796,
+1117,
+24211,
+19471,
+9913,
+57017,
+42882,
+28540,
+32006,
+30037,
+26211,
+14045,
+37734,
+6302,
+12000,
+53158,
+6614,
+55809,
+60633,
+28031,
+34717,
+54293,
+57896,
+40893,
+47709,
+49855,
+1096,
+22464,
+389,
+3866,
+2272,
+37426,
+33800,
+16536,
+47518,
+12968,
+35516,
+38832,
+50614,
+3721,
+44426,
+58916,
+19672,
+12117,
+50303,
+60870,
+55692,
+19248,
+44514,
+49598,
+56249,
+56477,
+2562,
+3632,
+48581,
+54974,
+17195,
+4502,
+46967,
+29796,
+48754,
+61254,
+35402,
+52652,
+62135,
+46191,
+24383,
+17205,
+31962,
+27542,
+55028,
+6531,
+20840,
+62733,
+49031,
+8685,
+14797,
+40573,
+41298,
+15205,
+52578,
+24040,
+20291,
+61601,
+59409,
+600,
+35114,
+41309,
+11936,
+46130,
+13540,
+9100,
+30764,
+20053,
+45188,
+5821,
+60146,
+20878,
+13131,
+56480,
+10878,
+15700,
+61965,
+2864,
+184,
+22319,
+63051,
+36976,
+56467,
+25048,
+65122,
+57148,
+712,
+13329,
+12602,
+28092,
+50792,
+28729,
+37715,
+10373,
+49915,
+59463,
+58653,
+17654,
+37727,
+59314,
+24975,
+43583,
+42539,
+46393,
+6136,
+8085,
+54345,
+20937,
+34009,
+60919,
+55332,
+43900,
+8128,
+48148,
+52855,
+17123,
+42548,
+14994,
+13257,
+12621,
+57329,
+16523,
+62607,
+26180,
+56231,
+47768,
+62495,
+3794,
+18790,
+53931,
+5349,
+32745,
+42236,
+60551,
+58465,
+1762,
+23346,
+30194,
+23850,
+39077,
+10897,
+34309,
+18325,
+36267,
+13129,
+20880,
+34400,
+23506,
+31494,
+48221,
+10739,
+61834,
+32203,
+45279,
+10933,
+30324,
+34305,
+61723,
+38536,
+57108,
+58314,
+21644,
+24278,
+48651,
+18193,
+21394,
+53371,
+13534,
+46257,
+28250,
+35972,
+21741,
+21074,
+10801,
+39504,
+63602,
+44291,
+28187,
+16362,
+63996,
+40302,
+3489,
+37051,
+11889,
+36231,
+62255,
+52115,
+2467,
+17235,
+59017,
+39852,
+16768,
+56936,
+55837,
+40753,
+27318,
+54997,
+28942,
+41588,
+38760,
+43233,
+63484,
+9806,
+61642,
+10015,
+27018,
+9878,
+62765,
+28737,
+39798,
+55559,
+64822,
+30383,
+54393,
+34878,
+16459,
+58184,
+38430,
+61464,
+42217,
+51022,
+10680,
+63912,
+48387,
+32416,
+57241,
+62512,
+8995,
+54909,
+17096,
+29126,
+39325,
+13798,
+27804,
+33466,
+10744,
+46313,
+49337,
+42631,
+38090,
+60034,
+23955,
+2061,
+4546,
+51168,
+5543,
+7011,
+1431,
+37758,
+63752,
+45223,
+5289,
+49098,
+20729,
+54514,
+9033,
+19015,
+4085,
+38672,
+12216,
+13235,
+12028,
+29111,
+57580,
+31295,
+41148,
+14961,
+9528,
+63908,
+54065,
+61493,
+22387,
+27392,
+43544,
+48370,
+9076,
+4954,
+54732,
+14432,
+18877,
+42490,
+32815,
+35184,
+34512,
+37550,
+20152,
+6596,
+34056,
+7427,
+1845,
+8202,
+5274,
+40665,
+42265,
+12339,
+42770,
+57515,
+40070,
+44340,
+39377,
+53793,
+15170,
+19812,
+62561,
+30886,
+51309,
+37148,
+55544,
+35885,
+13203,
+54683,
+51148,
+27964,
+9265,
+64470,
+19367,
+34167,
+22713,
+60607,
+27338,
+41753,
+62843,
+44682,
+38420,
+63080,
+59534,
+64457,
+60193,
+1154,
+33649,
+46691,
+25795,
+18936,
+30276,
+15218,
+38443,
+34568,
+43662,
+31613,
+9850,
+24357,
+33426,
+11098,
+125,
+46293,
+36555,
+54905,
+65014,
+34025,
+24522,
+8903,
+11785,
+36028,
+31066,
+39770,
+17203,
+24385,
+54608,
+61310,
+6747,
+2497,
+53076,
+30755,
+5765,
+58025,
+24633,
+41086,
+57435,
+58346,
+41447,
+20558,
+60801,
+30202,
+43505,
+16621,
+49550,
+55006,
+33840,
+31196,
+13221,
+4576,
+45549,
+37083,
+39144,
+2442,
+62292,
+1597,
+31047,
+7467,
+18860,
+59280,
+64019,
+10067,
+346,
+49862,
+60809,
+43965,
+48597,
+6853,
+8425,
+10089,
+52567,
+30023,
+4753,
+16337,
+41803,
+17699,
+52849,
+32333,
+1811,
+3654,
+26682,
+17862,
+30548,
+37147,
+51419,
+30887,
+19744,
+41401,
+1690,
+18294,
+56020,
+46578,
+35701,
+38930,
+50628,
+40089,
+43294,
+27230,
+1652,
+37547,
+29626,
+38011,
+31554,
+49147,
+41393,
+17165,
+56463,
+745,
+40149,
+34725,
+25881,
+14928,
+55529,
+24069,
+35269,
+48430,
+12441,
+13396,
+38246,
+49873,
+29805,
+17743,
+19979,
+64531,
+57191,
+43996,
+27588,
+45565,
+22037,
+46229,
+52897,
+36967,
+51797,
+55888,
+8689,
+32679,
+47395,
+60567,
+43855,
+64344,
+17100,
+36987,
+32952,
+55592,
+12912,
+52525,
+43647,
+30753,
+53078,
+54780,
+41379,
+13167,
+23577,
+22246,
+20631,
+55,
+38621,
+7676,
+22504,
+29655,
+28732,
+10890,
+49068,
+30911,
+60384,
+30925,
+1406,
+24688,
+41098,
+30431,
+59269,
+26929,
+15194,
+22485,
+3822,
+65418,
+2778,
+45351,
+31112,
+46635,
+56821,
+52393,
+15833,
+16884,
+47346,
+9646,
+33921,
+61220,
+15049,
+61719,
+7384,
+57134,
+56170,
+36702,
+22996,
+19146,
+41164,
+36838,
+6731,
+40470,
+23425,
+32652,
+56743,
+5509,
+50587,
+13460,
+16793,
+45585,
+61514,
+11228,
+2534,
+56050,
+13563,
+13206,
+3291,
+28768,
+8612,
+59504,
+13028,
+9865,
+29488,
+55646,
+28608,
+6587,
+5542,
+51480,
+4547,
+63535,
+23124,
+45269,
+42553,
+61101,
+2986,
+2963,
+43626,
+36335,
+20723,
+41115,
+25678,
+16636,
+28718,
+34133,
+18719,
+9639,
+27963,
+51413,
+54684,
+13571,
+8775,
+11001,
+60433,
+56304,
+26345,
+22879,
+49592,
+37254,
+578,
+54219,
+38575,
+55754,
+35503,
+3990,
+23861,
+63772,
+52869,
+26817,
+805,
+42039,
+42268,
+6375,
+52556,
+29220,
+4287,
+45990,
+46805,
+50297,
+24665,
+32998,
+59425,
+3776,
+44959,
+37832,
+55865,
+48820,
+12496,
+50571,
+61890,
+10804,
+14091,
+46661,
+27438,
+622,
+40291,
+46008,
+3300,
+22118,
+3118,
+43118,
+6757,
+52514,
+43590,
+48555,
+25782,
+11536,
+61323,
+45362,
+576,
+37256,
+62866,
+56137,
+63746,
+22225,
+15337,
+53057,
+24301,
+65120,
+25050,
+27065,
+26687,
+5347,
+53933,
+24475,
+4245,
+31226,
+47214,
+53244,
+59092,
+28020,
+5792,
+22220,
+55236,
+34532,
+23809,
+19552,
+5518,
+29118,
+45111,
+2815,
+17898,
+9195,
+22728,
+16546,
+14699,
+38222,
+18162,
+38660,
+37210,
+37221,
+35812,
+13272,
+20976,
+2541,
+40504,
+53869,
+38193,
+5171,
+22721,
+65189,
+15677,
+34985,
+59568,
+27751,
+42473,
+53353,
+34074,
+26587,
+19634,
+25111,
+15892,
+45460,
+10679,
+51504,
+42218,
+17435,
+29428,
+35777,
+27113,
+56515,
+40612,
+1250,
+48512,
+57650,
+41828,
+63755,
+3460,
+47530,
+43332,
+29325,
+53392,
+30994,
+3179,
+59211,
+1835,
+39159,
+31921,
+20866,
+27256,
+37404,
+43134,
+4556,
+23932,
+29860,
+56116,
+1302,
+41999,
+42870,
+34354,
+9688,
+61209,
+22296,
+42450,
+41479,
+15916,
+11818,
+31508,
+28958,
+25058,
+18350,
+36864,
+28086,
+32773,
+53622,
+9831,
+6780,
+46566,
+2789,
+45642,
+53438,
+22141,
+30005,
+53427,
+16871,
+31577,
+2203,
+60929,
+11481,
+42644,
+11839,
+18782,
+59097,
+19085,
+40832,
+46859,
+44214,
+29161,
+26850,
+45265,
+4815,
+12643,
+56317,
+13252,
+50144,
+678,
+31563,
+44046,
+42382,
+35583,
+36779,
+45533,
+7055,
+52321,
+10599,
+17764,
+58798,
+4446,
+45731,
+28830,
+45182,
+65084,
+21895,
+435,
+60277,
+19940,
+48024,
+14907,
+25450,
+9714,
+55102,
+35395,
+20903,
+26239,
+34850,
+30779,
+21193,
+64063,
+59843,
+45634,
+23615,
+46004,
+30612,
+52160,
+37625,
+44666,
+25459,
+30512,
+7454,
+53213,
+39888,
+11078,
+11122,
+36944,
+44558,
+48090,
+1575,
+42625,
+18969,
+62750,
+9434,
+13289,
+37543,
+18967,
+42627,
+35769,
+28277,
+541,
+33495,
+33828,
+27563,
+44408,
+24292,
+62994,
+25035,
+64326,
+63209,
+27179,
+48305,
+52032,
+8693,
+25367,
+23668,
+12090,
+3470,
+47563,
+26948,
+57058,
+37856,
+1817,
+5156,
+43794,
+44465,
+424,
+45954,
+62141,
+6796,
+44258,
+55186,
+52095,
+31080,
+61732,
+50153,
+46916,
+7769,
+6774,
+5302,
+48990,
+14486,
+27316,
+40755,
+32189,
+42314,
+51983,
+57390,
+25754,
+4220,
+8230,
+31189,
+63296,
+31804,
+17244,
+28401,
+29036,
+40449,
+47024,
+48428,
+35271,
+49962,
+63253,
+62897,
+29777,
+38188,
+57129,
+63098,
+56694,
+35423,
+8824,
+55312,
+32199,
+16025,
+46101,
+4088,
+24940,
+5146,
+20845,
+63309,
+43761,
+42971,
+24752,
+17997,
+52258,
+35958,
+28728,
+51634,
+28093,
+48805,
+39172,
+61409,
+54549,
+49707,
+31840,
+31056,
+62130,
+23572,
+64893,
+10703,
+58001,
+65164,
+18147,
+7126,
+13776,
+48175,
+46516,
+62461,
+34677,
+35438,
+7167,
+18735,
+4202,
+12756,
+40812,
+57880,
+55942,
+53126,
+30298,
+56077,
+52450,
+59413,
+42333,
+61112,
+43480,
+56502,
+46524,
+3555,
+24347,
+30958,
+59399,
+57454,
+38981,
+58102,
+61421,
+29417,
+17175,
+33918,
+56851,
+13863,
+24965,
+45879,
+11190,
+44602,
+24179,
+58201,
+38528,
+4347,
+48792,
+24640,
+40819,
+19866,
+12510,
+55758,
+62046,
+28488,
+15453,
+55305,
+2685,
+9346,
+58863,
+53782,
+56812,
+23582,
+64268,
+19383,
+25213,
+54052,
+37295,
+20527,
+8075,
+41623,
+30170,
+65210,
+47646,
+19772,
+27352,
+40917,
+33250,
+12633,
+41505,
+56991,
+6037,
+20830,
+10443,
+30956,
+24349,
+48240,
+7818,
+35481,
+56493,
+19199,
+32336,
+33415,
+15740,
+52344,
+39819,
+49134,
+64615,
+5836,
+16607,
+18820,
+22788,
+441,
+26542,
+8907,
+54598,
+3602,
+27540,
+31964,
+30710,
+58833,
+25373,
+49372,
+19491,
+13682,
+9178,
+6177,
+57444,
+10392,
+21668,
+48350,
+36499,
+25090,
+59473,
+13664,
+36225,
+17070,
+39390,
+32960,
+5278,
+54700,
+16228,
+21319,
+58016,
+63856,
+42405,
+52434,
+9710,
+58850,
+27692,
+23960,
+56419,
+6219,
+31326,
+30053,
+61663,
+27380,
+17512,
+14618,
+40088,
+51299,
+38931,
+26068,
+4496,
+59490,
+4803,
+4271,
+30453,
+65269,
+37065,
+123,
+11100,
+32371,
+3720,
+51712,
+38833,
+887,
+10112,
+39655,
+14255,
+10009,
+29090,
+56574,
+50013,
+62342,
+39869,
+12515,
+23339,
+16155,
+22397,
+32196,
+30562,
+61155,
+47497,
+8833,
+18941,
+54802,
+11107,
+32740,
+28599,
+13459,
+51189,
+5510,
+47474,
+19083,
+59099,
+19488,
+33474,
+4500,
+17197,
+16048,
+24388,
+14079,
+56010,
+46020,
+2711,
+61889,
+51108,
+12497,
+40803,
+1204,
+27086,
+38142,
+47253,
+21211,
+61401,
+10731,
+20721,
+36337,
+30782,
+46509,
+16393,
+23527,
+13285,
+15633,
+56890,
+45034,
+54206,
+19477,
+2077,
+411,
+54244,
+24766,
+55181,
+22988,
+13930,
+52281,
+45202,
+17834,
+46000,
+26352,
+50363,
+10376,
+21253,
+24222,
+17782,
+12675,
+13552,
+8149,
+24310,
+24697,
+12125,
+25938,
+56033,
+4863,
+27609,
+2453,
+50449,
+36729,
+1523,
+13117,
+2474,
+2596,
+12316,
+16634,
+25680,
+61511,
+2883,
+7750,
+35843,
+54862,
+24100,
+38465,
+1042,
+17909,
+53876,
+37474,
+30733,
+43440,
+17367,
+24517,
+64696,
+19272,
+58091,
+32457,
+10660,
+7218,
+58938,
+9765,
+4316,
+14332,
+3819,
+49291,
+39182,
+62054,
+2956,
+47763,
+34958,
+36083,
+39001,
+27100,
+39196,
+1362,
+9281,
+48077,
+59059,
+770,
+32812,
+39765,
+56973,
+63793,
+65350,
+36293,
+58158,
+64250,
+5352,
+47504,
+65011,
+43942,
+39410,
+35574,
+44533,
+17500,
+23930,
+4558,
+29801,
+29660,
+1994,
+36728,
+50521,
+2454,
+3418,
+12403,
+49587,
+41898,
+17016,
+44098,
+57232,
+27900,
+34227,
+22857,
+17912,
+28370,
+65307,
+60959,
+19990,
+1332,
+60474,
+2681,
+21529,
+59794,
+9052,
+58125,
+63000,
+9692,
+40643,
+41243,
+2545,
+42963,
+7660,
+13312,
+41121,
+63812,
+62915,
+27306,
+13,
+23407,
+53401,
+18023,
+19785,
+12909,
+63878,
+33481,
+38074,
+36101,
+37021,
+30493,
+31725,
+46647,
+28292,
+25078,
+42019,
+64061,
+21195,
+26162,
+32643,
+62906,
+36308,
+32987,
+33292,
+29552,
+51811,
+39285,
+21726,
+17858,
+11733,
+58886,
+8644,
+33683,
+38908,
+16528,
+20262,
+4487,
+27936,
+12772,
+63147,
+44305,
+22285,
+15331,
+53642,
+11743,
+14114,
+45726,
+49913,
+10375,
+50537,
+26353,
+3485,
+95,
+44375,
+1373,
+11687,
+62269,
+3993,
+42582,
+51859,
+33759,
+22739,
+52030,
+48307,
+38656,
+51804,
+1428,
+22290,
+38607,
+14316,
+43053,
+8673,
+69,
+30897,
+21689,
+48838,
+46373,
+62696,
+24162,
+24238,
+13866,
+12960,
+12804,
+24404,
+32650,
+23427,
+17079,
+1202,
+40805,
+43710,
+48876,
+12538,
+35260,
+14147,
+27095,
+61864,
+45959,
+19327,
+35160,
+56923,
+6035,
+56993,
+538,
+53379,
+19047,
+40824,
+34793,
+56721,
+60869,
+51706,
+12118,
+35130,
+12082,
+55417,
+24664,
+51118,
+46806,
+45513,
+34843,
+44587,
+30187,
+57030,
+2089,
+8248,
+7779,
+48463,
+54072,
+26018,
+38627,
+24339,
+21038,
+29276,
+50043,
+63093,
+3247,
+41345,
+7760,
+43667,
+38387,
+58663,
+16688,
+5216,
+60287,
+18230,
+11224,
+24365,
+15733,
+12801,
+38585,
+28452,
+26395,
+36531,
+53714,
+59124,
+44309,
+20446,
+29880,
+25864,
+3898,
+27147,
+49231,
+17574,
+25071,
+36348,
+6627,
+29991,
+42937,
+30,
+44397,
+4298,
+18005,
+22207,
+61197,
+64813,
+42242,
+544,
+22007,
+26781,
+5940,
+5991,
+57324,
+29266,
+26738,
+60087,
+62459,
+46518,
+3099,
+60739,
+40714,
+25382,
+25513,
+53175,
+42586,
+44055,
+60446,
+7559,
+48833,
+26113,
+7673,
+41600,
+48012,
+11875,
+5238,
+16551,
+32916,
+43251,
+15167,
+11896,
+12032,
+9407,
+39241,
+43311,
+14456,
+12481,
+27729,
+44889,
+48607,
+32965,
+61556,
+52480,
+33407,
+40430,
+35051,
+11969,
+17045,
+46226,
+65426,
+39919,
+6228,
+21517,
+29736,
+27327,
+25328,
+31164,
+18250,
+8734,
+40126,
+34219,
+9257,
+49221,
+21058,
+56124,
+44196,
+35340,
+49883,
+23908,
+59069,
+35075,
+45884,
+15845,
+33150,
+43778,
+24920,
+54009,
+21009,
+62584,
+25199,
+27758,
+46915,
+50844,
+61733,
+4809,
+10942,
+8667,
+61372,
+23153,
+29956,
+677,
+50942,
+13253,
+25125,
+32926,
+41556,
+56101,
+52250,
+49906,
+62171,
+57397,
+39940,
+234,
+53825,
+37031,
+12306,
+25040,
+336,
+37923,
+20353,
+455,
+54184,
+42301,
+11275,
+21558,
+36656,
+1560,
+41943,
+8190,
+58257,
+63017,
+47248,
+64445,
+5107,
+6275,
+1181,
+56833,
+24407,
+36005,
+42471,
+27753,
+58768,
+5414,
+13532,
+53373,
+58307,
+48603,
+65329,
+48758,
+58589,
+8416,
+28234,
+26051,
+16345,
+20990,
+63212,
+65397,
+22161,
+1175,
+51773,
+37048,
+14814,
+10233,
+32936,
+10308,
+57833,
+8526,
+38922,
+32671,
+2378,
+10304,
+62727,
+3454,
+64931,
+60880,
+25436,
+48585,
+64266,
+23584,
+7248,
+54689,
+38603,
+65465,
+48923,
+63246,
+45419,
+29686,
+29574,
+31020,
+42736,
+10714,
+64119,
+21785,
+11351,
+23238,
+45140,
+62283,
+8382,
+16911,
+2733,
+23229,
+63092,
+50280,
+29277,
+61811,
+29066,
+64095,
+8051,
+40282,
+21351,
+47616,
+44937,
+35030,
+52083,
+45930,
+12252,
+44352,
+59397,
+30960,
+3790,
+9561,
+4833,
+18507,
+31647,
+36118,
+48364,
+44864,
+59042,
+40399,
+54301,
+43704,
+62341,
+50605,
+56575,
+42705,
+45163,
+29506,
+43123,
+9820,
+10638,
+20705,
+28332,
+11851,
+12791,
+9310,
+30767,
+40116,
+54934,
+29382,
+31729,
+29977,
+32564,
+39291,
+42969,
+43763,
+33972,
+21493,
+45411,
+53007,
+29121,
+56363,
+61930,
+48763,
+23097,
+28644,
+61358,
+40393,
+22931,
+41306,
+32261,
+46388,
+44629,
+53692,
+21819,
+58729,
+18785,
+47010,
+41762,
+32356,
+32013,
+2824,
+56792,
+63252,
+50818,
+35272,
+19038,
+59786,
+54448,
+62033,
+23967,
+30716,
+33065,
+18836,
+21791,
+19540,
+25812,
+9918,
+62426,
+2888,
+19374,
+20205,
+30791,
+49075,
+4484,
+25982,
+28783,
+28382,
+11747,
+40864,
+21504,
+45723,
+2978,
+65516,
+22939,
+22379,
+58138,
+48166,
+6932,
+35128,
+12120,
+785,
+28934,
+37629,
+10543,
+37599,
+52767,
+22815,
+33186,
+63972,
+59462,
+51630,
+10374,
+50365,
+45727,
+31418,
+52620,
+56156,
+38897,
+62170,
+50137,
+52251,
+14983,
+8648,
+16838,
+7423,
+18089,
+47797,
+42233,
+51822,
+59034,
+18255,
+33089,
+44134,
+59974,
+61520,
+15912,
+48780,
+31866,
+63975,
+23596,
+26358,
+23907,
+50168,
+35341,
+41689,
+58170,
+26666,
+34813,
+62302,
+34796,
+11955,
+29804,
+51274,
+38247,
+1209,
+2330,
+732,
+44348,
+46704,
+38362,
+26224,
+35982,
+60808,
+51330,
+347,
+14502,
+30553,
+38681,
+41993,
+1095,
+51725,
+47710,
+40946,
+37279,
+11310,
+7261,
+34051,
+32144,
+65065,
+26021,
+43715,
+36312,
+32543,
+27004,
+42017,
+25080,
+764,
+54658,
+13888,
+3573,
+36767,
+56927,
+32347,
+35458,
+45028,
+2516,
+1381,
+31252,
+64565,
+17887,
+58859,
+6273,
+5109,
+1322,
+16893,
+16329,
+21444,
+45637,
+28045,
+4990,
+1905,
+65091,
+9132,
+18242,
+59165,
+43226,
+56599,
+21854,
+35110,
+53172,
+41171,
+15271,
+52737,
+49260,
+13623,
+43261,
+43404,
+32121,
+14852,
+13194,
+33368,
+34161,
+29301,
+60821,
+56809,
+27922,
+5073,
+7557,
+60448,
+44916,
+32315,
+1739,
+29116,
+5520,
+37215,
+60130,
+11433,
+2437,
+8845,
+46031,
+26199,
+48711,
+37695,
+18362,
+52472,
+22350,
+47511,
+19733,
+56548,
+5477,
+53682,
+61972,
+25506,
+2501,
+57025,
+28492,
+27485,
+44720,
+16974,
+64179,
+42984,
+21991,
+17169,
+39098,
+52090,
+19395,
+31793,
+61133,
+65151,
+30131,
+49132,
+39821,
+40860,
+44405,
+13787,
+64421,
+33034,
+53611,
+27903,
+13231,
+4840,
+61788,
+24114,
+46699,
+43748,
+45839,
+36877,
+48163,
+34352,
+42872,
+55287,
+62296,
+60825,
+59723,
+18626,
+36740,
+24797,
+40957,
+6243,
+43255,
+26547,
+28521,
+58840,
+8291,
+37978,
+47690,
+18910,
+31839,
+50786,
+54550,
+13143,
+14326,
+48654,
+1581,
+3108,
+47304,
+20156,
+15926,
+30315,
+46084,
+52907,
+55154,
+19652,
+15374,
+32388,
+8722,
+62390,
+44477,
+9415,
+54353,
+10497,
+23655,
+55124,
+1455,
+11569,
+27377,
+52523,
+12914,
+54161,
+63533,
+4549,
+37782,
+6771,
+5585,
+17799,
+4688,
+33070,
+9317,
+5480,
+56056,
+46506,
+32432,
+16312,
+20237,
+20438,
+44122,
+56378,
+32655,
+8448,
+54663,
+44496,
+45500,
+38582,
+12963,
+53107,
+23592,
+57349,
+35167,
+59338,
+40425,
+14512,
+20078,
+24497,
+42229,
+26745,
+8810,
+54485,
+38863,
+53695,
+20546,
+29422,
+41433,
+48786,
+55862,
+4899,
+49127,
+2649,
+5516,
+19554,
+4016,
+15647,
+16758,
+58499,
+24057,
+14556,
+53720,
+38022,
+27715,
+29504,
+45165,
+638,
+9129,
+45317,
+45380,
+15839,
+35223,
+41010,
+17239,
+6817,
+59934,
+11267,
+31298,
+12154,
+35522,
+36844,
+61141,
+56248,
+51701,
+44515,
+20478,
+12485,
+57628,
+37253,
+51139,
+22880,
+49312,
+34252,
+41897,
+50445,
+12404,
+18270,
+60484,
+25933,
+56529,
+56661,
+10354,
+42551,
+45271,
+52754,
+23807,
+34534,
+43445,
+59572,
+64560,
+11752,
+37618,
+17201,
+39772,
+21081,
+19110,
+36564,
+40119,
+46447,
+49443,
+5739,
+56324,
+56140,
+16455,
+49325,
+27710,
+29734,
+21519,
+17643,
+8741,
+55005,
+51349,
+16622,
+2197,
+31875,
+37414,
+18880,
+62480,
+36694,
+16500,
+422,
+44467,
+18537,
+55968,
+26735,
+39459,
+45563,
+27590,
+45975,
+25273,
+823,
+59891,
+43851,
+39296,
+30689,
+16066,
+6250,
+17300,
+23896,
+28531,
+22780,
+13384,
+45943,
+26466,
+9356,
+36219,
+18951,
+42497,
+33337,
+14731,
+19437,
+23619,
+35275,
+22230,
+43938,
+8998,
+38978,
+58167,
+21986,
+21486,
+55610,
+16514,
+56641,
+2705,
+55923,
+4821,
+52803,
+23142,
+25714,
+24679,
+54154,
+48339,
+14959,
+41150,
+10931,
+45281,
+59866,
+44120,
+20440,
+22194,
+48716,
+64786,
+24206,
+27857,
+44264,
+35835,
+27252,
+102,
+20362,
+34847,
+16011,
+59288,
+28857,
+7484,
+11366,
+32167,
+31378,
+40375,
+33323,
+10914,
+23706,
+20317,
+65109,
+17498,
+44535,
+22476,
+2873,
+42803,
+2783,
+26390,
+57132,
+7386,
+19191,
+3829,
+35302,
+54251,
+61949,
+5738,
+49562,
+46448,
+44165,
+11921,
+29878,
+20448,
+33489,
+7404,
+40518,
+22836,
+12628,
+10385,
+49403,
+33389,
+55573,
+56013,
+24315,
+7615,
+17633,
+48141,
+24008,
+21298,
+31211,
+21580,
+48122,
+63931,
+43774,
+22717,
+59102,
+439,
+22790,
+55994,
+12879,
+40061,
+42647,
+42108,
+48460,
+38300,
+5456,
+33388,
+49431,
+10386,
+65273,
+35386,
+23753,
+63466,
+63860,
+6062,
+51875,
+25264,
+16299,
+44924,
+61961,
+23411,
+55273,
+56483,
+7414,
+5198,
+44726,
+4747,
+42197,
+14937,
+43141,
+41240,
+62061,
+53317,
+60861,
+25613,
+61851,
+33472,
+19490,
+50666,
+25374,
+61917,
+29700,
+38287,
+6959,
+23664,
+38365,
+17218,
+60160,
+62405,
+45632,
+59845,
+31140,
+24746,
+46285,
+45626,
+32809,
+7897,
+37964,
+43335,
+58099,
+18357,
+3089,
+59663,
+8772,
+7800,
+39629,
+22017,
+31692,
+46404,
+10096,
+64837,
+8533,
+42630,
+51487,
+46314,
+61941,
+17356,
+58447,
+49267,
+53312,
+58327,
+28074,
+25149,
+24165,
+27709,
+49557,
+16456,
+10586,
+3969,
+31746,
+41928,
+47502,
+5354,
+26534,
+38439,
+21812,
+36782,
+34251,
+49590,
+22881,
+10836,
+14890,
+44219,
+13596,
+48538,
+51870,
+18632,
+9378,
+42374,
+34371,
+47289,
+53998,
+2256,
+17946,
+3660,
+48836,
+21691,
+41939,
+39181,
+50486,
+3820,
+22487,
+6905,
+31365,
+41683,
+63307,
+20847,
+10818,
+20409,
+39731,
+25365,
+8695,
+57165,
+46944,
+40944,
+47712,
+6351,
+44418,
+27891,
+2494,
+34096,
+46792,
+53311,
+49332,
+58448,
+11531,
+29013,
+6093,
+35243,
+13622,
+49802,
+52738,
+467,
+57836,
+34276,
+31086,
+19742,
+30889,
+24597,
+34853,
+10277,
+10554,
+13245,
+18253,
+59036,
+45645,
+8439,
+25416,
+19092,
+25992,
+40186,
+40272,
+16144,
+20614,
+20468,
+3984,
+18552,
+42058,
+17573,
+50252,
+27148,
+6876,
+22644,
+20963,
+33104,
+15874,
+45301,
+28687,
+21057,
+50173,
+9258,
+23989,
+55493,
+510,
+13211,
+5264,
+63397,
+17404,
+30418,
+28690,
+18316,
+48643,
+15717,
+44094,
+43999,
+2195,
+16624,
+35866,
+53228,
+60783,
+14217,
+23735,
+48157,
+36286,
+17426,
+20553,
+24626,
+39485,
+15445,
+44548,
+42665,
+21243,
+9296,
+16743,
+40899,
+52836,
+8913,
+27623,
+7842,
+42918,
+46898,
+38698,
+43475,
+31755,
+37349,
+13991,
+1542,
+33173,
+52770,
+42195,
+4749,
+27223,
+34380,
+29894,
+54408,
+54837,
+46604,
+17583,
+53138,
+57593,
+40678,
+44565,
+62416,
+7710,
+5041,
+36409,
+26214,
+65533,
+21847,
+9363,
+23832,
+56799,
+41392,
+51290,
+31555,
+19027,
+14980,
+42011,
+38103,
+37649,
+62967,
+57196,
+22216,
+22886,
+23476,
+64614,
+50682,
+39820,
+49745,
+30132,
+46092,
+60708,
+2648,
+49630,
+4900,
+4133,
+10258,
+8261,
+59676,
+25802,
+26515,
+6311,
+23103,
+47205,
+47258,
+21267,
+56235,
+47401,
+53475,
+25116,
+11881,
+2996,
+36979,
+54077,
+5257,
+54557,
+34617,
+3952,
+41001,
+63954,
+62431,
+20728,
+51472,
+5290,
+33779,
+47947,
+46852,
+48810,
+10788,
+3184,
+35249,
+65023,
+39221,
+10466,
+17182,
+59733,
+37654,
+9396,
+15979,
+16213,
+54952,
+30658,
+14028,
+7354,
+4483,
+49943,
+30792,
+61839,
+40986,
+6213,
+792,
+30910,
+51231,
+10891,
+62704,
+62923,
+52245,
+1643,
+12638,
+28572,
+48402,
+62566,
+30607,
+29774,
+47925,
+33858,
+7964,
+51904,
+56918,
+13043,
+59956,
+9949,
+31439,
+18747,
+4451,
+23569,
+58456,
+64492,
+17013,
+7624,
+34149,
+61648,
+28842,
+15995,
+65116,
+38543,
+25249,
+21344,
+8684,
+51676,
+62734,
+13637,
+39007,
+16938,
+58686,
+12697,
+9926,
+16037,
+8897,
+58946,
+31596,
+980,
+48636,
+27639,
+57270,
+8207,
+13934,
+2931,
+27472,
+60798,
+26628,
+15159,
+45276,
+48675,
+19315,
+4103,
+27867,
+24508,
+31736,
+13820,
+18020,
+38150,
+42562,
+35451,
+21114,
+53489,
+57590,
+29025,
+42808,
+14485,
+50839,
+5303,
+18597,
+60298,
+15328,
+32622,
+27477,
+4826,
+57093,
+37586,
+12140,
+17728,
+34128,
+48943,
+31239,
+33552,
+9441,
+19415,
+37890,
+17759,
+56213,
+22559,
+44253,
+53541,
+20091,
+1309,
+56683,
+17442,
+30750,
+52122,
+65367,
+11247,
+47365,
+3348,
+8494,
+40681,
+35695,
+13780,
+2122,
+62451,
+25536,
+10695,
+26967,
+64024,
+17989,
+58518,
+31238,
+48977,
+34129,
+42866,
+26184,
+28591,
+20981,
+1291,
+38400,
+37095,
+26814,
+10811,
+59822,
+59700,
+14858,
+46718,
+2916,
+57989,
+34033,
+54818,
+63245,
+50062,
+65466,
+34642,
+31607,
+53481,
+5095,
+22031,
+14187,
+15988,
+63341,
+16333,
+29672,
+26635,
+6007,
+59353,
+4686,
+17801,
+64209,
+19509,
+53220,
+15023,
+39858,
+14383,
+36820,
+34415,
+4695,
+38310,
+18187,
+29177,
+52113,
+62257,
+59442,
+30296,
+53128,
+38701,
+56873,
+53638,
+9029,
+59173,
+17253,
+44157,
+57065,
+5788,
+20424,
+62724,
+7156,
+12537,
+50322,
+43711,
+1058,
+2167,
+41143,
+31809,
+37607,
+46078,
+36077,
+61122,
+44796,
+33708,
+62019,
+36133,
+36854,
+18420,
+58821,
+61800,
+10228,
+43876,
+45771,
+57338,
+9762,
+31484,
+33395,
+42352,
+6873,
+15265,
+60322,
+54766,
+55175,
+58403,
+39959,
+32266,
+19930,
+64591,
+2567,
+46372,
+50337,
+21690,
+49295,
+3661,
+26112,
+50216,
+7560,
+5245,
+863,
+34059,
+17305,
+28619,
+64835,
+10098,
+28308,
+55699,
+63506,
+12495,
+51110,
+55866,
+8859,
+56314,
+18002,
+19404,
+25054,
+2363,
+28417,
+10787,
+49093,
+46853,
+57933,
+25921,
+39171,
+50790,
+28094,
+13351,
+29849,
+32326,
+29912,
+42518,
+42860,
+3316,
+54928,
+56240,
+16350,
+24639,
+50731,
+4348,
+64453,
+63420,
+41101,
+55861,
+49633,
+41434,
+19239,
+51889,
+27134,
+31865,
+49889,
+15913,
+7039,
+46482,
+58057,
+34395,
+40793,
+40513,
+60072,
+57082,
+25684,
+14004,
+8602,
+63923,
+25842,
+58734,
+23096,
+49983,
+61931,
+33124,
+26657,
+58588,
+50097,
+65330,
+22589,
+61253,
+51690,
+29797,
+53558,
+61418,
+55082,
+34412,
+23195,
+19544,
+7170,
+52076,
+14350,
+19953,
+359,
+42418,
+22150,
+52588,
+5017,
+56670,
+20011,
+30631,
+6182,
+60853,
+7866,
+25601,
+57902,
+36461,
+38381,
+57494,
+5567,
+45964,
+28393,
+64317,
+47850,
+58175,
+59555,
+51863,
+63012,
+64785,
+49481,
+22195,
+25692,
+63227,
+37694,
+49774,
+26200,
+10449,
+54080,
+36242,
+33557,
+7721,
+13436,
+18652,
+62411,
+32025,
+56888,
+15635,
+58115,
+20887,
+48247,
+53260,
+58144,
+27512,
+1778,
+52018,
+61005,
+62110,
+33879,
+7106,
+25941,
+60493,
+38728,
+33344,
+54852,
+61925,
+43287,
+58551,
+57910,
+52037,
+19314,
+49007,
+45277,
+32205,
+54120,
+34486,
+61057,
+9585,
+43804,
+12109,
+14575,
+33961,
+19879,
+52724,
+52049,
+42714,
+13001,
+40915,
+27354,
+20061,
+3859,
+1580,
+49703,
+14327,
+18192,
+51561,
+24279,
+24491,
+54955,
+35182,
+32817,
+64214,
+15716,
+49209,
+18317,
+7940,
+42083,
+38206,
+47426,
+27638,
+49018,
+981,
+41312,
+34455,
+3807,
+35265,
+57482,
+45610,
+28007,
+57154,
+15490,
+62805,
+56179,
+29320,
+12348,
+24249,
+58033,
+27105,
+4075,
+28380,
+28785,
+27269,
+44036,
+9228,
+24209,
+1119,
+1932,
+37906,
+32964,
+50196,
+44890,
+11546,
+65328,
+50099,
+58308,
+30675,
+26529,
+16510,
+6852,
+51327,
+43966,
+35662,
+40847,
+24441,
+34507,
+22079,
+45215,
+46152,
+40326,
+45149,
+64265,
+50069,
+25437,
+55127,
+54973,
+51696,
+3633,
+58198,
+29590,
+46243,
+8636,
+37235,
+3199,
+62937,
+32303,
+43175,
+62613,
+39238,
+59709,
+1958,
+52357,
+31305,
+20816,
+15780,
+1359,
+59418,
+12245,
+44372,
+24325,
+36385,
+25781,
+51092,
+43591,
+8578,
+9191,
+31094,
+36343,
+41059,
+59685,
+45197,
+32755,
+10487,
+36550,
+6637,
+12984,
+2464,
+28263,
+51869,
+49306,
+13597,
+56000,
+43099,
+55157,
+23661,
+9238,
+23743,
+4420,
+60231,
+6635,
+36552,
+17907,
+1044,
+16495,
+57914,
+23638,
+15094,
+21440,
+37952,
+61278,
+6881,
+30903,
+47014,
+54969,
+57649,
+51013,
+1251,
+19526,
+28200,
+63809,
+30929,
+304,
+1720,
+55637,
+63671,
+23264,
+44898,
+29016,
+54209,
+57079,
+58641,
+38943,
+4957,
+38004,
+53567,
+15349,
+19312,
+52039,
+32534,
+32559,
+36620,
+44920,
+7729,
+196,
+13308,
+61702,
+42171,
+45250,
+35942,
+39701,
+25919,
+57935,
+59856,
+44850,
+33118,
+45344,
+8099,
+28101,
+11454,
+29311,
+14885,
+26148,
+60558,
+54071,
+50287,
+7780,
+38299,
+49407,
+42109,
+15211,
+64336,
+6937,
+38815,
+26801,
+20113,
+65046,
+33037,
+41220,
+57924,
+48310,
+3018,
+41659,
+25349,
+25267,
+20753,
+10627,
+14042,
+36412,
+18433,
+1192,
+14284,
+28551,
+6871,
+42354,
+11951,
+60104,
+12440,
+51278,
+35270,
+50820,
+47025,
+4554,
+43136,
+9735,
+19825,
+25874,
+31593,
+559,
+3863,
+24306,
+22536,
+37383,
+12681,
+13928,
+22990,
+57508,
+31896,
+65162,
+58003,
+88,
+19883,
+53689,
+62075,
+18009,
+62565,
+49060,
+28573,
+37742,
+32562,
+29979,
+38416,
+20027,
+63591,
+2764,
+9079,
+60002,
+45749,
+27538,
+3604,
+32415,
+51501,
+63913,
+4887,
+7630,
+43741,
+28889,
+8492,
+3350,
+60942,
+61244,
+43107,
+59209,
+3181,
+33305,
+24143,
+14249,
+9075,
+51450,
+43545,
+15784,
+26440,
+2145,
+44863,
+50020,
+36119,
+14794,
+31620,
+29100,
+11518,
+58136,
+22381,
+55070,
+16451,
+5805,
+2315,
+46324,
+36498,
+50658,
+21669,
+11442,
+32738,
+11109,
+36592,
+2897,
+10562,
+13843,
+61036,
+14958,
+49490,
+54155,
+27918,
+15968,
+25766,
+14168,
+41153,
+13854,
+33576,
+57432,
+56149,
+12575,
+46866,
+44887,
+27731,
+25167,
+27210,
+64117,
+10716,
+27171,
+16173,
+5378,
+24917,
+7147,
+18384,
+58954,
+7180,
+39482,
+3017,
+48448,
+57925,
+38655,
+50349,
+52031,
+50868,
+27180,
+63069,
+14001,
+6521,
+54374,
+24526,
+15929,
+58721,
+63682,
+53484,
+33732,
+14712,
+37137,
+17872,
+40963,
+2428,
+7124,
+18149,
+56955,
+6363,
+16653,
+21381,
+5325,
+28192,
+6289,
+19933,
+59150,
+30327,
+1196,
+39367,
+22896,
+3762,
+39884,
+28950,
+31924,
+45570,
+2000,
+42415,
+6356,
+41249,
+59140,
+46920,
+52551,
+63075,
+11452,
+28103,
+46493,
+44173,
+22948,
+13049,
+3883,
+28298,
+20918,
+3325,
+42070,
+7578,
+53259,
+48696,
+20888,
+55285,
+42874,
+58045,
+26571,
+7817,
+50692,
+24350,
+59286,
+16013,
+41642,
+6784,
+43834,
+31477,
+16709,
+36919,
+23349,
+21958,
+57635,
+9681,
+63663,
+61899,
+36923,
+1586,
+10738,
+51575,
+31495,
+30233,
+11288,
+35141,
+33232,
+22364,
+39584,
+32969,
+37079,
+59587,
+18057,
+24444,
+40720,
+19351,
+8376,
+27297,
+55918,
+7694,
+19232,
+2083,
+58022,
+6150,
+60200,
+480,
+43874,
+10230,
+62854,
+44010,
+36904,
+7598,
+3744,
+65503,
+1987,
+2935,
+56453,
+20875,
+54679,
+53592,
+3595,
+16203,
+7091,
+9473,
+22490,
+12560,
+46515,
+50774,
+13777,
+5552,
+44070,
+22096,
+15038,
+41021,
+5607,
+6931,
+49929,
+58139,
+34351,
+49728,
+36878,
+4824,
+27479,
+7139,
+36285,
+49198,
+23736,
+35634,
+2756,
+6752,
+34463,
+40104,
+18202,
+52854,
+51611,
+8129,
+37187,
+36955,
+38705,
+34886,
+24007,
+49424,
+17634,
+31322,
+25640,
+29857,
+27765,
+35465,
+2139,
+16324,
+35120,
+12203,
+6472,
+57823,
+12551,
+591,
+47936,
+60053,
+52127,
+63930,
+49419,
+21581,
+46896,
+42920,
+11543,
+20944,
+40046,
+41902,
+24811,
+33517,
+29250,
+16341,
+44985,
+19355,
+3309,
+26196,
+31665,
+58375,
+62225,
+23318,
+14517,
+36110,
+46970,
+46148,
+28054,
+44369,
+8853,
+16574,
+34788,
+40692,
+42121,
+1574,
+50891,
+44559,
+62998,
+58127,
+52190,
+59660,
+31933,
+46729,
+10145,
+57664,
+58603,
+30411,
+59058,
+50474,
+9282,
+38781,
+26473,
+26145,
+32579,
+23643,
+3208,
+7680,
+62637,
+12706,
+59220,
+64573,
+7419,
+53464,
+37042,
+57343,
+18727,
+44685,
+30112,
+56715,
+22498,
+40761,
+28377,
+38035,
+63989,
+39230,
+56771,
+64394,
+36098,
+24982,
+64027,
+34181,
+11940,
+8755,
+35726,
+13691,
+427,
+16079,
+21003,
+15081,
+41608,
+12181,
+22451,
+38336,
+307,
+60644,
+11616,
+26798,
+62711,
+8747,
+38156,
+14906,
+50920,
+19941,
+31008,
+1882,
+51825,
+19914,
+43417,
+62421,
+57480,
+35267,
+24071,
+11874,
+50212,
+41601,
+22596,
+12649,
+64621,
+64965,
+6658,
+24025,
+62442,
+52384,
+39382,
+45920,
+12934,
+39308,
+4966,
+46881,
+17960,
+35705,
+59358,
+8095,
+51817,
+28869,
+65227,
+58751,
+21705,
+7196,
+10209,
+60037,
+36911,
+61307,
+31460,
+59874,
+27746,
+1019,
+57378,
+34810,
+37398,
+42277,
+30621,
+31220,
+30472,
+44782,
+54912,
+55267,
+64970,
+42959,
+64612,
+23478,
+16305,
+15286,
+40467,
+26575,
+37487,
+38883,
+47202,
+30446,
+58678,
+18932,
+16374,
+12400,
+59656,
+2754,
+35636,
+58364,
+46851,
+49095,
+33780,
+28190,
+5327,
+12609,
+46064,
+55626,
+5404,
+53631,
+26436,
+60052,
+48126,
+592,
+24978,
+43360,
+31017,
+3071,
+21868,
+27152,
+63624,
+46583,
+33857,
+49056,
+29775,
+62899,
+28209,
+12437,
+25319,
+52164,
+46615,
+40717,
+53349,
+38295,
+43224,
+59167,
+1030,
+9091,
+58510,
+10644,
+62786,
+30434,
+37874,
+57921,
+16112,
+17397,
+34216,
+31152,
+18297,
+21079,
+39774,
+44609,
+13389,
+44017,
+15984,
+56797,
+23834,
+47825,
+17685,
+7364,
+1140,
+5113,
+32180,
+30901,
+6883,
+13910,
+27811,
+27583,
+4261,
+939,
+13457,
+28601,
+61700,
+13310,
+7662,
+41788,
+8763,
+40240,
+21471,
+56067,
+46650,
+30736,
+47698,
+46694,
+58265,
+20732,
+35047,
+59601,
+57996,
+23530,
+41519,
+20636,
+59447,
+26134,
+64881,
+17711,
+17647,
+58174,
+48722,
+64318,
+42670,
+7880,
+13140,
+42522,
+32274,
+64874,
+100,
+27254,
+20868,
+23733,
+14219,
+12689,
+27698,
+26645,
+33955,
+43352,
+9428,
+41489,
+61499,
+24482,
+40183,
+655,
+17684,
+47891,
+23835,
+58711,
+18505,
+4835,
+28853,
+41128,
+29534,
+4612,
+57852,
+5144,
+24942,
+47193,
+45948,
+23311,
+53396,
+16558,
+31410,
+22426,
+45208,
+10005,
+61261,
+57137,
+39640,
+2509,
+26973,
+11413,
+42232,
+49899,
+18090,
+35731,
+14265,
+45559,
+57530,
+12501,
+41419,
+43407,
+29377,
+37945,
+19564,
+57559,
+51867,
+28265,
+11893,
+53796,
+37107,
+27646,
+16566,
+9325,
+31870,
+6719,
+54937,
+14606,
+6740,
+36391,
+47171,
+62494,
+51599,
+56232,
+21633,
+10425,
+34957,
+50482,
+2957,
+1707,
+12011,
+13319,
+31259,
+34453,
+41314,
+15004,
+52863,
+3652,
+1813,
+3939,
+30158,
+21336,
+60685,
+1352,
+46308,
+57518,
+45684,
+41026,
+2346,
+3296,
+43096,
+59951,
+15866,
+60850,
+16630,
+1329,
+22323,
+20507,
+63023,
+39850,
+59019,
+59645,
+43722,
+20007,
+55374,
+19497,
+64654,
+40200,
+58334,
+5470,
+34764,
+35174,
+63904,
+5101,
+43277,
+3225,
+19849,
+6350,
+49275,
+40945,
+49854,
+51726,
+40894,
+23844,
+33884,
+18990,
+20334,
+14997,
+19538,
+21793,
+25793,
+46693,
+47866,
+30737,
+7828,
+12206,
+3684,
+32067,
+54545,
+18909,
+49710,
+37979,
+56622,
+37916,
+53216,
+29615,
+12360,
+22953,
+13680,
+19493,
+43810,
+15153,
+53805,
+23437,
+38854,
+14225,
+34990,
+52062,
+29355,
+37009,
+23912,
+35369,
+23946,
+31969,
+28399,
+17246,
+24575,
+43695,
+4682,
+57673,
+52794,
+54382,
+25989,
+36093,
+30311,
+61216,
+45619,
+45818,
+8985,
+971,
+15711,
+6679,
+11313,
+19771,
+50705,
+65211,
+60900,
+47316,
+38370,
+5885,
+62721,
+14482,
+56343,
+54652,
+43036,
+55422,
+3053,
+9250,
+19242,
+28984,
+17011,
+64494,
+15033,
+11662,
+42679,
+15100,
+37168,
+63170,
+38254,
+2396,
+42544,
+11035,
+58299,
+44936,
+50035,
+21352,
+24788,
+25006,
+59146,
+25303,
+20019,
+47314,
+60902,
+18740,
+42339,
+53268,
+32668,
+1977,
+53577,
+20283,
+18550,
+3986,
+59604,
+9889,
+36931,
+19984,
+39133,
+44087,
+63280,
+16240,
+61878,
+52943,
+19392,
+19997,
+42175,
+42169,
+61704,
+668,
+10483,
+9559,
+3792,
+62497,
+18655,
+17541,
+29833,
+135,
+4338,
+726,
+44714,
+12720,
+19099,
+9859,
+39835,
+41711,
+34116,
+65052,
+26947,
+50861,
+3471,
+2738,
+41826,
+57652,
+29486,
+9867,
+62038,
+59374,
+36258,
+3728,
+9263,
+27966,
+40553,
+1512,
+16591,
+7020,
+31346,
+38083,
+1049,
+10799,
+21076,
+40784,
+14741,
+13280,
+54741,
+52892,
+9222,
+16721,
+46134,
+61238,
+15295,
+43331,
+51008,
+3461,
+17133,
+2973,
+4210,
+21027,
+45046,
+28330,
+20707,
+24791,
+55432,
+12967,
+51716,
+16537,
+27388,
+12135,
+15127,
+45753,
+19732,
+49769,
+22351,
+18769,
+64501,
+62182,
+57468,
+65010,
+50462,
+5353,
+49319,
+41929,
+28937,
+38916,
+8832,
+50595,
+61156,
+53074,
+2499,
+25508,
+6018,
+13557,
+4917,
+10222,
+34978,
+54924,
+57034,
+47323,
+37388,
+6015,
+34872,
+5949,
+17327,
+41094,
+31032,
+35951,
+64169,
+19082,
+50585,
+5511,
+24078,
+7034,
+57368,
+56030,
+7109,
+25377,
+56688,
+54509,
+43203,
+29273,
+20040,
+46303,
+63456,
+44333,
+26209,
+30039,
+64301,
+26375,
+13580,
+10767,
+63986,
+9599,
+64671,
+58113,
+15637,
+17741,
+29807,
+1938,
+54587,
+9602,
+45262,
+26188,
+65030,
+22926,
+60750,
+9874,
+47108,
+2871,
+22478,
+7555,
+5075,
+36475,
+10395,
+24694,
+46823,
+27637,
+48638,
+38207,
+31399,
+58594,
+41757,
+6566,
+3943,
+34049,
+7263,
+58618,
+33660,
+28125,
+10158,
+64680,
+57499,
+1110,
+38226,
+44941,
+35966,
+34547,
+42078,
+29389,
+28885,
+39281,
+53474,
+49113,
+56236,
+45292,
+64706,
+25821,
+60566,
+51257,
+32680,
+1499,
+20667,
+372,
+14760,
+58412,
+17250,
+8583,
+43985,
+7044,
+21611,
+30292,
+51917,
+31386,
+3891,
+8572,
+39577,
+2768,
+27314,
+14488,
+26878,
+11802,
+55821,
+10437,
+43820,
+24093,
+3975,
+1468,
+3347,
+48958,
+11248,
+30576,
+61127,
+59072,
+53848,
+3270,
+26328,
+32253,
+12739,
+8813,
+44250,
+3514,
+59814,
+27226,
+16641,
+36361,
+26400,
+9645,
+51209,
+16885,
+13977,
+44144,
+52055,
+36734,
+59879,
+25998,
+38813,
+6939,
+25432,
+17387,
+12684,
+55453,
+13111,
+34194,
+19182,
+34435,
+24683,
+11038,
+3480,
+21012,
+37387,
+47485,
+57035,
+20662,
+27579,
+23034,
+63242,
+38369,
+47643,
+60901,
+47609,
+20020,
+60330,
+2909,
+24842,
+43170,
+5249,
+15268,
+54314,
+20155,
+49700,
+3109,
+62651,
+58087,
+55385,
+44749,
+4355,
+62544,
+56404,
+15725,
+6373,
+42270,
+52972,
+11555,
+53997,
+49300,
+34372,
+15359,
+56804,
+52291,
+16290,
+6502,
+15642,
+32449,
+40743,
+40730,
+40315,
+42879,
+24261,
+32991,
+61726,
+7862,
+12745,
+23648,
+8711,
+64041,
+53132,
+15952,
+5054,
+63697,
+6879,
+61280,
+33986,
+34020,
+38486,
+21266,
+49116,
+47206,
+11074,
+31944,
+21210,
+50565,
+38143,
+21995,
+23542,
+64444,
+50114,
+63018,
+63638,
+21651,
+63846,
+14233,
+9518,
+34384,
+17637,
+44459,
+24755,
+29463,
+9719,
+1572,
+42123,
+52701,
+55770,
+61223,
+25453,
+54150,
+55586,
+34100,
+10723,
+45377,
+25892,
+61139,
+36846,
+16327,
+16895,
+57598,
+40877,
+54830,
+9018,
+53243,
+51069,
+31227,
+13955,
+29271,
+43205,
+30834,
+60996,
+11073,
+47257,
+49117,
+23104,
+30445,
+47958,
+38884,
+19818,
+38601,
+54691,
+33401,
+8122,
+30103,
+45947,
+47813,
+24943,
+63733,
+31988,
+57761,
+40478,
+14561,
+28674,
+4796,
+24825,
+25927,
+26554,
+7437,
+16187,
+62449,
+2124,
+22299,
+27841,
+15765,
+13066,
+30174,
+62493,
+47770,
+36392,
+43840,
+57802,
+31634,
+64902,
+38765,
+25358,
+42028,
+61175,
+31695,
+34343,
+10338,
+12723,
+28479,
+43891,
+41261,
+58389,
+17222,
+6584,
+60094,
+20534,
+23819,
+59906,
+40010,
+32446,
+21405,
+25391,
+62113,
+63834,
+3274,
+61996,
+63446,
+57291,
+37495,
+2226,
+21778,
+59312,
+37729,
+6381,
+22521,
+324,
+4639,
+2727,
+33274,
+19151,
+43422,
+43231,
+38762,
+10666,
+21007,
+54011,
+1434,
+28682,
+16676,
+6717,
+31872,
+36630,
+60838,
+32909,
+24780,
+33350,
+2870,
+47436,
+9875,
+9590,
+29124,
+17098,
+64346,
+44445,
+551,
+39062,
+39394,
+34750,
+25472,
+9631,
+6322,
+21591,
+41769,
+38807,
+57177,
+46196,
+4430,
+7840,
+27625,
+64990,
+34690,
+23625,
+10750,
+10998,
+8462,
+63300,
+53433,
+32117,
+18841,
+7274,
+62367,
+11334,
+13172,
+45757,
+25571,
+22003,
+30217,
+35641,
+11131,
+61517,
+29288,
+32341,
+53706,
+20759,
+41271,
+56698,
+53666,
+61767,
+63434,
+6642,
+31265,
+25278,
+27482,
+8197,
+55777,
+46486,
+27788,
+59543,
+22922,
+4604,
+64371,
+31335,
+65172,
+19266,
+450,
+7848,
+64010,
+20229,
+8952,
+7570,
+23148,
+33507,
+6825,
+17715,
+60703,
+15514,
+33891,
+21730,
+58870,
+4553,
+48427,
+50821,
+40450,
+23422,
+40927,
+20089,
+53543,
+9187,
+2148,
+34950,
+54968,
+48515,
+30904,
+58557,
+41761,
+49969,
+18786,
+28777,
+7063,
+26804,
+534,
+65293,
+2698,
+6460,
+21069,
+15998,
+9570,
+65134,
+11902,
+39974,
+52788,
+11635,
+28213,
+26600,
+5601,
+54272,
+11911,
+45253,
+17937,
+61552,
+16303,
+23480,
+10247,
+45694,
+4602,
+22924,
+65032,
+53965,
+32087,
+52681,
+7531,
+12957,
+41499,
+12592,
+46147,
+48100,
+36111,
+29795,
+51692,
+4503,
+31911,
+4439,
+28238,
+5123,
+25850,
+23492,
+57568,
+8152,
+30571,
+56751,
+20382,
+65075,
+35826,
+24568,
+44433,
+44212,
+46861,
+37558,
+54915,
+45743,
+40943,
+49277,
+57166,
+6633,
+60233,
+16549,
+5240,
+1339,
+55414,
+63874,
+23700,
+41412,
+24557,
+28980,
+10634,
+54495,
+10816,
+20849,
+56652,
+59452,
+25800,
+59678,
+45859,
+20198,
+52550,
+48263,
+59141,
+41834,
+7768,
+50843,
+50154,
+27759,
+22743,
+5813,
+15816,
+28798,
+28902,
+41217,
+36223,
+13666,
+16233,
+56160,
+40703,
+18963,
+30029,
+31543,
+38697,
+49180,
+42919,
+48120,
+21582,
+6405,
+26979,
+11255,
+11279,
+22968,
+24485,
+10244,
+8880,
+52100,
+16834,
+3127,
+3039,
+17959,
+47997,
+4967,
+11623,
+11373,
+11404,
+52195,
+28449,
+56607,
+30307,
+26903,
+52016,
+1780,
+62079,
+39054,
+44886,
+48327,
+12576,
+10652,
+2025,
+37557,
+46949,
+44213,
+50951,
+40833,
+54475,
+8539,
+2403,
+57932,
+48809,
+49094,
+47948,
+58365,
+13463,
+53004,
+64194,
+20463,
+40177,
+23511,
+36106,
+52994,
+62617,
+54110,
+12201,
+35122,
+6403,
+21584,
+29082,
+12422,
+61497,
+41491,
+29293,
+31982,
+46098,
+41920,
+19535,
+39187,
+8010,
+27636,
+47428,
+24695,
+24312,
+30981,
+31403,
+14386,
+55899,
+26700,
+36045,
+25206,
+54613,
+7,
+8193,
+17477,
+20853,
+27119,
+45512,
+50296,
+51119,
+45991,
+58369,
+14769,
+61336,
+30070,
+63400,
+4634,
+17930,
+23199,
+26761,
+12385,
+53310,
+49269,
+34097,
+16490,
+61087,
+13185,
+27183,
+45482,
+20351,
+37925,
+23452,
+27333,
+8117,
+62333,
+11582,
+52649,
+19448,
+58802,
+57723,
+62837,
+59079,
+31719,
+5689,
+20883,
+43185,
+18028,
+1037,
+38957,
+27605,
+62231,
+6287,
+28194,
+56933,
+1616,
+61485,
+37178,
+20544,
+53697,
+14582,
+6441,
+6807,
+15567,
+18528,
+36761,
+10824,
+61742,
+13941,
+11607,
+8227,
+33112,
+57548,
+11479,
+60931,
+1168,
+64641,
+44449,
+25542,
+8976,
+64202,
+1917,
+38920,
+8528,
+59191,
+10144,
+48083,
+31934,
+42775,
+55538,
+26752,
+41744,
+7667,
+44453,
+45917,
+33937,
+2915,
+48929,
+14859,
+42750,
+20143,
+33007,
+54866,
+5142,
+57854,
+32906,
+43298,
+6293,
+7925,
+44544,
+38361,
+49867,
+44349,
+32471,
+15584,
+43747,
+49732,
+24115,
+33419,
+18460,
+58264,
+47865,
+47699,
+25794,
+51394,
+33650,
+28815,
+6335,
+22519,
+6383,
+64727,
+8183,
+11092,
+63163,
+41621,
+8077,
+44793,
+58069,
+14202,
+6258,
+60690,
+30227,
+12448,
+52777,
+1393,
+27598,
+40094,
+55989,
+58893,
+34274,
+57838,
+316,
+53547,
+27437,
+51104,
+14092,
+23048,
+28122,
+15992,
+3058,
+41050,
+55295,
+55830,
+43438,
+30735,
+47868,
+56068,
+28291,
+50400,
+31726,
+34673,
+36527,
+61607,
+2036,
+54213,
+7016,
+44178,
+7258,
+6682,
+56820,
+51214,
+31113,
+34859,
+52440,
+4600,
+45696,
+10991,
+17846,
+15828,
+33699,
+61985,
+59466,
+33115,
+56674,
+28850,
+55743,
+58993,
+53288,
+25380,
+40716,
+47918,
+52165,
+3432,
+59830,
+11590,
+41638,
+29358,
+40588,
+13007,
+6858,
+17582,
+49164,
+54838,
+39741,
+14145,
+35262,
+56093,
+61471,
+56703,
+14944,
+10736,
+1588,
+61991,
+33130,
+8679,
+40418,
+21524,
+32497,
+29317,
+16022,
+23041,
+33856,
+47927,
+63625,
+28637,
+32829,
+35700,
+51302,
+56021,
+64863,
+24833,
+18635,
+35509,
+26823,
+27993,
+14654,
+22911,
+14649,
+2788,
+50969,
+6781,
+26411,
+25591,
+22457,
+34036,
+30225,
+60692,
+29103,
+2165,
+1060,
+32368,
+6891,
+37971,
+51753,
+27570,
+36215,
+53191,
+31652,
+17832,
+45204,
+54988,
+7053,
+45535,
+59368,
+20476,
+44517,
+63926,
+7206,
+25963,
+62350,
+34086,
+9122,
+42428,
+6283,
+2431,
+55217,
+58031,
+24251,
+45738,
+31836,
+3554,
+50753,
+56503,
+14069,
+61453,
+8781,
+3098,
+50227,
+62460,
+50773,
+48176,
+12561,
+26509,
+10169,
+33981,
+16392,
+50558,
+30783,
+32431,
+49665,
+56057,
+5864,
+58420,
+3381,
+41916,
+30264,
+15577,
+5752,
+19162,
+2843,
+63441,
+44172,
+48258,
+28104,
+12157,
+15229,
+24714,
+1242,
+27787,
+47050,
+55778,
+5421,
+58056,
+48777,
+7040,
+22945,
+815,
+55338,
+59028,
+57794,
+43571,
+5676,
+64626,
+7707,
+22422,
+59900,
+5757,
+55852,
+62743,
+29942,
+21549,
+44776,
+24268,
+33076,
+6799,
+64314,
+35022,
+57791,
+33823,
+1367,
+18062,
+14953,
+6439,
+14584,
+24706,
+879,
+44164,
+49442,
+49563,
+40120,
+16295,
+39546,
+29446,
+3010,
+23334,
+55197,
+52816,
+17967,
+55679,
+11151,
+28027,
+33796,
+2741,
+34825,
+6706,
+343,
+45915,
+44455,
+15971,
+28974,
+5538,
+36254,
+19945,
+24,
+60295,
+64200,
+8978,
+6081,
+30873,
+3616,
+7756,
+28524,
+63121,
+26650,
+52677,
+35830,
+26173,
+7390,
+44115,
+2213,
+10095,
+49342,
+31693,
+61177,
+13367,
+13380,
+24769,
+23398,
+9269,
+41734,
+202,
+6135,
+51621,
+42540,
+14339,
+24374,
+44628,
+49975,
+32262,
+18411,
+22116,
+3302,
+21053,
+33280,
+54093,
+32801,
+53800,
+65041,
+52563,
+59784,
+19040,
+62695,
+50336,
+48839,
+2568,
+8343,
+35008,
+62101,
+8434,
+41320,
+10867,
+59562,
+64057,
+62126,
+52027,
+28037,
+60599,
+36201,
+41984,
+31342,
+18207,
+31103,
+4588,
+35790,
+6842,
+21454,
+59331,
+60595,
+58150,
+16088,
+61709,
+43738,
+13750,
+34969,
+775,
+44002,
+9043,
+14849,
+33202,
+63728,
+38243,
+61660,
+9047,
+13162,
+10138,
+52216,
+34280,
+28079,
+28272,
+40886,
+36497,
+48352,
+2316,
+28060,
+31701,
+5593,
+12853,
+12467,
+21915,
+35099,
+61940,
+49336,
+51488,
+10745,
+2410,
+40068,
+57517,
+47746,
+1353,
+57842,
+17335,
+63455,
+47461,
+20041,
+36615,
+38463,
+24102,
+16753,
+13649,
+56286,
+17905,
+36554,
+51380,
+126,
+26319,
+9844,
+13834,
+21024,
+13710,
+45625,
+49357,
+24747,
+9643,
+26402,
+21452,
+6844,
+5918,
+19754,
+20032,
+61573,
+41989,
+22356,
+29608,
+364,
+18495,
+65088,
+27942,
+585,
+4139,
+840,
+15041,
+7447,
+46118,
+6552,
+56177,
+62807,
+39962,
+28249,
+51556,
+13535,
+30063,
+65017,
+53163,
+32359,
+9627,
+22634,
+19568,
+58351,
+9095,
+31768,
+39933,
+8635,
+48577,
+29591,
+4344,
+65357,
+25630,
+8398,
+25899,
+21694,
+146,
+20955,
+36907,
+60163,
+60720,
+52896,
+51264,
+22038,
+65425,
+50187,
+17046,
+13499,
+16861,
+16531,
+61435,
+28207,
+62901,
+9502,
+60676,
+62396,
+3904,
+6261,
+9954,
+65459,
+19577,
+15222,
+30378,
+23602,
+28978,
+24559,
+53031,
+3156,
+1122,
+38868,
+28142,
+27056,
+39594,
+5046,
+4429,
+47090,
+57178,
+24991,
+58324,
+24382,
+51685,
+62136,
+2449,
+32716,
+24816,
+17330,
+57162,
+61304,
+20551,
+17428,
+62229,
+27607,
+4865,
+64474,
+36719,
+21620,
+8655,
+7277,
+33706,
+44798,
+1066,
+14128,
+31371,
+59883,
+23234,
+54264,
+2879,
+62648,
+15591,
+45333,
+40044,
+20946,
+34933,
+63934,
+28024,
+36828,
+1268,
+14823,
+40325,
+48589,
+45216,
+5571,
+28053,
+48099,
+46971,
+12593,
+17949,
+45541,
+13446,
+15787,
+33745,
+22830,
+20376,
+54635,
+58534,
+33537,
+61237,
+47534,
+16722,
+40484,
+13539,
+51661,
+11937,
+7253,
+62927,
+38861,
+54487,
+61469,
+56095,
+16564,
+27648,
+63765,
+6551,
+46263,
+7448,
+10154,
+3344,
+54527,
+2156,
+36613,
+20043,
+35722,
+7310,
+60285,
+5218,
+22102,
+23686,
+53757,
+38670,
+4087,
+50805,
+16026,
+41919,
+46829,
+31983,
+56098,
+36672,
+39815,
+60707,
+49130,
+30133,
+12887,
+18760,
+6736,
+6830,
+32285,
+52906,
+49696,
+30316,
+56061,
+58635,
+40780,
+36076,
+48869,
+37608,
+30918,
+61587,
+55149,
+266,
+28474,
+13403,
+64389,
+11200,
+56428,
+41325,
+13790,
+55625,
+47942,
+12610,
+36519,
+11330,
+14987,
+63461,
+10329,
+54391,
+30385,
+13466,
+30947,
+37,
+60364,
+42818,
+6437,
+14955,
+32064,
+16164,
+14872,
+51856,
+17856,
+21728,
+33893,
+10162,
+17812,
+884,
+8369,
+58906,
+11339,
+10710,
+38261,
+31663,
+26198,
+49776,
+8846,
+1327,
+16632,
+12318,
+32703,
+39120,
+59740,
+27489,
+628,
+2710,
+50574,
+56011,
+55575,
+56294,
+54965,
+4099,
+39531,
+41110,
+64285,
+17027,
+37338,
+3299,
+51100,
+40292,
+36874,
+30611,
+50905,
+23616,
+18710,
+26351,
+50539,
+17835,
+43745,
+15586,
+39726,
+53266,
+42341,
+16790,
+58368,
+46804,
+51120,
+4288,
+31570,
+54565,
+41727,
+14477,
+60020,
+15910,
+61522,
+63791,
+56975,
+25363,
+39733,
+39024,
+25272,
+49533,
+27591,
+17751,
+7979,
+25567,
+42902,
+19288,
+7187,
+59566,
+34987,
+28392,
+48725,
+5568,
+62949,
+77,
+19326,
+50316,
+61865,
+1697,
+29373,
+62140,
+50852,
+425,
+13693,
+29496,
+41038,
+23310,
+47812,
+47194,
+30104,
+35187,
+26465,
+49519,
+13385,
+36745,
+52859,
+30120,
+12242,
+20487,
+23445,
+22511,
+5640,
+64504,
+6542,
+12251,
+50031,
+52084,
+27045,
+1026,
+44607,
+39776,
+45508,
+1948,
+34972,
+12933,
+48001,
+39383,
+33936,
+46721,
+44454,
+46429,
+344,
+10069,
+63540,
+6535,
+40360,
+63557,
+39608,
+38985,
+56610,
+59231,
+38949,
+14510,
+40427,
+10183,
+43510,
+43975,
+6524,
+7765,
+24128,
+27450,
+22087,
+3066,
+407,
+45138,
+23240,
+32018,
+36289,
+22436,
+10382,
+15844,
+50164,
+35076,
+20413,
+6425,
+11189,
+50738,
+24966,
+5957,
+65527,
+7283,
+37943,
+29379,
+6722,
+26750,
+55540,
+20896,
+34013,
+63328,
+6620,
+13829,
+53421,
+64521,
+24989,
+57180,
+20197,
+46923,
+59679,
+62763,
+9880,
+27281,
+63143,
+19747,
+56488,
+52377,
+65284,
+42392,
+23490,
+25852,
+35070,
+63204,
+56292,
+55577,
+29772,
+30609,
+36876,
+49730,
+43749,
+7076,
+64223,
+53359,
+45829,
+6569,
+30156,
+3941,
+6568,
+45834,
+53360,
+11794,
+64749,
+34893,
+7324,
+5575,
+56053,
+55987,
+40096,
+8984,
+47653,
+45620,
+61270,
+1399,
+40868,
+38447,
+39794,
+11007,
+13724,
+26254,
+12065,
+39950,
+19783,
+18025,
+15769,
+8990,
+12327,
+1750,
+43605,
+64770,
+20107,
+52413,
+34450,
+16283,
+34649,
+14230,
+24137,
+2393,
+19684,
+9050,
+59796,
+9215,
+7290,
+27974,
+59618,
+19259,
+45506,
+39778,
+8064,
+34155,
+11715,
+42609,
+4789,
+51897,
+44506,
+63690,
+57337,
+48856,
+43877,
+11587,
+12087,
+54223,
+17890,
+14833,
+38186,
+29779,
+57424,
+2966,
+57564,
+8970,
+25570,
+47072,
+13173,
+25586,
+19731,
+47513,
+15128,
+52629,
+27537,
+48391,
+60003,
+62944,
+10494,
+34646,
+40942,
+46946,
+54916,
+42995,
+38676,
+31835,
+46527,
+24252,
+609,
+33629,
+41613,
+39258,
+28829,
+50928,
+4447,
+32077,
+31417,
+49912,
+50366,
+14115,
+2977,
+49935,
+21505,
+52675,
+26652,
+18337,
+26937,
+35470,
+41724,
+56739,
+25219,
+55801,
+17114,
+39664,
+45311,
+37029,
+53827,
+44739,
+63184,
+39652,
+62487,
+19862,
+26456,
+7794,
+59762,
+1556,
+4126,
+10990,
+46630,
+4601,
+46982,
+10248,
+3243,
+63942,
+51929,
+62311,
+17823,
+59188,
+13701,
+41025,
+47744,
+57519,
+43973,
+43512,
+736,
+29257,
+2848,
+6557,
+3049,
+19834,
+39082,
+20769,
+24717,
+14809,
+53028,
+53817,
+53369,
+21396,
+37249,
+29863,
+7763,
+6526,
+42956,
+43732,
+23070,
+31575,
+16873,
+12814,
+26942,
+65448,
+17130,
+11849,
+28334,
+31938,
+35278,
+39224,
+39512,
+25625,
+8438,
+49245,
+59037,
+53437,
+50967,
+2790,
+39450,
+8244,
+28044,
+49818,
+21445,
+23614,
+50907,
+59844,
+49361,
+62406,
+57728,
+9662,
+26384,
+32808,
+49356,
+46286,
+13711,
+17518,
+22121,
+61269,
+45817,
+47654,
+61217,
+55690,
+60872,
+60915,
+44392,
+23009,
+35612,
+28006,
+48629,
+57483,
+33270,
+16695,
+947,
+59247,
+17258,
+6388,
+40509,
+2837,
+27412,
+54555,
+5259,
+14616,
+17514,
+34530,
+55238,
+29282,
+19114,
+65514,
+2980,
+59712,
+62646,
+2881,
+61513,
+51186,
+16794,
+65179,
+60765,
+25757,
+57475,
+52668,
+220,
+53083,
+58107,
+33215,
+42179,
+65061,
+55466,
+1999,
+48269,
+31925,
+11047,
+30806,
+22036,
+51266,
+27589,
+49535,
+39460,
+26348,
+57529,
+47793,
+14266,
+1007,
+38556,
+54284,
+28605,
+17942,
+26405,
+58477,
+37082,
+51343,
+4577,
+15104,
+64206,
+43216,
+2488,
+22935,
+13445,
+46144,
+17950,
+23363,
+63869,
+21388,
+59367,
+46543,
+7054,
+50935,
+36780,
+21814,
+17810,
+10164,
+20684,
+8866,
+16801,
+34907,
+9405,
+12034,
+5000,
+58409,
+51763,
+40233,
+17927,
+18224,
+5208,
+38752,
+34842,
+50295,
+46807,
+27120,
+17141,
+1947,
+45924,
+39777,
+45782,
+19260,
+3965,
+38454,
+28220,
+38581,
+49654,
+44497,
+36263,
+18075,
+19235,
+28923,
+19426,
+29816,
+44488,
+6300,
+37736,
+39434,
+2906,
+31631,
+56225,
+34772,
+13880,
+20350,
+46786,
+27184,
+58783,
+31076,
+57944,
+38182,
+16880,
+61679,
+30722,
+3237,
+21678,
+10618,
+64100,
+20584,
+40810,
+12758,
+64146,
+2217,
+28751,
+64045,
+61626,
+10678,
+51024,
+15893,
+41041,
+29397,
+58295,
+35797,
+4598,
+52442,
+36193,
+33023,
+26421,
+24609,
+18517,
+11995,
+16571,
+64229,
+32594,
+31353,
+58287,
+10850,
+2586,
+65462,
+9743,
+35628,
+62319,
+877,
+24708,
+62326,
+54656,
+766,
+19739,
+13125,
+30303,
+54675,
+58472,
+38735,
+17084,
+492,
+10237,
+27546,
+29685,
+50060,
+63247,
+35332,
+65423,
+22040,
+12415,
+64192,
+53006,
+49988,
+21494,
+42924,
+54962,
+62532,
+12371,
+33527,
+12513,
+39871,
+31351,
+32596,
+38215,
+9812,
+52468,
+999,
+31000,
+22786,
+18822,
+32531,
+64291,
+39060,
+553,
+59978,
+42863,
+61234,
+27986,
+14098,
+58942,
+11222,
+18232,
+15838,
+49612,
+45318,
+25891,
+47225,
+10724,
+35889,
+33898,
+53248,
+43687,
+6555,
+2850,
+56435,
+34775,
+39111,
+874,
+26066,
+38933,
+575,
+51088,
+61324,
+51830,
+39875,
+5492,
+39254,
+11509,
+5712,
+41138,
+21219,
+31111,
+51216,
+2779,
+64430,
+42843,
+9566,
+2152,
+8098,
+48472,
+33119,
+8298,
+8356,
+19677,
+18615,
+12988,
+22474,
+44537,
+62932,
+40043,
+46162,
+15592,
+34747,
+40674,
+6483,
+37894,
+23760,
+41415,
+12396,
+36515,
+45173,
+30692,
+20952,
+60668,
+25890,
+45379,
+49613,
+9130,
+65093,
+53407,
+7463,
+37028,
+45710,
+39665,
+1526,
+18499,
+56586,
+43768,
+54287,
+34963,
+12389,
+28686,
+49224,
+15875,
+45100,
+36698,
+57918,
+4724,
+38810,
+54128,
+64705,
+47399,
+56237,
+10481,
+670,
+24614,
+54297,
+36207,
+28992,
+64618,
+52759,
+59865,
+49486,
+10932,
+51571,
+32204,
+48674,
+49008,
+15160,
+61051,
+39557,
+52753,
+49578,
+42552,
+51164,
+23125,
+64719,
+4814,
+50947,
+26851,
+26187,
+47442,
+9603,
+62515,
+6661,
+21181,
+1186,
+37711,
+11959,
+17936,
+46988,
+11912,
+35941,
+48480,
+42172,
+1670,
+41496,
+13869,
+3937,
+1815,
+37858,
+1282,
+63648,
+28847,
+10290,
+8178,
+53679,
+9320,
+22374,
+8709,
+23650,
+59766,
+29044,
+38547,
+42479,
+22021,
+31097,
+10064,
+6011,
+5288,
+51474,
+63753,
+41830,
+16077,
+429,
+62947,
+5570,
+46151,
+48590,
+22080,
+9748,
+63770,
+23863,
+21908,
+10004,
+47806,
+22427,
+12043,
+54987,
+46546,
+17833,
+50541,
+52282,
+40414,
+42281,
+32754,
+48547,
+59686,
+21589,
+6324,
+63704,
+24005,
+34888,
+20801,
+5820,
+51656,
+20054,
+18752,
+27613,
+15411,
+65083,
+50926,
+28831,
+30626,
+37842,
+24378,
+34658,
+65434,
+16064,
+30691,
+45323,
+36516,
+9818,
+43125,
+11014,
+10193,
+33901,
+637,
+49616,
+29505,
+50010,
+42706,
+19398,
+10903,
+1161,
+283,
+20539,
+8785,
+18582,
+463,
+27261,
+44823,
+42953,
+64264,
+48587,
+40327,
+38914,
+28939,
+35992,
+20407,
+10820,
+27031,
+62282,
+50050,
+23239,
+45891,
+408,
+40541,
+63131,
+43656,
+4455,
+40138,
+29544,
+20393,
+54718,
+16255,
+25807,
+2482,
+30460,
+19655,
+59113,
+1756,
+29170,
+22126,
+41544,
+57253,
+35519,
+28107,
+20792,
+59748,
+16657,
+2814,
+51057,
+29119,
+53009,
+33447,
+31588,
+7891,
+31200,
+54542,
+39446,
+3887,
+36697,
+45299,
+15876,
+21416,
+16054,
+43344,
+63078,
+38422,
+9024,
+21227,
+15280,
+35658,
+16099,
+32049,
+25404,
+5310,
+37181,
+9898,
+1868,
+13431,
+41108,
+39533,
+10831,
+64576,
+8671,
+43055,
+1342,
+18691,
+25361,
+56977,
+7727,
+44922,
+16301,
+61554,
+32967,
+39586,
+32522,
+31368,
+42826,
+6168,
+18996,
+21021,
+15941,
+53053,
+39319,
+29791,
+60683,
+21338,
+23327,
+6117,
+53294,
+19600,
+42093,
+26875,
+28329,
+47524,
+21028,
+32930,
+9277,
+56334,
+9922,
+3191,
+63916,
+56112,
+57631,
+3168,
+54205,
+50552,
+56891,
+31856,
+43213,
+17804,
+2515,
+49831,
+35459,
+54325,
+38133,
+35588,
+63470,
+55736,
+40975,
+11723,
+61407,
+39174,
+33749,
+6745,
+61312,
+31761,
+5438,
+37764,
+28241,
+14827,
+57463,
+58386,
+11706,
+36607,
+6419,
+5442,
+18387,
+41719,
+59432,
+5058,
+65253,
+20380,
+56753,
+24028,
+60225,
+28358,
+18780,
+11841,
+15901,
+36116,
+31649,
+28624,
+4232,
+19354,
+48110,
+16342,
+40912,
+39048,
+27492,
+24089,
+14183,
+4737,
+38911,
+3452,
+62729,
+57413,
+35436,
+34679,
+65298,
+25610,
+53859,
+40366,
+65400,
+61201,
+1921,
+42999,
+28156,
+32098,
+37199,
+37831,
+51113,
+3777,
+10149,
+23717,
+28680,
+1436,
+20068,
+24728,
+34997,
+22268,
+60326,
+55437,
+37231,
+39263,
+3154,
+53033,
+35849,
+35965,
+47409,
+38227,
+22797,
+35029,
+50034,
+47617,
+58300,
+11585,
+43879,
+2201,
+31579,
+194,
+7731,
+58630,
+34174,
+33317,
+61960,
+49392,
+16300,
+45070,
+7728,
+48486,
+36621,
+27408,
+32314,
+49786,
+60449,
+23281,
+52823,
+12796,
+53841,
+12817,
+5436,
+31763,
+43290,
+14298,
+65483,
+51936,
+613,
+24017,
+17680,
+6091,
+29015,
+48501,
+23265,
+12567,
+1216,
+27519,
+31705,
+20942,
+11545,
+48606,
+50197,
+27730,
+48326,
+46867,
+39055,
+31120,
+60844,
+41667,
+33806,
+35562,
+42508,
+4768,
+38342,
+8501,
+18216,
+37959,
+31687,
+55712,
+64037,
+18145,
+65166,
+54379,
+11056,
+44615,
+59041,
+50019,
+48365,
+2146,
+9189,
+8580,
+59176,
+35933,
+23220,
+6021,
+44436,
+55372,
+20009,
+56672,
+33117,
+48474,
+59857,
+21512,
+39205,
+62718,
+11513,
+56004,
+63716,
+3854,
+32778,
+31209,
+21300,
+39989,
+26047,
+62159,
+25424,
+13593,
+54898,
+15532,
+37206,
+18015,
+40747,
+40533,
+44262,
+27859,
+58041,
+42952,
+45152,
+27262,
+6691,
+33504,
+11391,
+44189,
+54805,
+14976,
+29201,
+14380,
+24423,
+55108,
+60152,
+3105,
+16438,
+14066,
+11024,
+7271,
+17674,
+30491,
+37023,
+8556,
+25888,
+60670,
+1065,
+46172,
+33707,
+48866,
+61123,
+58068,
+46679,
+8078,
+6953,
+12149,
+40580,
+39621,
+40355,
+12832,
+40269,
+17094,
+54911,
+47971,
+30473,
+59497,
+11830,
+18475,
+24267,
+46464,
+21550,
+58495,
+42594,
+30747,
+24847,
+26632,
+26454,
+19864,
+40821,
+64297,
+40836,
+60303,
+32901,
+56947,
+23037,
+61837,
+30794,
+57734,
+65496,
+11470,
+4046,
+53182,
+40207,
+34375,
+21935,
+4354,
+47299,
+55386,
+41822,
+37429,
+20422,
+5790,
+28022,
+63936,
+5203,
+63183,
+45707,
+53828,
+30648,
+20347,
+59718,
+9114,
+35002,
+62913,
+63814,
+52560,
+400,
+56876,
+4746,
+49385,
+5199,
+8483,
+52599,
+34231,
+16973,
+49758,
+27486,
+37854,
+57060,
+34602,
+12719,
+47572,
+727,
+23137,
+57610,
+20995,
+11828,
+59499,
+14841,
+55095,
+54158,
+9384,
+63464,
+23755,
+39465,
+24885,
+36725,
+59005,
+13859,
+2805,
+4462,
+12615,
+60390,
+14736,
+2229,
+16253,
+54720,
+18609,
+8886,
+30111,
+48059,
+18728,
+38419,
+51402,
+62844,
+6209,
+22406,
+28761,
+12212,
+2507,
+39642,
+21942,
+36585,
+33615,
+14539,
+38282,
+17697,
+41805,
+25458,
+50901,
+37626,
+41932,
+9083,
+17352,
+10859,
+5390,
+13884,
+52407,
+13958,
+31138,
+59847,
+18767,
+22353,
+53462,
+7421,
+16840,
+17291,
+10253,
+60508,
+23867,
+33261,
+665,
+35328,
+9996,
+19668,
+31525,
+8414,
+58591,
+5729,
+32861,
+30141,
+14623,
+39299,
+54320,
+62073,
+53691,
+49974,
+46389,
+24375,
+41580,
+63478,
+35954,
+29052,
+40632,
+21830,
+37238,
+17285,
+62213,
+65183,
+59040,
+44866,
+11057,
+27234,
+41975,
+44206,
+13388,
+47897,
+39775,
+45926,
+1027,
+8225,
+11609,
+24178,
+50736,
+11191,
+13920,
+31529,
+56357,
+59362,
+33831,
+20645,
+53993,
+17420,
+25492,
+19377,
+30605,
+62568,
+30186,
+50293,
+34844,
+20216,
+4870,
+65390,
+19303,
+28631,
+57941,
+38072,
+33483,
+37418,
+36275,
+42892,
+16310,
+32434,
+7665,
+41746,
+57954,
+60758,
+22153,
+52714,
+62415,
+49159,
+40679,
+8496,
+52264,
+14494,
+62997,
+48089,
+50892,
+36945,
+16599,
+64247,
+58725,
+32553,
+40637,
+1296,
+11067,
+42664,
+49191,
+15446,
+2326,
+38360,
+46706,
+7926,
+20827,
+53710,
+63349,
+43992,
+62931,
+45336,
+22475,
+49457,
+17499,
+50457,
+35575,
+54870,
+18416,
+11527,
+15379,
+2954,
+62056,
+18212,
+28151,
+36748,
+62986,
+3265,
+41679,
+25840,
+63925,
+46540,
+20477,
+49597,
+51702,
+19249,
+51828,
+61326,
+18012,
+20492,
+32608,
+63689,
+45774,
+51898,
+26532,
+5356,
+34574,
+21369,
+41197,
+15255,
+36262,
+45499,
+49655,
+54664,
+10315,
+109,
+25620,
+26335,
+11789,
+6299,
+45492,
+29817,
+7120,
+62234,
+19154,
+32258,
+35117,
+36849,
+16315,
+15365,
+9414,
+49688,
+62391,
+25674,
+33243,
+57544,
+35287,
+4371,
+4573,
+59076,
+18536,
+49540,
+423,
+50854,
+43795,
+13985,
+54737,
+17995,
+24754,
+47239,
+17638,
+25764,
+15970,
+46428,
+45916,
+46722,
+7668,
+40576,
+25541,
+46738,
+64642,
+954,
+550,
+47102,
+64347,
+30923,
+60386,
+3526,
+25532,
+56041,
+35665,
+55371,
+44855,
+6022,
+44211,
+46951,
+24569,
+60398,
+61367,
+8144,
+29032,
+58915,
+51710,
+3722,
+30085,
+16541,
+12049,
+35478,
+27432,
+27890,
+49273,
+6352,
+30598,
+64088,
+57889,
+8597,
+9003,
+58616,
+7265,
+24291,
+50875,
+27564,
+13786,
+49742,
+40861,
+28824,
+36488,
+24257,
+61807,
+56276,
+4297,
+50244,
+31,
+2049,
+13524,
+23008,
+45614,
+60916,
+57126,
+64034,
+24741,
+22704,
+42989,
+54231,
+1230,
+34553,
+59812,
+3516,
+29406,
+12040,
+6331,
+4110,
+1372,
+50359,
+96,
+24324,
+48559,
+12246,
+8852,
+48097,
+28055,
+24540,
+31959,
+8107,
+2046,
+63553,
+54169,
+33372,
+34206,
+51893,
+13730,
+58875,
+20279,
+64352,
+8802,
+59396,
+50029,
+12253,
+32470,
+46703,
+49868,
+733,
+29198,
+34757,
+14627,
+1475,
+15142,
+39376,
+51426,
+40071,
+13479,
+23887,
+63741,
+3360,
+26208,
+47459,
+63457,
+24999,
+61535,
+52827,
+33592,
+18283,
+14973,
+4137,
+587,
+38008,
+25044,
+60888,
+32699,
+24398,
+20184,
+34427,
+51933,
+3783,
+4175,
+61285,
+29108,
+56512,
+20445,
+50258,
+59125,
+59292,
+22284,
+50372,
+63148,
+21929,
+59062,
+10033,
+24370,
+719,
+53978,
+21497,
+11814,
+22871,
+16348,
+56242,
+28186,
+51548,
+63603,
+27633,
+64402,
+23985,
+60945,
+55160,
+15002,
+41316,
+58251,
+25748,
+59488,
+4498,
+33476,
+21970,
+35384,
+65275,
+2512,
+25689,
+27050,
+18278,
+16422,
+9303,
+43906,
+38290,
+7869,
+35834,
+49477,
+27858,
+44827,
+40534,
+8874,
+55185,
+50849,
+6797,
+33078,
+54879,
+53540,
+48968,
+22560,
+3513,
+47354,
+8814,
+4670,
+1376,
+30366,
+29829,
+30634,
+26096,
+22811,
+27423,
+41528,
+16115,
+34327,
+20698,
+25549,
+26480,
+34419,
+42105,
+55961,
+64525,
+9206,
+53551,
+43922,
+52242,
+26272,
+62385,
+27601,
+59538,
+32398,
+54896,
+13595,
+49308,
+14891,
+7522,
+13199,
+29160,
+50950,
+46860,
+46950,
+44434,
+6023,
+33085,
+36743,
+13387,
+44611,
+41976,
+23053,
+55034,
+35378,
+17788,
+4853,
+20483,
+42211,
+35339,
+50170,
+56125,
+40287,
+16504,
+56854,
+11105,
+54804,
+44818,
+11392,
+25254,
+15501,
+17922,
+40750,
+1955,
+2983,
+10038,
+33752,
+7257,
+46639,
+7017,
+34301,
+813,
+22947,
+48257,
+46494,
+63442,
+3788,
+30962,
+21036,
+24341,
+11920,
+49441,
+46449,
+880,
+37642,
+62002,
+22607,
+26766,
+57064,
+48883,
+17254,
+60213,
+53780,
+58865,
+28216,
+5703,
+1667,
+20000,
+6171,
+8294,
+3027,
+52054,
+47343,
+13978,
+12167,
+19870,
+28289,
+56070,
+35165,
+57351,
+59789,
+59973,
+49893,
+33090,
+18392,
+40147,
+747,
+36537,
+38972,
+33295,
+13419,
+37277,
+40948,
+56377,
+49660,
+20439,
+49484,
+59867,
+13261,
+9751,
+2212,
+46407,
+7391,
+38027,
+4518,
+11400,
+3496,
+7741,
+21968,
+33478,
+11116,
+8608,
+33192,
+51952,
+29098,
+31622,
+34586,
+57231,
+50442,
+17017,
+27586,
+43998,
+49207,
+15718,
+19901,
+41924,
+13754,
+42849,
+63279,
+47593,
+39134,
+19256,
+58694,
+19187,
+6207,
+62846,
+55476,
+5128,
+32712,
+34836,
+23114,
+40256,
+58401,
+55177,
+22783,
+22095,
+48172,
+5553,
+65479,
+25296,
+17667,
+9471,
+7093,
+31975,
+57227,
+41426,
+2548,
+39750,
+1132,
+1852,
+60445,
+50219,
+42587,
+52490,
+43550,
+16035,
+9928,
+7024,
+32401,
+42381,
+50939,
+31564,
+15548,
+20127,
+22614,
+11558,
+21240,
+53724,
+61693,
+9227,
+48614,
+27270,
+24672,
+15601,
+19815,
+12863,
+59022,
+28725,
+27837,
+5597,
+5679,
+20689,
+26333,
+25622,
+61507,
+59308,
+15354,
+35802,
+15983,
+47895,
+13390,
+11879,
+25118,
+18948,
+33041,
+36903,
+48193,
+62855,
+15108,
+5744,
+23535,
+25978,
+12786,
+9042,
+46340,
+776,
+2194,
+49206,
+44095,
+27587,
+51268,
+57192,
+30531,
+62930,
+44539,
+63350,
+56394,
+35594,
+19072,
+20762,
+7043,
+47386,
+8584,
+12658,
+24877,
+62822,
+54261,
+15416,
+28882,
+54372,
+6523,
+45899,
+43511,
+45682,
+57520,
+12420,
+29084,
+34224,
+53614,
+35661,
+48596,
+51328,
+60810,
+8363,
+37369,
+40991,
+60577,
+31177,
+34325,
+16117,
+17416,
+18642,
+59133,
+18701,
+36456,
+13629,
+929,
+5850,
+20342,
+59214,
+18443,
+23769,
+29483,
+39409,
+50460,
+65012,
+54907,
+8997,
+49507,
+22231,
+22054,
+6993,
+3871,
+13017,
+9531,
+1823,
+29931,
+39984,
+37753,
+58492,
+18153,
+35246,
+31454,
+52241,
+44228,
+53552,
+4108,
+6333,
+28817,
+19361,
+56883,
+8302,
+11675,
+2858,
+65240,
+35205,
+53049,
+12445,
+6957,
+38289,
+44268,
+9304,
+17771,
+55113,
+4459,
+8127,
+51613,
+55333,
+59089,
+10197,
+6511,
+52022,
+7222,
+13974,
+41260,
+47156,
+28480,
+9394,
+37656,
+1898,
+18292,
+1692,
+22337,
+5015,
+52590,
+36627,
+2200,
+44933,
+11586,
+45770,
+48857,
+10229,
+48196,
+481,
+34945,
+29060,
+55144,
+14878,
+17188,
+54096,
+31893,
+40882,
+42637,
+25324,
+384,
+35408,
+62299,
+20065,
+54920,
+35208,
+64343,
+51255,
+60568,
+53916,
+39295,
+49529,
+59892,
+34639,
+15199,
+16700,
+16243,
+23599,
+39637,
+19301,
+65392,
+57801,
+47169,
+36393,
+2830,
+10048,
+151,
+31476,
+48234,
+6785,
+17549,
+58342,
+55927,
+25707,
+6234,
+6602,
+36824,
+39551,
+64055,
+59564,
+7189,
+24092,
+47370,
+10438,
+37770,
+30074,
+22605,
+62004,
+11932,
+55482,
+26251,
+15152,
+47680,
+19494,
+31645,
+18509,
+6668,
+12108,
+48668,
+9586,
+2961,
+2988,
+53676,
+2029,
+53044,
+29564,
+13984,
+44464,
+50855,
+5157,
+217,
+55016,
+25462,
+24828,
+65311,
+36993,
+61614,
+5131,
+32297,
+4528,
+39274,
+39712,
+7145,
+24919,
+50161,
+33151,
+39477,
+22716,
+49417,
+63932,
+34935,
+61698,
+28603,
+54286,
+45306,
+56587,
+28965,
+28172,
+33971,
+49991,
+42970,
+50799,
+63310,
+18402,
+6226,
+39921,
+3716,
+8235,
+5229,
+1417,
+15484,
+54187,
+7075,
+45838,
+49731,
+46700,
+15585,
+45998,
+17836,
+39745,
+28888,
+48383,
+7631,
+13749,
+46344,
+61710,
+23965,
+62035,
+1536,
+23069,
+45661,
+42957,
+64972,
+64957,
+11320,
+61862,
+27097,
+16222,
+28756,
+20006,
+47728,
+59646,
+26672,
+11844,
+15565,
+6809,
+36311,
+49845,
+26022,
+4033,
+1057,
+48875,
+50323,
+40806,
+24904,
+20916,
+28300,
+62340,
+50015,
+54302,
+6144,
+63277,
+42851,
+59838,
+62260,
+38551,
+4681,
+47663,
+24576,
+52429,
+32767,
+3,
+16385,
+56175,
+6554,
+45372,
+53249,
+31044,
+8937,
+15729,
+30428,
+63423,
+13493,
+58452,
+34407,
+12193,
+35618,
+8025,
+11343,
+42897,
+1130,
+39752,
+62465,
+17607,
+38386,
+50275,
+7761,
+29865,
+34367,
+31612,
+51387,
+34569,
+55079,
+58105,
+53085,
+4454,
+45134,
+63132,
+7607,
+40462,
+8331,
+61204,
+32240,
+52120,
+30752,
+51247,
+52526,
+943,
+7846,
+452,
+14613,
+65103,
+29364,
+37569,
+24986,
+23722,
+59615,
+33738,
+52201,
+2247,
+35714,
+13035,
+41673,
+20327,
+55534,
+36334,
+51159,
+2964,
+57426,
+53069,
+19220,
+30439,
+34606,
+65235,
+59877,
+36736,
+290,
+51800,
+33051,
+35139,
+11290,
+52484,
+9938,
+2388,
+21234,
+6476,
+64769,
+45800,
+1751,
+31161,
+20786,
+61184,
+34682,
+33360,
+42365,
+61137,
+25894,
+20863,
+28953,
+55762,
+8577,
+48554,
+51093,
+52515,
+12896,
+27848,
+56619,
+29995,
+42538,
+51623,
+24976,
+594,
+42187,
+17308,
+7903,
+57321,
+42618,
+2629,
+29231,
+55568,
+5675,
+46475,
+57795,
+21429,
+32060,
+21260,
+30154,
+6571,
+28651,
+24853,
+63679,
+58162,
+12808,
+30705,
+62424,
+9920,
+56336,
+9454,
+33882,
+23846,
+7369,
+16034,
+44052,
+52491,
+7773,
+22552,
+15783,
+48369,
+51451,
+27393,
+18286,
+41996,
+4475,
+28538,
+42884,
+61149,
+38394,
+39965,
+18467,
+35321,
+34688,
+64992,
+15301,
+38757,
+33571,
+34829,
+8060,
+12871,
+54179,
+9863,
+13030,
+25907,
+16193,
+33846,
+7688,
+23057,
+20309,
+34441,
+29196,
+735,
+45681,
+43974,
+45900,
+10184,
+63273,
+60441,
+16620,
+51351,
+30203,
+116,
+34730,
+19971,
+14272,
+19309,
+113,
+20122,
+53123,
+13686,
+52806,
+4516,
+38029,
+25735,
+32660,
+10365,
+30190,
+61902,
+7341,
+64690,
+43305,
+21601,
+58545,
+56501,
+50755,
+61113,
+39015,
+32106,
+31754,
+49178,
+38699,
+53130,
+64043,
+28753,
+31288,
+11496,
+60744,
+8959,
+41203,
+36388,
+59483,
+53195,
+62203,
+9105,
+23514,
+31029,
+16946,
+20323,
+55790,
+38176,
+41697,
+23465,
+57961,
+38086,
+19643,
+62472,
+37796,
+63570,
+59571,
+49574,
+34535,
+42753,
+21466,
+17366,
+50500,
+30734,
+46652,
+55831,
+60318,
+19431,
+41852,
+57588,
+53491,
+37174,
+29174,
+39139,
+35755,
+38347,
+741,
+10335,
+12993,
+43230,
+47125,
+19152,
+62236,
+14439,
+62420,
+48018,
+19915,
+4269,
+4805,
+56197,
+36366,
+20417,
+23994,
+64918,
+29376,
+47789,
+41420,
+32120,
+49799,
+43262,
+32693,
+11437,
+60513,
+60306,
+64122,
+12098,
+14572,
+35533,
+9184,
+2270,
+3868,
+9165,
+32841,
+41909,
+23118,
+16797,
+12950,
+4778,
+20969,
+12824,
+24647,
+7321,
+57187,
+27208,
+25169,
+139,
+15449,
+64363,
+35591,
+63890,
+8004,
+4160,
+13121,
+3592,
+38579,
+28222,
+35253,
+36283,
+7141,
+31338,
+597,
+31016,
+47933,
+24979,
+38077,
+62662,
+22736,
+61445,
+1619,
+9427,
+47833,
+33956,
+65345,
+62793,
+52875,
+6725,
+11450,
+63077,
+45096,
+16055,
+16822,
+39846,
+38489,
+65475,
+56943,
+7460,
+58098,
+49352,
+37965,
+29324,
+51007,
+47531,
+15296,
+4341,
+32528,
+10673,
+41439,
+5367,
+27654,
+7473,
+8340,
+16399,
+42652,
+41364,
+59744,
+14473,
+10302,
+2380,
+64437,
+1790,
+14455,
+50201,
+39242,
+33597,
+30261,
+16029,
+21600,
+43484,
+64691,
+28465,
+56391,
+60500,
+64588,
+6292,
+46709,
+32907,
+60840,
+27229,
+51297,
+40090,
+41959,
+14297,
+44907,
+31764,
+58550,
+48680,
+61926,
+12473,
+39526,
+321,
+29513,
+27418,
+27670,
+24583,
+3224,
+47716,
+5102,
+9235,
+6962,
+34779,
+28705,
+59924,
+30539,
+20587,
+5212,
+4376,
+23883,
+37662,
+60885,
+32692,
+43403,
+49800,
+13624,
+32339,
+29290,
+5080,
+26546,
+49716,
+6244,
+21231,
+15166,
+50207,
+32917,
+58524,
+64108,
+15192,
+26931,
+55797,
+61386,
+5165,
+4327,
+14553,
+23088,
+63412,
+18360,
+37697,
+29602,
+42319,
+63483,
+51524,
+38761,
+47124,
+43423,
+12994,
+6648,
+56598,
+49810,
+59166,
+47914,
+38296,
+23692,
+22434,
+36291,
+65352,
+13892,
+2487,
+45545,
+64207,
+17803,
+45031,
+31857,
+24150,
+28003,
+3261,
+26260,
+54757,
+30833,
+47210,
+29272,
+47464,
+54510,
+63844,
+21653,
+60523,
+8015,
+53119,
+28270,
+28081,
+5315,
+28319,
+33031,
+36065,
+40307,
+59927,
+13064,
+15767,
+18027,
+46769,
+20884,
+12076,
+23547,
+19263,
+35930,
+61870,
+55366,
+31558,
+62612,
+48571,
+32304,
+17322,
+25954,
+5248,
+47309,
+24843,
+23418,
+4708,
+3202,
+53957,
+56216,
+20302,
+56829,
+62324,
+24710,
+42597,
+15606,
+7400,
+17180,
+10468,
+2480,
+25809,
+12273,
+4043,
+14084,
+38146,
+28587,
+12080,
+35132,
+31349,
+39873,
+51832,
+41239,
+49381,
+14938,
+33342,
+38730,
+9734,
+48425,
+4555,
+50995,
+37405,
+59327,
+20858,
+36777,
+35585,
+42696,
+64066,
+11013,
+45170,
+9819,
+50008,
+29507,
+34473,
+38351,
+6756,
+51096,
+3119,
+57829,
+19066,
+5638,
+22513,
+34126,
+17730,
+20330,
+53495,
+59208,
+48377,
+61245,
+31036,
+41844,
+22316,
+29247,
+19650,
+55156,
+48535,
+56001,
+59950,
+47740,
+3297,
+37340,
+10077,
+33862,
+23653,
+10499,
+40403,
+34072,
+53355,
+17503,
+27202,
+36941,
+2011,
+39903,
+39945,
+18975,
+31502,
+10721,
+34102,
+40934,
+32290,
+64440,
+13814,
+65149,
+61135,
+42367,
+35840,
+61016,
+54620,
+59144,
+25008,
+52548,
+20200,
+21001,
+16081,
+62665,
+18103,
+57983,
+55412,
+1341,
+45076,
+8672,
+50342,
+14317,
+61716,
+16248,
+58633,
+56063,
+17359,
+52978,
+32853,
+26829,
+8289,
+58842,
+41855,
+8821,
+17481,
+24777,
+55421,
+47636,
+54653,
+57860,
+14302,
+35883,
+55546,
+2015,
+52047,
+52726,
+42091,
+19602,
+59898,
+22424,
+31412,
+29929,
+1825,
+29970,
+59201,
+18850,
+59155,
+18406,
+65214,
+36026,
+11787,
+26337,
+53303,
+22861,
+5955,
+24968,
+23495,
+53201,
+2801,
+51902,
+7966,
+41230,
+17120,
+28155,
+44964,
+1922,
+52008,
+38675,
+45741,
+54917,
+1439,
+59758,
+23083,
+54230,
+44386,
+22705,
+40476,
+57763,
+21990,
+49755,
+64180,
+7299,
+59909,
+11578,
+9323,
+16568,
+21285,
+62025,
+26227,
+60573,
+33027,
+24751,
+50798,
+43762,
+49992,
+39292,
+30974,
+11475,
+24723,
+7659,
+50420,
+2546,
+41428,
+64611,
+47967,
+64971,
+43731,
+45662,
+6527,
+64263,
+45151,
+44824,
+58042,
+4155,
+206,
+16781,
+4285,
+29222,
+34715,
+28033,
+27762,
+23935,
+17527,
+52889,
+11702,
+29,
+50246,
+29992,
+37982,
+41451,
+35462,
+21721,
+3534,
+21377,
+38772,
+5180,
+33383,
+3568,
+54961,
+45409,
+21495,
+53980,
+11542,
+48119,
+46897,
+49181,
+7843,
+62973,
+61907,
+37799,
+62197,
+34114,
+41713,
+29665,
+55652,
+23175,
+6044,
+36190,
+11835,
+55909,
+19287,
+45970,
+25568,
+8972,
+33058,
+1129,
+43673,
+11344,
+1838,
+58660,
+16309,
+44575,
+36276,
+22818,
+56089,
+57678,
+33787,
+8165,
+61148,
+43538,
+28539,
+51744,
+57018,
+24260,
+47277,
+40316,
+9738,
+4153,
+58044,
+48244,
+55286,
+49726,
+34353,
+50988,
+42000,
+39561,
+26183,
+48941,
+34130,
+61233,
+45388,
+59979,
+3315,
+48798,
+42519,
+54553,
+27414,
+28898,
+53208,
+63789,
+61524,
+59837,
+43700,
+63278,
+44089,
+13755,
+31850,
+29073,
+13190,
+9565,
+45348,
+64431,
+2492,
+27893,
+3537,
+26138,
+54644,
+2835,
+40511,
+40795,
+21150,
+22549,
+25062,
+40707,
+228,
+4580,
+6167,
+45063,
+31369,
+14130,
+4931,
+26059,
+21762,
+16121,
+6436,
+46051,
+60365,
+14500,
+349,
+32395,
+28503,
+12695,
+58688,
+56341,
+14484,
+48992,
+29026,
+7822,
+8035,
+2782,
+49454,
+2874,
+22181,
+4093,
+31777,
+15186,
+13508,
+37502,
+37777,
+18624,
+59725,
+55602,
+42098,
+26685,
+27067,
+58548,
+31766,
+9097,
+57607,
+39510,
+39226,
+31255,
+37460,
+61390,
+55121,
+29683,
+27548,
+55537,
+46727,
+31935,
+24169,
+29917,
+57514,
+51429,
+12340,
+3336,
+29637,
+38647,
+60561,
+2662,
+56383,
+56910,
+12727,
+8327,
+53962,
+37005,
+16016,
+52154,
+10359,
+21465,
+43443,
+34536,
+20142,
+46716,
+14860,
+65097,
+16124,
+9544,
+31949,
+8048,
+5782,
+59577,
+27309,
+20598,
+30509,
+55019,
+10713,
+50056,
+31021,
+63643,
+64534,
+33816,
+59850,
+42290,
+9330,
+8640,
+39440,
+21295,
+38115,
+32362,
+12769,
+15618,
+32352,
+29260,
+22647,
+9449,
+52174,
+29140,
+13000,
+48661,
+52050,
+60518,
+8869,
+31277,
+11653,
+31791,
+19397,
+45162,
+50011,
+56576,
+24336,
+25018,
+16103,
+52910,
+59439,
+59841,
+64065,
+43128,
+35586,
+38135,
+29891,
+27216,
+59051,
+37110,
+37814,
+57285,
+11550,
+12625,
+6100,
+4706,
+23420,
+40452,
+31309,
+15099,
+47626,
+11663,
+12233,
+22527,
+59583,
+30342,
+2703,
+56643,
+7879,
+47848,
+64319,
+38020,
+53722,
+21242,
+49190,
+44549,
+11068,
+10463,
+35281,
+26075,
+39191,
+14397,
+15946,
+16226,
+54702,
+29757,
+41363,
+43320,
+16400,
+42062,
+55959,
+42107,
+49409,
+40062,
+11838,
+50957,
+11482,
+26169,
+17263,
+61776,
+62174,
+25323,
+43864,
+40883,
+33999,
+25971,
+41407,
+38089,
+51486,
+49338,
+8534,
+35768,
+50882,
+18968,
+50889,
+1576,
+24354,
+52401,
+5225,
+39416,
+2628,
+43576,
+57322,
+5993,
+18490,
+60934,
+18557,
+506,
+18098,
+4788,
+45777,
+11716,
+25143,
+15559,
+24232,
+52297,
+63048,
+19994,
+52093,
+55188,
+20,
+15605,
+43159,
+24711,
+30746,
+44773,
+58496,
+19057,
+64723,
+21363,
+17281,
+52489,
+44054,
+50220,
+53176,
+17854,
+51858,
+50354,
+3994,
+64849,
+42166,
+63202,
+35072,
+61130,
+40058,
+41860,
+11447,
+31012,
+52453,
+54257,
+18647,
+41053,
+25356,
+38767,
+12607,
+5329,
+35450,
+48998,
+38151,
+29370,
+6791,
+7344,
+26885,
+5176,
+33624,
+61100,
+51163,
+45270,
+49579,
+10355,
+14993,
+51608,
+17124,
+8030,
+11034,
+47620,
+2397,
+28834,
+14338,
+46392,
+51622,
+43584,
+29996,
+28117,
+29264,
+57326,
+28874,
+56398,
+2504,
+52012,
+21144,
+19924,
+29926,
+64281,
+11594,
+131,
+32273,
+47845,
+13141,
+54552,
+42859,
+48799,
+29913,
+63288,
+29825,
+35793,
+1409,
+52663,
+10346,
+3643,
+4767,
+44879,
+35563,
+58036,
+35190,
+37957,
+18218,
+2296,
+8589,
+20776,
+3701,
+33336,
+49514,
+18952,
+31682,
+967,
+52065,
+39763,
+32814,
+51444,
+18878,
+37416,
+33485,
+57363,
+23560,
+21333,
+10504,
+59630,
+57848,
+22020,
+45229,
+38548,
+3544,
+32409,
+8136,
+53352,
+51031,
+27752,
+50106,
+36006,
+64000,
+21804,
+19877,
+33963,
+39992,
+40351,
+34876,
+54395,
+33896,
+35891,
+57418,
+3206,
+23645,
+40408,
+5778,
+22546,
+11308,
+37281,
+41478,
+50983,
+22297,
+2126,
+10323,
+30681,
+8466,
+30877,
+105,
+6802,
+16773,
+15555,
+36894,
+55445,
+11425,
+53338,
+59225,
+16419,
+18333,
+35093,
+156,
+53015,
+6282,
+46533,
+9123,
+32475,
+38539,
+30700,
+21461,
+25282,
+10748,
+23627,
+22149,
+48741,
+360,
+6355,
+48267,
+2001,
+14372,
+20269,
+819,
+1040,
+38467,
+9039,
+5460,
+52433,
+50643,
+63857,
+64367,
+898,
+64809,
+20825,
+7928,
+26029,
+51849,
+12733,
+16043,
+39824,
+23489,
+45849,
+65285,
+57966,
+18502,
+32031,
+54148,
+25455,
+4164,
+15390,
+35582,
+50938,
+44047,
+32402,
+60876,
+40603,
+54351,
+9417,
+34370,
+49302,
+9379,
+26642,
+39213,
+7754,
+3618,
+35839,
+43070,
+61136,
+43598,
+33361,
+9884,
+22629,
+59494,
+3331,
+2853,
+58672,
+9463,
+6822,
+11950,
+48434,
+6872,
+48851,
+33396,
+5684,
+11506,
+992,
+65472,
+64514,
+58933,
+28715,
+31124,
+16789,
+45994,
+53267,
+47606,
+18741,
+38777,
+8720,
+32390,
+61111,
+50757,
+59414,
+12237,
+8489,
+58305,
+53375,
+2674,
+65489,
+58566,
+7589,
+42164,
+64851,
+61424,
+63482,
+43235,
+29603,
+20680,
+59517,
+51982,
+50834,
+32190,
+63779,
+36369,
+35631,
+62803,
+15492,
+2446,
+64922,
+41522,
+5653,
+19370,
+11274,
+50123,
+54185,
+15486,
+20398,
+19647,
+33520,
+54695,
+36974,
+63053,
+35079,
+9329,
+42730,
+59851,
+54033,
+5009,
+58775,
+53809,
+31744,
+3971,
+32753,
+45199,
+40415,
+29528,
+30620,
+47975,
+37399,
+29568,
+1865,
+17035,
+20640,
+52971,
+47293,
+6374,
+51125,
+42040,
+12338,
+51431,
+40666,
+2233,
+58644,
+27568,
+51755,
+63417,
+7331,
+5087,
+20623,
+12715,
+29136,
+36239,
+36229,
+11891,
+28267,
+1348,
+28470,
+65416,
+3824,
+63362,
+33493,
+543,
+50238,
+64814,
+62361,
+35649,
+8132,
+60550,
+51592,
+32746,
+51821,
+49898,
+47798,
+11414,
+26744,
+49642,
+24498,
+19588,
+17726,
+12142,
+63676,
+29845,
+39682,
+26194,
+3311,
+17434,
+51021,
+51505,
+61465,
+58020,
+2085,
+8955,
+35338,
+44198,
+20484,
+59421,
+9293,
+37353,
+10663,
+64905,
+62526,
+19096,
+10341,
+25520,
+5083,
+53021,
+14936,
+49383,
+4748,
+49171,
+52771,
+52867,
+63774,
+13949,
+35344,
+28617,
+17307,
+43580,
+595,
+31340,
+41986,
+52951,
+65262,
+55253,
+65060,
+45574,
+33216,
+63200,
+42168,
+47586,
+19998,
+1669,
+45249,
+48481,
+61703,
+47585,
+42176,
+63201,
+42579,
+64850,
+42323,
+7590,
+57552,
+34403,
+52655,
+23680,
+56718,
+62305,
+9149,
+6612,
+53160,
+29481,
+23771,
+2099,
+5400,
+13470,
+25776,
+6408,
+10871,
+55224,
+40110,
+39167,
+32557,
+32536,
+40767,
+19139,
+56269,
+5397,
+35999,
+39092,
+30482,
+11299,
+30583,
+65523,
+15125,
+12137,
+36765,
+3575,
+16147,
+10265,
+52700,
+47234,
+1573,
+48092,
+40693,
+22369,
+60830,
+22854,
+25086,
+18515,
+24611,
+25066,
+14344,
+32886,
+15210,
+48459,
+49408,
+42648,
+55960,
+44233,
+34420,
+52966,
+61977,
+11731,
+17860,
+26684,
+42791,
+55603,
+29619,
+59283,
+26874,
+45049,
+19601,
+43027,
+52727,
+34834,
+32714,
+2451,
+27611,
+18754,
+38205,
+48640,
+7941,
+53911,
+10019,
+29388,
+47406,
+34548,
+25141,
+11718,
+12170,
+23839,
+22084,
+7577,
+48250,
+3326,
+62859,
+32439,
+52271,
+32520,
+39588,
+55958,
+42650,
+16401,
+924,
+17572,
+49233,
+18553,
+55291,
+56659,
+56531,
+5947,
+34874,
+40353,
+39623,
+25712,
+23144,
+19689,
+39686,
+18577,
+7970,
+40568,
+22530,
+12337,
+42267,
+51126,
+806,
+33541,
+52617,
+488,
+11929,
+7983,
+31136,
+13960,
+34044,
+61174,
+47163,
+25359,
+18693,
+29432,
+56431,
+22077,
+34509,
+30107,
+64060,
+50397,
+25079,
+49841,
+27005,
+2867,
+56329,
+11988,
+38102,
+49143,
+14981,
+52253,
+7540,
+17678,
+24019,
+855,
+8040,
+59792,
+21531,
+39560,
+42869,
+50989,
+1303,
+4474,
+43541,
+18287,
+1094,
+49857,
+38682,
+53460,
+22355,
+46275,
+61574,
+52950,
+42184,
+31341,
+46357,
+36202,
+64374,
+65256,
+57814,
+52103,
+8173,
+23052,
+44205,
+44612,
+27235,
+60083,
+64889,
+52775,
+12450,
+61085,
+16492,
+7082,
+3406,
+14755,
+645,
+11262,
+24469,
+52369,
+14296,
+43292,
+40091,
+62388,
+8724,
+14821,
+1270,
+15470,
+58290,
+6088,
+39405,
+9855,
+61289,
+33163,
+12429,
+15887,
+8189,
+50118,
+1561,
+15651,
+39180,
+49293,
+21692,
+25901,
+35158,
+19329,
+36560,
+9082,
+44664,
+37627,
+28936,
+47501,
+49320,
+31747,
+57382,
+13753,
+44091,
+19902,
+14631,
+19534,
+46828,
+46099,
+16027,
+30263,
+46501,
+3382,
+3798,
+30846,
+18453,
+54788,
+23117,
+43389,
+32842,
+54102,
+63102,
+51779,
+55429,
+24810,
+48115,
+40047,
+7622,
+17015,
+50444,
+49588,
+34253,
+32957,
+17466,
+62330,
+23080,
+25130,
+52791,
+18266,
+56827,
+20304,
+5772,
+31674,
+22446,
+55039,
+15853,
+10414,
+35856,
+35788,
+4590,
+10379,
+17004,
+56300,
+58577,
+16171,
+27173,
+39315,
+40169,
+14673,
+53448,
+11316,
+26964,
+37583,
+28656,
+12737,
+32255,
+11446,
+42574,
+40059,
+12881,
+23378,
+8820,
+43041,
+58843,
+57587,
+43434,
+19432,
+11781,
+10909,
+51920,
+57755,
+27240,
+22315,
+43104,
+31037,
+36017,
+24203,
+177,
+9942,
+1662,
+18602,
+24126,
+7767,
+46918,
+59142,
+54622,
+16076,
+45221,
+63754,
+51011,
+57651,
+47560,
+2739,
+33798,
+37428,
+44747,
+55387,
+20581,
+36858,
+9574,
+1014,
+40550,
+21900,
+5923,
+34263,
+1732,
+3102,
+38377,
+2472,
+13119,
+4162,
+25457,
+44668,
+17698,
+51319,
+16338,
+17381,
+29904,
+2736,
+3473,
+30883,
+61330,
+56298,
+17006,
+61118,
+55703,
+53501,
+60539,
+8762,
+47873,
+7663,
+32436,
+30150,
+16138,
+2072,
+20835,
+26073,
+35283,
+10761,
+27642,
+20210,
+24123,
+40982,
+57117,
+31271,
+29058,
+34947,
+38806,
+47093,
+21592,
+36055,
+23385,
+63439,
+2845,
+32355,
+49968,
+47011,
+58558,
+6998,
+6565,
+47422,
+58595,
+11643,
+62842,
+51404,
+27339,
+41646,
+56861,
+5871,
+28227,
+57953,
+44571,
+7666,
+46724,
+26753,
+7318,
+4514,
+52808,
+57212,
+11866,
+28437,
+23920,
+201,
+46396,
+9270,
+25499,
+12782,
+15961,
+31004,
+14476,
+45986,
+54566,
+56738,
+45716,
+35471,
+31515,
+27053,
+59431,
+45002,
+18388,
+5632,
+54334,
+9274,
+29664,
+42911,
+34115,
+47567,
+39836,
+4915,
+13559,
+25486,
+56173,
+16387,
+8739,
+17645,
+17713,
+6827,
+61094,
+53366,
+23464,
+43454,
+38177,
+40817,
+24642,
+17830,
+31654,
+21984,
+58169,
+49881,
+35342,
+13951,
+26461,
+24869,
+63306,
+49286,
+31366,
+32524,
+25839,
+44520,
+3266,
+61546,
+14406,
+1766,
+20326,
+43630,
+13036,
+5028,
+20891,
+32277,
+33805,
+44882,
+60845,
+39706,
+38458,
+9704,
+5221,
+61911,
+25348,
+48446,
+3019,
+41515,
+59623,
+61160,
+22271,
+27876,
+36732,
+52057,
+21200,
+1797,
+32405,
+56860,
+41751,
+27340,
+26409,
+6783,
+48236,
+16014,
+37007,
+29357,
+46610,
+11591,
+51794,
+8552,
+40658,
+2717,
+24282,
+39178,
+15653,
+15370,
+6469,
+7831,
+28527,
+17867,
+30169,
+50708,
+8076,
+46681,
+63164,
+10141,
+37099,
+23801,
+65337,
+989,
+39257,
+45734,
+33630,
+30243,
+1312,
+12180,
+48036,
+15082,
+60980,
+14544,
+35678,
+37774,
+22595,
+48011,
+50213,
+7674,
+38623,
+57015,
+9915,
+51771,
+1177,
+63257,
+30167,
+17869,
+33569,
+38759,
+51526,
+28943,
+62642,
+33313,
+55326,
+5502,
+14054,
+63477,
+44626,
+24376,
+37844,
+14899,
+9119,
+5987,
+63360,
+3826,
+21801,
+3503,
+15581,
+34822,
+12665,
+54729,
+38946,
+54883,
+63514,
+37372,
+59802,
+11762,
+32789,
+13718,
+36670,
+56100,
+50140,
+32927,
+1850,
+1134,
+21250,
+4593,
+4295,
+56278,
+16318,
+10045,
+2617,
+57252,
+45119,
+22127,
+620,
+27440,
+12358,
+29617,
+55605,
+39669,
+52310,
+6190,
+52221,
+1832,
+20345,
+30650,
+17395,
+16114,
+44240,
+27424,
+3521,
+29234,
+54762,
+5652,
+42305,
+64923,
+20635,
+47858,
+23531,
+64683,
+59622,
+41657,
+3020,
+58878,
+17532,
+3096,
+8783,
+20541,
+5313,
+28083,
+56990,
+50699,
+12634,
+5800,
+59121,
+379,
+12591,
+46973,
+12958,
+13868,
+45247,
+1671,
+26694,
+5078,
+29292,
+46832,
+61498,
+47831,
+9429,
+37134,
+36503,
+6624,
+2207,
+58919,
+41104,
+7037,
+15915,
+50982,
+42451,
+37282,
+32236,
+26820,
+35202,
+40435,
+52593,
+60834,
+61259,
+10007,
+14257,
+30269,
+17523,
+53664,
+56700,
+27852,
+29133,
+62148,
+60025,
+18065,
+58504,
+10579,
+57318,
+62372,
+31216,
+54323,
+35461,
+42934,
+37983,
+38044,
+20557,
+51355,
+58347,
+21716,
+18168,
+61563,
+58049,
+57266,
+5366,
+43326,
+10674,
+15030,
+27073,
+19238,
+48785,
+49634,
+29423,
+56916,
+51906,
+64610,
+42961,
+2547,
+44061,
+57228,
+3748,
+15804,
+18839,
+32119,
+43406,
+47790,
+12502,
+29690,
+12395,
+45326,
+23761,
+24556,
+46934,
+23701,
+57771,
+19641,
+38088,
+42633,
+25972,
+12794,
+52825,
+61537,
+1689,
+51306,
+19745,
+63145,
+12774,
+52959,
+54887,
+32231,
+17164,
+51289,
+49148,
+56800,
+63610,
+3710,
+33679,
+22326,
+37535,
+11177,
+27012,
+53764,
+34178,
+52110,
+13166,
+51243,
+54781,
+11195,
+2840,
+15203,
+41300,
+40387,
+16210,
+37829,
+37201,
+33601,
+22764,
+35621,
+37851,
+59743,
+43319,
+42653,
+29758,
+38426,
+53927,
+20934,
+14802,
+7337,
+35949,
+31034,
+61247,
+33969,
+28174,
+37039,
+64464,
+12656,
+8586,
+58837,
+7759,
+50277,
+3248,
+707,
+10461,
+11070,
+60009,
+25483,
+5579,
+55590,
+32954,
+29519,
+23696,
+65361,
+750,
+16058,
+37681,
+56308,
+16925,
+64419,
+13789,
+46067,
+56429,
+29434,
+28518,
+10866,
+46366,
+8435,
+63153,
+58250,
+44283,
+15003,
+47756,
+34454,
+48634,
+982,
+11935,
+51663,
+35115,
+32260,
+49977,
+22932,
+13241,
+6915,
+9335,
+40386,
+41374,
+15204,
+51672,
+40574,
+7670,
+65145,
+41234,
+419,
+26996,
+34839,
+55066,
+53188,
+16916,
+7505,
+19009,
+19800,
+64929,
+3456,
+26485,
+29048,
+21136,
+3731,
+8518,
+22657,
+18261,
+32547,
+29723,
+52267,
+56697,
+47061,
+20760,
+19074,
+32462,
+9011,
+39569,
+57974,
+27,
+11704,
+58388,
+47155,
+43892,
+13975,
+16887,
+41047,
+62819,
+23905,
+26360,
+11656,
+5848,
+931,
+59139,
+48265,
+6357,
+58271,
+21128,
+14663,
+2544,
+50422,
+40644,
+62060,
+49380,
+43142,
+51833,
+12479,
+14458,
+418,
+41294,
+65146,
+61673,
+17119,
+43002,
+7967,
+57387,
+15008,
+40841,
+18716,
+34557,
+9523,
+16110,
+57923,
+48450,
+33038,
+36222,
+46908,
+28903,
+34105,
+65440,
+21164,
+16979,
+29153,
+31668,
+57819,
+10829,
+39535,
+26976,
+25779,
+36387,
+43466,
+8960,
+22834,
+40520,
+26909,
+15254,
+44500,
+21370,
+53618,
+13655,
+63316,
+9064,
+61981,
+14786,
+34580,
+14844,
+17298,
+6252,
+38665,
+63960,
+22914,
+13137,
+55686,
+14153,
+32112,
+5050,
+11964,
+35374,
+8751,
+54403,
+54312,
+15270,
+49805,
+53173,
+25515,
+55503,
+21237,
+64819,
+36837,
+51197,
+19147,
+20910,
+23958,
+27694,
+20359,
+30880,
+12223,
+7212,
+19345,
+13853,
+48333,
+14169,
+10930,
+49488,
+14960,
+51459,
+31296,
+11269,
+27059,
+31808,
+48872,
+2168,
+29612,
+32837,
+21218,
+45354,
+5713,
+5839,
+9349,
+52732,
+15208,
+32888,
+8703,
+15429,
+29533,
+47819,
+28854,
+4062,
+8921,
+1404,
+30927,
+63811,
+50417,
+13313,
+32484,
+30400,
+60403,
+25677,
+51156,
+20724,
+10430,
+51791,
+64284,
+46013,
+39532,
+45081,
+13432,
+57366,
+7036,
+41482,
+58920,
+55860,
+48788,
+63421,
+30430,
+51225,
+24689,
+16944,
+31031,
+47479,
+17328,
+24818,
+12190,
+26848,
+29163,
+56147,
+57434,
+51358,
+24634,
+2656,
+56165,
+53523,
+6193,
+4392,
+37163,
+55347,
+2323,
+142,
+4584,
+63008,
+59705,
+35821,
+11430,
+11909,
+54274,
+23575,
+13169,
+62816,
+3061,
+29559,
+11603,
+14465,
+61293,
+59684,
+48549,
+36344,
+340,
+4037,
+17708,
+25355,
+42568,
+18648,
+55294,
+46655,
+3059,
+62818,
+41257,
+16888,
+10583,
+34881,
+28343,
+29396,
+45458,
+15894,
+23309,
+45950,
+29497,
+2065,
+9803,
+26907,
+40522,
+12281,
+10593,
+37669,
+16534,
+33802,
+2345,
+47743,
+45685,
+13702,
+53060,
+5606,
+48169,
+15039,
+842,
+7853,
+62776,
+31511,
+14162,
+56635,
+2659,
+11162,
+17238,
+49609,
+35224,
+4323,
+34270,
+40261,
+26584,
+5666,
+28612,
+63953,
+49102,
+3953,
+56571,
+37481,
+21170,
+33579,
+55766,
+31173,
+52632,
+60576,
+43961,
+37370,
+63516,
+28130,
+6212,
+49072,
+61840,
+55023,
+57116,
+41775,
+24124,
+18604,
+36558,
+19331,
+12507,
+11722,
+45021,
+55737,
+60241,
+38890,
+31584,
+29710,
+18585,
+21105,
+5140,
+54868,
+35577,
+2427,
+48290,
+17873,
+21843,
+13109,
+55455,
+6242,
+49718,
+24798,
+37866,
+22258,
+62314,
+56039,
+25534,
+62453,
+56376,
+44124,
+37278,
+49853,
+47711,
+49276,
+46945,
+45744,
+34647,
+16285,
+30685,
+34760,
+37103,
+24618,
+32289,
+43076,
+34103,
+28905,
+4701,
+4280,
+39115,
+20088,
+47021,
+23423,
+40472,
+64866,
+20249,
+11241,
+30391,
+28433,
+24601,
+33249,
+50702,
+27353,
+48659,
+13002,
+39047,
+44983,
+16343,
+26053,
+62522,
+64603,
+31470,
+60588,
+7511,
+14178,
+24452,
+62882,
+9755,
+52835,
+49186,
+16744,
+14807,
+24719,
+23843,
+47708,
+51727,
+57897,
+52712,
+22155,
+58217,
+61025,
+36496,
+46326,
+28273,
+33998,
+42636,
+43865,
+31894,
+57510,
+54196,
+54829,
+47218,
+57599,
+32625,
+10119,
+15239,
+14420,
+32540,
+30825,
+38446,
+45814,
+1400,
+38266,
+21503,
+49937,
+11748,
+28823,
+44404,
+49743,
+39822,
+16045,
+11384,
+6264,
+31101,
+18209,
+54500,
+7240,
+29742,
+64130,
+14311,
+24440,
+48594,
+35663,
+56043,
+55141,
+18108,
+18715,
+41226,
+15009,
+53413,
+4142,
+60302,
+44765,
+64298,
+54474,
+46858,
+50952,
+19086,
+64659,
+20103,
+3212,
+22377,
+22941,
+34792,
+50307,
+19048,
+64296,
+44767,
+19865,
+50729,
+24641,
+41695,
+38178,
+54061,
+22572,
+57879,
+50765,
+12757,
+45468,
+20585,
+30541,
+24903,
+43709,
+50324,
+1203,
+50569,
+12498,
+61380,
+65333,
+59404,
+9823,
+1502,
+21149,
+42834,
+40512,
+48774,
+34396,
+16179,
+19253,
+55895,
+60127,
+32509,
+22565,
+14740,
+47541,
+21077,
+18299,
+36075,
+46080,
+58636,
+12605,
+38769,
+8475,
+30937,
+33405,
+52482,
+11292,
+14991,
+10357,
+52156,
+19138,
+42140,
+32537,
+6114,
+56246,
+61143,
+28376,
+48055,
+22499,
+10117,
+32627,
+18426,
+32188,
+50836,
+27317,
+51530,
+55838,
+1954,
+44184,
+17923,
+40532,
+44829,
+18016,
+10106,
+40729,
+47280,
+32450,
+28796,
+15818,
+24581,
+27672,
+8792,
+60030,
+2941,
+11206,
+31059,
+60628,
+40314,
+47279,
+40744,
+10107,
+23634,
+17423,
+32021,
+57505,
+54505,
+32820,
+19350,
+48208,
+24445,
+53348,
+47917,
+46616,
+25381,
+50224,
+60740,
+18353,
+39611,
+40074,
+21748,
+227,
+42830,
+25063,
+673,
+18962,
+46903,
+56161,
+54367,
+55510,
+4008,
+39709,
+10783,
+62627,
+18227,
+22368,
+42120,
+48093,
+34789,
+20766,
+38906,
+33685,
+36466,
+62375,
+36885,
+21974,
+1859,
+35694,
+48955,
+8495,
+44564,
+49160,
+57594,
+11628,
+6482,
+45330,
+34748,
+39396,
+37676,
+7498,
+39075,
+23852,
+2232,
+42264,
+51432,
+5275,
+8808,
+26747,
+52878,
+21555,
+2716,
+41634,
+8553,
+60535,
+7130,
+54583,
+26071,
+20837,
+28813,
+33652,
+39012,
+60663,
+10814,
+54497,
+62059,
+41242,
+50423,
+9693,
+9432,
+62752,
+39149,
+1295,
+44552,
+32554,
+34995,
+24730,
+21829,
+44622,
+29053,
+892,
+55557,
+39800,
+32879,
+62893,
+52991,
+29763,
+3033,
+60505,
+60158,
+17220,
+58391,
+21758,
+15377,
+11529,
+58450,
+13495,
+1249,
+51015,
+56516,
+35654,
+63347,
+53712,
+36533,
+28414,
+59110,
+54350,
+42378,
+60877,
+54580,
+5929,
+61482,
+61448,
+14734,
+60392,
+17723,
+18345,
+1805,
+33713,
+11126,
+64896,
+13006,
+46608,
+29359,
+31861,
+35571,
+30800,
+53280,
+25834,
+39620,
+44789,
+12150,
+64702,
+25540,
+44451,
+7669,
+41297,
+51673,
+14798,
+19659,
+59581,
+22529,
+42043,
+7971,
+56569,
+3955,
+26789,
+6222,
+13913,
+21955,
+64944,
+17603,
+22178,
+27461,
+29438,
+17490,
+1511,
+47550,
+27967,
+21899,
+41816,
+1015,
+19675,
+8358,
+19343,
+7214,
+7295,
+40252,
+63130,
+45136,
+409,
+2079,
+21206,
+4003,
+39955,
+8873,
+44261,
+44828,
+40748,
+17924,
+6392,
+22472,
+12990,
+34346,
+2243,
+55871,
+57957,
+12280,
+41033,
+26908,
+41200,
+22835,
+49435,
+7405,
+6983,
+4890,
+60071,
+48773,
+40794,
+42835,
+2836,
+45602,
+6389,
+40236,
+33228,
+53868,
+51041,
+2542,
+14665,
+1212,
+5620,
+60781,
+53230,
+15408,
+65202,
+35909,
+27528,
+21215,
+4650,
+32847,
+9069,
+13896,
+416,
+14460,
+38473,
+13538,
+46132,
+16723,
+63175,
+55323,
+36578,
+14560,
+47188,
+57762,
+42987,
+22706,
+24831,
+64865,
+40925,
+23424,
+51194,
+6732,
+26574,
+47962,
+15287,
+29190,
+5625,
+8330,
+43653,
+7608,
+37244,
+15139,
+23519,
+15697,
+64774,
+39306,
+12936,
+31308,
+42682,
+23421,
+47023,
+50822,
+29037,
+14881,
+6065,
+53081,
+222,
+31447,
+60972,
+64260,
+7962,
+33860,
+10079,
+36625,
+52592,
+41473,
+35203,
+65242,
+55061,
+35050,
+50191,
+33408,
+10182,
+45902,
+14511,
+49646,
+59339,
+6156,
+12061,
+16426,
+53657,
+21523,
+46590,
+8680,
+29527,
+42280,
+45200,
+52283,
+15395,
+32612,
+57741,
+5777,
+42456,
+23646,
+12747,
+53904,
+34071,
+43089,
+10500,
+32161,
+54300,
+50017,
+59043,
+30988,
+23461,
+53820,
+22930,
+49979,
+61359,
+5163,
+61388,
+37462,
+16209,
+41373,
+41301,
+9336,
+61488,
+27443,
+64461,
+53467,
+60754,
+39156,
+11347,
+60554,
+33322,
+49464,
+31379,
+237,
+39929,
+37805,
+17213,
+38328,
+22159,
+65399,
+44968,
+53860,
+62681,
+53762,
+27014,
+63556,
+45910,
+6536,
+62791,
+65347,
+12831,
+44787,
+39622,
+42051,
+34875,
+42464,
+39993,
+56563,
+32481,
+63708,
+13302,
+29821,
+52504,
+6433,
+65100,
+5262,
+13213,
+2952,
+15381,
+37780,
+4551,
+58872,
+64112,
+39719,
+29491,
+18723,
+10925,
+3450,
+38913,
+45148,
+48588,
+46153,
+14824,
+11486,
+60175,
+62873,
+9156,
+8797,
+19823,
+9737,
+42878,
+47278,
+40731,
+60629,
+57618,
+6217,
+56421,
+30537,
+59926,
+43190,
+36066,
+2574,
+58488,
+3488,
+51544,
+63997,
+34920,
+62813,
+11337,
+58908,
+35548,
+6966,
+32492,
+36873,
+46007,
+51101,
+623,
+26993,
+16503,
+44194,
+56126,
+29719,
+54144,
+21350,
+50037,
+8052,
+32269,
+60345,
+39303,
+57616,
+60631,
+55811,
+13661,
+16143,
+49239,
+40187,
+17093,
+44785,
+12833,
+29869,
+26512,
+19561,
+16005,
+64715,
+26583,
+41006,
+34271,
+18483,
+38056,
+58400,
+44075,
+23115,
+54790,
+63129,
+40543,
+7296,
+39034,
+37379,
+33441,
+56537,
+26561,
+27371,
+54517,
+38619,
+57,
+21470,
+47871,
+8764,
+1145,
+33227,
+40507,
+6390,
+17926,
+45519,
+51764,
+63763,
+27650,
+795,
+65291,
+536,
+56995,
+12094,
+13401,
+28476,
+36301,
+10777,
+5431,
+53099,
+17617,
+13374,
+26206,
+3362,
+22899,
+21674,
+65154,
+31397,
+38209,
+15357,
+34374,
+44753,
+53183,
+38173,
+32330,
+13408,
+1609,
+58333,
+47723,
+64655,
+27897,
+53865,
+32806,
+26386,
+64244,
+27139,
+60112,
+34594,
+3625,
+23877,
+17092,
+40271,
+49240,
+25993,
+654,
+47828,
+24483,
+22970,
+26771,
+18373,
+23510,
+46845,
+20464,
+8561,
+19558,
+25805,
+16257,
+56843,
+14672,
+41870,
+39316,
+37811,
+63238,
+6703,
+14870,
+16166,
+3621,
+58427,
+18070,
+8850,
+12248,
+18920,
+24822,
+15314,
+40025,
+28067,
+60198,
+6152,
+34724,
+51285,
+746,
+44131,
+18393,
+3425,
+31660,
+8256,
+33464,
+27806,
+59158,
+29543,
+45132,
+4456,
+55245,
+57361,
+33487,
+20450,
+60228,
+4878,
+54698,
+5280,
+31150,
+34218,
+50176,
+8735,
+1929,
+3159,
+57097,
+16294,
+46446,
+49564,
+36565,
+54933,
+49999,
+30768,
+3377,
+20702,
+51945,
+39166,
+42144,
+55225,
+55362,
+11641,
+58597,
+18201,
+48151,
+34464,
+4656,
+15467,
+16953,
+10411,
+64084,
+8983,
+45820,
+55988,
+46669,
+27599,
+62387,
+41958,
+43293,
+51298,
+50629,
+14619,
+55824,
+4313,
+18307,
+11388,
+23151,
+61374,
+23731,
+20870,
+14637,
+35026,
+10520,
+21747,
+40710,
+39612,
+13478,
+44339,
+51427,
+57516,
+46310,
+2411,
+30786,
+18440,
+55907,
+11837,
+42646,
+49410,
+12880,
+41859,
+42575,
+61131,
+31795,
+5527,
+24198,
+59959,
+51977,
+23830,
+9365,
+28837,
+7621,
+41901,
+48116,
+20945,
+46161,
+45334,
+62933,
+5817,
+56425,
+56266,
+23805,
+52756,
+12652,
+3549,
+6887,
+36014,
+3564,
+15774,
+63037,
+18044,
+59520,
+30667,
+28066,
+40154,
+15315,
+12363,
+24368,
+10035,
+61104,
+696,
+55195,
+23336,
+60455,
+38993,
+56632,
+53754,
+54630,
+32445,
+47147,
+59907,
+7301,
+26433,
+62010,
+21097,
+9497,
+24586,
+3085,
+16587,
+54843,
+11407,
+834,
+24960,
+21087,
+18800,
+56562,
+40350,
+42465,
+33964,
+26046,
+44838,
+21301,
+39058,
+64293,
+37752,
+43929,
+29932,
+33434,
+55410,
+57985,
+65078,
+30480,
+39094,
+7097,
+52787,
+46996,
+11903,
+3530,
+57557,
+19566,
+22636,
+56566,
+19779,
+18466,
+43535,
+38395,
+28248,
+46259,
+62808,
+32265,
+48844,
+58404,
+59611,
+8872,
+40536,
+4004,
+28114,
+52363,
+19782,
+45807,
+12066,
+8001,
+39125,
+18974,
+43081,
+39904,
+38617,
+54519,
+233,
+50134,
+57398,
+27245,
+57670,
+16806,
+7636,
+8634,
+46245,
+31769,
+26288,
+37804,
+40372,
+238,
+20567,
+11679,
+4675,
+16161,
+3687,
+3715,
+43757,
+6227,
+50185,
+65427,
+21045,
+39203,
+21514,
+4031,
+26024,
+19890,
+57100,
+2386,
+9940,
+179,
+19298,
+57140,
+38616,
+39944,
+43082,
+2012,
+30051,
+31328,
+38745,
+34214,
+17399,
+35681,
+61624,
+64047,
+31207,
+32780,
+55052,
+55980,
+11077,
+50896,
+53214,
+37918,
+28949,
+48272,
+3763,
+56780,
+29813,
+58229,
+871,
+30139,
+32863,
+5491,
+45359,
+51831,
+43144,
+31350,
+45403,
+12514,
+50603,
+62343,
+60653,
+56908,
+56385,
+3145,
+21062,
+32295,
+5133,
+24421,
+14382,
+48902,
+15024,
+4027,
+60543,
+8454,
+16767,
+51534,
+59018,
+47731,
+63024,
+21264,
+38488,
+43341,
+16823,
+2311,
+61938,
+35101,
+27665,
+37692,
+63229,
+2160,
+4914,
+41710,
+47568,
+9860,
+62740,
+2134,
+9609,
+21708,
+59304,
+56778,
+3765,
+25913,
+23488,
+42394,
+16044,
+40859,
+49744,
+49133,
+50683,
+52345,
+55974,
+60706,
+46094,
+36673,
+55629,
+34334,
+10753,
+8088,
+3258,
+35615,
+2947,
+7315,
+39067,
+53730,
+27507,
+22046,
+32878,
+40628,
+55558,
+51515,
+28738,
+61415,
+11006,
+45812,
+38448,
+64142,
+37941,
+7285,
+21256,
+6050,
+705,
+3250,
+29030,
+8146,
+14706,
+61318,
+4771,
+19956,
+8063,
+45781,
+45507,
+45925,
+44608,
+47898,
+21080,
+49568,
+17202,
+51370,
+31067,
+26844,
+58612,
+56972,
+50470,
+32813,
+42492,
+52066,
+23389,
+59130,
+38560,
+24332,
+29151,
+16981,
+14446,
+54002,
+62464,
+43671,
+1131,
+44059,
+2549,
+62274,
+39279,
+28887,
+43743,
+17837,
+18845,
+14144,
+46602,
+54839,
+27957,
+6084,
+18612,
+5860,
+16416,
+39023,
+45978,
+25364,
+49281,
+20410,
+63056,
+22384,
+53265,
+45996,
+15587,
+55620,
+13768,
+31244,
+55644,
+29490,
+40333,
+64113,
+830,
+18140,
+4972,
+65169,
+7144,
+43781,
+39275,
+10782,
+40698,
+4009,
+38457,
+41665,
+60846,
+12256,
+24955,
+25918,
+48478,
+35943,
+33012,
+23857,
+21018,
+1613,
+16771,
+6804,
+60270,
+33566,
+37140,
+31147,
+33912,
+60413,
+18576,
+42046,
+19690,
+30468,
+26193,
+42222,
+29846,
+60042,
+36038,
+63474,
+60993,
+64759,
+63345,
+35656,
+15282,
+61640,
+9808,
+52309,
+41537,
+55606,
+13115,
+1525,
+45310,
+45711,
+17115,
+33945,
+55130,
+10399,
+4242,
+5855,
+30811,
+14254,
+50610,
+10113,
+62486,
+45705,
+63185,
+56980,
+20736,
+4868,
+20218,
+20159,
+18313,
+4399,
+21941,
+44675,
+2508,
+47802,
+57138,
+19300,
+43844,
+23600,
+30380,
+52211,
+38251,
+6431,
+52506,
+22016,
+49345,
+7801,
+43,
+31280,
+61771,
+25711,
+42050,
+40354,
+44788,
+40581,
+25835,
+17060,
+7603,
+16730,
+26367,
+30545,
+13477,
+40073,
+40711,
+18354,
+38984,
+45908,
+63558,
+11962,
+5052,
+15954,
+9009,
+32464,
+6911,
+53411,
+15011,
+9505,
+58430,
+16851,
+5045,
+46199,
+27057,
+11271,
+62660,
+38079,
+55957,
+42064,
+32521,
+45066,
+32968,
+48214,
+22365,
+60290,
+64676,
+34065,
+4951,
+2767,
+47378,
+8573,
+58330,
+19000,
+3188,
+6454,
+62218,
+57973,
+41266,
+9012,
+31537,
+22903,
+24131,
+52744,
+56229,
+26182,
+42868,
+42001,
+21532,
+52752,
+45273,
+61052,
+15596,
+36755,
+60369,
+64054,
+43825,
+36825,
+11154,
+17879,
+29445,
+46444,
+16296,
+25352,
+64884,
+56592,
+13359,
+5657,
+18198,
+26725,
+11411,
+26975,
+41207,
+10830,
+45080,
+41109,
+46014,
+4100,
+37332,
+33692,
+320,
+43284,
+12474,
+6820,
+9465,
+30374,
+29340,
+11464,
+15252,
+26911,
+471,
+15691,
+21823,
+61505,
+25624,
+45648,
+39225,
+42784,
+57608,
+23139,
+13689,
+35728,
+63601,
+51550,
+10802,
+61892,
+60772,
+28161,
+33584,
+35639,
+30219,
+61012,
+39216,
+55695,
+54563,
+31572,
+25547,
+20700,
+3379,
+58422,
+37302,
+15444,
+49193,
+24627,
+3016,
+48312,
+7181,
+6562,
+60605,
+22715,
+43776,
+33152,
+52584,
+10966,
+3216,
+64662,
+20172,
+26605,
+14911,
+61042,
+10953,
+24884,
+44701,
+23756,
+30255,
+22877,
+26347,
+45562,
+49536,
+26736,
+29268,
+52410,
+35236,
+58675,
+4309,
+27265,
+8243,
+45640,
+2791,
+13545,
+3886,
+45103,
+54543,
+32069,
+54795,
+34902,
+21294,
+42727,
+8641,
+8942,
+63596,
+61045,
+2905,
+45489,
+37737,
+4775,
+54574,
+4742,
+38414,
+29981,
+53155,
+61875,
+16703,
+15322,
+57865,
+394,
+63547,
+59640,
+61881,
+38857,
+2627,
+42620,
+5226,
+62253,
+36233,
+30798,
+35573,
+50459,
+43943,
+29484,
+57654,
+9854,
+41950,
+6089,
+17682,
+657,
+499,
+61999,
+22699,
+10894,
+37675,
+40672,
+34749,
+47099,
+39063,
+17464,
+32959,
+50651,
+17071,
+4980,
+39039,
+37391,
+1461,
+33935,
+45919,
+48002,
+52385,
+52611,
+37153,
+53792,
+51425,
+44341,
+15143,
+32895,
+258,
+6580,
+27128,
+37293,
+54054,
+22895,
+48275,
+1197,
+29706,
+19128,
+58148,
+60597,
+28039,
+1387,
+61619,
+2929,
+13936,
+9307,
+25975,
+22670,
+16597,
+36947,
+38685,
+20274,
+18096,
+508,
+55495,
+6058,
+35068,
+25854,
+23316,
+62227,
+17430,
+33873,
+19505,
+15762,
+16827,
+55089,
+5120,
+37767,
+37623,
+52162,
+25321,
+62176,
+21203,
+18079,
+35499,
+13797,
+51493,
+29127,
+16383,
+5,
+54615,
+29790,
+45057,
+53054,
+37810,
+40168,
+41871,
+27174,
+15672,
+29556,
+3511,
+22562,
+4965,
+47999,
+12935,
+40455,
+64775,
+57615,
+40278,
+60346,
+31532,
+54319,
+44633,
+14624,
+30688,
+49528,
+43852,
+53917,
+30973,
+42968,
+49993,
+32565,
+55667,
+64004,
+10054,
+21725,
+50386,
+51812,
+19728,
+53473,
+47403,
+28886,
+39747,
+62275,
+22329,
+10781,
+39711,
+43782,
+4529,
+27677,
+64257,
+12555,
+31783,
+58282,
+2612,
+13294,
+21311,
+3153,
+44946,
+37232,
+4926,
+13276,
+28828,
+45733,
+41614,
+990,
+11508,
+45357,
+5493,
+29501,
+7916,
+33767,
+25606,
+27734,
+28411,
+65365,
+52124,
+9554,
+33596,
+43310,
+50202,
+9408,
+59708,
+48569,
+62614,
+13077,
+26305,
+8768,
+52459,
+26302,
+56770,
+48051,
+63990,
+64563,
+31254,
+42783,
+39511,
+45649,
+35279,
+10465,
+49088,
+65024,
+65410,
+19246,
+55694,
+39495,
+61013,
+7753,
+42371,
+26643,
+27700,
+62266,
+18179,
+33285,
+6713,
+62717,
+44847,
+21513,
+39916,
+21046,
+53749,
+22525,
+12235,
+59416,
+1361,
+50477,
+27101,
+6339,
+9232,
+14396,
+42659,
+26076,
+19976,
+8009,
+46826,
+19536,
+14999,
+15175,
+62053,
+50485,
+49292,
+41940,
+15652,
+41631,
+24283,
+53882,
+33748,
+45018,
+61408,
+50789,
+48806,
+25922,
+34993,
+32556,
+42143,
+40111,
+51946,
+34467,
+1235,
+18082,
+51958,
+31920,
+51000,
+1836,
+11346,
+40379,
+60755,
+55874,
+19909,
+32730,
+25342,
+1294,
+40639,
+62753,
+16085,
+8406,
+2441,
+51341,
+37084,
+15275,
+29007,
+35754,
+43429,
+29175,
+18189,
+55893,
+19255,
+44086,
+47594,
+19985,
+7061,
+28779,
+38196,
+23893,
+58755,
+18973,
+39947,
+8002,
+63892,
+52901,
+59739,
+46025,
+32704,
+12902,
+32513,
+20087,
+40929,
+4281,
+30137,
+873,
+45367,
+34776,
+31293,
+57582,
+5299,
+35527,
+58235,
+25530,
+3528,
+11905,
+17599,
+5486,
+52089,
+49752,
+17170,
+63393,
+7096,
+39977,
+30481,
+42135,
+36000,
+36353,
+38614,
+57142,
+3288,
+11598,
+35863,
+38904,
+20768,
+45674,
+19835,
+57744,
+37673,
+10896,
+51585,
+23851,
+40669,
+7499,
+30349,
+19593,
+16019,
+56182,
+26660,
+53729,
+39805,
+7316,
+26755,
+17463,
+39393,
+47100,
+552,
+45391,
+64292,
+39987,
+21302,
+31119,
+44885,
+46868,
+62080,
+24255,
+36490,
+626,
+27491,
+44982,
+40913,
+13003,
+56411,
+30719,
+61171,
+5286,
+6013,
+37390,
+39387,
+4981,
+32217,
+4763,
+37378,
+40250,
+7297,
+64182,
+7697,
+65223,
+52975,
+19024,
+55369,
+35667,
+25271,
+45977,
+39734,
+16417,
+59227,
+59378,
+30045,
+57221,
+35603,
+32105,
+43478,
+61114,
+60662,
+40649,
+33653,
+52438,
+34861,
+16937,
+49028,
+13638,
+21033,
+60182,
+16220,
+27099,
+50479,
+36084,
+28348,
+28584,
+53405,
+65095,
+14862,
+56631,
+40015,
+60456,
+22281,
+15822,
+37017,
+54673,
+30305,
+56609,
+45907,
+39609,
+18355,
+58101,
+50747,
+57455,
+58166,
+49505,
+8999,
+25291,
+38167,
+29550,
+33294,
+44128,
+36538,
+35355,
+53773,
+32750,
+7491,
+17295,
+57721,
+58804,
+25069,
+17576,
+28051,
+5573,
+7326,
+27604,
+46766,
+1038,
+821,
+25275,
+24532,
+59872,
+31462,
+14509,
+45904,
+59232,
+54882,
+41566,
+54730,
+4956,
+48496,
+58642,
+2235,
+12106,
+6670,
+37323,
+18545,
+59937,
+914,
+574,
+45364,
+26067,
+50627,
+51300,
+35702,
+65404,
+33266,
+5194,
+165,
+1975,
+32670,
+50078,
+8527,
+46733,
+1918,
+8334,
+8831,
+47499,
+28938,
+45147,
+40328,
+3451,
+44977,
+4738,
+16527,
+50379,
+33684,
+40689,
+20767,
+39084,
+35864,
+16626,
+59531,
+62122,
+8889,
+62169,
+49908,
+56157,
+23165,
+26325,
+37838,
+17702,
+31583,
+40972,
+60242,
+2415,
+19125,
+12861,
+19817,
+47201,
+47959,
+37488,
+32412,
+16777,
+6950,
+9354,
+26468,
+11809,
+5012,
+57524,
+1138,
+7366,
+15658,
+30725,
+28141,
+46202,
+1123,
+5890,
+21817,
+53694,
+49638,
+54486,
+46126,
+62928,
+30533,
+2626,
+39418,
+61882,
+14224,
+47676,
+23438,
+22747,
+38471,
+14462,
+60191,
+64459,
+27445,
+5505,
+62598,
+63865,
+24043,
+60460,
+29064,
+61813,
+35454,
+9637,
+18721,
+29493,
+8367,
+886,
+50613,
+51713,
+35517,
+57255,
+1568,
+4721,
+37877,
+2034,
+61609,
+5525,
+31797,
+13338,
+4472,
+1305,
+12998,
+29142,
+62709,
+26800,
+48455,
+6938,
+47338,
+25999,
+54127,
+45295,
+4725,
+57176,
+47092,
+41770,
+34948,
+2150,
+9568,
+16000,
+26079,
+36270,
+15307,
+5451,
+2321,
+55349,
+34484,
+54122,
+23307,
+15896,
+56728,
+24875,
+12660,
+58814,
+19666,
+9998,
+18812,
+23794,
+58220,
+26472,
+48075,
+9283,
+58853,
+8719,
+42337,
+18742,
+53153,
+29983,
+5179,
+42929,
+21378,
+8474,
+40777,
+12606,
+42566,
+25357,
+47165,
+64903,
+10665,
+47123,
+43232,
+51525,
+41589,
+33570,
+43529,
+15302,
+59598,
+55064,
+34841,
+45515,
+5209,
+10102,
+61581,
+24973,
+59316,
+34213,
+39899,
+31329,
+13646,
+38405,
+34708,
+53920,
+17508,
+2827,
+26721,
+17083,
+45425,
+58473,
+18531,
+65004,
+9733,
+43138,
+33343,
+48684,
+60494,
+29130,
+6343,
+28441,
+16603,
+54727,
+12667,
+14409,
+64733,
+33300,
+24062,
+30336,
+30027,
+18965,
+37545,
+1654,
+52391,
+56823,
+32683,
+22493,
+57249,
+34885,
+48144,
+36956,
+54593,
+56872,
+48889,
+53129,
+43474,
+49179,
+46899,
+31544,
+1233,
+34469,
+25769,
+6926,
+31128,
+53346,
+24447,
+36375,
+14589,
+20273,
+39351,
+36948,
+53459,
+41992,
+49858,
+30554,
+64783,
+63014,
+31834,
+45740,
+42996,
+52009,
+12215,
+51466,
+4086,
+46103,
+53758,
+64976,
+61676,
+63959,
+41185,
+6253,
+23443,
+20489,
+37209,
+51048,
+18163,
+53736,
+51803,
+50348,
+48308,
+57926,
+58192,
+22980,
+60362,
+39,
+54069,
+60560,
+42766,
+29638,
+9697,
+63585,
+57644,
+34339,
+52318,
+35199,
+35512,
+60786,
+38316,
+1318,
+35229,
+18665,
+13522,
+2051,
+55733,
+24503,
+25016,
+24338,
+50284,
+26019,
+65067,
+57014,
+41598,
+7675,
+51237,
+56,
+40243,
+54518,
+39943,
+39905,
+57141,
+39089,
+36354,
+35177,
+9344,
+2687,
+6267,
+14315,
+50344,
+22291,
+9741,
+65464,
+50064,
+54690,
+47199,
+19819,
+30068,
+61338,
+11694,
+52722,
+19881,
+90,
+54015,
+63365,
+484,
+17973,
+14677,
+8480,
+56605,
+28451,
+50264,
+12802,
+12962,
+49653,
+45501,
+28221,
+43368,
+3593,
+53594,
+55753,
+51135,
+54220,
+23671,
+27955,
+54841,
+16589,
+1514,
+6187,
+6578,
+260,
+63027,
+17737,
+54125,
+26001,
+24331,
+39759,
+59131,
+18644,
+54283,
+45556,
+1008,
+1365,
+33825,
+4680,
+43697,
+62261,
+3543,
+42478,
+45230,
+29045,
+64913,
+25248,
+49035,
+65117,
+24245,
+30699,
+42425,
+32476,
+57107,
+51566,
+61724,
+32993,
+15695,
+23521,
+62671,
+65355,
+4346,
+50733,
+58202,
+54359,
+24153,
+11774,
+25175,
+1982,
+11370,
+55521,
+14018,
+55660,
+55949,
+62578,
+63832,
+62115,
+38373,
+54811,
+29650,
+52336,
+36379,
+27947,
+54716,
+20395,
+3256,
+8090,
+65457,
+9956,
+30502,
+58682,
+57114,
+55025,
+33658,
+58620,
+56204,
+60615,
+13452,
+19609,
+64512,
+65474,
+43340,
+39847,
+21265,
+47260,
+34021,
+23816,
+6107,
+37394,
+19337,
+25944,
+54267,
+24392,
+33730,
+53486,
+30061,
+13537,
+40486,
+14461,
+38851,
+22748,
+6990,
+9038,
+42409,
+1041,
+50506,
+24101,
+46300,
+36616,
+12526,
+32092,
+9703,
+41664,
+39707,
+4010,
+28219,
+45503,
+3966,
+64332,
+14970,
+27396,
+64141,
+39793,
+45813,
+40869,
+30826,
+34567,
+51389,
+15219,
+3674,
+21811,
+49316,
+26535,
+15059,
+26342,
+59199,
+29972,
+7503,
+16918,
+61463,
+51507,
+58185,
+64379,
+53926,
+41361,
+29759,
+14520,
+9023,
+45094,
+63079,
+51401,
+44683,
+18729,
+20026,
+48397,
+29980,
+39429,
+4743,
+60468,
+7178,
+58956,
+26166,
+28245,
+5428,
+34707,
+38742,
+13647,
+16755,
+33673,
+37094,
+48936,
+1292,
+25344,
+5426,
+28247,
+39964,
+43536,
+61150,
+32381,
+61638,
+15284,
+16307,
+58662,
+50274,
+43668,
+17608,
+1914,
+5066,
+57493,
+48728,
+36462,
+52349,
+2471,
+41810,
+3103,
+60154,
+54810,
+38513,
+62116,
+5884,
+47642,
+47317,
+63243,
+54820,
+17217,
+49365,
+23665,
+26223,
+49866,
+46705,
+44545,
+2327,
+14668,
+17233,
+2469,
+52351,
+18750,
+20056,
+6755,
+43120,
+34474,
+5320,
+740,
+43427,
+35756,
+8115,
+27335,
+8500,
+44877,
+4769,
+61320,
+63284,
+1718,
+306,
+48033,
+22452,
+28597,
+32742,
+64253,
+1891,
+8349,
+22158,
+40369,
+17214,
+1421,
+61344,
+56017,
+31155,
+35645,
+22845,
+6487,
+64740,
+248,
+1317,
+38637,
+60787,
+16368,
+31158,
+5722,
+18186,
+48897,
+4696,
+22242,
+14241,
+63886,
+28877,
+54479,
+20924,
+23563,
+5455,
+49406,
+48461,
+7781,
+23691,
+43223,
+47915,
+53350,
+8138,
+25599,
+7868,
+44267,
+43907,
+6958,
+49368,
+29701,
+16062,
+65436,
+17696,
+44670,
+14540,
+28134,
+10793,
+53838,
+65073,
+20384,
+20715,
+58847,
+61227,
+26085,
+57295,
+2180,
+36751,
+16922,
+21502,
+40866,
+1401,
+27721,
+8254,
+31662,
+46034,
+10711,
+55021,
+61842,
+55656,
+19682,
+2395,
+47622,
+63171,
+6430,
+39633,
+52212,
+54948,
+1208,
+49872,
+51275,
+13397,
+61659,
+46335,
+63729,
+57160,
+17332,
+9620,
+29728,
+4626,
+61048,
+37603,
+33437,
+28340,
+2621,
+15123,
+65525,
+5959,
+22796,
+44940,
+47410,
+1111,
+62768,
+18161,
+51050,
+14700,
+24783,
+14008,
+10541,
+37631,
+9811,
+45400,
+32597,
+57466,
+62184,
+35800,
+15356,
+40210,
+31398,
+47425,
+48639,
+42084,
+18755,
+57421,
+27193,
+57038,
+4929,
+14132,
+23785,
+23892,
+39129,
+28780,
+5170,
+51039,
+53870,
+64274,
+64032,
+57128,
+50814,
+29778,
+45764,
+14834,
+24621,
+16879,
+45477,
+57945,
+7987,
+54060,
+40816,
+41696,
+43455,
+55791,
+32329,
+40205,
+53184,
+28180,
+58245,
+37565,
+29549,
+38975,
+25292,
+11021,
+56506,
+6366,
+32085,
+53967,
+15857,
+13015,
+3873,
+14905,
+48026,
+8748,
+33380,
+65324,
+29369,
+42561,
+48999,
+18021,
+53403,
+28586,
+43149,
+14085,
+21994,
+47252,
+50566,
+27087,
+1397,
+61272,
+37611,
+3174,
+29890,
+42694,
+35587,
+45025,
+54326,
+6239,
+28915,
+20619,
+33224,
+54340,
+62699,
+19937,
+10983,
+22680,
+36640,
+63431,
+36023,
+22256,
+37868,
+9625,
+32361,
+42725,
+21296,
+24010,
+62017,
+33710,
+8380,
+62285,
+36509,
+57717,
+30058,
+21117,
+37648,
+49142,
+42012,
+11989,
+18875,
+14434,
+64916,
+23996,
+53180,
+4048,
+25154,
+1077,
+26690,
+60033,
+51485,
+42632,
+41408,
+19642,
+43451,
+57962,
+1048,
+47545,
+31347,
+35134,
+55956,
+39590,
+62661,
+43358,
+24980,
+36100,
+50405,
+33482,
+44579,
+57942,
+31078,
+52097,
+36327,
+59637,
+62605,
+16525,
+4740,
+54576,
+7936,
+32914,
+16553,
+60712,
+17377,
+58399,
+40258,
+18484,
+57341,
+37044,
+15778,
+20818,
+27684,
+5796,
+19514,
+64910,
+26488,
+20556,
+41449,
+37984,
+7382,
+61721,
+34307,
+10899,
+7655,
+9597,
+63988,
+48053,
+28378,
+4077,
+2042,
+11806,
+25734,
+43492,
+4517,
+44113,
+7392,
+16844,
+63760,
+27714,
+49619,
+53721,
+42668,
+64320,
+20643,
+33833,
+3753,
+60353,
+5160,
+4255,
+31553,
+51292,
+29627,
+25043,
+44323,
+588,
+60976,
+53566,
+48494,
+4958,
+34362,
+12907,
+19787,
+17147,
+61934,
+14866,
+33574,
+13856,
+10510,
+31881,
+55299,
+20116,
+35853,
+22760,
+10001,
+29205,
+29242,
+7381,
+38043,
+41450,
+42935,
+29993,
+56621,
+47689,
+49711,
+8292,
+6173,
+31708,
+14661,
+21130,
+51752,
+46553,
+6892,
+23276,
+13783,
+18986,
+29323,
+43334,
+49353,
+7898,
+65007,
+33327,
+31686,
+44874,
+18217,
+42504,
+35191,
+37725,
+17656,
+61277,
+48519,
+21441,
+21540,
+14270,
+19973,
+16003,
+19563,
+47787,
+29378,
+45874,
+7284,
+39791,
+64143,
+18867,
+25444,
+3151,
+21313,
+6000,
+58210,
+26579,
+18772,
+3307,
+19357,
+60673,
+15014,
+63085,
+23451,
+46784,
+20352,
+50127,
+337,
+59915,
+4758,
+28948,
+39886,
+53215,
+47687,
+56623,
+16354,
+57696,
+18450,
+548,
+956,
+11232,
+8805,
+32963,
+48609,
+1933,
+21248,
+1136,
+57526,
+18713,
+18110,
+16900,
+16520,
+8268,
+54775,
+23759,
+45328,
+6484,
+28429,
+17758,
+48972,
+19416,
+1773,
+25307,
+64743,
+34629,
+60169,
+22213,
+58065,
+29458,
+4417,
+9836,
+2033,
+38827,
+4722,
+57920,
+47906,
+30435,
+25696,
+27846,
+12898,
+9624,
+38118,
+22257,
+40955,
+24799,
+56746,
+60893,
+62108,
+61007,
+26265,
+1281,
+45243,
+1816,
+50858,
+57059,
+44718,
+27487,
+59742,
+41366,
+35622,
+2251,
+63821,
+56252,
+6686,
+14898,
+41578,
+24377,
+45179,
+30627,
+52847,
+17701,
+38893,
+26326,
+3272,
+63836,
+4897,
+55864,
+51112,
+44960,
+37200,
+41371,
+16211,
+15981,
+35804,
+13771,
+1776,
+27514,
+15919,
+36034,
+57623,
+10420,
+56614,
+64135,
+27273,
+57284,
+42689,
+37111,
+63237,
+40167,
+39317,
+53055,
+15339,
+20649,
+17212,
+40371,
+39930,
+26289,
+37127,
+12020,
+62196,
+42914,
+61908,
+63569,
+43448,
+62473,
+62029,
+300,
+5662,
+17557,
+64519,
+53423,
+1965,
+5968,
+14505,
+23065,
+15432,
+6770,
+49674,
+4550,
+40337,
+15382,
+18623,
+42795,
+37503,
+22594,
+41603,
+35679,
+17401,
+30073,
+43818,
+10439,
+37622,
+39334,
+5121,
+28240,
+45012,
+5439,
+64008,
+7850,
+57541,
+63751,
+51476,
+1432,
+54013,
+92,
+58491,
+43928,
+39985,
+64294,
+19050,
+18488,
+5995,
+61002,
+65379,
+12524,
+36618,
+32561,
+48400,
+28574,
+3322,
+356,
+4774,
+39433,
+45490,
+6301,
+51738,
+14046,
+32072,
+25396,
+6380,
+47133,
+59313,
+51626,
+17655,
+37955,
+35192,
+57726,
+62408,
+62500,
+58436,
+62095,
+3734,
+8827,
+10372,
+51632,
+28730,
+29657,
+11958,
+45256,
+1187,
+54560,
+59991,
+54038,
+28677,
+55965,
+405,
+3068,
+29577,
+36811,
+21509,
+59941,
+29601,
+43237,
+18361,
+49773,
+48712,
+63228,
+39840,
+27666,
+52640,
+21950,
+8919,
+4064,
+8836,
+24074,
+36405,
+59196,
+56307,
+41330,
+16059,
+21274,
+20711,
+7497,
+40671,
+39397,
+10895,
+39079,
+57745,
+61433,
+16533,
+41030,
+10594,
+64072,
+7835,
+13090,
+13588,
+60884,
+43265,
+23884,
+28806,
+64016,
+29622,
+1897,
+43888,
+9395,
+49084,
+59734,
+36610,
+35443,
+62966,
+49141,
+38104,
+21118,
+16265,
+63044,
+22697,
+62001,
+44162,
+881,
+25339,
+11673,
+8304,
+15097,
+31311,
+11925,
+15800,
+52307,
+9810,
+38217,
+10542,
+49923,
+28935,
+41931,
+44665,
+50902,
+52161,
+39333,
+37768,
+10440,
+11381,
+17200,
+49570,
+11753,
+8727,
+15747,
+60465,
+56879,
+3173,
+38138,
+61273,
+30917,
+46077,
+48870,
+31810,
+55408,
+33436,
+38235,
+61049,
+15162,
+52766,
+49921,
+10544,
+63630,
+448,
+19268,
+64953,
+28554,
+2303,
+29157,
+27029,
+10822,
+36763,
+12139,
+48981,
+57094,
+28655,
+41865,
+26965,
+10697,
+25798,
+59454,
+34666,
+6767,
+60125,
+55897,
+14388,
+14125,
+56273,
+17986,
+24985,
+43639,
+29365,
+10537,
+29548,
+38169,
+58246,
+19752,
+5920,
+31801,
+55265,
+54914,
+46948,
+46862,
+2026,
+8181,
+64729,
+31998,
+4302,
+20151,
+51440,
+34513,
+29625,
+51294,
+1653,
+38713,
+18966,
+50884,
+13290,
+63896,
+32250,
+58826,
+56523,
+34002,
+11176,
+41386,
+22327,
+62277,
+29924,
+19926,
+16909,
+8384,
+3229,
+28468,
+1350,
+60687,
+3907,
+53145,
+36136,
+13659,
+55813,
+60267,
+6444,
+14471,
+59746,
+20794,
+1602,
+19380,
+28710,
+54385,
+19102,
+31742,
+53811,
+4903,
+13806,
+30821,
+22593,
+37776,
+42796,
+13509,
+19177,
+4939,
+26176,
+64151,
+2225,
+47137,
+57292,
+19899,
+15720,
+60548,
+8134,
+32411,
+38882,
+47960,
+26576,
+59965,
+19388,
+17439,
+21169,
+40998,
+56572,
+29092,
+63220,
+19215,
+33837,
+30732,
+50502,
+53877,
+34869,
+25511,
+25384,
+5340,
+3584,
+31956,
+9773,
+56962,
+7209,
+16208,
+40389,
+61389,
+42781,
+31256,
+19209,
+37266,
+22342,
+60223,
+24030,
+8548,
+28672,
+14563,
+32765,
+52431,
+5462,
+6899,
+52934,
+13902,
+14746,
+16733,
+52187,
+5372,
+4819,
+55925,
+58344,
+57437,
+17067,
+54084,
+17278,
+19045,
+53381,
+30017,
+20421,
+44746,
+41823,
+33799,
+51719,
+2273,
+61532,
+25584,
+13175,
+35540,
+23001,
+36274,
+44577,
+33484,
+42488,
+18879,
+49546,
+31876,
+18122,
+55380,
+12023,
+35413,
+2020,
+24549,
+59326,
+43133,
+50996,
+27257,
+54745,
+37285,
+29567,
+42276,
+47976,
+34811,
+26668,
+19336,
+38482,
+6108,
+1460,
+39386,
+39040,
+6014,
+47484,
+47324,
+21013,
+28790,
+12680,
+48416,
+22537,
+20574,
+33440,
+40249,
+39035,
+4764,
+26037,
+31954,
+3586,
+59801,
+41563,
+63515,
+40990,
+43962,
+8364,
+13696,
+36571,
+21772,
+31567,
+24185,
+28499,
+4538,
+5531,
+2477,
+12352,
+27366,
+7293,
+7216,
+10662,
+42207,
+9294,
+21245,
+13990,
+49176,
+31756,
+17592,
+60211,
+17256,
+59249,
+18775,
+8605,
+10076,
+43094,
+3298,
+46010,
+17028,
+1729,
+36482,
+64647,
+33691,
+39529,
+4101,
+19317,
+2637,
+17615,
+53101,
+4465,
+53901,
+18544,
+38938,
+6671,
+21385,
+59835,
+61526,
+27082,
+20035,
+26957,
+22170,
+30285,
+52457,
+8770,
+59665,
+23789,
+7585,
+31249,
+9906,
+31205,
+64049,
+57120,
+15443,
+39487,
+58423,
+61097,
+18574,
+60415,
+27772,
+20526,
+50711,
+54053,
+39370,
+27129,
+60110,
+27141,
+25574,
+19279,
+13982,
+29566,
+37401,
+54746,
+32235,
+41477,
+42452,
+11309,
+49852,
+40947,
+44125,
+13420,
+63072,
+16997,
+13669,
+17754,
+53645,
+23287,
+64190,
+12417,
+22341,
+37457,
+19210,
+24652,
+31540,
+58704,
+29767,
+14657,
+64997,
+12160,
+62865,
+51086,
+577,
+51138,
+49593,
+57629,
+56114,
+29862,
+45666,
+21397,
+9468,
+18889,
+15138,
+40460,
+7609,
+7347,
+51990,
+61653,
+17284,
+44620,
+21831,
+3198,
+48575,
+8637,
+4925,
+39262,
+44947,
+55438,
+16713,
+9839,
+5549,
+35698,
+32831,
+52637,
+55915,
+35811,
+51046,
+37211,
+60221,
+22344,
+32507,
+60129,
+49781,
+5521,
+12261,
+60220,
+37220,
+51047,
+38661,
+20490,
+18014,
+44831,
+15533,
+53687,
+19885,
+33600,
+41370,
+37830,
+44961,
+32099,
+27304,
+62917,
+30655,
+24494,
+36142,
+2652,
+34017,
+29452,
+26538,
+36954,
+48146,
+8130,
+35651,
+12297,
+61781,
+9897,
+45085,
+5311,
+20543,
+46758,
+61486,
+9338,
+29173,
+43431,
+53492,
+10552,
+10279,
+53253,
+63169,
+47624,
+15101,
+231,
+54521,
+55346,
+41079,
+4393,
+24775,
+17483,
+27503,
+61461,
+16920,
+36753,
+15598,
+53791,
+39379,
+52612,
+2288,
+30207,
+55543,
+51418,
+51310,
+30549,
+19572,
+16813,
+1221,
+30517,
+31146,
+39691,
+33567,
+17871,
+48292,
+14713,
+36502,
+41487,
+9430,
+9695,
+29640,
+59241,
+64778,
+12019,
+37802,
+26290,
+173,
+29003,
+24931,
+6428,
+63173,
+16725,
+54751,
+5786,
+57067,
+33021,
+36195,
+54075,
+36981,
+63236,
+37813,
+42690,
+59052,
+27645,
+47780,
+53797,
+35145,
+24617,
+40937,
+34761,
+53670,
+23800,
+41618,
+10142,
+59193,
+26813,
+48935,
+38401,
+33674,
+29968,
+1827,
+52237,
+13341,
+18662,
+8931,
+26951,
+15274,
+39143,
+51342,
+45550,
+58478,
+59586,
+48212,
+32970,
+9872,
+60752,
+53469,
+57538,
+845,
+22234,
+9147,
+62307,
+28010,
+64188,
+23289,
+122,
+50619,
+65270,
+19465,
+52135,
+25314,
+29237,
+52813,
+18380,
+21647,
+56144,
+13615,
+7784,
+826,
+11888,
+51542,
+3490,
+14813,
+50085,
+51774,
+8511,
+15777,
+38053,
+57342,
+48062,
+53465,
+64463,
+41351,
+28175,
+61000,
+5997,
+33141,
+22724,
+52843,
+12305,
+50131,
+53826,
+45709,
+45312,
+7464,
+7358,
+60533,
+8555,
+44803,
+30492,
+50403,
+36102,
+24215,
+54672,
+38989,
+15823,
+6698,
+57780,
+19123,
+2417,
+57705,
+23911,
+47671,
+29356,
+41640,
+16015,
+42758,
+53963,
+65034,
+61752,
+61364,
+61061,
+21998,
+22654,
+13794,
+33532,
+55551,
+61613,
+43787,
+65312,
+55883,
+55904,
+59217,
+32951,
+51252,
+17101,
+58667,
+583,
+27944,
+63235,
+37113,
+54076,
+49108,
+2997,
+56466,
+51643,
+63052,
+42294,
+54696,
+4880,
+32172,
+28670,
+8550,
+51796,
+51262,
+52898,
+16476,
+17136,
+27796,
+62216,
+6456,
+33820,
+59031,
+1885,
+54592,
+38704,
+48145,
+37188,
+26539,
+12133,
+27390,
+22389,
+53458,
+38684,
+39352,
+16598,
+44557,
+50893,
+11123,
+2010,
+43084,
+27203,
+32796,
+14948,
+52079,
+17660,
+3838,
+21767,
+11252,
+19983,
+47596,
+9890,
+14366,
+58951,
+5445,
+59870,
+24534,
+1585,
+48224,
+61900,
+30192,
+23348,
+48231,
+16710,
+703,
+6052,
+12344,
+33887,
+20549,
+61306,
+47984,
+60038,
+62403,
+60162,
+46233,
+20956,
+7597,
+48192,
+44011,
+33042,
+1911,
+18956,
+32709,
+61617,
+1389,
+63322,
+55444,
+42439,
+15556,
+4180,
+56086,
+13365,
+61179,
+11884,
+58965,
+21973,
+40685,
+62376,
+8442,
+27953,
+23673,
+52801,
+4823,
+48162,
+49729,
+45840,
+30610,
+46006,
+40293,
+32493,
+63628,
+10546,
+3692,
+33364,
+28567,
+56988,
+28085,
+50975,
+18351,
+60742,
+11498,
+27831,
+9573,
+41819,
+20582,
+64102,
+18419,
+48862,
+36134,
+53147,
+20235,
+16314,
+44481,
+35118,
+16326,
+47222,
+61140,
+49601,
+35523,
+29410,
+58062,
+57199,
+6730,
+51196,
+41165,
+64820,
+55561,
+5763,
+30757,
+22265,
+5614,
+60204,
+1267,
+46156,
+28025,
+11153,
+39550,
+43826,
+6603,
+23193,
+34414,
+48900,
+14384,
+31405,
+4306,
+30449,
+29752,
+65375,
+6514,
+21508,
+37701,
+29578,
+14744,
+13904,
+27823,
+35881,
+14304,
+54648,
+36062,
+64424,
+2771,
+14021,
+25740,
+4123,
+35042,
+33814,
+64536,
+34539,
+11562,
+63900,
+55403,
+33620,
+54045,
+1712,
+63319,
+20073,
+1493,
+22066,
+34250,
+49314,
+21813,
+45532,
+50936,
+35584,
+43130,
+20859,
+53326,
+55270,
+12839,
+35490,
+8318,
+62245,
+14359,
+56926,
+49835,
+3574,
+42128,
+12138,
+37588,
+10823,
+46750,
+18529,
+58475,
+26407,
+27342,
+60368,
+39554,
+15597,
+37156,
+16921,
+38269,
+2181,
+62985,
+44523,
+28152,
+52858,
+45941,
+13386,
+44208,
+33086,
+24796,
+49720,
+18627,
+36636,
+289,
+43617,
+59878,
+47341,
+52056,
+41652,
+27877,
+1522,
+50520,
+50450,
+1995,
+59004,
+44699,
+24886,
+31886,
+18848,
+59203,
+21619,
+46177,
+64475,
+33171,
+1544,
+53788,
+24675,
+21134,
+29050,
+35956,
+52260,
+26278,
+19805,
+14610,
+20356,
+34912,
+63139,
+22995,
+51200,
+56171,
+25488,
+57917,
+45298,
+45101,
+3888,
+16499,
+49543,
+62481,
+902,
+1888,
+20297,
+55248,
+8862,
+18916,
+9795,
+60701,
+17717,
+24410,
+61476,
+6248,
+16068,
+58054,
+5423,
+30945,
+13468,
+5402,
+55628,
+39814,
+46095,
+56099,
+41558,
+13719,
+17546,
+311,
+53985,
+52914,
+8210,
+23004,
+9140,
+1650,
+27232,
+11059,
+35039,
+1559,
+50120,
+21559,
+11166,
+36147,
+18133,
+26257,
+25791,
+21795,
+17155,
+57046,
+7234,
+12891,
+6147,
+5768,
+61028,
+63430,
+38122,
+22681,
+12330,
+288,
+36738,
+18628,
+24321,
+31433,
+24605,
+60837,
+47114,
+31873,
+2199,
+43881,
+52591,
+40437,
+10080,
+59297,
+27407,
+44919,
+48487,
+32560,
+37744,
+12525,
+38462,
+46301,
+20042,
+46112,
+2157,
+35442,
+37652,
+59735,
+6418,
+45006,
+11707,
+34891,
+64751,
+15613,
+27909,
+23607,
+18037,
+26622,
+20391,
+29546,
+10539,
+14010,
+23950,
+2896,
+48345,
+11110,
+60778,
+27618,
+25652,
+17266,
+33614,
+44673,
+21943,
+9776,
+33718,
+2759,
+34028,
+14559,
+40480,
+55324,
+33315,
+34176,
+53766,
+8949,
+21771,
+37366,
+13697,
+11364,
+7486,
+8273,
+54932,
+40118,
+49565,
+19111,
+60000,
+9081,
+41934,
+19330,
+40979,
+18605,
+54904,
+51379,
+46294,
+17906,
+48527,
+6636,
+48544,
+10488,
+34897,
+34039,
+29329,
+20252,
+920,
+59703,
+63010,
+51865,
+57561,
+35354,
+38971,
+44129,
+748,
+65363,
+28413,
+40607,
+53713,
+50261,
+26396,
+29784,
+61606,
+46644,
+34674,
+54005,
+16273,
+59445,
+20638,
+17037,
+11329,
+46062,
+12611,
+9817,
+45172,
+45324,
+12397,
+7946,
+16544,
+22730,
+57716,
+38108,
+62286,
+25752,
+57392,
+21109,
+6623,
+41486,
+37135,
+14714,
+25089,
+50657,
+48351,
+46325,
+40887,
+61026,
+5770,
+20306,
+26991,
+625,
+39051,
+24256,
+44402,
+28825,
+29582,
+20914,
+24906,
+64646,
+37335,
+1730,
+34265,
+23765,
+30591,
+21666,
+10394,
+47431,
+5076,
+26696,
+3725,
+15497,
+1443,
+34358,
+31214,
+62374,
+40687,
+33686,
+22691,
+52348,
+38380,
+48729,
+57903,
+10433,
+30144,
+13628,
+43952,
+18702,
+53116,
+21567,
+31314,
+2278,
+35372,
+11966,
+55527,
+14930,
+8964,
+28058,
+2318,
+651,
+8947,
+53768,
+13584,
+21435,
+33916,
+17177,
+54020,
+4844,
+18979,
+63373,
+55206,
+11148,
+4251,
+21225,
+9026,
+53508,
+14075,
+7958,
+55031,
+5909,
+63797,
+15754,
+60018,
+14479,
+20427,
+12529,
+15551,
+9175,
+55946,
+18432,
+48440,
+14043,
+26213,
+49155,
+5042,
+26811,
+59195,
+37684,
+24075,
+3390,
+52821,
+23283,
+29299,
+34163,
+13362,
+22821,
+52538,
+26719,
+2829,
+43839,
+47170,
+47771,
+6741,
+59482,
+43465,
+41204,
+25780,
+48557,
+24326,
+33870,
+7164,
+63233,
+27946,
+38509,
+52337,
+21322,
+14588,
+38688,
+24448,
+9391,
+51924,
+62317,
+35630,
+42311,
+63780,
+20416,
+43412,
+56198,
+28362,
+22107,
+26399,
+47349,
+16642,
+13425,
+52373,
+55401,
+63902,
+35176,
+38613,
+39090,
+36001,
+60927,
+2205,
+6626,
+50249,
+25072,
+59913,
+339,
+41058,
+48550,
+31095,
+22023,
+64803,
+21191,
+30781,
+50560,
+20722,
+51158,
+43627,
+55535,
+27550,
+36296,
+4858,
+58929,
+59636,
+38068,
+52098,
+8882,
+58293,
+29399,
+51880,
+2223,
+64153,
+55634,
+12980,
+14415,
+61251,
+22591,
+30823,
+32542,
+49844,
+43716,
+6810,
+32986,
+50391,
+62907,
+21963,
+9970,
+62015,
+24012,
+10776,
+40222,
+28477,
+12725,
+56912,
+4857,
+36331,
+27551,
+58157,
+50466,
+65351,
+43220,
+22435,
+45888,
+32019,
+17425,
+49197,
+48158,
+7140,
+43365,
+35254,
+64800,
+19858,
+62690,
+33184,
+22817,
+42891,
+44576,
+37419,
+23002,
+8212,
+15306,
+38800,
+26080,
+13128,
+51581,
+18326,
+22333,
+18074,
+45498,
+44498,
+15256,
+15495,
+3727,
+47554,
+59375,
+10298,
+19944,
+46424,
+5539,
+64982,
+5561,
+4019,
+18472,
+33197,
+22279,
+60458,
+24045,
+18706,
+33556,
+48707,
+54081,
+36228,
+42253,
+29137,
+31375,
+15744,
+4717,
+30797,
+39413,
+62254,
+51540,
+11890,
+42252,
+36240,
+54082,
+17069,
+50653,
+13665,
+46907,
+41218,
+33039,
+18950,
+49516,
+9357,
+16914,
+53190,
+46550,
+27571,
+22204,
+14531,
+12146,
+30231,
+31497,
+28991,
+45286,
+54298,
+32163,
+31333,
+64373,
+41983,
+46358,
+60600,
+56921,
+35162,
+26016,
+54074,
+37115,
+33022,
+45452,
+52443,
+11834,
+42906,
+6045,
+60736,
+1735,
+5475,
+56550,
+64568,
+13474,
+17865,
+28529,
+23898,
+18928,
+24950,
+33677,
+3712,
+2578,
+433,
+21897,
+27969,
+55001,
+33544,
+212,
+27385,
+22977,
+58747,
+63839,
+19581,
+3398,
+618,
+22129,
+17749,
+27593,
+19664,
+58816,
+10602,
+15540,
+33809,
+30866,
+9617,
+57845,
+55517,
+23458,
+18132,
+36653,
+11167,
+3387,
+5514,
+2651,
+37193,
+24495,
+20080,
+7243,
+29214,
+13658,
+37522,
+53146,
+36853,
+48863,
+62020,
+21157,
+53498,
+58760,
+30013,
+28695,
+7476,
+12877,
+55996,
+2110,
+684,
+4115,
+14793,
+48363,
+50021,
+31648,
+44990,
+15902,
+24105,
+8622,
+29794,
+46969,
+48101,
+14518,
+29761,
+52993,
+46843,
+23512,
+9107,
+24214,
+37020,
+50404,
+38075,
+24981,
+48048,
+64395,
+60426,
+64217,
+30310,
+47657,
+25990,
+19094,
+62528,
+61333,
+52783,
+31549,
+31519,
+28347,
+39000,
+50480,
+34959,
+4412,
+7471,
+27656,
+61121,
+48868,
+46079,
+40781,
+18300,
+55819,
+11804,
+2044,
+8109,
+12470,
+30092,
+2573,
+40306,
+43191,
+33032,
+64423,
+36803,
+54649,
+34446,
+54254,
+518,
+12376,
+23384,
+41767,
+21593,
+26129,
+7884,
+17979,
+55671,
+14364,
+9892,
+25817,
+25205,
+46815,
+26701,
+57171,
+51843,
+19586,
+24500,
+63473,
+39679,
+60043,
+26285,
+57622,
+37821,
+15920,
+20373,
+15528,
+22415,
+31065,
+51372,
+11786,
+43014,
+65215,
+22255,
+38120,
+63432,
+61769,
+31282,
+32582,
+24202,
+41842,
+31038,
+3563,
+40033,
+6888,
+11103,
+56856,
+25121,
+23258,
+34918,
+63999,
+42470,
+50107,
+24408,
+17719,
+60926,
+36352,
+39091,
+42136,
+5398,
+2101,
+58897,
+6378,
+25398,
+20406,
+45145,
+28940,
+54999,
+27971,
+27369,
+26563,
+32244,
+15149,
+13727,
+60807,
+49864,
+26225,
+62027,
+62475,
+24473,
+53935,
+64781,
+30556,
+12492,
+21740,
+51554,
+28251,
+28137,
+23262,
+63673,
+34546,
+47408,
+44942,
+35850,
+15479,
+34518,
+62468,
+27835,
+28727,
+50794,
+52259,
+36711,
+29051,
+44624,
+63479,
+64168,
+47477,
+31033,
+41356,
+7338,
+16959,
+19295,
+61194,
+33011,
+39700,
+48479,
+45251,
+11913,
+55708,
+12851,
+5595,
+27839,
+22301,
+23219,
+44858,
+59177,
+61869,
+43180,
+19264,
+65174,
+34942,
+63368,
+24396,
+32701,
+12320,
+17852,
+53178,
+23998,
+9933,
+23441,
+6255,
+1520,
+27879,
+63641,
+31023,
+27989,
+14244,
+27527,
+40495,
+65203,
+60421,
+34685,
+34111,
+11684,
+4673,
+11681,
+62200,
+22770,
+26891,
+55841,
+28406,
+6297,
+11791,
+34598,
+32134,
+57417,
+42460,
+33897,
+45375,
+10725,
+27026,
+13202,
+51416,
+55545,
+43032,
+14303,
+36806,
+27824,
+53580,
+35735,
+15085,
+63544,
+32605,
+56262,
+23823,
+22627,
+9886,
+5980,
+13676,
+31780,
+53227,
+49203,
+16625,
+38903,
+39085,
+11599,
+53521,
+56167,
+61264,
+35086,
+35787,
+41880,
+10415,
+22759,
+37990,
+20117,
+15478,
+35964,
+44943,
+53034,
+24120,
+53143,
+3909,
+54861,
+50509,
+7751,
+61015,
+43069,
+42368,
+3619,
+16168,
+27251,
+49476,
+44265,
+7870,
+64339,
+26172,
+46410,
+52678,
+33952,
+24567,
+46953,
+65076,
+57987,
+2918,
+11429,
+41072,
+59706,
+9410,
+25252,
+11394,
+33290,
+32989,
+24263,
+13271,
+51045,
+37222,
+55916,
+27299,
+60912,
+1801,
+31242,
+13770,
+37826,
+15982,
+44019,
+15355,
+38211,
+62185,
+4597,
+45455,
+58296,
+24686,
+1408,
+42514,
+29826,
+6841,
+46352,
+4589,
+41879,
+35857,
+35087,
+28722,
+163,
+5196,
+7416,
+10834,
+22883,
+24175,
+27112,
+51018,
+29429,
+12545,
+24913,
+62505,
+32283,
+6832,
+28276,
+50881,
+42628,
+8535,
+9959,
+19695,
+34929,
+16480,
+17613,
+2639,
+14722,
+27350,
+19774,
+8114,
+38346,
+43428,
+39140,
+29008,
+3932,
+31928,
+63196,
+53971,
+10921,
+26217,
+20481,
+4855,
+56914,
+29425,
+18569,
+13846,
+16784,
+11980,
+53564,
+60978,
+15084,
+35878,
+53581,
+15663,
+14264,
+47795,
+18091,
+63600,
+39506,
+13690,
+48042,
+8756,
+25656,
+7309,
+46110,
+20044,
+21050,
+58418,
+5866,
+52234,
+25192,
+13034,
+43632,
+2248,
+23022,
+29745,
+58697,
+35170,
+21292,
+34904,
+59357,
+47995,
+17961,
+65403,
+38929,
+51301,
+46579,
+32830,
+37226,
+5550,
+13779,
+48954,
+40682,
+1860,
+23314,
+25856,
+5496,
+52185,
+16735,
+33212,
+63695,
+5056,
+59434,
+52596,
+61623,
+39896,
+17400,
+37773,
+41604,
+14545,
+9422,
+28287,
+19872,
+57775,
+21872,
+31171,
+55768,
+52703,
+25270,
+39026,
+55370,
+44438,
+56042,
+40846,
+48595,
+43967,
+53615,
+16098,
+45090,
+15281,
+39674,
+63346,
+40610,
+56517,
+12296,
+37185,
+8131,
+42239,
+62362,
+27401,
+22844,
+38322,
+31156,
+16370,
+11130,
+47068,
+30218,
+39498,
+33585,
+58363,
+47950,
+2755,
+48155,
+23737,
+62802,
+42310,
+36370,
+62318,
+45437,
+9744,
+33210,
+16737,
+23020,
+2250,
+37850,
+41367,
+22765,
+8024,
+43676,
+12194,
+2946,
+39808,
+3259,
+28005,
+45612,
+23010,
+1257,
+34124,
+22515,
+63713,
+21607,
+25554,
+32104,
+39017,
+57222,
+53170,
+35112,
+602,
+57604,
+13543,
+2793,
+19071,
+43989,
+56395,
+63889,
+43374,
+64364,
+63469,
+45024,
+38134,
+42695,
+43129,
+36778,
+50937,
+42383,
+15391,
+64861,
+56023,
+2426,
+40965,
+54869,
+44532,
+50458,
+39411,
+30799,
+40585,
+31862,
+28445,
+63711,
+22517,
+6337,
+27103,
+58035,
+42507,
+44880,
+33807,
+15542,
+32974,
+17139,
+27122,
+26109,
+12383,
+26763,
+60359,
+31915,
+11494,
+31290,
+6965,
+40296,
+58909,
+15290,
+22601,
+27556,
+8917,
+21952,
+23000,
+37421,
+13176,
+6969,
+57950,
+34290,
+5188,
+9183,
+43395,
+14573,
+12111,
+62167,
+8891,
+58234,
+39106,
+5300,
+6776,
+29409,
+36843,
+49602,
+12155,
+28106,
+45117,
+57254,
+38831,
+51714,
+12969,
+14215,
+60785,
+38639,
+35200,
+26822,
+46573,
+18636,
+33995,
+6835,
+57993,
+3989,
+51133,
+55755,
+33530,
+13796,
+39327,
+18080,
+1237,
+11065,
+1298,
+12059,
+6158,
+26518,
+8317,
+36772,
+12840,
+9989,
+63524,
+25636,
+34210,
+14903,
+3875,
+56492,
+50690,
+7819,
+27431,
+44421,
+12050,
+27062,
+52001,
+19282,
+521,
+31514,
+41723,
+45717,
+26938,
+4200,
+18737,
+2138,
+48135,
+27766,
+21720,
+42933,
+41452,
+54324,
+45027,
+49832,
+32348,
+57258,
+9636,
+38839,
+61814,
+21113,
+48997,
+42563,
+5330,
+9172,
+3609,
+53066,
+29469,
+62965,
+37651,
+36611,
+2158,
+63231,
+7166,
+50770,
+34678,
+44973,
+57414,
+18342,
+19591,
+30351,
+31027,
+23516,
+1478,
+3413,
+56650,
+20851,
+17479,
+8823,
+50810,
+56695,
+52269,
+32441,
+13550,
+12677,
+19598,
+53296,
+681,
+2019,
+37409,
+12024,
+56807,
+60823,
+62298,
+43861,
+385,
+15791,
+16406,
+19446,
+52651,
+51688,
+61255,
+26424,
+64665,
+25659,
+32675,
+20902,
+50915,
+55103,
+29079,
+51910,
+32125,
+65487,
+2676,
+33100,
+23752,
+49400,
+65274,
+44276,
+21971,
+58967,
+19705,
+61597,
+17787,
+44202,
+55035,
+33378,
+8750,
+41176,
+11965,
+36450,
+2279,
+23945,
+47669,
+23913,
+14830,
+328,
+997,
+52470,
+18364,
+3045,
+58029,
+55219,
+1227,
+12009,
+1709,
+53772,
+38970,
+36539,
+57562,
+2968,
+20949,
+15245,
+8508,
+55171,
+22049,
+3636,
+28616,
+42190,
+13950,
+41688,
+49882,
+50169,
+44197,
+42212,
+8956,
+4735,
+14185,
+22033,
+65422,
+45417,
+63248,
+28387,
+9995,
+44643,
+666,
+61706,
+30237,
+58882,
+34109,
+34687,
+43533,
+18468,
+29897,
+26040,
+4526,
+32299,
+61831,
+30638,
+31500,
+18977,
+4846,
+18863,
+53090,
+58582,
+53390,
+29327,
+34041,
+17318,
+54250,
+49447,
+3830,
+30212,
+6656,
+64967,
+53329,
+61855,
+31906,
+23926,
+64226,
+8856,
+13653,
+53620,
+32775,
+4370,
+44472,
+57545,
+59469,
+10760,
+41780,
+26074,
+42661,
+10464,
+39223,
+45650,
+31939,
+22229,
+49509,
+23620,
+19037,
+49961,
+50819,
+48429,
+51279,
+24070,
+48015,
+57481,
+48631,
+3808,
+56092,
+46600,
+14146,
+50320,
+12539,
+34144,
+53778,
+60215,
+64799,
+36282,
+43366,
+28223,
+13081,
+65022,
+49090,
+3185,
+31453,
+43925,
+18154,
+13621,
+49262,
+6094,
+16646,
+12410,
+14776,
+9461,
+58674,
+39455,
+52411,
+20109,
+10863,
+26550,
+8929,
+18664,
+38635,
+1319,
+29443,
+17881,
+4322,
+41009,
+49610,
+15840,
+19454,
+9477,
+8264,
+33725,
+10131,
+51961,
+35012,
+54603,
+13968,
+31233,
+6386,
+17260,
+64342,
+43857,
+54921,
+53048,
+43911,
+65241,
+40434,
+41474,
+26821,
+35511,
+38640,
+52319,
+7057,
+5953,
+22863,
+62835,
+57725,
+37724,
+37956,
+42505,
+58037,
+26464,
+45945,
+30105,
+34511,
+51442,
+32816,
+48647,
+54956,
+8221,
+33455,
+9343,
+38612,
+36355,
+63903,
+47719,
+34765,
+64160,
+21291,
+35709,
+58698,
+59337,
+49648,
+57350,
+44138,
+56071,
+26015,
+36198,
+56922,
+50314,
+19328,
+41936,
+25902,
+13327,
+714,
+14025,
+11145,
+19481,
+52072,
+12535,
+7158,
+57894,
+54295,
+24616,
+37105,
+53798,
+32803,
+33231,
+48217,
+11289,
+43613,
+33052,
+24419,
+5135,
+55955,
+38081,
+31348,
+43146,
+12081,
+50301,
+12119,
+49927,
+6933,
+17458,
+6480,
+11630,
+6402,
+46838,
+12202,
+48132,
+16325,
+36848,
+44482,
+32259,
+41308,
+51664,
+601,
+35600,
+53171,
+49807,
+21855,
+2115,
+24438,
+14313,
+6269,
+2750,
+17630,
+27664,
+39842,
+61939,
+46316,
+21916,
+30932,
+3393,
+55748,
+155,
+42432,
+18334,
+17580,
+6860,
+61230,
+28721,
+35786,
+35858,
+61265,
+14916,
+269,
+56557,
+59458,
+9328,
+42292,
+63054,
+20412,
+45883,
+50165,
+59070,
+61129,
+42577,
+63203,
+45846,
+25853,
+39345,
+6059,
+34316,
+17565,
+20756,
+6041,
+62208,
+60477,
+286,
+12332,
+458,
+25559,
+26444,
+23403,
+54239,
+55525,
+11968,
+50190,
+40431,
+55062,
+59600,
+47862,
+20733,
+28515,
+15135,
+33813,
+36797,
+4124,
+1558,
+36658,
+11060,
+57000,
+55580,
+27036,
+6856,
+13009,
+64596,
+52082,
+50033,
+44938,
+22798,
+10519,
+40077,
+14638,
+58084,
+57790,
+46459,
+64315,
+28395,
+34392,
+1413,
+53441,
+52138,
+3880,
+59729,
+54602,
+35215,
+51962,
+32615,
+62100,
+46369,
+8344,
+21543,
+2608,
+26678,
+62912,
+44733,
+9115,
+25228,
+5612,
+22267,
+44951,
+24729,
+40635,
+32555,
+39169,
+25923,
+52061,
+47674,
+14226,
+28391,
+45966,
+59567,
+51034,
+15678,
+26857,
+755,
+29562,
+53046,
+54923,
+47488,
+10223,
+33340,
+14940,
+4383,
+12932,
+45922,
+1949,
+774,
+46342,
+13751,
+57384,
+18580,
+8787,
+12388,
+45304,
+54288,
+18174,
+4411,
+36082,
+50481,
+47764,
+10426,
+54237,
+23405,
+15,
+4097,
+54967,
+47016,
+2149,
+38805,
+41771,
+29059,
+43872,
+482,
+63367,
+35927,
+65175,
+59184,
+21121,
+1450,
+272,
+61697,
+43772,
+63933,
+46159,
+20947,
+2970,
+16479,
+35764,
+19696,
+28662,
+19532,
+14633,
+13378,
+13369,
+60896,
+62812,
+40300,
+63998,
+36008,
+23259,
+17563,
+34318,
+3467,
+63138,
+36705,
+20357,
+27696,
+12691,
+9404,
+45525,
+16802,
+59356,
+35707,
+21293,
+39442,
+54796,
+3705,
+30223,
+34038,
+36548,
+10489,
+57185,
+7323,
+45825,
+64750,
+36605,
+11708,
+20800,
+45191,
+24006,
+48143,
+38706,
+57250,
+2619,
+28342,
+41044,
+10584,
+16458,
+51510,
+54394,
+42463,
+40352,
+42052,
+5948,
+47482,
+6016,
+25510,
+37472,
+53878,
+52389,
+1656,
+54424,
+3581,
+7228,
+16936,
+39009,
+52439,
+46633,
+31114,
+55213,
+23091,
+57072,
+10276,
+49251,
+24598,
+30778,
+50912,
+26240,
+16010,
+49472,
+20363,
+20215,
+44586,
+50294,
+45514,
+38753,
+55065,
+41291,
+26997,
+23113,
+44077,
+32713,
+42089,
+52728,
+21978,
+3114,
+8059,
+43527,
+33572,
+14868,
+6705,
+46432,
+2742,
+12664,
+41569,
+15582,
+32473,
+9125,
+26493,
+4367,
+3857,
+20063,
+62301,
+49878,
+26667,
+37397,
+47977,
+57379,
+57375,
+10529,
+31699,
+28062,
+15036,
+22098,
+18886,
+17670,
+17840,
+57710,
+52717,
+11954,
+49876,
+62303,
+56720,
+50306,
+40825,
+22942,
+20765,
+40691,
+48094,
+16575,
+13416,
+21889,
+14291,
+12460,
+14175,
+59773,
+28704,
+43273,
+6963,
+31292,
+39110,
+45368,
+56436,
+13879,
+45485,
+56226,
+15537,
+25422,
+62161,
+63635,
+64159,
+35173,
+47720,
+5471,
+53669,
+37102,
+40938,
+30686,
+14626,
+44345,
+29199,
+14978,
+19029,
+33107,
+63125,
+25471,
+47098,
+39395,
+40673,
+45331,
+15593,
+16463,
+3699,
+20778,
+7804,
+60819,
+29303,
+26125,
+29965,
+24953,
+12258,
+63854,
+58018,
+61467,
+54489,
+19970,
+43502,
+117,
+62761,
+59681,
+25880,
+51284,
+40150,
+6153,
+9511,
+12520,
+59257,
+168,
+54292,
+51730,
+28032,
+42945,
+29223,
+32996,
+24667,
+27092,
+30971,
+53919,
+38741,
+38406,
+5429,
+10779,
+22331,
+18328,
+4542,
+780,
+31301,
+53740,
+31775,
+4095,
+17,
+61897,
+63665,
+64277,
+61738,
+23624,
+47085,
+64991,
+43532,
+35322,
+34110,
+35906,
+60422,
+33359,
+43600,
+61185,
+65297,
+44972,
+35437,
+50771,
+62462,
+54004,
+36526,
+46645,
+31727,
+29384,
+55921,
+2707,
+61690,
+6766,
+37578,
+59455,
+19120,
+61922,
+61349,
+57070,
+23093,
+65433,
+45177,
+24379,
+53315,
+62063,
+23740,
+4208,
+2975,
+14117,
+14229,
+45794,
+16284,
+40941,
+45745,
+10495,
+54355,
+31606,
+48921,
+65467,
+15198,
+43849,
+59893,
+33356,
+17652,
+58655,
+17021,
+18524,
+6981,
+7407,
+60168,
+37885,
+64744,
+16468,
+14307,
+5154,
+1819,
+61685,
+937,
+4263,
+27928,
+19461,
+3951,
+49104,
+54558,
+1189,
+21094,
+5152,
+14309,
+64132,
+24414,
+1727,
+17030,
+65234,
+43620,
+30440,
+62145,
+12718,
+44716,
+57061,
+24762,
+32133,
+35894,
+11792,
+53362,
+3624,
+40191,
+60113,
+28860,
+52070,
+19483,
+65501,
+3746,
+57230,
+44100,
+31623,
+5126,
+55478,
+55093,
+14843,
+41189,
+14787,
+6139,
+15510,
+61748,
+21368,
+44502,
+5357,
+31106,
+64828,
+55078,
+43661,
+51388,
+38444,
+30827,
+11619,
+26244,
+22570,
+54063,
+63910,
+10682,
+4191,
+9522,
+41224,
+18717,
+34135,
+59811,
+44383,
+1231,
+31546,
+6493,
+25140,
+42077,
+47407,
+35967,
+63674,
+12144,
+14533,
+62359,
+64816,
+11561,
+36794,
+64537,
+20141,
+42752,
+43444,
+49575,
+23808,
+51062,
+55237,
+45595,
+17515,
+63656,
+58810,
+54089,
+22965,
+13085,
+6126,
+64939,
+22176,
+17605,
+62467,
+35962,
+15480,
+22802,
+1895,
+29624,
+37549,
+51441,
+35185,
+30106,
+42022,
+22078,
+48592,
+24442,
+18059,
+29587,
+24182,
+4291,
+22432,
+23694,
+29521,
+61975,
+52968,
+64323,
+59919,
+12287,
+21852,
+56601,
+63939,
+14200,
+58071,
+58181,
+61056,
+48671,
+54121,
+38795,
+55350,
+33696,
+14753,
+3408,
+54330,
+24804,
+4910,
+53274,
+5319,
+38350,
+43121,
+29508,
+14166,
+25768,
+38694,
+1234,
+39164,
+51947,
+4655,
+40103,
+48152,
+6753,
+20058,
+7594,
+27467,
+13441,
+14691,
+3806,
+48633,
+41313,
+47757,
+31260,
+16282,
+45796,
+52414,
+61947,
+54253,
+36060,
+54650,
+56345,
+16986,
+29195,
+43515,
+20310,
+55474,
+62848,
+59272,
+24682,
+47329,
+19183,
+60950,
+60179,
+30965,
+710,
+57150,
+51932,
+44317,
+20185,
+14322,
+18903,
+30679,
+10325,
+52965,
+42104,
+44234,
+26481,
+20653,
+4694,
+48899,
+36821,
+23194,
+48749,
+55083,
+58610,
+26846,
+12192,
+43678,
+58453,
+62133,
+52654,
+42161,
+57553,
+23505,
+51578,
+20881,
+5691,
+16178,
+40792,
+48775,
+58058,
+1412,
+35019,
+28396,
+25847,
+31626,
+60256,
+25409,
+31320,
+17636,
+47241,
+9519,
+27214,
+29893,
+49168,
+27224,
+59816,
+63785,
+21934,
+44752,
+40208,
+15358,
+47288,
+49301,
+42375,
+9418,
+31611,
+43664,
+29866,
+9770,
+24543,
+12906,
+38002,
+4959,
+21578,
+31213,
+36469,
+1444,
+59778,
+9687,
+50987,
+42871,
+49727,
+48164,
+58140,
+57306,
+14709,
+2242,
+40527,
+12991,
+10337,
+47160,
+31696,
+14191,
+52317,
+38642,
+57645,
+20432,
+10996,
+10752,
+39812,
+55630,
+28231,
+1489,
+25644,
+32758,
+20697,
+44238,
+16116,
+43958,
+31178,
+29069,
+3164,
+24083,
+11215,
+3466,
+34915,
+17564,
+35066,
+6060,
+63862,
+33161,
+61291,
+14467,
+18324,
+51583,
+10898,
+38040,
+61722,
+51568,
+30325,
+59152,
+812,
+44176,
+7018,
+16593,
+16561,
+31986,
+63735,
+29731,
+51768,
+25815,
+9894,
+5187,
+35536,
+57951,
+28229,
+55632,
+64155,
+15709,
+973,
+26310,
+22755,
+28078,
+46329,
+52217,
+17779,
+31085,
+49256,
+57837,
+46666,
+58894,
+18482,
+40260,
+41007,
+4324,
+1486,
+8419,
+23764,
+36480,
+1731,
+41813,
+5924,
+56667,
+22827,
+53885,
+65493,
+25911,
+3767,
+29517,
+32956,
+41896,
+49589,
+49313,
+36783,
+22067,
+54042,
+29987,
+58573,
+30760,
+26428,
+7956,
+14077,
+24390,
+54269,
+15933,
+53384,
+64756,
+30837,
+11798,
+32489,
+13179,
+16972,
+44722,
+52600,
+25084,
+22856,
+50439,
+27901,
+53613,
+43969,
+29085,
+63260,
+13333,
+9256,
+50175,
+40127,
+31151,
+47902,
+17398,
+39898,
+38746,
+59317,
+14902,
+35485,
+25637,
+26793,
+51892,
+44360,
+33373,
+29333,
+1165,
+18493,
+366,
+58395,
+22174,
+64941,
+23352,
+23552,
+19181,
+47331,
+13112,
+58516,
+17991,
+27455,
+30404,
+22392,
+25665,
+20135,
+32942,
+20094,
+7251,
+11939,
+48045,
+64028,
+52109,
+41382,
+53765,
+36575,
+33316,
+44927,
+58631,
+16250,
+23855,
+33014,
+20473,
+22712,
+51408,
+19368,
+5655,
+13361,
+36399,
+29300,
+49794,
+33369,
+16072,
+25163,
+65301,
+11714,
+45779,
+8065,
+54626,
+13619,
+18156,
+61647,
+49040,
+7625,
+13502,
+5733,
+53777,
+35258,
+12540,
+52463,
+382,
+25326,
+27329,
+18113,
+6944,
+59810,
+34555,
+18718,
+51152,
+28719,
+61232,
+42865,
+48942,
+48978,
+17729,
+43112,
+22514,
+35609,
+1258,
+3801,
+33459,
+19809,
+15626,
+12918,
+65051,
+47566,
+41712,
+42912,
+62198,
+11683,
+35905,
+34686,
+35323,
+58883,
+661,
+65439,
+41215,
+28904,
+40933,
+43077,
+10722,
+47227,
+55587,
+16489,
+46791,
+49270,
+2495,
+6749,
+33721,
+55881,
+65314,
+25094,
+4566,
+5985,
+9121,
+46535,
+62351,
+30290,
+21613,
+13297,
+61068,
+3448,
+10927,
+4229,
+17555,
+5664,
+26586,
+51029,
+53354,
+43088,
+40404,
+53905,
+59137,
+933,
+24630,
+4950,
+39580,
+64677,
+54399,
+65385,
+21702,
+17304,
+48829,
+864,
+7426,
+51437,
+6597,
+55774,
+16684,
+32143,
+49849,
+7262,
+47419,
+3944,
+63179,
+5284,
+61173,
+42030,
+13961,
+17317,
+35305,
+29328,
+36547,
+34898,
+30224,
+46561,
+22458,
+54817,
+48926,
+57990,
+33638,
+53718,
+14558,
+36580,
+2760,
+24521,
+51376,
+65015,
+30065,
+23815,
+38485,
+47261,
+33987,
+29451,
+37191,
+2653,
+55784,
+63327,
+45868,
+20897,
+57124,
+60918,
+51616,
+20938,
+9181,
+5190,
+26236,
+11219,
+11175,
+37537,
+56524,
+25970,
+42635,
+40884,
+28274,
+6834,
+35507,
+18637,
+26841,
+64305,
+30986,
+59045,
+12753,
+29450,
+34019,
+47262,
+61281,
+27525,
+14246,
+16391,
+46511,
+10170,
+22360,
+2926,
+8487,
+12239,
+64858,
+14597,
+21492,
+49990,
+43764,
+28173,
+41353,
+61248,
+19549,
+10973,
+26045,
+39991,
+42466,
+19878,
+48665,
+14576,
+63335,
+20606,
+65344,
+43351,
+47834,
+26646,
+24566,
+35828,
+52679,
+32089,
+20430,
+57647,
+54971,
+55129,
+39662,
+17116,
+64979,
+6590,
+16517,
+64558,
+59574,
+2914,
+46720,
+45918,
+39384,
+1462,
+2420,
+11181,
+30894,
+16617,
+12057,
+1300,
+56118,
+31169,
+21874,
+14151,
+55688,
+61219,
+51207,
+9647,
+56850,
+50742,
+17176,
+36438,
+21436,
+30952,
+60412,
+39689,
+31148,
+5282,
+63181,
+5205,
+62630,
+20050,
+9313,
+7734,
+18370,
+636,
+45167,
+10194,
+53247,
+45374,
+35890,
+42461,
+54396,
+10161,
+46042,
+21729,
+47029,
+15515,
+29420,
+20548,
+36914,
+12345,
+18989,
+47706,
+23845,
+43554,
+9455,
+7105,
+48688,
+62111,
+25393,
+57282,
+27275,
+19504,
+39340,
+17431,
+7163,
+36383,
+24327,
+7719,
+33559,
+32873,
+1554,
+59764,
+23652,
+43092,
+10078,
+40439,
+7963,
+49055,
+47926,
+46584,
+23042,
+64571,
+59222,
+32209,
+27577,
+20664,
+9826,
+60923,
+7687,
+43519,
+16194,
+19134,
+4361,
+18376,
+31195,
+51347,
+55007,
+30731,
+37476,
+19216,
+59945,
+3752,
+38017,
+20644,
+44596,
+59363,
+27562,
+50877,
+33496,
+4679,
+38553,
+1366,
+46457,
+57792,
+59030,
+36960,
+6457,
+18765,
+59849,
+42732,
+64535,
+36796,
+35043,
+15136,
+18891,
+30865,
+36154,
+15541,
+35561,
+44881,
+41668,
+32278,
+2344,
+41028,
+16535,
+51718,
+37427,
+41824,
+2740,
+46434,
+28028,
+11727,
+23253,
+61567,
+16216,
+23610,
+8156,
+8164,
+42887,
+57679,
+10219,
+33430,
+22061,
+16360,
+28189,
+47946,
+49096,
+5291,
+4224,
+20523,
+60100,
+20629,
+22248,
+6988,
+22750,
+56441,
+19291,
+25605,
+39250,
+7917,
+59322,
+64747,
+11796,
+30839,
+61443,
+22738,
+50352,
+51860,
+29669,
+12311,
+52981,
+26268,
+7256,
+44180,
+10039,
+6744,
+45017,
+39175,
+53883,
+22829,
+46141,
+15788,
+4405,
+25481,
+60011,
+26714,
+52200,
+43635,
+59616,
+27976,
+10273,
+2240,
+14711,
+48294,
+53485,
+38477,
+24393,
+16666,
+27931,
+10130,
+35218,
+8265,
+57332,
+55880,
+34093,
+6750,
+2758,
+36582,
+9777,
+3493,
+2008,
+11125,
+40592,
+1806,
+8379,
+38111,
+62018,
+48865,
+44797,
+46173,
+7278,
+2522,
+26860,
+19798,
+19011,
+61984,
+46626,
+15829,
+14752,
+34482,
+55351,
+2266,
+319,
+39528,
+37333,
+64648,
+55427,
+51781,
+22690,
+36465,
+40688,
+38907,
+50380,
+8645,
+20505,
+22325,
+41388,
+3711,
+36177,
+24951,
+29967,
+37093,
+38402,
+16756,
+15649,
+1563,
+6077,
+56211,
+17761,
+58819,
+18422,
+28908,
+63339,
+15990,
+28124,
+47416,
+58619,
+38497,
+55026,
+27544,
+10239,
+52437,
+39011,
+40650,
+28814,
+46690,
+51395,
+1155,
+58338,
+52691,
+11440,
+21671,
+24656,
+8028,
+17126,
+376,
+53717,
+34031,
+57991,
+6837,
+6202,
+2991,
+9135,
+3681,
+30242,
+41612,
+45735,
+610,
+61033,
+18572,
+61099,
+42555,
+5177,
+29985,
+54044,
+36790,
+55404,
+13578,
+26377,
+14538,
+44672,
+36586,
+17267,
+19968,
+54491,
+61604,
+29786,
+2342,
+32280,
+62446,
+5876,
+17453,
+18809,
+22763,
+41369,
+37202,
+19886,
+30260,
+43309,
+39243,
+9555,
+54462,
+18282,
+44328,
+52828,
+32589,
+55992,
+22792,
+57490,
+58362,
+35638,
+39499,
+28162,
+61952,
+28070,
+55765,
+40996,
+21171,
+57431,
+48331,
+13855,
+37996,
+14867,
+34828,
+43528,
+38758,
+41590,
+17870,
+37139,
+39692,
+60271,
+2142,
+58443,
+21474,
+8311,
+32872,
+33867,
+7720,
+48706,
+36243,
+18707,
+19440,
+9440,
+48975,
+31240,
+1803,
+18347,
+7777,
+8250,
+53989,
+211,
+36170,
+55002,
+52616,
+42037,
+807,
+27984,
+61236,
+46136,
+58535,
+52498,
+52695,
+55550,
+36996,
+13795,
+35501,
+55756,
+12512,
+45405,
+12372,
+33180,
+15571,
+62714,
+3740,
+54694,
+42296,
+19648,
+29249,
+48113,
+24812,
+8033,
+7824,
+3125,
+16836,
+8650,
+51970,
+11948,
+6824,
+47034,
+23149,
+11390,
+44820,
+6692,
+7952,
+55740,
+4838,
+13233,
+12218,
+4678,
+33827,
+50878,
+542,
+42244,
+63363,
+54017,
+7403,
+49437,
+20449,
+40134,
+57362,
+42487,
+37417,
+44578,
+38073,
+50406,
+63879,
+11115,
+44107,
+21969,
+44278,
+4499,
+50581,
+19489,
+49374,
+61852,
+17844,
+10993,
+26093,
+10743,
+51490,
+27805,
+40142,
+8257,
+13183,
+61089,
+19808,
+34121,
+3802,
+60730,
+9342,
+35179,
+8222,
+59170,
+27375,
+11571,
+18560,
+12857,
+31587,
+45108,
+53010,
+27077,
+55835,
+56938,
+56536,
+40248,
+37380,
+20575,
+28339,
+38234,
+37604,
+55409,
+39982,
+29933,
+26521,
+22060,
+33784,
+10220,
+4919,
+11097,
+51383,
+24358,
+60488,
+32393,
+351,
+12103,
+18459,
+46697,
+24116,
+22132,
+15739,
+50686,
+32337,
+13626,
+30146,
+51,
+61757,
+10181,
+40429,
+50192,
+52481,
+40774,
+30938,
+29644,
+8121,
+47197,
+54692,
+3742,
+7600,
+5683,
+42351,
+48852,
+31485,
+15689,
+473,
+52956,
+55572,
+49430,
+49404,
+5457,
+12789,
+11853,
+3567,
+42927,
+5181,
+65323,
+38154,
+8749,
+35376,
+55036,
+25136,
+11238,
+29332,
+34205,
+44361,
+54170,
+16071,
+34160,
+49795,
+13195,
+60656,
+28566,
+36868,
+3693,
+9883,
+42364,
+43599,
+34683,
+60423,
+17651,
+34637,
+59894,
+3358,
+63743,
+56327,
+2869,
+47110,
+24781,
+14702,
+23224,
+1759,
+54851,
+48683,
+38729,
+43139,
+14939,
+34976,
+10224,
+14730,
+49513,
+42498,
+3702,
+13965,
+20259,
+16864,
+11418,
+12197,
+6314,
+31685,
+37961,
+65008,
+57470,
+10913,
+49463,
+40376,
+60555,
+26500,
+60775,
+61959,
+44926,
+34175,
+36576,
+55325,
+41585,
+62643,
+56839,
+4236,
+12004,
+10690,
+10649,
+24142,
+48374,
+3182,
+10790,
+28254,
+24061,
+38718,
+64734,
+4332,
+21887,
+13418,
+44127,
+38973,
+29551,
+50389,
+32988,
+35816,
+11395,
+1640,
+27997,
+6712,
+39208,
+18180,
+52799,
+23675,
+54092,
+46382,
+21054,
+30421,
+24230,
+15561,
+19150,
+47127,
+2728,
+25577,
+16694,
+45608,
+57484,
+25257,
+5193,
+38927,
+65405,
+23014,
+17693,
+664,
+44645,
+23868,
+31733,
+63194,
+31930,
+3092,
+3031,
+29765,
+58706,
+19451,
+12632,
+50701,
+40918,
+24602,
+22462,
+1098,
+63749,
+57543,
+44474,
+25675,
+60405,
+21753,
+61010,
+30221,
+3707,
+6028,
+2599,
+16965,
+22363,
+48216,
+35142,
+32804,
+53867,
+40506,
+40237,
+1146,
+54339,
+38128,
+20620,
+15752,
+63799,
+3774,
+59427,
+64079,
+63199,
+42178,
+45575,
+58108,
+63694,
+35687,
+16736,
+35626,
+9745,
+27363,
+25333,
+52130,
+12687,
+14221,
+63727,
+46337,
+14850,
+32123,
+51912,
+22278,
+36248,
+18473,
+11832,
+52445,
+51951,
+44104,
+8609,
+15704,
+30080,
+8280,
+63971,
+49918,
+22816,
+36278,
+62691,
+23774,
+15570,
+33525,
+12373,
+19285,
+55911,
+27421,
+22813,
+52769,
+49173,
+1543,
+36717,
+64476,
+18982,
+58647,
+54436,
+14921,
+61818,
+12428,
+41947,
+61290,
+34313,
+63863,
+62600,
+9086,
+4386,
+27145,
+3900,
+85,
+52583,
+39476,
+43777,
+50162,
+15846,
+56816,
+23202,
+4427,
+5048,
+32114,
+65187,
+22723,
+37035,
+5998,
+21315,
+10642,
+58512,
+21489,
+23157,
+4662,
+63739,
+23889,
+8678,
+46592,
+61992,
+23061,
+57402,
+28048,
+26656,
+48761,
+61932,
+17149,
+14037,
+8297,
+45343,
+48473,
+44851,
+56673,
+46623,
+59467,
+57547,
+46744,
+8228,
+4222,
+5293,
+63124,
+34753,
+19030,
+15873,
+49226,
+20964,
+57873,
+23751,
+35388,
+2677,
+23470,
+13267,
+30117,
+51988,
+7349,
+10906,
+25526,
+18391,
+44133,
+49894,
+18256,
+24795,
+36742,
+44209,
+6024,
+5418,
+54980,
+7445,
+15043,
+54878,
+44256,
+6798,
+46462,
+24269,
+56527,
+25935,
+58627,
+9316,
+49669,
+4689,
+53283,
+21187,
+18835,
+49954,
+30717,
+56413,
+23111,
+26999,
+8431,
+1128,
+42899,
+8973,
+25189,
+1830,
+52223,
+24418,
+35138,
+43614,
+51801,
+53738,
+31303,
+52359,
+58570,
+25243,
+28665,
+1910,
+36902,
+44012,
+18949,
+36221,
+41219,
+48451,
+65047,
+53610,
+49739,
+64422,
+36064,
+43192,
+28320,
+5409,
+24750,
+42973,
+60574,
+52634,
+26420,
+45451,
+36194,
+37116,
+57068,
+61351,
+13265,
+23472,
+25950,
+20472,
+34170,
+23856,
+39699,
+35944,
+61195,
+22209,
+54865,
+46714,
+20144,
+1247,
+13497,
+17048,
+32979,
+23396,
+24771,
+59424,
+51116,
+24666,
+34713,
+29224,
+15694,
+38534,
+61725,
+47275,
+24262,
+35815,
+33291,
+50390,
+36309,
+6811,
+20984,
+24901,
+30543,
+26369,
+23395,
+33002,
+17049,
+28918,
+27794,
+17138,
+35559,
+15543,
+26247,
+9871,
+37078,
+48213,
+39585,
+45067,
+61555,
+50195,
+48608,
+37907,
+8806,
+5277,
+50650,
+39391,
+17465,
+41895,
+34254,
+29518,
+41336,
+55591,
+51251,
+36988,
+59218,
+12708,
+31947,
+9546,
+6646,
+12996,
+1307,
+20093,
+34185,
+20136,
+27323,
+8072,
+7153,
+10307,
+50082,
+10234,
+65141,
+1992,
+29662,
+9276,
+45044,
+21029,
+1849,
+41555,
+50141,
+25126,
+7797,
+13574,
+30527,
+3438,
+61860,
+11322,
+58523,
+43250,
+50208,
+16552,
+38061,
+7937,
+24662,
+55419,
+24779,
+47112,
+60839,
+43297,
+46710,
+57855,
+2902,
+4629,
+56946,
+44763,
+60304,
+60515,
+13158,
+255,
+257,
+39374,
+15144,
+2632,
+63945,
+64467,
+60143,
+8702,
+41132,
+15209,
+42111,
+14345,
+8795,
+9158,
+15879,
+57090,
+62892,
+40627,
+39801,
+22047,
+55173,
+54768,
+1553,
+33866,
+33560,
+8312,
+12268,
+17933,
+63561,
+26614,
+15460,
+63160,
+5490,
+39877,
+30140,
+44636,
+5730,
+56847,
+14279,
+18143,
+64039,
+8713,
+26828,
+43045,
+52979,
+12313,
+6031,
+52511,
+9068,
+40491,
+4651,
+22274,
+22135,
+54101,
+41908,
+43390,
+9166,
+4648,
+21217,
+41140,
+29613,
+53218,
+19511,
+26418,
+52636,
+37225,
+35699,
+46580,
+28638,
+3921,
+60408,
+21459,
+30702,
+23449,
+63087,
+19349,
+40722,
+54506,
+64213,
+48646,
+35183,
+51443,
+42491,
+39764,
+50471,
+771,
+7896,
+49355,
+45627,
+26385,
+40196,
+53866,
+33230,
+35143,
+53799,
+46380,
+54094,
+17190,
+54429,
+14947,
+36939,
+27204,
+1285,
+60187,
+13944,
+23302,
+13717,
+41560,
+11763,
+26569,
+58047,
+61565,
+23255,
+20339,
+16991,
+55051,
+39892,
+31208,
+44841,
+3855,
+4369,
+35289,
+53621,
+50973,
+28087,
+13107,
+21845,
+65535,
+2,
+43692,
+52430,
+37450,
+14564,
+11916,
+30248,
+61168,
+61682,
+20696,
+34329,
+25645,
+10486,
+48546,
+45198,
+42282,
+3972,
+7490,
+38968,
+53774,
+23977,
+51820,
+42235,
+51593,
+5350,
+64252,
+38333,
+28598,
+50590,
+11108,
+48347,
+11443,
+19157,
+11738,
+30394,
+32307,
+11671,
+25341,
+39152,
+19910,
+16182,
+57641,
+64766,
+15066,
+25773,
+23046,
+14094,
+30354,
+16891,
+1324,
+11031,
+24815,
+46188,
+2450,
+42088,
+34835,
+44078,
+5129,
+61616,
+36899,
+18957,
+29838,
+28312,
+12901,
+39119,
+46026,
+12319,
+35924,
+24397,
+44320,
+60889,
+56371,
+22147,
+23629,
+11436,
+43402,
+43263,
+60886,
+25046,
+56469,
+13026,
+59506,
+53224,
+12558,
+22492,
+38709,
+56824,
+1498,
+47394,
+51258,
+8690,
+59807,
+20901,
+35397,
+25660,
+60047,
+2377,
+50077,
+38923,
+1976,
+47604,
+53269,
+57225,
+31977,
+31671,
+63984,
+10769,
+10364,
+43490,
+25736,
+24910,
+2645,
+8447,
+49658,
+56379,
+56742,
+51192,
+23426,
+50328,
+24405,
+56835,
+5394,
+1070,
+1592,
+62905,
+50393,
+26163,
+65280,
+65282,
+52379,
+5022,
+5233,
+20774,
+8591,
+60908,
+62635,
+7682,
+18827,
+22240,
+4698,
+18425,
+40758,
+10118,
+40875,
+57600,
+27476,
+48985,
+15329,
+22287,
+7014,
+54215,
+3677,
+62099,
+35010,
+51963,
+57740,
+40411,
+15396,
+23982,
+63688,
+44508,
+20493,
+56261,
+35875,
+63545,
+396,
+55303,
+15455,
+55230,
+58384,
+57465,
+38214,
+45401,
+31352,
+45444,
+64230,
+52541,
+58891,
+55991,
+33590,
+52829,
+6319,
+28549,
+14286,
+9946,
+24201,
+36019,
+31283,
+23642,
+48072,
+26146,
+14887,
+60260,
+56731,
+27630,
+16929,
+15342,
+3982,
+20470,
+25952,
+17324,
+28459,
+55666,
+39290,
+49994,
+29978,
+48399,
+37743,
+36619,
+48488,
+32535,
+42142,
+39168,
+34994,
+40636,
+44553,
+58726,
+29228,
+15147,
+32246,
+29722,
+41275,
+18262,
+3812,
+27003,
+49843,
+36313,
+30824,
+40871,
+14421,
+6113,
+40766,
+42141,
+32558,
+48489,
+52040,
+64290,
+45393,
+18823,
+10672,
+43328,
+4342,
+29593,
+25838,
+41681,
+31367,
+45065,
+39587,
+42065,
+52272,
+4186,
+8469,
+28421,
+29343,
+20086,
+39117,
+12903,
+4963,
+22564,
+40787,
+60128,
+37217,
+22345,
+29473,
+1115,
+15798,
+11927,
+490,
+17086,
+24318,
+29316,
+46588,
+21525,
+28635,
+63627,
+36872,
+40294,
+6967,
+13178,
+34234,
+11799,
+13736,
+21923,
+30399,
+41119,
+13314,
+63707,
+40348,
+56564,
+22638,
+64409,
+57106,
+38538,
+42426,
+9124,
+34820,
+15583,
+46702,
+44350,
+12254,
+60848,
+15868,
+56221,
+6910,
+39602,
+9010,
+41268,
+19075,
+58739,
+21281,
+10659,
+50494,
+58092,
+10557,
+60119,
+59513,
+21328,
+28795,
+40742,
+47281,
+15643,
+21404,
+47146,
+40011,
+54631,
+58188,
+13549,
+35420,
+52270,
+42067,
+62860,
+30149,
+41786,
+7664,
+44573,
+16311,
+49664,
+46507,
+30784,
+2413,
+60244,
+61315,
+57309,
+18793,
+7990,
+6070,
+18914,
+8864,
+20686,
+17064,
+20194,
+57240,
+51500,
+48388,
+3605,
+16776,
+38881,
+37489,
+8135,
+42476,
+3545,
+18945,
+56859,
+41648,
+1798,
+60875,
+42380,
+44048,
+7025,
+54895,
+44222,
+59539,
+28502,
+42814,
+350,
+33423,
+60489,
+61110,
+42335,
+8721,
+49691,
+15375,
+21760,
+26061,
+12954,
+65452,
+61637,
+38392,
+61151,
+56451,
+2937,
+1674,
+31224,
+4247,
+23590,
+53109,
+3719,
+50616,
+11101,
+6890,
+46555,
+1061,
+28820,
+63994,
+16364,
+12768,
+42724,
+38116,
+9626,
+46252,
+53164,
+32012,
+49967,
+41763,
+2846,
+29259,
+42721,
+15619,
+1566,
+57257,
+35457,
+49833,
+56928,
+21433,
+13586,
+13092,
+53705,
+47064,
+29289,
+43259,
+13625,
+33414,
+50687,
+19200,
+1810,
+51316,
+52850,
+13407,
+40204,
+38174,
+55792,
+29911,
+48801,
+29850,
+1843,
+7429,
+19962,
+23359,
+2338,
+1458,
+6110,
+61763,
+1738,
+49785,
+44917,
+27409,
+11198,
+64391,
+25300,
+25031,
+11670,
+32733,
+30395,
+17321,
+43174,
+48572,
+62938,
+59509,
+61830,
+35316,
+4527,
+43784,
+5132,
+39862,
+21063,
+7994,
+1788,
+64439,
+43075,
+40935,
+24619,
+14836,
+52905,
+46086,
+6831,
+35772,
+62506,
+62445,
+33607,
+2343,
+33804,
+41669,
+20892,
+64873,
+47844,
+42523,
+132,
+26610,
+60344,
+40280,
+8053,
+19929,
+48843,
+39960,
+62809,
+18410,
+46387,
+49976,
+41307,
+35116,
+44483,
+19155,
+11445,
+41862,
+12738,
+47357,
+26329,
+58825,
+37540,
+63897,
+54142,
+29721,
+32549,
+15148,
+35986,
+26564,
+21410,
+52119,
+43650,
+61205,
+803,
+26819,
+41476,
+37283,
+54747,
+62355,
+17163,
+41395,
+54888,
+54847,
+53533,
+61301,
+8698,
+2894,
+23952,
+10212,
+28961,
+31362,
+20611,
+3578,
+4762,
+39037,
+4982,
+25161,
+16074,
+54624,
+8067,
+529,
+27576,
+33852,
+59223,
+53340,
+54119,
+48673,
+45278,
+51572,
+61835,
+23039,
+16024,
+50807,
+55313,
+30561,
+50598,
+22398,
+26808,
+16854,
+10686,
+63778,
+42313,
+50835,
+40756,
+18427,
+62668,
+9210,
+1794,
+62179,
+5643,
+30900,
+47886,
+5114,
+30854,
+21938,
+23173,
+55654,
+61844,
+28669,
+36971,
+4881,
+52342,
+15742,
+31377,
+49466,
+11367,
+21176,
+31332,
+36205,
+54299,
+40401,
+10501,
+30161,
+56788,
+27383,
+214,
+60356,
+22610,
+61970,
+53684,
+52748,
+60080,
+16133,
+16107,
+9965,
+12581,
+65064,
+49848,
+34052,
+16685,
+23186,
+59854,
+57937,
+61795,
+10845,
+18340,
+57416,
+35893,
+34599,
+24763,
+13056,
+15345,
+25108,
+55356,
+3780,
+65486,
+35391,
+51911,
+33200,
+14851,
+49798,
+43405,
+41421,
+18840,
+47078,
+53434,
+65186,
+33144,
+5049,
+41179,
+14154,
+54440,
+60618,
+55317,
+31753,
+43477,
+39016,
+35604,
+25555,
+10128,
+27933,
+27303,
+37198,
+44962,
+28157,
+25289,
+9001,
+8599,
+9702,
+38460,
+12527,
+20429,
+33950,
+52680,
+46977,
+53966,
+38162,
+6367,
+57315,
+30358,
+56195,
+4807,
+61735,
+31416,
+45729,
+4448,
+52354,
+57280,
+25395,
+37732,
+14047,
+54794,
+39444,
+54544,
+47693,
+3685,
+16163,
+46048,
+14956,
+61038,
+21259,
+43568,
+21430,
+54422,
+1658,
+15131,
+27464,
+20959,
+28120,
+23050,
+8175,
+25403,
+45088,
+16100,
+58134,
+11520,
+2174,
+23293,
+64995,
+14659,
+31710,
+60859,
+53319,
+52304,
+60699,
+9797,
+57585,
+58845,
+20717,
+54147,
+42388,
+18503,
+58713,
+53589,
+13567,
+56887,
+48701,
+62412,
+57713,
+57504,
+40725,
+17424,
+36288,
+45889,
+23241,
+10843,
+61797,
+2823,
+49966,
+32357,
+53165,
+20132,
+53025,
+9781,
+30036,
+51742,
+28541,
+9987,
+12842,
+13039,
+7002,
+19401,
+4301,
+37553,
+64730,
+23500,
+12921,
+10535,
+29367,
+65326,
+11548,
+57287,
+57760,
+47190,
+63734,
+34297,
+16562,
+56097,
+46097,
+46830,
+29294,
+16831,
+57817,
+31670,
+32665,
+57226,
+44063,
+7094,
+63395,
+5266,
+25845,
+28398,
+47667,
+23947,
+26779,
+22009,
+30709,
+50670,
+27541,
+51682,
+17206,
+8106,
+44366,
+24541,
+9772,
+37467,
+3585,
+37375,
+26038,
+29899,
+7175,
+8047,
+42745,
+9545,
+32948,
+12709,
+21209,
+47255,
+11075,
+55982,
+26294,
+22228,
+35277,
+45651,
+28335,
+24168,
+42774,
+46728,
+48084,
+59661,
+3091,
+33257,
+63195,
+35751,
+3933,
+11046,
+45569,
+48270,
+28951,
+20865,
+50999,
+39160,
+51959,
+10133,
+28747,
+11493,
+35552,
+60360,
+22982,
+4438,
+46965,
+4504,
+64927,
+19802,
+23925,
+35295,
+61856,
+56402,
+62546,
+59335,
+58700,
+7546,
+31506,
+11820,
+65161,
+48411,
+57509,
+40881,
+43866,
+54097,
+12846,
+7199,
+64382,
+14142,
+18847,
+36723,
+24887,
+26526,
+13100,
+55298,
+37993,
+10511,
+27040,
+6918,
+18121,
+37413,
+49547,
+2198,
+36629,
+47115,
+6718,
+47776,
+9326,
+59460,
+63974,
+49888,
+48781,
+27135,
+28444,
+35570,
+40586,
+29360,
+61826,
+24149,
+43212,
+45032,
+56892,
+15157,
+26630,
+24849,
+29072,
+42847,
+13756,
+10110,
+889,
+28204,
+26009,
+65218,
+63271,
+10186,
+31055,
+50785,
+49708,
+18911,
+3553,
+46526,
+45739,
+38677,
+63015,
+58259,
+19364,
+20740,
+16672,
+3519,
+27426,
+57158,
+63731,
+24945,
+54412,
+63529,
+62044,
+55760,
+28955,
+62779,
+58787,
+31444,
+9657,
+8205,
+57272,
+30319,
+55407,
+37606,
+48871,
+41144,
+27060,
+12052,
+17243,
+50826,
+63297,
+55264,
+37561,
+5921,
+21902,
+13337,
+38823,
+5526,
+40056,
+61132,
+49749,
+19396,
+42708,
+11654,
+26362,
+20520,
+58793,
+55498,
+21357,
+58281,
+39269,
+12556,
+53226,
+35868,
+13677,
+15185,
+42799,
+4094,
+34698,
+53741,
+790,
+6215,
+57620,
+26287,
+39932,
+46246,
+9096,
+42787,
+58549,
+43289,
+44908,
+5437,
+45014,
+61313,
+60246,
+16441,
+17591,
+37348,
+49177,
+43476,
+32107,
+55318,
+7858,
+22650,
+57373,
+57381,
+41927,
+49321,
+3970,
+42284,
+53810,
+37509,
+19103,
+62797,
+23713,
+28110,
+13819,
+49002,
+24509,
+63193,
+33259,
+23869,
+20656,
+29976,
+49996,
+29383,
+34672,
+46646,
+50401,
+30494,
+28741,
+1002,
+22805,
+5688,
+46772,
+59080,
+28577,
+6507,
+7745,
+19080,
+64171,
+26984,
+60858,
+32041,
+14660,
+37975,
+6174,
+20941,
+44893,
+27520,
+61785,
+5592,
+46321,
+28061,
+34806,
+10530,
+14190,
+34342,
+47161,
+61176,
+46403,
+49343,
+22018,
+57850,
+4614,
+55711,
+44873,
+37960,
+33328,
+6315,
+966,
+42495,
+18953,
+17611,
+16482,
+57574,
+25098,
+55459,
+22445,
+41885,
+5773,
+63983,
+32664,
+31978,
+57818,
+41210,
+29154,
+58374,
+48106,
+26197,
+46033,
+38262,
+8255,
+40144,
+3426,
+16849,
+58432,
+56082,
+21983,
+41692,
+17831,
+46548,
+53192,
+28623,
+44989,
+36117,
+50022,
+18508,
+43808,
+19495,
+55376,
+11141,
+23076,
+29647,
+20163,
+12290,
+55343,
+20370,
+64901,
+47167,
+57803,
+56224,
+45487,
+2907,
+60332,
+2356,
+60255,
+34389,
+25848,
+5125,
+34585,
+44101,
+29099,
+48361,
+14795,
+8687,
+55890,
+14330,
+4318,
+9849,
+51386,
+43663,
+34368,
+9419,
+64237,
+53480,
+48920,
+34643,
+54356,
+63117,
+64075,
+26153,
+19619,
+18817,
+62623,
+5963,
+979,
+49020,
+58947,
+558,
+48421,
+25875,
+10408,
+10566,
+7890,
+45107,
+33448,
+12858,
+29709,
+40971,
+38891,
+17703,
+22186,
+193,
+44931,
+2202,
+50961,
+16872,
+45659,
+23071,
+25546,
+39492,
+54564,
+45988,
+4289,
+24184,
+37364,
+21773,
+15547,
+44045,
+50940,
+679,
+53298,
+57050,
+62611,
+43177,
+55367,
+19026,
+49146,
+51291,
+38012,
+4256,
+18275,
+31518,
+36087,
+52784,
+6492,
+34551,
+1232,
+38696,
+46900,
+30030,
+58703,
+37263,
+24653,
+22902,
+39567,
+9013,
+28000,
+54362,
+54318,
+39301,
+60347,
+56356,
+44599,
+13921,
+4619,
+8413,
+44640,
+19669,
+2210,
+9753,
+62884,
+28346,
+36086,
+31550,
+18276,
+27052,
+41722,
+35472,
+522,
+14161,
+41016,
+62777,
+28957,
+50979,
+11819,
+31899,
+7547,
+16611,
+10720,
+43079,
+18976,
+35313,
+30639,
+28990,
+36209,
+30232,
+48220,
+51576,
+23507,
+4364,
+60253,
+2358,
+61593,
+18118,
+58275,
+15688,
+33394,
+48853,
+9763,
+58940,
+14100,
+58111,
+64673,
+16708,
+48233,
+43835,
+152,
+11138,
+860,
+25957,
+60587,
+40907,
+64604,
+6400,
+11632,
+25133,
+22449,
+12183,
+14508,
+38951,
+59873,
+47982,
+61308,
+54610,
+62089,
+4469,
+52240,
+43924,
+35247,
+3186,
+19002,
+56416,
+59552,
+60971,
+40443,
+223,
+9656,
+31816,
+58788,
+63042,
+16267,
+18746,
+49048,
+9950,
+18310,
+54815,
+22460,
+24604,
+36633,
+24322,
+98,
+64876,
+6654,
+30214,
+30851,
+29632,
+53854,
+3339,
+22625,
+23825,
+17971,
+486,
+52619,
+49911,
+45728,
+32078,
+61736,
+64279,
+29928,
+43023,
+22425,
+47808,
+16559,
+16595,
+22672,
+4305,
+36818,
+14385,
+46819,
+30982,
+5727,
+58593,
+47424,
+38208,
+40211,
+65155,
+13074,
+52997,
+30279,
+11760,
+59804,
+52035,
+57912,
+16497,
+3890,
+47381,
+51918,
+10911,
+57472,
+19758,
+53823,
+236,
+40374,
+49465,
+32168,
+15743,
+36237,
+29138,
+52176,
+59882,
+46169,
+14129,
+42825,
+45064,
+32523,
+41682,
+49287,
+6906,
+20610,
+32221,
+28962,
+11772,
+24155,
+13428,
+1509,
+17492,
+53598,
+58286,
+45443,
+32595,
+45402,
+39872,
+43145,
+35133,
+38082,
+47546,
+7021,
+52425,
+18206,
+46356,
+41985,
+42185,
+596,
+43363,
+7142,
+65171,
+47044,
+64372,
+36204,
+32164,
+21177,
+13645,
+38744,
+39900,
+30052,
+50635,
+6220,
+26791,
+25639,
+48139,
+17635,
+34386,
+25410,
+15362,
+56281,
+52204,
+2277,
+36452,
+21568,
+11924,
+37636,
+15098,
+42681,
+40453,
+12937,
+20815,
+48565,
+52358,
+33048,
+53739,
+34700,
+781,
+12153,
+49604,
+11268,
+41147,
+51460,
+57581,
+39109,
+34777,
+6964,
+35550,
+11495,
+43470,
+28754,
+16224,
+15948,
+23641,
+32581,
+36020,
+61770,
+39626,
+44,
+11652,
+42710,
+8870,
+59613,
+23724,
+63806,
+29057,
+41773,
+57118,
+64051,
+61213,
+24530,
+25277,
+47055,
+6643,
+18871,
+7700,
+16281,
+34452,
+47758,
+13320,
+19208,
+37459,
+42782,
+39227,
+64564,
+49828,
+1382,
+9905,
+37308,
+7586,
+30001,
+60350,
+55643,
+39722,
+13769,
+35806,
+1802,
+33551,
+48976,
+48944,
+58519,
+21361,
+64725,
+6385,
+35212,
+13969,
+3815,
+27497,
+54639,
+13954,
+47213,
+51070,
+4246,
+32376,
+1675,
+59477,
+30471,
+47973,
+30622,
+62071,
+54322,
+41454,
+62373,
+36468,
+34359,
+21579,
+49421,
+21299,
+44840,
+32779,
+39893,
+64048,
+37306,
+9907,
+25725,
+54785,
+54541,
+45105,
+7892,
+26895,
+13220,
+51346,
+33841,
+18377,
+55200,
+52546,
+25010,
+63295,
+50828,
+8231,
+7397,
+17373,
+15810,
+22666,
+14550,
+55850,
+5759,
+64093,
+29068,
+34324,
+43959,
+60578,
+27535,
+52631,
+40994,
+55767,
+35671,
+21873,
+33926,
+56119,
+23162,
+16236,
+18249,
+50179,
+25329,
+20785,
+43603,
+1752,
+5721,
+38313,
+16369,
+35644,
+38323,
+56018,
+18296,
+47901,
+34217,
+40128,
+5281,
+33911,
+39690,
+37141,
+30518,
+64125,
+7479,
+16762,
+24745,
+49359,
+59846,
+44656,
+13959,
+42032,
+7984,
+23376,
+12883,
+3404,
+7084,
+21859,
+53345,
+38691,
+6927,
+10454,
+16788,
+42343,
+28716,
+16638,
+60843,
+44884,
+39056,
+21303,
+23433,
+12572,
+55212,
+34858,
+46634,
+51215,
+45352,
+21220,
+13605,
+8387,
+64827,
+34572,
+5358,
+4587,
+46354,
+18208,
+40855,
+6265,
+2689,
+10063,
+45227,
+22022,
+36342,
+48551,
+9192,
+10887,
+29477,
+11284,
+3590,
+13123,
+19741,
+49255,
+34277,
+17780,
+24224,
+16904,
+61731,
+50846,
+52096,
+38070,
+57943,
+45479,
+58784,
+17385,
+25434,
+60882,
+13590,
+26373,
+64303,
+26843,
+39769,
+51371,
+36029,
+22416,
+27187,
+60218,
+12263,
+60627,
+40733,
+11207,
+62129,
+50784,
+31841,
+10187,
+26742,
+11416,
+16866,
+4481,
+7356,
+7466,
+51337,
+1598,
+8936,
+43685,
+53250,
+5618,
+1214,
+12569,
+3562,
+36016,
+41843,
+43105,
+61246,
+41355,
+35950,
+47478,
+41095,
+16945,
+43459,
+23515,
+35431,
+30352,
+14096,
+27988,
+35913,
+63642,
+42735,
+50057,
+29575,
+3070,
+47932,
+43361,
+598,
+59411,
+52452,
+42572,
+11448,
+6727,
+1881,
+48022,
+19942,
+10300,
+14475,
+41729,
+15962,
+22093,
+22785,
+45396,
+1000,
+28743,
+53976,
+721,
+3178,
+51004,
+53393,
+1863,
+29570,
+18130,
+23460,
+40397,
+59044,
+33991,
+64306,
+21154,
+5726,
+31402,
+46820,
+24313,
+56015,
+61346,
+54855,
+1627,
+11474,
+42967,
+39293,
+53918,
+34710,
+27093,
+14149,
+21876,
+10459,
+709,
+34431,
+60180,
+21035,
+44169,
+3789,
+50027,
+59398,
+50750,
+24348,
+50694,
+10444,
+10059,
+60411,
+33914,
+21437,
+8307,
+61791,
+36,
+46054,
+13467,
+36677,
+5424,
+25346,
+61913,
+12175,
+53745,
+29643,
+33404,
+40775,
+8476,
+20821,
+52819,
+3392,
+35097,
+21917,
+303,
+48507,
+63810,
+41123,
+1405,
+51228,
+60385,
+44443,
+64348,
+10979,
+16433,
+61586,
+46076,
+37609,
+61274,
+64600,
+3852,
+63718,
+60383,
+51230,
+49069,
+793,
+27652,
+5369,
+58130,
+58556,
+47013,
+48516,
+6882,
+47885,
+32181,
+5644,
+21688,
+50339,
+70,
+16616,
+33931,
+11182,
+55042,
+19792,
+24596,
+49253,
+19743,
+51308,
+51420,
+62562,
+61329,
+41797,
+3474,
+12222,
+41158,
+20360,
+104,
+42444,
+8467,
+4188,
+3615,
+46417,
+6082,
+27959,
+58771,
+3847,
+62830,
+9616,
+36153,
+33810,
+18892,
+29011,
+11533,
+3075,
+14766,
+30100,
+20565,
+240,
+4352,
+21937,
+32178,
+5115,
+29631,
+31427,
+30215,
+22005,
+546,
+18452,
+41913,
+3799,
+1260,
+1872,
+3640,
+65520,
+61442,
+33762,
+11797,
+34236,
+64757,
+60995,
+47209,
+43206,
+54758,
+60108,
+27131,
+26796,
+11618,
+34566,
+38445,
+40870,
+32541,
+36314,
+22592,
+37505,
+13807,
+11245,
+65369,
+6280,
+53017,
+56026,
+59384,
+25387,
+14253,
+39657,
+5856,
+2776,
+65420,
+22035,
+45567,
+11048,
+4070,
+7519,
+1683,
+53279,
+40584,
+35572,
+39412,
+36234,
+4718,
+57733,
+44759,
+61838,
+49074,
+49944,
+20206,
+24864,
+26142,
+18439,
+40066,
+2412,
+32430,
+46508,
+50559,
+36338,
+21192,
+50911,
+34851,
+24599,
+28435,
+11868,
+60066,
+17817,
+30364,
+1378,
+1902,
+3376,
+40115,
+50000,
+9311,
+20052,
+51658,
+9101,
+20169,
+26427,
+34245,
+58574,
+22264,
+36833,
+5764,
+51362,
+53077,
+51246,
+43648,
+52121,
+48962,
+17443,
+24846,
+44772,
+42595,
+24712,
+15231,
+8817,
+3234,
+15661,
+53583,
+12820,
+7827,
+47697,
+47867,
+46651,
+43439,
+50501,
+37475,
+33838,
+55008,
+8162,
+8158,
+17560,
+28140,
+38870,
+15659,
+3236,
+45474,
+61680,
+61170,
+39044,
+56412,
+33064,
+49955,
+23968,
+52873,
+62795,
+19105,
+58832,
+50669,
+31965,
+22010,
+57478,
+62423,
+43559,
+12809,
+23448,
+32824,
+21460,
+42424,
+38540,
+24246,
+10472,
+26784,
+20023,
+15243,
+20951,
+45322,
+45174,
+16065,
+49527,
+39297,
+14625,
+34759,
+40939,
+16286,
+55261,
+8465,
+42446,
+10324,
+34423,
+18904,
+13098,
+26528,
+48601,
+58309,
+61956,
+11113,
+63881,
+13148,
+11659,
+28065,
+40027,
+59521,
+18055,
+59589,
+23485,
+25185,
+23074,
+11143,
+14027,
+49079,
+54953,
+24493,
+37195,
+62918,
+8287,
+26831,
+17394,
+41531,
+20346,
+44737,
+53829,
+28932,
+787,
+19712,
+58214,
+8352,
+55048,
+28989,
+31499,
+35314,
+61832,
+10741,
+26095,
+44244,
+29830,
+6181,
+48735,
+20012,
+56133,
+52846,
+37841,
+45180,
+28832,
+2399,
+62070,
+31219,
+47974,
+42278,
+29529,
+1539,
+25237,
+2725,
+4641,
+52168,
+52159,
+50904,
+46005,
+36875,
+45841,
+29773,
+49058,
+62567,
+44590,
+19378,
+1604,
+21765,
+3840,
+65265,
+64087,
+44416,
+6353,
+362,
+29610,
+2170,
+65055,
+21665,
+36478,
+23766,
+26732,
+63388,
+18521,
+10570,
+61440,
+65522,
+42132,
+11300,
+55111,
+17773,
+15056,
+29455,
+61126,
+47363,
+11249,
+20233,
+53149,
+56750,
+46957,
+8153,
+51786,
+19195,
+64763,
+63588,
+19615,
+52605,
+61154,
+50597,
+32197,
+55314,
+18049,
+10614,
+12491,
+35975,
+64782,
+38680,
+49859,
+14503,
+5970,
+19571,
+37146,
+51311,
+17863,
+13476,
+39614,
+26368,
+32982,
+24902,
+40808,
+20586,
+43270,
+59925,
+40309,
+56422,
+20804,
+2625,
+38859,
+62929,
+43994,
+57193,
+19168,
+3437,
+32922,
+13575,
+30322,
+10935,
+5808,
+61657,
+13399,
+12096,
+64124,
+31145,
+37142,
+1222,
+19078,
+7747,
+7453,
+50899,
+25460,
+55018,
+42739,
+20599,
+15504,
+13455,
+941,
+52528,
+58681,
+38501,
+9957,
+8537,
+54477,
+28879,
+63979,
+61413,
+28740,
+31724,
+50402,
+37022,
+44804,
+17675,
+5937,
+10475,
+25106,
+15347,
+53569,
+10012,
+11298,
+42134,
+39093,
+39978,
+65079,
+53205,
+65251,
+5060,
+3329,
+59496,
+44781,
+47972,
+31221,
+59478,
+26192,
+39684,
+19691,
+2334,
+22960,
+4408,
+6467,
+15372,
+19654,
+45125,
+2483,
+23245,
+56209,
+6079,
+8980,
+65268,
+50621,
+4272,
+29679,
+29751,
+36816,
+4307,
+58677,
+47957,
+47203,
+23105,
+65382,
+9789,
+62144,
+34605,
+43621,
+19221,
+3598,
+25695,
+37873,
+47907,
+62787,
+59268,
+51224,
+41099,
+63422,
+43682,
+15730,
+12366,
+29937,
+64936,
+13873,
+24229,
+33278,
+21055,
+28689,
+49212,
+17405,
+53335,
+18699,
+59135,
+53907,
+59057,
+48079,
+58604,
+15610,
+53418,
+15236,
+53456,
+22391,
+34189,
+27456,
+9582,
+60402,
+41118,
+32485,
+21924,
+54248,
+17320,
+32306,
+32734,
+11739,
+28432,
+40921,
+11242,
+7643,
+58379,
+53002,
+13465,
+46056,
+54392,
+51512,
+64823,
+52210,
+39635,
+23601,
+46209,
+15223,
+8615,
+29339,
+39522,
+9466,
+21399,
+8187,
+15889,
+6200,
+6839,
+29828,
+44246,
+1377,
+30772,
+17818,
+1634,
+29182,
+26925,
+56194,
+32082,
+57316,
+10581,
+16890,
+32721,
+14095,
+31026,
+35432,
+19592,
+39073,
+7500,
+23032,
+27581,
+27813,
+7812,
+2702,
+42674,
+59584,
+58480,
+13218,
+26897,
+30026,
+38716,
+24063,
+25103,
+296,
+21626,
+12489,
+10616,
+21680,
+1195,
+48277,
+59151,
+34304,
+51569,
+10934,
+30525,
+13576,
+55406,
+31812,
+57273,
+56060,
+46083,
+49697,
+15927,
+24528,
+61215,
+47656,
+36094,
+64218,
+26902,
+46873,
+56608,
+38987,
+54674,
+45428,
+13126,
+26082,
+6863,
+56076,
+50761,
+53127,
+48891,
+59443,
+16275,
+51916,
+47383,
+21612,
+34084,
+62352,
+16447,
+515,
+52456,
+37314,
+22171,
+29255,
+738,
+5322,
+11759,
+31393,
+52998,
+15217,
+51391,
+18937,
+9445,
+61729,
+16906,
+8056,
+17522,
+41467,
+14258,
+17108,
+14718,
+15576,
+46500,
+41917,
+16028,
+43308,
+33598,
+19887,
+26314,
+55073,
+22876,
+39463,
+23757,
+54777,
+6068,
+7992,
+21065,
+61167,
+32762,
+11917,
+51884,
+56681,
+1311,
+41611,
+33631,
+3682,
+12208,
+570,
+58881,
+35325,
+61707,
+16090,
+11287,
+48219,
+31496,
+36210,
+12147,
+6955,
+12447,
+46674,
+60691,
+46560,
+34037,
+34899,
+3706,
+33238,
+61011,
+39497,
+35640,
+47069,
+22004,
+30850,
+31428,
+6655,
+35300,
+3831,
+64871,
+20894,
+55542,
+37150,
+2289,
+20120,
+115,
+43504,
+51352,
+60802,
+56581,
+23325,
+21340,
+15368,
+15655,
+23849,
+51587,
+23347,
+36921,
+61901,
+43488,
+10366,
+57029,
+50292,
+44588,
+62569,
+7975,
+28998,
+26567,
+11765,
+63828,
+53528,
+64310,
+10318,
+7508,
+62492,
+47173,
+13067,
+64540,
+65209,
+50707,
+41624,
+17868,
+41592,
+63258,
+29087,
+53572,
+24361,
+56787,
+32159,
+10502,
+21335,
+47750,
+3940,
+45832,
+6570,
+43566,
+21261,
+263,
+16137,
+41785,
+32437,
+62861,
+50,
+33412,
+13627,
+36458,
+10434,
+14622,
+44635,
+32862,
+39878,
+872,
+39113,
+4282,
+13849,
+12886,
+46091,
+49131,
+49746,
+65152,
+21676,
+3239,
+4714,
+8730,
+53513,
+28128,
+63518,
+64856,
+12241,
+45939,
+52860,
+51987,
+33096,
+13268,
+8842,
+14727,
+56714,
+48058,
+44686,
+8887,
+62124,
+64059,
+42021,
+34510,
+35186,
+45946,
+47195,
+8123,
+20564,
+30859,
+14767,
+58371,
+2306,
+63379,
+28926,
+27886,
+2572,
+36068,
+12471,
+61928,
+56365,
+60207,
+22974,
+16540,
+44424,
+3723,
+26698,
+55901,
+8279,
+33189,
+15705,
+20510,
+11213,
+24085,
+22604,
+43817,
+37771,
+17402,
+63399,
+46800,
+61337,
+38599,
+19820,
+23814,
+34023,
+65016,
+46255,
+13536,
+38475,
+53487,
+21116,
+38106,
+57718,
+14847,
+9045,
+61662,
+50634,
+31327,
+39901,
+2013,
+55548,
+52697,
+28765,
+57220,
+39019,
+59379,
+53478,
+64239,
+54472,
+64300,
+47457,
+26210,
+51741,
+32007,
+9782,
+14404,
+61548,
+7544,
+58702,
+31542,
+46901,
+18964,
+38715,
+30337,
+26898,
+4752,
+51322,
+52568,
+13823,
+26706,
+8283,
+20420,
+37431,
+53382,
+15935,
+28694,
+36128,
+58761,
+60237,
+26431,
+7303,
+56646,
+3370,
+53426,
+50964,
+22142,
+56354,
+60349,
+31247,
+7587,
+58568,
+52361,
+28116,
+42537,
+43585,
+56620,
+37981,
+42936,
+50247,
+6628,
+25241,
+58572,
+34247,
+54043,
+33622,
+5178,
+38774,
+53154,
+39428,
+38415,
+48398,
+32563,
+49995,
+31730,
+20657,
+23030,
+7502,
+38434,
+59200,
+43020,
+1826,
+37092,
+33675,
+24952,
+34738,
+26126,
+15079,
+21005,
+10668,
+24572,
+63111,
+9514,
+676,
+50146,
+23154,
+14600,
+28759,
+22408,
+22842,
+27403,
+20513,
+2068,
+1173,
+22163,
+1648,
+9142,
+21548,
+46466,
+62744,
+51994,
+7933,
+64935,
+30425,
+12367,
+8315,
+26520,
+33433,
+39983,
+43930,
+1824,
+43022,
+31413,
+64280,
+42527,
+19925,
+37532,
+62278,
+25729,
+62591,
+53343,
+21861,
+57513,
+42772,
+24170,
+27687,
+63287,
+42517,
+48800,
+32327,
+55793,
+20100,
+3219,
+29167,
+23227,
+2735,
+41800,
+17382,
+62782,
+17587,
+7174,
+31952,
+26039,
+35319,
+18469,
+54407,
+49167,
+34381,
+27215,
+42693,
+38136,
+3175,
+23298,
+57406,
+28352,
+53241,
+9020,
+24927,
+2191,
+25863,
+50256,
+20447,
+49439,
+11922,
+21570,
+16942,
+24691,
+9495,
+21099,
+10167,
+26511,
+40267,
+12834,
+9769,
+34366,
+43665,
+7762,
+45665,
+37250,
+56115,
+50992,
+23933,
+27764,
+48137,
+25641,
+3914,
+12941,
+6103,
+60481,
+1842,
+32325,
+48802,
+13352,
+60041,
+39681,
+42223,
+63677,
+24855,
+16583,
+51758,
+59987,
+28311,
+32707,
+18958,
+14236,
+26608,
+134,
+47576,
+17542,
+6180,
+30633,
+44245,
+30367,
+6840,
+35792,
+42515,
+63289,
+21905,
+52503,
+40345,
+13303,
+59671,
+7119,
+44487,
+45493,
+19427,
+58228,
+39881,
+56781,
+17495,
+51847,
+26031,
+1937,
+47446,
+17742,
+51273,
+49874,
+11956,
+29659,
+50453,
+4559,
+11973,
+53557,
+48753,
+51691,
+46968,
+36112,
+8623,
+60682,
+45056,
+39320,
+54616,
+11566,
+2341,
+33609,
+61605,
+36529,
+26397,
+22109,
+27191,
+57423,
+45763,
+38187,
+50815,
+62898,
+47924,
+49057,
+30608,
+45842,
+55578,
+57002,
+22909,
+14656,
+37261,
+58705,
+33254,
+3032,
+40624,
+52992,
+36108,
+14519,
+38425,
+41362,
+42654,
+54703,
+58121,
+60341,
+65374,
+36815,
+30450,
+29680,
+23658,
+60948,
+19185,
+58696,
+35711,
+23023,
+64129,
+40851,
+7241,
+20082,
+60940,
+3352,
+27326,
+50182,
+21518,
+49555,
+27711,
+51767,
+34295,
+63736,
+4625,
+38238,
+9621,
+28315,
+14492,
+52266,
+41274,
+32548,
+32247,
+54143,
+40285,
+56127,
+5831,
+24465,
+54108,
+62619,
+7550,
+461,
+18584,
+40970,
+31585,
+12859,
+19127,
+39365,
+1198,
+57667,
+21272,
+16061,
+38286,
+49369,
+61918,
+23169,
+22468,
+4522,
+57786,
+15028,
+10676,
+61628,
+12394,
+41417,
+12503,
+20594,
+29573,
+50059,
+45420,
+27547,
+42778,
+55122,
+23657,
+29750,
+30451,
+4273,
+9370,
+12016,
+53938,
+26452,
+26634,
+48912,
+16334,
+12310,
+33757,
+51861,
+59557,
+55651,
+42910,
+41714,
+9275,
+32932,
+1993,
+50452,
+29802,
+11957,
+37713,
+28731,
+51234,
+22505,
+9992,
+14121,
+52335,
+38511,
+54812,
+20162,
+31640,
+23077,
+8120,
+33403,
+30939,
+53746,
+59240,
+37131,
+9696,
+38646,
+42767,
+3337,
+53856,
+60864,
+53853,
+31426,
+30852,
+5116,
+334,
+25042,
+38010,
+51293,
+37548,
+34514,
+1896,
+37658,
+64017,
+59282,
+42096,
+55604,
+41539,
+12359,
+47685,
+53217,
+32836,
+41141,
+2169,
+30595,
+363,
+46273,
+22357,
+3668,
+6360,
+20679,
+42318,
+43236,
+37698,
+59942,
+52686,
+9829,
+53624,
+20286,
+17058,
+25837,
+32526,
+4343,
+46242,
+48578,
+58199,
+24181,
+34504,
+18060,
+1369,
+2058,
+20913,
+36486,
+28826,
+13278,
+14743,
+36810,
+37702,
+3069,
+31019,
+50058,
+29687,
+20595,
+18129,
+30991,
+1864,
+42275,
+37400,
+37286,
+13983,
+43797,
+53045,
+34981,
+756,
+11602,
+41064,
+3062,
+3510,
+39312,
+15673,
+4730,
+51810,
+50388,
+33293,
+38974,
+38168,
+37566,
+10538,
+36597,
+20392,
+45131,
+40139,
+59159,
+6640,
+63436,
+60139,
+23401,
+26446,
+29185,
+4611,
+47818,
+41129,
+15430,
+23067,
+1538,
+30619,
+42279,
+40416,
+8681,
+54434,
+58649,
+25504,
+61974,
+34499,
+23695,
+41335,
+32955,
+34255,
+3768,
+2105,
+27417,
+43282,
+322,
+22523,
+53751,
+14165,
+34472,
+43122,
+50009,
+45164,
+49617,
+27716,
+7915,
+39252,
+5494,
+25858,
+2064,
+41037,
+45951,
+13694,
+8366,
+38836,
+18722,
+40332,
+39720,
+55645,
+51173,
+9866,
+47558,
+57653,
+39408,
+43944,
+23770,
+42153,
+53161,
+65019,
+11283,
+31091,
+10888,
+28734,
+1114,
+32505,
+22346,
+25231,
+62964,
+35445,
+53067,
+57428,
+63493,
+53733,
+9718,
+47237,
+24756,
+19458,
+16669,
+4416,
+37881,
+58066,
+61125,
+30578,
+15057,
+26537,
+37190,
+34018,
+33988,
+12754,
+4204,
+3009,
+46443,
+39547,
+17880,
+35227,
+1320,
+5111,
+1142,
+17489,
+40556,
+27462,
+15133,
+28517,
+41323,
+56430,
+42025,
+18694,
+12544,
+35776,
+51019,
+17436,
+18568,
+35743,
+56915,
+41432,
+49635,
+20547,
+33889,
+15516,
+17174,
+50744,
+61422,
+64853,
+63427,
+9541,
+52660,
+58061,
+36842,
+35524,
+6777,
+12039,
+44380,
+3517,
+16674,
+28684,
+12391,
+28015,
+51879,
+36323,
+58294,
+45457,
+41042,
+28344,
+62886,
+60023,
+62150,
+54370,
+28884,
+47405,
+42079,
+10020,
+7692,
+55920,
+34671,
+31728,
+49997,
+54935,
+6721,
+45873,
+37944,
+47788,
+43408,
+64919,
+62139,
+45956,
+1698,
+6790,
+42560,
+38152,
+65325,
+31993,
+10536,
+37568,
+43640,
+65104,
+62959,
+61825,
+31860,
+40587,
+46609,
+41639,
+37008,
+47672,
+52063,
+969,
+8987,
+60062,
+63802,
+4801,
+59492,
+22631,
+53703,
+13094,
+20085,
+32515,
+28422,
+11463,
+39521,
+30375,
+8616,
+4937,
+19179,
+23554,
+1164,
+34204,
+33374,
+11239,
+20251,
+36546,
+34040,
+35306,
+53391,
+51006,
+43333,
+37966,
+18987,
+12347,
+48623,
+56180,
+16021,
+46587,
+32498,
+24319,
+18630,
+51872,
+14884,
+48468,
+11455,
+29297,
+23285,
+53647,
+13997,
+3283,
+26124,
+34740,
+60820,
+49793,
+34162,
+36400,
+23284,
+29309,
+11456,
+16830,
+31981,
+46831,
+41492,
+5079,
+43258,
+32340,
+47065,
+61518,
+59976,
+555,
+59998,
+19113,
+45593,
+55239,
+6162,
+17983,
+61810,
+50042,
+50281,
+21039,
+20039,
+47463,
+43204,
+47211,
+13956,
+52409,
+39457,
+26737,
+50231,
+57325,
+42535,
+28118,
+20961,
+22646,
+42720,
+32353,
+2847,
+45679,
+737,
+30283,
+22172,
+58397,
+17379,
+16340,
+48112,
+33518,
+19649,
+43102,
+22317,
+186,
+15071,
+7380,
+37986,
+29206,
+60680,
+8625,
+52812,
+37060,
+25315,
+54761,
+41525,
+3522,
+55567,
+43574,
+2630,
+15146,
+32551,
+58727,
+21821,
+15693,
+32995,
+34714,
+42946,
+4286,
+51122,
+52557,
+9903,
+1384,
+63314,
+13657,
+36138,
+7244,
+56655,
+13439,
+27469,
+5901,
+6608,
+60679,
+29241,
+37987,
+10002,
+21910,
+14379,
+44815,
+14977,
+34756,
+44346,
+734,
+43514,
+34442,
+16987,
+24479,
+65198,
+5624,
+40465,
+15288,
+58911,
+63332,
+4610,
+29536,
+26447,
+26924,
+30361,
+1635,
+10136,
+13164,
+52112,
+48895,
+18188,
+39138,
+43430,
+37175,
+9339,
+22125,
+45121,
+1757,
+23226,
+29907,
+3220,
+13613,
+56146,
+41089,
+26849,
+50949,
+44215,
+13200,
+27028,
+37591,
+2304,
+58373,
+31667,
+41211,
+16980,
+39757,
+24333,
+22072,
+6055,
+58796,
+17766,
+18367,
+21698,
+62708,
+38818,
+12999,
+42716,
+52175,
+31374,
+36238,
+42254,
+12716,
+62147,
+41462,
+27853,
+6342,
+38726,
+60495,
+16382,
+39324,
+51494,
+17097,
+47105,
+9591,
+56362,
+49986,
+53008,
+45110,
+51058,
+5519,
+49783,
+1740,
+59628,
+10506,
+57579,
+51462,
+12029,
+56511,
+44312,
+61286,
+54389,
+10331,
+2164,
+46558,
+60693,
+11517,
+48360,
+31621,
+44102,
+51953,
+11769,
+56590,
+64886,
+63219,
+37479,
+56573,
+50607,
+10010,
+53571,
+30165,
+63259,
+34223,
+43970,
+12421,
+46835,
+21585,
+51909,
+35393,
+55104,
+7634,
+16808,
+27157,
+13189,
+42846,
+31851,
+24850,
+3163,
+34323,
+31179,
+64094,
+50040,
+61812,
+38841,
+60461,
+18106,
+55143,
+43871,
+34946,
+41772,
+31272,
+63807,
+28202,
+891,
+40631,
+44623,
+35955,
+36712,
+21135,
+41281,
+26486,
+64912,
+38546,
+45231,
+59767,
+4511,
+24650,
+19212,
+17186,
+14880,
+40448,
+50823,
+28402,
+24051,
+58914,
+44428,
+8145,
+39785,
+3251,
+27429,
+7821,
+42807,
+48993,
+57591,
+53140,
+20213,
+20365,
+55720,
+52926,
+19475,
+54208,
+48500,
+44899,
+6092,
+49264,
+11532,
+30863,
+18893,
+3931,
+35753,
+39141,
+15276,
+11824,
+24930,
+37124,
+174,
+64789,
+21408,
+26566,
+30183,
+7976,
+13672,
+2527,
+5834,
+64617,
+45285,
+36208,
+31498,
+30640,
+55049,
+16993,
+58901,
+17010,
+47631,
+19243,
+14158,
+10633,
+46932,
+24558,
+46207,
+23603,
+56456,
+5537,
+46426,
+15972,
+25419,
+10605,
+57264,
+58051,
+54173,
+51840,
+28171,
+43766,
+56588,
+11771,
+31361,
+32222,
+10213,
+25057,
+50978,
+31509,
+62778,
+31819,
+55761,
+43594,
+20864,
+31923,
+48271,
+39885,
+37919,
+4759,
+54427,
+17192,
+62641,
+41587,
+51527,
+54998,
+35991,
+45146,
+38915,
+47500,
+41930,
+37628,
+49924,
+786,
+30646,
+53830,
+20531,
+59119,
+5802,
+27885,
+30095,
+63380,
+19425,
+45495,
+19236,
+27075,
+53012,
+27793,
+32977,
+17050,
+20618,
+38130,
+6240,
+55457,
+25100,
+12946,
+21656,
+63338,
+33664,
+18423,
+4700,
+40932,
+34104,
+41216,
+46909,
+28799,
+65249,
+53207,
+42856,
+27415,
+2107,
+4509,
+59769,
+59390,
+52646,
+58303,
+8491,
+48382,
+43742,
+39746,
+39280,
+47404,
+29390,
+54371,
+43978,
+15417,
+63978,
+30498,
+54478,
+38305,
+63887,
+56397,
+42533,
+57327,
+12623,
+11552,
+65226,
+47991,
+51818,
+23979,
+9438,
+19442,
+25723,
+9909,
+60135,
+52069,
+34592,
+60114,
+7483,
+49469,
+59289,
+4061,
+41127,
+47820,
+4836,
+55742,
+46621,
+56675,
+10289,
+45240,
+63649,
+64635,
+3056,
+15994,
+49038,
+61649,
+20436,
+20239,
+7620,
+40049,
+9366,
+14337,
+42542,
+2398,
+30625,
+45181,
+50927,
+45732,
+39259,
+13277,
+29581,
+36487,
+44403,
+40862,
+11749,
+63993,
+32366,
+1062,
+19360,
+43918,
+6334,
+46689,
+33651,
+40651,
+20838,
+6533,
+63542,
+15087,
+15017,
+64015,
+37660,
+23885,
+13481,
+18196,
+5659,
+21920,
+65248,
+28901,
+46910,
+15817,
+40741,
+32451,
+21329,
+52151,
+19596,
+12679,
+37385,
+21014,
+2552,
+63356,
+27268,
+48616,
+28381,
+49940,
+25983,
+5169,
+38195,
+39130,
+7062,
+47008,
+18787,
+54714,
+27949,
+53399,
+23409,
+61963,
+15702,
+8611,
+51178,
+3292,
+57219,
+30047,
+52698,
+10267,
+12211,
+44678,
+22407,
+29953,
+14601,
+20005,
+43724,
+16223,
+31287,
+43471,
+64044,
+45464,
+2218,
+63825,
+11492,
+31917,
+10134,
+1637,
+53975,
+30998,
+1001,
+31723,
+30495,
+61414,
+39797,
+51516,
+62766,
+1113,
+29475,
+10889,
+51233,
+29656,
+37714,
+51633,
+50793,
+35959,
+27836,
+44029,
+59023,
+162,
+35785,
+35088,
+61231,
+34132,
+51153,
+16637,
+31123,
+42344,
+58934,
+21711,
+25987,
+54384,
+37512,
+19381,
+64270,
+5333,
+59923,
+43272,
+34780,
+59774,
+9976,
+717,
+24372,
+14341,
+58807,
+8338,
+7475,
+36127,
+30014,
+15936,
+4397,
+18315,
+49211,
+30419,
+21056,
+49223,
+45302,
+12390,
+29403,
+16675,
+47118,
+1435,
+44955,
+23718,
+55964,
+37706,
+54039,
+4795,
+47186,
+14562,
+37452,
+8549,
+36970,
+32173,
+61845,
+54991,
+1909,
+33044,
+25244,
+19531,
+34927,
+19697,
+15808,
+17375,
+60714,
+12736,
+41864,
+37584,
+57095,
+3161,
+24852,
+43564,
+6572,
+53653,
+4358,
+52172,
+9451,
+61357,
+49981,
+23098,
+10093,
+2215,
+64148,
+3920,
+32828,
+46581,
+63626,
+32495,
+21526,
+17345,
+57940,
+44581,
+19304,
+5375,
+3134,
+5499,
+17553,
+4231,
+44988,
+31650,
+53193,
+59485,
+64834,
+48827,
+17306,
+42189,
+35345,
+3637,
+17313,
+63952,
+41003,
+5667,
+60092,
+6586,
+51171,
+55647,
+17941,
+45554,
+54285,
+43770,
+61699,
+47877,
+13458,
+50589,
+32741,
+38334,
+22453,
+63527,
+54414,
+14644,
+20980,
+48939,
+26185,
+26853,
+12079,
+43148,
+38147,
+53404,
+38998,
+28349,
+17392,
+26833,
+10945,
+63652,
+6506,
+31717,
+59081,
+3321,
+37741,
+48401,
+49061,
+12639,
+14275,
+13741,
+56987,
+36867,
+33365,
+60657,
+10841,
+23243,
+2485,
+13894,
+9071,
+62540,
+16429,
+11461,
+28424,
+2302,
+37593,
+64954,
+6870,
+48436,
+14285,
+32586,
+6320,
+9633,
+9654,
+225,
+21750,
+3924,
+9986,
+32005,
+51743,
+42883,
+43539,
+4476,
+4173,
+3785,
+503,
+11574,
+22779,
+49522,
+23897,
+36181,
+17866,
+41626,
+7832,
+63120,
+46414,
+7757,
+58839,
+49714,
+26548,
+10865,
+41322,
+29435,
+15134,
+35045,
+20734,
+56982,
+5004,
+58179,
+58073,
+20225,
+19828,
+11711,
+15406,
+53232,
+12694,
+42813,
+32396,
+59540,
+4537,
+37362,
+24186,
+1074,
+12072,
+17475,
+8195,
+27484,
+49760,
+57026,
+13763,
+15452,
+50724,
+62047,
+13995,
+53649,
+64711,
+57753,
+51922,
+9393,
+43890,
+47157,
+12724,
+36300,
+40223,
+13402,
+46072,
+267,
+14918,
+65415,
+42248,
+1349,
+37527,
+3230,
+56390,
+43303,
+64692,
+7999,
+12068,
+4785,
+55665,
+32567,
+17325,
+5951,
+7059,
+19987,
+14195,
+26394,
+50263,
+38586,
+56606,
+46875,
+52196,
+13300,
+63710,
+35569,
+31863,
+27136,
+16602,
+38724,
+6344,
+55395,
+23919,
+41737,
+11867,
+30776,
+24600,
+40920,
+30392,
+11740,
+17757,
+37892,
+6485,
+22847,
+27724,
+2301,
+28556,
+11462,
+29342,
+32516,
+8470,
+59751,
+10786,
+48812,
+2364,
+59109,
+40606,
+36534,
+65364,
+39247,
+27735,
+53518,
+759,
+6296,
+35897,
+55842,
+56107,
+24050,
+29035,
+50824,
+17245,
+47666,
+31970,
+25846,
+34391,
+35020,
+64316,
+48724,
+45965,
+34988,
+14227,
+14119,
+9994,
+35330,
+63249,
+12925,
+26158,
+11746,
+49939,
+28784,
+48617,
+4076,
+38034,
+48054,
+40762,
+61144,
+24458,
+10191,
+11016,
+65306,
+50436,
+17913,
+2368,
+12284,
+55258,
+54985,
+12045,
+22106,
+36364,
+56199,
+4051,
+18779,
+44994,
+60226,
+20452,
+55640,
+3756,
+53240,
+29886,
+57407,
+17391,
+28583,
+38999,
+36085,
+31520,
+62885,
+29395,
+41043,
+34882,
+2620,
+38233,
+33438,
+20576,
+27707,
+24167,
+31937,
+45652,
+11850,
+50004,
+20706,
+47523,
+45047,
+26876,
+14490,
+28317,
+5317,
+53276,
+60016,
+15756,
+5408,
+33030,
+43193,
+5316,
+28326,
+14491,
+29726,
+9622,
+12900,
+32706,
+29839,
+59988,
+55698,
+48824,
+10099,
+20590,
+26275,
+60612,
+56900,
+16962,
+62339,
+43706,
+20917,
+48253,
+3884,
+13547,
+58190,
+57928,
+25077,
+50399,
+46648,
+56069,
+44140,
+19871,
+35675,
+9423,
+7908,
+15944,
+14399,
+64960,
+14451,
+2672,
+53377,
+540,
+50880,
+35770,
+6833,
+33997,
+40885,
+46327,
+28080,
+43196,
+53120,
+1347,
+42250,
+11892,
+47783,
+51868,
+48540,
+2465,
+52117,
+21412,
+24590,
+15685,
+54228,
+23085,
+24060,
+33302,
+10791,
+28136,
+35971,
+51555,
+46258,
+39963,
+38396,
+5427,
+38408,
+26167,
+11484,
+14826,
+45011,
+37765,
+5122,
+46963,
+4440,
+7114,
+26050,
+50094,
+8417,
+1488,
+34332,
+55631,
+34288,
+57952,
+41748,
+5872,
+56767,
+13080,
+35252,
+43367,
+38580,
+45502,
+38455,
+4011,
+5702,
+44152,
+58866,
+26599,
+46993,
+11636,
+61072,
+12436,
+47922,
+62900,
+46220,
+61436,
+26008,
+31846,
+890,
+29055,
+63808,
+48509,
+19527,
+62239,
+22443,
+55461,
+56932,
+46762,
+6288,
+48281,
+5326,
+47945,
+33781,
+16361,
+51547,
+44292,
+56243,
+23330,
+54875,
+58321,
+58244,
+38171,
+53185,
+63060,
+60006,
+60999,
+37038,
+41352,
+33970,
+43765,
+28966,
+51841,
+57173,
+21575,
+24547,
+2022,
+23974,
+5736,
+61951,
+33583,
+39500,
+60773,
+26502,
+25288,
+32097,
+44963,
+43000,
+17121,
+52857,
+36747,
+44524,
+18213,
+18053,
+59523,
+26497,
+26151,
+64077,
+59429,
+27055,
+46201,
+38869,
+30726,
+17561,
+23261,
+35970,
+28252,
+10792,
+38280,
+14541,
+22404,
+6211,
+40988,
+63517,
+30124,
+53514,
+10157,
+47415,
+33661,
+15991,
+46658,
+23049,
+32053,
+20960,
+29263,
+42536,
+29997,
+52362,
+39953,
+4005,
+63264,
+13818,
+31738,
+23714,
+20791,
+45116,
+35520,
+12156,
+46492,
+48259,
+11453,
+48470,
+8100,
+7193,
+9612,
+60718,
+60165,
+13350,
+48804,
+50791,
+51635,
+12603,
+58638,
+60075,
+13106,
+32772,
+50974,
+36865,
+56989,
+41507,
+5314,
+43195,
+28271,
+46328,
+34281,
+22756,
+4148,
+25148,
+49329,
+58328,
+8575,
+55764,
+33581,
+61953,
+60197,
+40153,
+40026,
+30668,
+11660,
+15035,
+34805,
+31700,
+46322,
+2317,
+36445,
+8965,
+24539,
+44368,
+48098,
+46149,
+5572,
+38961,
+17577,
+26655,
+33126,
+57403,
+4989,
+49817,
+45638,
+8245,
+18400,
+63312,
+1386,
+39361,
+60598,
+46360,
+52028,
+22741,
+27761,
+42944,
+34716,
+51731,
+60634,
+11726,
+33795,
+46435,
+11152,
+36827,
+46157,
+63935,
+44743,
+5791,
+51066,
+59093,
+5270,
+9724,
+51878,
+29401,
+12392,
+61630,
+4850,
+64187,
+37069,
+62308,
+57153,
+48628,
+45611,
+35613,
+3260,
+43210,
+24151,
+54361,
+31535,
+9014,
+6711,
+33287,
+1641,
+52247,
+14653,
+46571,
+26824,
+63884,
+14243,
+35912,
+31024,
+14097,
+45386,
+61235,
+33539,
+808,
+57356,
+63511,
+18675,
+18589,
+10623,
+10272,
+33736,
+59617,
+45785,
+7291,
+27368,
+35989,
+55000,
+36172,
+21898,
+40552,
+47551,
+9264,
+51412,
+51149,
+9640,
+5412,
+58770,
+30871,
+6083,
+39739,
+54840,
+38572,
+23672,
+36882,
+8443,
+16556,
+53398,
+28774,
+54715,
+38508,
+36380,
+63234,
+36983,
+584,
+46269,
+65089,
+1907,
+54993,
+15616,
+12771,
+50375,
+4488,
+27302,
+32101,
+10129,
+33727,
+16667,
+19460,
+34620,
+4264,
+27399,
+62364,
+8658,
+5072,
+49790,
+56810,
+53784,
+15967,
+48337,
+54156,
+55097,
+27769,
+26204,
+13376,
+14635,
+20872,
+23606,
+36602,
+15614,
+54995,
+27320,
+55727,
+13230,
+49737,
+53612,
+34226,
+50440,
+57233,
+53864,
+40198,
+64656,
+21375,
+3536,
+42840,
+2493,
+49272,
+44419,
+27433,
+16396,
+2571,
+30094,
+28927,
+5803,
+16453,
+56142,
+21649,
+63640,
+35915,
+1521,
+36731,
+41653,
+22272,
+4653,
+51949,
+52447,
+57702,
+1465,
+54943,
+24507,
+49004,
+4104,
+7792,
+26458,
+54642,
+26140,
+24866,
+58040,
+44826,
+44263,
+49478,
+24207,
+9230,
+6341,
+29132,
+41463,
+56701,
+61473,
+56618,
+43587,
+12897,
+37871,
+25697,
+2309,
+16825,
+15764,
+47176,
+22300,
+35936,
+5596,
+44028,
+28726,
+35960,
+62469,
+65132,
+9572,
+36860,
+11499,
+5546,
+10977,
+64350,
+20281,
+53579,
+35880,
+36807,
+13905,
+55165,
+56710,
+61803,
+3442,
+7101,
+3037,
+3129,
+7811,
+30345,
+27582,
+47882,
+13911,
+6224,
+18404,
+59157,
+40141,
+33465,
+51491,
+13799,
+59236,
+20047,
+26104,
+60763,
+65181,
+62215,
+36963,
+17137,
+32976,
+28919,
+53013,
+158,
+4535,
+59542,
+47049,
+46487,
+1243,
+25552,
+21609,
+7046,
+65445,
+11524,
+64105,
+14061,
+1786,
+7996,
+57687,
+1529,
+60098,
+20525,
+37297,
+60416,
+26203,
+27915,
+55098,
+21719,
+35464,
+48136,
+29858,
+23934,
+42943,
+28034,
+22742,
+46914,
+50155,
+25200,
+60987,
+15863,
+58767,
+50105,
+42472,
+51032,
+59569,
+63572,
+12114,
+1018,
+47980,
+59875,
+65237,
+3508,
+3064,
+22089,
+1968,
+3629,
+22623,
+3341,
+53517,
+28410,
+39248,
+25607,
+25166,
+48325,
+44888,
+50198,
+12482,
+26220,
+25370,
+2300,
+28426,
+22848,
+8253,
+38264,
+1402,
+8923,
+55117,
+7914,
+29503,
+49618,
+38023,
+63761,
+51766,
+29733,
+49556,
+49326,
+24166,
+28337,
+20577,
+14425,
+9673,
+60640,
+1701,
+62265,
+39211,
+26644,
+47836,
+12690,
+34910,
+20358,
+41160,
+23959,
+50639,
+58851,
+9285,
+1716,
+63286,
+29915,
+24171,
+5795,
+38050,
+20819,
+8478,
+14679,
+63721,
+20294,
+64256,
+39272,
+4530,
+3429,
+4644,
+8791,
+40738,
+24582,
+43280,
+27419,
+55913,
+52639,
+37691,
+39841,
+35102,
+17631,
+7617,
+60815,
+26710,
+63504,
+55701,
+61120,
+36079,
+7472,
+43324,
+5368,
+30908,
+794,
+40230,
+63764,
+46121,
+16565,
+47779,
+37108,
+59053,
+20209,
+41778,
+10762,
+57269,
+49017,
+48637,
+47427,
+46824,
+8011,
+64401,
+44289,
+63604,
+16928,
+32574,
+56732,
+54305,
+64581,
+64989,
+47087,
+7841,
+49183,
+8914,
+17850,
+12322,
+25651,
+36589,
+60779,
+5622,
+65200,
+15410,
+45185,
+18753,
+42086,
+2452,
+50523,
+4864,
+46180,
+62230,
+46765,
+38958,
+7327,
+59537,
+44224,
+62386,
+40093,
+46670,
+1394,
+25746,
+58253,
+19663,
+36159,
+17750,
+45974,
+49534,
+45564,
+51267,
+43997,
+44096,
+17018,
+4260,
+47881,
+27812,
+30346,
+23033,
+47320,
+20663,
+33851,
+32210,
+530,
+57639,
+16184,
+22203,
+36214,
+46551,
+51754,
+42261,
+58645,
+18984,
+13785,
+44407,
+50876,
+33829,
+59364,
+57245,
+15826,
+17848,
+8916,
+35544,
+22602,
+24087,
+27494,
+58156,
+36295,
+36332,
+55536,
+42777,
+29684,
+45421,
+10238,
+33656,
+55027,
+51681,
+31963,
+50671,
+3603,
+48390,
+45750,
+52630,
+31175,
+60579,
+17043,
+11971,
+4561,
+64949,
+21214,
+40494,
+35910,
+14245,
+33984,
+61282,
+60377,
+5184,
+61784,
+31704,
+44894,
+1217,
+22869,
+11816,
+15918,
+37823,
+1777,
+48693,
+58145,
+21424,
+61020,
+22045,
+39803,
+53731,
+63495,
+61460,
+37159,
+17484,
+1549,
+55940,
+57882,
+54638,
+31230,
+3816,
+58155,
+27553,
+24088,
+44981,
+39049,
+627,
+46023,
+59741,
+37853,
+44719,
+49759,
+28493,
+8196,
+47053,
+25279,
+7138,
+48160,
+4825,
+48984,
+32623,
+57601,
+10961,
+60797,
+49012,
+2932,
+5900,
+29210,
+13440,
+34459,
+7595,
+20958,
+32055,
+15132,
+29437,
+40557,
+22179,
+2876,
+25947,
+9581,
+30403,
+34190,
+17992,
+64489,
+7575,
+22086,
+45895,
+24129,
+22905,
+14052,
+5504,
+38847,
+64460,
+40383,
+61489,
+12357,
+41541,
+621,
+51103,
+46662,
+53548,
+23525,
+16395,
+27889,
+44420,
+35479,
+7820,
+29028,
+3252,
+57157,
+31827,
+3520,
+41527,
+44241,
+22812,
+33176,
+55912,
+27669,
+43281,
+29514,
+2106,
+28897,
+42857,
+54554,
+45600,
+2838,
+11197,
+32313,
+44918,
+36622,
+59298,
+11211,
+20512,
+29950,
+22843,
+35647,
+62363,
+27926,
+4265,
+64140,
+38450,
+14971,
+18285,
+43543,
+51452,
+22388,
+36951,
+12134,
+47516,
+16538,
+22976,
+36168,
+213,
+32157,
+56789,
+17511,
+50632,
+61664,
+52522,
+49680,
+11570,
+33452,
+59171,
+9031,
+54516,
+40245,
+26562,
+35988,
+27972,
+7292,
+37357,
+12353,
+25332,
+33208,
+9746,
+22082,
+23841,
+24721,
+11477,
+57550,
+7592,
+20060,
+48658,
+40916,
+50703,
+19773,
+35759,
+14723,
+8409,
+1505,
+1263,
+11667,
+14498,
+60367,
+36757,
+26408,
+41645,
+41752,
+51405,
+60608,
+8499,
+38344,
+8116,
+46782,
+23453,
+16898,
+18112,
+34139,
+25327,
+50181,
+29737,
+3353,
+8071,
+32940,
+20137,
+55726,
+27906,
+54996,
+51529,
+40754,
+50837,
+14487,
+47376,
+2769,
+64426,
+18127,
+20597,
+42741,
+59578,
+12,
+50414,
+62916,
+37197,
+32100,
+27934,
+4489,
+60911,
+35809,
+55917,
+48205,
+8377,
+1808,
+19202,
+3878,
+52140,
+14262,
+15665,
+64329,
+10589,
+13325,
+25904,
+59107,
+2366,
+17915,
+63142,
+45855,
+9881,
+3695,
+10516,
+54826,
+19503,
+33875,
+57283,
+37816,
+64136,
+24671,
+44035,
+48615,
+28786,
+63357,
+8242,
+39452,
+4310,
+6690,
+44822,
+45153,
+464,
+11699,
+54744,
+37403,
+50997,
+20867,
+47841,
+101,
+49475,
+35836,
+16169,
+58579,
+21630,
+21270,
+57669,
+39938,
+57399,
+12187,
+17622,
+22314,
+41846,
+57756,
+17076,
+16131,
+60082,
+41974,
+44613,
+11058,
+36660,
+1651,
+51296,
+43295,
+60841,
+16640,
+47351,
+59815,
+34379,
+49169,
+4750,
+26900,
+64220,
+65289,
+797,
+59050,
+42692,
+29892,
+34382,
+9520,
+4193,
+64116,
+48323,
+25168,
+43379,
+57188,
+63646,
+1284,
+32795,
+36940,
+43085,
+17504,
+65231,
+63819,
+2253,
+4217,
+60768,
+20660,
+57037,
+38202,
+57422,
+29781,
+22110,
+64797,
+60217,
+31063,
+22417,
+58782,
+45481,
+46787,
+13186,
+63068,
+48304,
+50869,
+63210,
+20992,
+18832,
+15671,
+39314,
+41872,
+16172,
+48320,
+10717,
+2294,
+18220,
+17895,
+59653,
+3421,
+27024,
+10727,
+5383,
+62434,
+23727,
+63066,
+13188,
+29075,
+16809,
+8044,
+60471,
+63623,
+47929,
+21869,
+15263,
+6875,
+49230,
+50253,
+3899,
+33156,
+4387,
+22001,
+25573,
+37290,
+60111,
+40193,
+64245,
+16601,
+28443,
+31864,
+48782,
+51890,
+26795,
+30830,
+60109,
+37292,
+39371,
+6581,
+58359,
+5069,
+60794,
+26108,
+35557,
+17140,
+45511,
+46808,
+20854,
+14136,
+2458,
+20443,
+56514,
+51017,
+35778,
+24176,
+11611,
+1158,
+7352,
+14030,
+4074,
+48619,
+58034,
+35565,
+6338,
+39195,
+50478,
+39002,
+16221,
+43726,
+61863,
+50318,
+14148,
+30970,
+34711,
+24668,
+19724,
+25744,
+1396,
+38141,
+50567,
+1205,
+61571,
+20034,
+37318,
+61527,
+25830,
+23873,
+55834,
+33445,
+53011,
+28921,
+19237,
+41436,
+15031,
+64496,
+23128,
+56499,
+58547,
+42789,
+26686,
+51076,
+25051,
+52000,
+35476,
+12051,
+31807,
+41145,
+11270,
+39593,
+46200,
+28143,
+59430,
+41721,
+31516,
+18277,
+44272,
+25690,
+22197,
+63,
+1025,
+45928,
+52085,
+4922,
+9333,
+6917,
+31879,
+10512,
+8423,
+6855,
+35035,
+55581,
+80,
+57447,
+62281,
+45142,
+10821,
+37590,
+29158,
+13201,
+35887,
+10726,
+27164,
+3422,
+1705,
+2959,
+9588,
+9877,
+51519,
+10016,
+54167,
+63555,
+40362,
+53763,
+41384,
+11178,
+7335,
+14804,
+61191,
+182,
+2866,
+42016,
+49842,
+32544,
+3813,
+13971,
+8430,
+33061,
+23112,
+34838,
+41292,
+420,
+16502,
+40289,
+624,
+36492,
+20307,
+23059,
+61994,
+3276,
+21840,
+60857,
+31712,
+64172,
+64529,
+19981,
+11254,
+46893,
+6406,
+25778,
+41206,
+39536,
+11412,
+47800,
+2510,
+65277,
+58959,
+59350,
+64023,
+48948,
+10696,
+37582,
+41866,
+11317,
+14402,
+9784,
+11943,
+61944,
+22169,
+37316,
+20036,
+15884,
+52575,
+52735,
+15273,
+37086,
+8932,
+57057,
+50860,
+47564,
+65053,
+2172,
+11522,
+65447,
+45656,
+12815,
+53843,
+4199,
+35469,
+45718,
+18338,
+10847,
+15473,
+73,
+55796,
+43246,
+15193,
+51222,
+59270,
+62850,
+56193,
+30360,
+29183,
+26448,
+8396,
+25632,
+25594,
+11019,
+25294,
+65481,
+14300,
+57862,
+23939,
+8523,
+470,
+39518,
+15253,
+41199,
+40521,
+41034,
+9804,
+63486,
+52015,
+46872,
+30308,
+64219,
+27221,
+4751,
+30025,
+30338,
+13219,
+31198,
+7893,
+1952,
+55840,
+35899,
+22771,
+4977,
+11468,
+65498,
+5175,
+42557,
+7345,
+7611,
+17229,
+15189,
+13734,
+11801,
+47374,
+14489,
+28328,
+45048,
+42094,
+59284,
+24352,
+1578,
+3861,
+561,
+11779,
+19434,
+61451,
+14071,
+53322,
+13528,
+22541,
+19797,
+33703,
+2523,
+754,
+34983,
+15679,
+23545,
+12078,
+28589,
+26186,
+45264,
+50948,
+29162,
+41090,
+12191,
+34409,
+58611,
+39768,
+31068,
+64304,
+33993,
+18638,
+16358,
+22063,
+12408,
+16648,
+8665,
+10944,
+28581,
+17393,
+30652,
+8288,
+43044,
+32854,
+8714,
+13146,
+63883,
+27992,
+46572,
+35510,
+35201,
+41475,
+32237,
+804,
+51128,
+52870,
+10810,
+48934,
+37096,
+59194,
+36407,
+5043,
+16853,
+32194,
+22399,
+55804,
+533,
+47006,
+7064,
+20112,
+48454,
+38816,
+62710,
+48029,
+11617,
+30829,
+27132,
+51891,
+34208,
+25638,
+31324,
+6221,
+40564,
+3956,
+55435,
+60328,
+20022,
+30696,
+10473,
+5939,
+50235,
+22008,
+31967,
+23948,
+14012,
+22305,
+18414,
+54872,
+634,
+18372,
+40180,
+22971,
+52931,
+24760,
+57063,
+44159,
+22608,
+60358,
+35554,
+12384,
+46795,
+23200,
+56818,
+6684,
+56254,
+17462,
+39065,
+7317,
+41743,
+46725,
+55539,
+45871,
+6723,
+52877,
+40662,
+8809,
+49641,
+42230,
+11415,
+31053,
+10188,
+63216,
+60086,
+50230,
+29267,
+39458,
+49537,
+55969,
+63387,
+30589,
+23767,
+18445,
+8361,
+60812,
+20242,
+11410,
+39538,
+18199,
+58599,
+17082,
+38737,
+2828,
+36395,
+52539,
+64232,
+61065,
+52199,
+33740,
+60012,
+3928,
+63503,
+27660,
+60816,
+63969,
+8282,
+30020,
+13824,
+14924,
+64651,
+57170,
+36044,
+46816,
+55900,
+30083,
+3724,
+36473,
+5077,
+41494,
+1672,
+2939,
+60032,
+38092,
+1078,
+5346,
+51075,
+27066,
+42790,
+42099,
+17861,
+51313,
+3655,
+26119,
+62911,
+35004,
+2609,
+19411,
+11051,
+15899,
+11843,
+43720,
+59647,
+64414,
+19335,
+37396,
+34812,
+49879,
+58171,
+64399,
+8013,
+60525,
+53728,
+39069,
+56183,
+58587,
+48760,
+33125,
+28049,
+17578,
+18336,
+45720,
+52676,
+46412,
+63122,
+5295,
+24565,
+33954,
+47835,
+27699,
+39212,
+42372,
+9380,
+15629,
+3368,
+56648,
+3415,
+6006,
+48911,
+29673,
+26453,
+44770,
+24848,
+31853,
+15158,
+49010,
+60799,
+20560,
+62086,
+25209,
+20390,
+36599,
+18038,
+56706,
+57203,
+63619,
+18804,
+57010,
+15459,
+32867,
+63562,
+65372,
+60343,
+32271,
+133,
+29835,
+14237,
+14910,
+39470,
+20173,
+64963,
+64623,
+5600,
+46992,
+28214,
+58867,
+53431,
+63302,
+6895,
+17041,
+60581,
+10807,
+23971,
+10655,
+6305,
+19633,
+51028,
+34075,
+5665,
+41005,
+40262,
+64716,
+64499,
+18771,
+37933,
+58211,
+59964,
+37486,
+47961,
+40468,
+6733,
+7816,
+48242,
+58046,
+32787,
+11764,
+30182,
+28999,
+21409,
+32243,
+35987,
+27370,
+40246,
+56538,
+16032,
+7371,
+8082,
+14790,
+7436,
+47182,
+25928,
+19630,
+8928,
+35232,
+10864,
+28520,
+49715,
+43256,
+5081,
+25522,
+8906,
+50675,
+442,
+12132,
+36953,
+37189,
+29453,
+15058,
+38438,
+49317,
+5355,
+44504,
+51899,
+16509,
+48600,
+30676,
+13099,
+31884,
+24888,
+4875,
+4423,
+22059,
+33432,
+29934,
+8316,
+35492,
+6159,
+6310,
+49120,
+25803,
+19560,
+40266,
+29870,
+10168,
+46513,
+12562,
+314,
+57840,
+1355,
+58240,
+25287,
+28159,
+60774,
+33320,
+60556,
+26150,
+28147,
+59524,
+60251,
+4366,
+34818,
+9126,
+52534,
+24624,
+20555,
+38046,
+64911,
+29047,
+41282,
+3457,
+18669,
+20652,
+34418,
+44235,
+25550,
+1245,
+20146,
+64985,
+18437,
+26144,
+48074,
+38782,
+58221,
+25732,
+11808,
+38877,
+9355,
+49518,
+45944,
+35188,
+58038,
+24868,
+41686,
+13952,
+54641,
+27864,
+7793,
+45702,
+19863,
+44769,
+26633,
+29674,
+53939,
+62249,
+8395,
+26923,
+29184,
+29537,
+23402,
+35056,
+25560,
+58441,
+2144,
+48367,
+15785,
+13448,
+60051,
+47938,
+53632,
+62009,
+40007,
+7302,
+30010,
+60238,
+7955,
+34244,
+30761,
+20170,
+64664,
+35400,
+61256,
+24608,
+45450,
+33024,
+52635,
+32833,
+19512,
+5798,
+12636,
+1645,
+57535,
+25590,
+46564,
+6782,
+41644,
+27341,
+36758,
+58476,
+45552,
+17943,
+21451,
+46282,
+9644,
+47348,
+36362,
+22108,
+29783,
+36530,
+50262,
+28453,
+14196,
+63096,
+57131,
+49452,
+2784,
+24898,
+64243,
+40195,
+32807,
+45628,
+9663,
+4782,
+25158,
+54191,
+63701,
+14537,
+33617,
+13579,
+47455,
+64302,
+31070,
+13591,
+25426,
+23394,
+32981,
+30544,
+39615,
+16731,
+14748,
+4129,
+20519,
+31789,
+11655,
+41254,
+23906,
+49885,
+23597,
+16245,
+15052,
+3484,
+50362,
+50538,
+46001,
+18711,
+57528,
+45561,
+39461,
+22878,
+51141,
+56305,
+59198,
+38436,
+15060,
+64176,
+6121,
+53302,
+43012,
+11788,
+44491,
+25621,
+44024,
+20690,
+2820,
+58824,
+32252,
+47358,
+3271,
+37837,
+38894,
+23166,
+57784,
+4524,
+26042,
+9843,
+46291,
+127,
+513,
+16449,
+55072,
+30258,
+19888,
+26026,
+22754,
+34283,
+974,
+1547,
+17486,
+8767,
+39235,
+13078,
+56769,
+39232,
+52460,
+2923,
+16968,
+5829,
+56129,
+15335,
+22227,
+31941,
+55983,
+13413,
+172,
+37126,
+37803,
+39931,
+31770,
+57621,
+36036,
+60044,
+20180,
+54029,
+64547,
+23923,
+19804,
+36709,
+52261,
+60611,
+28305,
+20591,
+62384,
+44226,
+52243,
+62925,
+7255,
+33754,
+52982,
+1280,
+37860,
+61008,
+21755,
+25826,
+54756,
+43208,
+3262,
+25790,
+36651,
+18134,
+12064,
+45809,
+13725,
+15151,
+43812,
+55483,
+1533,
+9870,
+32972,
+15544,
+22569,
+34564,
+11620,
+21685,
+16009,
+34849,
+50913,
+20904,
+11218,
+34005,
+5191,
+25259,
+7922,
+762,
+25082,
+52602,
+17538,
+60572,
+42975,
+62026,
+35981,
+49865,
+38363,
+23666,
+25369,
+27727,
+12483,
+20480,
+35747,
+10922,
+65532,
+49154,
+36410,
+14044,
+51740,
+30038,
+47458,
+44334,
+3361,
+40216,
+13375,
+27914,
+27770,
+60417,
+10448,
+48710,
+49775,
+46032,
+31664,
+48107,
+3310,
+42221,
+39683,
+30469,
+59479,
+10042,
+65029,
+47441,
+45263,
+26852,
+28590,
+48940,
+42867,
+39562,
+56230,
+51601,
+62608,
+3918,
+64150,
+37498,
+4940,
+7389,
+46409,
+35831,
+64340,
+17262,
+42642,
+11483,
+28244,
+38409,
+58957,
+65279,
+32642,
+50394,
+21196,
+14112,
+11745,
+28384,
+12926,
+13152,
+17535,
+19618,
+31602,
+64076,
+28146,
+26498,
+60557,
+48466,
+14886,
+32578,
+48073,
+26474,
+18438,
+30788,
+24865,
+27862,
+54643,
+42838,
+3538,
+65038,
+64880,
+47855,
+59448,
+23587,
+55683,
+7883,
+36053,
+21594,
+15078,
+29964,
+34739,
+29304,
+3284,
+60311,
+22917,
+62910,
+26680,
+3656,
+5897,
+1990,
+65143,
+7672,
+50215,
+48834,
+3662,
+12382,
+35556,
+27123,
+60795,
+10963,
+60762,
+27800,
+20048,
+62632,
+4492,
+1941,
+56595,
+56896,
+22810,
+44243,
+30635,
+10742,
+33468,
+10994,
+20434,
+61651,
+51992,
+62746,
+19897,
+57294,
+38272,
+61228,
+6862,
+30301,
+13127,
+36269,
+38801,
+16001,
+19975,
+39190,
+42660,
+35282,
+41781,
+20836,
+40653,
+54584,
+4495,
+50626,
+38932,
+45365,
+875,
+62321,
+54571,
+12953,
+32385,
+21761,
+42822,
+4932,
+58975,
+58356,
+17225,
+62521,
+40910,
+16344,
+50093,
+28235,
+7115,
+62158,
+44837,
+39990,
+33965,
+10974,
+9842,
+26321,
+4525,
+35318,
+29898,
+31953,
+37376,
+4765,
+3645,
+54735,
+13987,
+1936,
+29809,
+51848,
+42398,
+7929,
+22753,
+26312,
+19889,
+39913,
+4032,
+43714,
+49846,
+65066,
+38626,
+50285,
+54073,
+36197,
+35163,
+56072,
+58717,
+53063,
+22253,
+65217,
+31845,
+28205,
+61437,
+19523,
+24428,
+20457,
+54707,
+24330,
+38562,
+54126,
+38812,
+47339,
+59880,
+52178,
+8945,
+653,
+40185,
+49241,
+19093,
+36092,
+47658,
+54383,
+28712,
+21712,
+1483,
+5168,
+28782,
+49941,
+4485,
+20264,
+12785,
+44005,
+23536,
+22669,
+39355,
+9308,
+12793,
+41406,
+42634,
+34000,
+56525,
+24271,
+7564,
+3319,
+59083,
+62349,
+46537,
+7207,
+56964,
+18159,
+62770,
+60586,
+31472,
+861,
+5247,
+43172,
+17323,
+32569,
+20471,
+33016,
+23473,
+9580,
+27458,
+2877,
+54266,
+38480,
+19338,
+60492,
+48686,
+7107,
+56032,
+50526,
+12126,
+58626,
+33073,
+56528,
+49583,
+60485,
+53575,
+1979,
+19629,
+26553,
+47183,
+24826,
+25464,
+52060,
+34992,
+39170,
+48807,
+57934,
+48477,
+39702,
+24956,
+20749,
+25183,
+23487,
+39826,
+3766,
+34257,
+65494,
+57736,
+16192,
+43521,
+13031,
+59106,
+27286,
+13326,
+35157,
+41937,
+21693,
+46237,
+8399,
+17735,
+63029,
+20862,
+43596,
+61138,
+47224,
+45378,
+45319,
+60669,
+44801,
+8557,
+2352,
+19832,
+3051,
+55424,
+14927,
+51283,
+34726,
+59682,
+61295,
+55138,
+10407,
+31592,
+48422,
+19826,
+20227,
+64012,
+52422,
+9931,
+24000,
+13228,
+55729,
+3897,
+50255,
+29881,
+2192,
+778,
+4544,
+2063,
+29499,
+5495,
+35691,
+23315,
+39344,
+35069,
+45847,
+23491,
+46961,
+5124,
+31625,
+34390,
+28397,
+31971,
+5267,
+58733,
+48766,
+63924,
+44519,
+41680,
+32525,
+29594,
+17059,
+39619,
+40582,
+53281,
+4691,
+23872,
+27080,
+61528,
+24839,
+54755,
+26262,
+21756,
+58393,
+368,
+60565,
+47397,
+64707,
+1424,
+25204,
+36047,
+9893,
+34293,
+51769,
+9917,
+49950,
+19541,
+12272,
+43153,
+2481,
+45127,
+16256,
+40173,
+19559,
+26514,
+49121,
+59677,
+46925,
+59453,
+37580,
+10698,
+18935,
+51393,
+46692,
+47700,
+21794,
+36650,
+26258,
+3263,
+62988,
+21478,
+64406,
+21866,
+3073,
+11535,
+51091,
+48556,
+36386,
+41205,
+26977,
+6407,
+42148,
+13471,
+23045,
+32724,
+15067,
+8565,
+6925,
+38693,
+34470,
+14167,
+48335,
+15969,
+44457,
+17639,
+20929,
+19612,
+20030,
+19756,
+57474,
+45581,
+60766,
+4219,
+50831,
+57391,
+36507,
+62287,
+64832,
+59487,
+44281,
+58252,
+27596,
+1395,
+27089,
+19725,
+54531,
+4122,
+36799,
+14022,
+9979,
+24909,
+32659,
+43491,
+38030,
+11807,
+26470,
+58222,
+62590,
+29922,
+62279,
+57449,
+54784,
+31203,
+9908,
+28864,
+19443,
+4831,
+9563,
+13192,
+14854,
+16404,
+15793,
+24678,
+49493,
+23143,
+42049,
+39624,
+61772,
+8759,
+6233,
+43829,
+55928,
+63403,
+2797,
+55677,
+17969,
+23827,
+60623,
+63377,
+2308,
+27845,
+37872,
+30436,
+3599,
+63226,
+48714,
+22196,
+27049,
+44273,
+2513,
+17806,
+6519,
+14003,
+48770,
+57083,
+4994,
+61510,
+50513,
+16635,
+51155,
+41116,
+60404,
+33242,
+44475,
+62392,
+9152,
+8629,
+4884,
+3194,
+14934,
+53023,
+20134,
+34187,
+22393,
+13706,
+20178,
+60046,
+32674,
+35398,
+64666,
+7308,
+35724,
+8757,
+61774,
+17265,
+36588,
+27619,
+12323,
+14641,
+54460,
+9557,
+10485,
+32757,
+34330,
+1490,
+3913,
+29856,
+48138,
+31323,
+26792,
+34209,
+35486,
+63525,
+22455,
+25593,
+26921,
+8397,
+46239,
+65358,
+55597,
+63151,
+8437,
+45647,
+39513,
+61506,
+44023,
+26334,
+44492,
+110,
+15352,
+59310,
+21780,
+61822,
+61850,
+49376,
+60862,
+53858,
+44970,
+65299,
+25165,
+27733,
+39249,
+33768,
+19292,
+56903,
+57901,
+48731,
+7867,
+38292,
+8139,
+15403,
+65304,
+11018,
+26920,
+25633,
+22456,
+46563,
+26412,
+57536,
+53471,
+19730,
+45755,
+13174,
+37423,
+61533,
+25001,
+56665,
+5926,
+7133,
+16693,
+33272,
+2729,
+19278,
+37289,
+27142,
+22002,
+47071,
+45758,
+8971,
+42901,
+45971,
+7980,
+62007,
+53634,
+18541,
+12750,
+58440,
+26443,
+35057,
+459,
+7552,
+10127,
+32103,
+35605,
+21608,
+27785,
+1244,
+26479,
+44236,
+20699,
+39491,
+31573,
+23072,
+25187,
+8975,
+46737,
+44450,
+40577,
+64703,
+54130,
+10694,
+48950,
+62452,
+40951,
+56040,
+44440,
+3527,
+39104,
+58236,
+5630,
+18390,
+33092,
+10907,
+11783,
+8905,
+26544,
+5082,
+42201,
+10342,
+64841,
+4433,
+55502,
+41169,
+53174,
+50222,
+25383,
+37471,
+34870,
+6017,
+47493,
+2500,
+49763,
+61973,
+29523,
+58650,
+61988,
+52885,
+12781,
+41732,
+9271,
+9112,
+59720,
+2604,
+20203,
+19376,
+44592,
+17421,
+23636,
+57916,
+36700,
+56172,
+41707,
+13560,
+5578,
+41339,
+60010,
+33742,
+4406,
+22962,
+23678,
+52657,
+16127,
+22891,
+53700,
+9630,
+47097,
+34751,
+63126,
+19227,
+8021,
+14110,
+21198,
+52059,
+25925,
+24827,
+43790,
+55017,
+30511,
+50900,
+44667,
+41806,
+4163,
+42386,
+54149,
+47230,
+61224,
+9713,
+50918,
+14908,
+14239,
+22244,
+23579,
+3150,
+37938,
+18868,
+9549,
+19717,
+56554,
+1453,
+55126,
+48584,
+50070,
+60881,
+31073,
+17386,
+47336,
+6940,
+53896,
+10175,
+10312,
+23393,
+26371,
+13592,
+44835,
+62160,
+34769,
+15538,
+10604,
+28972,
+15973,
+19091,
+49243,
+8440,
+62378,
+63608,
+56802,
+15361,
+31319,
+34387,
+60257,
+10839,
+60659,
+5309,
+45087,
+32050,
+8176,
+10292,
+61081,
+20405,
+35994,
+6379,
+37731,
+32073,
+57281,
+33877,
+62112,
+47144,
+21406,
+64791,
+14252,
+30813,
+59385,
+5339,
+37470,
+25512,
+50223,
+40715,
+46617,
+53289,
+56687,
+47467,
+7110,
+61916,
+49371,
+50667,
+58834,
+2299,
+27726,
+26221,
+23667,
+50865,
+8694,
+49280,
+39732,
+45979,
+56976,
+45073,
+18692,
+42027,
+47164,
+38766,
+42567,
+41054,
+17709,
+64883,
+39544,
+16297,
+25266,
+48445,
+41660,
+61912,
+30943,
+5425,
+38398,
+1293,
+39151,
+32731,
+11672,
+37640,
+882,
+17814,
+7204,
+63928,
+52129,
+33207,
+27364,
+12354,
+20784,
+31163,
+50180,
+27328,
+34140,
+383,
+43863,
+42638,
+62175,
+39331,
+52163,
+47920,
+12438,
+60106,
+54760,
+29236,
+37061,
+52136,
+53443,
+8542,
+65001,
+246,
+64742,
+37887,
+1774,
+13773,
+20018,
+47611,
+59147,
+25030,
+32310,
+64392,
+56773,
+17666,
+44067,
+65480,
+26918,
+11020,
+38166,
+38976,
+9000,
+32096,
+28158,
+26503,
+58241,
+24994,
+2408,
+10747,
+42422,
+21462,
+7137,
+27481,
+47054,
+31266,
+24531,
+38954,
+822,
+49532,
+45976,
+39025,
+35668,
+52704,
+20752,
+48444,
+25350,
+16298,
+49394,
+51876,
+9726,
+4906,
+7921,
+26234,
+5192,
+33268,
+57485,
+15500,
+44187,
+11393,
+35818,
+9411,
+21343,
+49034,
+38544,
+64914,
+14436,
+19530,
+28664,
+33045,
+58571,
+29989,
+6629,
+54200,
+2724,
+30617,
+1540,
+13993,
+62049,
+7049,
+62963,
+29471,
+22347,
+5611,
+35000,
+9116,
+59320,
+7919,
+4908,
+24806,
+18259,
+22659,
+55800,
+45714,
+56740,
+56381,
+2664,
+5362,
+54051,
+50713,
+19384,
+16949,
+20389,
+26624,
+62087,
+54612,
+46814,
+36046,
+25818,
+1425,
+6665,
+60986,
+27757,
+50156,
+62585,
+52324,
+60275,
+437,
+59104,
+13033,
+35716,
+52235,
+1829,
+33056,
+8974,
+25544,
+23073,
+30662,
+23486,
+25915,
+20750,
+52706,
+190,
+2746,
+59001,
+19627,
+1981,
+38523,
+11775,
+2856,
+11677,
+20569,
+138,
+43378,
+27209,
+48324,
+27732,
+25608,
+65300,
+34158,
+16073,
+32215,
+4983,
+54190,
+26381,
+4783,
+12070,
+1076,
+38094,
+4049,
+56201,
+24236,
+24164,
+49328,
+28075,
+4149,
+60374,
+4178,
+15558,
+42607,
+11717,
+42076,
+34549,
+6494,
+15520,
+11237,
+33376,
+55037,
+22448,
+31466,
+11633,
+52790,
+41891,
+23081,
+59760,
+7796,
+32925,
+50142,
+13254,
+20337,
+23257,
+36010,
+56857,
+18947,
+44014,
+11880,
+49111,
+53476,
+59381,
+6198,
+15891,
+51026,
+19635,
+55355,
+32129,
+15346,
+30487,
+10476,
+295,
+30334,
+24064,
+12945,
+28912,
+55458,
+31677,
+57575,
+1277,
+4565,
+34090,
+65315,
+12456,
+59472,
+50656,
+36500,
+14715,
+18514,
+42116,
+22855,
+34229,
+52601,
+26231,
+763,
+49840,
+42018,
+50398,
+28293,
+57929,
+21884,
+57207,
+59912,
+36347,
+50250,
+17575,
+38963,
+58805,
+14343,
+42113,
+24612,
+672,
+40706,
+42831,
+22550,
+7775,
+18349,
+50977,
+28959,
+10214,
+2362,
+48814,
+19405,
+51999,
+27064,
+51077,
+65121,
+51641,
+56468,
+32690,
+60887,
+44322,
+38009,
+29628,
+335,
+50129,
+12307,
+4756,
+59917,
+64325,
+50872,
+62995,
+14496,
+11669,
+32309,
+25301,
+59148,
+19935,
+62701,
+22702,
+24743,
+16764,
+5466,
+60647,
+58554,
+58132,
+16102,
+42702,
+24337,
+38629,
+24504,
+53257,
+7580,
+9666,
+63294,
+31191,
+52547,
+43065,
+59145,
+47613,
+24789,
+20709,
+21276,
+56664,
+25582,
+61534,
+44331,
+63458,
+65128,
+20401,
+2407,
+25285,
+58242,
+58323,
+46194,
+57179,
+45862,
+64522,
+23721,
+43638,
+37570,
+17987,
+64026,
+48047,
+36099,
+38076,
+43359,
+47934,
+593,
+43582,
+51624,
+59315,
+38748,
+61582,
+8968,
+57566,
+23494,
+43008,
+5956,
+45878,
+50739,
+13864,
+24240,
+57868,
+21086,
+39997,
+835,
+53387,
+20748,
+25917,
+39703,
+12257,
+34737,
+29966,
+33676,
+36178,
+18929,
+52531,
+641,
+54411,
+31824,
+63732,
+47192,
+47814,
+5145,
+50803,
+4089,
+4055,
+59608,
+10957,
+4660,
+23159,
+11187,
+6427,
+37123,
+29004,
+11825,
+2190,
+29883,
+9021,
+14522,
+64288,
+52042,
+7087,
+54008,
+50160,
+43779,
+7146,
+48317,
+5379,
+5032,
+62504,
+35774,
+12546,
+2644,
+32658,
+25737,
+9980,
+64645,
+36484,
+20915,
+43708,
+40807,
+30542,
+32983,
+20985,
+64242,
+26388,
+2785,
+56105,
+55844,
+15114,
+24295,
+3478,
+11040,
+54723,
+4874,
+26525,
+31885,
+36724,
+44700,
+39466,
+10954,
+58407,
+5002,
+56984,
+23903,
+62821,
+43982,
+12659,
+38790,
+56729,
+60262,
+14105,
+23273,
+63305,
+41685,
+26462,
+58039,
+27861,
+26141,
+30789,
+20207,
+59055,
+53909,
+7943,
+16377,
+20973,
+57042,
+16582,
+29843,
+63678,
+43563,
+28652,
+3162,
+29071,
+31852,
+26631,
+44771,
+30748,
+17444,
+23417,
+43169,
+47310,
+2910,
+54754,
+25828,
+61529,
+10027,
+11170,
+9376,
+18634,
+46575,
+64864,
+40474,
+22707,
+65310,
+43789,
+25463,
+25926,
+47184,
+4797,
+15313,
+40156,
+18921,
+17620,
+12189,
+41092,
+17329,
+46187,
+32717,
+11032,
+8032,
+33516,
+48114,
+41903,
+55430,
+24793,
+18258,
+25223,
+4909,
+34478,
+54331,
+62596,
+5507,
+56745,
+37865,
+40956,
+49719,
+36741,
+33087,
+18257,
+24808,
+55431,
+47521,
+20708,
+25005,
+47614,
+21353,
+63583,
+9699,
+14007,
+38220,
+14701,
+33349,
+47111,
+32910,
+55420,
+43038,
+17482,
+37161,
+4394,
+9291,
+59423,
+33000,
+23397,
+46399,
+13381,
+55180,
+50546,
+54245,
+13055,
+32132,
+34600,
+57062,
+26768,
+52932,
+6901,
+19457,
+29462,
+47238,
+44460,
+17996,
+50797,
+42972,
+33028,
+5410,
+9642,
+46284,
+49358,
+31141,
+16763,
+25025,
+22703,
+44388,
+64035,
+55714,
+6133,
+204,
+4157,
+53039,
+14968,
+64334,
+15213,
+21828,
+40634,
+34996,
+44952,
+20069,
+52780,
+14772,
+7658,
+42965,
+11476,
+27359,
+23842,
+40896,
+14808,
+45672,
+20770,
+1241,
+46489,
+15230,
+30745,
+42596,
+43160,
+62325,
+45434,
+878,
+46451,
+14585,
+18679,
+11063,
+1239,
+20772,
+5235,
+61395,
+12124,
+50528,
+24311,
+46822,
+47429,
+10396,
+9494,
+29874,
+16943,
+41097,
+51226,
+1407,
+35795,
+58297,
+11037,
+47328,
+34436,
+59273,
+54153,
+49492,
+25715,
+15794,
+21133,
+36714,
+53789,
+15600,
+44034,
+27271,
+64137,
+19723,
+27091,
+34712,
+32997,
+51117,
+50298,
+55418,
+32912,
+7938,
+18319,
+10708,
+11341,
+8027,
+33643,
+21672,
+22901,
+31539,
+37264,
+19211,
+29041,
+4512,
+7320,
+43382,
+12825,
+7640,
+13810,
+17829,
+41694,
+40818,
+50730,
+48793,
+16351,
+12702,
+55782,
+2655,
+41085,
+51359,
+58026,
+4949,
+34067,
+934,
+3015,
+39484,
+49194,
+20554,
+26490,
+52535,
+16878,
+38184,
+14835,
+32288,
+40936,
+37104,
+35146,
+54296,
+45288,
+671,
+25065,
+42114,
+18516,
+45449,
+26422,
+61257,
+60836,
+36632,
+31434,
+22461,
+33248,
+40919,
+28434,
+30777,
+34852,
+49252,
+30890,
+19793,
+18592,
+16261,
+17826,
+15684,
+28259,
+21413,
+9161,
+3084,
+40003,
+9498,
+3223,
+43279,
+27671,
+40739,
+15819,
+59295,
+10082,
+52428,
+43694,
+47664,
+17247,
+63110,
+29960,
+10669,
+60397,
+44432,
+46952,
+35827,
+33953,
+26647,
+5296,
+9800,
+20516,
+53815,
+53030,
+46206,
+28979,
+46933,
+41413,
+23762,
+8421,
+10514,
+3697,
+16465,
+59325,
+37407,
+2021,
+28167,
+21576,
+4961,
+12905,
+34364,
+9771,
+31958,
+44367,
+28056,
+8966,
+61584,
+16435,
+1584,
+36925,
+59871,
+38953,
+25276,
+31267,
+61214,
+30313,
+15928,
+48299,
+54375,
+62878,
+8902,
+51375,
+34026,
+2761,
+57684,
+64695,
+50498,
+17368,
+59931,
+12477,
+51835,
+19852,
+17794,
+63192,
+31735,
+49003,
+27868,
+54944,
+53256,
+25015,
+38630,
+55734,
+63472,
+36040,
+19587,
+42228,
+49643,
+20079,
+36141,
+37194,
+30656,
+54954,
+48649,
+24280,
+2719,
+62478,
+18882,
+10243,
+46889,
+22969,
+40182,
+47829,
+61500,
+65197,
+29193,
+16988,
+5853,
+4244,
+51072,
+53934,
+35978,
+62476,
+2721,
+52368,
+41962,
+11263,
+53628,
+54107,
+29716,
+5832,
+2529,
+60953,
+65395,
+63214,
+10190,
+28374,
+61145,
+59344,
+12014,
+9372,
+62881,
+40903,
+14179,
+8103,
+9390,
+36374,
+38689,
+53347,
+40719,
+48209,
+18058,
+34506,
+48593,
+40848,
+14312,
+35107,
+2116,
+16749,
+9677,
+6617,
+24055,
+58501,
+8171,
+52105,
+20456,
+26005,
+19524,
+1253,
+13746,
+55107,
+44813,
+14381,
+39860,
+5134,
+35137,
+33053,
+52224,
+15120,
+1726,
+34610,
+64133,
+56616,
+61475,
+36683,
+17718,
+36004,
+50108,
+56834,
+32649,
+50329,
+12805,
+56188,
+59698,
+59824,
+20183,
+44319,
+32700,
+35925,
+63369,
+16665,
+33729,
+38478,
+54268,
+34241,
+14078,
+50577,
+16049,
+54607,
+51368,
+17204,
+51684,
+46192,
+58325,
+53314,
+34657,
+45178,
+37843,
+41579,
+44627,
+46390,
+14340,
+28700,
+718,
+44300,
+10034,
+40022,
+12364,
+15732,
+50267,
+11225,
+11134,
+56786,
+30163,
+53573,
+60487,
+33425,
+51384,
+9851,
+52400,
+42623,
+1577,
+26872,
+59285,
+48239,
+50693,
+30957,
+50751,
+3556,
+56473,
+2221,
+51882,
+11919,
+44167,
+21037,
+50283,
+38628,
+25017,
+42703,
+56577,
+22071,
+29150,
+39758,
+38561,
+26002,
+54708,
+7718,
+33869,
+36384,
+48558,
+44373,
+97,
+31432,
+36634,
+18629,
+29315,
+32499,
+17087,
+7614,
+49427,
+56014,
+30980,
+46821,
+24696,
+50529,
+8150,
+57570,
+22535,
+48418,
+3864,
+391,
+24243,
+65119,
+51079,
+53058,
+13704,
+22395,
+16157,
+3477,
+24893,
+15115,
+62993,
+50874,
+44409,
+7266,
+59180,
+17272,
+6398,
+64606,
+59689,
+53881,
+39177,
+41632,
+2718,
+24490,
+48650,
+51562,
+21645,
+18382,
+7149,
+53833,
+65510,
+7563,
+25968,
+56526,
+33075,
+46463,
+44777,
+18476,
+8840,
+13270,
+35814,
+32990,
+47276,
+42880,
+57019,
+61806,
+44401,
+36489,
+39052,
+62081,
+608,
+45737,
+46528,
+58032,
+48621,
+12349,
+10471,
+30698,
+38541,
+65118,
+24303,
+392,
+57867,
+24963,
+13865,
+50333,
+24163,
+25151,
+56202,
+58622,
+52296,
+42605,
+15560,
+33277,
+30422,
+13874,
+53602,
+64555,
+16903,
+31083,
+17781,
+50534,
+21254,
+7287,
+2384,
+57102,
+19171,
+54671,
+37019,
+36103,
+9108,
+19470,
+51748,
+1118,
+48612,
+9229,
+27856,
+49479,
+64787,
+176,
+41841,
+36018,
+32583,
+9947,
+59958,
+40054,
+5528,
+54467,
+55076,
+64830,
+62289,
+15260,
+57778,
+6700,
+56951,
+52881,
+1073,
+28498,
+37363,
+31568,
+4290,
+34503,
+29588,
+58200,
+50735,
+44603,
+11610,
+27111,
+35779,
+22884,
+22218,
+5794,
+27686,
+29916,
+42773,
+31936,
+28336,
+27708,
+49327,
+25150,
+24237,
+50334,
+62697,
+54342,
+10756,
+14294,
+52371,
+13427,
+31359,
+11773,
+38525,
+54360,
+28002,
+43211,
+31858,
+61827,
+1927,
+8737,
+16389,
+14248,
+48373,
+33306,
+10650,
+12578,
+52763,
+2392,
+45792,
+14231,
+63848,
+4000,
+12712,
+52743,
+39565,
+22904,
+27449,
+45896,
+7766,
+41836,
+18603,
+40981,
+41776,
+20211,
+53142,
+35847,
+53035,
+17747,
+22131,
+33418,
+46698,
+49733,
+61789,
+8309,
+21476,
+62990,
+22576,
+927,
+13631,
+8621,
+36114,
+15903,
+16752,
+46299,
+38464,
+50507,
+54863,
+22211,
+60171,
+8271,
+7488,
+3974,
+47369,
+43821,
+7190,
+14182,
+44980,
+27493,
+27554,
+22603,
+30076,
+11214,
+34321,
+3165,
+8219,
+54958,
+7033,
+47472,
+5512,
+3389,
+36404,
+37685,
+8837,
+11873,
+48014,
+35268,
+51280,
+55530,
+4278,
+4703,
+12944,
+25102,
+30335,
+38717,
+33301,
+28255,
+23086,
+14555,
+49622,
+58500,
+24433,
+6618,
+63330,
+58913,
+29034,
+28403,
+56108,
+17706,
+4039,
+18705,
+36245,
+60459,
+38843,
+63866,
+20290,
+51669,
+52579,
+18564,
+52946,
+65158,
+6416,
+59737,
+52903,
+14838,
+8547,
+37454,
+60224,
+44996,
+56754,
+62441,
+48005,
+6659,
+62517,
+23880,
+5915,
+854,
+42006,
+17679,
+44902,
+614,
+23232,
+59885,
+10775,
+36303,
+62016,
+38113,
+21297,
+49423,
+48142,
+34887,
+45192,
+63705,
+13316,
+59347,
+13227,
+25868,
+9932,
+35920,
+53179,
+38097,
+64917,
+43410,
+20418,
+8285,
+62920,
+55492,
+49219,
+9259,
+61242,
+60944,
+44287,
+64403,
+63687,
+32610,
+15397,
+9437,
+28867,
+51819,
+32748,
+53775,
+5735,
+28165,
+2023,
+10654,
+26591,
+10808,
+52872,
+30715,
+49956,
+62034,
+43736,
+61711,
+2177,
+59550,
+56418,
+50638,
+27693,
+41161,
+20911,
+2060,
+51483,
+60035,
+10211,
+32224,
+2895,
+36594,
+14011,
+26778,
+31968,
+47668,
+35370,
+2280,
+64360,
+13766,
+55622,
+8522,
+26914,
+57863,
+15324,
+17526,
+42942,
+27763,
+29859,
+50993,
+4557,
+50455,
+17501,
+53357,
+64225,
+35294,
+31907,
+19803,
+26280,
+64548,
+200,
+41736,
+28438,
+55396,
+2373,
+18856,
+57461,
+14829,
+35368,
+47670,
+37010,
+57706,
+59068,
+50167,
+49884,
+26359,
+41255,
+62820,
+24879,
+56985,
+13743,
+53946,
+18927,
+36180,
+28530,
+49523,
+17301,
+58754,
+39128,
+38197,
+23786,
+8677,
+33132,
+63740,
+44337,
+13480,
+28805,
+37661,
+43266,
+4377,
+5914,
+24022,
+62518,
+17091,
+40189,
+3626,
+60316,
+55833,
+27079,
+25831,
+4692,
+20655,
+31732,
+33260,
+44646,
+60509,
+52501,
+21907,
+45211,
+63771,
+51131,
+3991,
+62271,
+21017,
+39698,
+33013,
+34171,
+16251,
+2231,
+40668,
+39076,
+51586,
+30195,
+15656,
+7368,
+43553,
+33883,
+47707,
+40895,
+24720,
+27360,
+22083,
+42073,
+12171,
+4443,
+58710,
+47824,
+47892,
+56798,
+49150,
+9364,
+40051,
+51978,
+60622,
+25701,
+17970,
+31422,
+22626,
+35873,
+56263,
+11203,
+59905,
+47149,
+20535,
+6106,
+38484,
+34022,
+30066,
+19821,
+8799,
+10971,
+19551,
+51061,
+34533,
+49576,
+52755,
+40038,
+56267,
+19141,
+65336,
+41617,
+37100,
+53671,
+8325,
+12729,
+61023,
+58219,
+38784,
+18813,
+57871,
+20966,
+7584,
+37310,
+59666,
+8676,
+23891,
+38198,
+14133,
+2695,
+53894,
+6942,
+18115,
+58972,
+58539,
+6979,
+18526,
+15569,
+33182,
+62692,
+2098,
+42152,
+29482,
+43945,
+18444,
+26731,
+30590,
+36479,
+34266,
+8420,
+24555,
+41414,
+45327,
+37895,
+54776,
+30254,
+39464,
+44702,
+63465,
+49399,
+35387,
+33101,
+57874,
+17664,
+56775,
+4998,
+12036,
+9834,
+4419,
+48531,
+9239,
+4207,
+34654,
+62064,
+62801,
+35633,
+48156,
+49199,
+14218,
+47839,
+20869,
+40080,
+61375,
+52418,
+63065,
+27160,
+62435,
+63805,
+31274,
+59614,
+43637,
+24987,
+64523,
+55963,
+28679,
+44956,
+10150,
+20790,
+28109,
+31739,
+62798,
+17977,
+7886,
+14526,
+1783,
+20316,
+49461,
+10915,
+6395,
+18619,
+57770,
+41411,
+46935,
+63875,
+55595,
+65360,
+41334,
+29520,
+34500,
+22433,
+43222,
+38297,
+7782,
+13617,
+54628,
+53756,
+46105,
+22103,
+7950,
+6694,
+22496,
+56717,
+42159,
+52656,
+25478,
+22963,
+54091,
+33282,
+52800,
+36881,
+27954,
+38573,
+54221,
+12089,
+50864,
+25368,
+26222,
+38364,
+49366,
+6960,
+9237,
+48533,
+55158,
+60947,
+29749,
+29681,
+55123,
+49684,
+10498,
+43091,
+33863,
+59765,
+45233,
+8710,
+47271,
+12746,
+40407,
+42457,
+3207,
+48071,
+32580,
+31284,
+15949,
+15093,
+48522,
+57915,
+25490,
+17422,
+40727,
+10108,
+13758,
+2435,
+11435,
+32695,
+22148,
+42420,
+10749,
+47084,
+34691,
+61739,
+62155,
+19036,
+35274,
+49510,
+19438,
+18709,
+46003,
+50906,
+45635,
+21446,
+51784,
+8155,
+33790,
+16217,
+18036,
+36601,
+27910,
+20873,
+56455,
+28977,
+46208,
+30379,
+39636,
+43845,
+16244,
+26357,
+49886,
+63976,
+15419,
+57348,
+49650,
+53108,
+32374,
+4248,
+55682,
+26132,
+59449,
+7247,
+50067,
+64267,
+50716,
+56813,
+3149,
+25446,
+22245,
+51241,
+13168,
+41068,
+54275,
+64892,
+50782,
+62131,
+58455,
+49045,
+4452,
+53087,
+12762,
+648,
+5454,
+38302,
+20925,
+21332,
+42485,
+57364,
+13434,
+7723,
+281,
+1163,
+29335,
+19180,
+34196,
+23353,
+12587,
+3963,
+19262,
+43182,
+12077,
+26855,
+15680,
+64443,
+47250,
+21996,
+61063,
+64234,
+14548,
+22668,
+25977,
+44006,
+5745,
+57497,
+64682,
+41518,
+47859,
+57997,
+13284,
+50556,
+16394,
+27435,
+53549,
+9208,
+62670,
+38532,
+15696,
+40458,
+15140,
+1477,
+35430,
+31028,
+43460,
+9106,
+36105,
+46844,
+40178,
+18374,
+4363,
+31493,
+51577,
+34401,
+57554,
+53608,
+65049,
+12920,
+31996,
+64731,
+14411,
+23270,
+53200,
+43007,
+24969,
+57567,
+46960,
+25851,
+45848,
+42393,
+39825,
+25914,
+25184,
+30663,
+59590,
+9006,
+8878,
+10246,
+46984,
+16304,
+47965,
+64613,
+49136,
+22887,
+9579,
+25949,
+33017,
+13266,
+33098,
+2678,
+62211,
+17287,
+57960,
+43453,
+41698,
+53367,
+53819,
+40396,
+30989,
+18131,
+36149,
+55518,
+11626,
+57596,
+16897,
+27332,
+46783,
+37926,
+63086,
+32823,
+30703,
+12810,
+22510,
+45936,
+20488,
+38663,
+6254,
+35918,
+9934,
+22746,
+38853,
+47677,
+53806,
+3560,
+12571,
+31117,
+21304,
+9577,
+22889,
+16129,
+17078,
+50327,
+32651,
+51193,
+40471,
+40926,
+47022,
+40451,
+42683,
+4707,
+43168,
+24844,
+17445,
+63964,
+18303,
+12837,
+55272,
+49390,
+61962,
+28772,
+53400,
+50412,
+14,
+34954,
+54238,
+35055,
+26445,
+29538,
+60140,
+9268,
+46398,
+24770,
+33001,
+32980,
+26370,
+25427,
+10313,
+54666,
+59129,
+39761,
+52067,
+60137,
+63438,
+41766,
+36056,
+12377,
+58925,
+56388,
+3232,
+8819,
+41857,
+12882,
+31134,
+7985,
+57947,
+23214,
+687,
+22958,
+2336,
+23361,
+17952,
+3834,
+17056,
+20288,
+63868,
+45539,
+17951,
+23369,
+2337,
+32321,
+19963,
+477,
+10284,
+275,
+12586,
+23551,
+34197,
+64942,
+21957,
+48230,
+36920,
+30193,
+51588,
+1763,
+12670,
+12766,
+16366,
+60789,
+16154,
+50601,
+12516,
+60454,
+40017,
+55196,
+46441,
+3011,
+632,
+54874,
+28184,
+56244,
+6116,
+45053,
+21339,
+30199,
+56582,
+63452,
+7517,
+4072,
+14032,
+14516,
+48103,
+62226,
+39343,
+25855,
+35692,
+1861,
+53395,
+47811,
+45949,
+41039,
+15895,
+38793,
+54123,
+17739,
+15639,
+13716,
+32791,
+13945,
+4987,
+57405,
+29888,
+3176,
+723,
+15299,
+64994,
+32044,
+2175,
+61713,
+121,
+37067,
+64189,
+37270,
+53646,
+29308,
+29298,
+36401,
+52822,
+44914,
+60450,
+58206,
+2120,
+13782,
+37969,
+6893,
+63304,
+24871,
+14106,
+53199,
+23497,
+14412,
+59163,
+18244,
+12566,
+44897,
+48502,
+63672,
+35969,
+28138,
+17562,
+34917,
+36009,
+25122,
+20338,
+32784,
+61566,
+33793,
+11728,
+6762,
+60528,
+11504,
+5686,
+22807,
+56208,
+30458,
+2484,
+28563,
+10842,
+32017,
+45890,
+45139,
+50051,
+11352,
+15414,
+54263,
+46167,
+59884,
+24015,
+615,
+63091,
+50045,
+2734,
+29906,
+29168,
+1758,
+33347,
+14703,
+13555,
+6020,
+44857,
+35934,
+22302,
+2055,
+4113,
+686,
+23373,
+57948,
+6971,
+48,
+62863,
+12162,
+7232,
+57048,
+53300,
+6123,
+14780,
+4426,
+33147,
+56817,
+26760,
+46796,
+17931,
+12270,
+19543,
+48748,
+34413,
+36822,
+6604,
+1023,
+65,
+64545,
+54031,
+59853,
+32141,
+16686,
+58665,
+17103,
+53587,
+58715,
+56074,
+6865,
+62684,
+62206,
+6043,
+42908,
+55653,
+32176,
+21939,
+4401,
+22467,
+29698,
+61919,
+57783,
+26324,
+38895,
+56158,
+16235,
+31167,
+56120,
+11186,
+24934,
+4661,
+33135,
+21490,
+14599,
+29955,
+50147,
+61373,
+40082,
+11389,
+33506,
+47035,
+7571,
+252,
+19688,
+42048,
+25713,
+49494,
+52804,
+13688,
+39508,
+57609,
+44712,
+728,
+64639,
+1170,
+1680,
+14894,
+13104,
+60077,
+56498,
+27070,
+64497,
+64718,
+45268,
+51165,
+63536,
+17270,
+59182,
+65177,
+16796,
+43388,
+41910,
+54789,
+40255,
+44076,
+34837,
+26998,
+33062,
+56414,
+19004,
+59255,
+12522,
+65381,
+30444,
+47204,
+49118,
+6312,
+12199,
+54112,
+10092,
+28643,
+49982,
+48764,
+58735,
+65432,
+34660,
+57071,
+34856,
+55214,
+63411,
+43240,
+14554,
+24059,
+28256,
+54229,
+42991,
+59759,
+25129,
+41892,
+62331,
+8119,
+29646,
+31641,
+11142,
+30661,
+25186,
+25545,
+31574,
+45660,
+43733,
+1537,
+29531,
+15431,
+37785,
+14506,
+12185,
+57401,
+33128,
+61993,
+26989,
+20308,
+43517,
+7689,
+5907,
+55033,
+44204,
+41977,
+8174,
+32052,
+28121,
+46659,
+14093,
+32723,
+25774,
+13472,
+64570,
+33855,
+46585,
+16023,
+32201,
+61836,
+44761,
+56948,
+63241,
+47319,
+27580,
+30347,
+7501,
+29974,
+20658,
+60770,
+61894,
+55191,
+12874,
+64128,
+29744,
+35712,
+2249,
+35624,
+16738,
+22678,
+10985,
+52396,
+17692,
+33264,
+65406,
+53943,
+1256,
+35611,
+45613,
+44393,
+13525,
+63033,
+9139,
+36663,
+8211,
+36273,
+37420,
+35541,
+21953,
+13915,
+19145,
+51199,
+36703,
+63140,
+17917,
+54503,
+57507,
+48413,
+13929,
+50544,
+55182,
+15958,
+20267,
+14374,
+4437,
+31913,
+60361,
+38652,
+58193,
+58746,
+36167,
+27386,
+16539,
+30087,
+60208,
+52930,
+26770,
+40181,
+24484,
+46890,
+11280,
+13084,
+34525,
+54090,
+23677,
+25479,
+4407,
+30465,
+2335,
+23371,
+688,
+61543,
+15183,
+13679,
+47683,
+12361,
+15317,
+21483,
+13048,
+48256,
+44174,
+814,
+46480,
+7041,
+20764,
+34791,
+40826,
+22378,
+49932,
+65517,
+10349,
+13444,
+45543,
+2489,
+13240,
+41305,
+49978,
+40394,
+53821,
+19760,
+60749,
+47439,
+65031,
+46980,
+4603,
+47047,
+59544,
+8215,
+21961,
+62909,
+26121,
+60312,
+13136,
+41183,
+63961,
+14648,
+46569,
+14655,
+29769,
+57003,
+16199,
+14051,
+27448,
+24130,
+39566,
+31538,
+24654,
+21673,
+40214,
+3363,
+3761,
+48274,
+39368,
+54055,
+14580,
+53699,
+25475,
+16128,
+23430,
+9578,
+23475,
+49137,
+22217,
+24174,
+35780,
+10835,
+49311,
+49591,
+51140,
+26346,
+39462,
+30256,
+55074,
+54469,
+20988,
+16347,
+44295,
+11815,
+27517,
+1218,
+17689,
+57658,
+8894,
+62834,
+35195,
+5954,
+43010,
+53304,
+53874,
+17911,
+50438,
+34228,
+25085,
+42117,
+60831,
+59437,
+52912,
+53987,
+8252,
+27723,
+28427,
+6486,
+38321,
+35646,
+27402,
+29951,
+22409,
+1472,
+5696,
+6098,
+12627,
+49434,
+40519,
+41201,
+8961,
+15526,
+20375,
+46140,
+33746,
+53884,
+34260,
+56668,
+5019,
+13487,
+16876,
+52537,
+36397,
+13363,
+56088,
+42890,
+36277,
+33185,
+49919,
+52768,
+33175,
+27422,
+44242,
+26097,
+56897,
+56207,
+23247,
+5687,
+31721,
+1003,
+1894,
+34516,
+15481,
+54824,
+10518,
+35028,
+44939,
+38228,
+5960,
+59755,
+57489,
+33588,
+55993,
+49413,
+440,
+50677,
+18821,
+45395,
+31001,
+22094,
+44072,
+55178,
+13383,
+49521,
+28532,
+11575,
+57210,
+52810,
+8627,
+9154,
+62875,
+4976,
+26890,
+35900,
+62201,
+53197,
+14108,
+8023,
+35620,
+41368,
+33602,
+18810,
+10000,
+37989,
+35854,
+10416,
+4147,
+28077,
+34282,
+26311,
+26027,
+7930,
+56440,
+33771,
+6989,
+38470,
+38852,
+23439,
+9935,
+5812,
+46913,
+27760,
+28035,
+52029,
+50351,
+33760,
+61444,
+43356,
+62663,
+16083,
+62755,
+57502,
+57715,
+36511,
+16545,
+51053,
+9196,
+6849,
+52842,
+37034,
+33142,
+65188,
+51037,
+5172,
+19486,
+59101,
+49416,
+43775,
+39478,
+60606,
+51407,
+34168,
+20474,
+59370,
+60957,
+65309,
+24830,
+40475,
+42988,
+44387,
+24742,
+25026,
+62702,
+10893,
+39399,
+62000,
+37644,
+63045,
+1106,
+20189,
+55972,
+52347,
+36464,
+33687,
+51782,
+21448,
+2259,
+21837,
+5700,
+4013,
+1748,
+12329,
+36639,
+38123,
+10984,
+23018,
+16739,
+62222,
+5558,
+20149,
+4304,
+31407,
+16596,
+39354,
+25976,
+23537,
+14549,
+31184,
+15811,
+10251,
+17293,
+7493,
+61384,
+55799,
+25221,
+18260,
+41277,
+8519,
+13793,
+36998,
+21999,
+4389,
+57372,
+31750,
+7859,
+9448,
+42719,
+29261,
+20962,
+49228,
+6877,
+63699,
+54193,
+21864,
+64408,
+32479,
+56565,
+39969,
+19567,
+46250,
+9628,
+53702,
+29347,
+59493,
+42362,
+9885,
+35872,
+23824,
+31423,
+3340,
+27738,
+3630,
+2564,
+57694,
+16356,
+18640,
+17418,
+53995,
+11557,
+44042,
+20128,
+10576,
+61969,
+32154,
+60357,
+26765,
+44160,
+62003,
+43816,
+30075,
+24086,
+27555,
+35545,
+15291,
+65508,
+53835,
+12648,
+48010,
+41602,
+37775,
+37504,
+30822,
+36315,
+61252,
+48756,
+65331,
+61382,
+7495,
+20713,
+20386,
+1274,
+59010,
+19875,
+21806,
+566,
+17570,
+926,
+24109,
+62991,
+15117,
+57878,
+40814,
+54062,
+34563,
+26245,
+15545,
+21775,
+14739,
+40786,
+32510,
+4964,
+39310,
+3512,
+44252,
+48969,
+56214,
+53959,
+5628,
+58238,
+1357,
+15782,
+43547,
+7774,
+25061,
+42832,
+21151,
+11307,
+42454,
+5779,
+64098,
+10620,
+19796,
+26862,
+13529,
+63615,
+20573,
+37382,
+48417,
+24307,
+57571,
+13635,
+62736,
+12336,
+42042,
+40569,
+59582,
+42676,
+12234,
+39200,
+53750,
+29511,
+323,
+47131,
+6382,
+46687,
+6336,
+35567,
+63712,
+35608,
+34125,
+43113,
+5639,
+45935,
+23446,
+12811,
+13490,
+63522,
+9991,
+29654,
+51235,
+7677,
+10884,
+17901,
+10116,
+40760,
+48056,
+56716,
+23682,
+6695,
+57248,
+38708,
+32684,
+12559,
+48178,
+9474,
+6904,
+49289,
+3821,
+51220,
+15195,
+56865,
+20461,
+64196,
+10125,
+7554,
+47434,
+2872,
+49456,
+44536,
+45337,
+12989,
+40529,
+6393,
+10917,
+4521,
+29697,
+23170,
+4402,
+388,
+51723,
+1097,
+33247,
+24603,
+31435,
+54816,
+34035,
+46562,
+25592,
+25634,
+63526,
+28596,
+38335,
+48034,
+12182,
+31465,
+25134,
+55038,
+41884,
+31675,
+55460,
+28197,
+62240,
+17797,
+5587,
+9536,
+17002,
+10381,
+45887,
+36290,
+43221,
+23693,
+34501,
+4292,
+17160,
+6329,
+12042,
+45207,
+47807,
+31411,
+43024,
+59899,
+46471,
+7708,
+62418,
+14441,
+58781,
+27186,
+31064,
+36030,
+15529,
+55718,
+20367,
+54524,
+1471,
+22841,
+29952,
+28760,
+44679,
+6210,
+28132,
+14542,
+60982,
+17112,
+55803,
+26807,
+32195,
+50599,
+16156,
+24298,
+13705,
+25664,
+34188,
+30405,
+53457,
+36950,
+27391,
+51453,
+61494,
+53264,
+39728,
+63057,
+55069,
+48357,
+58137,
+49931,
+22940,
+40827,
+3213,
+8708,
+45235,
+9321,
+11580,
+62335,
+60829,
+42119,
+40694,
+18228,
+60289,
+39583,
+48215,
+33233,
+16966,
+2925,
+33979,
+10171,
+3667,
+29607,
+46274,
+41990,
+53461,
+44653,
+18768,
+47510,
+49770,
+52473,
+5610,
+25230,
+29472,
+32506,
+37218,
+60222,
+37456,
+37267,
+12418,
+57522,
+5014,
+43884,
+1693,
+11027,
+18073,
+36265,
+18327,
+34704,
+10780,
+39277,
+62276,
+37534,
+41387,
+33680,
+20506,
+47734,
+1330,
+19992,
+63050,
+51645,
+185,
+29246,
+43103,
+41845,
+27241,
+17623,
+6371,
+15727,
+8939,
+58889,
+52543,
+22114,
+18413,
+26776,
+14013,
+2054,
+23218,
+35935,
+27840,
+47177,
+2125,
+42449,
+50984,
+61210,
+60372,
+4151,
+9740,
+38606,
+50345,
+1429,
+7013,
+32620,
+15330,
+50371,
+44306,
+59293,
+15821,
+38991,
+60457,
+36247,
+33198,
+51913,
+15737,
+22134,
+32845,
+4652,
+27875,
+41654,
+61161,
+60325,
+44950,
+34998,
+5613,
+36832,
+30758,
+58575,
+56302,
+60435,
+17821,
+62313,
+40954,
+37867,
+38119,
+36024,
+65216,
+26011,
+53064,
+3611,
+16857,
+6987,
+33773,
+20630,
+51240,
+23578,
+25447,
+14240,
+38308,
+4697,
+32630,
+18828,
+59245,
+949,
+5037,
+9146,
+37072,
+846,
+22053,
+43937,
+49508,
+35276,
+31940,
+26295,
+15336,
+51082,
+63747,
+1100,
+63157,
+55235,
+51064,
+5793,
+24173,
+22885,
+49138,
+57197,
+58064,
+37883,
+60170,
+24098,
+54864,
+33009,
+61196,
+50241,
+18006,
+14530,
+36213,
+27572,
+16185,
+7439,
+58077,
+8594,
+62,
+27048,
+25691,
+48715,
+49482,
+20441,
+2460,
+54087,
+58812,
+12662,
+2744,
+192,
+31581,
+17704,
+56110,
+63918,
+4092,
+42801,
+2875,
+27460,
+40558,
+17604,
+34521,
+64940,
+34199,
+58396,
+29254,
+30284,
+37315,
+26958,
+61945,
+52416,
+61377,
+57533,
+1647,
+29946,
+1174,
+50088,
+65398,
+40368,
+38329,
+8350,
+58216,
+40890,
+52713,
+44568,
+60759,
+52587,
+48740,
+42419,
+23628,
+32696,
+56372,
+61669,
+63267,
+56353,
+30004,
+50965,
+53439,
+1415,
+5231,
+5024,
+54100,
+32844,
+22275,
+15738,
+33417,
+24117,
+17748,
+36161,
+619,
+41543,
+45120,
+29171,
+9340,
+60732,
+61268,
+45622,
+17519,
+3117,
+51098,
+3301,
+46385,
+18412,
+22307,
+52544,
+55202,
+64796,
+27190,
+29782,
+26398,
+36363,
+28363,
+12046,
+7949,
+23685,
+46106,
+5219,
+9706,
+18885,
+34803,
+15037,
+48171,
+44071,
+22784,
+31002,
+15963,
+5966,
+1967,
+27741,
+3065,
+45894,
+27451,
+7576,
+42072,
+23840,
+27361,
+9747,
+45214,
+48591,
+34508,
+42023,
+56432,
+3334,
+12342,
+6054,
+29149,
+24334,
+56578,
+4793,
+54041,
+34249,
+36784,
+1494,
+12407,
+26838,
+16359,
+33783,
+33431,
+26522,
+4424,
+14782,
+9036,
+6992,
+43936,
+22232,
+847,
+58196,
+3635,
+35347,
+55172,
+32877,
+39802,
+27508,
+61021,
+12731,
+51851,
+12414,
+45415,
+65424,
+46228,
+51265,
+45566,
+30807,
+65421,
+35334,
+14186,
+48917,
+5096,
+2264,
+55353,
+19637,
+21563,
+19856,
+64802,
+36341,
+31096,
+45228,
+42480,
+57849,
+31691,
+49344,
+39630,
+52507,
+55674,
+11358,
+52666,
+57477,
+30708,
+31966,
+26780,
+50236,
+545,
+30849,
+30216,
+47070,
+25572,
+27143,
+4388,
+22653,
+36999,
+61062,
+23541,
+47251,
+38144,
+14086,
+17168,
+49754,
+42985,
+57764,
+13046,
+21485,
+49503,
+58168,
+41691,
+31655,
+56083,
+9201,
+55617,
+3113,
+34832,
+52729,
+7375,
+1858,
+40684,
+36886,
+58966,
+35383,
+44277,
+33477,
+44108,
+7742,
+19062,
+59862,
+9969,
+36306,
+62908,
+22919,
+8216,
+57634,
+48229,
+23350,
+64943,
+40561,
+13914,
+22999,
+35542,
+8918,
+37689,
+52641,
+13022,
+14444,
+16983,
+56960,
+9775,
+36584,
+44674,
+39643,
+4400,
+23172,
+32177,
+30855,
+4353,
+44751,
+34376,
+63786,
+19737,
+768,
+59061,
+44303,
+63149,
+55599,
+13053,
+54247,
+30398,
+32486,
+13737,
+65247,
+28801,
+5660,
+302,
+30931,
+35098,
+46317,
+12468,
+8111,
+62573,
+14378,
+29203,
+10003,
+45210,
+23864,
+52502,
+29823,
+63290,
+13336,
+31799,
+5922,
+41815,
+40551,
+27968,
+36173,
+434,
+50924,
+65085,
+57691,
+64594,
+13011,
+14290,
+34785,
+13417,
+33297,
+4333,
+57206,
+25075,
+57930,
+2405,
+20403,
+61083,
+12452,
+7432,
+10458,
+30968,
+14150,
+33925,
+31170,
+35672,
+57776,
+15262,
+27151,
+47930,
+3072,
+25785,
+64407,
+22640,
+54194,
+57512,
+29919,
+53344,
+31130,
+7085,
+52044,
+2114,
+35109,
+49808,
+56600,
+34493,
+12288,
+20165,
+58983,
+9362,
+49152,
+65534,
+32770,
+13108,
+40961,
+17874,
+60856,
+26986,
+3277,
+5699,
+22686,
+2260,
+59393,
+11235,
+15522,
+3197,
+37237,
+44621,
+40633,
+24731,
+15214,
+16662,
+64480,
+61504,
+39515,
+15692,
+29226,
+58728,
+49972,
+53693,
+38865,
+5891,
+17809,
+45531,
+36781,
+49315,
+38440,
+3675,
+54217,
+580,
+565,
+22580,
+19876,
+42468,
+64001,
+3502,
+41572,
+3827,
+19193,
+51788,
+57906,
+17154,
+36649,
+25792,
+47701,
+19539,
+49952,
+18837,
+15806,
+19699,
+58462,
+11350,
+50053,
+64120,
+60308,
+57302,
+61821,
+25616,
+59311,
+47135,
+2227,
+14738,
+22567,
+15546,
+31566,
+37365,
+36572,
+8950,
+20231,
+11251,
+36934,
+3839,
+30602,
+1605,
+16120,
+42821,
+26060,
+32386,
+15376,
+40618,
+58392,
+25825,
+26263,
+61009,
+33240,
+60406,
+3923,
+28544,
+226,
+40709,
+40075,
+10521,
+65320,
+60380,
+14682,
+21073,
+51553,
+35973,
+12493,
+63508,
+11861,
+62549,
+62576,
+55951,
+16869,
+53429,
+58869,
+47028,
+33892,
+46043,
+17857,
+50385,
+39286,
+10055,
+53605,
+3533,
+42932,
+35463,
+27767,
+55099,
+18167,
+41445,
+58348,
+5973,
+1482,
+25986,
+28713,
+58935,
+59303,
+39830,
+9610,
+7195,
+47988,
+58752,
+17303,
+34061,
+65386,
+55489,
+62707,
+29144,
+18368,
+7736,
+145,
+46236,
+25900,
+41938,
+49294,
+48837,
+50338,
+30898,
+5645,
+16008,
+26242,
+11621,
+4969,
+14282,
+1194,
+30329,
+10617,
+45472,
+3238,
+30129,
+65153,
+40213,
+22900,
+24655,
+33644,
+11441,
+48349,
+50659,
+10393,
+36477,
+30592,
+65056,
+17341,
+55308,
+16680,
+19767,
+53951,
+20604,
+63337,
+28910,
+12947,
+60522,
+43200,
+63845,
+47245,
+63639,
+27881,
+56143,
+37057,
+18381,
+24277,
+51563,
+58315,
+13515,
+59026,
+55340,
+6448,
+10204,
+13924,
+57410,
+61078,
+10424,
+47766,
+56233,
+21269,
+27248,
+58580,
+53092,
+12488,
+30332,
+297,
+62679,
+53862,
+57235,
+8654,
+46176,
+36720,
+59204,
+61429,
+11985,
+21309,
+13296,
+34083,
+30291,
+47384,
+7045,
+27784,
+25553,
+35606,
+63714,
+56006,
+52520,
+61666,
+58544,
+43483,
+43306,
+16030,
+56540,
+10630,
+525,
+15077,
+26128,
+36054,
+41768,
+47094,
+6323,
+45195,
+59687,
+64608,
+51908,
+29081,
+46836,
+6404,
+46895,
+48121,
+49420,
+31212,
+34360,
+4960,
+24546,
+28168,
+57174,
+4727,
+14209,
+16941,
+29876,
+11923,
+31313,
+36453,
+53117,
+8017,
+19855,
+22026,
+19638,
+59014,
+11165,
+36655,
+50121,
+11276,
+2715,
+40660,
+52879,
+56953,
+18151,
+58494,
+44775,
+46465,
+29943,
+9143,
+2187,
+20998,
+2607,
+35006,
+8345,
+14269,
+37950,
+21442,
+16331,
+63343,
+64761,
+19197,
+56495,
+52751,
+39559,
+42002,
+59793,
+50429,
+2682,
+17344,
+28634,
+32496,
+46589,
+40419,
+53658,
+52147,
+17642,
+49554,
+29735,
+50183,
+6229,
+4030,
+39915,
+39204,
+44848,
+59858,
+59940,
+37700,
+36812,
+6515,
+52674,
+45722,
+49936,
+40865,
+38267,
+16923,
+56310,
+3844,
+11813,
+44297,
+53979,
+42923,
+45410,
+49989,
+33973,
+14598,
+23156,
+33136,
+58513,
+55609,
+49502,
+21987,
+13047,
+22950,
+15318,
+5092,
+63685,
+64405,
+25787,
+62989,
+24111,
+8310,
+33562,
+58444,
+56066,
+47870,
+40241,
+58,
+59984,
+17365,
+43442,
+42754,
+10360,
+7136,
+25281,
+42423,
+30701,
+32825,
+60409,
+10061,
+2691,
+59330,
+46350,
+6843,
+46281,
+26403,
+17944,
+2258,
+22688,
+51783,
+23613,
+45636,
+49819,
+16330,
+21539,
+37951,
+48520,
+15095,
+8306,
+30951,
+33915,
+36439,
+13585,
+32345,
+56929,
+54421,
+32059,
+43569,
+57796,
+62041,
+54137,
+61019,
+27510,
+58146,
+19130,
+63325,
+55786,
+52276,
+5670,
+16053,
+45098,
+15877,
+9160,
+24589,
+28260,
+52118,
+32242,
+26565,
+29000,
+64790,
+25390,
+47145,
+32447,
+15644,
+5564,
+5748,
+8186,
+30372,
+9467,
+37248,
+45667,
+53370,
+51559,
+18194,
+13483,
+62510,
+57243,
+59366,
+45537,
+63870,
+59834,
+37321,
+6672,
+11757,
+5324,
+48283,
+16654,
+8473,
+38771,
+42930,
+3535,
+27895,
+64657,
+19088,
+16096,
+53617,
+41196,
+44501,
+34575,
+61749,
+2095,
+19043,
+17280,
+42590,
+64724,
+31236,
+58520,
+18898,
+58280,
+31785,
+55499,
+62554,
+63582,
+24787,
+47615,
+50036,
+40283,
+54145,
+20719,
+10733,
+54432,
+8683,
+49033,
+25250,
+9412,
+15367,
+30198,
+23326,
+45054,
+60684,
+47749,
+30159,
+10503,
+42484,
+23561,
+20926,
+52150,
+28794,
+32452,
+59514,
+21103,
+18587,
+18677,
+14587,
+36377,
+52338,
+58015,
+50646,
+16229,
+51942,
+10641,
+33139,
+5999,
+37936,
+3152,
+39265,
+13295,
+21615,
+11986,
+56331,
+1012,
+9576,
+23432,
+31118,
+39057,
+39988,
+44839,
+31210,
+49422,
+24009,
+38114,
+42726,
+39441,
+34903,
+35708,
+35171,
+64161,
+60867,
+56723,
+18183,
+62024,
+42977,
+16569,
+11997,
+10658,
+32459,
+58740,
+14688,
+10352,
+56663,
+25003,
+20710,
+37679,
+16060,
+29703,
+57668,
+27247,
+21631,
+56234,
+49115,
+47259,
+38487,
+39848,
+63025,
+262,
+30153,
+43567,
+32061,
+61039,
+6049,
+39789,
+7286,
+24221,
+50535,
+10377,
+4592,
+41552,
+1135,
+37904,
+1934,
+13989,
+37351,
+9295,
+49189,
+42666,
+53723,
+44040,
+11559,
+64818,
+41167,
+55504,
+6475,
+43608,
+2389,
+15165,
+43253,
+6245,
+6412,
+15279,
+45092,
+9025,
+36429,
+4252,
+61362,
+61754,
+13604,
+31110,
+45353,
+41139,
+32838,
+4649,
+40493,
+27529,
+64950,
+61400,
+50564,
+47254,
+31945,
+12710,
+4002,
+40538,
+2080,
+18078,
+39329,
+62177,
+1796,
+41650,
+52058,
+25466,
+14111,
+26161,
+50395,
+64062,
+50910,
+30780,
+36339,
+64804,
+15669,
+18834,
+33067,
+53284,
+15508,
+6141,
+56735,
+1185,
+45258,
+6662,
+51807,
+13644,
+31331,
+32165,
+11368,
+1984,
+63491,
+57430,
+33578,
+40997,
+37482,
+17440,
+56685,
+53291,
+16978,
+41213,
+65441,
+15178,
+64165,
+61427,
+59206,
+53497,
+36131,
+62021,
+5725,
+30984,
+64307,
+11306,
+22548,
+42833,
+40796,
+1503,
+8411,
+4621,
+19923,
+42529,
+52013,
+63488,
+65506,
+15293,
+61240,
+9261,
+3730,
+41280,
+29049,
+36713,
+24676,
+15795,
+51751,
+37973,
+14662,
+41246,
+58272,
+6921,
+56534,
+56940,
+7648,
+1449,
+34939,
+59185,
+16264,
+37647,
+38105,
+30059,
+53488,
+48996,
+35452,
+61815,
+13827,
+6622,
+36505,
+57393,
+4170,
+5139,
+40968,
+18586,
+21326,
+59515,
+20682,
+10166,
+29872,
+9496,
+40005,
+62011,
+5151,
+34614,
+1190,
+18435,
+64987,
+64583,
+895,
+18799,
+39996,
+24961,
+57869,
+18815,
+19621,
+19109,
+49567,
+39773,
+47899,
+18298,
+40783,
+47542,
+10800,
+51552,
+21742,
+14683,
+65114,
+15997,
+47001,
+6461,
+60964,
+61166,
+30250,
+7993,
+32294,
+39863,
+3146,
+15849,
+56123,
+50172,
+49222,
+28688,
+30420,
+33279,
+46383,
+3303,
+58417,
+35720,
+20045,
+59238,
+53748,
+39202,
+39917,
+65428,
+55222,
+10873,
+15882,
+20038,
+29275,
+50282,
+24340,
+44168,
+30963,
+60181,
+39005,
+13639,
+19763,
+1848,
+32929,
+45045,
+47525,
+4211,
+13709,
+46288,
+13835,
+15940,
+45060,
+18997,
+1612,
+39697,
+23858,
+62272,
+2551,
+28789,
+37386,
+47325,
+3481,
+62583,
+50158,
+54010,
+47121,
+10667,
+29962,
+15080,
+48038,
+16080,
+43062,
+20201,
+2606,
+21545,
+2188,
+11827,
+44710,
+57611,
+18831,
+27177,
+63211,
+50091,
+16346,
+22873,
+54470,
+64241,
+24900,
+32984,
+6812,
+1290,
+48938,
+28592,
+14645,
+17448,
+2540,
+51043,
+13273,
+57041,
+24858,
+16378,
+3122,
+12823,
+43384,
+4779,
+7583,
+23791,
+57872,
+33103,
+49227,
+22645,
+29262,
+28119,
+32054,
+27465,
+7596,
+36906,
+46234,
+147,
+60667,
+45321,
+30693,
+15244,
+35351,
+2969,
+34932,
+46160,
+40045,
+48117,
+11544,
+44892,
+31706,
+6175,
+9180,
+34008,
+51617,
+54346,
+14801,
+41359,
+53928,
+57312,
+64510,
+19611,
+25762,
+17640,
+52149,
+21331,
+23562,
+38303,
+54480,
+20671,
+6497,
+354,
+3324,
+48252,
+28299,
+43707,
+24905,
+36485,
+29583,
+2059,
+23957,
+41162,
+19148,
+15563,
+11846,
+3464,
+11217,
+26238,
+50914,
+35396,
+32676,
+59808,
+6946,
+57123,
+34012,
+45869,
+55541,
+30209,
+64872,
+32276,
+41670,
+5029,
+55284,
+48246,
+48697,
+58116,
+12075,
+43184,
+46770,
+5690,
+34399,
+51579,
+13130,
+51653,
+60147,
+54678,
+48185,
+56454,
+23605,
+27911,
+14636,
+40079,
+23732,
+47840,
+27255,
+50998,
+31922,
+28952,
+43595,
+25895,
+63030,
+53325,
+36776,
+43131,
+59328,
+2693,
+14135,
+27118,
+46809,
+17478,
+35426,
+56651,
+46928,
+10817,
+49284,
+63308,
+50801,
+5147,
+52922,
+61075,
+62732,
+51678,
+6532,
+28812,
+40652,
+26072,
+41782,
+2073,
+56153,
+11379,
+10442,
+50696,
+6038,
+53709,
+44542,
+7927,
+42400,
+64810,
+17965,
+52818,
+30935,
+8477,
+27683,
+38051,
+15779,
+48564,
+31306,
+12938,
+57054,
+60338,
+16152,
+60791,
+8661,
+65137,
+12977,
+1723,
+2624,
+30535,
+56423,
+5819,
+45190,
+34889,
+11709,
+19830,
+2354,
+60334,
+1601,
+37515,
+59747,
+45115,
+28108,
+23715,
+10151,
+18238,
+61183,
+43602,
+31162,
+25330,
+12355,
+61491,
+54067,
+41,
+7803,
+34743,
+3700,
+42500,
+8590,
+32636,
+5234,
+24701,
+1240,
+24716,
+45673,
+39083,
+38905,
+40690,
+34790,
+22943,
+7042,
+43987,
+19073,
+41270,
+47062,
+53707,
+6040,
+35064,
+17566,
+10626,
+48443,
+25268,
+52705,
+25182,
+25916,
+24957,
+53388,
+58584,
+57459,
+18858,
+7469,
+4414,
+16671,
+31830,
+19365,
+64472,
+4867,
+39649,
+56981,
+28514,
+35046,
+47863,
+58266,
+54513,
+51471,
+49099,
+62432,
+5385,
+10429,
+41114,
+51157,
+36336,
+50561,
+10732,
+21348,
+54146,
+32033,
+58846,
+38275,
+20385,
+22585,
+7496,
+37678,
+21275,
+25004,
+24790,
+47522,
+28331,
+50005,
+10639,
+51944,
+40113,
+3378,
+39490,
+25548,
+44237,
+34328,
+32759,
+61683,
+1821,
+9533,
+54024,
+2819,
+26332,
+44025,
+5680,
+17063,
+32420,
+8865,
+45528,
+10165,
+21101,
+59516,
+42317,
+29604,
+6361,
+56957,
+56348,
+63391,
+17172,
+15518,
+6496,
+20922,
+54481,
+11158,
+371,
+47392,
+1500,
+9825,
+33850,
+27578,
+47321,
+57036,
+27195,
+60769,
+23029,
+29975,
+31731,
+23870,
+4693,
+34417,
+26482,
+18670,
+17211,
+37807,
+15340,
+16931,
+53992,
+44595,
+33832,
+38018,
+64321,
+52970,
+42272,
+17036,
+36522,
+59446,
+47857,
+41520,
+64924,
+13601,
+54,
+51239,
+22247,
+33774,
+60101,
+52720,
+11696,
+52741,
+12714,
+42256,
+5088,
+15751,
+33223,
+38129,
+28916,
+17051,
+57809,
+20467,
+49237,
+16145,
+3577,
+32220,
+31363,
+6907,
+57806,
+65343,
+33958,
+63336,
+21658,
+53952,
+7237,
+17920,
+15503,
+30508,
+42740,
+27310,
+18128,
+29572,
+29688,
+12504,
+62383,
+26274,
+28306,
+10100,
+5211,
+43269,
+30540,
+40809,
+45469,
+64101,
+36857,
+41820,
+55388,
+61760,
+14424,
+27706,
+28338,
+33439,
+37381,
+22538,
+63616,
+4336,
+137,
+25171,
+11678,
+39927,
+239,
+30858,
+30101,
+8124,
+2808,
+62085,
+26626,
+60800,
+51354,
+41448,
+38045,
+26489,
+24625,
+49195,
+17427,
+46183,
+61305,
+36913,
+33888,
+29421,
+49636,
+53696,
+46757,
+37179,
+5312,
+41509,
+8784,
+45157,
+284,
+60479,
+6105,
+23818,
+47150,
+60095,
+59118,
+28930,
+53831,
+7151,
+8074,
+50710,
+37296,
+27773,
+60099,
+33776,
+4225,
+58792,
+31788,
+26363,
+4130,
+53814,
+24562,
+9801,
+2067,
+29949,
+27404,
+11212,
+30078,
+15706,
+63022,
+47733,
+22324,
+33681,
+8646,
+14985,
+11332,
+62369,
+7906,
+9425,
+1621,
+62346,
+55615,
+9203,
+56260,
+32607,
+44509,
+18013,
+37208,
+38662,
+23444,
+45937,
+12243,
+59420,
+42210,
+44199,
+4854,
+35746,
+26218,
+12484,
+49596,
+44516,
+46541,
+59369,
+22711,
+34169,
+33015,
+25951,
+32570,
+3983,
+49236,
+20615,
+57810,
+8560,
+40176,
+46846,
+64195,
+22482,
+56866,
+961,
+54706,
+26004,
+24429,
+52106,
+63669,
+55639,
+28356,
+60227,
+40133,
+33488,
+49438,
+29879,
+50257,
+44310,
+56513,
+27115,
+2459,
+22193,
+49483,
+44121,
+49661,
+20238,
+28840,
+61650,
+26091,
+10995,
+34337,
+57646,
+33949,
+32090,
+12528,
+36418,
+14480,
+62723,
+48880,
+5789,
+44745,
+37430,
+30018,
+8284,
+23993,
+43411,
+36367,
+63781,
+6424,
+45882,
+35077,
+63055,
+39730,
+49282,
+10819,
+45144,
+35993,
+25399,
+61082,
+21881,
+2406,
+24996,
+65129,
+19646,
+42298,
+15487,
+3255,
+38506,
+54717,
+45130,
+29545,
+36598,
+26623,
+25210,
+16950,
+1273,
+22584,
+20714,
+38276,
+65074,
+46955,
+56752,
+44998,
+65254,
+64376,
+54634,
+46139,
+22831,
+15527,
+36032,
+15921,
+64900,
+31636,
+55344,
+54523,
+22412,
+55719,
+29021,
+20214,
+34846,
+49473,
+103,
+30879,
+41159,
+27695,
+34911,
+36706,
+14611,
+454,
+50126,
+37924,
+46785,
+45483,
+13881,
+59717,
+44736,
+30649,
+41532,
+1833,
+59213,
+43948,
+5851,
+16990,
+32783,
+23256,
+25123,
+13255,
+14996,
+47704,
+18991,
+10550,
+53494,
+43110,
+17731,
+55533,
+43629,
+41674,
+1767,
+55789,
+43457,
+16947,
+19386,
+59967,
+2590,
+65108,
+49460,
+23707,
+1784,
+14063,
+60249,
+59526,
+55473,
+34440,
+43516,
+23058,
+26990,
+36493,
+5771,
+41887,
+56828,
+43163,
+56217,
+11858,
+57359,
+55247,
+36690,
+1889,
+64255,
+27679,
+63722,
+61600,
+51668,
+24041,
+63867,
+23365,
+17057,
+29596,
+53625,
+18549,
+47601,
+53578,
+27826,
+64351,
+44356,
+58876,
+3022,
+55934,
+18095,
+39350,
+38686,
+14590,
+60723,
+818,
+42412,
+14373,
+22985,
+15959,
+12784,
+25980,
+4486,
+50377,
+16529,
+16863,
+33333,
+13966,
+54605,
+16051,
+5672,
+12778,
+919,
+36545,
+29330,
+11240,
+40923,
+64867,
+55056,
+9300,
+18138,
+832,
+11409,
+26727,
+60813,
+7619,
+28839,
+20437,
+49662,
+16313,
+36851,
+53148,
+30574,
+11250,
+21769,
+8951,
+47038,
+64011,
+25872,
+19827,
+28509,
+58074,
+12130,
+444,
+56407,
+15924,
+20158,
+39647,
+4869,
+44585,
+34845,
+20364,
+29022,
+53141,
+24122,
+41777,
+27643,
+59054,
+24863,
+30790,
+49945,
+19375,
+25494,
+2605,
+21000,
+43063,
+52549,
+46922,
+45860,
+57181,
+57239,
+32418,
+17065,
+57439,
+63385,
+55971,
+22694,
+1107,
+62758,
+14321,
+34426,
+44318,
+24399,
+59825,
+54028,
+26283,
+60045,
+25662,
+13707,
+4213,
+14449,
+64962,
+26604,
+39471,
+64663,
+26426,
+30762,
+9102,
+62687,
+58982,
+21850,
+12289,
+31639,
+29648,
+54813,
+18312,
+39646,
+20219,
+15925,
+49699,
+47305,
+54315,
+6595,
+51439,
+37551,
+4303,
+22674,
+5559,
+64984,
+26477,
+1246,
+33006,
+46715,
+42751,
+34537,
+64538,
+13069,
+55725,
+27322,
+32941,
+34186,
+25666,
+53024,
+32010,
+53166,
+3005,
+10575,
+22613,
+44043,
+15549,
+12531,
+1345,
+53122,
+43497,
+114,
+30205,
+2290,
+15477,
+35852,
+37991,
+55300,
+65045,
+48453,
+26802,
+7065,
+10862,
+35234,
+52412,
+45798,
+64771,
+10881,
+3211,
+40829,
+64660,
+3218,
+29909,
+55794,
+75,
+62951,
+54687,
+7250,
+34184,
+32943,
+1308,
+48966,
+53542,
+47020,
+40928,
+39116,
+32514,
+29344,
+13095,
+60939,
+29740,
+7242,
+36140,
+24496,
+49644,
+14513,
+54859,
+3911,
+1492,
+36786,
+63320,
+1391,
+52779,
+24727,
+44953,
+1437,
+54919,
+43859,
+62300,
+34815,
+3858,
+48657,
+27355,
+7593,
+34461,
+6754,
+38353,
+18751,
+45187,
+51657,
+30765,
+9312,
+33906,
+62631,
+26103,
+27801,
+59237,
+21049,
+35721,
+46111,
+36614,
+46302,
+47462,
+29274,
+21040,
+15883,
+26956,
+37317,
+27083,
+61572,
+46277,
+19755,
+25760,
+19613,
+63590,
+48396,
+38417,
+18730,
+15242,
+30695,
+26785,
+60329,
+47313,
+47610,
+25304,
+13774,
+7128,
+60537,
+53503,
+56132,
+30630,
+48736,
+56671,
+44853,
+55373,
+47727,
+43723,
+28757,
+14602,
+58483,
+18994,
+6170,
+44149,
+1668,
+42174,
+47587,
+19393,
+52092,
+42602,
+63049,
+22321,
+1331,
+50433,
+60960,
+14194,
+28455,
+7060,
+39132,
+47595,
+36932,
+11253,
+26981,
+64530,
+51271,
+17744,
+8008,
+39189,
+26077,
+16002,
+37948,
+14271,
+43501,
+34731,
+54490,
+33612,
+17268,
+63538,
+10071,
+476,
+23358,
+32322,
+7430,
+12454,
+65317,
+12869,
+8062,
+39780,
+4772,
+358,
+48743,
+14351,
+4024,
+62656,
+2891,
+5825,
+62557,
+23,
+46423,
+36255,
+10299,
+31007,
+48023,
+50921,
+60278,
+10982,
+38125,
+62700,
+25028,
+59149,
+48279,
+6290,
+64590,
+48842,
+32267,
+8054,
+16908,
+37531,
+29925,
+42528,
+21145,
+4622,
+4665,
+9847,
+4320,
+17883,
+19720,
+4268,
+43416,
+48019,
+51826,
+19251,
+16181,
+32729,
+39153,
+55875,
+7809,
+3131,
+16176,
+5693,
+14630,
+41923,
+44092,
+15719,
+37493,
+57293,
+26087,
+62747,
+14570,
+12100,
+6500,
+16292,
+57099,
+39912,
+26025,
+26313,
+30259,
+33599,
+37203,
+53688,
+48407,
+89,
+38595,
+52723,
+48664,
+33962,
+42467,
+21805,
+22581,
+59011,
+57774,
+35674,
+28288,
+44141,
+12168,
+11720,
+12509,
+50728,
+40820,
+44768,
+26455,
+45703,
+62488,
+58980,
+62689,
+36280,
+64801,
+22025,
+21564,
+8018,
+17793,
+24512,
+51836,
+6349,
+47714,
+3226,
+13608,
+1595,
+62294,
+55289,
+18555,
+60936,
+18907,
+54547,
+61411,
+63981,
+5775,
+57743,
+39081,
+45675,
+3050,
+25885,
+2353,
+20798,
+11710,
+28508,
+20226,
+25873,
+48423,
+9736,
+40318,
+8798,
+23813,
+30067,
+38600,
+47200,
+38885,
+12862,
+44032,
+15602,
+62560,
+51422,
+15171,
+15625,
+34120,
+33460,
+61090,
+14609,
+36708,
+26279,
+23924,
+31908,
+64928,
+41285,
+19010,
+33702,
+26861,
+22542,
+10621,
+18591,
+24595,
+30891,
+55043,
+2593,
+5534,
+17146,
+38000,
+12908,
+50409,
+18024,
+45806,
+39951,
+52364,
+18465,
+39967,
+56567,
+7973,
+62571,
+8113,
+35758,
+27351,
+50704,
+47647,
+11314,
+53450,
+53950,
+21660,
+16681,
+8200,
+1847,
+21031,
+13640,
+60748,
+22928,
+53822,
+31382,
+57473,
+25759,
+20031,
+46278,
+5919,
+37563,
+58247,
+56322,
+5741,
+56487,
+45853,
+63144,
+41400,
+51307,
+30888,
+49254,
+31087,
+13124,
+45430,
+767,
+21932,
+63787,
+53210,
+56547,
+49768,
+47512,
+45754,
+25587,
+53472,
+39283,
+51813,
+54530,
+25743,
+27090,
+24669,
+64138,
+4267,
+19917,
+17884,
+56553,
+25441,
+9550,
+9484,
+59962,
+58213,
+30644,
+788,
+53743,
+12177,
+19703,
+58969,
+61596,
+35381,
+58968,
+19708,
+12178,
+1314,
+58461,
+21788,
+15807,
+28661,
+34928,
+35765,
+9960,
+9245,
+2333,
+30467,
+39685,
+42047,
+23145,
+253,
+13160,
+9049,
+45790,
+2394,
+38256,
+55657,
+2774,
+5858,
+18614,
+45340,
+8357,
+40548,
+1016,
+12116,
+51708,
+58917,
+2209,
+31524,
+44641,
+9997,
+38787,
+58815,
+36158,
+27594,
+58254,
+10,
+59580,
+40571,
+14799,
+54348,
+59112,
+45124,
+30461,
+15373,
+49693,
+55155,
+43101,
+29248,
+33519,
+42297,
+20399,
+65130,
+62471,
+43450,
+38087,
+41409,
+57772,
+59013,
+21562,
+22027,
+55354,
+25110,
+51027,
+26588,
+6306,
+8927,
+26552,
+25929,
+1980,
+25177,
+59002,
+1997,
+55468,
+65193,
+19108,
+21083,
+18816,
+31601,
+26154,
+17536,
+52604,
+30565,
+63589,
+20029,
+25761,
+20930,
+64511,
+38492,
+13453,
+15506,
+53286,
+58995,
+1033,
+59897,
+43026,
+42092,
+45050,
+53295,
+35417,
+12678,
+28792,
+52152,
+16018,
+39072,
+30350,
+35433,
+18343,
+17725,
+42227,
+24499,
+36041,
+51844,
+65112,
+14685,
+3397,
+36164,
+63840,
+3672,
+15221,
+46211,
+65460,
+2588,
+59969,
+16812,
+37145,
+30550,
+5971,
+58350,
+46249,
+22635,
+39970,
+57558,
+47786,
+37946,
+16004,
+40265,
+26513,
+25804,
+40174,
+8562,
+1746,
+4015,
+49627,
+5517,
+51060,
+23810,
+10972,
+33967,
+61249,
+14417,
+18733,
+7169,
+48747,
+23196,
+12271,
+25811,
+49951,
+21792,
+47702,
+14998,
+39186,
+46827,
+41921,
+14632,
+34926,
+28663,
+25245,
+14437,
+62238,
+28199,
+48510,
+1252,
+24427,
+26006,
+61438,
+10572,
+9243,
+9962,
+9526,
+14963,
+62827,
+64909,
+38048,
+5797,
+26417,
+32834,
+53219,
+48905,
+64210,
+56691,
+15761,
+39339,
+33874,
+27276,
+54827,
+54198,
+6631,
+57168,
+64653,
+47725,
+55375,
+31644,
+43809,
+47681,
+13681,
+50665,
+49373,
+33473,
+50582,
+59100,
+22719,
+5173,
+65500,
+34590,
+52071,
+35152,
+11146,
+55208,
+2076,
+50550,
+54207,
+29018,
+52927,
+17595,
+9912,
+51747,
+24212,
+9109,
+54337,
+1148,
+52134,
+37063,
+65271,
+10388,
+3950,
+34619,
+27929,
+16668,
+29461,
+24757,
+6902,
+9476,
+35221,
+15841,
+12631,
+33252,
+58707,
+58801,
+46777,
+52650,
+35404,
+16407,
+4830,
+25722,
+28865,
+9439,
+33554,
+18708,
+23618,
+49511,
+14732,
+61450,
+26867,
+11780,
+41851,
+43435,
+60319,
+5252,
+58227,
+29815,
+45494,
+28924,
+63381,
+11010,
+62193,
+55383,
+58089,
+19274,
+58986,
+1772,
+37889,
+48973,
+9442,
+4068,
+11050,
+26676,
+2610,
+58284,
+53600,
+13876,
+51998,
+25053,
+48815,
+18003,
+4300,
+32000,
+7003,
+10902,
+45161,
+42707,
+31792,
+49750,
+52091,
+19996,
+47588,
+52944,
+18566,
+17438,
+37484,
+59966,
+20321,
+16948,
+25212,
+50714,
+64269,
+28709,
+37513,
+1603,
+30604,
+44591,
+25493,
+20204,
+49946,
+2889,
+62658,
+11273,
+42303,
+5654,
+34166,
+51409,
+64471,
+20739,
+31831,
+58260,
+56882,
+43917,
+28818,
+1063,
+60672,
+37930,
+3308,
+48109,
+44986,
+4233,
+8375,
+48207,
+40721,
+32821,
+63088,
+3401,
+13852,
+41155,
+7213,
+40546,
+8359,
+18447,
+61108,
+60491,
+25943,
+38481,
+37395,
+26669,
+64415,
+62381,
+12506,
+40978,
+36559,
+41935,
+35159,
+50315,
+45960,
+78,
+55583,
+59276,
+61633,
+3141,
+4861,
+56035,
+2636,
+37330,
+4102,
+49006,
+48676,
+52038,
+48491,
+15350,
+112,
+43499,
+14273,
+12641,
+4817,
+5374,
+28630,
+44582,
+65391,
+43843,
+39638,
+57139,
+39907,
+180,
+61193,
+35946,
+16960,
+56902,
+25604,
+33769,
+56442,
+7186,
+45969,
+42903,
+55910,
+33178,
+12374,
+520,
+35474,
+52002,
+13981,
+37288,
+25575,
+2730,
+9360,
+58985,
+19419,
+58090,
+50496,
+64697,
+61398,
+64952,
+37595,
+449,
+47042,
+65173,
+35929,
+43181,
+23548,
+3964,
+45505,
+45783,
+59619,
+58693,
+44085,
+39135,
+55894,
+40790,
+16180,
+19912,
+51827,
+44513,
+51703,
+55693,
+39218,
+65411,
+14157,
+28983,
+47632,
+9251,
+51888,
+48784,
+41435,
+27074,
+28922,
+45496,
+18076,
+2082,
+48202,
+7695,
+64184,
+17791,
+8020,
+25469,
+63127,
+54792,
+14049,
+16201,
+3597,
+30438,
+43622,
+53070,
+52684,
+59944,
+33836,
+37477,
+63221,
+17185,
+29040,
+24651,
+37265,
+37458,
+31257,
+13321,
+55399,
+52375,
+56490,
+3877,
+27294,
+1809,
+32335,
+50688,
+56494,
+21535,
+64762,
+30568,
+51787,
+21799,
+3828,
+49449,
+7387,
+4942,
+6206,
+44083,
+58695,
+29747,
+60949,
+34434,
+47330,
+34195,
+23553,
+29336,
+4938,
+37500,
+13510,
+3411,
+1480,
+5975,
+54670,
+24217,
+57103,
+3436,
+30529,
+57194,
+62969,
+16698,
+15201,
+2842,
+46497,
+5753,
+12974,
+496,
+11737,
+32736,
+11444,
+32257,
+44484,
+62235,
+43421,
+47126,
+33275,
+15562,
+20909,
+41163,
+51198,
+22997,
+13916,
+59402,
+65335,
+23803,
+56268,
+42139,
+40768,
+52157,
+52170,
+4360,
+33844,
+16195,
+55442,
+63324,
+21422,
+58147,
+39364,
+29707,
+12860,
+38887,
+2416,
+37013,
+57781,
+61921,
+34664,
+59456,
+56559,
+1337,
+5242,
+65513,
+45592,
+29283,
+59999,
+36563,
+49566,
+21082,
+19622,
+65194,
+58831,
+30712,
+62796,
+31741,
+37510,
+54386,
+9858,
+47570,
+12721,
+10340,
+42203,
+62527,
+36091,
+25991,
+49242,
+25417,
+15974,
+16095,
+21373,
+64658,
+40831,
+50953,
+59098,
+50584,
+47475,
+64170,
+31714,
+7746,
+30515,
+1223,
+58738,
+32461,
+41269,
+20761,
+43988,
+35595,
+2794,
+9685,
+59780,
+5637,
+43115,
+57830,
+911,
+59861,
+21966,
+7743,
+6509,
+10199,
+64722,
+42592,
+58497,
+16760,
+7481,
+60116,
+9759,
+18487,
+37750,
+64295,
+40823,
+50308,
+53380,
+37433,
+17279,
+21365,
+2096,
+62694,
+46375,
+59785,
+49960,
+35273,
+23621,
+62156,
+7117,
+59673,
+9480,
+15872,
+33106,
+34754,
+14979,
+49145,
+31556,
+55368,
+39028,
+52976,
+17361,
+14764,
+3077,
+15310,
+62439,
+56756,
+4084,
+51468,
+9034,
+14784,
+61983,
+33701,
+19799,
+41286,
+7506,
+10320,
+63106,
+59254,
+23109,
+56415,
+31451,
+3187,
+39574,
+58331,
+1611,
+21020,
+45061,
+6169,
+20002,
+58484,
+10549,
+20333,
+47705,
+33885,
+12346,
+29322,
+37967,
+13784,
+27566,
+58646,
+33169,
+64477,
+63372,
+36434,
+4845,
+35312,
+31501,
+43080,
+39946,
+39126,
+58756,
+14568,
+62749,
+50888,
+42626,
+50883,
+37544,
+38714,
+30028,
+46902,
+40704,
+674,
+9516,
+14235,
+29837,
+32708,
+36900,
+1912,
+17610,
+31681,
+42496,
+49515,
+36220,
+33040,
+44013,
+25119,
+56858,
+32407,
+3546,
+63949,
+54801,
+50593,
+8834,
+4066,
+9444,
+30275,
+51392,
+25796,
+10699,
+16373,
+47955,
+58679,
+52530,
+24949,
+36179,
+23899,
+53947,
+5707,
+62105,
+13372,
+17619,
+24821,
+40157,
+12249,
+6544,
+9794,
+36687,
+8863,
+32422,
+6071,
+3552,
+31838,
+49709,
+47691,
+54546,
+19841,
+60937,
+13097,
+30678,
+34424,
+14323,
+8717,
+58855,
+58279,
+21359,
+58521,
+11324,
+63501,
+3930,
+29010,
+30864,
+33811,
+15137,
+37246,
+9469,
+17669,
+34802,
+22099,
+9707,
+10242,
+24487,
+62479,
+49545,
+37415,
+42489,
+51445,
+14433,
+38100,
+11990,
+65221,
+7699,
+31263,
+6644,
+9548,
+25443,
+37939,
+64144,
+12760,
+53089,
+35310,
+4847,
+59279,
+51335,
+7468,
+20744,
+57460,
+23916,
+2374,
+54445,
+57354,
+810,
+59154,
+43018,
+59202,
+36722,
+31887,
+14143,
+39743,
+17838,
+17672,
+7273,
+47077,
+32118,
+41422,
+15805,
+21790,
+49953,
+33066,
+21188,
+15670,
+27176,
+20993,
+57612,
+59244,
+22239,
+32631,
+7683,
+60395,
+10671,
+32530,
+45394,
+22787,
+50678,
+16608,
+62622,
+31600,
+19620,
+21084,
+57870,
+23793,
+38785,
+9999,
+22762,
+33603,
+17454,
+55393,
+6346,
+57009,
+26617,
+63620,
+1335,
+56561,
+39995,
+21088,
+896,
+64369,
+4606,
+54058,
+7989,
+32425,
+57310,
+53930,
+51596,
+3795,
+54713,
+28776,
+47009,
+49970,
+58730,
+59096,
+50955,
+11840,
+44993,
+28359,
+4052,
+63921,
+8604,
+37343,
+59250,
+3306,
+37932,
+26580,
+64500,
+47509,
+22352,
+44654,
+59848,
+33818,
+6458,
+2700,
+7814,
+6735,
+46089,
+12888,
+53955,
+3204,
+57420,
+38204,
+42085,
+27612,
+45186,
+20055,
+38354,
+52352,
+4450,
+49047,
+31440,
+16268,
+12227,
+53152,
+38776,
+42338,
+47607,
+60903,
+2137,
+35467,
+4201,
+50768,
+7168,
+19546,
+14418,
+15241,
+20025,
+38418,
+44684,
+48060,
+57344,
+65530,
+10924,
+40331,
+29492,
+38837,
+9638,
+51151,
+34134,
+34556,
+41225,
+40842,
+18109,
+37901,
+57527,
+26350,
+46002,
+23617,
+19439,
+33555,
+36244,
+24046,
+4040,
+53115,
+36455,
+43953,
+59134,
+30415,
+53336,
+11427,
+2920,
+12543,
+29431,
+42026,
+25360,
+45074,
+1343,
+12533,
+52074,
+7172,
+17589,
+16443,
+16728,
+7605,
+63134,
+56998,
+11062,
+24704,
+14586,
+21324,
+18588,
+27980,
+63512,
+54885,
+52961,
+17210,
+20651,
+26483,
+3458,
+63757,
+13521,
+38634,
+35230,
+8930,
+37088,
+13342,
+54133,
+54164,
+53914,
+60570,
+17540,
+47578,
+62498,
+62410,
+48703,
+13437,
+56657,
+55293,
+41052,
+42569,
+54258,
+54282,
+38558,
+59132,
+43955,
+17417,
+22618,
+16357,
+26840,
+33994,
+35508,
+46574,
+24834,
+9377,
+49304,
+51871,
+29314,
+24320,
+36635,
+36739,
+49721,
+59724,
+42794,
+37778,
+15383,
+14694,
+57769,
+23703,
+6396,
+17274,
+12987,
+45339,
+19678,
+5859,
+39737,
+6085,
+8885,
+44688,
+54721,
+11042,
+54903,
+36557,
+40980,
+24125,
+41837,
+1663,
+10123,
+64198,
+60297,
+48988,
+5304,
+58904,
+8371,
+16260,
+24594,
+19794,
+10622,
+27979,
+18676,
+21325,
+21104,
+40969,
+29711,
+462,
+45155,
+8786,
+34966,
+57385,
+7969,
+42045,
+39687,
+60414,
+37299,
+61098,
+33626,
+61034,
+13845,
+35742,
+29426,
+17437,
+19390,
+52945,
+24038,
+52580,
+58006,
+12856,
+33450,
+11572,
+505,
+42613,
+60935,
+19843,
+55290,
+42057,
+49234,
+3985,
+47600,
+20284,
+53626,
+11265,
+59936,
+38937,
+37324,
+53902,
+12749,
+25563,
+53635,
+403,
+55967,
+49539,
+44468,
+59077,
+62839,
+244,
+65003,
+38733,
+58474,
+36760,
+46751,
+15568,
+23776,
+6980,
+34633,
+17022,
+10569,
+30587,
+63389,
+56350,
+11994,
+45448,
+24610,
+42115,
+25087,
+14716,
+17110,
+60984,
+6667,
+43807,
+31646,
+50023,
+4834,
+47822,
+58712,
+32030,
+42389,
+57967,
+56585,
+45308,
+1527,
+57689,
+65087,
+46271,
+365,
+34202,
+1166,
+60933,
+42615,
+5994,
+37749,
+19051,
+9760,
+57340,
+38055,
+40259,
+34272,
+58895,
+2103,
+3770,
+11871,
+8839,
+24266,
+44778,
+11831,
+33196,
+36249,
+4020,
+54406,
+29896,
+35320,
+43534,
+39966,
+19780,
+52365,
+54203,
+3170,
+58263,
+46696,
+33420,
+12104,
+2237,
+57075,
+54539,
+54787,
+41912,
+30847,
+547,
+37912,
+57697,
+61107,
+19341,
+8360,
+26730,
+23768,
+43946,
+59215,
+55906,
+40065,
+30787,
+26143,
+26475,
+64986,
+21092,
+1191,
+48439,
+36413,
+55947,
+55662,
+18101,
+62667,
+32187,
+40757,
+32628,
+4699,
+28907,
+33665,
+58820,
+48861,
+36855,
+64103,
+11526,
+44530,
+54871,
+26775,
+22306,
+22115,
+46386,
+32263,
+62810,
+60898,
+65213,
+43016,
+59156,
+27808,
+6225,
+43759,
+63311,
+28042,
+8246,
+2091,
+3541,
+62263,
+1703,
+3424,
+40146,
+44132,
+33091,
+25527,
+5631,
+41718,
+45003,
+5443,
+58953,
+48315,
+7148,
+24276,
+21646,
+37058,
+52814,
+55199,
+31194,
+33842,
+4362,
+23509,
+40179,
+26772,
+635,
+33903,
+7735,
+21697,
+29145,
+17767,
+3044,
+35363,
+52471,
+49772,
+37696,
+43238,
+63413,
+3088,
+49350,
+58100,
+38983,
+39610,
+40712,
+60741,
+36863,
+50976,
+25059,
+7776,
+33549,
+1804,
+40594,
+17724,
+19590,
+35434,
+57415,
+32136,
+10846,
+26936,
+45719,
+26653,
+17579,
+35092,
+42433,
+16420,
+18280,
+54464,
+4541,
+34703,
+22332,
+36266,
+51582,
+34310,
+14468,
+12294,
+56519,
+10707,
+24660,
+7939,
+48642,
+49210,
+28691,
+4398,
+39645,
+20160,
+54814,
+31437,
+9951,
+11387,
+40084,
+4314,
+9767,
+12836,
+23414,
+63965,
+55818,
+36074,
+40782,
+21078,
+47900,
+31153,
+56019,
+51304,
+1691,
+43886,
+1899,
+2519,
+15423,
+1093,
+41995,
+43542,
+27394,
+14972,
+44327,
+33593,
+54463,
+18331,
+16421,
+44271,
+27051,
+31517,
+31551,
+4257,
+58658,
+1840,
+60483,
+49585,
+12405,
+1496,
+56826,
+41889,
+52792,
+57675,
+3811,
+32546,
+41276,
+22658,
+25222,
+24807,
+24794,
+33088,
+49895,
+59035,
+49247,
+13246,
+8733,
+50178,
+31165,
+16237,
+11540,
+53982,
+12565,
+23267,
+59164,
+49812,
+9133,
+2993,
+61182,
+20788,
+10152,
+7450,
+2886,
+62428,
+15837,
+45382,
+11223,
+50269,
+60288,
+22367,
+40695,
+62628,
+5207,
+45517,
+17928,
+4636,
+17894,
+27168,
+2295,
+42503,
+37958,
+44875,
+8502,
+18052,
+28150,
+44525,
+62057,
+54499,
+40854,
+31102,
+46355,
+31343,
+52426,
+10084,
+52853,
+48150,
+40105,
+58598,
+26724,
+39539,
+5658,
+28803,
+13482,
+21393,
+51560,
+48652,
+14328,
+55892,
+39137,
+29176,
+48896,
+38311,
+5723,
+62023,
+21287,
+56724,
+52798,
+33284,
+39209,
+62267,
+11689,
+6465,
+4410,
+34961,
+54289,
+16579,
+62190,
+64069,
+61562,
+41444,
+21717,
+55100,
+9716,
+53735,
+38659,
+51049,
+38223,
+62769,
+25960,
+56965,
+61646,
+34151,
+13620,
+35245,
+43926,
+58493,
+21552,
+56954,
+48287,
+7125,
+50777,
+65165,
+44870,
+64038,
+32857,
+14280,
+4971,
+39716,
+831,
+20245,
+9301,
+16424,
+12063,
+26256,
+36652,
+36148,
+23459,
+30990,
+29571,
+20596,
+27311,
+64427,
+8038,
+857,
+55379,
+37412,
+31877,
+6919,
+58274,
+31488,
+61594,
+58971,
+23780,
+6943,
+34138,
+27330,
+16899,
+37900,
+18714,
+40843,
+55142,
+29062,
+60462,
+57982,
+43059,
+62666,
+18429,
+55663,
+4787,
+42611,
+507,
+39349,
+20275,
+55935,
+10950,
+63599,
+35730,
+47796,
+49900,
+7424,
+866,
+55938,
+1551,
+54770,
+51957,
+39162,
+1236,
+35498,
+39328,
+21204,
+2081,
+19234,
+45497,
+36264,
+22334,
+11028,
+8849,
+40160,
+58428,
+9507,
+8169,
+58503,
+41459,
+60026,
+14952,
+46455,
+1368,
+29586,
+34505,
+24443,
+48210,
+59588,
+30665,
+59522,
+28149,
+18214,
+8503,
+10613,
+30559,
+55315,
+60620,
+51980,
+59519,
+40029,
+63038,
+12463,
+4381,
+14942,
+56705,
+26621,
+36600,
+23608,
+16218,
+60184,
+15075,
+527,
+8069,
+3355,
+1036,
+46768,
+43186,
+15768,
+45805,
+19784,
+50410,
+53402,
+38149,
+49000,
+13821,
+52570,
+10105,
+40746,
+44830,
+37207,
+20491,
+44510,
+61327,
+62564,
+48404,
+62076,
+14529,
+22206,
+50242,
+4299,
+19403,
+48816,
+56315,
+12645,
+10796,
+52257,
+50796,
+24753,
+44461,
+54738,
+64488,
+27454,
+34191,
+58517,
+48946,
+64025,
+24984,
+37571,
+56274,
+61809,
+29279,
+6163,
+3499,
+55670,
+36051,
+7885,
+23711,
+62799,
+62066,
+14676,
+38590,
+485,
+31421,
+23826,
+25702,
+55678,
+46438,
+52817,
+20823,
+64811,
+61199,
+65402,
+35704,
+47996,
+46882,
+3040,
+61745,
+55978,
+55054,
+64869,
+3833,
+23368,
+23362,
+45540,
+46145,
+12594,
+3659,
+49297,
+2257,
+21450,
+26404,
+45553,
+28606,
+55648,
+5933,
+61551,
+46987,
+45254,
+11960,
+63560,
+32869,
+12269,
+23198,
+46797,
+4635,
+18223,
+45518,
+40234,
+6391,
+40531,
+40749,
+44185,
+15502,
+20601,
+7238,
+54502,
+22993,
+63141,
+27283,
+2367,
+28369,
+50437,
+22858,
+53875,
+50504,
+1043,
+48526,
+36553,
+46295,
+56287,
+62484,
+10115,
+22501,
+10885,
+9194,
+51055,
+2816,
+59652,
+27167,
+18221,
+4637,
+326,
+14832,
+45766,
+54224,
+58858,
+49826,
+64566,
+56552,
+19719,
+19918,
+4321,
+35226,
+29444,
+39548,
+11155,
+12743,
+7864,
+60855,
+21842,
+40962,
+48291,
+37138,
+33568,
+41591,
+30168,
+41625,
+28528,
+36182,
+13475,
+30547,
+51312,
+26683,
+42100,
+11732,
+50384,
+21727,
+46044,
+51857,
+42584,
+53177,
+35922,
+12321,
+27621,
+8915,
+27558,
+15827,
+46628,
+10992,
+33470,
+61853,
+53331,
+57709,
+34800,
+17671,
+18844,
+39744,
+43744,
+45999,
+50540,
+45203,
+46547,
+31653,
+41693,
+24643,
+13811,
+15683,
+24592,
+16262,
+59187,
+45688,
+62312,
+22260,
+60436,
+1633,
+30363,
+30773,
+60067,
+7203,
+25337,
+883,
+46040,
+10163,
+45530,
+21815,
+5892,
+6518,
+25687,
+2514,
+45030,
+43214,
+64208,
+48907,
+4687,
+49671,
+5586,
+22441,
+62241,
+63191,
+24511,
+19853,
+8019,
+19229,
+64185,
+4852,
+44201,
+35379,
+61598,
+63724,
+61885,
+12674,
+50533,
+24223,
+31084,
+34278,
+52218,
+53526,
+63830,
+62580,
+15055,
+30580,
+55112,
+43904,
+9305,
+13938,
+3043,
+18366,
+29146,
+58797,
+50931,
+10600,
+58818,
+33667,
+56212,
+48971,
+37891,
+28430,
+11741,
+53644,
+37272,
+13670,
+7978,
+45973,
+27592,
+36160,
+22130,
+24118,
+53036,
+8007,
+19978,
+51272,
+29806,
+47447,
+15638,
+23305,
+54124,
+38564,
+63028,
+25897,
+8400,
+4276,
+55532,
+20329,
+43111,
+34127,
+48979,
+12141,
+42226,
+19589,
+18344,
+40595,
+60393,
+7685,
+60925,
+36003,
+24409,
+36684,
+60702,
+47032,
+6826,
+41702,
+17646,
+47853,
+64882,
+25354,
+41055,
+4038,
+24048,
+56109,
+22185,
+31582,
+38892,
+37839,
+52848,
+51318,
+41804,
+44669,
+38283,
+65437,
+663,
+33263,
+23015,
+52397,
+57657,
+22867,
+1219,
+16815,
+7363,
+47890,
+47826,
+656,
+39403,
+6090,
+44901,
+24018,
+42007,
+7541,
+5936,
+30490,
+44805,
+7272,
+18843,
+17839,
+34801,
+18887,
+9470,
+44066,
+25297,
+56774,
+23749,
+57875,
+52227,
+3837,
+36936,
+52080,
+64598,
+61276,
+37954,
+37726,
+51627,
+58654,
+34636,
+33357,
+60424,
+64397,
+58173,
+47852,
+17712,
+41703,
+8740,
+49553,
+21520,
+52148,
+20928,
+25763,
+44458,
+47240,
+34385,
+31321,
+48140,
+49425,
+7616,
+27663,
+35103,
+2751,
+54893,
+7027,
+56446,
+64507,
+6370,
+22313,
+27242,
+12188,
+24820,
+18922,
+13373,
+40218,
+53100,
+37328,
+2638,
+35762,
+16481,
+31680,
+18954,
+1913,
+38385,
+43669,
+62466,
+34520,
+22177,
+40559,
+64945,
+17411,
+5485,
+39101,
+11906,
+60133,
+9911,
+19473,
+52928,
+60210,
+37347,
+31757,
+16442,
+18686,
+7173,
+29901,
+62783,
+5881,
+53137,
+49163,
+46605,
+6859,
+35091,
+18335,
+26654,
+28050,
+38962,
+25070,
+50251,
+49232,
+42059,
+925,
+22578,
+567,
+10270,
+10625,
+20755,
+35065,
+34317,
+34916,
+23260,
+28139,
+30727,
+8159,
+64518,
+37791,
+5663,
+34077,
+4230,
+28626,
+5500,
+55328,
+58341,
+43832,
+6786,
+310,
+36668,
+13720,
+57442,
+6179,
+29832,
+47577,
+18656,
+60571,
+26229,
+52603,
+19617,
+26155,
+13153,
+3095,
+41512,
+58879,
+572,
+916,
+52888,
+42941,
+23936,
+15325,
+53663,
+41466,
+30270,
+8057,
+3116,
+22120,
+45623,
+13712,
+63655,
+34529,
+45596,
+14617,
+50631,
+27381,
+56790,
+2826,
+38739,
+53921,
+4893,
+65230,
+27201,
+43086,
+53356,
+23929,
+50456,
+44534,
+49458,
+65110,
+51846,
+29811,
+56782,
+53597,
+31356,
+1510,
+40555,
+29439,
+1143,
+8766,
+26307,
+1548,
+27502,
+37160,
+24776,
+43039,
+8822,
+35425,
+20852,
+46810,
+8194,
+28495,
+12073,
+58118,
+964,
+6317,
+52831,
+2900,
+57857,
+62329,
+41894,
+32958,
+39392,
+39064,
+26756,
+56255,
+15063,
+6479,
+35126,
+6934,
+7873,
+55392,
+18808,
+33604,
+5877,
+13347,
+7410,
+2539,
+20978,
+14646,
+63963,
+23416,
+24845,
+30749,
+48963,
+56684,
+21168,
+37483,
+19389,
+18567,
+29427,
+51020,
+42219,
+3312,
+7162,
+33872,
+39341,
+62228,
+46182,
+20552,
+49196,
+36287,
+32020,
+40726,
+23635,
+25491,
+44593,
+53994,
+22617,
+18641,
+43956,
+16118,
+1607,
+13410,
+5484,
+17601,
+64946,
+52986,
+7714,
+59065,
+53334,
+30417,
+49213,
+63398,
+30072,
+37772,
+35680,
+39897,
+34215,
+47903,
+16113,
+41530,
+30651,
+26832,
+28582,
+28350,
+57408,
+13926,
+12683,
+47335,
+25433,
+31074,
+58785,
+62781,
+29903,
+41801,
+16339,
+29252,
+58398,
+38058,
+60713,
+28659,
+15809,
+31186,
+7398,
+15608,
+58606,
+59930,
+24516,
+50499,
+43441,
+21467,
+59985,
+51760,
+14763,
+19022,
+52977,
+43047,
+56064,
+58446,
+49334,
+61942,
+11945,
+10858,
+44662,
+9084,
+62602,
+63550,
+34,
+61793,
+57939,
+28633,
+21527,
+2683,
+55307,
+21663,
+65057,
+5337,
+59387,
+7515,
+63454,
+46305,
+57843,
+9619,
+38240,
+57161,
+46186,
+24817,
+41093,
+47480,
+5950,
+28458,
+32568,
+25953,
+43173,
+32305,
+30396,
+54249,
+35304,
+34042,
+13962,
+54799,
+63951,
+28614,
+3638,
+1874,
+55856,
+7902,
+43579,
+42188,
+28618,
+48828,
+34060,
+21703,
+58753,
+23895,
+49524,
+6251,
+41187,
+14845,
+57720,
+38966,
+7492,
+22663,
+10252,
+44649,
+16841,
+12278,
+57959,
+23467,
+62212,
+44619,
+37239,
+61654,
+52488,
+42589,
+21364,
+19044,
+37434,
+54085,
+2462,
+12986,
+18617,
+6397,
+24288,
+59181,
+23122,
+63537,
+19967,
+33613,
+36587,
+25653,
+61775,
+42641,
+26170,
+64341,
+35210,
+6387,
+45604,
+59248,
+37345,
+60212,
+44156,
+48884,
+59174,
+8582,
+47388,
+58413,
+63109,
+24574,
+47665,
+28400,
+50825,
+31805,
+12053,
+1855,
+6816,
+49608,
+41011,
+11163,
+59016,
+51536,
+2468,
+38357,
+14669,
+13506,
+15188,
+26882,
+7612,
+17089,
+62520,
+26055,
+58357,
+6583,
+47153,
+58390,
+40620,
+60159,
+49364,
+38366,
+54821,
+1420,
+38327,
+40370,
+37806,
+20650,
+18671,
+52962,
+9388,
+8105,
+31961,
+51683,
+24384,
+51369,
+39771,
+49569,
+37619,
+11382,
+16047,
+50579,
+4501,
+51694,
+54975,
+62640,
+28945,
+54428,
+32799,
+54095,
+43868,
+14879,
+29039,
+19213,
+63222,
+59732,
+49086,
+10467,
+43156,
+7401,
+54019,
+36437,
+33917,
+50743,
+29418,
+15517,
+20674,
+63392,
+39097,
+49753,
+21992,
+14087,
+56462,
+51288,
+41394,
+32232,
+62356,
+6328,
+22430,
+4293,
+4595,
+62187,
+57045,
+36648,
+21796,
+57907,
+60650,
+1624,
+14036,
+33122,
+61933,
+37999,
+19788,
+5535,
+56458,
+62773,
+1946,
+45510,
+27121,
+35558,
+32975,
+27795,
+36964,
+16477,
+2972,
+47528,
+3462,
+11848,
+45654,
+65449,
+7534,
+375,
+33641,
+8029,
+42547,
+51609,
+52856,
+28154,
+43001,
+41231,
+61674,
+64978,
+33944,
+39663,
+45712,
+55802,
+22401,
+60983,
+18512,
+14717,
+30267,
+14259,
+53097,
+5433,
+53586,
+23183,
+58666,
+36986,
+51253,
+64345,
+47104,
+29125,
+51495,
+54910,
+44784,
+40270,
+40188,
+23878,
+62519,
+17227,
+7613,
+24317,
+32500,
+491,
+45424,
+38736,
+26722,
+58600,
+1201,
+50326,
+23428,
+16130,
+27238,
+57757,
+15250,
+11466,
+4979,
+39389,
+50652,
+36226,
+54083,
+37436,
+57438,
+20193,
+32419,
+20687,
+5681,
+7602,
+39618,
+25836,
+29595,
+20287,
+23366,
+3835,
+52229,
+65341,
+57808,
+20617,
+28917,
+32978,
+33003,
+13498,
+46225,
+50188,
+11970,
+27533,
+60580,
+26594,
+6896,
+8458,
+11328,
+36521,
+20639,
+42273,
+1866,
+9900,
+63817,
+65233,
+34608,
+1728,
+37337,
+46011,
+64286,
+14524,
+7888,
+10568,
+18523,
+34634,
+58656,
+4259,
+27585,
+44097,
+50443,
+41899,
+7623,
+49042,
+64493,
+47630,
+28985,
+58902,
+5306,
+61117,
+41794,
+56299,
+41876,
+10380,
+22438,
+9537,
+51940,
+16231,
+13668,
+37274,
+63073,
+52553,
+58900,
+28987,
+55050,
+32782,
+20340,
+5852,
+24478,
+29194,
+34443,
+56346,
+56959,
+21946,
+14445,
+39756,
+29152,
+41212,
+21165,
+53292,
+6119,
+64178,
+49757,
+44721,
+34232,
+13180,
+63579,
+5828,
+26299,
+2924,
+22362,
+33234,
+2600,
+62338,
+28302,
+56901,
+19294,
+35947,
+7339,
+61904,
+13841,
+10564,
+10410,
+40100,
+15468,
+1272,
+20388,
+25211,
+19385,
+20322,
+43458,
+31030,
+41096,
+24690,
+29875,
+21571,
+14210,
+58685,
+49027,
+39008,
+34862,
+7229,
+15438,
+209,
+53991,
+20647,
+15341,
+32573,
+27631,
+63605,
+64418,
+41328,
+56309,
+21501,
+38268,
+36752,
+37157,
+61462,
+38432,
+7504,
+41288,
+53189,
+36217,
+9358,
+2732,
+50047,
+8383,
+37530,
+19927,
+8055,
+30272,
+61730,
+31082,
+24225,
+64556,
+16519,
+37899,
+18111,
+27331,
+23454,
+57597,
+47220,
+16328,
+49821,
+1323,
+32720,
+30355,
+10582,
+41046,
+41258,
+13976,
+47345,
+51210,
+15834,
+63957,
+61678,
+45476,
+38183,
+24622,
+52536,
+22823,
+13488,
+12813,
+45658,
+31576,
+50962,
+53428,
+21733,
+55952,
+4480,
+31051,
+11417,
+33332,
+20260,
+16530,
+46223,
+13500,
+7627,
+6986,
+22250,
+3612,
+10685,
+32193,
+26809,
+5044,
+39596,
+58431,
+31658,
+3427,
+4532,
+13519,
+63759,
+38025,
+7393,
+12277,
+17290,
+44650,
+7422,
+49902,
+8649,
+33512,
+3126,
+46885,
+52101,
+57816,
+31980,
+29295,
+11457,
+55088,
+39337,
+15763,
+27843,
+2310,
+39845,
+43342,
+16056,
+752,
+2525,
+13674,
+5982,
+7362,
+17687,
+1220,
+37144,
+19573,
+59970,
+8043,
+27156,
+29076,
+7635,
+39936,
+57671,
+4684,
+59355,
+34906,
+45526,
+8867,
+60520,
+12949,
+43387,
+23119,
+65178,
+45584,
+51187,
+13461,
+58367,
+45993,
+42342,
+31125,
+10455,
+4119,
+11979,
+35740,
+13847,
+4284,
+42948,
+207,
+15440,
+6949,
+38880,
+32413,
+3606,
+15554,
+42441,
+6803,
+39695,
+1614,
+56935,
+51533,
+39853,
+8455,
+5465,
+25024,
+24744,
+31142,
+7480,
+19055,
+58498,
+49624,
+15648,
+33672,
+38403,
+13648,
+46298,
+24103,
+15904,
+9676,
+24436,
+2117,
+1084,
+61189,
+14806,
+40898,
+49187,
+9297,
+10609,
+62221,
+22677,
+23019,
+35625,
+33211,
+35688,
+52186,
+37443,
+14747,
+26366,
+39616,
+7604,
+18684,
+16444,
+54750,
+37120,
+63174,
+40483,
+46133,
+47535,
+9223,
+56678,
+9671,
+14427,
+53042,
+2031,
+9838,
+37229,
+55439,
+702,
+36918,
+48232,
+31478,
+64674,
+60292,
+57977,
+15321,
+39425,
+61876,
+16242,
+43847,
+15200,
+19165,
+62970,
+946,
+45607,
+33271,
+25578,
+7134,
+10362,
+10771,
+5215,
+50272,
+58664,
+23185,
+32142,
+34053,
+55775,
+8199,
+19766,
+21661,
+55309,
+3737,
+6716,
+47117,
+28683,
+29404,
+3518,
+31829,
+20741,
+4415,
+29460,
+19459,
+27930,
+33728,
+24394,
+63370,
+64479,
+21826,
+15215,
+53000,
+58381,
+2813,
+45113,
+59749,
+8472,
+21380,
+48284,
+6364,
+56508,
+11899,
+8664,
+26836,
+12409,
+35241,
+6095,
+3280,
+13424,
+36360,
+47350,
+27227,
+60842,
+31122,
+28717,
+51154,
+25679,
+50514,
+12317,
+46028,
+1328,
+47736,
+60851,
+6184,
+59530,
+38902,
+35865,
+49204,
+2196,
+49549,
+51350,
+43506,
+60442,
+12056,
+33930,
+30895,
+71,
+15475,
+2292,
+10719,
+31504,
+7548,
+62621,
+18819,
+50679,
+5837,
+5715,
+54726,
+38723,
+28442,
+27137,
+64246,
+44556,
+36946,
+39353,
+22671,
+31408,
+16560,
+34299,
+7019,
+47548,
+1513,
+38570,
+54842,
+40001,
+3086,
+63415,
+51757,
+29842,
+24856,
+57043,
+62189,
+18172,
+54290,
+170,
+13415,
+34787,
+48095,
+8854,
+64228,
+45446,
+11996,
+21284,
+42978,
+9324,
+47778,
+27647,
+46122,
+56096,
+31985,
+34298,
+16594,
+31409,
+47809,
+53397,
+27951,
+8444,
+60711,
+38060,
+32915,
+50209,
+5239,
+46940,
+60234,
+14698,
+51052,
+22729,
+36512,
+7947,
+12048,
+44423,
+30086,
+22975,
+27387,
+47517,
+51717,
+33801,
+41029,
+37670,
+61434,
+46222,
+16862,
+20261,
+50378,
+38909,
+4739,
+38065,
+62606,
+51603,
+57330,
+8267,
+37898,
+16901,
+64557,
+33941,
+6591,
+56640,
+49500,
+55611,
+52840,
+6851,
+48599,
+26530,
+51900,
+2803,
+13861,
+56853,
+44193,
+40288,
+26994,
+421,
+49542,
+36695,
+3889,
+31388,
+57913,
+48524,
+1045,
+7081,
+41968,
+61086,
+46790,
+34098,
+55588,
+5581,
+52494,
+8619,
+13633,
+57573,
+31679,
+17612,
+35763,
+34930,
+2971,
+17135,
+36965,
+52899,
+63894,
+13292,
+2614,
+2833,
+54646,
+14306,
+34627,
+64745,
+59324,
+24551,
+3698,
+34745,
+15594,
+61054,
+58183,
+51509,
+34879,
+10585,
+49324,
+49558,
+56141,
+27883,
+5804,
+48355,
+55071,
+26316,
+514,
+30288,
+62353,
+54749,
+16727,
+18685,
+17590,
+31758,
+60247,
+14065,
+44809,
+3106,
+1583,
+24536,
+61585,
+30920,
+10980,
+60280,
+11460,
+28558,
+62541,
+53656,
+40421,
+12062,
+18136,
+9302,
+44270,
+18279,
+18332,
+42434,
+59226,
+39022,
+39735,
+5861,
+57276,
+55279,
+61404,
+60637,
+15907,
+62889,
+4829,
+19445,
+35405,
+15792,
+25717,
+14855,
+923,
+42061,
+42651,
+43321,
+8341,
+2570,
+27888,
+27434,
+23526,
+50557,
+46510,
+33982,
+14247,
+24145,
+8738,
+41705,
+56174,
+43690,
+4,
+39323,
+29128,
+60496,
+57827,
+3121,
+20972,
+24859,
+7944,
+12399,
+47954,
+18933,
+10700,
+11129,
+35643,
+31157,
+38314,
+60788,
+23342,
+12767,
+32364,
+63995,
+51546,
+28188,
+33782,
+22062,
+26839,
+18639,
+22619,
+57695,
+37914,
+56624,
+12701,
+24638,
+48794,
+56241,
+44294,
+22872,
+20989,
+50092,
+26052,
+40911,
+44984,
+48111,
+29251,
+17380,
+41802,
+51320,
+4754,
+12309,
+29671,
+48913,
+63342,
+21538,
+21443,
+49820,
+16894,
+47221,
+36847,
+35119,
+48133,
+2140,
+60273,
+52326,
+65027,
+10044,
+41548,
+56279,
+15364,
+44480,
+36850,
+20236,
+49663,
+32433,
+44574,
+42893,
+58661,
+38389,
+15285,
+47964,
+23479,
+46985,
+61553,
+45069,
+44923,
+49393,
+25265,
+25351,
+39545,
+46445,
+40121,
+57098,
+19892,
+6501,
+47284,
+52292,
+54983,
+55260,
+30684,
+40940,
+34648,
+45795,
+34451,
+31261,
+7701,
+65070,
+12799,
+15735,
+51915,
+30294,
+59444,
+36524,
+54006,
+7089,
+16205,
+12226,
+18745,
+31441,
+63043,
+37646,
+21119,
+59186,
+17825,
+24593,
+18593,
+8372,
+56842,
+40172,
+25806,
+45128,
+54719,
+44690,
+2230,
+23854,
+34172,
+58632,
+43050,
+61717,
+15051,
+26356,
+23598,
+43846,
+16701,
+61877,
+47591,
+63281,
+11539,
+18248,
+31166,
+23163,
+56159,
+46905,
+13667,
+16999,
+51941,
+21318,
+50647,
+54701,
+42656,
+15947,
+31286,
+28755,
+43725,
+27098,
+39003,
+60183,
+18035,
+23609,
+33791,
+61568,
+54951,
+49081,
+15980,
+37828,
+41372,
+40388,
+37463,
+7210,
+12225,
+16270,
+7090,
+48181,
+3596,
+19223,
+14050,
+22907,
+57004,
+700,
+55441,
+19133,
+33845,
+43520,
+25908,
+57737,
+56765,
+5874,
+62448,
+47180,
+7438,
+22202,
+27573,
+57640,
+32728,
+19911,
+19252,
+40791,
+34397,
+5692,
+19905,
+3132,
+5377,
+48319,
+27172,
+41873,
+58578,
+27250,
+35837,
+3620,
+40163,
+14871,
+46047,
+32065,
+3686,
+39924,
+4676,
+12220,
+3476,
+24297,
+22396,
+50600,
+23340,
+60790,
+20811,
+60339,
+58123,
+9054,
+10264,
+42126,
+3576,
+20613,
+49238,
+40273,
+13662,
+59475,
+1677,
+2071,
+41784,
+30151,
+264,
+55151,
+16106,
+32149,
+60081,
+27237,
+17077,
+23429,
+22890,
+25476,
+52658,
+9543,
+42747,
+65098,
+6435,
+42820,
+21763,
+1606,
+17415,
+43957,
+34326,
+44239,
+41529,
+17396,
+47904,
+57922,
+41222,
+9524,
+9964,
+32148,
+16134,
+55152,
+52909,
+42701,
+25019,
+58133,
+32048,
+45089,
+35659,
+53616,
+21372,
+19089,
+15975,
+59799,
+3588,
+11286,
+30235,
+61708,
+46346,
+58151,
+8405,
+39147,
+62754,
+22734,
+62664,
+43061,
+21002,
+48039,
+428,
+45220,
+41831,
+54623,
+32214,
+25162,
+34159,
+33370,
+54171,
+58053,
+36680,
+6249,
+49526,
+30690,
+45175,
+65435,
+38285,
+29702,
+21273,
+37680,
+41331,
+751,
+16821,
+43343,
+45097,
+21417,
+5671,
+20256,
+54606,
+24387,
+50578,
+17198,
+11383,
+40858,
+39823,
+42395,
+12734,
+60716,
+9614,
+62832,
+8896,
+49023,
+9927,
+44051,
+43551,
+7370,
+26559,
+56539,
+21599,
+43307,
+30262,
+41918,
+46100,
+50806,
+32200,
+23040,
+46586,
+29318,
+56181,
+39071,
+19594,
+52153,
+42757,
+37006,
+41641,
+48237,
+59287,
+49471,
+34848,
+26241,
+21686,
+5646,
+64714,
+40264,
+19562,
+37947,
+19974,
+26078,
+38802,
+9569,
+47000,
+21070,
+65115,
+49037,
+28843,
+3057,
+46657,
+28123,
+33662,
+63340,
+48915,
+14188,
+10532,
+56796,
+47894,
+44018,
+35803,
+37827,
+16212,
+49082,
+9397,
+9213,
+59798,
+16094,
+19090,
+25418,
+28973,
+46427,
+44456,
+25765,
+48336,
+27919,
+53785,
+977,
+5965,
+22092,
+31003,
+41730,
+12783,
+20266,
+22986,
+55183,
+8876,
+9008,
+39604,
+5053,
+47267,
+53133,
+15092,
+23640,
+31285,
+16225,
+42657,
+14398,
+28284,
+7909,
+53052,
+45059,
+21022,
+13836,
+9289,
+4396,
+28693,
+30015,
+53383,
+34239,
+54270,
+5603,
+58720,
+48298,
+24527,
+30314,
+49698,
+20157,
+20220,
+56408,
+64899,
+20372,
+36033,
+37822,
+27515,
+11817,
+50981,
+41480,
+7038,
+48779,
+49890,
+61521,
+45983,
+60021,
+62888,
+16410,
+60638,
+9675,
+16751,
+24104,
+36115,
+44991,
+11842,
+26674,
+11052,
+56727,
+38792,
+23308,
+41040,
+45459,
+51025,
+25112,
+6199,
+30370,
+8188,
+41945,
+12430,
+52574,
+26955,
+20037,
+21041,
+10874,
+57089,
+32882,
+9159,
+21415,
+45099,
+45300,
+49225,
+33105,
+19031,
+9481,
+60057,
+56220,
+32467,
+60849,
+47738,
+59952,
+58766,
+27755,
+60988,
+2004,
+11376,
+52623,
+13014,
+38160,
+53968,
+64082,
+10413,
+41882,
+55040,
+11184,
+56122,
+21060,
+3147,
+56815,
+33149,
+50163,
+45885,
+10383,
+12630,
+19453,
+35222,
+49611,
+45381,
+18233,
+62429,
+63956,
+16883,
+51211,
+52394,
+10987,
+14751,
+33698,
+46627,
+17847,
+27559,
+57246,
+6697,
+37016,
+38990,
+22282,
+59294,
+24580,
+40740,
+28797,
+46911,
+5814,
+4712,
+3241,
+10250,
+22665,
+31185,
+17374,
+28660,
+19698,
+21789,
+18838,
+41423,
+3749,
+60697,
+52306,
+37634,
+11926,
+32503,
+1116,
+51750,
+21132,
+24677,
+25716,
+16405,
+35406,
+386,
+4404,
+33744,
+46142,
+13447,
+26439,
+48368,
+43546,
+22553,
+1358,
+48563,
+20817,
+38052,
+37045,
+8512,
+63036,
+40031,
+3565,
+11855,
+60060,
+8989,
+45804,
+18026,
+43187,
+13065,
+47175,
+27842,
+16826,
+39338,
+19506,
+56692,
+63100,
+54104,
+5407,
+28322,
+60017,
+36421,
+63798,
+33222,
+20621,
+5089,
+57980,
+60464,
+37615,
+8728,
+4716,
+36236,
+31376,
+32169,
+52343,
+50685,
+33416,
+22133,
+22276,
+51914,
+16277,
+12800,
+50266,
+24366,
+12365,
+30427,
+43683,
+8938,
+22311,
+6372,
+47295,
+56405,
+446,
+63632,
+60547,
+37492,
+19900,
+44093,
+49208,
+48644,
+64215,
+60428,
+59264,
+6678,
+47650,
+972,
+34285,
+64156,
+63021,
+20509,
+30079,
+33190,
+8610,
+28770,
+61964,
+51649,
+10879,
+64773,
+40457,
+23520,
+38533,
+32994,
+29225,
+21822,
+39516,
+472,
+33393,
+31486,
+58276,
+54227,
+28258,
+24591,
+17827,
+13812,
+64442,
+23544,
+26856,
+34984,
+51035,
+65190,
+14207,
+4729,
+29555,
+39313,
+27175,
+18833,
+21189,
+64805,
+63207,
+64328,
+27290,
+14263,
+35733,
+53582,
+30741,
+3235,
+30724,
+38871,
+7367,
+23848,
+30196,
+15369,
+41630,
+39179,
+41941,
+1562,
+33671,
+16757,
+49625,
+4017,
+5563,
+21403,
+32448,
+47282,
+6503,
+13715,
+23304,
+17740,
+47448,
+58114,
+48699,
+56889,
+50554,
+13286,
+15400,
+3367,
+26640,
+9381,
+12917,
+34119,
+19810,
+15172,
+55163,
+13907,
+6075,
+1565,
+32351,
+42722,
+12770,
+27938,
+54994,
+27908,
+36603,
+64752,
+53417,
+30409,
+58605,
+17371,
+7399,
+43158,
+42598,
+21,
+62559,
+19814,
+44033,
+24673,
+53790,
+37155,
+36754,
+39555,
+61053,
+16462,
+34746,
+45332,
+46163,
+62649,
+3111,
+55619,
+39725,
+45997,
+43746,
+46701,
+32472,
+34821,
+41570,
+3504,
+11089,
+5751,
+46499,
+30265,
+14719,
+2285,
+8745,
+62713,
+33524,
+33181,
+23775,
+18527,
+46752,
+6808,
+43718,
+11845,
+20908,
+19149,
+33276,
+24231,
+42606,
+25144,
+4179,
+36893,
+42440,
+16774,
+3607,
+9174,
+36416,
+12530,
+20126,
+44044,
+31565,
+21774,
+22568,
+26246,
+32973,
+35560,
+33808,
+36155,
+10603,
+25421,
+34770,
+56227,
+52746,
+53686,
+37205,
+44832,
+54899,
+55717,
+22414,
+36031,
+20374,
+22832,
+8962,
+14932,
+3196,
+21833,
+11236,
+25138,
+6495,
+20673,
+17173,
+29419,
+33890,
+47030,
+60704,
+55976,
+61747,
+34577,
+6140,
+21185,
+53285,
+19607,
+13454,
+30507,
+20600,
+17921,
+44186,
+25255,
+57486,
+1442,
+36471,
+3726,
+36260,
+15257,
+2445,
+42308,
+62804,
+48626,
+57155,
+3254,
+20397,
+42299,
+54186,
+43752,
+1418,
+54823,
+22801,
+34517,
+35963,
+35851,
+20118,
+2291,
+16614,
+72,
+26934,
+10848,
+58289,
+41953,
+1271,
+16952,
+40101,
+4657,
+606,
+62083,
+2810,
+55233,
+63159,
+32866,
+26615,
+57011,
+7704,
+55229,
+32601,
+55304,
+50723,
+28489,
+13764,
+64362,
+43376,
+140,
+2325,
+44547,
+49192,
+39486,
+37303,
+57121,
+6948,
+16779,
+208,
+16934,
+7230,
+12164,
+52005,
+60123,
+6769,
+37784,
+23066,
+29532,
+41130,
+8704,
+64356,
+59262,
+60430,
+1092,
+18289,
+2520,
+7280,
+57347,
+23594,
+63977,
+28881,
+43979,
+54262,
+23236,
+11353,
+65082,
+45184,
+27614,
+65201,
+40497,
+53231,
+28506,
+11712,
+65303,
+25597,
+8140,
+3366,
+15631,
+13287,
+9436,
+23981,
+32611,
+40412,
+52284,
+14595,
+64860,
+35581,
+42384,
+4165,
+56870,
+54595,
+60728,
+3804,
+14693,
+18622,
+37779,
+40338,
+2953,
+44528,
+11528,
+40617,
+21759,
+32387,
+49692,
+19653,
+30462,
+6468,
+41629,
+15654,
+30197,
+21341,
+9413,
+44479,
+16316,
+56280,
+31318,
+25411,
+56803,
+47287,
+34373,
+40209,
+38210,
+35801,
+44020,
+59309,
+25618,
+111,
+19311,
+48492,
+53568,
+30486,
+25107,
+32130,
+13057,
+3981,
+32572,
+16930,
+20648,
+37808,
+53056,
+51081,
+22226,
+26296,
+56130,
+53505,
+53641,
+50370,
+22286,
+32621,
+48986,
+60299,
+53662,
+17525,
+23937,
+57864,
+39424,
+16704,
+57978,
+5091,
+21482,
+22951,
+12362,
+40024,
+40155,
+24823,
+4798,
+62438,
+19019,
+3078,
+5450,
+38799,
+36271,
+8213,
+59546,
+59597,
+38756,
+43530,
+64993,
+23295,
+724,
+4340,
+43330,
+47532,
+61239,
+21140,
+65507,
+22600,
+35546,
+58910,
+29189,
+40466,
+47963,
+16306,
+38390,
+61639,
+39673,
+35657,
+45091,
+21228,
+6413,
+11823,
+29006,
+39142,
+37085,
+26952,
+52736,
+49804,
+41172,
+54313,
+47307,
+5250,
+60321,
+48849,
+6874,
+27150,
+21870,
+57777,
+24192,
+62290,
+2444,
+15494,
+36261,
+44499,
+41198,
+26910,
+39519,
+11465,
+17074,
+57758,
+57289,
+63448,
+8507,
+35350,
+20950,
+30694,
+20024,
+18731,
+14419,
+40873,
+10120,
+53455,
+30407,
+53419,
+13831,
+4668,
+8816,
+30744,
+24713,
+46490,
+12158,
+64999,
+8544,
+59502,
+8614,
+30377,
+46210,
+19578,
+3673,
+38442,
+51390,
+30277,
+52999,
+16661,
+21827,
+24732,
+64335,
+48458,
+42110,
+32887,
+41133,
+52733,
+52577,
+51671,
+41299,
+41375,
+2841,
+19164,
+16699,
+43848,
+34640,
+65468,
+56864,
+22484,
+51221,
+26930,
+43247,
+64109,
+13733,
+26881,
+17230,
+13507,
+42798,
+31778,
+13678,
+22955,
+61544,
+3268,
+53850,
+64164,
+21162,
+65442,
+62052,
+39184,
+15000,
+55162,
+15624,
+19811,
+51423,
+53794,
+11895,
+50206,
+43252,
+21232,
+2390,
+52765,
+37601,
+61050,
+45275,
+49009,
+26629,
+31854,
+56893,
+6651,
+53804,
+47679,
+43811,
+26252,
+13726,
+35985,
+32245,
+32550,
+29229,
+2631,
+32894,
+39375,
+44342,
+1476,
+23518,
+40459,
+37245,
+18890,
+33812,
+35044,
+28516,
+29436,
+27463,
+32056,
+1659,
+52628,
+45752,
+47514,
+12136,
+42130,
+65524,
+38231,
+2622,
+1725,
+24416,
+52225,
+57877,
+22574,
+62992,
+24294,
+24894,
+55845,
+2537,
+7412,
+56485,
+5743,
+44008,
+62856,
+5063,
+64205,
+45547,
+4578,
+230,
+37167,
+47625,
+42680,
+31310,
+37637,
+8305,
+21439,
+48521,
+23639,
+15950,
+53134,
+62119,
+63083,
+15016,
+28809,
+63543,
+35877,
+35736,
+60979,
+41607,
+48037,
+21004,
+29963,
+26127,
+21595,
+526,
+18033,
+60185,
+1287,
+7379,
+29244,
+187,
+1744,
+8564,
+25772,
+32725,
+64767,
+6478,
+17460,
+56256,
+64175,
+26341,
+38437,
+26536,
+29454,
+30579,
+17774,
+62581,
+3483,
+26355,
+16246,
+61718,
+51205,
+61221,
+55772,
+6599,
+58319,
+54877,
+33080,
+7446,
+46265,
+841,
+41020,
+48170,
+22097,
+34804,
+28063,
+11661,
+47628,
+64495,
+27072,
+41437,
+10675,
+29694,
+57787,
+62654,
+4026,
+39857,
+48903,
+53221,
+62941,
+63063,
+52420,
+64014,
+28808,
+15088,
+63084,
+37928,
+60674,
+9504,
+39599,
+53412,
+40840,
+41227,
+57388,
+51985,
+52862,
+47755,
+41315,
+44284,
+55161,
+15174,
+39185,
+19537,
+47703,
+20335,
+13256,
+51607,
+42549,
+10356,
+40771,
+11293,
+65126,
+63460,
+46060,
+11331,
+20503,
+8647,
+49904,
+52252,
+42010,
+49144,
+19028,
+34755,
+29200,
+44816,
+54806,
+4136,
+44326,
+18284,
+27395,
+38451,
+64333,
+24734,
+53040,
+14429,
+3648,
+62826,
+19517,
+9527,
+51458,
+41149,
+49489,
+48340,
+61037,
+32063,
+46049,
+6438,
+46454,
+18063,
+60027,
+14348,
+52078,
+36938,
+32797,
+54430,
+10735,
+46596,
+56704,
+18040,
+4382,
+34975,
+33341,
+43140,
+49382,
+42198,
+53022,
+25668,
+3195,
+15524,
+8963,
+36447,
+55528,
+51282,
+25882,
+55425,
+64650,
+26704,
+13825,
+61817,
+33166,
+54437,
+65414,
+28472,
+268,
+35084,
+61266,
+60734,
+6047,
+61041,
+39469,
+26606,
+14238,
+25449,
+50919,
+48025,
+38157,
+3874,
+35484,
+34211,
+59318,
+9118,
+41577,
+37845,
+6687,
+55827,
+13103,
+23132,
+1681,
+7521,
+44218,
+49309,
+10837,
+60259,
+32577,
+26147,
+48467,
+29312,
+51873,
+6064,
+40447,
+29038,
+17187,
+43869,
+55145,
+8093,
+59360,
+56359,
+51855,
+46046,
+16165,
+40164,
+6704,
+34827,
+33573,
+37997,
+61935,
+10939,
+56630,
+38995,
+65096,
+42749,
+46717,
+48930,
+59701,
+922,
+16403,
+25718,
+13193,
+49797,
+32122,
+33201,
+46338,
+9044,
+30056,
+57719,
+17297,
+41188,
+34581,
+55094,
+44707,
+59500,
+8546,
+24032,
+52904,
+32287,
+24620,
+38185,
+45765,
+17891,
+327,
+35367,
+23914,
+57462,
+45010,
+28242,
+11485,
+40324,
+46154,
+1269,
+41955,
+8725,
+11755,
+6674,
+56191,
+62852,
+10232,
+50084,
+37049,
+3491,
+9779,
+53027,
+45671,
+24718,
+40897,
+16745,
+61190,
+27009,
+7336,
+41358,
+20935,
+54347,
+19658,
+40572,
+51674,
+8686,
+31619,
+48362,
+36120,
+4116,
+7435,
+26556,
+8083,
+6138,
+34579,
+41190,
+61982,
+19013,
+9035,
+22057,
+4425,
+23204,
+6124,
+13087,
+9460,
+35239,
+12411,
+9595,
+7657,
+24725,
+52781,
+61335,
+46802,
+58370,
+30099,
+30860,
+3076,
+19021,
+17362,
+51761,
+58411,
+47390,
+373,
+7536,
+54834,
+644,
+41965,
+3407,
+34481,
+33697,
+15830,
+10988,
+4128,
+26365,
+16732,
+37444,
+13903,
+36809,
+29579,
+13279,
+47540,
+40785,
+22566,
+21776,
+2228,
+44692,
+60391,
+40597,
+61449,
+19436,
+49512,
+33338,
+10225,
+56713,
+30114,
+8843,
+2439,
+8408,
+27349,
+35760,
+2640,
+2284,
+15575,
+30266,
+17109,
+18513,
+25088,
+36501,
+37136,
+48293,
+33733,
+2241,
+34348,
+57307,
+61317,
+39783,
+8147,
+13554,
+23223,
+33348,
+24782,
+38221,
+51051,
+16547,
+60235,
+58763,
+57768,
+18621,
+15384,
+3805,
+34457,
+13442,
+10351,
+21279,
+58741,
+3396,
+19583,
+65113,
+21072,
+21743,
+60381,
+63720,
+27681,
+8479,
+38589,
+17974,
+62067,
+53447,
+41869,
+40170,
+56844,
+13505,
+17232,
+38358,
+2328,
+1211,
+40502,
+2543,
+41245,
+21129,
+37974,
+31709,
+32042,
+64996,
+37260,
+29768,
+22910,
+46570,
+27994,
+52248,
+56103,
+2787,
+46568,
+22912,
+63962,
+17447,
+20979,
+28593,
+54415,
+54459,
+25649,
+12324,
+58083,
+35025,
+40078,
+20871,
+27912,
+13377,
+34925,
+19533,
+41922,
+19903,
+5694,
+1474,
+44344,
+34758,
+30687,
+39298,
+44634,
+30142,
+10435,
+55823,
+40087,
+50630,
+17513,
+45597,
+5260,
+65102,
+43642,
+453,
+20355,
+36707,
+19806,
+61091,
+6739,
+47773,
+54938,
+13216,
+58482,
+20004,
+28758,
+29954,
+23155,
+21491,
+33974,
+64859,
+15393,
+52285,
+9220,
+52894,
+60722,
+20272,
+38687,
+36376,
+21323,
+18678,
+24705,
+46452,
+6440,
+46755,
+53698,
+22893,
+54056,
+4608,
+63334,
+33960,
+48666,
+12110,
+35532,
+43396,
+12099,
+19895,
+62748,
+18971,
+58757,
+55706,
+11915,
+32764,
+37451,
+28673,
+47187,
+40479,
+36579,
+34029,
+53719,
+49621,
+24058,
+23087,
+43241,
+4328,
+55849,
+31183,
+22667,
+23538,
+64235,
+9421,
+35677,
+41605,
+60981,
+22403,
+28133,
+38281,
+44671,
+33616,
+26378,
+63702,
+6326,
+62358,
+34543,
+12145,
+36212,
+22205,
+18007,
+62077,
+1782,
+23709,
+7887,
+17025,
+64287,
+24925,
+9022,
+38424,
+29760,
+36109,
+48102,
+23319,
+14033,
+54858,
+20077,
+49645,
+40426,
+45903,
+38950,
+31463,
+12184,
+23064,
+37786,
+5969,
+30552,
+49860,
+348,
+42816,
+60366,
+27344,
+11668,
+25033,
+62996,
+44561,
+52265,
+29725,
+28316,
+28327,
+26877,
+47375,
+27315,
+50838,
+48991,
+42809,
+56342,
+47639,
+62722,
+20426,
+36419,
+60019,
+45985,
+41728,
+31005,
+10301,
+43317,
+59745,
+37517,
+6445,
+12293,
+18323,
+34311,
+61292,
+41062,
+11604,
+60190,
+38850,
+38472,
+40487,
+417,
+41236,
+12480,
+50200,
+43312,
+1791,
+9400,
+2671,
+28281,
+64961,
+20175,
+4214,
+54001,
+39755,
+16982,
+21947,
+13023,
+58780,
+22419,
+62419,
+43419,
+62237,
+19529,
+25246,
+64915,
+38099,
+18876,
+51446,
+54733,
+3647,
+14966,
+53041,
+16717,
+9672,
+27705,
+20578,
+61761,
+6112,
+32539,
+40872,
+15240,
+18732,
+19547,
+61250,
+36317,
+12981,
+59162,
+23269,
+23498,
+64732,
+38720,
+12668,
+1765,
+41676,
+61547,
+30034,
+9783,
+26962,
+11318,
+64959,
+28283,
+15945,
+42658,
+39192,
+9233,
+5104,
+54536,
+56762,
+51966,
+10853,
+14124,
+37574,
+55898,
+46818,
+31404,
+36819,
+48901,
+39859,
+24422,
+44814,
+29202,
+21911,
+62574,
+62551,
+4436,
+22984,
+20268,
+42413,
+2002,
+60990,
+14057,
+59995,
+58950,
+36929,
+9891,
+36049,
+55672,
+52509,
+6033,
+56925,
+36769,
+62246,
+52331,
+51974,
+9487,
+63003,
+54309,
+4023,
+19952,
+48744,
+52077,
+14950,
+60028,
+8794,
+32885,
+42112,
+25067,
+58806,
+28699,
+24373,
+46391,
+42541,
+28835,
+9367,
+8403,
+58153,
+3818,
+50488,
+4317,
+31616,
+55891,
+18191,
+48653,
+49704,
+13144,
+8716,
+18902,
+34425,
+20186,
+62759,
+119,
+61715,
+43052,
+50343,
+38608,
+6268,
+35106,
+24439,
+40849,
+64131,
+34612,
+5153,
+34626,
+16469,
+54647,
+36805,
+35882,
+43033,
+57861,
+26916,
+65482,
+44906,
+43291,
+41960,
+52370,
+24158,
+10757,
+12459,
+34784,
+21890,
+13012,
+52625,
+9945,
+32585,
+28550,
+48437,
+1193,
+21682,
+4970,
+18142,
+32858,
+56848,
+9649,
+13740,
+28570,
+12640,
+19308,
+43500,
+19972,
+37949,
+21541,
+8346,
+1006,
+45558,
+47794,
+35732,
+15664,
+27291,
+52141,
+53096,
+17107,
+30268,
+41468,
+10008,
+50609,
+39656,
+30812,
+25388,
+64792,
+9074,
+48372,
+24144,
+16390,
+33983,
+27526,
+35911,
+27990,
+63885,
+38307,
+22243,
+25448,
+14909,
+26607,
+29836,
+18959,
+9517,
+47243,
+63847,
+24136,
+45793,
+34650,
+14118,
+28390,
+34989,
+47675,
+38855,
+61883,
+63726,
+33204,
+12688,
+47838,
+23734,
+49200,
+60784,
+35514,
+12970,
+55448,
+57112,
+58684,
+16940,
+21572,
+4728,
+15675,
+65191,
+55470,
+1518,
+6257,
+46677,
+58070,
+34490,
+63940,
+3245,
+63095,
+26393,
+28454,
+19988,
+60961,
+52316,
+34341,
+31697,
+10531,
+15987,
+48916,
+22032,
+35335,
+4736,
+44979,
+24090,
+7191,
+8102,
+24451,
+40904,
+7512,
+59772,
+34782,
+12461,
+63040,
+58790,
+4227,
+10929,
+41152,
+48334,
+25767,
+34471,
+29509,
+53752,
+56634,
+41015,
+31512,
+523,
+10632,
+28982,
+19244,
+65412,
+54439,
+32111,
+41180,
+55687,
+33924,
+21875,
+30969,
+27094,
+50319,
+35261,
+46601,
+39742,
+18846,
+31888,
+64383,
+5344,
+1080,
+6003,
+2457,
+27117,
+20855,
+2694,
+23784,
+38199,
+4930,
+42824,
+31370,
+46170,
+1067,
+56272,
+37573,
+14389,
+10854,
+52334,
+29652,
+9993,
+28389,
+14228,
+34651,
+2976,
+45725,
+50367,
+11744,
+26160,
+21197,
+25467,
+8022,
+22767,
+53198,
+23272,
+24872,
+60263,
+57335,
+63692,
+58110,
+31481,
+58941,
+45385,
+27987,
+31025,
+30353,
+32722,
+23047,
+46660,
+51105,
+10805,
+60583,
+56461,
+17167,
+21993,
+38145,
+43150,
+4044,
+11472,
+1629,
+56009,
+50576,
+24389,
+34242,
+7957,
+36426,
+53509,
+52302,
+53321,
+26865,
+61452,
+46522,
+56504,
+11023,
+44808,
+16439,
+60248,
+20314,
+1785,
+27779,
+64106,
+58526,
+59994,
+14369,
+60991,
+63476,
+41582,
+5503,
+27447,
+22906,
+16200,
+19224,
+54793,
+32071,
+37733,
+51739,
+26212,
+36411,
+48441,
+10628,
+56542,
+3025,
+8296,
+33121,
+17150,
+1625,
+54857,
+14515,
+23320,
+4073,
+27107,
+7353,
+49078,
+30659,
+11144,
+35154,
+715,
+9978,
+25739,
+36800,
+2772,
+55659,
+38519,
+55522,
+3895,
+55731,
+2053,
+22304,
+26777,
+23949,
+36595,
+10540,
+38219,
+24784,
+9700,
+8601,
+48769,
+25685,
+6520,
+48302,
+63070,
+13422,
+3282,
+29306,
+53648,
+28486,
+62048,
+25235,
+1541,
+49175,
+37350,
+21246,
+1935,
+26033,
+54736,
+44463,
+43796,
+29565,
+37287,
+19280,
+52003,
+12166,
+44143,
+47344,
+16886,
+41259,
+43893,
+7223,
+8429,
+27001,
+3814,
+31232,
+35213,
+54604,
+20258,
+33334,
+3703,
+54798,
+17316,
+34043,
+42031,
+31137,
+44657,
+52408,
+29270,
+47212,
+31228,
+54640,
+26460,
+41687,
+35343,
+42191,
+63775,
+7072,
+4986,
+23301,
+32792,
+60188,
+11606,
+46747,
+61743,
+3042,
+17769,
+9306,
+39357,
+2930,
+49014,
+8208,
+52916,
+52280,
+50543,
+22989,
+48414,
+12682,
+17389,
+57409,
+21637,
+10205,
+4618,
+31528,
+44600,
+11192,
+57452,
+59401,
+19144,
+22998,
+21954,
+40562,
+6223,
+27810,
+47883,
+6884,
+6074,
+15622,
+55164,
+27822,
+36808,
+14745,
+37445,
+52935,
+52208,
+64825,
+8389,
+415,
+40489,
+9070,
+28561,
+2486,
+43218,
+65353,
+62673,
+3572,
+49837,
+54659,
+9057,
+52406,
+44659,
+5391,
+59716,
+20349,
+45484,
+34773,
+56437,
+51997,
+19407,
+53601,
+24228,
+30423,
+64937,
+6128,
+3936,
+45246,
+41497,
+12959,
+50332,
+24239,
+24964,
+50740,
+56852,
+16506,
+2804,
+44697,
+59006,
+10509,
+37995,
+33575,
+48332,
+41154,
+19346,
+3402,
+12885,
+30135,
+4283,
+16783,
+35741,
+18570,
+61035,
+48342,
+10563,
+16956,
+61905,
+62975,
+9060,
+9288,
+15939,
+21023,
+46289,
+9845,
+4667,
+15234,
+53420,
+45865,
+6621,
+21111,
+61816,
+14923,
+26705,
+30021,
+52569,
+18019,
+49001,
+31737,
+28111,
+63265,
+61671,
+65148,
+43073,
+64441,
+15682,
+17828,
+24644,
+7641,
+11244,
+30820,
+37506,
+4904,
+9728,
+58923,
+12379,
+53537,
+59235,
+27803,
+51492,
+39326,
+35500,
+33531,
+36997,
+22655,
+8520,
+55624,
+46066,
+41326,
+64420,
+49741,
+44406,
+27565,
+18985,
+37968,
+23277,
+2121,
+48953,
+35696,
+5551,
+48174,
+50775,
+7127,
+20017,
+25305,
+1775,
+37825,
+35805,
+31243,
+39723,
+55621,
+23942,
+64361,
+15451,
+28490,
+57027,
+10368,
+63408,
+2434,
+23632,
+10109,
+31849,
+42848,
+44090,
+41925,
+57383,
+34968,
+46343,
+43739,
+7632,
+55106,
+24425,
+1254,
+53945,
+23901,
+56986,
+28569,
+14276,
+9650,
+65246,
+21922,
+32487,
+11800,
+26880,
+15190,
+64110,
+58874,
+44358,
+51894,
+60806,
+35984,
+15150,
+26253,
+45810,
+11008,
+63383,
+57441,
+17545,
+36669,
+41559,
+32790,
+23303,
+15640,
+6504,
+63654,
+17517,
+45624,
+46287,
+21025,
+4212,
+20177,
+25663,
+22394,
+24299,
+53059,
+41024,
+45686,
+59189,
+8530,
+11363,
+36570,
+37367,
+8365,
+29495,
+45952,
+426,
+48041,
+35727,
+39507,
+23140,
+52805,
+43495,
+53124,
+55944,
+9177,
+50664,
+19492,
+47682,
+22954,
+15184,
+31779,
+35869,
+5981,
+16818,
+2526,
+28996,
+7977,
+17753,
+37273,
+16998,
+16232,
+46906,
+36224,
+50654,
+59474,
+16142,
+40274,
+55812,
+37521,
+36137,
+29215,
+63315,
+41194,
+53619,
+35291,
+8857,
+55868,
+56285,
+46297,
+16754,
+38404,
+38743,
+31330,
+21178,
+51808,
+4732,
+60747,
+19762,
+21032,
+39006,
+49029,
+62735,
+22533,
+57572,
+16484,
+8620,
+24107,
+928,
+43951,
+36457,
+30145,
+33413,
+32338,
+43260,
+49801,
+49261,
+35244,
+18155,
+34152,
+54627,
+23689,
+7783,
+37055,
+56145,
+29165,
+3221,
+9500,
+62903,
+1594,
+19847,
+3227,
+8386,
+31109,
+21221,
+61755,
+53,
+20633,
+64925,
+4506,
+55999,
+48537,
+49307,
+44220,
+54897,
+44834,
+25425,
+26372,
+31071,
+60883,
+37664,
+13091,
+32344,
+21434,
+36440,
+53769,
+54048,
+10766,
+47454,
+26376,
+33618,
+55405,
+30321,
+30526,
+32923,
+7798,
+8774,
+51146,
+54685,
+62953,
+56886,
+32027,
+53590,
+54681,
+13205,
+51181,
+56051,
+5577,
+25485,
+41708,
+4916,
+47491,
+6019,
+23222,
+14704,
+8148,
+50531,
+12676,
+35419,
+32442,
+58189,
+28296,
+3885,
+39448,
+2792,
+35597,
+57605,
+9099,
+51660,
+46131,
+40485,
+38474,
+30062,
+46256,
+51557,
+53372,
+50102,
+5415,
+63614,
+22540,
+26863,
+53323,
+63032,
+23007,
+44394,
+2050,
+38633,
+18666,
+63758,
+16846,
+4533,
+160,
+59025,
+21642,
+58316,
+6237,
+54328,
+3410,
+19176,
+37501,
+42797,
+15187,
+17231,
+14670,
+56845,
+5732,
+34147,
+7626,
+16860,
+46224,
+17047,
+33004,
+1248,
+40614,
+58451,
+43680,
+63424,
+63521,
+22508,
+12812,
+16875,
+22824,
+5020,
+52381,
+62509,
+21392,
+18195,
+28804,
+23886,
+44338,
+40072,
+39613,
+30546,
+17864,
+36183,
+64569,
+23044,
+25775,
+42149,
+5401,
+36676,
+30946,
+46055,
+30386,
+53003,
+46849,
+58366,
+16792,
+51188,
+50588,
+28600,
+47878,
+940,
+30506,
+15505,
+19608,
+38493,
+60616,
+54442,
+60050,
+26438,
+15786,
+46143,
+45542,
+22936,
+10350,
+14690,
+34458,
+27468,
+29211,
+56656,
+18651,
+48704,
+7722,
+23558,
+57365,
+41107,
+45082,
+1869,
+1508,
+31358,
+24156,
+52372,
+36359,
+16643,
+3281,
+13999,
+63071,
+37276,
+44126,
+33296,
+21888,
+34786,
+16576,
+171,
+26292,
+55984,
+5483,
+17413,
+1608,
+40203,
+32331,
+52851,
+10086,
+64388,
+46071,
+28475,
+40224,
+12095,
+30521,
+61658,
+38245,
+51276,
+12442,
+7912,
+55119,
+61392,
+11878,
+44016,
+47896,
+44610,
+44207,
+36744,
+45942,
+49520,
+22781,
+55179,
+24768,
+46400,
+13368,
+34924,
+14634,
+27913,
+26205,
+40217,
+17618,
+18923,
+62106,
+60895,
+34923,
+13379,
+46401,
+61178,
+36890,
+56087,
+22820,
+36398,
+34164,
+5656,
+39541,
+56593,
+1943,
+7856,
+55320,
+62401,
+60040,
+29848,
+48803,
+28095,
+60166,
+7409,
+17451,
+5878,
+10647,
+10692,
+54132,
+18661,
+37089,
+52238,
+4471,
+38822,
+31798,
+21903,
+63291,
+9255,
+34221,
+63261,
+55513,
+12601,
+51637,
+713,
+35156,
+25903,
+27287,
+10590,
+2371,
+55398,
+19207,
+31258,
+47759,
+12012,
+59346,
+24003,
+63706,
+32483,
+41120,
+50418,
+7661,
+47875,
+61701,
+48483,
+197,
+65207,
+64542,
+59670,
+29820,
+40346,
+63709,
+28447,
+52197,
+61067,
+34082,
+21614,
+21310,
+39266,
+2613,
+16473,
+63895,
+37542,
+50885,
+9435,
+15399,
+15632,
+50555,
+23528,
+57998,
+64486,
+54740,
+47539,
+14742,
+29580,
+28827,
+39260,
+4927,
+57040,
+20975,
+51044,
+35813,
+24264,
+8841,
+30116,
+33097,
+23471,
+33018,
+61352,
+63768,
+9750,
+44118,
+59868,
+5447,
+12620,
+51606,
+14995,
+20336,
+25124,
+50143,
+50943,
+56318,
+1103,
+52300,
+53511,
+8732,
+18252,
+49248,
+10555,
+58094,
+6914,
+41304,
+22933,
+2490,
+64433,
+52288,
+12027,
+51464,
+12217,
+33499,
+4839,
+49736,
+27904,
+55728,
+25867,
+24001,
+59348,
+58961,
+53846,
+59074,
+4575,
+51345,
+31197,
+26896,
+30339,
+58481,
+14604,
+54939,
+2951,
+40340,
+5263,
+49216,
+511,
+129,
+11596,
+3290,
+51180,
+13564,
+54682,
+51415,
+35886,
+27027,
+29159,
+44216,
+7523,
+56906,
+60655,
+33367,
+49796,
+14853,
+25719,
+9564,
+42845,
+29074,
+27158,
+63067,
+27182,
+46788,
+61088,
+33462,
+8258,
+63578,
+16971,
+34233,
+32490,
+6968,
+35539,
+37422,
+25585,
+45756,
+47073,
+11335,
+62815,
+41067,
+23576,
+51242,
+41380,
+52111,
+29179,
+10137,
+46332,
+9048,
+19686,
+254,
+32898,
+60516,
+52052,
+3029,
+3094,
+17534,
+26156,
+12927,
+5846,
+11658,
+30670,
+63882,
+26826,
+8715,
+14325,
+49705,
+54551,
+42521,
+47846,
+7881,
+55685,
+41182,
+22915,
+60313,
+1971,
+2560,
+56479,
+51652,
+20879,
+51580,
+36268,
+26081,
+30302,
+45429,
+19740,
+31088,
+3591,
+43370,
+4161,
+41808,
+2473,
+50518,
+1524,
+39667,
+55607,
+58515,
+34193,
+47332,
+55454,
+40960,
+21844,
+32771,
+28088,
+60076,
+23131,
+14895,
+55828,
+55297,
+31883,
+26527,
+30677,
+18905,
+60938,
+20084,
+29345,
+53704,
+32343,
+13587,
+37665,
+7836,
+9459,
+14778,
+6125,
+34524,
+22966,
+11281,
+65021,
+35251,
+28224,
+56768,
+26304,
+39236,
+62615,
+52996,
+31395,
+65156,
+52948,
+61576,
+55724,
+20139,
+64539,
+30173,
+47174,
+15766,
+43188,
+59928,
+58608,
+55085,
+60283,
+7312,
+3980,
+15344,
+32131,
+24764,
+54246,
+21926,
+55600,
+59727,
+3882,
+48255,
+22949,
+21484,
+21988,
+57765,
+59955,
+49051,
+56919,
+60602,
+7001,
+32002,
+12843,
+5027,
+41672,
+43631,
+35715,
+25193,
+59105,
+25906,
+43522,
+9864,
+51175,
+59505,
+32688,
+56470,
+58779,
+14443,
+21948,
+52642,
+5099,
+63906,
+9530,
+43933,
+3872,
+38159,
+15858,
+52624,
+14289,
+21891,
+64595,
+35033,
+6857,
+46607,
+40589,
+64897,
+56410,
+39046,
+40914,
+48660,
+42715,
+29141,
+38819,
+1306,
+32945,
+6647,
+43229,
+43424,
+10336,
+34345,
+40528,
+22473,
+45338,
+18616,
+17275,
+2463,
+48542,
+6638,
+59161,
+14414,
+36318,
+55635,
+1722,
+20807,
+65138,
+495,
+19160,
+5754,
+11423,
+55447,
+14214,
+35515,
+51715,
+47519,
+55433,
+3958,
+53106,
+49652,
+38583,
+12803,
+50331,
+13867,
+41498,
+46974,
+7532,
+65451,
+32384,
+26062,
+54572,
+4777,
+43386,
+16798,
+60521,
+21655,
+28911,
+25101,
+24065,
+4704,
+6102,
+29854,
+3915,
+57053,
+20814,
+31307,
+40454,
+39307,
+48000,
+45921,
+34973,
+4384,
+9088,
+58998,
+5845,
+13151,
+26157,
+28385,
+63250,
+56794,
+10534,
+31995,
+23501,
+65050,
+34118,
+15627,
+9382,
+54160,
+49678,
+52524,
+51249,
+55593,
+63877,
+50408,
+19786,
+38001,
+34363,
+24544,
+4962,
+32512,
+39118,
+32705,
+28313,
+9623,
+37870,
+27847,
+43588,
+52516,
+60439,
+63275,
+6146,
+36645,
+7235,
+53954,
+18759,
+46090,
+30134,
+13850,
+3403,
+31133,
+23377,
+41858,
+40060,
+49411,
+55995,
+36125,
+7477,
+64127,
+23025,
+55192,
+54178,
+43525,
+8061,
+19958,
+65318,
+10523,
+52940,
+59643,
+59021,
+44031,
+19816,
+38886,
+19126,
+29708,
+31586,
+33449,
+18561,
+58007,
+12466,
+46319,
+5594,
+35938,
+55709,
+4616,
+10207,
+7198,
+31891,
+54098,
+5026,
+13038,
+32003,
+9988,
+35489,
+36773,
+55271,
+23413,
+18304,
+9768,
+29868,
+40268,
+44786,
+40356,
+65348,
+63795,
+5911,
+58011,
+7639,
+24646,
+43383,
+20970,
+3123,
+7826,
+30739,
+53584,
+5435,
+44910,
+53842,
+26941,
+45657,
+16874,
+13489,
+22509,
+23447,
+30704,
+43560,
+58163,
+56187,
+24403,
+50330,
+12961,
+38584,
+50265,
+15734,
+16278,
+65071,
+53840,
+44912,
+52824,
+41405,
+25973,
+9309,
+50002,
+11852,
+33386,
+5458,
+9041,
+44004,
+25979,
+20265,
+15960,
+41731,
+25500,
+52886,
+918,
+20254,
+5673,
+55570,
+52958,
+41398,
+63146,
+50374,
+27937,
+15617,
+42723,
+32363,
+16365,
+23343,
+12671,
+11260,
+647,
+23566,
+53088,
+18865,
+64145,
+45467,
+40811,
+50766,
+4203,
+29449,
+33989,
+59046,
+58439,
+25562,
+18542,
+53903,
+40406,
+23647,
+47272,
+7863,
+17877,
+11156,
+54483,
+8812,
+47356,
+32254,
+41863,
+28657,
+60715,
+16042,
+42396,
+51850,
+22043,
+61022,
+23797,
+8326,
+42761,
+56911,
+36299,
+28478,
+47158,
+10339,
+19098,
+47571,
+44715,
+34603,
+62146,
+29135,
+42255,
+20624,
+52742,
+24133,
+4001,
+21208,
+31946,
+32949,
+59219,
+48067,
+62638,
+54977,
+55781,
+24637,
+16352,
+56625,
+6451,
+9925,
+49025,
+58687,
+42812,
+28504,
+53233,
+9403,
+34909,
+27697,
+47837,
+14220,
+33205,
+52131,
+55452,
+47334,
+17388,
+13927,
+48415,
+37384,
+28791,
+19597,
+35418,
+13551,
+50532,
+17783,
+61886,
+11259,
+12765,
+23344,
+1764,
+14408,
+38721,
+54728,
+41568,
+34823,
+2743,
+22189,
+58813,
+38789,
+24876,
+43983,
+8585,
+41349,
+64465,
+63947,
+3548,
+40036,
+52757,
+64620,
+48009,
+22597,
+53836,
+10795,
+18000,
+56316,
+50945,
+4816,
+19307,
+14274,
+28571,
+49062,
+1644,
+26415,
+5799,
+41504,
+50700,
+33251,
+19452,
+15842,
+10384,
+49433,
+22837,
+6099,
+42686,
+11551,
+28872,
+57328,
+51605,
+13258,
+5448,
+3080,
+58561,
+60389,
+44694,
+4463,
+53103,
+9816,
+36518,
+46063,
+47943,
+5328,
+42565,
+38768,
+40778,
+58637,
+28091,
+51636,
+13330,
+55514,
+59633,
+55013,
+52671,
+5895,
+3658,
+17948,
+46146,
+46972,
+41500,
+380,
+52465,
+3962,
+23550,
+23354,
+276,
+54418,
+55464,
+65063,
+32146,
+9966,
+52762,
+24140,
+10651,
+46865,
+48328,
+56150,
+55211,
+31116,
+23434,
+3561,
+31040,
+1215,
+44896,
+23266,
+18245,
+53983,
+313,
+26508,
+46514,
+48177,
+22491,
+32685,
+53225,
+31782,
+39270,
+64258,
+60974,
+590,
+48128,
+57824,
+63354,
+2554,
+2643,
+24912,
+35775,
+29430,
+18695,
+2921,
+52462,
+34143,
+35259,
+50321,
+48877,
+7157,
+35150,
+52073,
+18689,
+1344,
+20125,
+15550,
+36417,
+20428,
+32091,
+38461,
+36617,
+37745,
+65380,
+23107,
+59256,
+34721,
+9512,
+63113,
+60453,
+23338,
+50602,
+39870,
+45404,
+33528,
+55757,
+50727,
+19867,
+11721,
+40977,
+19332,
+62382,
+20593,
+29689,
+41418,
+47791,
+57531,
+61379,
+40802,
+50570,
+51109,
+48821,
+63507,
+21739,
+35974,
+30557,
+10615,
+30331,
+21627,
+53093,
+57627,
+49595,
+20479,
+26219,
+27728,
+50199,
+14457,
+41237,
+51834,
+24514,
+59932,
+6819,
+39525,
+43285,
+61927,
+30091,
+36069,
+8110,
+21914,
+46318,
+12854,
+58008,
+4380,
+18042,
+63039,
+14174,
+34783,
+14292,
+10758,
+59471,
+25092,
+65316,
+19960,
+7431,
+21879,
+61084,
+41970,
+52776,
+46673,
+30228,
+6956,
+43909,
+53050,
+7911,
+13395,
+51277,
+48431,
+60105,
+25318,
+47921,
+28210,
+61073,
+52924,
+55722,
+61578,
+52573,
+15886,
+41946,
+33164,
+61819,
+57304,
+58142,
+53262,
+61496,
+46834,
+29083,
+43971,
+57521,
+22340,
+37268,
+64191,
+45414,
+22041,
+51852,
+9594,
+14775,
+35240,
+16647,
+26837,
+22064,
+1495,
+18269,
+49586,
+50446,
+3419,
+59655,
+47953,
+16375,
+7945,
+36514,
+45325,
+41416,
+29691,
+61629,
+28014,
+29402,
+28685,
+45303,
+34964,
+8788,
+53309,
+46794,
+26762,
+35555,
+26110,
+3663,
+53536,
+13802,
+58924,
+23383,
+36057,
+519,
+19284,
+33179,
+33526,
+45406,
+62533,
+12266,
+8314,
+29936,
+30426,
+15731,
+24367,
+40023,
+15316,
+22952,
+47684,
+29616,
+41540,
+27441,
+61490,
+20783,
+25331,
+27365,
+37358,
+2478,
+10470,
+24248,
+48622,
+29321,
+18988,
+33886,
+36915,
+6053,
+22074,
+3335,
+42769,
+51430,
+42266,
+42041,
+22531,
+62737,
+54182,
+457,
+35059,
+287,
+36638,
+22682,
+1749,
+45802,
+8991,
+58082,
+14640,
+25650,
+27620,
+17851,
+35923,
+32702,
+46027,
+16633,
+50515,
+2597,
+6030,
+32851,
+52980,
+33756,
+29670,
+16335,
+4755,
+25039,
+50130,
+37032,
+52844,
+56135,
+62868,
+959,
+56868,
+4167,
+61780,
+37184,
+35652,
+56518,
+18322,
+14469,
+6446,
+55342,
+31638,
+20164,
+21851,
+34494,
+59920,
+55257,
+28367,
+2369,
+10592,
+41032,
+40523,
+57958,
+17289,
+16842,
+7394,
+53113,
+4042,
+43152,
+25810,
+19542,
+23197,
+17932,
+32870,
+8313,
+12369,
+62534,
+60626,
+31061,
+60219,
+37213,
+5522,
+63853,
+34736,
+24954,
+39704,
+60847,
+32469,
+44351,
+50030,
+45931,
+6543,
+18919,
+40158,
+8851,
+44371,
+48560,
+59419,
+20486,
+45938,
+30121,
+64857,
+33976,
+8488,
+42331,
+59415,
+39199,
+22526,
+42677,
+11664,
+56369,
+60891,
+56748,
+53151,
+18744,
+16269,
+16206,
+7211,
+41157,
+30881,
+3475,
+16159,
+4677,
+33498,
+13234,
+51465,
+38673,
+52010,
+2506,
+44677,
+28762,
+10268,
+569,
+30240,
+3683,
+47695,
+7829,
+6471,
+48131,
+35121,
+46839,
+54111,
+23101,
+6313,
+33330,
+11419,
+2945,
+35617,
+43677,
+34408,
+26847,
+41091,
+24819,
+17621,
+27243,
+57400,
+23063,
+14507,
+31464,
+22450,
+48035,
+41609,
+1313,
+19702,
+19709,
+53744,
+30941,
+61914,
+7112,
+4442,
+23838,
+42074,
+11719,
+19869,
+44142,
+13979,
+52004,
+15436,
+7231,
+23209,
+62864,
+37258,
+64998,
+15228,
+46491,
+28105,
+35521,
+49603,
+31299,
+782,
+64701,
+40579,
+44790,
+6954,
+30230,
+36211,
+14532,
+34544,
+63675,
+42225,
+17727,
+48980,
+37587,
+36764,
+42129,
+15126,
+47515,
+27389,
+36952,
+26540,
+443,
+20223,
+58075,
+7441,
+58625,
+25937,
+50527,
+24698,
+61396,
+64699,
+784,
+49926,
+35129,
+50302,
+51707,
+19673,
+1017,
+27748,
+63573,
+62166,
+35531,
+14574,
+48667,
+43805,
+6669,
+38940,
+2236,
+18458,
+33421,
+352,
+6499,
+19894,
+14571,
+43397,
+64123,
+30520,
+13400,
+40225,
+56996,
+63136,
+3469,
+50863,
+23669,
+54222,
+45768,
+11588,
+59832,
+63872,
+55416,
+50300,
+35131,
+43147,
+28588,
+26854,
+23546,
+43183,
+20885,
+58117,
+17474,
+28496,
+1075,
+25156,
+4784,
+28462,
+8000,
+39949,
+45808,
+26255,
+18135,
+16425,
+40422,
+6157,
+35494,
+1299,
+33929,
+16618,
+60443,
+1854,
+17242,
+31806,
+27061,
+35477,
+44422,
+16542,
+7948,
+22105,
+28364,
+54986,
+45206,
+22428,
+6330,
+44379,
+29407,
+6778,
+9833,
+23746,
+4999,
+45523,
+9406,
+50204,
+11897,
+56510,
+29110,
+51463,
+13236,
+52289,
+56806,
+35412,
+37410,
+55381,
+62195,
+37801,
+37128,
+64779,
+53937,
+29676,
+9371,
+24455,
+59345,
+13318,
+47760,
+1708,
+35358,
+1228,
+54233,
+7069,
+10689,
+33309,
+4237,
+61873,
+53157,
+51736,
+6303,
+10657,
+21283,
+16570,
+45447,
+18518,
+56351,
+63269,
+65220,
+18874,
+38101,
+42013,
+56330,
+21308,
+21616,
+61430,
+58531,
+57885,
+53563,
+35739,
+16785,
+4120,
+54533,
+64448,
+7651,
+53556,
+29799,
+4560,
+27532,
+17044,
+50189,
+35052,
+55526,
+36449,
+35373,
+41177,
+5051,
+39606,
+63559,
+17935,
+45255,
+37712,
+29658,
+29803,
+49875,
+34797,
+52718,
+60103,
+48433,
+42355,
+6823,
+33509,
+51971,
+10857,
+17354,
+61943,
+26960,
+9785,
+8754,
+48044,
+34182,
+7252,
+46129,
+51662,
+41310,
+983,
+55481,
+43814,
+62005,
+7982,
+42034,
+489,
+32502,
+15799,
+37635,
+31312,
+21569,
+29877,
+49440,
+44166,
+24342,
+51883,
+30247,
+32763,
+14565,
+55707,
+35940,
+45252,
+46989,
+54273,
+41070,
+11431,
+60132,
+17598,
+39102,
+3529,
+39973,
+46997,
+65135,
+8663,
+16650,
+56509,
+12031,
+50205,
+15168,
+53795,
+47782,
+28266,
+42251,
+36230,
+51541,
+37052,
+827,
+4196,
+58964,
+36888,
+61180,
+2995,
+49110,
+25117,
+44015,
+13391,
+61393,
+5237,
+50211,
+48013,
+24072,
+8838,
+18478,
+3771,
+60065,
+30775,
+28436,
+41738,
+57213,
+60593,
+59333,
+62548,
+21737,
+63509,
+57358,
+20300,
+56218,
+60059,
+15772,
+3566,
+33385,
+12790,
+50003,
+28333,
+45653,
+17131,
+3463,
+20907,
+15564,
+43719,
+26673,
+15900,
+44992,
+18781,
+50956,
+42645,
+40063,
+55908,
+42905,
+36191,
+52444,
+33195,
+18474,
+44779,
+59498,
+44709,
+20996,
+2189,
+24929,
+29005,
+15277,
+6414,
+65160,
+31898,
+31507,
+50980,
+15917,
+27516,
+22870,
+44296,
+21498,
+3845,
+58773,
+5011,
+38876,
+26469,
+25733,
+38031,
+2043,
+36072,
+55820,
+47373,
+26879,
+13735,
+32488,
+34235,
+30838,
+33763,
+64748,
+45827,
+53361,
+34597,
+35895,
+6298,
+44490,
+26336,
+43013,
+36027,
+51373,
+8904,
+25524,
+10908,
+41850,
+19433,
+26868,
+562,
+58670,
+2855,
+25174,
+38524,
+24154,
+31360,
+28963,
+56589,
+29096,
+51954,
+11490,
+63827,
+30181,
+26568,
+32788,
+41561,
+59803,
+31392,
+30280,
+5323,
+21383,
+6673,
+14819,
+8726,
+37617,
+49571,
+64561,
+63992,
+28822,
+40863,
+49938,
+28383,
+26159,
+14113,
+50368,
+53643,
+17756,
+28431,
+30393,
+32735,
+19158,
+497,
+659,
+58885,
+50383,
+17859,
+42101,
+61978,
+6761,
+23252,
+33794,
+28029,
+60635,
+61406,
+45020,
+40976,
+12508,
+19868,
+12169,
+42075,
+25142,
+42608,
+45778,
+34156,
+65302,
+15405,
+28507,
+19829,
+20799,
+34890,
+36606,
+45007,
+58387,
+41263,
+28,
+42939,
+52890,
+54743,
+27259,
+465,
+52740,
+20626,
+52721,
+38597,
+61339,
+6575,
+52313,
+6464,
+18177,
+62268,
+50357,
+1374,
+4672,
+35904,
+34112,
+62199,
+35902,
+4674,
+39926,
+20568,
+25172,
+2857,
+43914,
+8303,
+37639,
+25340,
+32732,
+32308,
+25032,
+14497,
+27345,
+1264,
+56368,
+12232,
+42678,
+47627,
+15034,
+28064,
+30669,
+13149,
+5847,
+41253,
+26361,
+31790,
+42709,
+31278,
+45,
+9974,
+59776,
+1446,
+64451,
+4350,
+242,
+62841,
+41755,
+58596,
+40107,
+55363,
+4240,
+10401,
+61071,
+28212,
+46994,
+52789,
+25132,
+31467,
+6401,
+35124,
+6481,
+40676,
+57595,
+23456,
+55519,
+11372,
+46879,
+4968,
+21684,
+26243,
+34565,
+30828,
+26797,
+48030,
+60645,
+5468,
+58336,
+1157,
+27110,
+24177,
+44604,
+8226,
+46746,
+13942,
+60189,
+14464,
+41063,
+29560,
+757,
+53520,
+35862,
+39086,
+3289,
+13208,
+130,
+42525,
+64282,
+51793,
+41637,
+46611,
+59831,
+12086,
+45769,
+43878,
+44934,
+58301,
+52648,
+46779,
+62334,
+22372,
+9322,
+42980,
+59910,
+57209,
+22778,
+28533,
+504,
+18559,
+33451,
+27376,
+49681,
+1456,
+2340,
+29788,
+54617,
+54140,
+63899,
+36793,
+34540,
+64817,
+21239,
+44041,
+22615,
+53996,
+47291,
+52973,
+65225,
+28871,
+12624,
+42687,
+57286,
+31991,
+65327,
+48605,
+44891,
+20943,
+48118,
+42921,
+53981,
+18247,
+16238,
+63282,
+61322,
+51090,
+25783,
+3074,
+30862,
+29012,
+49265,
+58449,
+40616,
+15378,
+44529,
+18417,
+64104,
+27781,
+65446,
+26944,
+2173,
+32046,
+58135,
+48359,
+29101,
+60694,
+59948,
+56003,
+44845,
+62719,
+5887,
+5711,
+45356,
+39255,
+991,
+42349,
+5685,
+23249,
+60529,
+7789,
+7009,
+5545,
+27830,
+36861,
+60743,
+43469,
+31289,
+35551,
+31916,
+28748,
+63826,
+11767,
+51955,
+54772,
+60174,
+40323,
+14825,
+28243,
+26168,
+42643,
+50958,
+60930,
+46742,
+57549,
+27358,
+24722,
+42966,
+30975,
+1628,
+14082,
+4045,
+44756,
+65497,
+26888,
+4978,
+17073,
+15251,
+39520,
+29341,
+28423,
+28557,
+16430,
+60281,
+55087,
+16829,
+29296,
+29310,
+48469,
+28102,
+48260,
+63076,
+43346,
+6726,
+31011,
+42573,
+41861,
+32256,
+19156,
+32737,
+48348,
+21670,
+33645,
+52692,
+60512,
+43401,
+32694,
+23630,
+2436,
+49779,
+60131,
+11908,
+41071,
+35822,
+2919,
+18697,
+53337,
+42437,
+55446,
+12972,
+5755,
+59902,
+2944,
+12196,
+33331,
+16865,
+31052,
+26743,
+42231,
+47799,
+26974,
+39537,
+26726,
+20243,
+833,
+39999,
+54844,
+52194,
+46877,
+11374,
+2006,
+3495,
+44111,
+4519,
+10919,
+53973,
+1639,
+33289,
+35817,
+25253,
+44188,
+44819,
+33505,
+23150,
+40083,
+18308,
+9952,
+6263,
+40857,
+16046,
+17199,
+37620,
+10441,
+20832,
+56154,
+52622,
+15860,
+2005,
+11403,
+46878,
+11624,
+55520,
+38521,
+1983,
+21175,
+32166,
+49467,
+7485,
+36569,
+13698,
+8531,
+64839,
+10344,
+52665,
+22013,
+55675,
+2799,
+53203,
+65081,
+15413,
+23237,
+50052,
+21786,
+58463,
+60553,
+40378,
+39157,
+1837,
+42896,
+43674,
+8026,
+24658,
+10709,
+46036,
+58907,
+40298,
+62814,
+13171,
+47074,
+62368,
+20502,
+14986,
+46061,
+36520,
+17038,
+8459,
+8778,
+63500,
+18896,
+58522,
+32919,
+61861,
+43728,
+64958,
+14401,
+26963,
+41867,
+53449,
+19770,
+47648,
+6680,
+7260,
+49851,
+37280,
+42453,
+22547,
+21152,
+64308,
+53530,
+58469,
+60150,
+55110,
+30582,
+42133,
+30483,
+10013,
+61644,
+56967,
+65125,
+14990,
+40772,
+52483,
+43612,
+35140,
+48218,
+30234,
+16091,
+3589,
+31090,
+29478,
+65020,
+13083,
+22967,
+46891,
+11256,
+2714,
+21557,
+50122,
+42302,
+19371,
+62659,
+39592,
+27058,
+41146,
+31297,
+49605,
+59935,
+18547,
+53627,
+24468,
+41963,
+646,
+12764,
+12672,
+61887,
+2713,
+11278,
+46892,
+26980,
+19982,
+36933,
+21768,
+20232,
+30575,
+47364,
+48959,
+65368,
+30819,
+13808,
+7642,
+30390,
+40922,
+20250,
+29331,
+33375,
+25137,
+15521,
+21834,
+59394,
+8804,
+37909,
+957,
+62870,
+2533,
+51184,
+61515,
+11133,
+24364,
+50268,
+18231,
+45383,
+58943,
+11174,
+34004,
+26237,
+20905,
+3465,
+34320,
+24084,
+30077,
+20511,
+27405,
+59299,
+52025,
+62128,
+31058,
+40734,
+2942,
+59904,
+23821,
+56264,
+56427,
+46069,
+64390,
+32312,
+27410,
+2839,
+41377,
+54782,
+57451,
+13919,
+44601,
+50737,
+45880,
+6426,
+24933,
+23160,
+56121,
+15851,
+55041,
+30893,
+33932,
+2421,
+7334,
+27011,
+41385,
+37536,
+34003,
+11220,
+58944,
+8899,
+9375,
+24836,
+10028,
+3386,
+36146,
+36654,
+21560,
+59015,
+17237,
+41012,
+2660,
+60563,
+370,
+20669,
+54482,
+12742,
+17878,
+39549,
+36826,
+28026,
+46436,
+55680,
+4250,
+36431,
+55207,
+19480,
+35153,
+14026,
+30660,
+23075,
+31642,
+55377,
+859,
+31474,
+153,
+55750,
+56785,
+24363,
+11226,
+61516,
+47067,
+35642,
+16371,
+10701,
+64895,
+40591,
+33714,
+2009,
+36943,
+50894,
+11079,
+65260,
+52953,
+10074,
+8607,
+44106,
+33479,
+63880,
+30672,
+61957,
+60777,
+36591,
+48346,
+32739,
+50591,
+54803,
+44191,
+56855,
+36012,
+6889,
+32370,
+50617,
+124,
+51382,
+33427,
+4920,
+52087,
+5488,
+63162,
+46683,
+8184,
+5750,
+15579,
+3505,
+2861,
+58508,
+9093,
+58353,
+57217,
+3294,
+2348,
+65259,
+11121,
+50895,
+39889,
+55981,
+31943,
+47256,
+47207,
+60997,
+60008,
+41341,
+10462,
+42663,
+44550,
+1297,
+35496,
+1238,
+24703,
+18680,
+56999,
+35038,
+36659,
+27233,
+44614,
+44867,
+54380,
+52796,
+56726,
+15898,
+26675,
+19412,
+4069,
+30805,
+45568,
+31926,
+3934,
+6130,
+54902,
+18607,
+54722,
+24891,
+3479,
+47327,
+24684,
+58298,
+47619,
+42545,
+8031,
+24814,
+32718,
+1325,
+8848,
+18072,
+22335,
+1694,
+7270,
+44807,
+14067,
+56505,
+38165,
+25293,
+26919,
+25595,
+65305,
+28372,
+10192,
+45169,
+43126,
+64067,
+62192,
+19423,
+63382,
+13723,
+45811,
+39795,
+61416,
+53560,
+1090,
+60432,
+51144,
+8776,
+8461,
+47082,
+10751,
+34336,
+20433,
+26092,
+33469,
+17845,
+46629,
+45697,
+4127,
+14750,
+15831,
+52395,
+23017,
+22679,
+38124,
+19938,
+60279,
+16432,
+30921,
+64349,
+27828,
+5547,
+9841,
+26044,
+33966,
+19550,
+23811,
+8800,
+64354,
+8706,
+3215,
+39474,
+52585,
+60761,
+26106,
+60796,
+27474,
+57602,
+604,
+4659,
+24936,
+59609,
+58406,
+24883,
+39467,
+61043,
+63598,
+18093,
+55936,
+868,
+64633,
+63651,
+28580,
+26834,
+8666,
+50150,
+4810,
+56629,
+14864,
+61936,
+2313,
+5807,
+30524,
+30323,
+51570,
+45280,
+49487,
+41151,
+14170,
+4228,
+34079,
+3449,
+40330,
+18724,
+65531,
+26216,
+35748,
+53972,
+11398,
+4520,
+22470,
+6394,
+23705,
+49462,
+33324,
+57471,
+31384,
+51919,
+41849,
+11782,
+25525,
+33093,
+7350,
+1160,
+45160,
+19399,
+7004,
+7654,
+38039,
+34308,
+51584,
+39078,
+37674,
+39398,
+22700,
+62703,
+49067,
+51232,
+28733,
+29476,
+31092,
+9193,
+17900,
+22502,
+7678,
+3210,
+20105,
+64772,
+15699,
+51650,
+56481,
+55275,
+57088,
+15881,
+21042,
+55223,
+42146,
+6409,
+61479,
+59561,
+46365,
+41321,
+28519,
+26549,
+35233,
+20110,
+7066,
+5389,
+44661,
+17353,
+11946,
+51972,
+52333,
+14123,
+14390,
+51967,
+2585,
+45441,
+58288,
+15472,
+26935,
+18339,
+32137,
+61796,
+32016,
+23242,
+28564,
+60658,
+25407,
+60258,
+14889,
+49310,
+22882,
+35781,
+7417,
+64575,
+45079,
+39534,
+41208,
+57820,
+55507,
+62153,
+61741,
+46749,
+36762,
+37589,
+27030,
+45143,
+20408,
+49283,
+20848,
+46929,
+54496,
+40647,
+60664,
+59821,
+48933,
+26815,
+52871,
+23970,
+26592,
+60582,
+14090,
+51106,
+61891,
+39503,
+51551,
+21075,
+47543,
+1050,
+52256,
+17999,
+12646,
+53837,
+38279,
+28135,
+28253,
+33303,
+3183,
+49092,
+48811,
+28418,
+59752,
+62626,
+40697,
+39710,
+39276,
+22330,
+34705,
+5430,
+40221,
+36302,
+24013,
+59886,
+4374,
+5214,
+16690,
+10363,
+32662,
+63985,
+47453,
+13581,
+54049,
+5364,
+57268,
+27641,
+41779,
+35284,
+59470,
+12458,
+14293,
+24159,
+54343,
+8087,
+39811,
+34335,
+10997,
+47083,
+23626,
+42421,
+25283,
+2409,
+46312,
+51489,
+33467,
+26094,
+30636,
+61833,
+51574,
+48222,
+1587,
+46595,
+14945,
+54431,
+21347,
+20720,
+50562,
+61402,
+55281,
+5382,
+27163,
+27025,
+35888,
+45376,
+47226,
+34101,
+43078,
+31503,
+16612,
+2293,
+27170,
+48321,
+64118,
+50055,
+42737,
+55020,
+38260,
+46035,
+11340,
+24659,
+18320,
+56520,
+64484,
+58000,
+50780,
+64894,
+11128,
+16372,
+18934,
+25797,
+37581,
+26966,
+48949,
+25537,
+54131,
+13344,
+10648,
+33308,
+12005,
+7070,
+63777,
+32192,
+16855,
+3613,
+4190,
+34560,
+63911,
+51503,
+51023,
+45461,
+61627,
+29693,
+15029,
+41438,
+43327,
+32529,
+18824,
+60396,
+24571,
+29961,
+21006,
+47122,
+38763,
+64904,
+42206,
+37354,
+7217,
+50493,
+32458,
+21282,
+11998,
+6304,
+26590,
+23972,
+2024,
+46864,
+12577,
+24141,
+33307,
+10691,
+13345,
+5879,
+62785,
+47909,
+58511,
+33138,
+21316,
+51943,
+20704,
+50006,
+9821,
+59406,
+54494,
+46931,
+28981,
+14159,
+524,
+21597,
+56541,
+14041,
+48442,
+20754,
+17567,
+10271,
+27978,
+18590,
+19795,
+22543,
+64099,
+45471,
+21679,
+30330,
+12490,
+30558,
+18050,
+8504,
+57971,
+62220,
+16741,
+9298,
+55058,
+57263,
+28971,
+25420,
+15539,
+36156,
+58817,
+17763,
+50932,
+52322,
+62587,
+61560,
+64071,
+37668,
+41031,
+12282,
+2370,
+13324,
+27288,
+64330,
+3968,
+49323,
+16457,
+34880,
+41045,
+16889,
+30356,
+57317,
+41457,
+58505,
+61968,
+22612,
+20129,
+3006,
+9242,
+19521,
+61439,
+30586,
+18522,
+17023,
+7889,
+31590,
+10409,
+16955,
+13842,
+48343,
+2898,
+52833,
+9757,
+60118,
+32455,
+58093,
+13244,
+49249,
+10278,
+37172,
+53493,
+20332,
+18992,
+58485,
+3691,
+36870,
+63629,
+37598,
+49922,
+37630,
+38218,
+14009,
+36596,
+29547,
+37567,
+29366,
+31994,
+12922,
+56795,
+15986,
+14189,
+31698,
+34807,
+57376,
+1021,
+6606,
+5903,
+52939,
+12867,
+65319,
+21746,
+40076,
+35027,
+22799,
+54825,
+27278,
+3696,
+24553,
+8422,
+27039,
+31880,
+37994,
+13857,
+59007,
+57578,
+29113,
+59629,
+42483,
+21334,
+30160,
+32160,
+40402,
+43090,
+23654,
+49685,
+54354,
+34645,
+45746,
+62945,
+431,
+2580,
+57184,
+34896,
+36549,
+48545,
+32756,
+25646,
+9558,
+47582,
+669,
+45290,
+56238,
+54930,
+8275,
+294,
+25105,
+30488,
+5938,
+26783,
+30697,
+24247,
+12350,
+2479,
+43155,
+17181,
+49087,
+39222,
+35280,
+42662,
+11069,
+41342,
+708,
+30967,
+21877,
+7433,
+4118,
+16787,
+31126,
+6928,
+52476,
+5255,
+54079,
+48709,
+26201,
+60418,
+64552,
+10058,
+30955,
+50695,
+20831,
+11380,
+37621,
+37769,
+43819,
+47371,
+55822,
+14621,
+30143,
+36459,
+57904,
+51790,
+41113,
+20725,
+5386,
+54236,
+34956,
+47765,
+21634,
+61079,
+10294,
+56613,
+37819,
+57624,
+52144,
+4146,
+22758,
+35855,
+41881,
+15854,
+64083,
+40099,
+16954,
+10565,
+31591,
+25876,
+55139,
+56045,
+64737,
+3446,
+61070,
+11638,
+4241,
+39660,
+55131,
+9493,
+24693,
+47430,
+36476,
+21667,
+50660,
+57445,
+82,
+3949,
+19463,
+65272,
+49402,
+49432,
+12629,
+15843,
+45886,
+22437,
+17003,
+41877,
+4591,
+21252,
+50536,
+50364,
+49914,
+51631,
+37716,
+8828,
+63660,
+63407,
+13761,
+57028,
+30189,
+43489,
+32661,
+10770,
+16691,
+7135,
+21464,
+42755,
+52155,
+40770,
+14992,
+42550,
+49580,
+56662,
+21278,
+14689,
+13443,
+22937,
+65518,
+3642,
+42511,
+52664,
+11360,
+64840,
+25519,
+42202,
+19097,
+12722,
+47159,
+34344,
+12992,
+43425,
+742,
+3000,
+2163,
+29105,
+54390,
+46058,
+63462,
+9386,
+52964,
+34422,
+30680,
+42447,
+2127,
+63105,
+19007,
+7507,
+30177,
+64311,
+108,
+44494,
+54665,
+23392,
+25428,
+10176,
+909,
+57832,
+50081,
+32937,
+7154,
+62726,
+50075,
+2379,
+43316,
+14474,
+31006,
+19943,
+36256,
+59376,
+59229,
+56612,
+10422,
+61080,
+25401,
+8177,
+45239,
+28848,
+56676,
+9225,
+61695,
+274,
+23356,
+478,
+60202,
+5616,
+53252,
+37171,
+10553,
+49250,
+34854,
+57073,
+2239,
+33735,
+27977,
+10624,
+17568,
+568,
+12210,
+28763,
+52699,
+42125,
+16148,
+9055,
+54661,
+8450,
+63576,
+8260,
+49124,
+4134,
+54808,
+60156,
+60507,
+44648,
+17292,
+22664,
+15812,
+3242,
+45693,
+46983,
+23481,
+8879,
+46888,
+24486,
+18883,
+9708,
+52436,
+33655,
+27545,
+45422,
+493,
+65140,
+32935,
+50083,
+14815,
+62853,
+48195,
+43875,
+48858,
+61801,
+56712,
+14729,
+33339,
+34977,
+47489,
+4918,
+33429,
+33785,
+57680,
+52182,
+3137,
+2361,
+25056,
+28960,
+32223,
+23953,
+60036,
+47986,
+7197,
+12848,
+4617,
+13923,
+21638,
+6449,
+56627,
+4812,
+64721,
+19059,
+6510,
+43897,
+59090,
+53246,
+33900,
+45168,
+11015,
+28373,
+24459,
+63215,
+26741,
+31054,
+31842,
+63272,
+43509,
+45901,
+40428,
+33409,
+61758,
+55390,
+7875,
+908,
+10311,
+25429,
+53897,
+61298,
+3666,
+22359,
+33980,
+46512,
+26510,
+29871,
+21100,
+20683,
+45529,
+17811,
+46041,
+33894,
+54397,
+64679,
+47414,
+28126,
+53515,
+3343,
+46116,
+7449,
+18237,
+20789,
+23716,
+44957,
+3778,
+55358,
+57663,
+48082,
+46730,
+59192,
+37098,
+41619,
+63165,
+52215,
+46331,
+13163,
+29180,
+1636,
+28746,
+31918,
+51960,
+35217,
+33726,
+27932,
+32102,
+25556,
+7553,
+22480,
+64197,
+18600,
+1664,
+53454,
+15238,
+40874,
+32626,
+40759,
+22500,
+17902,
+62485,
+39654,
+50611,
+888,
+31848,
+13757,
+23633,
+40728,
+40745,
+18017,
+52571,
+61580,
+38750,
+5210,
+20589,
+28307,
+48825,
+64836,
+49341,
+46405,
+2214,
+28642,
+23099,
+54113,
+52566,
+51324,
+8426,
+64387,
+13405,
+52852,
+18204,
+52427,
+24578,
+59296,
+36624,
+40438,
+33861,
+43093,
+37341,
+8606,
+11118,
+52954,
+475,
+19965,
+63539,
+45913,
+345,
+51332,
+64020,
+6010,
+45226,
+31098,
+2690,
+21457,
+60410,
+30954,
+10445,
+64553,
+53604,
+21724,
+39287,
+64005,
+6422,
+63783,
+59818,
+150,
+43837,
+2831,
+2616,
+41547,
+16319,
+65028,
+26190,
+59480,
+6743,
+33751,
+44181,
+2984,
+61103,
+40021,
+24369,
+44301,
+59063,
+7716,
+54710,
+3385,
+11169,
+24837,
+61530,
+2275,
+52206,
+52937,
+5905,
+7691,
+29387,
+42080,
+53912,
+54166,
+27017,
+51520,
+61643,
+11297,
+30484,
+53570,
+29089,
+50608,
+14256,
+41469,
+61260,
+47805,
+45209,
+21909,
+29204,
+37988,
+22761,
+18811,
+38786,
+19667,
+44642,
+35329,
+28388,
+14120,
+29653,
+22506,
+63523,
+35488,
+12841,
+32004,
+28542,
+3925,
+1687,
+61539,
+952,
+64644,
+24908,
+25738,
+14023,
+716,
+28702,
+59775,
+11650,
+46,
+6973,
+62014,
+36305,
+21964,
+59863,
+52761,
+12580,
+32147,
+16108,
+9525,
+19519,
+9244,
+19694,
+35766,
+8536,
+30501,
+38502,
+65458,
+46213,
+6262,
+11386,
+18309,
+31438,
+49049,
+59957,
+24200,
+32584,
+14287,
+52626,
+1661,
+41839,
+178,
+39909,
+2387,
+43610,
+52485,
+5811,
+22745,
+23440,
+35919,
+23999,
+25869,
+52423,
+7023,
+44050,
+16036,
+49024,
+12698,
+6452,
+3190,
+45041,
+56335,
+43557,
+62425,
+49949,
+25813,
+51770,
+41596,
+57016,
+51746,
+19472,
+17596,
+60134,
+28863,
+25724,
+31204,
+37307,
+31250,
+1383,
+29218,
+52558,
+63816,
+17033,
+1867,
+45084,
+37182,
+61782,
+5186,
+34292,
+25816,
+36048,
+14365,
+36930,
+47597,
+59605,
+5979,
+35871,
+22628,
+42363,
+33362,
+3694,
+27280,
+45856,
+62764,
+51518,
+27019,
+9589,
+47107,
+47437,
+60751,
+37077,
+32971,
+26248,
+1534,
+62037,
+47557,
+29487,
+51174,
+13029,
+43523,
+54180,
+62739,
+39834,
+47569,
+19100,
+54387,
+61288,
+41949,
+39406,
+57655,
+52399,
+24356,
+51385,
+31614,
+4319,
+19920,
+4666,
+13833,
+46290,
+26320,
+26043,
+10975,
+5548,
+37228,
+16714,
+2032,
+37879,
+4418,
+23745,
+12037,
+6779,
+50971,
+53623,
+29598,
+52687,
+60922,
+33849,
+20665,
+1501,
+40798,
+59405,
+10637,
+50007,
+43124,
+45171,
+36517,
+12612,
+53104,
+3960,
+52467,
+45399,
+38216,
+37632,
+52308,
+39671,
+61641,
+51522,
+63485,
+26906,
+41035,
+2066,
+20515,
+24563,
+5297,
+57584,
+32036,
+60700,
+36686,
+18917,
+6545,
+64687,
+6794,
+62143,
+30442,
+65383,
+54401,
+8753,
+11942,
+26961,
+14403,
+30035,
+32008,
+53026,
+14811,
+3492,
+33717,
+36583,
+21944,
+56961,
+37466,
+31957,
+24542,
+34365,
+29867,
+12835,
+18305,
+4315,
+50490,
+58939,
+31483,
+48854,
+57339,
+18486,
+19052,
+60117,
+10559,
+52834,
+40901,
+62883,
+31522,
+2211,
+44117,
+13262,
+63769,
+45213,
+22081,
+27362,
+33209,
+35627,
+45438,
+65463,
+38605,
+22292,
+4152,
+42877,
+40317,
+19824,
+48424,
+43137,
+38731,
+65005,
+7900,
+55858,
+58922,
+13804,
+4905,
+25262,
+51877,
+28017,
+5271,
+9660,
+57730,
+1571,
+47236,
+29464,
+53734,
+18165,
+55101,
+50917,
+25451,
+61225,
+58849,
+50641,
+52435,
+10241,
+18884,
+22100,
+5220,
+41663,
+38459,
+32093,
+8600,
+14006,
+24785,
+63584,
+38645,
+29639,
+37132,
+9431,
+40642,
+50424,
+63001,
+9489,
+61208,
+50986,
+34355,
+59779,
+19069,
+2795,
+63405,
+63662,
+48227,
+57636,
+55807,
+6616,
+24435,
+16750,
+15905,
+60639,
+27704,
+14426,
+16718,
+56679,
+51886,
+9253,
+63293,
+25012,
+7581,
+4781,
+26383,
+45629,
+57729,
+9722,
+5272,
+8204,
+31815,
+31445,
+224,
+28546,
+9634,
+57260,
+65245,
+13739,
+14277,
+56849,
+33920,
+51208,
+47347,
+26401,
+46283,
+24748,
+5411,
+27962,
+51150,
+18720,
+38838,
+35455,
+57259,
+9653,
+28547,
+6321,
+47096,
+25473,
+53701,
+22633,
+46251,
+32360,
+38117,
+37869,
+12899,
+28314,
+29727,
+38239,
+17333,
+57844,
+36152,
+30867,
+62831,
+16040,
+60717,
+28098,
+7194,
+21707,
+39831,
+2135,
+60905,
+58080,
+8993,
+62514,
+45261,
+47443,
+54588,
+64670,
+47451,
+63987,
+38037,
+7656,
+14774,
+12412,
+51853,
+56361,
+29123,
+47106,
+9876,
+27020,
+2960,
+43803,
+48669,
+61058,
+60401,
+30402,
+27457,
+25948,
+23474,
+22888,
+23431,
+21305,
+1013,
+41818,
+36859,
+27832,
+65133,
+46999,
+15999,
+38803,
+2151,
+45347,
+42844,
+13191,
+25720,
+4832,
+50025,
+3791,
+47581,
+10484,
+25647,
+54461,
+33595,
+39244,
+52125,
+60055,
+9483,
+19716,
+25442,
+18869,
+6645,
+32947,
+31948,
+42746,
+16125,
+52659,
+29413,
+63428,
+61030,
+51939,
+17001,
+22439,
+5588,
+54023,
+20693,
+1822,
+43932,
+13018,
+63907,
+51457,
+14962,
+19518,
+9963,
+16109,
+41223,
+34558,
+4192,
+27213,
+34383,
+47242,
+14234,
+18960,
+675,
+29958,
+63112,
+12519,
+34722,
+6154,
+59341,
+8168,
+18068,
+58429,
+39598,
+15012,
+60675,
+46218,
+62902,
+13611,
+3222,
+24585,
+40004,
+21098,
+29873,
+24692,
+10397,
+55132,
+801,
+61207,
+9690,
+63002,
+14355,
+51975,
+59961,
+19715,
+9551,
+60056,
+15871,
+19032,
+59674,
+8263,
+35220,
+19455,
+6903,
+22489,
+48179,
+7092,
+44065,
+17668,
+18888,
+37247,
+21398,
+30373,
+39523,
+6821,
+42357,
+58673,
+35238,
+14777,
+13088,
+7837,
+64844,
+7104,
+33881,
+43555,
+56337,
+61356,
+28646,
+52173,
+42718,
+22648,
+7860,
+61728,
+30274,
+18938,
+4067,
+19414,
+48974,
+33553,
+19441,
+28866,
+23980,
+15398,
+13288,
+50886,
+62751,
+40641,
+9694,
+37133,
+41488,
+47832,
+43353,
+1620,
+20499,
+7907,
+28286,
+35676,
+14546,
+64236,
+31610,
+34369,
+42376,
+54352,
+49687,
+44478,
+15366,
+21342,
+25251,
+35819,
+59707,
+39240,
+50203,
+12033,
+45524,
+34908,
+12692,
+53234,
+2670,
+14453,
+1792,
+9212,
+15978,
+49083,
+37655,
+43889,
+28481,
+51923,
+36373,
+24449,
+8104,
+17208,
+52963,
+10327,
+63463,
+44704,
+54159,
+12916,
+15628,
+26641,
+42373,
+49303,
+18633,
+24835,
+11171,
+8900,
+62880,
+24454,
+12015,
+29677,
+4274,
+8402,
+14336,
+28836,
+40050,
+23831,
+49151,
+21848,
+58984,
+19276,
+2731,
+16913,
+36218,
+49517,
+26467,
+38878,
+6951,
+8080,
+7373,
+52731,
+41135,
+5840,
+58862,
+50720,
+2686,
+38611,
+35178,
+33456,
+60731,
+22124,
+29172,
+37176,
+61487,
+40385,
+41302,
+6916,
+27042,
+4923,
+8639,
+42729,
+42291,
+35080,
+59459,
+31869,
+47777,
+16567,
+42979,
+11579,
+22373,
+45236,
+53680,
+5479,
+49668,
+33071,
+58628,
+7733,
+33905,
+20051,
+30766,
+50001,
+12792,
+25974,
+39356,
+13937,
+17770,
+43905,
+44269,
+16423,
+18137,
+20246,
+55057,
+10608,
+16742,
+49188,
+21244,
+37352,
+42208,
+59422,
+24773,
+4395,
+15938,
+13837,
+9061,
+1715,
+27690,
+58852,
+38780,
+48076,
+50475,
+1363,
+1010,
+56333,
+45043,
+32931,
+29663,
+41715,
+54335,
+9111,
+25498,
+41733,
+46397,
+23399,
+60141,
+64469,
+51411,
+27965,
+47552,
+3729,
+21138,
+61241,
+23988,
+49220,
+50174,
+34220,
+13334,
+63292,
+9668,
+51887,
+19241,
+47633,
+3054,
+64637,
+730,
+2332,
+19693,
+9961,
+19520,
+10573,
+3007,
+4206,
+23742,
+48532,
+23662,
+6961,
+43275,
+5103,
+14395,
+39193,
+6340,
+27855,
+24208,
+48613,
+44037,
+61694,
+10287,
+56677,
+16720,
+47536,
+52893,
+14593,
+52286,
+64435,
+2382,
+7289,
+45787,
+59797,
+15977,
+9398,
+1793,
+32185,
+62669,
+23523,
+53550,
+44230,
+64526,
+56259,
+20495,
+55616,
+21981,
+56084,
+4182,
+58989,
+6848,
+22727,
+51054,
+17899,
+10886,
+31093,
+48552,
+8579,
+44861,
+2147,
+47018,
+53544,
+2269,
+43394,
+35534,
+5189,
+34007,
+20939,
+6176,
+50663,
+13683,
+55945,
+36415,
+15552,
+3608,
+35448,
+5331,
+64272,
+53872,
+53306,
+4647,
+32840,
+43391,
+3869,
+6995,
+3083,
+24588,
+21414,
+15878,
+32883,
+8796,
+40320,
+62874,
+22774,
+8628,
+25672,
+62393,
+6611,
+42156,
+62306,
+37071,
+22235,
+5038,
+2186,
+21547,
+29944,
+1649,
+36662,
+23005,
+63034,
+8514,
+3680,
+33633,
+2992,
+18241,
+49813,
+65092,
+45316,
+49614,
+639,
+52533,
+26492,
+34819,
+32474,
+42427,
+46534,
+34087,
+5986,
+41576,
+14900,
+59319,
+25227,
+35001,
+44734,
+59719,
+25497,
+9272,
+54336,
+19469,
+24213,
+36104,
+23513,
+43461,
+62204,
+62686,
+20168,
+30763,
+51659,
+13541,
+57606,
+42786,
+31767,
+46247,
+58352,
+11085,
+58509,
+47911,
+1031,
+58997,
+12930,
+4385,
+33158,
+62601,
+17351,
+44663,
+41933,
+36561,
+60001,
+48393,
+2765,
+4953,
+51449,
+48371,
+14250,
+64793,
+62539,
+28560,
+13895,
+40490,
+32848,
+52512,
+6759,
+61980,
+41192,
+63317,
+1714,
+9287,
+13838,
+62976,
+52405,
+13886,
+54660,
+10263,
+16149,
+58124,
+50427,
+59795,
+45789,
+19685,
+13161,
+46333,
+61661,
+30055,
+14848,
+46339,
+44003,
+12787,
+5459,
+42408,
+38468,
+6991,
+22056,
+14783,
+19014,
+51469,
+54515,
+27373,
+59172,
+48886,
+53639,
+53507,
+36428,
+21226,
+45093,
+38423,
+14521,
+24926,
+29884,
+53242,
+47216,
+54831,
+1054,
+6710,
+27999,
+31536,
+39568,
+41267,
+32463,
+39603,
+15955,
+8877,
+23483,
+59591,
+58615,
+44412,
+8598,
+32095,
+25290,
+38977,
+49506,
+43939,
+54908,
+51497,
+62513,
+9605,
+58081,
+12326,
+45803,
+15770,
+60061,
+29352,
+970,
+47652,
+45819,
+40097,
+64085,
+65267,
+30455,
+6080,
+46419,
+64201,
+46736,
+25543,
+25188,
+33057,
+42900,
+25569,
+45759,
+57565,
+24971,
+61583,
+24538,
+28057,
+36446,
+14931,
+15525,
+22833,
+41202,
+43467,
+60745,
+4734,
+35337,
+42213,
+2086,
+7569,
+47037,
+20230,
+21770,
+36573,
+53767,
+36442,
+652,
+25995,
+52179,
+63595,
+39438,
+8642,
+58888,
+22310,
+15728,
+43684,
+31045,
+1599,
+60336,
+57056,
+26950,
+37087,
+18663,
+35231,
+26551,
+19631,
+6307,
+55242,
+55116,
+27719,
+1403,
+41125,
+4063,
+37688,
+21951,
+35543,
+27557,
+17849,
+27622,
+49184,
+52837,
+59087,
+55335,
+60726,
+54597,
+50674,
+26543,
+25523,
+11784,
+51374,
+24523,
+62879,
+9374,
+11172,
+58945,
+49022,
+16038,
+62833,
+22865,
+57659,
+58233,
+35529,
+62168,
+38899,
+62123,
+30110,
+44687,
+18610,
+6086,
+58292,
+36325,
+52099,
+46887,
+10245,
+23482,
+9007,
+15956,
+55184,
+44260,
+40535,
+39956,
+59612,
+31276,
+42711,
+60519,
+16800,
+45527,
+20685,
+32421,
+18915,
+36688,
+55249,
+56313,
+48818,
+55867,
+13652,
+35292,
+64227,
+16573,
+48096,
+44370,
+12247,
+40159,
+18071,
+11029,
+1326,
+46030,
+49777,
+2438,
+14726,
+30115,
+13269,
+24265,
+18477,
+11872,
+24073,
+37686,
+4065,
+18940,
+50594,
+47498,
+38917,
+8335,
+63659,
+10371,
+37717,
+3735,
+55311,
+50809,
+35424,
+17480,
+43040,
+41856,
+23379,
+3233,
+30743,
+15232,
+4669,
+44249,
+47355,
+12740,
+54484,
+49640,
+26746,
+40663,
+5276,
+32962,
+37908,
+11233,
+59395,
+44354,
+64353,
+10970,
+23812,
+19822,
+40319,
+9157,
+32884,
+14346,
+60029,
+40737,
+27673,
+4645,
+53308,
+12387,
+34965,
+18581,
+45156,
+20540,
+41510,
+3097,
+46520,
+61454,
+63499,
+11326,
+8460,
+11000,
+51145,
+13572,
+7799,
+49347,
+59664,
+37312,
+52458,
+39234,
+26306,
+17487,
+1144,
+40239,
+47872,
+41789,
+60540,
+6232,
+25709,
+61773,
+25655,
+35725,
+48043,
+11941,
+9786,
+54402,
+41175,
+35375,
+33379,
+38155,
+48027,
+62712,
+15573,
+2286,
+52614,
+55004,
+49552,
+17644,
+41704,
+16388,
+24146,
+1928,
+40125,
+50177,
+18251,
+13247,
+53512,
+30126,
+4715,
+15746,
+37616,
+11754,
+14820,
+41956,
+62389,
+49690,
+32389,
+42336,
+38778,
+58854,
+18901,
+14324,
+13145,
+26827,
+32855,
+64040,
+47270,
+23649,
+45234,
+22375,
+3214,
+10968,
+64355,
+15428,
+41131,
+32889,
+60144,
+5823,
+2893,
+32226,
+61302,
+57164,
+49279,
+25366,
+50866,
+52033,
+59806,
+32678,
+51259,
+55889,
+31618,
+14796,
+51675,
+49032,
+21345,
+54433,
+29526,
+40417,
+46591,
+33131,
+23890,
+23787,
+59667,
+68,
+50341,
+43054,
+45077,
+64577,
+53237,
+61371,
+50149,
+10943,
+26835,
+16649,
+11900,
+65136,
+20809,
+60792,
+5071,
+27924,
+62365,
+7276,
+46175,
+21621,
+57236,
+2583,
+51969,
+33511,
+16837,
+49903,
+14984,
+20504,
+33682,
+50381,
+58887,
+8941,
+39439,
+42728,
+9331,
+4924,
+37234,
+48576,
+46244,
+39934,
+7637,
+58013,
+52340,
+4883,
+25671,
+9153,
+22775,
+52811,
+29239,
+60681,
+29793,
+36113,
+24106,
+13632,
+16485,
+52495,
+4936,
+29338,
+30376,
+15224,
+59503,
+51177,
+28769,
+15703,
+33191,
+44105,
+11117,
+10075,
+37342,
+18776,
+63922,
+48768,
+14005,
+9701,
+32094,
+9002,
+44413,
+57890,
+61,
+22199,
+58078,
+60907,
+32635,
+20775,
+42501,
+2297,
+58836,
+41348,
+12657,
+43984,
+47387,
+17251,
+59175,
+44860,
+9190,
+48553,
+43592,
+55763,
+28072,
+58329,
+39576,
+47379,
+3892,
+54242,
+413,
+8391,
+5944,
+6924,
+25771,
+15068,
+1745,
+19557,
+40175,
+20465,
+57811,
+2351,
+25887,
+44802,
+37024,
+60534,
+40657,
+41635,
+51795,
+36969,
+28671,
+37453,
+24031,
+14839,
+59501,
+15226,
+65000,
+25311,
+53444,
+2402,
+46856,
+54476,
+30500,
+9958,
+35767,
+42629,
+49339,
+64838,
+11362,
+13699,
+59190,
+46732,
+38921,
+50079,
+57834,
+469,
+26913,
+23940,
+55623,
+13792,
+22656,
+41278,
+3732,
+62097,
+3679,
+9137,
+63035,
+15776,
+37046,
+51775,
+55170,
+35349,
+15246,
+63449,
+57970,
+10612,
+18051,
+18215,
+44876,
+38343,
+27336,
+60609,
+52263,
+44563,
+40680,
+48956,
+3349,
+48381,
+28890,
+58304,
+42330,
+12238,
+33977,
+2927,
+61621,
+52598,
+44724,
+5200,
+56604,
+38588,
+14678,
+27682,
+20820,
+30936,
+40776,
+38770,
+21379,
+16655,
+59750,
+28420,
+32517,
+4187,
+30876,
+42445,
+30682,
+55262,
+63299,
+47081,
+10999,
+8777,
+11327,
+17039,
+6897,
+5464,
+16766,
+39854,
+60544,
+62164,
+63575,
+10261,
+54662,
+49657,
+32656,
+2646,
+60710,
+16555,
+27952,
+36883,
+62377,
+25415,
+49244,
+45646,
+25626,
+63152,
+41319,
+46367,
+62102,
+1127,
+33060,
+27000,
+13972,
+7224,
+64386,
+10088,
+51325,
+6854,
+27038,
+10513,
+24554,
+23763,
+34267,
+1487,
+28233,
+50095,
+58590,
+44639,
+31526,
+4620,
+21147,
+1504,
+27348,
+14724,
+2440,
+39146,
+16086,
+58152,
+14335,
+9368,
+4275,
+17734,
+25898,
+46238,
+25631,
+26922,
+26449,
+62250,
+8238,
+5943,
+8568,
+414,
+13898,
+64826,
+31108,
+13606,
+3228,
+37529,
+16910,
+50048,
+62284,
+38110,
+33711,
+1807,
+27296,
+48206,
+19352,
+4234,
+56841,
+16259,
+18594,
+58905,
+46038,
+885,
+38835,
+29494,
+13695,
+37368,
+43963,
+60811,
+26729,
+18446,
+19342,
+40547,
+19676,
+45341,
+8299,
+62956,
+55047,
+30642,
+58215,
+22157,
+38330,
+1892,
+1005,
+14268,
+21542,
+35007,
+46370,
+2569,
+16398,
+43322,
+7474,
+28697,
+58808,
+63658,
+8830,
+38918,
+1919,
+61203,
+43652,
+40463,
+5626,
+53961,
+42760,
+12728,
+23798,
+53672,
+4945,
+6560,
+7183,
+54454,
+62244,
+36771,
+35491,
+26519,
+29935,
+12368,
+12267,
+32871,
+33561,
+21475,
+24112,
+61790,
+30950,
+21438,
+15096,
+37638,
+11674,
+43915,
+56884,
+62955,
+8355,
+45342,
+33120,
+14038,
+3026,
+44147,
+6172,
+37977,
+49712,
+58841,
+43043,
+26830,
+30653,
+62919,
+23992,
+20419,
+30019,
+26707,
+63970,
+33188,
+30081,
+55902,
+55885,
+293,
+10478,
+54931,
+36567,
+7487,
+24096,
+60172,
+54774,
+37897,
+16521,
+57331,
+33724,
+35219,
+9478,
+59675,
+49123,
+10259,
+63577,
+13182,
+33463,
+40143,
+31661,
+38263,
+27722,
+22849,
+53988,
+33547,
+7778,
+50289,
+2090,
+18399,
+28043,
+45639,
+39451,
+27266,
+63358,
+5989,
+5942,
+8393,
+62251,
+5228,
+43755,
+3717,
+53111,
+7396,
+31188,
+50829,
+4221,
+33111,
+46745,
+11608,
+44605,
+1028,
+59169,
+33454,
+35180,
+54957,
+24081,
+3166,
+57633,
+21960,
+22920,
+59545,
+15305,
+36272,
+23003,
+36664,
+52915,
+13933,
+49015,
+57271,
+31814,
+9658,
+5273,
+51434,
+1846,
+19765,
+16682,
+55776,
+47052,
+27483,
+28494,
+17476,
+46811,
+8,
+58256,
+50117,
+41944,
+15888,
+30371,
+21400,
+5749,
+11091,
+46684,
+64728,
+37555,
+2027,
+53678,
+45238,
+10291,
+25402,
+32051,
+23051,
+41978,
+52104,
+24431,
+58502,
+18067,
+9508,
+59342,
+61147,
+42886,
+33788,
+8157,
+30729,
+55009,
+64517,
+17559,
+30728,
+8163,
+33789,
+23611,
+51785,
+30570,
+46958,
+57569,
+24309,
+50530,
+13553,
+14705,
+39784,
+29031,
+44429,
+61368,
+3759,
+3365,
+15402,
+25598,
+38293,
+53351,
+42475,
+32410,
+37490,
+60549,
+42238,
+35650,
+37186,
+48147,
+51612,
+43901,
+4460,
+2807,
+20563,
+30102,
+47196,
+33402,
+29645,
+23078,
+62332,
+46781,
+27334,
+38345,
+35757,
+19775,
+62572,
+21913,
+12469,
+36070,
+2045,
+44365,
+31960,
+17207,
+9389,
+24450,
+14180,
+7192,
+28100,
+48471,
+45345,
+2153,
+51816,
+47993,
+59359,
+14876,
+55146,
+65456,
+38504,
+3257,
+39810,
+10754,
+54344,
+51619,
+6137,
+14789,
+26557,
+7372,
+9352,
+6952,
+44792,
+46680,
+41622,
+50709,
+20528,
+7152,
+32939,
+27324,
+3354,
+18031,
+528,
+32212,
+54625,
+34154,
+45780,
+39779,
+19957,
+12870,
+43526,
+34830,
+3115,
+17521,
+30271,
+16907,
+19928,
+32268,
+40281,
+50038,
+64096,
+5781,
+42744,
+31950,
+7176,
+60470,
+27155,
+16810,
+59971,
+59791,
+42004,
+856,
+18125,
+64428,
+2781,
+42805,
+7823,
+33515,
+24813,
+11033,
+42546,
+17125,
+33642,
+24657,
+11342,
+43675,
+35619,
+22766,
+14109,
+25468,
+19228,
+17792,
+19854,
+21565,
+53118,
+43198,
+60524,
+26663,
+64400,
+27635,
+46825,
+39188,
+19977,
+17745,
+53037,
+4159,
+43372,
+63891,
+39124,
+39948,
+12067,
+28463,
+64693,
+57686,
+27777,
+1787,
+32293,
+21064,
+30251,
+6069,
+32424,
+18794,
+54059,
+38180,
+57946,
+23375,
+31135,
+42033,
+11930,
+62006,
+25566,
+45972,
+17752,
+13671,
+28997,
+30184,
+62570,
+19777,
+56568,
+40567,
+42044,
+18578,
+57386,
+41229,
+43003,
+51903,
+49054,
+33859,
+40440,
+64261,
+6529,
+55030,
+36425,
+14076,
+34243,
+26429,
+60239,
+55739,
+33502,
+6693,
+23684,
+22104,
+12047,
+16543,
+36513,
+12398,
+16376,
+24860,
+53910,
+42082,
+48641,
+18318,
+24661,
+32913,
+38062,
+54577,
+64934,
+29939,
+51995,
+56439,
+22752,
+26028,
+42399,
+20826,
+44543,
+46707,
+6294,
+761,
+26233,
+25260,
+4907,
+25225,
+59321,
+33766,
+39251,
+29502,
+27717,
+55118,
+13394,
+12443,
+53051,
+15943,
+28285,
+9424,
+20500,
+62370,
+57320,
+43578,
+17309,
+55857,
+9731,
+65006,
+37963,
+49354,
+32810,
+772,
+1951,
+26894,
+31199,
+45106,
+31589,
+10567,
+17024,
+14525,
+23710,
+17978,
+36052,
+26130,
+55684,
+13139,
+47847,
+42671,
+56644,
+7305,
+907,
+10178,
+55391,
+17456,
+6935,
+64338,
+35833,
+44266,
+38291,
+25600,
+48732,
+60854,
+17876,
+12744,
+47273,
+61727,
+9447,
+22649,
+31751,
+55319,
+13356,
+1944,
+62775,
+41018,
+843,
+57540,
+37761,
+64009,
+47040,
+451,
+43644,
+944,
+62972,
+42917,
+49182,
+27624,
+47088,
+4431,
+64843,
+9458,
+13089,
+37666,
+64073,
+63119,
+28526,
+41627,
+6470,
+12205,
+47696,
+30738,
+12821,
+3124,
+33514,
+8034,
+42806,
+29027,
+27430,
+35480,
+50691,
+48241,
+26572,
+6734,
+18762,
+2701,
+30344,
+27814,
+3130,
+19907,
+55876,
+55816,
+63967,
+60818,
+34742,
+20779,
+42,
+39628,
+49346,
+8773,
+13573,
+32924,
+25127,
+59761,
+45701,
+26457,
+27865,
+4105,
+7008,
+11502,
+60530,
+4570,
+59889,
+825,
+37054,
+13616,
+23690,
+38298,
+48462,
+50288,
+8249,
+33548,
+18348,
+25060,
+22551,
+43548,
+52492,
+5583,
+6773,
+50842,
+46917,
+41835,
+24127,
+45897,
+6525,
+45664,
+29864,
+43666,
+50276,
+41346,
+58838,
+28523,
+46415,
+3617,
+42370,
+39214,
+61014,
+35842,
+50510,
+2884,
+7452,
+30514,
+19079,
+31715,
+6508,
+19061,
+21967,
+44109,
+3497,
+6165,
+4582,
+144,
+21696,
+18369,
+33904,
+9314,
+58629,
+44929,
+195,
+48485,
+44921,
+45071,
+56978,
+63187,
+280,
+23557,
+13435,
+48705,
+33558,
+33868,
+24328,
+54709,
+10031,
+59064,
+17408,
+52987,
+2184,
+5040,
+49157,
+62417,
+22421,
+46472,
+64627,
+55228,
+15457,
+57012,
+65069,
+16280,
+31262,
+18872,
+65222,
+39031,
+64183,
+19231,
+48203,
+55919,
+29386,
+10021,
+5906,
+23056,
+43518,
+33847,
+60924,
+17721,
+60394,
+18826,
+32632,
+62636,
+48069,
+3209,
+10883,
+22503,
+51236,
+38622,
+41599,
+50214,
+26114,
+65144,
+41296,
+40575,
+44452,
+46723,
+41745,
+44572,
+32435,
+41787,
+47874,
+13311,
+50419,
+42964,
+24724,
+14773,
+9596,
+38038,
+10900,
+7005,
+53555,
+11975,
+64449,
+1448,
+21123,
+56941,
+65477,
+5555,
+58378,
+30389,
+11243,
+13809,
+24645,
+12826,
+58012,
+8633,
+39935,
+16807,
+29077,
+55105,
+13748,
+43740,
+48384,
+4888,
+6985,
+16859,
+13501,
+34148,
+49041,
+17014,
+41900,
+40048,
+28838,
+20240,
+60814,
+27662,
+17632,
+49426,
+24316,
+17088,
+17228,
+26883,
+7346,
+37243,
+40461,
+43654,
+63133,
+18683,
+16729,
+39617,
+17061,
+5682,
+33398,
+3743,
+48191,
+36905,
+20957,
+27466,
+34460,
+20059,
+27356,
+57551,
+42163,
+42324,
+58567,
+30000,
+31248,
+37309,
+23790,
+20967,
+4780,
+9665,
+25013,
+53258,
+48249,
+42071,
+22085,
+27452,
+64490,
+58458,
+251,
+23147,
+47036,
+8953,
+2087,
+57032,
+54926,
+3318,
+25967,
+24272,
+65511,
+5244,
+48832,
+50217,
+60447,
+49788,
+5074,
+47433,
+22479,
+10126,
+25557,
+460,
+29713,
+62620,
+16610,
+31505,
+31900,
+58701,
+30032,
+61549,
+5935,
+17677,
+42008,
+52254,
+1052,
+54833,
+14758,
+374,
+17128,
+65450,
+12956,
+46975,
+52682,
+53072,
+61158,
+59625,
+52710,
+57899,
+56905,
+13198,
+44217,
+14892,
+1682,
+30803,
+4071,
+23322,
+63453,
+17337,
+59388,
+59771,
+14177,
+40905,
+60589,
+62491,
+30176,
+10319,
+19008,
+41287,
+16917,
+38433,
+29973,
+23031,
+30348,
+39074,
+40670,
+37677,
+20712,
+22586,
+61383,
+22662,
+17294,
+38967,
+32751,
+3973,
+24095,
+8272,
+36568,
+11365,
+49468,
+28858,
+60115,
+19054,
+16761,
+31143,
+64126,
+12876,
+36126,
+28696,
+8339,
+43323,
+27655,
+36080,
+4413,
+20743,
+18859,
+51336,
+31048,
+7357,
+37027,
+45313,
+53408,
+58097,
+43337,
+56944,
+4631,
+55931,
+56545,
+53212,
+50898,
+30513,
+7748,
+2885,
+18236,
+10153,
+46117,
+46264,
+15042,
+33081,
+54981,
+52294,
+58624,
+12128,
+58076,
+22201,
+16186,
+47181,
+26555,
+14791,
+4117,
+10457,
+21878,
+12453,
+19961,
+32323,
+1844,
+51436,
+34057,
+865,
+18088,
+49901,
+16839,
+44651,
+53463,
+48064,
+64574,
+10833,
+35782,
+5197,
+49387,
+56484,
+15111,
+2538,
+17450,
+13348,
+60167,
+34631,
+6982,
+40517,
+49436,
+33490,
+54018,
+17179,
+43157,
+15607,
+17372,
+31187,
+8232,
+53112,
+12276,
+16843,
+38026,
+44114,
+46408,
+26174,
+4941,
+19190,
+49450,
+57133,
+51203,
+61720,
+38042,
+37985,
+29243,
+15072,
+1288,
+6814,
+1857,
+21976,
+52730,
+9351,
+8081,
+26558,
+16033,
+43552,
+23847,
+15657,
+38872,
+1139,
+47889,
+17686,
+16816,
+5983,
+4568,
+60532,
+37026,
+7465,
+31049,
+4482,
+49077,
+14029,
+27108,
+1159,
+10905,
+33094,
+51989,
+37242,
+7610,
+26884,
+42558,
+6792,
+64689,
+43486,
+61903,
+16958,
+35948,
+41357,
+14803,
+27010,
+11179,
+2422,
+5086,
+42258,
+63418,
+64455,
+59536,
+27603,
+38959,
+5574,
+45824,
+34894,
+57186,
+43381,
+24648,
+4513,
+41742,
+26754,
+39066,
+39806,
+2948,
+3979,
+13059,
+60284,
+46109,
+35723,
+25657,
+64667,
+906,
+7877,
+56645,
+30009,
+26432,
+40008,
+59908,
+42982,
+64181,
+39033,
+40251,
+40544,
+7215,
+37356,
+27367,
+27973,
+45786,
+9216,
+2383,
+24220,
+21255,
+39790,
+37942,
+45875,
+65528,
+57346,
+15421,
+2521,
+33705,
+46174,
+8656,
+62366,
+47076,
+18842,
+17673,
+44806,
+11025,
+1695,
+61867,
+59179,
+24290,
+44410,
+58617,
+47418,
+34050,
+49850,
+11311,
+6681,
+46638,
+44179,
+33753,
+26269,
+62926,
+46128,
+11938,
+34183,
+20095,
+54688,
+50066,
+23585,
+59450,
+56654,
+29213,
+36139,
+20081,
+29741,
+40852,
+54501,
+17919,
+20602,
+53953,
+12890,
+36646,
+57047,
+23208,
+12163,
+15437,
+16935,
+34863,
+3582,
+5342,
+64385,
+8428,
+13973,
+43894,
+52023,
+59301,
+58937,
+50492,
+10661,
+37355,
+7294,
+40545,
+19344,
+41156,
+12224,
+16207,
+37464,
+56963,
+25962,
+46538,
+63927,
+25336,
+17815,
+60068,
+53924,
+64381,
+31890,
+12847,
+10208,
+47987,
+21706,
+9611,
+28099,
+8101,
+14181,
+24091,
+43822,
+59565,
+45968,
+19289,
+56443,
+54453,
+8321,
+6561,
+39481,
+48313,
+58955,
+38411,
+60469,
+8046,
+31951,
+29900,
+17588,
+18687,
+52075,
+48746,
+19545,
+18734,
+50769,
+35439,
+63232,
+36382,
+33871,
+17432,
+3313,
+59981,
+57893,
+35149,
+12536,
+48878,
+62725,
+10306,
+32938,
+8073,
+20529,
+53832,
+24275,
+18383,
+48316,
+24918,
+43780,
+39713,
+65170,
+31337,
+43364,
+36284,
+48159,
+27480,
+25280,
+21463,
+10361,
+16692,
+25579,
+5927,
+54582,
+40655,
+60536,
+20016,
+13775,
+50776,
+18148,
+48288,
+2429,
+6285,
+62233,
+44486,
+29818,
+59672,
+19034,
+62157,
+26049,
+28236,
+4441,
+12173,
+61915,
+25376,
+47468,
+56031,
+25940,
+48687,
+33880,
+9456,
+64845,
+3036,
+27817,
+3443,
+6490,
+52786,
+39976,
+39095,
+63394,
+31974,
+44064,
+9472,
+48180,
+16204,
+16271,
+54007,
+24922,
+52043,
+21858,
+31131,
+3405,
+41967,
+16493,
+1046,
+57964,
+65287,
+64222,
+45837,
+43750,
+54188,
+4985,
+13947,
+63776,
+10688,
+12006,
+54234,
+5388,
+10861,
+20111,
+26803,
+47007,
+28778,
+39131,
+19986,
+28456,
+5952,
+35197,
+52320,
+50934,
+45534,
+46544,
+54989,
+61847,
+62962,
+25233,
+62050,
+65444,
+27783,
+21610,
+47385,
+43986,
+20763,
+22944,
+46481,
+48778,
+15914,
+41481,
+41105,
+57367,
+47471,
+24079,
+54959,
+3570,
+62675,
+54451,
+56445,
+17627,
+54894,
+32400,
+44049,
+9929,
+52424,
+31345,
+47547,
+16592,
+34300,
+44177,
+46640,
+54214,
+32619,
+22288,
+1430,
+51478,
+5544,
+11501,
+7790,
+4106,
+53554,
+7653,
+10901,
+19400,
+32001,
+13040,
+60603,
+6564,
+41759,
+58559,
+3082,
+9163,
+3870,
+43935,
+22055,
+9037,
+38469,
+22749,
+33772,
+22249,
+16858,
+7628,
+4889,
+40516,
+7406,
+34632,
+18525,
+23777,
+58540,
+62456,
+52920,
+5149,
+62013,
+9972,
+47,
+23212,
+57949,
+35538,
+13177,
+32491,
+40295,
+35549,
+31291,
+34778,
+43274,
+9236,
+23663,
+49367,
+38288,
+43908,
+12446,
+30229,
+12148,
+44791,
+8079,
+9353,
+38879,
+16778,
+15441,
+57122,
+20899,
+59809,
+34137,
+18114,
+23781,
+53895,
+25431,
+47337,
+38814,
+48456,
+64337,
+7872,
+17457,
+35127,
+49928,
+48167,
+5608,
+52475,
+10453,
+31127,
+38692,
+25770,
+8566,
+5945,
+56533,
+21126,
+58273,
+18120,
+31878,
+27041,
+9334,
+41303,
+13242,
+58095,
+53410,
+39601,
+32465,
+56222,
+57805,
+20609,
+31364,
+49288,
+22488,
+9475,
+19456,
+24758,
+52933,
+37447,
+5463,
+8457,
+17040,
+26595,
+63303,
+23275,
+37970,
+46554,
+32369,
+11102,
+36013,
+40034,
+3550,
+6073,
+13909,
+47884,
+30902,
+48517,
+61279,
+47264,
+63698,
+22643,
+49229,
+27149,
+15264,
+48850,
+42353,
+48435,
+28552,
+64955,
+64974,
+53760,
+62683,
+23179,
+56075,
+30300,
+26083,
+61229,
+35090,
+17581,
+46606,
+13008,
+35034,
+27037,
+8424,
+51326,
+48598,
+16511,
+52841,
+22726,
+9197,
+58990,
+852,
+5917,
+46280,
+21453,
+46351,
+35791,
+29827,
+30368,
+6201,
+33636,
+57992,
+35506,
+33996,
+28275,
+35771,
+32284,
+46087,
+6737,
+61093,
+41701,
+17714,
+47033,
+33508,
+11949,
+42356,
+9464,
+39524,
+12475,
+59933,
+49607,
+17240,
+1856,
+7377,
+1289,
+20983,
+32985,
+36310,
+43717,
+15566,
+46753,
+6442,
+60269,
+39694,
+16772,
+42442,
+106,
+64313,
+46461,
+33077,
+44257,
+50850,
+62142,
+9791,
+64688,
+7343,
+42559,
+29371,
+1699,
+60642,
+309,
+17548,
+43833,
+48235,
+41643,
+26410,
+46565,
+50970,
+9832,
+12038,
+29408,
+35525,
+5301,
+50841,
+7770,
+5584,
+49673,
+37783,
+15433,
+60124,
+37577,
+34667,
+61691,
+53726,
+60527,
+23251,
+11729,
+61979,
+9066,
+52513,
+51095,
+43119,
+38352,
+20057,
+34462,
+48153,
+2757,
+33720,
+34094,
+2496,
+51365,
+61311,
+45016,
+33750,
+10040,
+59481,
+36390,
+47772,
+14607,
+61092,
+6829,
+46088,
+18761,
+7815,
+26573,
+40469,
+51195,
+36839,
+57200,
+1880,
+31010,
+11449,
+43347,
+52876,
+26749,
+45872,
+29380,
+54936,
+47775,
+31871,
+47116,
+16677,
+3738,
+62716,
+39207,
+33286,
+27998,
+9015,
+1055,
+4035,
+342,
+46431,
+34826,
+14869,
+40165,
+63239,
+56950,
+24190,
+57779,
+37015,
+15824,
+57247,
+22495,
+23683,
+7951,
+33503,
+44821,
+27263,
+4311,
+55826,
+14897,
+37846,
+56253,
+26758,
+56819,
+46637,
+7259,
+11312,
+47649,
+15712,
+59265,
+59696,
+56190,
+14818,
+11756,
+21384,
+37322,
+38939,
+12107,
+43806,
+18510,
+60985,
+25202,
+1426,
+51806,
+21180,
+45259,
+62516,
+24024,
+48006,
+64966,
+35299,
+30213,
+31429,
+64877,
+53803,
+15155,
+56894,
+56597,
+43228,
+12995,
+32946,
+9547,
+18870,
+31264,
+47056,
+63435,
+29541,
+59160,
+12983,
+48543,
+36551,
+48528,
+60232,
+46942,
+57167,
+19500,
+54199,
+25240,
+29990,
+50248,
+36349,
+2206,
+41485,
+36504,
+21110,
+13828,
+45866,
+63329,
+24054,
+24434,
+9678,
+55808,
+51734,
+53159,
+42155,
+9150,
+62394,
+60678,
+29208,
+5902,
+10526,
+1022,
+23192,
+36823,
+43827,
+6235,
+58318,
+15046,
+55773,
+34055,
+51438,
+20153,
+54316,
+54364,
+56639,
+16516,
+33942,
+64980,
+5541,
+51170,
+28609,
+60093,
+47152,
+17223,
+58358,
+27127,
+39372,
+259,
+38567,
+6188,
+52312,
+11692,
+61340,
+53652,
+28650,
+43565,
+30155,
+45833,
+45830,
+3942,
+47421,
+41758,
+6999,
+60604,
+39480,
+7182,
+8322,
+4946,
+3048,
+45677,
+2849,
+45371,
+43688,
+56176,
+46262,
+46119,
+63766,
+61354,
+56339,
+58690,
+64686,
+9793,
+18918,
+12250,
+45932,
+64505,
+56448,
+52608,
+59693,
+62790,
+40359,
+45911,
+63541,
+28811,
+20839,
+51679,
+55029,
+7960,
+64262,
+42955,
+45663,
+7764,
+45898,
+43976,
+54373,
+48301,
+14002,
+25686,
+17807,
+5893,
+52673,
+21507,
+36813,
+65376,
+52021,
+43896,
+10198,
+19060,
+7744,
+31716,
+28578,
+63653,
+13714,
+15641,
+47283,
+16291,
+19893,
+12101,
+353,
+20921,
+20672,
+15519,
+25139,
+34550,
+31547,
+52785,
+7099,
+3444,
+64739,
+38320,
+22846,
+28428,
+37893,
+45329,
+40675,
+11629,
+35125,
+17459,
+15064,
+64768,
+43607,
+21235,
+55505,
+57822,
+48130,
+12204,
+7830,
+41628,
+15371,
+30463,
+4409,
+18176,
+11690,
+52314,
+60963,
+21068,
+47002,
+2699,
+18764,
+33819,
+36961,
+62217,
+39572,
+3189,
+9924,
+12699,
+56626,
+10203,
+21639,
+55341,
+12292,
+14470,
+37518,
+60268,
+6806,
+46754,
+14583,
+46453,
+14954,
+46050,
+42819,
+16122,
+65099,
+40343,
+52505,
+39632,
+38252,
+63172,
+37122,
+24932,
+11188,
+45881,
+20414,
+63782,
+10052,
+64006,
+5441,
+45005,
+36608,
+59736,
+24035,
+65159,
+11822,
+15278,
+21229,
+6246,
+61478,
+10870,
+42147,
+25777,
+26978,
+46894,
+21583,
+46837,
+35123,
+11631,
+31468,
+64605,
+24287,
+17273,
+18618,
+23704,
+10916,
+22471,
+40530,
+17925,
+40235,
+40508,
+45603,
+17259,
+35211,
+31234,
+64726,
+46686,
+22520,
+47132,
+37730,
+25397,
+35995,
+58898,
+52555,
+51124,
+42269,
+47294,
+15726,
+22312,
+17624,
+64508,
+57314,
+32084,
+38163,
+56507,
+16652,
+48285,
+56956,
+20678,
+29605,
+3669,
+58270,
+41248,
+48266,
+42416,
+361,
+30597,
+44417,
+49274,
+47713,
+19850,
+51837,
+57008,
+18806,
+55394,
+28440,
+38725,
+29131,
+27854,
+9231,
+39194,
+27102,
+35566,
+22518,
+46688,
+28816,
+43919,
+4109,
+44378,
+12041,
+22429,
+17161,
+62357,
+14535,
+63703,
+45194,
+21590,
+47095,
+9632,
+28548,
+32587,
+52830,
+17471,
+965,
+31684,
+33329,
+12198,
+23102,
+49119,
+26516,
+6160,
+55241,
+8926,
+19632,
+26589,
+10656,
+11999,
+51737,
+37735,
+45491,
+44489,
+11790,
+35896,
+28407,
+760,
+7924,
+46708,
+43299,
+64589,
+19932,
+48280,
+28193,
+46763,
+62232,
+7122,
+2430,
+46532,
+42429,
+53016,
+30817,
+65370,
+63564,
+62979,
+1180,
+50111,
+5108,
+49824,
+58860,
+5842,
+2749,
+35105,
+14314,
+38609,
+2688,
+31100,
+40856,
+11385,
+9953,
+46214,
+3905,
+60689,
+46676,
+14203,
+1519,
+35917,
+23442,
+38664,
+41186,
+17299,
+49525,
+16067,
+36681,
+61477,
+6411,
+21230,
+43254,
+49717,
+40958,
+55456,
+28914,
+38131,
+54327,
+13513,
+58317,
+6601,
+43828,
+25708,
+8760,
+60541,
+4029,
+21516,
+50184,
+39920,
+43758,
+18403,
+27809,
+13912,
+40563,
+26790,
+31325,
+50636,
+56420,
+40311,
+57619,
+31772,
+791,
+49071,
+40987,
+28131,
+22405,
+44680,
+62845,
+44082,
+19188,
+4943,
+53674,
+2990,
+33635,
+6838,
+30369,
+15890,
+25113,
+59382,
+56028,
+57370,
+4391,
+41081,
+53524,
+52220,
+41535,
+52311,
+6577,
+38568,
+1515,
+59529,
+16628,
+60852,
+48734,
+30632,
+29831,
+17543,
+57443,
+50662,
+9179,
+20940,
+31707,
+37976,
+8293,
+44148,
+20001,
+18995,
+45062,
+42827,
+4581,
+7739,
+3498,
+17982,
+29280,
+55240,
+6309,
+26517,
+35493,
+12060,
+40423,
+59340,
+9510,
+34723,
+40151,
+60199,
+48199,
+58023,
+5767,
+36644,
+12892,
+63276,
+43702,
+54303,
+56734,
+21184,
+15509,
+34578,
+14788,
+8084,
+51620,
+46394,
+203,
+24738,
+55715,
+54901,
+11044,
+3935,
+13871,
+64938,
+34523,
+13086,
+14779,
+23205,
+53301,
+26339,
+64177,
+16976,
+53293,
+45052,
+23328,
+56245,
+40765,
+32538,
+14422,
+61762,
+32318,
+1459,
+37393,
+38483,
+23817,
+20536,
+60480,
+29853,
+12942,
+4705,
+42685,
+12626,
+22838,
+5697,
+3279,
+16645,
+35242,
+49263,
+29014,
+44900,
+17681,
+39404,
+41951,
+58291,
+8884,
+18611,
+39738,
+27958,
+30872,
+46418,
+8979,
+30456,
+56210,
+33669,
+1564,
+15621,
+13908,
+6885,
+3551,
+18913,
+32423,
+7991,
+30252,
+54778,
+53080,
+40446,
+14882,
+51874,
+49396,
+63861,
+34315,
+35067,
+39346,
+55496,
+58795,
+29148,
+22073,
+12343,
+36916,
+704,
+39788,
+21257,
+61040,
+14913,
+60735,
+36189,
+42907,
+23176,
+62207,
+35063,
+20757,
+53708,
+20829,
+50697,
+56992,
+50312,
+56924,
+14361,
+52510,
+32850,
+12314,
+2598,
+33236,
+3708,
+63612,
+5417,
+33084,
+44210,
+44435,
+44856,
+23221,
+13556,
+47492,
+25509,
+34871,
+47483,
+37389,
+39041,
+5287,
+45225,
+10065,
+64021,
+59352,
+48910,
+26636,
+3416,
+2456,
+14138,
+1081,
+58209,
+37935,
+21314,
+33140,
+37036,
+61001,
+37748,
+18489,
+42616,
+57323,
+50233,
+5941,
+8240,
+63359,
+41575,
+9120,
+34088,
+4567,
+7361,
+16817,
+13675,
+35870,
+9887,
+59606,
+4057,
+54669,
+19173,
+1481,
+21714,
+58349,
+19570,
+30551,
+14504,
+37787,
+1966,
+22091,
+15964,
+978,
+31598,
+62624,
+59754,
+22795,
+38229,
+65526,
+45877,
+24967,
+43009,
+22862,
+35196,
+7058,
+28457,
+17326,
+47481,
+34873,
+42053,
+56532,
+6923,
+8567,
+8392,
+8239,
+5990,
+50234,
+26782,
+10474,
+30489,
+17676,
+7542,
+61550,
+17939,
+55649,
+59559,
+61481,
+40600,
+54581,
+7132,
+25580,
+56666,
+34262,
+41814,
+21901,
+31800,
+37562,
+19753,
+46279,
+6845,
+853,
+24021,
+23881,
+4378,
+58010,
+12828,
+63796,
+36423,
+55032,
+23055,
+7690,
+10022,
+52938,
+10525,
+6607,
+29209,
+27470,
+2933,
+1989,
+26117,
+3657,
+12596,
+52672,
+6517,
+17808,
+21816,
+38866,
+1124,
+5710,
+11511,
+62720,
+47641,
+38371,
+62117,
+53136,
+17585,
+62784,
+10646,
+13346,
+17452,
+33605,
+62447,
+16189,
+56766,
+28226,
+41749,
+56862,
+65470,
+994,
+52233,
+35718,
+58419,
+46504,
+56058,
+57275,
+16415,
+39736,
+18613,
+19679,
+2775,
+30810,
+39658,
+4243,
+24477,
+16989,
+20341,
+43949,
+930,
+41252,
+11657,
+13150,
+12928,
+58999,
+2748,
+6271,
+58861,
+9348,
+41136,
+5714,
+16606,
+50680,
+64616,
+28994,
+2528,
+24464,
+29717,
+56128,
+26298,
+16969,
+63580,
+62556,
+19948,
+2892,
+8700,
+60145,
+51655,
+45189,
+20802,
+56424,
+40041,
+62934,
+4711,
+15815,
+46912,
+22744,
+9936,
+52486,
+61656,
+30523,
+10936,
+2314,
+48354,
+16452,
+27884,
+28928,
+59120,
+41503,
+12635,
+26416,
+19513,
+38049,
+27685,
+24172,
+22219,
+51065,
+28021,
+44744,
+20423,
+48881,
+57066,
+37118,
+54752,
+2912,
+59576,
+42743,
+8049,
+64097,
+22545,
+42455,
+40409,
+57742,
+19837,
+63982,
+31673,
+41886,
+20305,
+36494,
+61027,
+36643,
+6148,
+58024,
+51361,
+30756,
+36834,
+55562,
+53888,
+64092,
+31181,
+55851,
+46469,
+59901,
+11422,
+12973,
+19161,
+46498,
+15578,
+11090,
+8185,
+21401,
+5565,
+57496,
+23534,
+44007,
+15109,
+56486,
+19749,
+56323,
+49561,
+49444,
+61950,
+28164,
+23975,
+53776,
+34146,
+13503,
+56846,
+32860,
+44637,
+58592,
+31401,
+30983,
+21155,
+62022,
+18185,
+38312,
+31159,
+1753,
+55487,
+65388,
+4872,
+54725,
+16605,
+5838,
+41137,
+45355,
+11510,
+5888,
+1125,
+62104,
+18925,
+53948,
+53452,
+1666,
+44151,
+28217,
+4012,
+22685,
+21838,
+3278,
+6097,
+22839,
+1473,
+14629,
+19904,
+16177,
+34398,
+20882,
+46771,
+31720,
+22806,
+23248,
+11505,
+42350,
+33397,
+7601,
+17062,
+20688,
+44026,
+5598,
+64625,
+46474,
+43572,
+55569,
+12777,
+20255,
+16052,
+21418,
+52277,
+60091,
+28611,
+41004,
+26585,
+34076,
+17556,
+37792,
+301,
+21919,
+28802,
+18197,
+39540,
+13360,
+34165,
+19369,
+42304,
+41523,
+54763,
+61164,
+60966,
+57751,
+64713,
+16007,
+21687,
+30899,
+32182,
+62180,
+64503,
+45934,
+22512,
+43114,
+19067,
+59781,
+54116,
+62594,
+54333,
+41717,
+18389,
+25528,
+58237,
+22556,
+53960,
+8329,
+40464,
+29191,
+65199,
+27616,
+60780,
+40500,
+1213,
+31042,
+53251,
+10281,
+60203,
+36831,
+22266,
+34999,
+25229,
+22348,
+52474,
+6930,
+48168,
+41022,
+53061,
+58719,
+15931,
+54271,
+46991,
+26601,
+64624,
+5678,
+44027,
+27838,
+35937,
+12852,
+46320,
+31702,
+61786,
+4842,
+54022,
+9535,
+22440,
+17798,
+49672,
+6772,
+7771,
+52493,
+16487,
+55589,
+41338,
+25484,
+13561,
+56052,
+45823,
+7325,
+38960,
+28052,
+46150,
+45217,
+62948,
+45963,
+48726,
+57495,
+5747,
+21402,
+15645,
+4018,
+36251,
+64983,
+20148,
+22675,
+62223,
+58377,
+7645,
+65478,
+44069,
+48173,
+13778,
+35697,
+37227,
+9840,
+10976,
+27829,
+11500,
+7010,
+51479,
+51169,
+6588,
+64981,
+36253,
+46425,
+28975,
+56457,
+17145,
+19789,
+2594,
+2476,
+37360,
+4539,
+54466,
+24197,
+40055,
+31796,
+38824,
+61610,
+63852,
+12260,
+37214,
+49782,
+29117,
+51059,
+19553,
+49628,
+2650,
+36144,
+3388,
+24077,
+47473,
+50586,
+51190,
+56744,
+24801,
+62597,
+38846,
+27446,
+14053,
+41583,
+55327,
+17552,
+28627,
+3135,
+52184,
+35690,
+25857,
+29500,
+39253,
+45358,
+39876,
+32864,
+63161,
+11094,
+52088,
+39100,
+17600,
+17412,
+13411,
+55985,
+56055,
+49667,
+9318,
+53681,
+49766,
+56549,
+36186,
+1736,
+61765,
+53668,
+34763,
+47721,
+58335,
+11614,
+60646,
+25023,
+16765,
+8456,
+6898,
+37448,
+52432,
+42407,
+9040,
+12788,
+33387,
+49405,
+38301,
+23564,
+649,
+2320,
+38798,
+15308,
+3079,
+12619,
+13259,
+59869,
+36927,
+58952,
+18386,
+45004,
+6420,
+64007,
+37763,
+45013,
+31762,
+44909,
+12818,
+53585,
+17105,
+53098,
+40220,
+10778,
+34706,
+38407,
+28246,
+38397,
+25345,
+30944,
+36678,
+58055,
+46484,
+55779,
+54979,
+33083,
+6025,
+63613,
+13531,
+50103,
+58769,
+27961,
+9641,
+24749,
+33029,
+28321,
+15757,
+54105,
+53630,
+47940,
+55627,
+36675,
+13469,
+42150,
+2100,
+35998,
+42137,
+56270,
+1069,
+32647,
+56836,
+59715,
+13883,
+44660,
+10860,
+7067,
+54235,
+10428,
+20726,
+62433,
+27162,
+10728,
+55282,
+5031,
+24916,
+48318,
+16174,
+3133,
+28629,
+19305,
+4818,
+37441,
+52188,
+58129,
+30907,
+27653,
+43325,
+41440,
+57267,
+10764,
+54050,
+25215,
+2665,
+63006,
+4586,
+31105,
+34573,
+44503,
+26533,
+49318,
+47503,
+50463,
+64251,
+32744,
+51594,
+53932,
+51074,
+26688,
+1079,
+14140,
+64384,
+7226,
+3583,
+37469,
+25385,
+59386,
+17339,
+65058,
+55255,
+59922,
+28707,
+64271,
+9171,
+35449,
+42564,
+12608,
+47944,
+28191,
+48282,
+21382,
+11758,
+30281,
+739,
+38349,
+34475,
+53275,
+28325,
+28318,
+43194,
+28082,
+41508,
+20542,
+37180,
+45086,
+25405,
+60660,
+61116,
+17008,
+58903,
+18596,
+48989,
+50840,
+6775,
+35526,
+39107,
+57583,
+9799,
+24564,
+26648,
+63123,
+33109,
+4223,
+33778,
+49097,
+51473,
+45224,
+6012,
+39042,
+61172,
+34046,
+63180,
+33910,
+31149,
+40129,
+54699,
+50649,
+32961,
+8807,
+40664,
+51433,
+8203,
+9659,
+9723,
+28018,
+59094,
+58732,
+25844,
+31972,
+63396,
+49215,
+13212,
+40341,
+65101,
+14615,
+45598,
+54556,
+49106,
+54078,
+10451,
+52477,
+58226,
+19429,
+60320,
+15267,
+47308,
+43171,
+25955,
+862,
+48831,
+7561,
+65512,
+19116,
+1338,
+46939,
+16550,
+50210,
+11876,
+61394,
+24700,
+20773,
+32637,
+5023,
+22138,
+1416,
+43754,
+8236,
+62252,
+39415,
+42621,
+52402,
+63567,
+61910,
+41662,
+9705,
+22101,
+46107,
+60286,
+50271,
+16689,
+10772,
+4375,
+43268,
+20588,
+10101,
+38751,
+45516,
+18225,
+62629,
+33908,
+63182,
+44741,
+63937,
+56603,
+8482,
+44725,
+49386,
+7415,
+35783,
+164,
+38926,
+33267,
+25258,
+26235,
+34006,
+9182,
+35535,
+34291,
+9895,
+61783,
+27522,
+60378,
+65322,
+33382,
+42928,
+38773,
+29984,
+33623,
+42556,
+26886,
+65499,
+19485,
+22720,
+51038,
+38194,
+28781,
+25984,
+1484,
+4326,
+43243,
+61387,
+40391,
+61360,
+4254,
+38014,
+60354,
+216,
+43793,
+50856,
+1818,
+34625,
+14308,
+34613,
+21095,
+62012,
+6975,
+52921,
+20844,
+50802,
+24941,
+47815,
+57853,
+46712,
+54867,
+40967,
+21106,
+4171,
+4478,
+55954,
+35136,
+24420,
+39861,
+32296,
+43785,
+61615,
+32711,
+44079,
+55477,
+34584,
+31624,
+25849,
+46962,
+28239,
+37766,
+39335,
+55090,
+986,
+333,
+29630,
+30853,
+32179,
+47887,
+1141,
+29441,
+1321,
+49823,
+6274,
+50112,
+64446,
+54535,
+14394,
+9234,
+43276,
+47717,
+63905,
+13020,
+52643,
+2263,
+22030,
+48918,
+53482,
+63684,
+21481,
+15319,
+57979,
+15750,
+20622,
+42257,
+7332,
+2423,
+53020,
+42200,
+25521,
+26545,
+43257,
+29291,
+41493,
+26695,
+36474,
+47432,
+7556,
+49789,
+27923,
+8659,
+60793,
+27125,
+58360,
+57492,
+38383,
+1915,
+64204,
+15106,
+62857,
+3328,
+30476,
+65252,
+45000,
+59433,
+35685,
+63696,
+47266,
+15953,
+39605,
+11963,
+41178,
+32113,
+33145,
+4428,
+46198,
+39595,
+16852,
+26810,
+36408,
+49156,
+7711,
+2185,
+9145,
+22236,
+950,
+61541,
+690,
+62503,
+24915,
+5380,
+55283,
+20890,
+41671,
+13037,
+12844,
+54099,
+22137,
+5232,
+32638,
+52380,
+13486,
+22825,
+56669,
+48738,
+52589,
+43883,
+22338,
+57523,
+38875,
+11810,
+58774,
+42287,
+54034,
+57749,
+60968,
+58178,
+28512,
+56983,
+24881,
+58408,
+45522,
+12035,
+23747,
+56776,
+59306,
+61509,
+25682,
+57084,
+3374,
+1904,
+49816,
+28046,
+57404,
+23300,
+13946,
+7073,
+54189,
+25160,
+32216,
+39038,
+39388,
+17072,
+11467,
+26889,
+22772,
+62876,
+54377,
+65168,
+39715,
+18141,
+14281,
+21683,
+11622,
+46880,
+47998,
+39309,
+22563,
+32511,
+12904,
+24545,
+21577,
+34361,
+38003,
+48495,
+38944,
+54731,
+51448,
+9077,
+2766,
+39579,
+34066,
+24631,
+58027,
+3047,
+6559,
+8323,
+53673,
+6205,
+19189,
+7388,
+26175,
+37499,
+19178,
+29337,
+8617,
+52496,
+58537,
+58974,
+26058,
+42823,
+14131,
+38200,
+57039,
+13275,
+39261,
+37233,
+8638,
+9332,
+27043,
+52086,
+11096,
+33428,
+10221,
+47490,
+13558,
+41709,
+39837,
+2161,
+3002,
+53273,
+34477,
+24805,
+25224,
+7920,
+25261,
+9727,
+13805,
+37507,
+53812,
+4132,
+49126,
+49631,
+55863,
+37834,
+63837,
+58749,
+65229,
+17506,
+53922,
+60070,
+40515,
+6984,
+7629,
+48385,
+63914,
+3193,
+25670,
+8630,
+52341,
+32171,
+36972,
+54697,
+40131,
+60229,
+4422,
+26524,
+24889,
+54724,
+5717,
+65389,
+44584,
+20217,
+39648,
+20737,
+64473,
+46179,
+27608,
+50524,
+56034,
+19320,
+3142,
+58928,
+36330,
+36297,
+56913,
+35745,
+20482,
+44200,
+17789,
+64186,
+28012,
+61631,
+59278,
+18862,
+35311,
+18978,
+36435,
+54021,
+5590,
+61787,
+49735,
+13232,
+33500,
+55741,
+28852,
+47821,
+18506,
+50024,
+9562,
+25721,
+19444,
+16408,
+62890,
+57092,
+48983,
+27478,
+48161,
+36879,
+52802,
+49496,
+55924,
+37440,
+5373,
+19306,
+12642,
+50946,
+45266,
+64720,
+10201,
+56628,
+10941,
+50151,
+61734,
+32080,
+56196,
+43414,
+4270,
+50623,
+59491,
+29349,
+63803,
+62437,
+15312,
+24824,
+47185,
+28675,
+54040,
+22069,
+56579,
+60804,
+51896,
+45776,
+42610,
+18099,
+55664,
+28461,
+12069,
+25157,
+26382,
+9664,
+7582,
+20968,
+43385,
+12951,
+54573,
+39432,
+37738,
+357,
+19955,
+39781,
+61319,
+38341,
+44878,
+42509,
+3644,
+26036,
+37377,
+39036,
+32218,
+3579,
+54426,
+28947,
+37920,
+59916,
+25038,
+12308,
+16336,
+51321,
+30024,
+26899,
+27222,
+49170,
+42196,
+49384,
+44727,
+56877,
+60467,
+38413,
+39430,
+54575,
+38064,
+16526,
+38910,
+44978,
+14184,
+35336,
+8957,
+60746,
+13642,
+51809,
+29554,
+15674,
+14208,
+21573,
+57175,
+38809,
+45296,
+57919,
+37876,
+38828,
+1569,
+57732,
+30796,
+36235,
+15745,
+8729,
+30127,
+3240,
+15814,
+5815,
+62935,
+3201,
+43167,
+23419,
+42684,
+6101,
+12943,
+24066,
+4279,
+40931,
+28906,
+18424,
+32629,
+22241,
+38309,
+48898,
+34416,
+20654,
+23871,
+25832,
+53282,
+33069,
+49670,
+17800,
+48908,
+59354,
+16804,
+57672,
+47662,
+43696,
+38552,
+33826,
+33497,
+12219,
+16160,
+39925,
+11680,
+35903,
+11685,
+1375,
+44248,
+8815,
+15233,
+13832,
+9846,
+19921,
+4623,
+63738,
+33134,
+23158,
+24935,
+10958,
+605,
+15466,
+40102,
+34465,
+51948,
+27874,
+22273,
+32846,
+40492,
+21216,
+32839,
+9167,
+53307,
+8790,
+27674,
+3430,
+52167,
+30615,
+2726,
+47129,
+325,
+17893,
+18222,
+17929,
+46798,
+63401,
+55930,
+7458,
+56945,
+32903,
+2903,
+61047,
+38237,
+29729,
+63737,
+4664,
+19922,
+21146,
+8412,
+31527,
+13922,
+10206,
+12849,
+55710,
+31689,
+57851,
+47817,
+29535,
+29186,
+63333,
+14578,
+54057,
+18796,
+64370,
+47046,
+22923,
+46981,
+45695,
+46631,
+52441,
+45454,
+35798,
+62186,
+17158,
+4294,
+41551,
+21251,
+10378,
+41878,
+35789,
+46353,
+31104,
+5359,
+63007,
+41075,
+143,
+7738,
+6166,
+42828,
+229,
+15103,
+45548,
+51344,
+13222,
+59075,
+44470,
+4372,
+59888,
+7787,
+60531,
+7360,
+5984,
+34089,
+25095,
+1278,
+52984,
+64948,
+27531,
+11972,
+29800,
+50454,
+23931,
+50994,
+43135,
+48426,
+47026,
+58871,
+40336,
+37781,
+49675,
+63534,
+51167,
+51481,
+2062,
+25860,
+779,
+34702,
+18329,
+54465,
+5530,
+37361,
+28500,
+59541,
+27790,
+159,
+13518,
+16847,
+3428,
+27676,
+39273,
+43783,
+32298,
+35317,
+26041,
+26322,
+57785,
+29696,
+22469,
+10918,
+11399,
+44112,
+38028,
+43493,
+52807,
+41741,
+7319,
+24649,
+29042,
+59768,
+28895,
+2108,
+55998,
+13599,
+64926,
+31910,
+46966,
+51693,
+17196,
+50580,
+33475,
+44279,
+59489,
+50625,
+26069,
+54585,
+1940,
+26101,
+62633,
+60910,
+27301,
+27935,
+50376,
+20263,
+25981,
+49942,
+49076,
+7355,
+31050,
+16867,
+55953,
+5137,
+4172,
+28537,
+43540,
+41997,
+1304,
+38821,
+13339,
+52239,
+31456,
+62090,
+55135,
+53900,
+37326,
+53102,
+12614,
+44695,
+2806,
+8126,
+43902,
+55114,
+55244,
+40137,
+45133,
+43657,
+53086,
+23568,
+49046,
+18748,
+52353,
+32076,
+45730,
+50929,
+58799,
+58709,
+23837,
+12172,
+7113,
+28237,
+46964,
+31912,
+22983,
+14375,
+62552,
+55501,
+25517,
+64842,
+7839,
+47089,
+46197,
+5047,
+33146,
+23203,
+14781,
+22058,
+26523,
+4876,
+60230,
+48530,
+23744,
+9835,
+37880,
+29459,
+16670,
+20742,
+7470,
+36081,
+34960,
+18175,
+6466,
+30464,
+22961,
+25480,
+33743,
+15789,
+387,
+22466,
+23171,
+21940,
+39644,
+18314,
+28692,
+15937,
+9290,
+24774,
+37162,
+41080,
+6194,
+57371,
+22652,
+22000,
+27144,
+33157,
+9087,
+12931,
+34974,
+14941,
+18041,
+12464,
+58009,
+5913,
+23882,
+43267,
+5213,
+10773,
+59887,
+4572,
+44471,
+35288,
+32776,
+3856,
+34817,
+26494,
+60252,
+31492,
+23508,
+18375,
+33843,
+19135,
+52171,
+28648,
+53654,
+62543,
+47298,
+44750,
+21936,
+30856,
+241,
+11646,
+64452,
+48791,
+50732,
+38529,
+65356,
+46241,
+29592,
+32527,
+43329,
+15297,
+725,
+47574,
+136,
+20571,
+63617,
+57205,
+21886,
+33298,
+64735,
+56047,
+55848,
+14552,
+43242,
+5166,
+1485,
+34269,
+41008,
+35225,
+17882,
+19919,
+9848,
+31615,
+14331,
+50489,
+9766,
+18306,
+40085,
+55825,
+6689,
+27264,
+39453,
+58676,
+30448,
+36817,
+31406,
+22673,
+20150,
+37552,
+31999,
+19402,
+18004,
+50243,
+44398,
+56277,
+41550,
+4594,
+17159,
+22431,
+34502,
+24183,
+31569,
+45989,
+51121,
+29221,
+42947,
+16782,
+13848,
+30136,
+39114,
+40930,
+4702,
+24067,
+55531,
+17733,
+8401,
+9369,
+29678,
+30452,
+50622,
+4804,
+43415,
+19916,
+19721,
+64139,
+27398,
+27927,
+34621,
+938,
+47880,
+27584,
+17019,
+58657,
+18274,
+31552,
+38013,
+5161,
+61361,
+21224,
+36430,
+11149,
+55681,
+23589,
+32375,
+31225,
+51071,
+24476,
+5854,
+39659,
+10400,
+11639,
+55364,
+61872,
+12003,
+33310,
+56840,
+8374,
+19353,
+44987,
+28625,
+17554,
+34078,
+10928,
+14171,
+58791,
+20522,
+33777,
+5292,
+33110,
+8229,
+50830,
+25755,
+60767,
+27197,
+2254,
+54000,
+14448,
+20176,
+13708,
+21026,
+47526,
+2974,
+34653,
+23741,
+9240,
+3008,
+29448,
+12755,
+50767,
+18736,
+35468,
+26939,
+53844,
+58963,
+11886,
+828,
+64115,
+27212,
+9521,
+34559,
+10683,
+3614,
+30875,
+8468,
+32518,
+52273,
+1770,
+58988,
+9199,
+56085,
+36892,
+15557,
+25145,
+60375,
+61284,
+44314,
+3784,
+28536,
+4477,
+5138,
+21107,
+57394,
+61779,
+12299,
+56869,
+15389,
+42385,
+25456,
+41807,
+13120,
+43371,
+8005,
+53038,
+24736,
+205,
+42950,
+58043,
+42876,
+9739,
+22293,
+60373,
+25147,
+28076,
+22757,
+10417,
+52145,
+53660,
+60301,
+40838,
+53414,
+839,
+46267,
+586,
+44325,
+14974,
+54807,
+10257,
+49125,
+4901,
+53813,
+20518,
+26364,
+14749,
+10989,
+45698,
+1557,
+35041,
+36798,
+25741,
+54532,
+11978,
+16786,
+10456,
+7434,
+14792,
+36121,
+685,
+23216,
+2056,
+1371,
+44377,
+6332,
+43920,
+53553,
+7007,
+7791,
+27866,
+49005,
+19316,
+37331,
+39530,
+46015,
+54966,
+34952,
+16,
+34697,
+31776,
+42800,
+22182,
+63919,
+4054,
+24939,
+50804,
+46102,
+38671,
+51467,
+19016,
+56757,
+2039,
+61458,
+63497,
+61456,
+2041,
+38033,
+28379,
+48618,
+27106,
+14031,
+23321,
+7518,
+30804,
+11049,
+19413,
+9443,
+18939,
+8835,
+37687,
+8920,
+41126,
+28855,
+59290,
+59127,
+54668,
+5977,
+59607,
+24938,
+4090,
+63920,
+18778,
+28360,
+56200,
+25153,
+38095,
+53181,
+44755,
+11471,
+14083,
+43151,
+12274,
+53114,
+18704,
+24047,
+17707,
+41056,
+341,
+6708,
+1056,
+43713,
+26023,
+39914,
+21515,
+6230,
+60542,
+39856,
+15025,
+62655,
+19951,
+14352,
+54310,
+54405,
+18471,
+36250,
+5562,
+15646,
+49626,
+19555,
+1747,
+22684,
+5701,
+28218,
+38456,
+39708,
+40699,
+55511,
+63263,
+28113,
+39954,
+40537,
+21207,
+12711,
+24134,
+63849,
+55554,
+64586,
+60502,
+64848,
+42581,
+50355,
+62270,
+23860,
+51132,
+35504,
+57994,
+59603,
+47599,
+18551,
+49235,
+20469,
+32571,
+15343,
+13058,
+7313,
+2949,
+54941,
+1467,
+47368,
+24094,
+7489,
+32752,
+42283,
+31745,
+49322,
+10587,
+64331,
+38453,
+45504,
+19261,
+23549,
+12588,
+52466,
+9814,
+53105,
+12965,
+55434,
+26788,
+40565,
+56570,
+41000,
+49103,
+34618,
+19462,
+10389,
+83,
+3902,
+62398,
+63178,
+34048,
+47420,
+6567,
+45831,
+30157,
+47751,
+1814,
+45245,
+13870,
+6129,
+11045,
+31927,
+35752,
+29009,
+18894,
+63502,
+26712,
+60013,
+1686,
+9985,
+28543,
+21751,
+60407,
+32827,
+28639,
+64149,
+26178,
+62609,
+57052,
+12940,
+29855,
+25642,
+1491,
+20075,
+54860,
+35845,
+53144,
+37524,
+60688,
+6260,
+46215,
+62397,
+3947,
+84,
+33155,
+27146,
+50254,
+25865,
+55730,
+14016,
+55523,
+54241,
+8571,
+47380,
+31387,
+16498,
+36696,
+45102,
+39447,
+13546,
+28297,
+48254,
+13050,
+59728,
+35015,
+52139,
+27293,
+19203,
+56491,
+35483,
+14904,
+38158,
+13016,
+43934,
+6994,
+9164,
+43392,
+2271,
+51721,
+390,
+24305,
+48419,
+560,
+26870,
+1579,
+48656,
+20062,
+34816,
+4368,
+32777,
+44842,
+63717,
+30914,
+64601,
+62524,
+64907,
+62829,
+30869,
+58772,
+11812,
+21499,
+56311,
+55251,
+65264,
+30601,
+21766,
+36935,
+17661,
+52228,
+17055,
+23367,
+17953,
+64870,
+30211,
+35301,
+49448,
+19192,
+21800,
+41573,
+63361,
+42246,
+65417,
+51219,
+22486,
+49290,
+50487,
+14333,
+58154,
+27496,
+31231,
+13970,
+27002,
+32545,
+18263,
+57676,
+56091,
+35264,
+48632,
+34456,
+14692,
+15385,
+60729,
+33458,
+34122,
+1259,
+30845,
+41914,
+3383,
+54712,
+18789,
+51597,
+62496,
+47580,
+9560,
+50026,
+30961,
+44170,
+63443,
+502,
+28535,
+4174,
+44315,
+51934,
+65485,
+32127,
+55357,
+10148,
+44958,
+51114,
+59426,
+33220,
+63800,
+60064,
+11870,
+18479,
+2104,
+29516,
+34256,
+25912,
+39827,
+56779,
+39883,
+48273,
+22897,
+3364,
+8142,
+61369,
+53239,
+28354,
+55641,
+60352,
+38016,
+33834,
+59946,
+60696,
+15803,
+41424,
+57229,
+34588,
+65502,
+48190,
+7599,
+33399,
+54693,
+33522,
+62715,
+6715,
+16678,
+55310,
+8826,
+37718,
+62096,
+8517,
+41279,
+21137,
+9262,
+47553,
+36259,
+15496,
+36472,
+26697,
+30084,
+44425,
+51711,
+50615,
+32372,
+53110,
+8234,
+43756,
+39922,
+3688,
+2577,
+36176,
+33678,
+41389,
+63611,
+6027,
+33237,
+30222,
+34900,
+54797,
+13964,
+33335,
+42499,
+20777,
+34744,
+16464,
+24552,
+10515,
+27279,
+9882,
+33363,
+36869,
+10547,
+58486,
+2576,
+3714,
+39923,
+16162,
+32066,
+47694,
+12207,
+30241,
+33632,
+9136,
+8515,
+62098,
+32617,
+54216,
+21810,
+38441,
+15220,
+19579,
+63841,
+58269,
+6359,
+29606,
+22358,
+10172,
+61299,
+53535,
+12381,
+26111,
+48835,
+49296,
+17947,
+12595,
+5896,
+26118,
+26681,
+51314,
+1812,
+47753,
+52864,
+54279,
+62825,
+14965,
+14430,
+54734,
+26035,
+4766,
+42510,
+10347,
+65519,
+30842,
+1873,
+17312,
+28615,
+35346,
+22050,
+58197,
+48580,
+51697,
+2563,
+22622,
+27739,
+1969,
+60315,
+23876,
+40190,
+34595,
+53363,
+58426,
+40162,
+16167,
+35838,
+42369,
+7755,
+46416,
+30874,
+4189,
+10684,
+16856,
+22251,
+53065,
+35447,
+9173,
+15553,
+16775,
+32414,
+48389,
+27539,
+50672,
+54599,
+63225,
+25694,
+30437,
+19222,
+16202,
+48182,
+53593,
+38578,
+43369,
+13122,
+31089,
+11285,
+16092,
+59800,
+37374,
+31955,
+37468,
+5341,
+7227,
+34864,
+54425,
+4761,
+32219,
+20612,
+16146,
+42127,
+36766,
+49836,
+13889,
+62674,
+7031,
+54960,
+42926,
+33384,
+11854,
+15773,
+40032,
+36015,
+31039,
+12570,
+23435,
+53807,
+58777,
+56472,
+24346,
+50752,
+46525,
+31837,
+18912,
+6072,
+6886,
+40035,
+12653,
+63948,
+18944,
+32408,
+42477,
+38549,
+62262,
+18397,
+2092,
+65037,
+26137,
+42839,
+27894,
+21376,
+42931,
+21722,
+53606,
+57556,
+39972,
+11904,
+39103,
+25531,
+44441,
+60387,
+58563,
+55566,
+29233,
+41526,
+27425,
+31828,
+16673,
+29405,
+44381,
+59813,
+47353,
+44251,
+22561,
+39311,
+29557,
+3063,
+27743,
+65238,
+2860,
+11088,
+15580,
+41571,
+21802,
+64002,
+55669,
+17981,
+6164,
+7740,
+44110,
+11401,
+2007,
+33716,
+9778,
+14812,
+37050,
+51543,
+40303,
+58489,
+94,
+50361,
+26354,
+15053,
+62582,
+21011,
+47326,
+11039,
+24892,
+24296,
+16158,
+12221,
+30882,
+41798,
+2737,
+47562,
+50862,
+12091,
+63137,
+34914,
+34319,
+11216,
+20906,
+11847,
+17132,
+47529,
+51009,
+63756,
+18668,
+26484,
+41283,
+64930,
+50073,
+62728,
+44976,
+38912,
+40329,
+10926,
+34080,
+61069,
+10403,
+64738,
+6489,
+7100,
+27818,
+61804,
+57021,
+61859,
+32921,
+30528,
+19169,
+57104,
+64411,
+59829,
+46613,
+52166,
+4643,
+27675,
+4531,
+16848,
+31659,
+40145,
+18394,
+1704,
+27023,
+27165,
+59654,
+12402,
+50447,
+2455,
+6005,
+26637,
+56649,
+35428,
+1479,
+19175,
+13511,
+54329,
+34480,
+14754,
+41966,
+7083,
+31132,
+12884,
+13851,
+19347,
+63089,
+617,
+36163,
+19582,
+14686,
+58742,
+55747,
+35096,
+30933,
+52820,
+36403,
+24076,
+5513,
+36145,
+11168,
+10029,
+54711,
+3797,
+41915,
+46502,
+58421,
+39489,
+20701,
+40114,
+30769,
+1903,
+4992,
+57085,
+1963,
+53425,
+30007,
+56647,
+26639,
+15630,
+15401,
+8141,
+3760,
+22898,
+40215,
+26207,
+44335,
+63742,
+33354,
+59895,
+1035,
+18030,
+8070,
+27325,
+29738,
+60941,
+48380,
+8493,
+48957,
+47366,
+1469,
+54526,
+46115,
+10155,
+53516,
+27737,
+22624,
+31424,
+53855,
+29636,
+42768,
+12341,
+22075,
+56433,
+2852,
+42360,
+59495,
+30475,
+5061,
+62858,
+42069,
+48251,
+20919,
+355,
+37740,
+28575,
+59082,
+25966,
+7565,
+54927,
+48797,
+42861,
+59980,
+7161,
+17433,
+42220,
+26195,
+48108,
+19356,
+37931,
+18773,
+59251,
+58416,
+21052,
+46384,
+22117,
+51099,
+46009,
+37339,
+43095,
+47741,
+2347,
+11082,
+57218,
+28767,
+51179,
+13207,
+11597,
+39087,
+57143,
+57300,
+60310,
+26123,
+29305,
+13998,
+13423,
+16644,
+6096,
+5698,
+21839,
+26987,
+61995,
+47141,
+63835,
+37836,
+26327,
+47359,
+53849,
+15181,
+61545,
+41678,
+44521,
+62987,
+25789,
+26259,
+43209,
+28004,
+35614,
+39809,
+8089,
+38505,
+20396,
+15488,
+57156,
+27428,
+29029,
+39786,
+706,
+41344,
+50278,
+63094,
+14198,
+63941,
+45692,
+10249,
+15813,
+4713,
+30128,
+21677,
+45473,
+30723,
+15660,
+30742,
+8818,
+23380,
+56389,
+28467,
+37528,
+8385,
+13607,
+19848,
+47715,
+43278,
+24584,
+9499,
+13612,
+29166,
+29908,
+20101,
+64661,
+39473,
+10967,
+8707,
+22376,
+40828,
+20104,
+10882,
+7679,
+48070,
+23644,
+42458,
+57419,
+18757,
+53956,
+43166,
+4709,
+62936,
+48574,
+37236,
+21832,
+15523,
+14933,
+25669,
+4885,
+63915,
+45040,
+9923,
+6453,
+39573,
+19001,
+31452,
+35248,
+49091,
+10789,
+33304,
+48375,
+59210,
+51003,
+30995,
+722,
+23297,
+29889,
+38137,
+37612,
+56880,
+58262,
+18462,
+54204,
+45036,
+57632,
+8218,
+24082,
+34322,
+29070,
+24851,
+28653,
+57096,
+40123,
+1930,
+1121,
+46204,
+53032,
+44945,
+39264,
+21312,
+37937,
+25445,
+23580,
+56814,
+15848,
+21061,
+39864,
+56386,
+58927,
+4860,
+19321,
+61634,
+61591,
+2360,
+10216,
+52183,
+5498,
+28628,
+5376,
+16175,
+19906,
+7810,
+27815,
+3038,
+46884,
+16835,
+33513,
+7825,
+12822,
+20971,
+16379,
+57828,
+43117,
+51097,
+22119,
+17520,
+8058,
+34831,
+21979,
+55618,
+15589,
+62650,
+47303,
+49701,
+1582,
+16437,
+44810,
+60153,
+38376,
+41811,
+1733,
+60738,
+50226,
+46519,
+8782,
+41511,
+17533,
+13154,
+3030,
+33256,
+31931,
+59662,
+49349,
+18358,
+63414,
+16586,
+40002,
+24587,
+9162,
+6996,
+58560,
+12618,
+5449,
+15309,
+19020,
+14765,
+30861,
+11534,
+25784,
+21867,
+47931,
+31018,
+29576,
+37703,
+406,
+45893,
+22088,
+27742,
+3509,
+29558,
+41065,
+62817,
+41049,
+46656,
+15993,
+28844,
+64636,
+9249,
+47634,
+55423,
+25884,
+19833,
+45676,
+6558,
+4947,
+58028,
+35362,
+18365,
+17768,
+13939,
+61744,
+17958,
+46883,
+3128,
+27816,
+7102,
+64846,
+60504,
+40623,
+29764,
+33255,
+3093,
+13155,
+52053,
+44146,
+8295,
+14039,
+56543,
+55933,
+20277,
+58877,
+41514,
+41658,
+48447,
+48311,
+39483,
+24628,
+935,
+61687,
+631,
+23333,
+46442,
+29447,
+4205,
+9241,
+10574,
+20130,
+53167,
+53272,
+4912,
+2162,
+10333,
+743,
+56465,
+36978,
+49109,
+11882,
+61181,
+18240,
+9134,
+33634,
+6203,
+53675,
+43801,
+2962,
+51161,
+61102,
+10037,
+44182,
+1956,
+59711,
+45590,
+65515,
+49934,
+45724,
+14116,
+34652,
+4209,
+47527,
+17134,
+16478,
+34931,
+20948,
+35352,
+57563,
+45761,
+57425,
+43625,
+51160,
+2987,
+43802,
+9587,
+27021,
+1706,
+47762,
+50483,
+62055,
+44527,
+15380,
+40339,
+13214,
+54940,
+3978,
+7314,
+39807,
+35616,
+12195,
+11420,
+59903,
+11205,
+40735,
+60031,
+26692,
+1673,
+32378,
+56452,
+48187,
+1988,
+5899,
+27471,
+49013,
+13935,
+39358,
+61620,
+8486,
+33978,
+22361,
+16967,
+26300,
+52461,
+12542,
+18696,
+11428,
+35823,
+57988,
+48928,
+46719,
+33938,
+59575,
+5784,
+54753,
+24841,
+47311,
+60331,
+31630,
+45488,
+39435,
+61046,
+4628,
+32904,
+57856,
+17469,
+52832,
+10561,
+48344,
+36593,
+23951,
+32225,
+8699,
+5824,
+19949,
+62657,
+19373,
+49947,
+62427,
+18235,
+7451,
+7749,
+50511,
+61512,
+45587,
+62647,
+46165,
+54265,
+25946,
+27459,
+22180,
+42802,
+49455,
+22477,
+47435,
+47109,
+33351,
+56328,
+42015,
+27006,
+183,
+51647,
+61966,
+58507,
+11087,
+3506,
+65239,
+43913,
+11676,
+25173,
+11776,
+58671,
+42359,
+3332,
+56434,
+45370,
+6556,
+45678,
+29258,
+32354,
+41764,
+63440,
+46496,
+19163,
+15202,
+41376,
+11196,
+27411,
+45601,
+40510,
+42836,
+54645,
+16471,
+2615,
+10047,
+43838,
+36394,
+26720,
+38738,
+17509,
+56791,
+49965,
+32014,
+61798,
+58823,
+26331,
+20691,
+54025,
+59651,
+17897,
+51056,
+45112,
+16658,
+58382,
+55232,
+15463,
+62084,
+20562,
+8125,
+4461,
+44696,
+13860,
+16507,
+51901,
+43005,
+53202,
+11356,
+55676,
+25704,
+63404,
+9684,
+19070,
+35596,
+13544,
+39449,
+45641,
+50968,
+46567,
+14650,
+56104,
+24897,
+26389,
+49453,
+42804,
+8036,
+64429,
+45350,
+51217,
+65419,
+30809,
+5857,
+19680,
+55658,
+14020,
+36801,
+64425,
+27313,
+47377,
+39578,
+4952,
+9078,
+48394,
+63592,
+57683,
+24520,
+34027,
+36581,
+33719,
+6751,
+48154,
+35635,
+47951,
+59657,
+54892,
+17629,
+35104,
+6270,
+5843,
+59000,
+25179,
+191,
+22188,
+12663,
+34824,
+46433,
+33797,
+41825,
+47561,
+3472,
+41799,
+29905,
+23228,
+50046,
+16912,
+9359,
+19277,
+25576,
+33273,
+47128,
+4640,
+30616,
+25238,
+54201,
+52367,
+24471,
+62477,
+24489,
+24281,
+41633,
+40659,
+21556,
+11277,
+11257,
+61888,
+50573,
+46021,
+629,
+61689,
+34669,
+55922,
+49498,
+56642,
+42673,
+30343,
+7813,
+18763,
+6459,
+47003,
+65294,
+53893,
+23783,
+14134,
+20856,
+59329,
+21456,
+10062,
+31099,
+6266,
+38610,
+9345,
+50721,
+55306,
+17343,
+21528,
+50430,
+60475,
+62210,
+23469,
+33099,
+35389,
+65488,
+42327,
+53376,
+28280,
+14452,
+9401,
+53235,
+64579,
+54307,
+63005,
+5361,
+25216,
+56382,
+42764,
+60562,
+11161,
+41013,
+56636,
+56164,
+41084,
+24635,
+55783,
+34016,
+37192,
+36143,
+5515,
+49629,
+49128,
+60709,
+8446,
+32657,
+24911,
+12547,
+2555,
+2283,
+14721,
+35761,
+17614,
+37329,
+19318,
+56036,
+51927,
+63944,
+32893,
+15145,
+29230,
+43575,
+42619,
+39417,
+38858,
+30534,
+20805,
+1724,
+15122,
+38232,
+28341,
+34883,
+57251,
+41546,
+10046,
+2832,
+16472,
+13293,
+39267,
+58283,
+19410,
+26677,
+35005,
+21544,
+20999,
+20202,
+25495,
+59721,
+60827,
+62337,
+16964,
+33235,
+6029,
+12315,
+50516,
+2475,
+5533,
+19790,
+55044,
+65107,
+20319,
+59968,
+19575,
+65461,
+45440,
+10851,
+51968,
+8652,
+57237,
+57183,
+10491,
+432,
+36175,
+3713,
+3689,
+58487,
+40305,
+36067,
+30093,
+27887,
+16397,
+8342,
+46371,
+48840,
+64592,
+57693,
+22621,
+3631,
+51698,
+56478,
+13133,
+1972,
+59260,
+64358,
+2282,
+2642,
+12548,
+63355,
+28788,
+21015,
+62273,
+39749,
+44060,
+41427,
+42962,
+50421,
+41244,
+14664,
+40503,
+51042,
+20977,
+17449,
+7411,
+15112,
+55846,
+56049,
+51183,
+11229,
+62871,
+60177,
+60952,
+24463,
+5833,
+28995,
+13673,
+16819,
+753,
+26859,
+33704,
+7279,
+15422,
+18290,
+1900,
+1380,
+49830,
+45029,
+17805,
+25688,
+44274,
+65276,
+26972,
+47801,
+39641,
+44676,
+12213,
+52011,
+42531,
+56399,
+57024,
+49762,
+25507,
+47494,
+53075,
+51364,
+6748,
+34095,
+49271,
+27892,
+42841,
+64432,
+13239,
+22934,
+45544,
+43217,
+13893,
+28562,
+23244,
+30459,
+45126,
+25808,
+43154,
+10469,
+12351,
+37359,
+5532,
+2595,
+50517,
+13118,
+41809,
+38378,
+52350,
+38356,
+17234,
+51537,
+52116,
+28262,
+48541,
+12985,
+17276,
+54086,
+22192,
+20442,
+27116,
+14137,
+6004,
+3417,
+50448,
+50522,
+27610,
+42087,
+32715,
+46189,
+62137,
+64921,
+42307,
+15493,
+15258,
+62291,
+51340,
+39145,
+8407,
+14725,
+8844,
+49778,
+11434,
+23631,
+13759,
+63409,
+55216,
+46531,
+6284,
+7123,
+48289,
+40964,
+35578,
+56024,
+53019,
+5085,
+7333,
+11180,
+33933,
+1463,
+57704,
+37012,
+19124,
+38888,
+60243,
+32429,
+30785,
+40067,
+46311,
+10746,
+25284,
+24995,
+20402,
+21882,
+57931,
+46855,
+8540,
+53445,
+62069,
+30624,
+28833,
+42543,
+47621,
+38255,
+19683,
+45791,
+24138,
+52764,
+15164,
+21233,
+43609,
+9939,
+39910,
+57101,
+24219,
+7288,
+9217,
+64436,
+43315,
+10303,
+50076,
+32672,
+60048,
+54444,
+18855,
+23917,
+55397,
+13323,
+10591,
+12283,
+28368,
+17914,
+27284,
+59108,
+28416,
+48813,
+25055,
+10215,
+3138,
+61592,
+31490,
+60254,
+31628,
+60333,
+20797,
+19831,
+25886,
+8558,
+57812,
+65258,
+11081,
+3295,
+47742,
+41027,
+33803,
+32279,
+33608,
+29787,
+11567,
+1457,
+32320,
+23360,
+23370,
+22959,
+30466,
+19692,
+9246,
+731,
+49870,
+1210,
+14667,
+38359,
+44546,
+15447,
+141,
+41077,
+55348,
+38797,
+5452,
+650,
+36444,
+28059,
+46323,
+48353,
+5806,
+10937,
+61937,
+39844,
+16824,
+27844,
+25698,
+63378,
+30097,
+58372,
+29156,
+37592,
+28555,
+28425,
+27725,
+25371,
+58835,
+8588,
+42502,
+18219,
+27169,
+10718,
+16613,
+15476,
+20119,
+30206,
+37151,
+52613,
+8744,
+15574,
+14720,
+2641,
+2556,
+64359,
+23944,
+35371,
+36451,
+31315,
+52205,
+10025,
+61531,
+37425,
+51720,
+3867,
+43393,
+9185,
+53545,
+318,
+33694,
+55352,
+22029,
+5097,
+52644,
+59392,
+21836,
+22687,
+21449,
+17945,
+49298,
+53999,
+4216,
+27198,
+63820,
+37849,
+35623,
+23021,
+35713,
+43633,
+52202,
+56283,
+55870,
+40526,
+34347,
+14710,
+33734,
+10274,
+57074,
+18457,
+12105,
+38941,
+58643,
+42263,
+40667,
+23853,
+16252,
+44691,
+14737,
+21777,
+47136,
+37496,
+64152,
+36321,
+51881,
+24344,
+56474,
+63824,
+28750,
+45465,
+64147,
+28641,
+10094,
+46406,
+44116,
+9752,
+31523,
+19670,
+58918,
+41484,
+6625,
+36350,
+60928,
+50960,
+31578,
+44932,
+43880,
+36628,
+31874,
+49548,
+16623,
+49205,
+44000,
+777,
+25862,
+29882,
+24928,
+11826,
+20997,
+21546,
+9144,
+5039,
+7712,
+52988,
+62984,
+36750,
+38270,
+57296,
+59549,
+23963,
+61712,
+23292,
+32045,
+11521,
+26945,
+65054,
+30594,
+29611,
+41142,
+48873,
+1059,
+46557,
+29104,
+10332,
+3001,
+4913,
+39838,
+63230,
+35441,
+36612,
+46113,
+54528,
+51815,
+8097,
+45346,
+9567,
+38804,
+34949,
+47017,
+9188,
+44862,
+48366,
+26441,
+58442,
+33564,
+60272,
+16323,
+48134,
+35466,
+18738,
+60904,
+9608,
+39832,
+62741,
+55854,
+1876,
+55168,
+51777,
+63104,
+10322,
+42448,
+22298,
+47178,
+62450,
+48952,
+13781,
+23278,
+58207,
+1083,
+16748,
+24437,
+35108,
+21856,
+52045,
+2017,
+683,
+36123,
+55997,
+4508,
+28896,
+27416,
+29515,
+3769,
+18480,
+58896,
+35997,
+5399,
+42151,
+23772,
+62693,
+19042,
+21366,
+61750,
+65036,
+3540,
+18398,
+8247,
+50290,
+57031,
+7568,
+8954,
+42214,
+58021,
+48201,
+19233,
+18077,
+21205,
+40539,
+410,
+50549,
+19478,
+55209,
+56152,
+20834,
+41783,
+16139,
+1678,
+1172,
+29948,
+20514,
+9802,
+41036,
+29498,
+25859,
+4545,
+51482,
+23956,
+20912,
+29584,
+1370,
+4112,
+23217,
+22303,
+14014,
+55732,
+38632,
+13523,
+44395,
+32,
+63552,
+44364,
+8108,
+36071,
+11805,
+38032,
+4078,
+61457,
+4082,
+56758,
+54212,
+46642,
+61608,
+38826,
+37878,
+9837,
+16715,
+53043,
+43799,
+53677,
+8180,
+37556,
+46863,
+10653,
+23973,
+28166,
+24548,
+37408,
+35414,
+682,
+2112,
+52046,
+43030,
+55547,
+30050,
+39902,
+43083,
+36942,
+11124,
+33715,
+3494,
+11402,
+11375,
+15861,
+60989,
+14371,
+42414,
+48268,
+45571,
+55467,
+19625,
+59003,
+36727,
+50451,
+29661,
+32933,
+65142,
+26116,
+5898,
+2934,
+48188,
+65504,
+63490,
+21174,
+11369,
+38522,
+25176,
+19628,
+25930,
+53576,
+47603,
+32669,
+38924,
+166,
+59259,
+2559,
+13134,
+60314,
+3628,
+27740,
+22090,
+5967,
+37788,
+53424,
+3372,
+57086,
+55277,
+57278,
+52356,
+48567,
+59710,
+2982,
+44183,
+40751,
+55839,
+26893,
+7894,
+773,
+34971,
+45923,
+45509,
+17142,
+62774,
+7855,
+13357,
+56594,
+26100,
+4493,
+54586,
+47445,
+29808,
+26032,
+13988,
+21247,
+37905,
+48610,
+1120,
+3158,
+40124,
+8736,
+24147,
+61828,
+59511,
+60121,
+52007,
+42998,
+44965,
+61202,
+8333,
+38919,
+46734,
+64203,
+5065,
+38384,
+17609,
+18955,
+36901,
+33043,
+28666,
+54992,
+27940,
+65090,
+49815,
+4991,
+3375,
+30770,
+1379,
+2518,
+18291,
+43887,
+37657,
+29623,
+34515,
+22803,
+1004,
+8348,
+38331,
+64254,
+20296,
+36691,
+903,
+54591,
+36958,
+59032,
+51824,
+48021,
+31009,
+6728,
+57201,
+56708,
+55167,
+2131,
+55855,
+17311,
+3639,
+30843,
+1261,
+1507,
+13430,
+45083,
+9899,
+17034,
+42274,
+29569,
+30992,
+53394,
+23313,
+35693,
+40683,
+21975,
+7376,
+6815,
+17241,
+12054,
+60444,
+44057,
+1133,
+41554,
+32928,
+21030,
+19764,
+8201,
+51435,
+7428,
+32324,
+29851,
+60482,
+18272,
+58659,
+42895,
+11345,
+39158,
+51001,
+59212,
+20344,
+41533,
+52222,
+33055,
+25190,
+52236,
+37091,
+29969,
+43021,
+29930,
+43931,
+9532,
+20694,
+61684,
+34624,
+5155,
+50857,
+37857,
+45244,
+3938,
+47752,
+3653,
+51315,
+32334,
+19201,
+27295,
+8378,
+33712,
+40593,
+18346,
+33550,
+31241,
+35807,
+60913,
+60874,
+32404,
+41649,
+21201,
+62178,
+32184,
+9211,
+9399,
+14454,
+43313,
+64438,
+32292,
+7995,
+27778,
+14062,
+20315,
+23708,
+14527,
+62078,
+46870,
+52017,
+48692,
+27513,
+37824,
+13772,
+25306,
+37888,
+19417,
+58987,
+4184,
+52274,
+55788,
+20325,
+41675,
+14407,
+12669,
+23345,
+51589,
+58466,
+54850,
+33346,
+23225,
+29169,
+45122,
+59114,
+55486,
+5720,
+31160,
+43604,
+45801,
+12328,
+22683,
+4014,
+19556,
+8563,
+15069,
+188,
+52708,
+59627,
+29115,
+49784,
+32316,
+61764,
+5474,
+36187,
+60737,
+3101,
+41812,
+34264,
+36481,
+37336,
+17029,
+34609,
+24415,
+15121,
+2623,
+20806,
+12978,
+55636,
+48505,
+305,
+38338,
+63285,
+27689,
+9286,
+9062,
+63318,
+36788,
+54046,
+53771,
+35357,
+12010,
+47761,
+2958,
+27022,
+3423,
+18395,
+62264,
+27702,
+60641,
+6789,
+29372,
+45957,
+61866,
+7269,
+11026,
+22336,
+43885,
+18293,
+51305,
+41402,
+61538,
+9984,
+3926,
+60014,
+53278,
+30802,
+7520,
+14893,
+23133,
+1171,
+2070,
+16140,
+59476,
+31223,
+32377,
+2938,
+26693,
+41495,
+45248,
+42173,
+19999,
+44150,
+5704,
+53453,
+10122,
+18601,
+41838,
+9943,
+52627,
+15130,
+32057,
+54423,
+34866,
+52390,
+38712,
+37546,
+51295,
+27231,
+36661,
+9141,
+29945,
+22164,
+57534,
+26414,
+12637,
+49063,
+52246,
+27996,
+33288,
+11396,
+53974,
+28745,
+10135,
+29181,
+30362,
+17819,
+60437,
+52518,
+56008,
+14081,
+11473,
+30976,
+54856,
+14035,
+17151,
+60651,
+62345,
+20498,
+9426,
+43354,
+61446,
+61484,
+46760,
+56934,
+16770,
+39696,
+21019,
+18998,
+58332,
+40202,
+13409,
+17414,
+16119,
+21764,
+30603,
+19379,
+37514,
+20795,
+60335,
+8935,
+31046,
+51338,
+62293,
+19846,
+13609,
+62904,
+32645,
+1071,
+52883,
+61990,
+46594,
+10737,
+48223,
+36924,
+24535,
+16436,
+3107,
+49702,
+48655,
+3860,
+26871,
+24353,
+42624,
+50890,
+48091,
+42122,
+47235,
+9720,
+57731,
+4720,
+38829,
+57256,
+32350,
+15620,
+6076,
+33670,
+15650,
+41942,
+50119,
+36657,
+35040,
+4125,
+45699,
+59763,
+33865,
+32874,
+54769,
+18085,
+55939,
+27501,
+17485,
+26308,
+975,
+53787,
+36716,
+33172,
+49174,
+13992,
+25236,
+30618,
+29530,
+23068,
+43734,
+62036,
+9869,
+26249,
+55484,
+59116,
+60097,
+27775,
+57688,
+18498,
+45309,
+39666,
+13116,
+50519,
+36730,
+27878,
+35916,
+6256,
+14204,
+55471,
+59528,
+6186,
+38569,
+16590,
+47549,
+40554,
+17491,
+31357,
+13429,
+1870,
+1262,
+27347,
+8410,
+21148,
+40797,
+9824,
+20666,
+47393,
+32681,
+56825,
+18268,
+12406,
+22065,
+36785,
+20074,
+3912,
+25643,
+34331,
+28232,
+8418,
+34268,
+4325,
+5167,
+25985,
+21713,
+5974,
+19174,
+3412,
+35429,
+23517,
+15141,
+44343,
+14628,
+5695,
+22840,
+22410,
+54525,
+3346,
+47367,
+3976,
+54942,
+27870,
+57703,
+2419,
+33934,
+39385,
+37392,
+6109,
+32319,
+2339,
+11568,
+49682,
+55125,
+25439,
+56555,
+271,
+34938,
+21122,
+7649,
+64450,
+11648,
+59777,
+34357,
+36470,
+15498,
+57487,
+59757,
+42993,
+54918,
+20067,
+44954,
+28681,
+47119,
+54012,
+37757,
+51477,
+7012,
+22289,
+50346,
+51805,
+6664,
+25203,
+25819,
+64708,
+61343,
+38326,
+17215,
+54822,
+15483,
+43753,
+5230,
+22139,
+53440,
+35018,
+34393,
+58059,
+52662,
+42513,
+35794,
+24687,
+51227,
+30926,
+41124,
+8922,
+27720,
+38265,
+40867,
+45815,
+61271,
+38140,
+27088,
+25745,
+27597,
+46671,
+52778,
+20071,
+63321,
+36897,
+61618,
+39360,
+28040,
+63313,
+29217,
+9904,
+31251,
+49829,
+2517,
+1901,
+30771,
+30365,
+44247,
+4671,
+11686,
+50358,
+44376,
+4111,
+2057,
+29585,
+18061,
+46456,
+33824,
+38554,
+1009,
+9280,
+50476,
+39197,
+59417,
+48562,
+15781,
+22554,
+58239,
+26505,
+57841,
+46307,
+47747,
+60686,
+37526,
+28469,
+42249,
+28268,
+53121,
+20124,
+12532,
+18690,
+45075,
+43056,
+55413,
+46938,
+5241,
+19117,
+56560,
+18802,
+63621,
+60473,
+50432,
+19991,
+22322,
+47735,
+16631,
+46029,
+8847,
+11030,
+32719,
+16892,
+49822,
+5110,
+29442,
+35228,
+38636,
+38317,
+249,
+58460,
+19701,
+12179,
+41610,
+30244,
+56682,
+48965,
+20092,
+32944,
+12997,
+38820,
+4473,
+41998,
+50990,
+56117,
+33928,
+12058,
+35495,
+11066,
+44551,
+40638,
+39150,
+25343,
+38399,
+48937,
+20982,
+6813,
+7378,
+15073,
+60186,
+32794,
+27205,
+63647,
+45242,
+37859,
+26266,
+52983,
+4564,
+25096,
+57576,
+59009,
+22583,
+20387,
+16951,
+15469,
+41954,
+14822,
+46155,
+36829,
+60205,
+56367,
+11666,
+27346,
+1506,
+1871,
+30844,
+3800,
+34123,
+35610,
+23011,
+53944,
+13745,
+24426,
+19525,
+48511,
+51014,
+40613,
+13496,
+33005,
+20145,
+26478,
+25551,
+27786,
+46488,
+24715,
+20771,
+24702,
+11064,
+35497,
+18081,
+39163,
+34468,
+38695,
+31545,
+34552,
+44384,
+54232,
+12008,
+35359,
+55220,
+65430,
+58737,
+19077,
+30516,
+37143,
+16814,
+17688,
+22868,
+27518,
+44895,
+12568,
+31041,
+5619,
+40501,
+14666,
+2329,
+49871,
+38248,
+54949,
+61570,
+27085,
+50568,
+40804,
+50325,
+17080,
+58601,
+57666,
+29705,
+39366,
+48276,
+30328,
+21681,
+14283,
+48438,
+18434,
+21093,
+34615,
+54559,
+37710,
+45257,
+21182,
+56736,
+54568,
+56832,
+50110,
+6276,
+62980,
+63256,
+41594,
+51772,
+50087,
+22162,
+29947,
+2069,
+1679,
+23134,
+64640,
+46740,
+60932,
+18492,
+34203,
+29334,
+23555,
+282,
+45159,
+10904,
+7351,
+27109,
+11612,
+58337,
+33648,
+51396,
+60194,
+58312,
+57110,
+55450,
+52133,
+19467,
+54338,
+33226,
+40238,
+8765,
+17488,
+29440,
+5112,
+47888,
+7365,
+38873,
+57525,
+37903,
+21249,
+41553,
+1851,
+44058,
+39751,
+43672,
+42898,
+33059,
+8432,
+62103,
+5709,
+5889,
+38867,
+46203,
+3157,
+1931,
+48611,
+24210,
+51749,
+15797,
+32504,
+29474,
+28735,
+62767,
+38225,
+47411,
+57500,
+62757,
+20188,
+22695,
+63046,
+52299,
+13250,
+56319,
+63156,
+22223,
+63748,
+33246,
+22463,
+51724,
+49856,
+41994,
+18288,
+15424,
+60431,
+11003,
+53561,
+57887,
+64090,
+53890,
+61188,
+16747,
+2118,
+58208,
+6002,
+14139,
+5345,
+26689,
+38093,
+25155,
+12071,
+28497,
+24187,
+52882,
+1591,
+32646,
+5395,
+56271,
+14127,
+46171,
+44799,
+60671,
+19359,
+28819,
+32367,
+46556,
+2166,
+48874,
+43712,
+4034,
+6709,
+9016,
+54832,
+7538,
+52255,
+10798,
+47544,
+38084,
+57963,
+7080,
+16494,
+48525,
+17908,
+50505,
+38466,
+42410,
+820,
+38956,
+46767,
+18029,
+3356,
+59896,
+19604,
+58996,
+9090,
+47912,
+59168,
+8224,
+44606,
+45927,
+27046,
+64,
+23191,
+6605,
+10527,
+57377,
+47979,
+27747,
+12115,
+19674,
+40549,
+41817,
+9575,
+21306,
+56332,
+9279,
+1364,
+38555,
+45557,
+14267,
+8347,
+1893,
+22804,
+31722,
+28742,
+30999,
+45397,
+52469,
+35365,
+329,
+52232,
+5868,
+65471,
+42348,
+11507,
+39256,
+41615,
+65338,
+332,
+5118,
+55091,
+55480,
+11934,
+41311,
+48635,
+49019,
+31597,
+5964,
+15965,
+53786,
+1546,
+26309,
+34284,
+15710,
+47651,
+8986,
+29353,
+52064,
+42494,
+31683,
+6316,
+17472,
+58119,
+54705,
+20459,
+56867,
+12301,
+62869,
+11231,
+37910,
+549,
+44447,
+64643,
+9982,
+61540,
+5036,
+22237,
+59246,
+45606,
+16696,
+62971,
+7845,
+43645,
+52527,
+30505,
+13456,
+47879,
+4262,
+34622,
+61686,
+3014,
+24629,
+34068,
+59138,
+41251,
+5849,
+43950,
+13630,
+24108,
+22577,
+17571,
+42060,
+16402,
+14856,
+59702,
+36544,
+20253,
+12779,
+52887,
+17529,
+573,
+38935,
+59938,
+59860,
+19064,
+57831,
+10310,
+10177,
+7876,
+7306,
+64668,
+54590,
+1887,
+36692,
+62482,
+56289,
+64808,
+42402,
+64368,
+18798,
+21089,
+64584,
+55556,
+40630,
+29054,
+28203,
+31847,
+10111,
+50612,
+38834,
+8368,
+46039,
+17813,
+25338,
+37641,
+44163,
+46450,
+24707,
+45435,
+62320,
+26065,
+45366,
+39112,
+30138,
+39879,
+58230,
+64632,
+10948,
+55937,
+18087,
+7425,
+34058,
+48830,
+5246,
+25956,
+31473,
+11139,
+55378,
+18124,
+8039,
+42005,
+24020,
+5916,
+6846,
+58991,
+55745,
+58744,
+58195,
+22052,
+22233,
+37073,
+57539,
+7852,
+41019,
+15040,
+46266,
+4140,
+53415,
+64754,
+53386,
+24959,
+39998,
+11408,
+20244,
+18139,
+39717,
+64114,
+4195,
+11887,
+37053,
+7785,
+59890,
+49531,
+25274,
+38955,
+1039,
+42411,
+20270,
+60724,
+55337,
+46479,
+22946,
+44175,
+34302,
+59153,
+18852,
+57355,
+27983,
+33540,
+42038,
+51127,
+26818,
+32238,
+61206,
+9491,
+55133,
+62092,
+59049,
+27218,
+65290,
+40229,
+27651,
+30909,
+49070,
+6214,
+31773,
+53742,
+19711,
+30645,
+28933,
+49925,
+12121,
+64700,
+12152,
+31300,
+34701,
+4543,
+25861,
+2193,
+44001,
+46341,
+34970,
+1950,
+7895,
+32811,
+50472,
+59060,
+21931,
+19738,
+45431,
+54657,
+49839,
+25081,
+26232,
+7923,
+6295,
+28408,
+53519,
+11601,
+29561,
+34982,
+26858,
+2524,
+16820,
+16057,
+41332,
+65362,
+36536,
+44130,
+40148,
+51286,
+56464,
+2999,
+10334,
+43426,
+38348,
+5321,
+30282,
+29256,
+45680,
+43513,
+29197,
+44347,
+49869,
+2331,
+9247,
+64638,
+23136,
+44713,
+47573,
+4339,
+15298,
+23296,
+3177,
+30996,
+53977,
+44299,
+24371,
+28701,
+9977,
+14024,
+35155,
+13328,
+51638,
+57149,
+34430,
+30966,
+10460,
+41343,
+3249,
+39787,
+6051,
+36917,
+16711,
+55440,
+16197,
+57005,
+54176,
+55194,
+40019,
+61105,
+57699,
+56080,
+58434,
+62502,
+5034,
+61542,
+22957,
+23372,
+23215,
+4114,
+36122,
+2111,
+2018,
+35415,
+53297,
+31562,
+50941,
+50145,
+29957,
+9515,
+18961,
+40705,
+25064,
+24613,
+45289,
+10482,
+47583,
+61705,
+35327,
+44644,
+33262,
+17694,
+65438,
+34107,
+58884,
+11735,
+498,
+39402,
+17683,
+47827,
+40184,
+25994,
+8946,
+36443,
+2319,
+5453,
+23565,
+12763,
+11261,
+41964,
+14756,
+54835,
+54410,
+24947,
+52532,
+9128,
+49615,
+45166,
+33902,
+18371,
+26773,
+54873,
+23332,
+3012,
+61688,
+2709,
+46022,
+27490,
+39050,
+36491,
+26992,
+40290,
+51102,
+27439,
+41542,
+22128,
+36162,
+3399,
+63090,
+23231,
+24016,
+44903,
+51937,
+61032,
+33628,
+45736,
+24253,
+62082,
+15465,
+4658,
+10959,
+57603,
+35599,
+35113,
+51665,
+59410,
+31015,
+43362,
+31339,
+42186,
+43581,
+24977,
+47935,
+48127,
+12552,
+60975,
+38007,
+44324,
+4138,
+46268,
+27943,
+36984,
+58668,
+564,
+21808,
+54218,
+51137,
+37255,
+51087,
+45363,
+38934,
+915,
+17530,
+58880,
+30239,
+12209,
+10269,
+17569,
+22579,
+21807,
+581,
+58669,
+11778,
+26869,
+3862,
+48420,
+31594,
+58948,
+59997,
+29285,
+59977,
+45390,
+39061,
+47101,
+44446,
+955,
+37911,
+18451,
+30848,
+22006,
+50237,
+42243,
+33494,
+50879,
+28278,
+53378,
+50310,
+56994,
+40227,
+65292,
+47005,
+26805,
+55805,
+57638,
+27575,
+32211,
+8068,
+18032,
+15076,
+21596,
+10631,
+14160,
+31513,
+35473,
+19283,
+12375,
+36058,
+54255,
+52455,
+30287,
+16448,
+26317,
+128,
+13210,
+49217,
+55494,
+39348,
+18097,
+42612,
+18558,
+11573,
+28534,
+3786,
+63444,
+61998,
+39401,
+658,
+11736,
+19159,
+12975,
+65139,
+10236,
+45423,
+17085,
+32501,
+11928,
+42035,
+52618,
+31420,
+17972,
+38591,
+63366,
+34944,
+43873,
+48197,
+60201,
+10283,
+23357,
+19964,
+10072,
+52955,
+33392,
+15690,
+39517,
+26912,
+8524,
+57835,
+49258,
+52739,
+11698,
+27260,
+45154,
+18583,
+29712,
+7551,
+25558,
+35058,
+12333,
+54183,
+50125,
+20354,
+14612,
+43643,
+7847,
+47041,
+19267,
+37596,
+63631,
+15723,
+56406,
+20222,
+12131,
+26541,
+50676,
+22789,
+49414,
+59103,
+25195,
+60276,
+50923,
+21896,
+36174,
+2579,
+10492,
+62946,
+45219,
+16078,
+48040,
+13692,
+45953,
+50853,
+44466,
+49541,
+16501,
+26995,
+41293,
+41235,
+14459,
+40488,
+13897,
+8390,
+8569,
+54243,
+50548,
+2078,
+40540,
+45137,
+45892,
+3067,
+37704,
+55966,
+18539,
+53636,
+56875,
+44729,
+52561,
+65043,
+55302,
+32603,
+63546,
+39422,
+57866,
+24242,
+24304,
+3865,
+51722,
+22465,
+4403,
+15790,
+35407,
+43862,
+25325,
+34141,
+52464,
+12590,
+41501,
+59122,
+53716,
+33640,
+17127,
+7535,
+14759,
+47391,
+20668,
+11159,
+60564,
+25823,
+58394,
+34201,
+18494,
+46272,
+29609,
+30596,
+6354,
+42417,
+48742,
+19954,
+4773,
+37739,
+3323,
+20920,
+6498,
+12102,
+33422,
+32394,
+42815,
+14501,
+49861,
+51331,
+10068,
+45914,
+46430,
+6707,
+4036,
+41057,
+36345,
+59914,
+37922,
+50128,
+25041,
+29629,
+5117,
+987,
+65339,
+52231,
+996,
+35366,
+14831,
+17892,
+4638,
+47130,
+22522,
+29512,
+43283,
+39527,
+33693,
+2267,
+53546,
+46664,
+57839,
+26507,
+12563,
+53984,
+36667,
+17547,
+6787,
+60643,
+48032,
+38337,
+1719,
+48506,
+30930,
+21918,
+5661,
+37793,
+62030,
+62678,
+21625,
+30333,
+25104,
+10477,
+8276,
+55886,
+51799,
+43616,
+36737,
+36637,
+12331,
+35060,
+60478,
+20538,
+45158,
+1162,
+23556,
+7724,
+63188,
+54457,
+54417,
+12585,
+23355,
+10285,
+61696,
+34937,
+1451,
+56556,
+35083,
+14917,
+28473,
+46073,
+55150,
+16136,
+30152,
+21262,
+63026,
+38566,
+6579,
+39373,
+32896,
+256,
+32897,
+13159,
+19687,
+23146,
+7572,
+58459,
+1316,
+38318,
+64741,
+25309,
+65002,
+18533,
+62840,
+11645,
+4351,
+30857,
+20566,
+39928,
+40373,
+31380,
+53824,
+50133,
+39941,
+54520,
+37166,
+15102,
+4579,
+42829,
+40708,
+21749,
+28545,
+9655,
+31446,
+40444,
+53082,
+45578,
+52669,
+55015,
+43792,
+5158,
+60355,
+32156,
+27384,
+36169,
+33545,
+53990,
+16933,
+15439,
+16780,
+42949,
+4156,
+24737,
+6134,
+46395,
+41735,
+23921,
+64549,
+65206,
+13307,
+48484,
+7730,
+44930,
+31580,
+22187,
+2745,
+25180,
+52707,
+1743,
+15070,
+29245,
+22318,
+51646,
+2865,
+27007,
+61192,
+19297,
+39908,
+9941,
+41840,
+24204,
+64788,
+29002,
+37125,
+26291,
+13414,
+16577,
+54291,
+34719,
+59258,
+1974,
+38925,
+5195,
+35784,
+28723,
+59024,
+13517,
+4534,
+27791,
+53014,
+42431,
+35094,
+55749,
+11137,
+31475,
+43836,
+10049,
+59819,
+60666,
+20954,
+46235,
+21695,
+7737,
+4583,
+41076,
+2324,
+15448,
+43377,
+25170,
+20570,
+4337,
+47575,
+29834,
+26609,
+32272,
+42524,
+11595,
+13209,
+512,
+26318,
+46292,
+51381,
+11099,
+50618,
+37066,
+23290,
+61714,
+14319,
+62760,
+34729,
+43503,
+30204,
+20121,
+43498,
+19310,
+15351,
+25619,
+44493,
+10316,
+64312,
+6801,
+42443,
+30878,
+20361,
+49474,
+27253,
+47842,
+64875,
+31431,
+24323,
+44374,
+50360,
+3486,
+58490,
+37755,
+54014,
+38594,
+19882,
+48408,
+58004,
+52582,
+33154,
+3901,
+3948,
+10390,
+57446,
+27034,
+55582,
+19325,
+45961,
+62950,
+20098,
+55795,
+26933,
+15474,
+16615,
+30896,
+50340,
+8674,
+59668,
+64544,
+23190,
+1024,
+27047,
+22198,
+8595,
+57891,
+59983,
+21469,
+40242,
+38620,
+51238,
+20632,
+13602,
+61756,
+33411,
+30147,
+62862,
+23211,
+6972,
+9973,
+11651,
+31279,
+39627,
+7802,
+20780,
+54068,
+38650,
+60363,
+46053,
+30948,
+61792,
+17348,
+63551,
+2048,
+44396,
+50245,
+42938,
+11703,
+41264,
+57975,
+60294,
+46422,
+19946,
+62558,
+15604,
+42599,
+55189,
+61896,
+34696,
+4096,
+34953,
+23406,
+50413,
+27307,
+59579,
+19661,
+58255,
+8192,
+46812,
+54614,
+39322,
+16384,
+43691,
+32768,
+65536,
+0,
+};
diff --git a/appl/lib/ida/idatest.b b/appl/lib/ida/idatest.b
new file mode 100644
index 00000000..f782cd56
--- /dev/null
+++ b/appl/lib/ida/idatest.b
@@ -0,0 +1,84 @@
+implement Idatest;
+
+#
+# Copyright © 2006 Vita Nuova Holdings Limited
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "rand.m";
+ rand: Rand;
+
+include "ida.m";
+ ida: Ida;
+ Frag: import ida;
+
+Idatest: module
+{
+ init: fn(nil: ref Draw->Context, nil: list of string);
+};
+
+init(nil: ref Draw->Context, args: list of string)
+{
+ sys = load Sys Sys->PATH;
+ rand = load Rand Rand->PATH;
+ ida = load Ida Ida->PATH;
+
+ rand->init(sys->pctl(0,nil));
+ ida->init();
+
+ stderr := sys->fildes(2);
+ args = tl args;
+ debug := 0;
+ nowrite := 0;
+ onlyenc := 0;
+ for(; args != nil; args = tl args)
+ case hd args {
+ "-d" => debug = 1;
+ "-w" => nowrite = 1;
+ "-e" => onlyenc = 1;
+ }
+ buf := array[1024] of byte;
+ while((n := sys->read(sys->fildes(0), buf, len buf)) > 0){
+ frags := array[14] of ref Frag;
+ for(x := 0; x < len frags; x++){
+ frags[x] = f := ida->fragment(buf[0:n], 7);
+ if(debug){
+ for(i := 0; i < len f.enc; i++)
+ sys->fprint(stderr, " %d", f.enc[i]);
+ sys->fprint(stderr, "\n");
+ }
+ }
+ if(onlyenc)
+ continue;
+ if(1){
+ # shuffle
+ for(i := 0; i < len frags; i++){
+ r := rand->rand(len frags);
+ if(r != i){
+ t := frags[i]; frags[i] = frags[r]; frags[r] = t;
+ }
+ }
+ }
+ # recover
+ (zot, err) := ida->reconstruct(frags);
+ if(err != nil){
+ sys->fprint(stderr, "reconstruction failed: %s\n", err);
+ raise "fail:reconstruct";
+ }
+ if(len zot != n){
+ sys->fprint(stderr, "bad length: expected %d got %d\n", n, len zot);
+ raise "fail:length";
+ }
+ if(debug){
+ for(i := 0; i < len zot; i++)
+ sys->fprint(stderr, " %.2ux", int zot[i]);
+ sys->fprint(stderr, "\n");
+ sys->fprint(stderr, "%q\n", string zot);
+ }else if(!nowrite)
+ sys->write(sys->fildes(1), zot, len zot);
+ }
+}
diff --git a/appl/lib/ida/mkfile b/appl/lib/ida/mkfile
new file mode 100644
index 00000000..d7ccdc64
--- /dev/null
+++ b/appl/lib/ida/mkfile
@@ -0,0 +1,18 @@
+<../../../mkconfig
+
+TARG=\
+ ida.dis\
+ idatab.dis\
+
+MODULES=\
+
+SYSMODULES= \
+ ida.m\
+ rand.m\
+ sys.m\
+
+DISBIN=$ROOT/dis/lib/ida
+
+<$ROOT/mkfiles/mkdis
+# force compilation
+LIMBOFLAGS= -c $LIMBOFLAGS
diff --git a/appl/lib/ida/mktab.b b/appl/lib/ida/mktab.b
new file mode 100644
index 00000000..96025d3f
--- /dev/null
+++ b/appl/lib/ida/mktab.b
@@ -0,0 +1,32 @@
+implement Genfield;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "keyring.m";
+ kr: Keyring;
+ IPint: import kr;
+
+Genfield: module
+{
+ init: fn(nil: ref Draw->Context, nil: list of string);
+};
+
+Field: con 65537;
+
+init(nil: ref Draw->Context, args: list of string)
+{
+ sys = load Sys Sys->PATH;
+ kr = load Keyring Keyring->PATH;
+
+ f := IPint.inttoip(Field);
+ fm2 := f.sub(IPint.inttoip(2));
+ for(i := 1; i <= Field; i++){
+ x := IPint.inttoip(i);
+ y := x.expmod(fm2, f);
+# sys->print("%s\n", x.mul(y).expmod(IPint.inttoip(1), f).iptostr(10));
+ sys->print("%d,\n", y.iptoint());
+ }
+}
diff --git a/appl/lib/imageremap.b b/appl/lib/imageremap.b
new file mode 100644
index 00000000..1737869e
--- /dev/null
+++ b/appl/lib/imageremap.b
@@ -0,0 +1,738 @@
+implement Imageremap;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+ draw: Draw;
+ Display, Image: import draw;
+
+include "bufio.m";
+
+include "imagefile.m";
+
+closest:= array[16*16*16] of {
+ byte 255,byte 255,byte 255,byte 254,byte 254,byte 237,byte 220,byte 203,
+ byte 253,byte 236,byte 219,byte 202,byte 252,byte 235,byte 218,byte 201,
+ byte 255,byte 255,byte 255,byte 254,byte 254,byte 237,byte 220,byte 203,
+ byte 253,byte 236,byte 219,byte 202,byte 252,byte 235,byte 218,byte 201,
+ byte 255,byte 255,byte 255,byte 250,byte 250,byte 250,byte 220,byte 249,
+ byte 249,byte 249,byte 232,byte 248,byte 248,byte 248,byte 231,byte 201,
+ byte 251,byte 251,byte 250,byte 250,byte 250,byte 250,byte 249,byte 249,
+ byte 249,byte 249,byte 232,byte 248,byte 248,byte 248,byte 231,byte 201,
+ byte 251,byte 251,byte 250,byte 250,byte 250,byte 233,byte 233,byte 249,
+ byte 249,byte 232,byte 215,byte 215,byte 248,byte 231,byte 214,byte 197,
+ byte 234,byte 234,byte 250,byte 250,byte 233,byte 233,byte 216,byte 216,
+ byte 249,byte 232,byte 215,byte 198,byte 198,byte 231,byte 214,byte 197,
+ byte 217,byte 217,byte 217,byte 246,byte 233,byte 216,byte 216,byte 199,
+ byte 199,byte 215,byte 215,byte 198,byte 198,byte 198,byte 214,byte 197,
+ byte 200,byte 200,byte 246,byte 246,byte 246,byte 216,byte 199,byte 199,
+ byte 245,byte 245,byte 198,byte 244,byte 244,byte 244,byte 227,byte 197,
+ byte 247,byte 247,byte 246,byte 246,byte 246,byte 246,byte 199,byte 245,
+ byte 245,byte 245,byte 228,byte 244,byte 244,byte 244,byte 227,byte 193,
+ byte 230,byte 230,byte 246,byte 246,byte 229,byte 229,byte 212,byte 245,
+ byte 245,byte 228,byte 228,byte 211,byte 244,byte 227,byte 210,byte 193,
+ byte 213,byte 213,byte 229,byte 229,byte 212,byte 212,byte 212,byte 195,
+ byte 228,byte 228,byte 211,byte 211,byte 194,byte 227,byte 210,byte 193,
+ byte 196,byte 196,byte 242,byte 242,byte 212,byte 195,byte 195,byte 241,
+ byte 241,byte 211,byte 211,byte 194,byte 194,byte 240,byte 210,byte 193,
+ byte 243,byte 243,byte 242,byte 242,byte 242,byte 195,byte 195,byte 241,
+ byte 241,byte 241,byte 194,byte 194,byte 240,byte 240,byte 239,byte 205,
+ byte 226,byte 226,byte 242,byte 242,byte 225,byte 225,byte 195,byte 241,
+ byte 241,byte 224,byte 224,byte 240,byte 240,byte 239,byte 239,byte 205,
+ byte 209,byte 209,byte 225,byte 225,byte 208,byte 208,byte 208,byte 224,
+ byte 224,byte 223,byte 223,byte 223,byte 239,byte 239,byte 222,byte 205,
+ byte 192,byte 192,byte 192,byte 192,byte 207,byte 207,byte 207,byte 207,
+ byte 206,byte 206,byte 206,byte 206,byte 205,byte 205,byte 205,byte 205,
+ byte 255,byte 255,byte 255,byte 254,byte 254,byte 237,byte 220,byte 203,
+ byte 253,byte 236,byte 219,byte 202,byte 252,byte 235,byte 218,byte 201,
+ byte 255,byte 238,byte 221,byte 221,byte 254,byte 237,byte 220,byte 203,
+ byte 253,byte 236,byte 219,byte 202,byte 252,byte 235,byte 218,byte 201,
+ byte 255,byte 221,byte 221,byte 221,byte 204,byte 250,byte 220,byte 249,
+ byte 249,byte 249,byte 232,byte 248,byte 248,byte 248,byte 231,byte 201,
+ byte 251,byte 221,byte 221,byte 204,byte 250,byte 250,byte 249,byte 249,
+ byte 249,byte 249,byte 232,byte 248,byte 248,byte 248,byte 231,byte 201,
+ byte 251,byte 251,byte 204,byte 250,byte 250,byte 233,byte 233,byte 249,
+ byte 249,byte 232,byte 215,byte 215,byte 248,byte 231,byte 214,byte 197,
+ byte 234,byte 234,byte 250,byte 250,byte 233,byte 233,byte 216,byte 216,
+ byte 249,byte 232,byte 215,byte 198,byte 198,byte 231,byte 214,byte 197,
+ byte 217,byte 217,byte 217,byte 246,byte 233,byte 216,byte 216,byte 199,
+ byte 199,byte 215,byte 215,byte 198,byte 198,byte 198,byte 214,byte 197,
+ byte 200,byte 200,byte 246,byte 246,byte 246,byte 216,byte 199,byte 199,
+ byte 245,byte 245,byte 198,byte 244,byte 244,byte 244,byte 227,byte 197,
+ byte 247,byte 247,byte 246,byte 246,byte 246,byte 246,byte 199,byte 245,
+ byte 245,byte 245,byte 228,byte 244,byte 244,byte 244,byte 227,byte 193,
+ byte 230,byte 230,byte 246,byte 246,byte 229,byte 229,byte 212,byte 245,
+ byte 245,byte 228,byte 228,byte 211,byte 244,byte 227,byte 210,byte 193,
+ byte 213,byte 213,byte 229,byte 229,byte 212,byte 212,byte 212,byte 195,
+ byte 228,byte 228,byte 211,byte 211,byte 194,byte 227,byte 210,byte 193,
+ byte 196,byte 196,byte 242,byte 242,byte 212,byte 195,byte 195,byte 241,
+ byte 241,byte 211,byte 211,byte 194,byte 194,byte 240,byte 210,byte 193,
+ byte 243,byte 243,byte 242,byte 242,byte 242,byte 195,byte 195,byte 241,
+ byte 241,byte 241,byte 194,byte 194,byte 240,byte 240,byte 239,byte 205,
+ byte 226,byte 226,byte 242,byte 242,byte 225,byte 225,byte 195,byte 241,
+ byte 241,byte 224,byte 224,byte 240,byte 240,byte 239,byte 239,byte 205,
+ byte 209,byte 209,byte 225,byte 225,byte 208,byte 208,byte 208,byte 224,
+ byte 224,byte 223,byte 223,byte 223,byte 239,byte 239,byte 222,byte 205,
+ byte 192,byte 192,byte 192,byte 192,byte 207,byte 207,byte 207,byte 207,
+ byte 206,byte 206,byte 206,byte 206,byte 205,byte 205,byte 205,byte 205,
+ byte 255,byte 255,byte 255,byte 191,byte 191,byte 191,byte 220,byte 190,
+ byte 190,byte 190,byte 173,byte 189,byte 189,byte 189,byte 172,byte 201,
+ byte 255,byte 221,byte 221,byte 221,byte 204,byte 191,byte 220,byte 190,
+ byte 190,byte 190,byte 173,byte 189,byte 189,byte 189,byte 172,byte 201,
+ byte 255,byte 221,byte 221,byte 204,byte 204,byte 204,byte 186,byte 186,
+ byte 186,byte 186,byte 186,byte 185,byte 185,byte 185,byte 168,byte 201,
+ byte 188,byte 221,byte 204,byte 204,byte 204,byte 187,byte 186,byte 186,
+ byte 186,byte 186,byte 232,byte 185,byte 185,byte 185,byte 168,byte 201,
+ byte 188,byte 204,byte 204,byte 204,byte 187,byte 187,byte 186,byte 186,
+ byte 186,byte 186,byte 169,byte 185,byte 185,byte 185,byte 168,byte 197,
+ byte 188,byte 188,byte 204,byte 187,byte 187,byte 233,byte 216,byte 186,
+ byte 186,byte 186,byte 215,byte 185,byte 185,byte 185,byte 168,byte 197,
+ byte 217,byte 217,byte 183,byte 183,byte 183,byte 216,byte 216,byte 199,
+ byte 182,byte 182,byte 215,byte 198,byte 198,byte 181,byte 214,byte 197,
+ byte 184,byte 184,byte 183,byte 183,byte 183,byte 183,byte 199,byte 182,
+ byte 182,byte 182,byte 182,byte 181,byte 181,byte 181,byte 181,byte 197,
+ byte 184,byte 184,byte 183,byte 183,byte 183,byte 183,byte 182,byte 182,
+ byte 182,byte 182,byte 182,byte 181,byte 181,byte 181,byte 164,byte 193,
+ byte 184,byte 184,byte 183,byte 183,byte 183,byte 183,byte 182,byte 182,
+ byte 182,byte 228,byte 165,byte 181,byte 181,byte 164,byte 164,byte 193,
+ byte 167,byte 167,byte 183,byte 229,byte 166,byte 212,byte 212,byte 182,
+ byte 182,byte 165,byte 211,byte 211,byte 181,byte 164,byte 210,byte 193,
+ byte 180,byte 180,byte 179,byte 179,byte 179,byte 179,byte 195,byte 178,
+ byte 178,byte 178,byte 211,byte 194,byte 177,byte 177,byte 177,byte 193,
+ byte 180,byte 180,byte 179,byte 179,byte 179,byte 179,byte 195,byte 178,
+ byte 178,byte 178,byte 178,byte 177,byte 177,byte 177,byte 177,byte 205,
+ byte 180,byte 180,byte 179,byte 179,byte 179,byte 179,byte 178,byte 178,
+ byte 178,byte 161,byte 161,byte 177,byte 177,byte 177,byte 160,byte 205,
+ byte 163,byte 163,byte 162,byte 162,byte 162,byte 162,byte 208,byte 178,
+ byte 161,byte 161,byte 223,byte 177,byte 177,byte 160,byte 160,byte 205,
+ byte 192,byte 192,byte 192,byte 192,byte 207,byte 207,byte 207,byte 207,
+ byte 206,byte 206,byte 206,byte 206,byte 205,byte 205,byte 205,byte 205,
+ byte 176,byte 176,byte 191,byte 191,byte 191,byte 191,byte 190,byte 190,
+ byte 190,byte 190,byte 173,byte 189,byte 189,byte 189,byte 172,byte 201,
+ byte 176,byte 221,byte 221,byte 204,byte 191,byte 191,byte 190,byte 190,
+ byte 190,byte 190,byte 173,byte 189,byte 189,byte 189,byte 172,byte 201,
+ byte 188,byte 221,byte 204,byte 204,byte 204,byte 187,byte 186,byte 186,
+ byte 186,byte 186,byte 173,byte 185,byte 185,byte 185,byte 168,byte 201,
+ byte 188,byte 204,byte 204,byte 204,byte 187,byte 187,byte 186,byte 186,
+ byte 186,byte 186,byte 169,byte 185,byte 185,byte 185,byte 168,byte 201,
+ byte 188,byte 188,byte 204,byte 187,byte 187,byte 187,byte 186,byte 186,
+ byte 186,byte 186,byte 169,byte 185,byte 185,byte 185,byte 168,byte 197,
+ byte 188,byte 188,byte 187,byte 187,byte 187,byte 170,byte 170,byte 186,
+ byte 186,byte 169,byte 169,byte 185,byte 185,byte 168,byte 168,byte 197,
+ byte 184,byte 184,byte 183,byte 183,byte 183,byte 170,byte 170,byte 182,
+ byte 182,byte 169,byte 152,byte 152,byte 181,byte 168,byte 151,byte 197,
+ byte 184,byte 184,byte 183,byte 183,byte 183,byte 183,byte 182,byte 182,
+ byte 182,byte 182,byte 182,byte 181,byte 181,byte 181,byte 164,byte 197,
+ byte 184,byte 184,byte 183,byte 183,byte 183,byte 183,byte 182,byte 182,
+ byte 182,byte 182,byte 165,byte 181,byte 181,byte 181,byte 164,byte 193,
+ byte 184,byte 184,byte 183,byte 183,byte 183,byte 166,byte 166,byte 182,
+ byte 182,byte 165,byte 165,byte 181,byte 181,byte 164,byte 164,byte 193,
+ byte 167,byte 167,byte 167,byte 166,byte 166,byte 166,byte 149,byte 182,
+ byte 165,byte 165,byte 165,byte 148,byte 181,byte 164,byte 147,byte 193,
+ byte 180,byte 180,byte 179,byte 179,byte 179,byte 179,byte 149,byte 178,
+ byte 178,byte 178,byte 148,byte 177,byte 177,byte 177,byte 147,byte 193,
+ byte 180,byte 180,byte 179,byte 179,byte 179,byte 179,byte 178,byte 178,
+ byte 178,byte 178,byte 178,byte 177,byte 177,byte 177,byte 160,byte 205,
+ byte 180,byte 180,byte 179,byte 179,byte 179,byte 162,byte 162,byte 178,
+ byte 178,byte 161,byte 161,byte 177,byte 177,byte 160,byte 160,byte 205,
+ byte 163,byte 163,byte 162,byte 162,byte 162,byte 162,byte 145,byte 161,
+ byte 161,byte 161,byte 144,byte 144,byte 160,byte 160,byte 160,byte 205,
+ byte 192,byte 192,byte 192,byte 192,byte 207,byte 207,byte 207,byte 207,
+ byte 206,byte 206,byte 206,byte 206,byte 205,byte 205,byte 205,byte 205,
+ byte 176,byte 176,byte 191,byte 191,byte 191,byte 174,byte 174,byte 190,
+ byte 190,byte 173,byte 156,byte 156,byte 189,byte 172,byte 155,byte 138,
+ byte 176,byte 176,byte 204,byte 191,byte 191,byte 174,byte 174,byte 190,
+ byte 190,byte 173,byte 156,byte 156,byte 189,byte 172,byte 155,byte 138,
+ byte 188,byte 204,byte 204,byte 204,byte 187,byte 187,byte 186,byte 186,
+ byte 186,byte 186,byte 169,byte 185,byte 185,byte 185,byte 168,byte 138,
+ byte 188,byte 188,byte 204,byte 187,byte 187,byte 187,byte 186,byte 186,
+ byte 186,byte 186,byte 169,byte 185,byte 185,byte 185,byte 168,byte 138,
+ byte 188,byte 188,byte 187,byte 187,byte 187,byte 170,byte 170,byte 186,
+ byte 186,byte 169,byte 169,byte 185,byte 185,byte 168,byte 151,byte 134,
+ byte 171,byte 171,byte 187,byte 187,byte 170,byte 170,byte 170,byte 186,
+ byte 186,byte 169,byte 152,byte 152,byte 185,byte 168,byte 151,byte 134,
+ byte 171,byte 171,byte 183,byte 183,byte 170,byte 170,byte 170,byte 153,
+ byte 182,byte 169,byte 152,byte 135,byte 135,byte 168,byte 151,byte 134,
+ byte 184,byte 184,byte 183,byte 183,byte 183,byte 183,byte 153,byte 182,
+ byte 182,byte 182,byte 182,byte 181,byte 181,byte 181,byte 164,byte 134,
+ byte 184,byte 184,byte 183,byte 183,byte 183,byte 183,byte 182,byte 182,
+ byte 182,byte 182,byte 165,byte 181,byte 181,byte 181,byte 164,byte 130,
+ byte 167,byte 167,byte 183,byte 183,byte 166,byte 166,byte 166,byte 182,
+ byte 182,byte 165,byte 165,byte 181,byte 181,byte 164,byte 147,byte 130,
+ byte 150,byte 150,byte 166,byte 166,byte 166,byte 149,byte 149,byte 182,
+ byte 165,byte 165,byte 148,byte 148,byte 164,byte 164,byte 147,byte 130,
+ byte 150,byte 150,byte 179,byte 179,byte 179,byte 149,byte 132,byte 178,
+ byte 178,byte 178,byte 148,byte 131,byte 177,byte 177,byte 147,byte 130,
+ byte 180,byte 180,byte 179,byte 179,byte 179,byte 179,byte 132,byte 178,
+ byte 178,byte 178,byte 161,byte 177,byte 177,byte 177,byte 160,byte 142,
+ byte 163,byte 163,byte 179,byte 179,byte 162,byte 162,byte 162,byte 178,
+ byte 178,byte 161,byte 161,byte 177,byte 177,byte 160,byte 160,byte 142,
+ byte 146,byte 146,byte 162,byte 162,byte 145,byte 145,byte 145,byte 161,
+ byte 161,byte 144,byte 144,byte 144,byte 160,byte 160,byte 159,byte 142,
+ byte 129,byte 129,byte 129,byte 129,byte 128,byte 128,byte 128,byte 128,
+ byte 143,byte 143,byte 143,byte 143,byte 142,byte 142,byte 142,byte 142,
+ byte 175,byte 175,byte 191,byte 191,byte 174,byte 174,byte 157,byte 157,
+ byte 190,byte 173,byte 156,byte 139,byte 139,byte 172,byte 155,byte 138,
+ byte 175,byte 175,byte 191,byte 191,byte 174,byte 174,byte 157,byte 157,
+ byte 190,byte 173,byte 156,byte 139,byte 139,byte 172,byte 155,byte 138,
+ byte 188,byte 188,byte 204,byte 187,byte 187,byte 187,byte 157,byte 186,
+ byte 186,byte 186,byte 156,byte 185,byte 185,byte 185,byte 168,byte 138,
+ byte 188,byte 188,byte 187,byte 187,byte 187,byte 170,byte 170,byte 186,
+ byte 186,byte 169,byte 169,byte 185,byte 185,byte 168,byte 168,byte 138,
+ byte 171,byte 171,byte 187,byte 187,byte 170,byte 170,byte 170,byte 186,
+ byte 186,byte 169,byte 152,byte 152,byte 185,byte 168,byte 151,byte 134,
+ byte 171,byte 171,byte 187,byte 170,byte 170,byte 170,byte 170,byte 153,
+ byte 169,byte 169,byte 152,byte 135,byte 135,byte 168,byte 151,byte 134,
+ byte 154,byte 154,byte 154,byte 170,byte 170,byte 170,byte 153,byte 153,
+ byte 169,byte 152,byte 152,byte 135,byte 135,byte 135,byte 151,byte 134,
+ byte 154,byte 154,byte 183,byte 183,byte 183,byte 153,byte 153,byte 153,
+ byte 182,byte 182,byte 135,byte 135,byte 181,byte 181,byte 164,byte 134,
+ byte 184,byte 184,byte 183,byte 183,byte 183,byte 166,byte 166,byte 182,
+ byte 182,byte 165,byte 165,byte 181,byte 181,byte 164,byte 164,byte 130,
+ byte 167,byte 167,byte 183,byte 166,byte 166,byte 166,byte 149,byte 182,
+ byte 165,byte 165,byte 165,byte 148,byte 181,byte 164,byte 147,byte 130,
+ byte 150,byte 150,byte 150,byte 166,byte 149,byte 149,byte 149,byte 132,
+ byte 165,byte 165,byte 148,byte 148,byte 131,byte 147,byte 147,byte 130,
+ byte 133,byte 133,byte 179,byte 179,byte 149,byte 132,byte 132,byte 132,
+ byte 178,byte 148,byte 148,byte 131,byte 131,byte 131,byte 130,byte 130,
+ byte 133,byte 133,byte 179,byte 179,byte 179,byte 132,byte 132,byte 178,
+ byte 178,byte 178,byte 131,byte 131,byte 131,byte 177,byte 160,byte 142,
+ byte 163,byte 163,byte 179,byte 162,byte 162,byte 162,byte 132,byte 178,
+ byte 161,byte 161,byte 144,byte 131,byte 177,byte 160,byte 160,byte 142,
+ byte 146,byte 146,byte 162,byte 162,byte 145,byte 145,byte 145,byte 161,
+ byte 161,byte 144,byte 144,byte 143,byte 160,byte 160,byte 159,byte 142,
+ byte 129,byte 129,byte 129,byte 129,byte 128,byte 128,byte 128,byte 128,
+ byte 143,byte 143,byte 143,byte 143,byte 142,byte 142,byte 142,byte 142,
+ byte 158,byte 158,byte 158,byte 112,byte 174,byte 157,byte 157,byte 140,
+ byte 140,byte 156,byte 156,byte 139,byte 139,byte 139,byte 155,byte 138,
+ byte 158,byte 158,byte 158,byte 112,byte 174,byte 157,byte 157,byte 140,
+ byte 140,byte 156,byte 156,byte 139,byte 139,byte 139,byte 155,byte 138,
+ byte 158,byte 158,byte 124,byte 124,byte 124,byte 157,byte 157,byte 140,
+ byte 123,byte 123,byte 156,byte 139,byte 139,byte 122,byte 155,byte 138,
+ byte 125,byte 125,byte 124,byte 124,byte 124,byte 170,byte 170,byte 123,
+ byte 123,byte 169,byte 152,byte 152,byte 122,byte 168,byte 151,byte 138,
+ byte 171,byte 171,byte 124,byte 124,byte 170,byte 170,byte 170,byte 153,
+ byte 123,byte 169,byte 152,byte 135,byte 135,byte 168,byte 151,byte 134,
+ byte 154,byte 154,byte 154,byte 170,byte 170,byte 170,byte 153,byte 153,
+ byte 169,byte 152,byte 152,byte 135,byte 135,byte 135,byte 151,byte 134,
+ byte 154,byte 154,byte 154,byte 170,byte 170,byte 153,byte 153,byte 153,
+ byte 136,byte 152,byte 135,byte 135,byte 135,byte 135,byte 134,byte 134,
+ byte 137,byte 137,byte 137,byte 120,byte 153,byte 153,byte 153,byte 136,
+ byte 136,byte 136,byte 135,byte 135,byte 135,byte 118,byte 164,byte 134,
+ byte 137,byte 137,byte 120,byte 120,byte 120,byte 166,byte 136,byte 136,
+ byte 136,byte 165,byte 165,byte 118,byte 118,byte 164,byte 147,byte 130,
+ byte 150,byte 150,byte 120,byte 166,byte 166,byte 149,byte 149,byte 136,
+ byte 165,byte 165,byte 148,byte 148,byte 118,byte 164,byte 147,byte 130,
+ byte 150,byte 150,byte 150,byte 149,byte 149,byte 149,byte 132,byte 132,
+ byte 165,byte 148,byte 148,byte 131,byte 131,byte 147,byte 147,byte 130,
+ byte 133,byte 133,byte 133,byte 149,byte 132,byte 132,byte 132,byte 132,
+ byte 115,byte 148,byte 131,byte 131,byte 131,byte 131,byte 130,byte 130,
+ byte 133,byte 133,byte 133,byte 116,byte 132,byte 132,byte 132,byte 132,
+ byte 115,byte 115,byte 131,byte 131,byte 131,byte 131,byte 160,byte 142,
+ byte 133,byte 133,byte 116,byte 162,byte 162,byte 132,byte 132,byte 115,
+ byte 161,byte 161,byte 144,byte 131,byte 131,byte 160,byte 160,byte 142,
+ byte 146,byte 146,byte 146,byte 145,byte 145,byte 145,byte 128,byte 161,
+ byte 144,byte 144,byte 144,byte 143,byte 160,byte 160,byte 159,byte 142,
+ byte 129,byte 129,byte 129,byte 129,byte 128,byte 128,byte 128,byte 128,
+ byte 143,byte 143,byte 143,byte 143,byte 142,byte 142,byte 142,byte 142,
+ byte 141,byte 141,byte 112,byte 112,byte 112,byte 157,byte 140,byte 140,
+ byte 140,byte 127,byte 139,byte 126,byte 126,byte 126,byte 109,byte 138,
+ byte 141,byte 141,byte 112,byte 112,byte 112,byte 157,byte 140,byte 140,
+ byte 140,byte 127,byte 139,byte 126,byte 126,byte 126,byte 109,byte 138,
+ byte 125,byte 125,byte 124,byte 124,byte 124,byte 124,byte 140,byte 123,
+ byte 123,byte 123,byte 123,byte 122,byte 122,byte 122,byte 122,byte 138,
+ byte 125,byte 125,byte 124,byte 124,byte 124,byte 124,byte 123,byte 123,
+ byte 123,byte 123,byte 123,byte 122,byte 122,byte 122,byte 105,byte 138,
+ byte 125,byte 125,byte 124,byte 124,byte 124,byte 124,byte 153,byte 123,
+ byte 123,byte 123,byte 152,byte 122,byte 122,byte 122,byte 105,byte 134,
+ byte 154,byte 154,byte 124,byte 124,byte 124,byte 153,byte 153,byte 153,
+ byte 123,byte 123,byte 135,byte 135,byte 122,byte 122,byte 105,byte 134,
+ byte 137,byte 137,byte 137,byte 120,byte 153,byte 153,byte 153,byte 136,
+ byte 136,byte 136,byte 135,byte 135,byte 135,byte 118,byte 105,byte 134,
+ byte 137,byte 137,byte 120,byte 120,byte 120,byte 153,byte 136,byte 136,
+ byte 136,byte 119,byte 119,byte 118,byte 118,byte 118,byte 118,byte 134,
+ byte 137,byte 137,byte 120,byte 120,byte 120,byte 120,byte 136,byte 136,
+ byte 119,byte 119,byte 119,byte 118,byte 118,byte 118,byte 101,byte 130,
+ byte 121,byte 121,byte 120,byte 120,byte 120,byte 120,byte 136,byte 119,
+ byte 119,byte 119,byte 102,byte 118,byte 118,byte 118,byte 101,byte 130,
+ byte 133,byte 133,byte 120,byte 120,byte 149,byte 132,byte 132,byte 119,
+ byte 119,byte 102,byte 148,byte 131,byte 131,byte 101,byte 101,byte 130,
+ byte 117,byte 117,byte 116,byte 116,byte 116,byte 132,byte 132,byte 115,
+ byte 115,byte 115,byte 131,byte 131,byte 114,byte 114,byte 114,byte 130,
+ byte 117,byte 117,byte 116,byte 116,byte 116,byte 116,byte 132,byte 115,
+ byte 115,byte 115,byte 131,byte 114,byte 114,byte 114,byte 114,byte 142,
+ byte 117,byte 117,byte 116,byte 116,byte 116,byte 116,byte 115,byte 115,
+ byte 115,byte 115,byte 98,byte 114,byte 114,byte 114,byte 97,byte 142,
+ byte 100,byte 100,byte 116,byte 99,byte 99,byte 99,byte 99,byte 115,
+ byte 98,byte 98,byte 98,byte 114,byte 114,byte 97,byte 97,byte 142,
+ byte 129,byte 129,byte 129,byte 129,byte 128,byte 128,byte 128,byte 128,
+ byte 143,byte 143,byte 143,byte 143,byte 142,byte 142,byte 142,byte 142,
+ byte 113,byte 113,byte 112,byte 112,byte 112,byte 112,byte 140,byte 140,
+ byte 127,byte 127,byte 110,byte 126,byte 126,byte 126,byte 109,byte 75,
+ byte 113,byte 113,byte 112,byte 112,byte 112,byte 112,byte 140,byte 140,
+ byte 127,byte 127,byte 110,byte 126,byte 126,byte 126,byte 109,byte 75,
+ byte 125,byte 125,byte 124,byte 124,byte 124,byte 124,byte 123,byte 123,
+ byte 123,byte 123,byte 123,byte 122,byte 122,byte 122,byte 105,byte 75,
+ byte 125,byte 125,byte 124,byte 124,byte 124,byte 124,byte 123,byte 123,
+ byte 123,byte 123,byte 106,byte 122,byte 122,byte 122,byte 105,byte 75,
+ byte 125,byte 125,byte 124,byte 124,byte 124,byte 124,byte 123,byte 123,
+ byte 123,byte 123,byte 106,byte 122,byte 122,byte 122,byte 105,byte 71,
+ byte 125,byte 125,byte 124,byte 124,byte 124,byte 107,byte 107,byte 123,
+ byte 123,byte 106,byte 106,byte 122,byte 122,byte 105,byte 105,byte 71,
+ byte 137,byte 137,byte 120,byte 120,byte 120,byte 107,byte 136,byte 136,
+ byte 136,byte 106,byte 106,byte 118,byte 118,byte 105,byte 88,byte 71,
+ byte 137,byte 137,byte 120,byte 120,byte 120,byte 120,byte 136,byte 136,
+ byte 119,byte 119,byte 119,byte 118,byte 118,byte 118,byte 101,byte 71,
+ byte 121,byte 121,byte 120,byte 120,byte 120,byte 120,byte 136,byte 119,
+ byte 119,byte 119,byte 102,byte 118,byte 118,byte 118,byte 101,byte 67,
+ byte 121,byte 121,byte 120,byte 120,byte 120,byte 103,byte 103,byte 119,
+ byte 119,byte 102,byte 102,byte 118,byte 118,byte 101,byte 101,byte 67,
+ byte 104,byte 104,byte 120,byte 103,byte 103,byte 103,byte 103,byte 119,
+ byte 102,byte 102,byte 102,byte 118,byte 118,byte 101,byte 84,byte 67,
+ byte 117,byte 117,byte 116,byte 116,byte 116,byte 116,byte 115,byte 115,
+ byte 115,byte 115,byte 115,byte 114,byte 114,byte 114,byte 114,byte 67,
+ byte 117,byte 117,byte 116,byte 116,byte 116,byte 116,byte 115,byte 115,
+ byte 115,byte 115,byte 115,byte 114,byte 114,byte 114,byte 97,byte 79,
+ byte 117,byte 117,byte 116,byte 116,byte 116,byte 99,byte 99,byte 115,
+ byte 115,byte 98,byte 98,byte 114,byte 114,byte 97,byte 97,byte 79,
+ byte 100,byte 100,byte 99,byte 99,byte 99,byte 99,byte 82,byte 98,
+ byte 98,byte 98,byte 81,byte 114,byte 97,byte 97,byte 97,byte 79,
+ byte 66,byte 66,byte 66,byte 66,byte 65,byte 65,byte 65,byte 65,
+ byte 64,byte 64,byte 64,byte 64,byte 79,byte 79,byte 79,byte 79,
+ byte 96,byte 96,byte 112,byte 112,byte 111,byte 111,byte 94,byte 127,
+ byte 127,byte 110,byte 110,byte 93,byte 126,byte 109,byte 92,byte 75,
+ byte 96,byte 96,byte 112,byte 112,byte 111,byte 111,byte 94,byte 127,
+ byte 127,byte 110,byte 110,byte 93,byte 126,byte 109,byte 92,byte 75,
+ byte 125,byte 125,byte 124,byte 124,byte 124,byte 124,byte 123,byte 123,
+ byte 123,byte 123,byte 106,byte 122,byte 122,byte 105,byte 105,byte 75,
+ byte 125,byte 125,byte 124,byte 124,byte 124,byte 107,byte 107,byte 123,
+ byte 123,byte 106,byte 106,byte 122,byte 122,byte 105,byte 105,byte 75,
+ byte 108,byte 108,byte 124,byte 124,byte 107,byte 107,byte 107,byte 123,
+ byte 123,byte 106,byte 106,byte 122,byte 122,byte 105,byte 88,byte 71,
+ byte 108,byte 108,byte 124,byte 107,byte 107,byte 107,byte 90,byte 123,
+ byte 106,byte 106,byte 106,byte 89,byte 122,byte 105,byte 88,byte 71,
+ byte 91,byte 91,byte 120,byte 107,byte 107,byte 90,byte 90,byte 136,
+ byte 106,byte 106,byte 89,byte 89,byte 118,byte 105,byte 88,byte 71,
+ byte 121,byte 121,byte 120,byte 120,byte 120,byte 120,byte 136,byte 119,
+ byte 119,byte 119,byte 102,byte 118,byte 118,byte 118,byte 101,byte 71,
+ byte 121,byte 121,byte 120,byte 120,byte 120,byte 103,byte 103,byte 119,
+ byte 119,byte 102,byte 102,byte 118,byte 118,byte 101,byte 101,byte 67,
+ byte 104,byte 104,byte 120,byte 103,byte 103,byte 103,byte 103,byte 119,
+ byte 102,byte 102,byte 102,byte 118,byte 118,byte 101,byte 84,byte 67,
+ byte 104,byte 104,byte 103,byte 103,byte 103,byte 103,byte 86,byte 102,
+ byte 102,byte 102,byte 85,byte 85,byte 101,byte 101,byte 84,byte 67,
+ byte 87,byte 87,byte 116,byte 116,byte 116,byte 86,byte 86,byte 115,
+ byte 115,byte 115,byte 85,byte 85,byte 114,byte 114,byte 84,byte 67,
+ byte 117,byte 117,byte 116,byte 116,byte 116,byte 116,byte 115,byte 115,
+ byte 115,byte 115,byte 98,byte 114,byte 114,byte 114,byte 97,byte 79,
+ byte 100,byte 100,byte 99,byte 99,byte 99,byte 99,byte 99,byte 115,
+ byte 98,byte 98,byte 98,byte 114,byte 114,byte 97,byte 97,byte 79,
+ byte 83,byte 83,byte 99,byte 99,byte 82,byte 82,byte 82,byte 98,
+ byte 98,byte 81,byte 81,byte 81,byte 97,byte 97,byte 80,byte 79,
+ byte 66,byte 66,byte 66,byte 66,byte 65,byte 65,byte 65,byte 65,
+ byte 64,byte 64,byte 64,byte 64,byte 79,byte 79,byte 79,byte 79,
+ byte 95,byte 95,byte 111,byte 111,byte 94,byte 94,byte 94,byte 77,
+ byte 110,byte 110,byte 93,byte 93,byte 76,byte 109,byte 92,byte 75,
+ byte 95,byte 95,byte 111,byte 111,byte 94,byte 94,byte 94,byte 77,
+ byte 110,byte 110,byte 93,byte 93,byte 76,byte 109,byte 92,byte 75,
+ byte 108,byte 108,byte 124,byte 111,byte 107,byte 94,byte 94,byte 123,
+ byte 123,byte 106,byte 93,byte 93,byte 122,byte 105,byte 92,byte 75,
+ byte 108,byte 108,byte 108,byte 107,byte 107,byte 107,byte 90,byte 123,
+ byte 106,byte 106,byte 106,byte 89,byte 122,byte 105,byte 88,byte 75,
+ byte 91,byte 91,byte 107,byte 107,byte 107,byte 90,byte 90,byte 123,
+ byte 106,byte 106,byte 89,byte 89,byte 105,byte 105,byte 88,byte 71,
+ byte 91,byte 91,byte 91,byte 107,byte 90,byte 90,byte 90,byte 73,
+ byte 106,byte 106,byte 89,byte 89,byte 72,byte 88,byte 88,byte 71,
+ byte 91,byte 91,byte 91,byte 90,byte 90,byte 90,byte 73,byte 73,
+ byte 106,byte 89,byte 89,byte 72,byte 72,byte 88,byte 88,byte 71,
+ byte 74,byte 74,byte 120,byte 120,byte 120,byte 73,byte 73,byte 119,
+ byte 119,byte 102,byte 89,byte 72,byte 72,byte 101,byte 101,byte 71,
+ byte 104,byte 104,byte 120,byte 103,byte 103,byte 103,byte 103,byte 119,
+ byte 102,byte 102,byte 102,byte 118,byte 118,byte 101,byte 84,byte 67,
+ byte 104,byte 104,byte 103,byte 103,byte 103,byte 103,byte 86,byte 102,
+ byte 102,byte 102,byte 85,byte 85,byte 101,byte 101,byte 84,byte 67,
+ byte 87,byte 87,byte 87,byte 103,byte 86,byte 86,byte 86,byte 86,
+ byte 102,byte 85,byte 85,byte 85,byte 85,byte 84,byte 84,byte 67,
+ byte 87,byte 87,byte 87,byte 86,byte 86,byte 86,byte 69,byte 69,
+ byte 115,byte 85,byte 85,byte 85,byte 68,byte 68,byte 67,byte 67,
+ byte 70,byte 70,byte 116,byte 116,byte 99,byte 69,byte 69,byte 69,
+ byte 115,byte 98,byte 85,byte 68,byte 68,byte 97,byte 97,byte 79,
+ byte 100,byte 100,byte 99,byte 99,byte 99,byte 82,byte 82,byte 98,
+ byte 98,byte 98,byte 81,byte 68,byte 97,byte 97,byte 97,byte 79,
+ byte 83,byte 83,byte 83,byte 82,byte 82,byte 82,byte 82,byte 98,
+ byte 81,byte 81,byte 81,byte 64,byte 97,byte 97,byte 80,byte 79,
+ byte 66,byte 66,byte 66,byte 66,byte 65,byte 65,byte 65,byte 65,
+ byte 64,byte 64,byte 64,byte 64,byte 79,byte 79,byte 79,byte 79,
+ byte 78,byte 78,byte 49,byte 49,byte 94,byte 77,byte 77,byte 48,
+ byte 48,byte 93,byte 93,byte 76,byte 76,byte 63,byte 92,byte 75,
+ byte 78,byte 78,byte 49,byte 49,byte 94,byte 77,byte 77,byte 48,
+ byte 48,byte 93,byte 93,byte 76,byte 76,byte 63,byte 92,byte 75,
+ byte 62,byte 62,byte 61,byte 61,byte 61,byte 61,byte 77,byte 60,
+ byte 60,byte 60,byte 93,byte 76,byte 59,byte 59,byte 59,byte 75,
+ byte 62,byte 62,byte 61,byte 61,byte 61,byte 61,byte 90,byte 60,
+ byte 60,byte 60,byte 89,byte 59,byte 59,byte 59,byte 88,byte 75,
+ byte 91,byte 91,byte 61,byte 61,byte 61,byte 90,byte 73,byte 60,
+ byte 60,byte 60,byte 89,byte 72,byte 59,byte 59,byte 88,byte 71,
+ byte 74,byte 74,byte 61,byte 61,byte 90,byte 73,byte 73,byte 73,
+ byte 60,byte 89,byte 89,byte 72,byte 72,byte 72,byte 71,byte 71,
+ byte 74,byte 74,byte 74,byte 90,byte 73,byte 73,byte 73,byte 73,
+ byte 56,byte 89,byte 72,byte 72,byte 72,byte 72,byte 71,byte 71,
+ byte 58,byte 58,byte 57,byte 57,byte 57,byte 73,byte 73,byte 56,
+ byte 56,byte 56,byte 72,byte 72,byte 55,byte 55,byte 55,byte 71,
+ byte 58,byte 58,byte 57,byte 57,byte 57,byte 57,byte 56,byte 56,
+ byte 56,byte 56,byte 56,byte 55,byte 55,byte 55,byte 55,byte 67,
+ byte 87,byte 87,byte 57,byte 57,byte 57,byte 86,byte 86,byte 56,
+ byte 56,byte 56,byte 85,byte 85,byte 55,byte 55,byte 84,byte 67,
+ byte 87,byte 87,byte 87,byte 86,byte 86,byte 86,byte 69,byte 69,
+ byte 56,byte 85,byte 85,byte 85,byte 68,byte 68,byte 67,byte 67,
+ byte 70,byte 70,byte 70,byte 53,byte 69,byte 69,byte 69,byte 69,
+ byte 52,byte 85,byte 85,byte 68,byte 68,byte 68,byte 67,byte 67,
+ byte 70,byte 70,byte 53,byte 53,byte 53,byte 69,byte 69,byte 52,
+ byte 52,byte 52,byte 68,byte 68,byte 68,byte 51,byte 51,byte 79,
+ byte 54,byte 54,byte 53,byte 53,byte 53,byte 69,byte 69,byte 52,
+ byte 52,byte 52,byte 68,byte 68,byte 51,byte 51,byte 80,byte 79,
+ byte 83,byte 83,byte 53,byte 82,byte 82,byte 65,byte 65,byte 52,
+ byte 52,byte 81,byte 64,byte 64,byte 51,byte 80,byte 80,byte 79,
+ byte 66,byte 66,byte 66,byte 66,byte 65,byte 65,byte 65,byte 65,
+ byte 64,byte 64,byte 64,byte 64,byte 79,byte 79,byte 79,byte 79,
+ byte 50,byte 50,byte 49,byte 49,byte 49,byte 77,byte 77,byte 48,
+ byte 48,byte 48,byte 76,byte 76,byte 63,byte 63,byte 46,byte 12,
+ byte 50,byte 50,byte 49,byte 49,byte 49,byte 77,byte 77,byte 48,
+ byte 48,byte 48,byte 76,byte 76,byte 63,byte 63,byte 46,byte 12,
+ byte 62,byte 62,byte 61,byte 61,byte 61,byte 61,byte 77,byte 60,
+ byte 60,byte 60,byte 60,byte 59,byte 59,byte 59,byte 59,byte 12,
+ byte 62,byte 62,byte 61,byte 61,byte 61,byte 61,byte 60,byte 60,
+ byte 60,byte 60,byte 60,byte 59,byte 59,byte 59,byte 42,byte 12,
+ byte 62,byte 62,byte 61,byte 61,byte 61,byte 61,byte 73,byte 60,
+ byte 60,byte 60,byte 43,byte 59,byte 59,byte 59,byte 42,byte 8,
+ byte 74,byte 74,byte 61,byte 61,byte 61,byte 73,byte 73,byte 60,
+ byte 60,byte 60,byte 72,byte 72,byte 72,byte 59,byte 42,byte 8,
+ byte 74,byte 74,byte 74,byte 57,byte 73,byte 73,byte 73,byte 73,
+ byte 56,byte 56,byte 72,byte 72,byte 72,byte 72,byte 42,byte 8,
+ byte 58,byte 58,byte 57,byte 57,byte 57,byte 57,byte 73,byte 56,
+ byte 56,byte 56,byte 72,byte 55,byte 55,byte 55,byte 55,byte 8,
+ byte 58,byte 58,byte 57,byte 57,byte 57,byte 57,byte 56,byte 56,
+ byte 56,byte 56,byte 56,byte 55,byte 55,byte 55,byte 38,byte 4,
+ byte 58,byte 58,byte 57,byte 57,byte 57,byte 57,byte 56,byte 56,
+ byte 56,byte 56,byte 39,byte 55,byte 55,byte 55,byte 38,byte 4,
+ byte 70,byte 70,byte 57,byte 57,byte 40,byte 69,byte 69,byte 69,
+ byte 56,byte 39,byte 85,byte 68,byte 68,byte 38,byte 38,byte 4,
+ byte 70,byte 70,byte 53,byte 53,byte 53,byte 69,byte 69,byte 52,
+ byte 52,byte 52,byte 68,byte 68,byte 68,byte 51,byte 51,byte 4,
+ byte 54,byte 54,byte 53,byte 53,byte 53,byte 69,byte 69,byte 52,
+ byte 52,byte 52,byte 68,byte 68,byte 51,byte 51,byte 51,byte 0,
+ byte 54,byte 54,byte 53,byte 53,byte 53,byte 53,byte 69,byte 52,
+ byte 52,byte 52,byte 35,byte 51,byte 51,byte 51,byte 34,byte 0,
+ byte 37,byte 37,byte 53,byte 36,byte 36,byte 36,byte 36,byte 52,
+ byte 35,byte 35,byte 35,byte 51,byte 51,byte 34,byte 34,byte 0,
+ byte 3,byte 3,byte 3,byte 3,byte 2,byte 2,byte 2,byte 2,
+ byte 1,byte 1,byte 1,byte 1,byte 0,byte 0,byte 0,byte 0,
+ byte 33,byte 33,byte 49,byte 49,byte 32,byte 32,byte 77,byte 48,
+ byte 48,byte 47,byte 47,byte 63,byte 63,byte 46,byte 46,byte 12,
+ byte 33,byte 33,byte 49,byte 49,byte 32,byte 32,byte 77,byte 48,
+ byte 48,byte 47,byte 47,byte 63,byte 63,byte 46,byte 46,byte 12,
+ byte 62,byte 62,byte 61,byte 61,byte 61,byte 61,byte 60,byte 60,
+ byte 60,byte 43,byte 43,byte 59,byte 59,byte 59,byte 42,byte 12,
+ byte 62,byte 62,byte 61,byte 61,byte 61,byte 44,byte 44,byte 60,
+ byte 60,byte 43,byte 43,byte 59,byte 59,byte 42,byte 42,byte 12,
+ byte 45,byte 45,byte 61,byte 61,byte 44,byte 44,byte 44,byte 60,
+ byte 60,byte 43,byte 43,byte 59,byte 59,byte 42,byte 42,byte 8,
+ byte 45,byte 45,byte 61,byte 44,byte 44,byte 44,byte 73,byte 60,
+ byte 43,byte 43,byte 26,byte 72,byte 59,byte 42,byte 42,byte 8,
+ byte 74,byte 74,byte 57,byte 44,byte 44,byte 73,byte 73,byte 56,
+ byte 43,byte 43,byte 26,byte 72,byte 72,byte 42,byte 42,byte 8,
+ byte 58,byte 58,byte 57,byte 57,byte 57,byte 57,byte 56,byte 56,
+ byte 56,byte 56,byte 39,byte 55,byte 55,byte 55,byte 38,byte 8,
+ byte 58,byte 58,byte 57,byte 57,byte 57,byte 40,byte 40,byte 56,
+ byte 56,byte 39,byte 39,byte 55,byte 55,byte 38,byte 38,byte 4,
+ byte 41,byte 41,byte 40,byte 40,byte 40,byte 40,byte 40,byte 56,
+ byte 39,byte 39,byte 39,byte 55,byte 55,byte 38,byte 38,byte 4,
+ byte 41,byte 41,byte 40,byte 40,byte 40,byte 23,byte 23,byte 39,
+ byte 39,byte 39,byte 22,byte 68,byte 38,byte 38,byte 38,byte 4,
+ byte 54,byte 54,byte 53,byte 53,byte 53,byte 69,byte 69,byte 52,
+ byte 52,byte 52,byte 68,byte 68,byte 51,byte 51,byte 21,byte 4,
+ byte 54,byte 54,byte 53,byte 53,byte 53,byte 53,byte 69,byte 52,
+ byte 52,byte 52,byte 35,byte 51,byte 51,byte 51,byte 34,byte 0,
+ byte 37,byte 37,byte 53,byte 36,byte 36,byte 36,byte 36,byte 52,
+ byte 35,byte 35,byte 35,byte 51,byte 51,byte 34,byte 34,byte 0,
+ byte 37,byte 37,byte 36,byte 36,byte 36,byte 36,byte 36,byte 35,
+ byte 35,byte 35,byte 35,byte 18,byte 34,byte 34,byte 34,byte 0,
+ byte 3,byte 3,byte 3,byte 3,byte 2,byte 2,byte 2,byte 2,
+ byte 1,byte 1,byte 1,byte 1,byte 0,byte 0,byte 0,byte 0,
+ byte 16,byte 16,byte 32,byte 32,byte 31,byte 31,byte 31,byte 47,
+ byte 47,byte 30,byte 30,byte 30,byte 46,byte 46,byte 29,byte 12,
+ byte 16,byte 16,byte 32,byte 32,byte 31,byte 31,byte 31,byte 47,
+ byte 47,byte 30,byte 30,byte 30,byte 46,byte 46,byte 29,byte 12,
+ byte 45,byte 45,byte 44,byte 44,byte 44,byte 44,byte 31,byte 60,
+ byte 43,byte 43,byte 30,byte 59,byte 59,byte 42,byte 42,byte 12,
+ byte 45,byte 45,byte 44,byte 44,byte 44,byte 44,byte 27,byte 43,
+ byte 43,byte 43,byte 26,byte 26,byte 42,byte 42,byte 42,byte 12,
+ byte 28,byte 28,byte 44,byte 44,byte 27,byte 27,byte 27,byte 43,
+ byte 43,byte 26,byte 26,byte 26,byte 42,byte 42,byte 25,byte 8,
+ byte 28,byte 28,byte 44,byte 44,byte 27,byte 27,byte 27,byte 43,
+ byte 43,byte 26,byte 26,byte 9,byte 42,byte 42,byte 25,byte 8,
+ byte 28,byte 28,byte 28,byte 27,byte 27,byte 27,byte 10,byte 43,
+ byte 26,byte 26,byte 26,byte 9,byte 42,byte 42,byte 25,byte 8,
+ byte 41,byte 41,byte 57,byte 40,byte 40,byte 40,byte 40,byte 56,
+ byte 39,byte 39,byte 39,byte 55,byte 55,byte 38,byte 38,byte 8,
+ byte 41,byte 41,byte 40,byte 40,byte 40,byte 40,byte 23,byte 39,
+ byte 39,byte 39,byte 22,byte 55,byte 38,byte 38,byte 38,byte 4,
+ byte 24,byte 24,byte 40,byte 40,byte 23,byte 23,byte 23,byte 39,
+ byte 39,byte 22,byte 22,byte 22,byte 38,byte 38,byte 21,byte 4,
+ byte 24,byte 24,byte 24,byte 23,byte 23,byte 23,byte 23,byte 39,
+ byte 22,byte 22,byte 22,byte 5,byte 38,byte 38,byte 21,byte 4,
+ byte 24,byte 24,byte 53,byte 23,byte 23,byte 6,byte 6,byte 52,
+ byte 52,byte 22,byte 5,byte 5,byte 51,byte 21,byte 21,byte 4,
+ byte 37,byte 37,byte 53,byte 36,byte 36,byte 36,byte 36,byte 52,
+ byte 35,byte 35,byte 35,byte 51,byte 51,byte 34,byte 34,byte 0,
+ byte 37,byte 37,byte 36,byte 36,byte 36,byte 36,byte 36,byte 35,
+ byte 35,byte 35,byte 35,byte 18,byte 34,byte 34,byte 34,byte 0,
+ byte 20,byte 20,byte 36,byte 36,byte 19,byte 19,byte 19,byte 35,
+ byte 35,byte 18,byte 18,byte 18,byte 34,byte 34,byte 17,byte 0,
+ byte 3,byte 3,byte 3,byte 3,byte 2,byte 2,byte 2,byte 2,
+ byte 1,byte 1,byte 1,byte 1,byte 0,byte 0,byte 0,byte 0,
+ byte 15,byte 15,byte 15,byte 15,byte 14,byte 14,byte 14,byte 14,
+ byte 13,byte 13,byte 13,byte 13,byte 12,byte 12,byte 12,byte 12,
+ byte 15,byte 15,byte 15,byte 15,byte 14,byte 14,byte 14,byte 14,
+ byte 13,byte 13,byte 13,byte 13,byte 12,byte 12,byte 12,byte 12,
+ byte 15,byte 15,byte 15,byte 15,byte 14,byte 14,byte 14,byte 14,
+ byte 13,byte 13,byte 13,byte 13,byte 12,byte 12,byte 12,byte 12,
+ byte 15,byte 15,byte 15,byte 15,byte 14,byte 14,byte 14,byte 14,
+ byte 13,byte 13,byte 13,byte 13,byte 12,byte 12,byte 12,byte 12,
+ byte 11,byte 11,byte 11,byte 11,byte 10,byte 10,byte 10,byte 10,
+ byte 9,byte 9,byte 9,byte 9,byte 8,byte 8,byte 8,byte 8,
+ byte 11,byte 11,byte 11,byte 11,byte 10,byte 10,byte 10,byte 10,
+ byte 9,byte 9,byte 9,byte 9,byte 8,byte 8,byte 8,byte 8,
+ byte 11,byte 11,byte 11,byte 11,byte 10,byte 10,byte 10,byte 10,
+ byte 9,byte 9,byte 9,byte 9,byte 8,byte 8,byte 8,byte 8,
+ byte 11,byte 11,byte 11,byte 11,byte 10,byte 10,byte 10,byte 10,
+ byte 9,byte 9,byte 9,byte 9,byte 8,byte 8,byte 8,byte 8,
+ byte 7,byte 7,byte 7,byte 7,byte 6,byte 6,byte 6,byte 6,
+ byte 5,byte 5,byte 5,byte 5,byte 4,byte 4,byte 4,byte 4,
+ byte 7,byte 7,byte 7,byte 7,byte 6,byte 6,byte 6,byte 6,
+ byte 5,byte 5,byte 5,byte 5,byte 4,byte 4,byte 4,byte 4,
+ byte 7,byte 7,byte 7,byte 7,byte 6,byte 6,byte 6,byte 6,
+ byte 5,byte 5,byte 5,byte 5,byte 4,byte 4,byte 4,byte 4,
+ byte 7,byte 7,byte 7,byte 7,byte 6,byte 6,byte 6,byte 6,
+ byte 5,byte 5,byte 5,byte 5,byte 4,byte 4,byte 4,byte 4,
+ byte 3,byte 3,byte 3,byte 3,byte 2,byte 2,byte 2,byte 2,
+ byte 1,byte 1,byte 1,byte 1,byte 0,byte 0,byte 0,byte 0,
+ byte 3,byte 3,byte 3,byte 3,byte 2,byte 2,byte 2,byte 2,
+ byte 1,byte 1,byte 1,byte 1,byte 0,byte 0,byte 0,byte 0,
+ byte 3,byte 3,byte 3,byte 3,byte 2,byte 2,byte 2,byte 2,
+ byte 1,byte 1,byte 1,byte 1,byte 0,byte 0,byte 0,byte 0,
+ byte 3,byte 3,byte 3,byte 3,byte 2,byte 2,byte 2,byte 2,
+ byte 1,byte 1,byte 1,byte 1,byte 0,byte 0,byte 0,byte 0,
+};
+
+clamp: array of int;
+rgbvmap: array of int;
+
+init(d: ref Display)
+{
+ # initialise in a way that make races slightly wasteful but benign
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ if(draw == nil)
+ draw = load Draw Draw->PATH;
+ if(clamp == nil){
+ m := array[64+256+64] of int;
+ for(j:=0; j<64; j++)
+ m[j] = 0;
+ for(j=0; j<256; j++)
+ m[64+j] = (j>>4);
+ for(j=0; j<64; j++)
+ m[64+256+j] = (255>>4);
+ clamp = m;
+ }
+ if(rgbvmap == nil){
+ m := array[3*256] of int;
+ for(j:=0; j<256; j++)
+ (m[3*j+0], m[3*j+1], m[3*j+2]) = d.cmap2rgb(j);
+ rgbvmap = m;
+ }
+}
+
+remap(i: ref RImagefile->Rawimage, d: ref Display, errdiff: int): (ref Image, string)
+{
+ if(sys == nil || draw == nil || clamp == nil || rgbvmap == nil)
+ init(d); # temporarily do this here until all clients change to call init
+ j: int;
+ im := d.newimage(i.r, Draw->CMAP8, 0, Draw->Black);
+ dx := i.r.max.x-i.r.min.x;
+ dy := i.r.max.y-i.r.min.y;
+ cmap := i.cmap;
+
+ pic := i.chans[0];
+
+ case i.chandesc{
+ RImagefile->CRGB1 =>
+ if(cmap == nil)
+ return (nil, sys->sprint("image has no color map"));
+ if(i.nchans != 1)
+ return (nil, sys->sprint("can't handle nchans %d", i.nchans));
+ for(j=1; j<=8; j++)
+ if(len cmap == 3*(1<<j))
+ break;
+ if(j > 8)
+ return (nil, sys->sprint("can't understand colormap size 3*%d", len cmap/3));
+ if(len cmap != 3*256){
+ # to avoid a range check in inner loop below, make a full-size cmap
+ cmap1 := array[3*256] of byte;
+ cmap1[0:] = cmap[0:];
+ cmap = cmap1;
+ errdiff = 0; # why not?
+ }
+ if(errdiff == 0){
+ map := array[256] of byte;
+ k := 0;
+ for(j=0; j<256; j++){
+ r := int cmap[k]>>4;
+ g := int cmap[k+1]>>4;
+ b := int cmap[k+2]>>4;
+ k += 3;
+ map[j] = byte 255 - closest[b+16*(g+16*r)];
+ }
+ for(j=0; j<len pic; j++)
+ pic[j] = map[int pic[j]];
+ }else{
+ # modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16
+ ered := array[dx+1] of int;
+ egrn := array[dx+1] of int;
+ eblu := array[dx+1] of int;
+ for(j=0; j<=dx; j++)
+ ered[j] = 0;
+ egrn[0:] = ered[0:];
+ eblu[0:] = ered[0:];
+ p := 0;
+ for(y:=0; y<dy; y++){
+ er := 0;
+ eg := 0;
+ eb := 0;
+ for(x:=0; x<dx; x++){
+ in := 3*int pic[p];
+ r := int cmap[in+0]+ered[x];
+ g := int cmap[in+1]+egrn[x];
+ b := int cmap[in+2]+eblu[x];
+ r1 := clamp[r+64];
+ g1 := clamp[g+64];
+ b1 := clamp[b+64];
+ col := 255 - int closest[b1+16*(g1+16*r1)];
+ pic[p++] = byte col;
+
+ col *= 3;
+ r -= rgbvmap[col+0];
+ t := (3*r)>>4;
+ ered[x] = t+er;
+ ered[x+1] += t;
+ er = r-3*t;
+
+ g -= rgbvmap[col+1];
+ t = (3*g)>>4;
+ egrn[x] = t+eg;
+ egrn[x+1] += t;
+ eg = g-3*t;
+
+ b -= rgbvmap[col+2];
+ t = (3*b)>>4;
+ eblu[x] = t+eb;
+ eblu[x+1] += t;
+ eb = b-3*t;
+ }
+ }
+ }
+ RImagefile->CRGB =>
+ if(i.nchans != 3)
+ return (nil, sys->sprint("RGB image has %d channels", i.nchans));
+ rpic := i.chans[0];
+ gpic := i.chans[1];
+ bpic := i.chans[2];
+ if(errdiff == 0){
+ for(j=0; j<len rpic; j++){
+ r := int rpic[j]>>4;
+ g := int gpic[j]>>4;
+ b := int bpic[j]>>4;
+ pic[j] = byte 255 - byte closest[b+16*(g+16*r)];
+ }
+ }else{
+ # modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16
+ ered := array[dx+1] of int;
+ egrn := array[dx+1] of int;
+ eblu := array[dx+1] of int;
+ for(j=0; j<=dx; j++)
+ ered[j] = 0;
+ egrn[0:] = ered[0:];
+ eblu[0:] = ered[0:];
+ p := 0;
+ for(y:=0; y<dy; y++){
+ er := 0;
+ eg := 0;
+ eb := 0;
+ for(x:=0; x<dx; x++){
+ r := int rpic[p]+ered[x];
+ g := int gpic[p]+egrn[x];
+ b := int bpic[p]+eblu[x];
+ r1 := clamp[r+64];
+ g1 := clamp[g+64];
+ b1 := clamp[b+64];
+ col := 255 - int closest[b1+16*(g1+16*r1)];
+ pic[p++] = byte col;
+
+ col *= 3;
+ r -= rgbvmap[col+0];
+ t := (3*r)>>4;
+ ered[x] = t+er;
+ ered[x+1] += t;
+ er = r-3*t;
+
+ g -= rgbvmap[col+1];
+ t = (3*g)>>4;
+ egrn[x] = t+eg;
+ egrn[x+1] += t;
+ eg = g-3*t;
+
+ b -= rgbvmap[col+2];
+ t = (3*b)>>4;
+ eblu[x] = t+eb;
+ eblu[x+1] += t;
+ eb = b-3*t;
+ }
+ }
+ }
+ RImagefile->CY =>
+ if(i.nchans != 1)
+ return (nil, sys->sprint("Y image has %d chans", i.nchans));
+ rpic := i.chans[0];
+ if(errdiff == 0){
+ for(j=0; j<len pic; j++){
+ r := int rpic[j]>>4;
+ pic[j] = byte 255 - byte closest[r+16*(r+16*r)];
+ }
+ }else{
+ # modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16
+ ered := array[dx+1] of int;
+ for(j=0; j<=dx; j++)
+ ered[j] = 0;
+ p := 0;
+ for(y:=0; y<dy; y++){
+ er := 0;
+ for(x:=0; x<dx; x++){
+ r := int rpic[p]+ered[x];
+ r1 := clamp[r+64];
+ col := 255-int closest[r1+16*(r1+16*r1)];
+ pic[p++] = byte col;
+
+ col *= 3;
+ r -= rgbvmap[col+0];
+ t := (3*r)>>4;
+ ered[x] = t+er;
+ ered[x+1] += t;
+ er = r-3*t;
+ }
+ }
+ }
+ }
+ im.writepixels(im.r, pic);
+ return (im, "");
+}
diff --git a/appl/lib/inflate.b b/appl/lib/inflate.b
new file mode 100644
index 00000000..3d8a6d22
--- /dev/null
+++ b/appl/lib/inflate.b
@@ -0,0 +1,745 @@
+# gzip-compatible decompression filter.
+
+implement Filter;
+
+include "sys.m";
+ sys: Sys;
+include "filter.m";
+
+GZMAGIC1: con byte 16r1f;
+GZMAGIC2: con byte 16r8b;
+
+GZDEFLATE: con byte 8;
+
+GZFTEXT: con 1 << 0; # file is text
+GZFHCRC: con 1 << 1; # crc of header included
+GZFEXTRA: con 1 << 2; # extra header included
+GZFNAME: con 1 << 3; # name of file included
+GZFCOMMENT: con 1 << 4; # header comment included
+GZFMASK: con (1 << 5) - 1; # mask of specified bits
+
+GZXBEST: con byte 2; # used maximum compression algorithm
+GZXFAST: con byte 4; # used fast algorithm little compression
+
+GZOSFAT: con byte 0; # FAT file system
+GZOSAMIGA: con byte 1; # Amiga
+GZOSVMS: con byte 2; # VMS or OpenVMS
+GZOSUNIX: con byte 3; # Unix
+GZOSVMCMS: con byte 4; # VM/CMS
+GZOSATARI: con byte 5; # Atari TOS
+GZOSHPFS: con byte 6; # HPFS file system
+GZOSMAC: con byte 7; # Macintosh
+GZOSZSYS: con byte 8; # Z-System
+GZOSCPM: con byte 9; # CP/M
+GZOSTOPS20: con byte 10; # TOPS-20
+GZOSNTFS: con byte 11; # NTFS file system
+GZOSQDOS: con byte 12; # QDOS
+GZOSACORN: con byte 13; # Acorn RISCOS
+GZOSUNK: con byte 255;
+
+GZCRCPOLY: con int 16redb88320;
+GZOSINFERNO: con GZOSUNIX;
+
+# huffman code table
+Huff: adt
+{
+ bits: int; # length of the code
+ encode: int; # the code
+};
+
+# huffman decode table
+DeHuff: adt
+{
+ l1: array of L1; # the table
+ nb1: int; # no. of bits in first level
+ nb2: int; # no. of bits in second level
+};
+
+# first level of decode table
+L1: adt
+{
+ bits: int; # length of the code
+ decode: int; # the symbol
+ l2: array of L2;
+};
+
+# second level
+L2: adt
+{
+ bits: int; # length of the code
+ decode: int; # the symbol
+};
+
+DeflateUnc: con 0; # uncompressed block
+DeflateFix: con 1; # fixed huffman codes
+DeflateDyn: con 2; # dynamic huffman codes
+DeflateErr: con 3; # reserved BTYPE (error)
+
+DeflateEob: con 256; # end of block code in lit/len book
+
+LenStart: con 257; # start of length codes in litlen
+LenEnd: con 285; # greatest valid length code
+Nlitlen: con 288; # number of litlen codes
+Noff: con 30; # number of offset codes
+Nclen: con 19; # number of codelen codes
+
+MaxHuffBits: con 15; # max bits in a huffman code
+RunlenBits: con 7; # max bits in a run-length huffman code
+MaxOff: con 32*1024; # max lempel-ziv distance
+
+Blocksize: con 32 * 1024;
+
+# tables from RFC 1951, section 3.2.5
+litlenbase := array[Noff] of
+{
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13,
+ 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
+ 67, 83, 99, 115, 131, 163, 195, 227, 258
+};
+
+litlenextra := array[Noff] of
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+ 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4,
+ 5, 5, 5, 5, 0
+};
+
+offbase := array[Noff] of
+{
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25,
+ 33, 49, 65, 97, 129, 193, 257, 385, 513, 769,
+ 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
+};
+
+offextra := array[Noff] of
+{
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3,
+ 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
+};
+
+# order of run-length codes
+clenorder := array[Nclen] of
+{
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+};
+
+# fixed huffman tables
+litlentab: array of Huff;
+offtab: array of Huff;
+
+# their decoding table counterparts
+litlendec: ref DeHuff;
+offdec: ref DeHuff;
+
+revtab: array of byte; # bit reversal for endian swap of huffman codes
+mask: array of int; # for masking low-order n bits of an int
+
+State: adt {
+ ibuf, obuf: array of byte;
+ c: chan of ref Rq;
+ rc: chan of int;
+ in: int; # next byte to consume from input buffer
+ ein: int; # valid bytes in input buffer
+ out: int; # valid bytes in output buffer
+ hist: array of byte; # history buffer for lempel-ziv backward references
+ usehist: int; # == 1 if 'hist' is valid
+ crctab: array of int;
+ crc, tot: int;
+
+ reg: int; # 24-bit shift register
+ nbits: int; # number of valid bits in reg
+ svreg: int; # save reg for efficient ungets
+ svn: int; # number of bits gotten in last call to getn()
+ # reg bits are consumed from right to left
+ # so low-order byte of reg came first in the input stream
+ headers: int;
+};
+
+
+init()
+{
+ sys = load Sys Sys->PATH;
+
+ # byte reverse table
+ revtab = array[256] of byte;
+ for(i := 0; i < 256; i++){
+ revtab[i] = byte 0;
+ for(j := 0; j < 8; j++) {
+ if(i & (1 << j))
+ revtab[i] |= byte 16r80 >> j;
+ }
+ }
+
+ # bit-masking table
+ mask = array[MaxHuffBits+1] of int;
+ for(i = 0; i <= MaxHuffBits; i++)
+ mask[i] = (1 << i) - 1;
+
+ litlentab = array[Nlitlen] of Huff;
+
+ # static litlen bit lengths
+ for(i = 0; i < 144; i++)
+ litlentab[i].bits = 8;
+ for(i = 144; i < 256; i++)
+ litlentab[i].bits = 9;
+ for(i = 256; i < 280; i++)
+ litlentab[i].bits = 7;
+ for(i = 280; i < Nlitlen; i++)
+ litlentab[i].bits = 8;
+
+ bitcount := array[MaxHuffBits+1] of { * => 0 };
+ bitcount[8] += 144 - 0;
+ bitcount[9] += 256 - 144;
+ bitcount[7] += 280 - 256;
+ bitcount[8] += Nlitlen - 280;
+
+ hufftabinit(litlentab, Nlitlen, bitcount, 9);
+ litlendec = decodeinit(litlentab, Nlitlen, 9, 0);
+
+ offtab = array[Noff] of Huff;
+
+ # static offset bit lengths
+ for(i = 0; i < Noff; i++)
+ offtab[i].bits = 5;
+
+ for(i = 0; i < 5; i++)
+ bitcount[i] = 0;
+ bitcount[5] = Noff;
+
+ hufftabinit(offtab, Noff, bitcount, 5);
+ offdec = decodeinit(offtab, Noff, 5, 0);
+}
+
+start(params: string): chan of ref Rq
+{
+ s := ref State;
+ s.c = chan of ref Rq;
+ s.rc = chan of int;
+ s.ibuf = array[Blocksize] of byte;
+ s.obuf = array[Blocksize] of byte;
+ s.in = 0;
+ s.ein = 0;
+ s.out = 0;
+ s.usehist = 0;
+ s.reg = 0;
+ s.nbits = 0;
+ s.crc = 0;
+ s.tot = 0;
+ s.hist = array[Blocksize] of byte;
+ s.headers = (params != nil && params[0] == 'h');
+ if (s.headers)
+ s.crctab = mkcrctab(GZCRCPOLY);
+ spawn inflate(s);
+ return s.c;
+}
+
+inflate(s: ref State)
+{
+ s.c <-= ref Rq.Start(sys->pctl(0, nil));
+ if (s.headers)
+ header(s);
+
+ for(;;) {
+ bfinal := getn(s, 1, 0);
+ btype := getn(s, 2, 0);
+ case(btype) {
+ DeflateUnc =>
+ flushbits(s);
+ unclen := getb(s);
+ unclen |= getb(s) << 8;
+ nlen := getb(s);
+ nlen |= getb(s) << 8;
+ if(unclen != (~nlen & 16rFFFF))
+ fatal(s, "corrupted data");
+ for(; unclen > 0; unclen--) {
+ # inline putb(s, getb(s));
+ b := byte getb(s);
+ if(s.out >= MaxOff)
+ flushout(s);
+ s.obuf[s.out++] = b;
+ }
+ DeflateFix =>
+ decodeblock(s, litlendec, offdec);
+ DeflateDyn =>
+ dynhuff(s);
+ DeflateErr =>
+ fatal(s, "bad block type");
+ }
+ if(bfinal) {
+ if(s.out) {
+ if (s.headers)
+ outblock(s);
+ s.c <- = ref Rq.Result(s.obuf[0:s.out], s.rc);
+ flag := <- s.rc;
+ if (flag == -1)
+ exit;
+ }
+ flushbits(s);
+ if (s.headers)
+ footer(s);
+ s.c <-= ref Rq.Finished(s.ibuf[s.in - s.nbits/8:s.ein]);
+ exit;
+ }
+ }
+}
+
+header(s: ref State)
+{
+ if(byte getb(s) != GZMAGIC1 || byte getb(s) != GZMAGIC2)
+ fatal(s, "not a gzip file");
+
+ if(byte getb(s) != GZDEFLATE)
+ fatal(s, "not compressed with deflate");
+
+ flags := getb(s);
+ if(flags & ~GZFMASK)
+ fatal(s, "reserved flag bits set");
+
+ # read modification time (ignored)
+ mtime := getb(s);
+ mtime |= (getb(s) << 8);
+ mtime |= (getb(s) << 16);
+ mtime |= (getb(s) << 24);
+ s.c <-= ref Rq.Info("mtime " + string mtime);
+ xfl := getb(s);
+ os := getb(s);
+
+ # skip optional "extra field"
+ if(flags & GZFEXTRA) {
+ skip := getb(s);
+ skip |= getb(s) << 8;
+ while (skip-- > 0)
+ getb(s);
+ }
+
+ # read optional filename (ignored)
+ file: string;
+ if(flags & GZFNAME){
+ n := 0;
+ while(c := getb(s))
+ file[n++] = c;
+ s.c <-= ref Rq.Info("file " + file);
+ }
+
+ # skip optional comment
+ if(flags & GZFCOMMENT) {
+ while(getb(s))
+ ;
+ }
+
+ # skip optional CRC16 field
+ if(flags & GZFHCRC) {
+ getb(s);
+ getb(s);
+ }
+}
+
+footer(s: ref State)
+{
+ fcrc := getword(s);
+ if(s.crc != fcrc)
+ fatal(s, sys->sprint("crc mismatch: computed %ux, expected %ux", s.crc, fcrc));
+ ftot := getword(s);
+ if(s.tot != ftot)
+ fatal(s, sys->sprint("byte count mismatch: computed %d, expected %d", s.tot, ftot));
+}
+
+getword(s: ref State): int
+{
+ n := 0;
+ for(i := 0; i < 4; i++)
+ n |= getb(s) << (8 * i);
+ return n;
+}
+
+#
+# uncompress a block using given huffman decoding tables
+#
+decodeblock(s: ref State, litlendec, offdec: ref DeHuff)
+{
+ b: byte;
+
+ for(;;) {
+ sym := decodesym(s, litlendec);
+ if(sym < DeflateEob) { # literal byte
+ # inline putb(s, byte sym);
+ b = byte sym;
+ if(s.out >= MaxOff)
+ flushout(s);
+ s.obuf[s.out++] = b;
+ } else if(sym == DeflateEob) { # End-of-block
+ break;
+ } else { # lempel-ziv <length, distance>
+ if(sym > LenEnd)
+ fatal(s, "symbol too long");
+ xbits := litlenextra[sym - LenStart];
+ xtra := 0;
+ if(xbits)
+ xtra = getn(s, xbits, 0);
+ length := litlenbase[sym - LenStart] + xtra;
+
+ sym = decodesym(s, offdec);
+ if(sym >= Noff)
+ fatal(s, "symbol too long");
+ xbits = offextra[sym];
+ if(xbits)
+ xtra = getn(s, xbits, 0);
+ else
+ xtra = 0;
+ dist := offbase[sym] + xtra;
+ if(dist > s.out && s.usehist == 0)
+ fatal(s, "corrupted data");
+ for(i := 0; i < length; i++) {
+ # inline putb(lzbyte(dist));
+ ix := s.out - dist;
+ if(dist <= s.out)
+ b = s.obuf[ix];
+ else
+ b = s.hist[MaxOff + ix];
+ if(s.out >= MaxOff)
+ flushout(s);
+ s.obuf[s.out++] = b;
+ }
+ }
+ }
+}
+
+#
+# decode next symbol in input stream using given huffman decoding table
+#
+decodesym(s: ref State, dec: ref DeHuff): int
+{
+ code, bits, n: int;
+
+ l1 := dec.l1;
+ nb1 := dec.nb1;
+ nb2 := dec.nb2;
+
+ code = getn(s, nb1, 1);
+ l2 := l1[code].l2;
+ if(l2 == nil) { # first level table has answer
+ bits = l1[code].bits;
+ if(bits == 0)
+ fatal(s, "corrupt data");
+ if(nb1 > bits) {
+ # inline ungetn(nb1 - bits);
+ n = nb1 - bits;
+ s.reg = s.svreg >> (s.svn - n);
+ s.nbits += n;
+ }
+ return l1[code].decode;
+ }
+ # must advance to second-level table
+ code = getn(s, nb2, 1);
+ bits = l2[code].bits;
+ if(bits == 0)
+ fatal(s, "corrupt data");
+ if(nb1 + nb2 > bits) {
+ # inline ungetn(nb1 + nb2 - bits);
+ n = nb1 + nb2 - bits;
+ s.reg = s.svreg >> (s.svn - n);
+ s.nbits += n;
+ }
+ return l2[code].decode;
+}
+
+#
+# uncompress a block that was encoded with dynamic huffman codes
+# RFC 1951, section 3.2.7
+#
+dynhuff(s: ref State)
+{
+ hlit := getn(s, 5, 0) + 257;
+ hdist := getn(s, 5, 0) + 1;
+ hclen := getn(s, 4, 0) + 4;
+ if(hlit > Nlitlen || hlit < 257 || hdist > Noff)
+ fatal(s, "corrupt data");
+
+ runlentab := array[Nclen] of { * => Huff(0, 0) };
+ count := array[RunlenBits+1] of { * => 0 };
+ for(i := 0; i < hclen; i++) {
+ nb := getn(s, 3, 0);
+ if(nb) {
+ runlentab[clenorder[i]].bits = nb;
+ count[nb]++;
+ }
+ }
+ hufftabinit(runlentab, Nclen, count, RunlenBits);
+ runlendec := decodeinit(runlentab, Nclen, RunlenBits, 0);
+ if(runlendec == nil)
+ fatal(s, "corrupt data");
+
+ lengths := decodelen(s, runlendec, hlit+hdist);
+ if(lengths == nil)
+ fatal(s, "corrupt length table");
+
+ dlitlendec := decodedyn(s, lengths[0:hlit], hlit, 9);
+ doffdec := decodedyn(s, lengths[hlit:], hdist, 5);
+ decodeblock(s, dlitlendec, doffdec);
+}
+
+#
+# return the decoded combined length table for literal and distance alphabets
+#
+decodelen(s: ref State, runlendec: ref DeHuff, nlen: int): array of int
+{
+ lengths := array[nlen] of int;
+ for(n := 0; n < nlen;) {
+ nb := decodesym(s, runlendec);
+ nr := 1;
+ case nb {
+ 0 to 15 =>
+ ;
+ 16 =>
+ nr = getn(s, 2, 0) + 3;
+ if(n == 0)
+ return nil;
+ nb = lengths[n-1];
+ 17 =>
+ nr = getn(s, 3, 0) + 3;
+ nb = 0;
+ 18 =>
+ nr = getn(s, 7, 0) + 11;
+ nb = 0;
+ * =>
+ return nil;
+ }
+ if(n+nr > nlen)
+ return nil;
+ while(--nr >= 0)
+ lengths[n++] = nb;
+ }
+ return lengths;
+}
+
+#
+# (1) read a dynamic huffman code from the input stream
+# (2) decode it using the run-length huffman code
+# (3) return the decode table for the dynamic huffman code
+#
+decodedyn(s: ref State, lengths: array of int, nlen, nb1: int): ref DeHuff
+{
+ hufftab := array[nlen] of Huff;
+ count := array[MaxHuffBits+1] of { * => 0 };
+
+ maxnb := 0;
+ for(n := 0; n < nlen; n++) {
+ c := lengths[n];
+ if(c) {
+ hufftab[n].bits = c;
+ count[c]++;
+ if(c > maxnb)
+ maxnb = c;
+ }else
+ hufftab[n].bits = 0;
+ hufftab[n].encode = 0;
+ }
+ hufftabinit(hufftab, nlen, count, maxnb);
+ nb2 := 0;
+ if(maxnb > nb1)
+ nb2 = maxnb - nb1;
+ d := decodeinit(hufftab, nlen, nb1, nb2);
+ if (d == nil)
+ fatal(s, "decodeinit failed");
+ return d;
+}
+
+#
+# RFC 1951, section 3.2.2
+#
+hufftabinit(tab: array of Huff, n: int, bitcount: array of int, nbits: int)
+{
+ nc := array[MaxHuffBits+1] of int;
+
+ code := 0;
+ for(bits := 1; bits <= nbits; bits++) {
+ code = (code + bitcount[bits-1]) << 1;
+ nc[bits] = code;
+ }
+
+ for(i := 0; i < n; i++) {
+ bits = tab[i].bits;
+ # differences from Deflate module:
+ # (1) leave huffman code right-justified in encode
+ # (2) don't reverse it
+ if(bits != 0)
+ tab[i].encode = nc[bits]++;
+ }
+}
+
+#
+# convert 'array of Huff' produced by hufftabinit()
+# into 2-level lookup table for decoding
+#
+# nb1(nb2): number of bits handled by first(second)-level table
+#
+decodeinit(tab: array of Huff, n, nb1, nb2: int): ref DeHuff
+{
+ i, j, k, d: int;
+
+ dehuff := ref DeHuff(array[1<<nb1] of { * => L1(0, 0, nil) }, nb1, nb2);
+ l1 := dehuff.l1;
+ for(i = 0; i < n; i++) {
+ bits := tab[i].bits;
+ if(bits == 0)
+ continue;
+ l1x := tab[i].encode;
+ if(l1x >= (1 << bits))
+ return nil;
+ if(bits <= nb1) {
+ d = nb1 - bits;
+ l1x <<= d;
+ k = l1x + mask[d];
+ for(j = l1x; j <= k; j++) {
+ l1[j].decode = i;
+ l1[j].bits = bits;
+ }
+ continue;
+ }
+ # advance to second-level table
+ d = bits - nb1;
+ l2x := l1x & mask[d];
+ l1x >>= d;
+ if(l1[l1x].l2 == nil)
+ l1[l1x].l2 = array[1<<nb2] of { * => L2(0, 0) };
+ l2 := l1[l1x].l2;
+ d = (nb1 + nb2) - bits;
+ l2x <<= d;
+ k = l2x + mask[d];
+ for(j = l2x; j <= k; j++) {
+ l2[j].decode = i;
+ l2[j].bits = bits;
+ }
+ }
+
+ return dehuff;
+}
+
+#
+# get next byte from reg
+# assumptions:
+# (1) flushbits() has been called
+# (2) ungetn() won't be called after a getb()
+#
+getb(s: ref State): int
+{
+ if(s.nbits < 8)
+ need(s, 8);
+ b := byte s.reg;
+ s.reg >>= 8;
+ s.nbits -= 8;
+ return int b;
+}
+
+#
+# get next n bits from reg; if r != 0, reverse the bits
+#
+getn(s: ref State, n, r: int): int
+{
+ if(s.nbits < n)
+ need(s, n);
+ s.svreg = s.reg;
+ s.svn = n;
+ i := s.reg & mask[n];
+ s.reg >>= n;
+ s.nbits -= n;
+ if(r) {
+ if(n <= 8) {
+ i = int revtab[i];
+ i >>= 8 - n;
+ } else {
+ i = ((int revtab[i & 16rff]) << 8)
+ | (int revtab[i >> 8]);
+ i >>= 16 - n;
+ }
+ }
+ return i;
+}
+
+#
+# ensure that at least n bits are available in reg
+#
+need(s: ref State, n: int)
+{
+ while(s.nbits < n) {
+ if(s.in >= s.ein) {
+ s.c <-= ref Rq.Fill(s.ibuf, s.rc);
+ s.ein = <- s.rc;
+ if (s.ein < 0)
+ exit;
+ if (s.ein == 0)
+ fatal(s, "premature end of stream");
+ s.in = 0;
+ }
+ s.reg = ((int s.ibuf[s.in++]) << s.nbits) | s.reg;
+ s.nbits += 8;
+ }
+}
+
+#
+# if partial byte consumed from reg, dispose of remaining bits
+#
+flushbits(s: ref State)
+{
+ drek := s.nbits % 8;
+ if(drek) {
+ s.reg >>= drek;
+ s.nbits -= drek;
+ }
+}
+
+#
+# output buffer is full, so flush it
+#
+flushout(s: ref State)
+{
+ if (s.headers)
+ outblock(s);
+ s.c <-= ref Rq.Result(s.obuf[0:s.out], s.rc);
+ flag := <- s.rc;
+ if (flag == -1)
+ exit;
+ buf := s.hist;
+ s.hist = s.obuf;
+ s.usehist = 1;
+ s.obuf = buf;
+ s.out = 0;
+}
+
+mkcrctab(poly: int): array of int
+{
+ crctab := array[256] of int;
+ for(i := 0; i < 256; i++){
+ crc := i;
+ for(j := 0; j < 8; j++){
+ c := crc & 1;
+ crc = (crc >> 1) & 16r7fffffff;
+ if(c)
+ crc ^= poly;
+ }
+ crctab[i] = crc;
+ }
+ return crctab;
+}
+
+outblock(s: ref State)
+{
+ buf := s.obuf;
+ n := s.out;
+ crc := s.crc;
+ crc ^= int 16rffffffff;
+ for(i := 0; i < n; i++)
+ crc = s.crctab[int(byte crc ^ buf[i])] ^ ((crc >> 8) & 16r00ffffff);
+ s.crc = crc ^ int 16rffffffff;
+ s.tot += n;
+}
+
+#
+# irrecoverable error; invariably denotes data corruption
+#
+fatal(s: ref State, e: string)
+{
+ s.c <-= ref Rq.Error(e);
+ exit;
+}
diff --git a/appl/lib/ip.b b/appl/lib/ip.b
new file mode 100644
index 00000000..49dfdb0a
--- /dev/null
+++ b/appl/lib/ip.b
@@ -0,0 +1,656 @@
+implement IP;
+
+#
+# Copyright © 2003,2004 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "ip.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ v4prefix = array[] of {
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 16rFF, byte 16rFF,
+ };
+
+ v4bcast = IPaddr(array[] of {
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 16rFF, byte 16rFF,
+ byte 16rFF, byte 16rFF, byte 16rFF, byte 16rFF,
+ });
+
+ v4allsys = IPaddr(array[] of {
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 16rFF, byte 16rFF,
+ byte 16rE0, byte 0, byte 0, byte 16r01,
+ });
+
+ v4allrouter = IPaddr(array[] of {
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 16rFF, byte 16rFF,
+ byte 16rE0, byte 0, byte 0, byte 16r02,
+ });
+
+ v4noaddr = IPaddr(array[] of {
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 16rFF, byte 16rFF,
+ byte 0, byte 0, byte 0, byte 0,
+ });
+
+ selfv6 = IPaddr(array[] of {
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 1,
+ });
+
+ selfv4 = IPaddr(array[] of {
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 0, byte 0,
+ byte 0, byte 0, byte 16rFF, byte 16rFF,
+ byte 127, byte 0, byte 0, byte 1,
+ });
+
+ noaddr = IPaddr(array[] of {0 to IPaddrlen-1 => byte 0});
+ allbits = IPaddr(array[] of {0 to IPaddrlen-1 => byte 16rFF});
+}
+
+IPaddr.newv6(a: array of byte): IPaddr
+{
+ b := array[len a] of byte;
+ b[0:] = a[0:IPaddrlen];
+ return IPaddr(b);
+}
+
+IPaddr.newv4(a: array of byte): IPaddr
+{
+ b := array[IPaddrlen] of byte;
+ b[0:] = v4prefix;
+ b[IPv4off:] = a[0:IPv4addrlen];
+ return IPaddr(b);
+}
+
+IPaddr.copy(ip: self IPaddr): IPaddr
+{
+ if(ip.a == nil)
+ return noaddr.copy();
+ a := array[len ip.a] of byte;
+ a[0:] = ip.a;
+ return IPaddr(a);
+}
+
+IPaddr.eq(ip: self IPaddr, v: IPaddr): int
+{
+ a := ip.a;
+ if(a == nil)
+ a = noaddr.a;
+ b := v.a;
+ if(b == nil)
+ b = noaddr.a;
+ for(i := 0; i < IPaddrlen; i++)
+ if(a[i] != b[i])
+ return 0;
+ return 1;
+}
+
+IPaddr.mask(a1: self IPaddr, a2: IPaddr): IPaddr
+{
+ c := array[IPaddrlen] of byte;
+ for(i := 0; i < IPaddrlen; i++)
+ c[i] = a1.a[i] & a2.a[i];
+ return IPaddr(c);
+}
+
+IPaddr.maskn(a1: self IPaddr, a2: IPaddr): IPaddr
+{
+ c := array[IPaddrlen] of byte;
+ for(i := 0; i < IPaddrlen; i++)
+ c[i] = a1.a[i] & ~a2.a[i];
+ return IPaddr(c);
+}
+
+IPaddr.isv4(ip: self IPaddr): int
+{
+ for(i := 0; i < IPv4off; i++)
+ if(ip.a[i] != v4prefix[i])
+ return 0;
+ return 1;
+}
+
+IPaddr.ismulticast(ip: self IPaddr): int
+{
+ if(ip.isv4()){
+ v := int ip.a[IPv4off];
+ return v >= 16rE0 && v < 16rF0 || ip.eq(v4bcast); # rfc1112
+ }
+ return ip.a[0] == byte 16rFF;
+}
+
+IPaddr.isvalid(ip: self IPaddr): int
+{
+ return !ip.eq(noaddr) && !ip.eq(v4noaddr);
+}
+
+IPaddr.v4(ip: self IPaddr): array of byte
+{
+ if(!ip.isv4() && !ip.eq(noaddr))
+ return nil;
+ a := array[4] of byte;
+ for(i := 0; i < 4; i++)
+ a[i] = ip.a[IPv4off+i];
+ return a;
+}
+
+IPaddr.v6(ip: self IPaddr): array of byte
+{
+ a := array[IPaddrlen] of byte;
+ a[0:] = ip.a;
+ return a;
+}
+
+IPaddr.class(ip: self IPaddr): int
+{
+ if(!ip.isv4())
+ return 6;
+ return int ip.a[IPv4off]>>6;
+}
+
+IPaddr.classmask(ip: self IPaddr): IPaddr
+{
+ m := allbits.copy();
+ if(!ip.isv4())
+ return m;
+ if((n := ip.class()) == 0)
+ n = 1;
+ for(i := IPaddrlen-4+n; i < IPaddrlen; i++)
+ m.a[i] = byte 0;
+ return m;
+}
+
+#
+# rfc2373
+#
+
+IPaddr.parse(s: string): (int, IPaddr)
+{
+ a := noaddr.copy();
+ col := 0;
+ gap := 0;
+ for(i:=0; i<IPaddrlen && s != ""; i+=2){
+ c := 'x';
+ v := 0;
+ for(m := 0; m < len s && (c = s[m]) != '.' && c != ':'; m++){
+ d := 0;
+ if(c >= '0' && c <= '9')
+ d = c-'0';
+ else if(c >= 'a' && c <= 'f')
+ d = c-'a'+10;
+ else if(c >= 'A' && c <= 'F')
+ d = c-'A'+10;
+ else
+ return (-1, a);
+ v = (v<<4) | d;
+ }
+ if(c == '.'){
+ if(parseipv4(a.a[i:], s) < 0)
+ return (-1, noaddr.copy());
+ i += IPv4addrlen;
+ break;
+ }
+ if(v > 16rFFFF)
+ return (-1, a);
+ a.a[i] = byte (v>>8);
+ a.a[i+1] = byte v;
+ if(c == ':'){
+ col = 1;
+ if(++m < len s && s[m] == ':'){
+ if(gap > 0)
+ return (-1, a);
+ gap = i+2;
+ m++;
+ }
+ }
+ s = s[m:];
+ }
+ if(i < IPaddrlen){ # mind the gap
+ ns := i-gap;
+ for(j := 1; j <= ns; j++){
+ a.a[IPaddrlen-j] = a.a[i-j];
+ a.a[i-j] = byte 0;
+ }
+ }
+ if(!col)
+ a.a[0:] = v4prefix;
+ return (0, IPaddr(a));
+}
+
+IPaddr.parsemask(s: string): (int, IPaddr)
+{
+ return parsemask(s, 128);
+}
+
+IPaddr.parsecidr(s: string): (int, IPaddr, IPaddr)
+{
+ for(i := 0; i < len s && s[i] != '/'; i++)
+ ;
+ (ok, a) := IPaddr.parse(s[0:i]);
+ if(i < len s){
+ (ok2, m) := IPaddr.parsemask(s[i:]);
+ if(ok < 0 || ok2 < 0)
+ return (-1, a, m);
+ return (0, a, m);
+ }
+ return (ok, a, allbits.copy());
+}
+
+parseipv4(b: array of byte, s: string): int
+{
+ a := array[4] of {* => 0};
+ o := 0;
+ for(i := 0; i < 4 && o < len s; i++){
+ for(m := o; m < len s && (c := s[m]) != '.'; m++)
+ if(!(c >= '0' && c <= '9'))
+ return -1;
+ if(m == o)
+ return -1;
+ a[i] = int big s[o:m];
+ b[i] = byte a[i];
+ if(m < len s && s[m] == '.')
+ m++;
+ o = m;
+ }
+ case i {
+ 1 => # 32 bit
+ b[0] = byte (a[0] >> 24);
+ b[1] = byte (a[0] >> 16);
+ b[2] = byte (a[0] >> 8);
+ b[3] = byte a[0];
+ 2 =>
+ if(a[0] < 256){ # 8/24
+ b[0] = byte a[0];
+ b[1] = byte (a[1]>>16);
+ b[2] = byte (a[1]>>8);
+ }else if(a[0] < 65536){ # 16/16
+ b[0] = byte (a[0]>>8);
+ b[1] = byte a[0];
+ b[2] = byte (a[1]>>16);
+ }else{ # 24/8
+ b[0] = byte (a[0]>>16);
+ b[1] = byte (a[0]>>8);
+ b[2] = byte a[0];
+ }
+ b[3] = byte a[1];
+ 3 => # 8/8/16
+ b[0] = byte a[0];
+ b[1] = byte a[1];
+ b[2] = byte (a[2]>>16);
+ b[3] = byte a[2];
+ }
+ return 0;
+}
+
+parsemask(s: string, abits: int): (int, IPaddr)
+{
+ m := allbits.copy();
+ if(s == nil)
+ return (0, m);
+ if(s[0] != '/'){
+ (ok, a) := IPaddr.parse(s);
+ if(ok < 0)
+ return (0, m);
+ if(a.isv4())
+ a.a[0:] = m.a[0:IPv4off];
+ return (0, a);
+ }
+ if(len s == 1)
+ return (0, m);
+ nbit := int s[1:];
+ if(nbit < 0)
+ return (-1, m);
+ if(nbit > abits)
+ return (0, m);
+ nbit = abits-nbit;
+ i := IPaddrlen;
+ for(; nbit >= 8; nbit -= 8)
+ m.a[--i] = byte 0;
+ if(nbit > 0)
+ m.a[i-1] &= byte (~0<<nbit);
+ return (0, m);
+}
+
+IPaddr.text(a: self IPaddr): string
+{
+ b := a.a;
+ if(b == nil)
+ return "::";
+ if(a.isv4())
+ return sys->sprint("%d.%d.%d.%d", int b[IPv4off], int b[IPv4off+1], int b[IPv4off+2], int b[IPv4off+3]);
+ cs := -1;
+ nc := 0;
+ for(i:=0; i<IPaddrlen; i+=2)
+ if(int b[i] == 0 && int b[i+1] == 0){
+ for(j:=i+2; j<IPaddrlen; j+=2)
+ if(int b[j] != 0 || int b[j+1] != 0)
+ break;
+ if(j-i > nc){
+ nc = j-i;
+ cs = i;
+ }
+ }
+ if(nc <= 2)
+ cs = -1;
+ s := "";
+ for(i=0; i<IPaddrlen; ){
+ if(i == cs){
+ s += "::";
+ i += nc;
+ }else{
+ if(s != "" && s[len s-1]!=':')
+ s[len s] = ':';
+ v := (int a.a[i] << 8) | int a.a[i+1];
+ s += sys->sprint("%ux", v);
+ i += 2;
+ }
+ }
+ return s;
+}
+
+IPaddr.masktext(a: self IPaddr): string
+{
+ b := a.a;
+ if(b == nil)
+ return "/0";
+ for(i:=0; i<IPaddrlen; i++)
+ if(i == IPv4off)
+ return sys->sprint("%d.%d.%d.%d", int b[IPv4off], int b[IPv4off+1], int b[IPv4off+2], int b[IPv4off+3]);
+ else if(b[i] != byte 16rFF)
+ break;
+ for(j:=i+1; j<IPaddrlen; j++)
+ if(b[j] != byte 0)
+ return a.text();
+ nbit := 8*i;
+ if(i < IPaddrlen){
+ v := int b[i];
+ for(m := 16r80; m != 0; m >>= 1){
+ if((v & m) == 0)
+ break;
+ v &= ~m;
+ nbit++;
+ }
+ if(v != 0)
+ return a.text();
+ }
+ return sys->sprint("/%d", nbit);
+}
+
+addressesof(ifcs: list of ref Ipifc, all: int): list of IPaddr
+{
+ ra: list of IPaddr;
+ runi: list of IPaddr;
+ for(; ifcs != nil; ifcs = tl ifcs){
+ for(ifcas :=(hd ifcs).addrs; ifcs != nil; ifcs = tl ifcs){
+ a := (hd ifcas).ip;
+ if(all || !(a.eq(noaddr) || a.eq(v4noaddr))){ # ignore unspecified and loopback
+ if(a.ismulticast() || a.eq(selfv4) || a.eq(selfv6))
+ ra = a :: ra;
+ else
+ runi = a :: runi;
+ }
+ }
+ }
+ # unicast first, then others, both sets in order as found
+ # for ipv6, might want to give priority to unicast other than link- and site-local
+ al: list of IPaddr;
+ for(; ra != nil; ra = tl ra)
+ al = hd ra :: al;
+ for(; runi != nil; runi = tl runi)
+ al = hd runi :: al;
+ return al;
+}
+
+interfaceof(l: list of ref Ipifc, ip: IPaddr): (ref Ipifc, ref Ifcaddr)
+{
+ for(; l != nil; l = tl l){
+ ifc := hd l;
+ for(addrs := ifc.addrs; addrs != nil; addrs = tl addrs){
+ a := hd addrs;
+ if(ip.mask(a.mask).eq(a.net))
+ return (ifc, a);
+ }
+ }
+ return (nil, nil);
+}
+
+ownerof(l: list of ref Ipifc, ip: IPaddr): (ref Ipifc, ref Ifcaddr)
+{
+ for(; l != nil; l = tl l){
+ ifc := hd l;
+ for(addrs := ifc.addrs; addrs != nil; addrs = tl addrs){
+ a := hd addrs;
+ if(ip.eq(a.ip))
+ return (ifc, a);
+ }
+ }
+ return (nil, nil);
+}
+
+readipifc(net: string, index: int): (list of ref Ipifc, string)
+{
+ if(net == nil)
+ net = "/net";
+ if(index < 0){
+ ifcs: list of ref Ipifc;
+ dirfd := sys->open(net+"/ipifc", Sys->OREAD);
+ if(dirfd == nil)
+ return (nil, sys->sprint("%r"));
+ err: string;
+ for(;;){
+ (nd, dirs) := sys->dirread(dirfd);
+ if(nd <= 0){
+ if(nd < 0)
+ err = sys->sprint("%r");
+ break;
+ }
+ for(i:=0; i<nd; i++)
+ if((dn := dirs[i].name) != nil && dn[0]>='0' && dn[0]<='9'){
+ index = int dn;
+ ifc := readstatus(net+"/ipifc/"+dn+"/status", index);
+ if(ifc != nil)
+ ifcs = ifc :: ifcs;
+ }
+ }
+ l := ifcs;
+ for(ifcs = nil; l != nil; l = tl l)
+ ifcs = hd l :: ifcs;
+ return (ifcs, err);
+ }
+ ifc := readstatus(net+"/ipifc/"+string index+"/status", index);
+ if(ifc == nil)
+ return (nil, sys->sprint("%r"));
+ return (ifc :: nil, nil);
+}
+
+#
+# return data structure containing values read from status file:
+#
+# device /net/ether0 maxtu 1514 sendra 0 recvra 0 mflag 0 oflag 0 maxraint 600000 minraint 200000 linkmtu 0 reachtime 0 rxmitra 0 ttl 255 routerlt 1800000 pktin 47609 pktout 42322 errin 0 errout 0
+# 144.32.112.83 /119 144.32.112.0 4294967295 4294967295
+# ...
+#
+
+readstatus(file: string, index: int): ref Ipifc
+{
+ fd := sys->open(file, Sys->OREAD);
+ if(fd == nil)
+ return nil;
+ contents := slurp(fd);
+ fd = nil;
+ (nline, lines) := sys->tokenize(contents, "\n");
+ if(nline <= 0){
+ sys->werrstr("unexpected ipifc status file format");
+ return nil;
+ }
+ (nil, details) := sys->tokenize(hd lines, " \t\n");
+ lines = tl lines;
+ ifc := ref Ipifc;
+ ifc.index = index;
+ ifc.dev = valof(details, "device");
+ ifc.mtu = int valof(details, "maxtu");
+ ifc.pktin = big valof(details, "pktin");
+ ifc.pktout = big valof(details, "pktout");
+ ifc.errin = big valof(details, "errin");
+ ifc.errout = big valof(details, "errout");
+ ifc.sendra = int valof(details, "sendra");
+ ifc.recvra = int valof(details, "recvra");
+ ifc.rp.mflag = int valof(details, "mflag");
+ ifc.rp.oflag = int valof(details, "oflag");
+ ifc.rp.maxraint = int valof(details, "maxraint");
+ ifc.rp.minraint = int valof(details, "minraint");
+ ifc.rp.linkmtu = int valof(details, "linkmtu");
+ ifc.rp.reachtime = int valof(details, "reachtime");
+ ifc.rp.rxmitra = int valof(details, "rxmitra");
+ ifc.rp.ttl = int valof(details, "ttl");
+ ifc.rp.routerlt = int valof(details, "routerlt");
+ addrs: list of ref Ifcaddr;
+ for(; lines != nil; lines = tl lines){
+ (nf, fields) := sys->tokenize(hd lines, " \t\n");
+ if(nf >= 3){
+ addr := ref Ifcaddr;
+ (nil, addr.ip) = IPaddr.parse(hd fields); fields = tl fields;
+ (nil, addr.mask) = IPaddr.parsemask(hd fields); fields = tl fields;
+ (nil, addr.net) = IPaddr.parse(hd fields); fields = tl fields;
+ if(nf >= 5){
+ addr.preflt = big hd fields; fields = tl fields;
+ addr.validlt = big hd fields; fields = tl fields;
+ }else{
+ addr.preflt = big 0;
+ addr.validlt = big 0;
+ }
+ addrs = addr :: addrs;
+ }
+ }
+ for(; addrs != nil; addrs = tl addrs)
+ ifc.addrs = hd addrs :: ifc.addrs;
+ return ifc;
+}
+
+slurp(fd: ref Sys->FD): string
+{
+ buf := array[2048] of byte;
+ s := "";
+ while((n := sys->read(fd, buf, len buf)) > 0)
+ s += string buf[0:n];
+ return s;
+}
+
+valof(l: list of string, attr: string): string
+{
+ while(l != nil){
+ label := hd l;
+ l = tl l;
+ if(label == attr){
+ if(l == nil)
+ return nil;
+ return hd l;
+ }
+ if(l != nil)
+ l = tl l;
+ }
+ return nil;
+}
+
+Udphdr.new(): ref Udphdr
+{
+ return ref Udphdr(noaddr, noaddr, noaddr, 0, 0);
+}
+
+Udphdr.unpack(a: array of byte, n: int): ref Udphdr
+{
+ case n {
+ Udp4hdrlen =>
+ u := ref Udphdr;
+ u.raddr = IPaddr.newv4(a[0:]);
+ u.laddr = IPaddr.newv4(a[IPv4addrlen:]);
+ u.rport = get2(a, 2*IPv4addrlen);
+ u.lport = get2(a, 2*IPv4addrlen+2);
+ u.ifcaddr = u.laddr.copy();
+ return u;
+ OUdphdrlen =>
+ u := ref Udphdr;
+ u.raddr = IPaddr.newv6(a[0:]);
+ u.laddr = IPaddr.newv6(a[IPaddrlen:]);
+ u.rport = get2(a, 2*IPaddrlen);
+ u.lport = get2(a, 2*IPaddrlen+2);
+ u.ifcaddr = u.laddr.copy();
+ return u;
+ Udphdrlen =>
+ u := ref Udphdr;
+ u.raddr = IPaddr.newv6(a[0:]);
+ u.laddr = IPaddr.newv6(a[IPaddrlen:]);
+ u.ifcaddr = IPaddr.newv6(a[2*IPaddrlen:]);
+ u.rport = get2(a, 3*IPaddrlen);
+ u.lport = get2(a, 3*IPaddrlen+2);
+ return u;
+ * =>
+ raise "Udphdr.unpack: bad length";
+ }
+}
+
+Udphdr.pack(u: self ref Udphdr, a: array of byte, n: int)
+{
+ case n {
+ Udp4hdrlen =>
+ a[0:] = u.raddr.v4();
+ a[IPv4addrlen:] = u.laddr.v4();
+ put2(a, 2*IPv4addrlen, u.rport);
+ put2(a, 2*IPv4addrlen+2, u.lport);
+ OUdphdrlen =>
+ a[0:] = u.raddr.v6();
+ a[IPaddrlen:] = u.laddr.v6();
+ put2(a, 2*IPaddrlen, u.rport);
+ put2(a, 2*IPaddrlen+2, u.lport);
+ Udphdrlen =>
+ a[0:] = u.raddr.v6();
+ a[IPaddrlen:] = u.laddr.v6();
+ a[2*IPaddrlen:] = u.ifcaddr.v6();
+ put2(a, 3*IPaddrlen, u.rport);
+ put2(a, 3*IPaddrlen+2, u.lport);
+ * =>
+ raise "Udphdr.pack: bad length";
+ }
+}
+
+get2(a: array of byte, o: int): int
+{
+ return (int a[o] << 8) | int a[o+1];
+}
+
+put2(a: array of byte, o: int, val: int): int
+{
+ a[o] = byte (val>>8);
+ a[o+1] = byte val;
+ return o+2;
+}
+
+get4(a: array of byte, o: int): int
+{
+ return (((((int a[o] << 8)| int a[o+1]) << 8) | int a[o+2]) << 8) | int a[o+3];
+}
+
+put4(a: array of byte, o: int, val: int): int
+{
+ a[o] = byte (val>>24);
+ a[o+1] = byte (val>>16);
+ a[o+2] = byte (val>>8);
+ a[o+3] = byte val;
+ return o+4;
+}
diff --git a/appl/lib/ipattr.b b/appl/lib/ipattr.b
new file mode 100644
index 00000000..453545de
--- /dev/null
+++ b/appl/lib/ipattr.b
@@ -0,0 +1,217 @@
+implement IPattr;
+
+include "sys.m";
+
+include "bufio.m";
+include "attrdb.m";
+ attrdb: Attrdb;
+ Db, Dbentry, Tuples: import attrdb;
+
+include "ip.m";
+ ip: IP;
+ IPaddr: import ip;
+
+include "ipattr.m";
+
+init(m: Attrdb, ipa: IP)
+{
+# sys = load Sys Sys->PATH;
+ attrdb = m;
+ ip = ipa;
+}
+
+dbattr(s: string): string
+{
+ digit := 0;
+ dot := 0;
+ alpha := 0;
+ hex := 0;
+ colon := 0;
+ for(i := 0; i < len s; i++){
+ case c := s[i] {
+ '0' to '9' =>
+ digit = 1;
+ 'a' to 'f' or 'A' to 'F' =>
+ hex = 1;
+ '.' =>
+ dot = 1;
+ ':' =>
+ colon = 1;
+ * =>
+ if(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '-' || c == '&')
+ alpha = 1;
+ }
+ }
+ if(alpha){
+ if(dot)
+ return "dom";
+ return "sys";
+ }
+ if(colon)
+ return "ip";
+ if(dot){
+ if(!hex)
+ return "ip";
+ return "dom";
+ }
+ return "sys";
+}
+
+findnetattr(ndb: ref Db, attr: string, val: string, rattr: string): (string, string)
+{
+ (matches, err) := findnetattrs(ndb, attr, val, rattr::nil);
+ if(matches == nil)
+ return (nil, err);
+ (nil, nattr) := hd matches;
+ na := hd nattr;
+#{sys := load Sys Sys->PATH; sys->print("%q=%q->%q ::", attr, val, rattr);for(al:=na.pairs; al != nil; al = tl al)sys->print(" %q=%q", (hd al).attr, (hd al).val); sys->print("\n");}
+ if(na.name == rattr && na.pairs != nil)
+ return ((hd na.pairs).val, nil);
+ return (nil, nil);
+}
+
+reverse(l: list of string): list of string
+{
+ rl: list of string;
+ for(; l != nil; l = tl l)
+ rl = hd l :: rl;
+ return rl;
+}
+
+valueof(l: list of ref Netattr, attr: string): list of string
+{
+ rl: list of string;
+ for(; l != nil; l = tl l){
+ na := hd l;
+ if(na.name == attr){
+ for(p := na.pairs; p != nil; p = tl p)
+ rl = (hd p).val :: rl;
+ }
+ }
+ return reverse(rl);
+}
+
+netvalueof(l: list of ref Netattr, attr: string, a: IP->IPaddr): list of string
+{
+ rl: list of string;
+ for(; l != nil; l = tl l){
+ na := hd l;
+ if(na.name == attr && a.mask(na.mask).eq(na.net)){
+ for(p := na.pairs; p != nil; p = tl p)
+ rl = (hd p).val :: rl;
+ }
+ }
+ return reverse(rl);
+}
+
+findnetattrs(ndb: ref Db, attr: string, val: string, rattrs: list of string): (list of (IPaddr, list of ref Netattr), string)
+{
+ rl: list of (IPaddr, list of ref Netattr);
+ if(ndb == nil)
+ return (nil, "no database");
+ (e, ptr) := ndb.findbyattr(nil, attr, val, "ip");
+ if(e == nil){
+ if(attr != "ip")
+ return (nil, "ip attribute not found");
+ # look for attributes associated with networks that include `a'
+ (ok, a) := IPaddr.parse(val);
+ if(ok < 0)
+ return (nil, "invalid ip address in db");
+ netattrs := mkattrlist(rattrs);
+ netattributes(ndb, a, netattrs);
+ rl = (a, netattrs) :: nil;
+ }else{
+ netattrs: list of ref Netattr;
+ for(matches := e.findbyattr(attr, val, "ip"); matches != nil; matches = tl matches){
+ for((nil, allip) := hd matches; allip != nil; allip = tl allip){
+ ipa := (hd allip).val;
+ (ok, a) := IPaddr.parse(ipa);
+ if(ok < 0)
+ return (nil, "invalid ip address in db");
+ netattrs = mkattrlist(rattrs);
+ pptr := ptr;
+ pe := e;
+ for(;;){
+ attribute(pe, a, ip->allbits, netattrs, 1);
+ (pe, pptr) = ndb.findpair(pptr, attr, val);
+ if(pe == nil)
+ break;
+ }
+ netattributes(ndb, a, netattrs);
+ rl = (a, netattrs) :: rl;
+ }
+ }
+ }
+ results: list of (IPaddr, list of ref Netattr);
+ for(; rl != nil; rl = tl rl)
+ results = hd rl :: results;
+ return (results, nil);
+}
+
+netattributes(ndb: ref Db, a: IPaddr, nas: list of ref Netattr): string
+{
+ e: ref Dbentry;
+ ptr: ref Attrdb->Dbptr;
+ for(;;){
+ (e, ptr) = ndb.find(ptr, "ipnet");
+ if(e == nil)
+ break;
+ ipaddr := e.findfirst("ip");
+ if(ipaddr == nil)
+ continue;
+ (ok, netip) := IPaddr.parse(ipaddr);
+ if(ok < 0)
+ return "bad ip address in db";
+ netmask: IPaddr;
+ mask := e.findfirst("ipmask");
+ if(mask == nil){
+ if(!netip.isv4())
+ continue;
+ netmask = netip.classmask();
+ }else{
+ (ok, netmask) = IPaddr.parsemask(mask);
+ if(ok < 0)
+ return "bad ipmask in db";
+ }
+ if(a.mask(netmask).eq(netip))
+ attribute(e, netip, netmask, nas, 0);
+ }
+ return nil;
+}
+
+attribute(e: ref Dbentry, netip: IPaddr, netmask: IPaddr, nas: list of ref Netattr, ishost: int)
+{
+ for(; nas != nil; nas = tl nas){
+ na := hd nas;
+ if(na.pairs != nil){
+ if(!na.mask.mask(netmask).eq(na.mask))
+ continue;
+ # new one is at least as specific
+ }
+ matches := e.find(na.name);
+ if(matches == nil){
+ if(na.name != "ipmask" || ishost)
+ continue;
+ matches = (nil, ref Attrdb->Attr("ipmask", netmask.masktext(), 0)::nil) :: nil;
+ }
+ na.net = netip;
+ na.mask = netmask;
+ rl: list of ref Attrdb->Attr;
+ for(; matches != nil; matches = tl matches){
+ (nil, al) := hd matches;
+ for(; al != nil; al = tl al)
+ rl = hd al :: rl;
+ }
+ na.pairs = nil;
+ for(; rl != nil; rl = tl rl)
+ na.pairs = hd rl :: na.pairs;
+ }
+}
+
+mkattrlist(rattrs: list of string): list of ref Netattr
+{
+ netattrs: list of ref Netattr;
+ for(; rattrs != nil; rattrs = tl rattrs)
+ netattrs = ref Netattr(hd rattrs, nil, ip->noaddr, ip->noaddr) :: netattrs;
+ return netattrs;
+}
diff --git a/appl/lib/ir.b b/appl/lib/ir.b
new file mode 100644
index 00000000..5b1316f5
--- /dev/null
+++ b/appl/lib/ir.b
@@ -0,0 +1,95 @@
+implement Ir;
+
+include "sys.m";
+FD, Dir: import Sys;
+include "ir.m";
+
+sys: Sys;
+
+init(keys, pid: chan of int): int
+{
+ sys = load Sys Sys->PATH;
+
+ cfd := sys->open("#t/eia1ctl", sys->OWRITE);
+ if(cfd == nil)
+ return -1;
+ sys->fprint(cfd, "b9600");
+
+ dfd := sys->open("#t/eia1", sys->OREAD);
+ cfd = nil;
+
+ spawn reader(keys, pid, dfd);
+ return 0;
+}
+
+reader(keys, pid: chan of int, dfd: ref FD)
+{
+ n, ta, tb: int;
+ dir: Dir;
+ b1:= array[1] of byte;
+ b2:= array[1] of byte;
+
+ pid <-= sys->pctl(0,nil);
+ (n, dir) = sys->fstat(dfd);
+ if(n >= 0 && dir.length > big 0) {
+ while(dir.length > big 0) {
+ l := int dir.length;
+ n = sys->read(dfd, array[l] of byte, l);
+ if(n < 0)
+ break;
+ dir.length -= big n;
+ }
+ }
+
+out: for(;;) {
+ n = sys->read(dfd, b1, len b1);
+ if(n <= 0)
+ break;
+ ta = sys->millisec();
+ for(;;) {
+ n = sys->read(dfd, b2, 1);
+ if(n <= 0)
+ break out;
+ tb = sys->millisec();
+ if(tb - ta <= 200)
+ break;
+ ta = tb;
+ b1[0] = b2[0];
+ }
+ case ((int b1[0]&16r1f)<<5) | (int b2[0]&16r1f) {
+ 71 => n = Ir->ChanDN;
+ 95 => n = Ir->Seven;
+ 135 => n = Ir->VolDN;
+ 207 => n = Ir->Three;
+ 215 => n = Ir->Select;
+ 263 => n = Ir->Dn;
+ 335 => n = Ir->Five;
+ 343 => n = Ir->Rew;
+ 399 => n = Ir->Nine;
+ 407 => n = Ir->Enter;
+ 455 => n = Ir->Power;
+ 479 => n = Ir->One;
+ 591 => n = Ir->Six;
+ 599 => n = Ir->ChanUP;
+ 663 => n = Ir->VolUP;
+ 711 => n = Ir->Up;
+ 735 => n = Ir->Two;
+ 791 => n = Ir->Mute;
+ 839 => n = Ir->FF;
+ 863 => n = Ir->Four;
+ 903 => n = Ir->Record;
+ 927 => n = Ir->Eight;
+ 975 => n = Ir->Zero;
+ 983 => n = Ir->Rcl;
+ * => n = Ir->Error;
+ }
+
+ keys <-= n;
+ }
+ keys <-= Ir->Error;
+}
+
+translate(c: int): int
+{
+ return c;
+}
diff --git a/appl/lib/irmpath.b b/appl/lib/irmpath.b
new file mode 100644
index 00000000..c58424f0
--- /dev/null
+++ b/appl/lib/irmpath.b
@@ -0,0 +1,118 @@
+# Driver for Mind Path IR50.
+
+implement Ir;
+
+include "sys.m";
+FD, Dir: import Sys;
+include "ir.m";
+
+sys: Sys;
+
+init(keys, pid: chan of int): int
+{
+ sys = load Sys Sys->PATH;
+
+ cfd := sys->open("#t/eia1ctl", sys->OWRITE);
+ if(cfd == nil)
+ return -1;
+ sys->fprint(cfd, "b1200"); # baud rate
+ sys->fprint(cfd, "d1"); # DTR on
+ sys->fprint(cfd, "r1"); # RTS on
+
+ dfd := sys->open("#t/eia1", sys->OREAD);
+ if(dfd == nil)
+ return -1;
+ cfd = nil;
+
+ spawn reader(keys, pid, dfd);
+ return 0;
+}
+
+reader(keys, pid: chan of int, dfd: ref FD)
+{
+ n: int;
+ dir: Dir;
+ button: int;
+
+ pid <-= sys->pctl(0,nil);
+ (n, dir) = sys->fstat(dfd);
+ if(n >= 0 && dir.length > 0) {
+ while(dir.length) {
+ n = sys->read(dfd, array[dir.length] of byte, dir.length);
+ if(n < 0)
+ break;
+ dir.length -= n;
+ }
+ }
+
+ for(;;) {
+ # Look for 2 consecutive characters that are the same.
+ if((button=getconsec(dfd,2)) < 0)
+ break;
+ case button {
+ '-' => n = Ir->Enter;
+ '+' => n = Ir->Rcl;
+ '1' => n = Ir->One;
+ '2' => n = Ir->Two;
+ '3' => n = Ir->Three;
+ '4' => n = Ir->ChanUP; # page up
+ '5' => n = Ir->ChanDN; # page down
+ 'U' => continue;
+ 'R' =>
+ if((button=getconsec(dfd,2)) < 0)
+ break;
+ case button {
+ 'a' or 'e' or 'i' or 'p' =>
+ n = Ir->Up;
+ 'b' or 'f' or 'j' or 'k' =>
+ n = Ir->FF; # right
+ 'c' or 'g' or 'l' or 'm' =>
+ n = Ir->Dn;
+ 'd' or 'h' or 'n' or 'o' =>
+ n = Ir->Rew; # left
+ 'Z' => n = Ir->Select;
+ * => ;
+ }
+ * => ;
+ }
+ keys <-= n; # Send translated key over channel
+ # Read through to trailer before looking for another key press
+ while((button=getconsec(dfd,2)) != 'U') {
+ if(button <= 0)
+ break;
+ }
+ }
+ keys <-= Ir->Error;
+}
+
+translate(c: int): int
+{
+ return c;
+}
+
+# Gets 'count' consecutive occurrences of a byte.
+getconsec(dfd: ref FD, count: int): int
+{
+ b1:= array[1] of byte;
+ b2:= array[1] of byte;
+
+ n := sys->read(dfd, b1, 1);
+ if(n <= 0) {
+ if(n==0)
+ n = -1;
+ return n;
+ }
+ for(sofar:=1; sofar < count; sofar++) {
+ n = sys->read(dfd, b2, 1);
+ if(n <= 0) {
+ if(n==0)
+ n = -1;
+ return n;
+ }
+ if(b1[0]!=b2[0]) {
+ sofar = 1;
+ b1[0] = b2[0];
+ }
+ }
+ return int b1[0];
+}
diff --git a/appl/lib/irsage.b b/appl/lib/irsage.b
new file mode 100644
index 00000000..6e56a230
--- /dev/null
+++ b/appl/lib/irsage.b
@@ -0,0 +1,99 @@
+implement Ir;
+
+include "sys.m";
+FD, Dir: import Sys;
+include "ir.m";
+
+sys: Sys;
+
+init(keys, pid: chan of int): int
+{
+ sys = load Sys Sys->PATH;
+
+ cfd := sys->open("#t/eia1ctl", sys->OWRITE);
+ if(cfd == nil)
+ return -1;
+ sys->fprint(cfd, "b9600");
+
+ dfd := sys->open("#t/eia1", sys->OREAD);
+ cfd = nil;
+
+ spawn reader(keys, pid, dfd);
+ return 0;
+}
+
+reader(keys, pid: chan of int, dfd: ref FD)
+{
+ n, ta, tb: int;
+ dir: Dir;
+ b1:= array[1] of byte;
+ b2:= array[1] of byte;
+
+ pid <-= sys->pctl(0,nil);
+ (n, dir) = sys->fstat(dfd);
+ if(n >= 0 && dir.length > big 0) {
+ while(dir.length > big 0) {
+ l := int dir.length;
+ n = sys->read(dfd, array[l] of byte, l);
+ if(n < 0)
+ break;
+ dir.length -= big n;
+ }
+ }
+
+out: for(;;) {
+ n = sys->read(dfd, b1, len b1);
+ if(n <= 0)
+ break;
+ ta = sys->millisec();
+ for(;;) {
+ n = sys->read(dfd, b2, 1);
+ if(n <= 0)
+ break out;
+ tb = sys->millisec();
+ if(tb - ta <= 200)
+ break;
+ ta = tb;
+ b1[0] = b2[0];
+ }
+#sys->print("IR Code = %d\n", ((int b1[0]&16r1f)<<5) | (int b2[0]&16r1f));
+ case ((int b1[0]&16r1f)<<5) | (int b2[0]&16r1f) {
+ 71 => n = Ir->ChanDN;
+ 95 => n = Ir->Seven;
+# 135 => n = Ir->VolDN;
+ 207 => n = Ir->Three;
+ 15 => n = Ir->Select;
+ 135 => n = Ir->Dn;
+ 335 => n = Ir->Five;
+# 343 => n = Ir->Rew;
+ 519 => n = Ir->Rew;
+ 399 => n = Ir->Nine;
+ 407 => n = Ir->Enter;
+ 455 => n = Ir->Power;
+ 479 => n = Ir->One;
+ 591 => n = Ir->Six;
+ 599 => n = Ir->ChanUP;
+# 663 => n = Ir->VolUP;
+ 663 => n = Ir->Up;
+ 735 => n = Ir->Two;
+ 791 => n = Ir->Mute;
+# 839 => n = Ir->FF;
+ 23 => n = Ir->FF;
+ 863 => n = Ir->Four;
+ 903 => n = Ir->Record;
+ 927 => n = Ir->Eight;
+ 975 => n = Ir->Zero;
+ 983 => n = Ir->Rcl;
+ * => n = Ir->Error;
+ }
+
+ keys <-= n;
+
+ }
+ keys <-= Ir->Error;
+}
+
+translate(c: int): int
+{
+ return c;
+}
diff --git a/appl/lib/irsim.b b/appl/lib/irsim.b
new file mode 100644
index 00000000..674df571
--- /dev/null
+++ b/appl/lib/irsim.b
@@ -0,0 +1,83 @@
+implement Ir;
+
+include "sys.m";
+sys: Sys;
+FD: import sys;
+
+include "ir.m";
+
+rawon: ref FD;
+
+init(keys, pid: chan of int): int
+{
+ dfd: ref FD;
+
+ sys = load Sys Sys->PATH;
+
+ dfd = sys->open("/dev/keyboard", sys->OREAD);
+ if(dfd == nil)
+ return -1;
+
+ spawn reader(keys, pid, dfd);
+ return 0;
+}
+
+reader(keys, pid: chan of int, dfd: ref FD)
+{
+ n: int;
+
+ nb := 0;
+ b:= array[1] of byte;
+ buf := array[10] of byte;
+ pid <-= sys->pctl(0,nil);
+ for(;;) {
+ n = sys->read(dfd, b, 1);
+ if(n != 1)
+ break;
+ if(nb>= len buf){
+ sys->print("irsim: confused by input\n");
+ break;
+ }
+
+ buf[nb++] = b[0];
+ nutf := sys->utfbytes(buf, nb);
+ if(nutf > 0){
+ s := string buf[0:nutf];
+ keys <-= s[0];
+ nb = 0;
+ }
+ }
+ keys <-= Ir->EOF;
+}
+
+translate(key: int): int
+{
+ n := Ir->Error;
+
+ case key {
+ '0' => n = Ir->Zero;
+ '1' => n = Ir->One;
+ '2' => n = Ir->Two;
+ '3' => n = Ir->Three;
+ '4' => n = Ir->Four;
+ '5' => n = Ir->Five;
+ '6' => n = Ir->Six;
+ '7' => n = Ir->Seven;
+ '8' => n = Ir->Eight;
+ '9' => n = Ir->Nine;
+ 'r' => n = Ir->ChanUP;
+ 'c' => n = Ir->ChanDN;
+ 't' => n = Ir->VolUP;
+ 'v' => n = Ir->VolDN;
+ 'k' => n = Ir->FF;
+ 'j' => n = Ir->Rew;
+ 'i' => n = Ir->Up;
+ 'm' => n = Ir->Dn;
+ 'x' => n = Ir->Rcl;
+ '\n' => n = Ir->Select;
+ ' ' => n = Ir->Enter;
+ 16r7f => n = Ir->Power;
+ }
+
+ return n;
+}
diff --git a/appl/lib/itslib.b b/appl/lib/itslib.b
new file mode 100644
index 00000000..d574965d
--- /dev/null
+++ b/appl/lib/itslib.b
@@ -0,0 +1,45 @@
+implement Itslib;
+
+include "sys.m";
+ sys: Sys;
+include "itslib.m";
+include "env.m";
+ env: Env;
+
+
+init(): ref Tconfig
+{
+ sys = load Sys Sys->PATH;
+ tc := ref Tconfig(-1, sys->fildes(2));
+ env = load Env Env->PATH;
+ if (env == nil)
+ sys->fprint(sys->fildes(2), "Failed to load %s\n", Env->PATH);
+ else {
+ vstr := env->getenv(ENV_VERBOSITY);
+ mstr := env->getenv(ENV_MFD);
+ if (vstr != nil && mstr != nil) {
+ tc.verbosity = int vstr;
+ tc.mfd = sys->fildes(int mstr);
+ }
+ }
+ if (tc.verbosity >= 0)
+ tc.report(S_STIME, 0, sys->sprint("%d", sys->millisec()));
+ else
+ sys->fprint(sys->fildes(2), "Test is running standalone\n");
+ return tc;
+}
+
+Tconfig.report(tc: self ref Tconfig, sev: int, verb: int, msg: string)
+{
+ if (sev < 0 || sev > S_ETIME) {
+ sys->fprint(sys->fildes(2), "Tconfig.report: Bad severity code: %d\n", sev);
+ sev = 0;
+ }
+ if (tc.mfd != nil && sys->fprint(tc.mfd, "%d%d%s\n", sev, verb, msg) <=0)
+ tc.mfd = nil; # Master test process was probably killed
+}
+
+Tconfig.done(tc: self ref Tconfig)
+{
+ tc.report(S_ETIME, 0, sys->sprint("%d", sys->millisec()));
+}
diff --git a/appl/lib/keyset.b b/appl/lib/keyset.b
new file mode 100644
index 00000000..13d8a3af
--- /dev/null
+++ b/appl/lib/keyset.b
@@ -0,0 +1,89 @@
+implement Keyset;
+
+include "sys.m";
+ sys: Sys;
+include "keyring.m";
+ keyring: Keyring;
+include "daytime.m";
+ daytime: Daytime;
+include "readdir.m";
+
+include "keyset.m";
+
+PKHASHLEN: con Keyring->SHA1dlen * 2;
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ keyring = load Keyring Keyring->PATH;
+ if(keyring == nil)
+ return cant(Keyring->PATH);
+ daytime = load Daytime Daytime->PATH;
+ if(daytime == nil)
+ return cant(Daytime->PATH);
+ return nil;
+}
+
+cant(s: string): string
+{
+ return sys->sprint("can't load %s: %r", s);
+}
+
+pkhash(pk: string): string
+{
+ d := array of byte pk;
+ digest := array[Keyring->SHA1dlen] of byte;
+ keyring->sha1(d, len d, digest, nil);
+ s := "";
+ for(i := 0; i < len digest; i++)
+ s += sys->sprint("%2.2ux", int digest[i]);
+ return s;
+}
+
+keysforsigner(signername: string, spkhash: string, user: string, dir: string): (list of (string, string, string), string)
+{
+ if(spkhash != nil && len spkhash != PKHASHLEN)
+ return (nil, "invalid hash string");
+ if(dir == nil){
+ if(user == nil)
+ user = readname("/dev/user");
+ if(user == nil)
+ dir = "/lib/keyring";
+ else
+ dir = "/usr/" + user + "/keyring";
+ }
+ readdir := load Readdir Readdir->PATH;
+ if(readdir == nil)
+ return (nil, sys->sprint("can't load Readdir: %r"));
+ now := daytime->now();
+ (a, ok) := readdir->init(dir, Readdir->COMPACT|Readdir->MTIME);
+ if(ok < 0)
+ return (nil, sys->sprint("can't open %s: %r", dir));
+ keys: list of (string, string, string);
+ for(i := 0; i < len a; i++){
+ if(a[i].mode & Sys->DMDIR)
+ continue;
+ f := dir + "/" + a[i].name;
+ info := keyring->readauthinfo(f);
+ if(info == nil || info.cert == nil || info.cert.exp != 0 && info.cert.exp < now)
+ continue;
+ if(signername != nil && info.cert.signer != signername)
+ continue;
+ if(spkhash != nil && pkhash(keyring->pktostr(info.spk)) != spkhash)
+ continue;
+ keys = (f, info.mypk.owner, info.cert.signer) :: keys;
+ }
+ return (keys, nil);
+}
+
+readname(f: string): string
+{
+ fd := sys->open(f, Sys->OREAD);
+ if(fd == nil)
+ return nil;
+ buf := array[Sys->NAMEMAX] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n <= 0)
+ return nil;
+ return string buf[0:n];
+}
diff --git a/appl/lib/libc.b b/appl/lib/libc.b
new file mode 100644
index 00000000..0acc6f42
--- /dev/null
+++ b/appl/lib/libc.b
@@ -0,0 +1,141 @@
+implement Libc;
+
+include "libc.m";
+
+islx(c: int): int
+{
+ return c >= 'a' && c <= 'f';
+}
+
+isux(c: int): int
+{
+ return c >= 'A' && c <= 'F';
+}
+
+isalnum(c: int): int
+{
+ return isalpha(c) || isdigit(c);
+}
+
+isalpha(c: int): int
+{
+ return islower(c) || isupper(c);
+}
+
+isascii(c: int): int
+{
+ return (c&~16r7f) == 0;
+}
+
+iscntrl(c: int): int
+{
+ return c == 16r7f || (c&~16r1f) == 0;
+}
+
+isdigit(c: int): int
+{
+ return c >= '0' && c <= '9';
+}
+
+isgraph(c: int): int
+{
+ return c >= '!' && c <= '~';
+}
+
+islower(c: int): int
+{
+ return c >= 'a' && c <= 'z';
+}
+
+isprint(c: int): int
+{
+ return c >= ' ' && c <= '~';
+}
+
+ispunct(c: int): int
+{
+ return isascii(c) && !iscntrl(c) && !isspace(c) && !isalnum(c);
+}
+
+isspace(c: int): int
+{
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
+}
+
+isupper(c: int): int
+{
+ return c >= 'A' && c <= 'Z';
+}
+
+isxdigit(c: int): int
+{
+ return isdigit(c) || islx(c) || isux(c);
+}
+
+tolower(c: int): int
+{
+ if(isupper(c))
+ return c+'a'-'A';
+ return c;
+}
+
+toupper(c: int): int
+{
+ if(islower(c))
+ return c+'A'-'a';
+ return c;
+}
+
+toascii(c: int): int
+{
+ return c&16r7f;
+}
+
+strchr(s: string, n: int): int
+{
+ l := len s;
+ for(i := 0; i < l; i++)
+ if(s[i] == n)
+ return i;
+ return -1;
+}
+
+strrchr(s: string, n: int): int
+{
+ l := len s;
+ for(i := l-1; i >= 0; i--)
+ if(s[i] == n)
+ return i;
+ return -1;
+}
+
+strncmp(s1: string, s2: string, n: int): int
+{
+ l1 := len s1;
+ l2 := len s2;
+ for(i := 0; i < l1 && i < l2 && i < n; i++)
+ if(s1[i] != s2[i])
+ return s1[i]-int s2[i];
+ return l1-l2;
+}
+
+abs(n: int): int
+{
+ if(n < 0)
+ return -n;
+ return n;
+}
+
+min(m: int, n: int): int
+{
+ if(m < n)
+ return m;
+ return n;
+}
+
+max(m: int, n: int): int
+{
+ if(m > n)
+ return m;
+ return n;
+}
diff --git a/appl/lib/libc0.b b/appl/lib/libc0.b
new file mode 100644
index 00000000..a9a2e834
--- /dev/null
+++ b/appl/lib/libc0.b
@@ -0,0 +1,241 @@
+implement Libc0;
+
+include "libc0.m";
+
+islx(c: int): int
+{
+ return c >= 'a' && c <= 'f';
+}
+
+isux(c: int): int
+{
+ return c >= 'A' && c <= 'F';
+}
+
+isalnum(c: int): int
+{
+ return isalpha(c) || isdigit(c);
+}
+
+isalpha(c: int): int
+{
+ return islower(c) || isupper(c);
+}
+
+isascii(c: int): int
+{
+ return (c&~16r7f) == 0;
+}
+
+iscntrl(c: int): int
+{
+ return c == 16r7f || (c&~16r1f) == 0;
+}
+
+isdigit(c: int): int
+{
+ return c >= '0' && c <= '9';
+}
+
+isgraph(c: int): int
+{
+ return c >= '!' && c <= '~';
+}
+
+islower(c: int): int
+{
+ return c >= 'a' && c <= 'z';
+}
+
+isprint(c: int): int
+{
+ return c >= ' ' && c <= '~';
+}
+
+ispunct(c: int): int
+{
+ return isascii(c) && !iscntrl(c) && !isspace(c) && !isalnum(c);
+}
+
+isspace(c: int): int
+{
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
+}
+
+isupper(c: int): int
+{
+ return c >= 'A' && c <= 'Z';
+}
+
+isxdigit(c: int): int
+{
+ return isdigit(c) || islx(c) || isux(c);
+}
+
+tolower(c: int): int
+{
+ if(isupper(c))
+ return c+'a'-'A';
+ return c;
+}
+
+toupper(c: int): int
+{
+ if(islower(c))
+ return c+'A'-'a';
+ return c;
+}
+
+toascii(c: int): int
+{
+ return c&16r7f;
+}
+
+strlen(s: array of byte): int
+{
+ l := len s;
+ for(i := 0; i < l; i++)
+ if(s[i] == byte 0)
+ break;
+ return i;
+}
+
+strcpy(s1: array of byte, s2: array of byte): array of byte
+{
+ l := strlen(s2)+1;
+ if(l == len s2)
+ s1[0: ] = s2;
+ else
+ s1[0: ] = s2[0: l];
+ return s1;
+}
+
+strncpy(s1: array of byte, s2: array of byte, n: int): array of byte
+{
+ l := strlen(s2);
+ if(l >= n)
+ s1[0: ] = s2[0: n];
+ else{
+ s1[0: ] = s2;
+ for(i := l; i < n; i++)
+ s1[i] = byte '\0';
+ }
+ return s1;
+}
+
+strcat(s1: array of byte, s2: array of byte): array of byte
+{
+ l := strlen(s2)+1;
+ m := strlen(s1);
+ if(l == len s2)
+ s1[m: ] = s2;
+ else
+ s1[m: ] = s2[0: l];
+ return s1;
+}
+
+strncat(s1: array of byte, s2: array of byte, n: int): array of byte
+{
+ l := strlen(s2);
+ if(l >= n){
+ m := strlen(s1);
+ s1[m: ] = s2[0: n];
+ s1[m+n] = byte '\0';
+ }
+ else
+ strcat(s1, s2);
+ return s1;
+}
+
+strdup(s: array of byte): array of byte
+{
+ l := strlen(s)+1;
+ t := array[l] of byte;
+ if(l == len s)
+ t[0: ] = s;
+ else
+ t[0: ] = s[0: l];
+ return t;
+}
+
+strcmp(s1: array of byte, s2: array of byte): int
+{
+ l1 := strlen(s1);
+ l2 := strlen(s2);
+ for(i := 0; i < l1 && i < l2; i++)
+ if(s1[i] != s2[i])
+ return int s1[i]-int s2[i];
+ return l1-l2;
+}
+
+strncmp(s1: array of byte, s2: array of byte, n: int): int
+{
+ l1 := strlen(s1);
+ l2 := strlen(s2);
+ for(i := 0; i < l1 && i < l2 && i < n; i++)
+ if(s1[i] != s2[i])
+ return int s1[i]-int s2[i];
+ return l1-l2;
+}
+
+strchr(s: array of byte, n: int): array of byte
+{
+ l := strlen(s);
+ for(i := 0; i < l; i++)
+ if(s[i] == byte n)
+ return s[i: ];
+ return nil;
+}
+
+strrchr(s: array of byte, n: int): array of byte
+{
+ l := strlen(s);
+ for(i := l-1; i >= 0; i--)
+ if(s[i] == byte n)
+ return s[i: ];
+ return nil;
+}
+
+ls2aab(argl: list of string): array of array of byte
+{
+ l := len argl;
+ ls := argl;
+ a := array[l+1] of array of byte;
+ for(i := 0; i < l; i++){
+ a[i] = array of byte (hd ls + "\0");
+ ls = tl ls;
+ }
+ a[l] = nil;
+ return a;
+}
+
+s2ab(s: string): array of byte
+{
+ return array of byte (s + "\0");
+}
+
+ab2s(a: array of byte): string
+{
+ return string a[0: strlen(a)];
+}
+
+abs(n: int): int
+{
+ if(n < 0)
+ return -n;
+ return n;
+}
+
+min(m: int, n: int): int
+{
+ if(m < n)
+ return m;
+ return n;
+}
+
+max(m: int, n: int): int
+{
+ if(m > n)
+ return m;
+ return n;
+}
diff --git a/appl/lib/lock.b b/appl/lib/lock.b
new file mode 100644
index 00000000..8b51e4f9
--- /dev/null
+++ b/appl/lib/lock.b
@@ -0,0 +1,26 @@
+implement Lock;
+
+include "sys.m";
+ sys: Sys;
+include "lock.m";
+
+Semaphore.obtain(l: self ref Semaphore)
+{
+ l.c <-= 0;
+}
+
+Semaphore.release(l: self ref Semaphore)
+{
+ <-l.c;
+}
+
+Semaphore.new(): ref Semaphore
+{
+ l := ref Semaphore;
+ l.c = chan[1] of int;
+ return l;
+}
+
+init()
+{
+}
diff --git a/appl/lib/login.b b/appl/lib/login.b
new file mode 100644
index 00000000..2feb3f6a
--- /dev/null
+++ b/appl/lib/login.b
@@ -0,0 +1,175 @@
+# Inferno Encrypt Key Exchange Protocol
+#
+# Copyright © 1995-1999 Lucent Techologies Inc. All rights reserved.
+#
+# This code uses methods that are subject to one or more patents
+# held by Lucent Technologies Inc. Its use outside Inferno
+# requires a separate licence from Lucent.
+#
+implement Login;
+
+include "sys.m";
+ sys: Sys;
+
+include "keyring.m";
+ kr: Keyring;
+ IPint: import kr;
+
+include "security.m";
+
+include "string.m";
+
+# see login(6)
+login(id, password, dest: string): (string, ref Keyring->Authinfo)
+{
+ sys = load Sys Sys->PATH;
+ kr = load Keyring Keyring->PATH;
+ if(kr == nil)
+ return nomod(Keyring->PATH);
+
+ ssl := load SSL SSL->PATH;
+ if(ssl == nil)
+ return nomod(SSL->PATH);
+
+ rand := load Random Random->PATH;
+ if(rand == nil)
+ return nomod(Random->PATH);
+
+ if(dest == nil)
+ dest = "$SIGNER";
+ for(j:=0; j<len dest && dest[j] != '!'; j++)
+ break;
+ if(j >= len dest)
+ dest = "net!"+dest+"!inflogin"; # BUG: must do better
+
+ (ok, lc) := sys->dial(dest, nil);
+ if(ok < 0)
+ return (sys->sprint("can't contact login service: %r"), nil);
+
+ # push ssl, leave in clear mode for now
+ (err, c) := ssl->connect(lc.dfd);
+ if(c == nil)
+ return ("can't push ssl: " + err, nil);
+ lc.dfd = nil;
+ lc.cfd = nil;
+
+ # user->CA name
+ if(kr->putstring(c.dfd, id) < 0)
+ return (sys->sprint("can't send user name: %r"), nil);
+
+ # CA->user ACK
+ (s, why) := kr->getstring(c.dfd);
+ if(why != nil)
+ return ("remote: " + why, nil);
+ if(s != id)
+ return ("unexpected reply from signer: " + s, nil);
+
+ # user->CA ivec
+ ivec := rand->randombuf(rand->ReallyRandom, 8);
+ if(kr->putbytearray(c.dfd, ivec, len ivec) < 0)
+ return (sys->sprint("can't send initialization vector: %r"), nil);
+
+ # start encrypting
+ pwbuf := array of byte password;
+ digest := array[Keyring->SHA1dlen] of byte;
+ kr->sha1(pwbuf, len pwbuf, digest, nil);
+ pwbuf = array[8] of byte;
+ for(i := 0; i < 8; i++)
+ pwbuf[i] = digest[i] ^ digest[8+i];
+ for(i = 0; i < 4; i++)
+ pwbuf[i] ^= digest[16+i];
+ for(i = 0; i < 8; i++)
+ pwbuf[i] ^= ivec[i];
+ err = ssl->secret(c, pwbuf, pwbuf);
+ if(err != nil)
+ return ("can't set secret: " + err, nil);
+ if(sys->fprint(c.cfd, "alg rc4") < 0)
+ return (sys->sprint("can't push alg rc4: %r"), nil);
+ #if(sys->fprint(c.cfd, "alg desebc") < 0)
+ # return (sys->sprint("can't push alg desecb: %r"), nil);
+
+ # CA -> user key(alpha**r0 mod p)
+ (s, err) = kr->getstring(c.dfd);
+ if(err != nil){
+ if(err == "failure") # calculated secret is wrong
+ return ("name or secret incorrect (alpha**r0 mod p)", nil);
+ return ("remote:" + err, nil);
+ }
+
+ # stop encrypting
+ if(sys->fprint(c.cfd, "alg clear") < 0)
+ return (sys->sprint("can't push alg clear: %r"), nil);
+ alphar0 := IPint.b64toip(s);
+
+ # CA->user alpha
+ (s, err) = kr->getstring(c.dfd);
+ if(err != nil){
+ if(err == "failure")
+ return ("name or secret incorrect (alpha)", nil);
+ return ("remote: " + err, nil);
+ }
+ info := ref Keyring->Authinfo;
+ info.alpha = IPint.b64toip(s);
+
+ # CA->user p
+ (s, err) = kr->getstring(c.dfd);
+ if(err != nil){
+ if(err == "failure")
+ return ("name or secret incorrect (p)", nil);
+ return ("remote: " + err, nil);
+ }
+ info.p = IPint.b64toip(s);
+
+ # sanity check
+ bits := info.p.bits();
+ abits := info.alpha.bits();
+ if(abits > bits || abits < 2)
+ return ("bogus diffie hellman constants", nil);
+
+ # generate our random diffie hellman part
+ r1 := kr->IPint.random(bits/4, bits);
+ alphar1 := info.alpha.expmod(r1, info.p);
+
+ # user->CA alpha**r1 mod p
+ if(kr->putstring(c.dfd, alphar1.iptob64()) < 0)
+ return (sys->sprint("can't send (alpha**r1 mod p): %r"), nil);
+
+ # compute alpha**(r0*r1) mod p
+ alphar0r1 := alphar0.expmod(r1, info.p);
+
+ # turn on digesting
+ secret := alphar0r1.iptobytes();
+ err = ssl->secret(c, secret, secret);
+ if(err != nil)
+ return ("can't set digesting: " + err, nil);
+ if(sys->fprint(c.cfd, "alg sha1") < 0)
+ return (sys->sprint("can't push alg sha1: %r"), nil);
+
+ # CA->user CA's public key, SHA(CA's public key + secret)
+ (s, err) = kr->getstring(c.dfd);
+ if(err != nil)
+ return ("can't get signer's public key: " + err, nil);
+
+ info.spk = kr->strtopk(s);
+
+ # generate a key pair
+ info.mysk = kr->genSKfromPK(info.spk, id);
+ info.mypk = kr->sktopk(info.mysk);
+
+ # user->CA user's public key, SHA(user's public key + secret)
+ if(kr->putstring(c.dfd, kr->pktostr(info.mypk)) < 0)
+ return (sys->sprint("can't send your public: %r"), nil);
+
+ # CA->user user's public key certificate
+ (s, err) = kr->getstring(c.dfd);
+ if(err != nil)
+ return ("can't get certificate: " + err, nil);
+
+ info.cert = kr->strtocert(s);
+ return(nil, info);
+}
+
+nomod(mod: string): (string, ref Keyring->Authinfo)
+{
+ return (sys->sprint("can't load module %s: %r", mod), nil);
+}
diff --git a/appl/lib/memfs.b b/appl/lib/memfs.b
new file mode 100644
index 00000000..4ffc7b65
--- /dev/null
+++ b/appl/lib/memfs.b
@@ -0,0 +1,46 @@
+# To be removed...
+# functionality has been moved to appl/cmd/memfs.b
+# some progs still refer to lib/memfs so it remains for the time being
+
+implement MemFS;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "memfs.m";
+
+Cmd: module {
+ PATH: con "/dis/memfs.dis";
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+cmd: Cmd;
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ cmd = load Cmd Cmd->PATH;
+ if (cmd == nil)
+ return sys->sprint("lib/memfs cannot load %s: %r\n", Cmd->PATH);
+ return nil;
+}
+
+newfs(maxsz: int): ref Sys->FD
+{
+ p := array [2] of ref Sys->FD;
+ if (sys->pipe(p) == -1)
+ return nil;
+ sync := chan of int;
+ spawn run(p[1].fd, maxsz, sync);
+ <- sync;
+ return p[0];
+}
+
+run(fd: int, sz: int, sync: chan of int)
+{
+ sys->pctl(Sys->FORKFD, nil);
+ sys->dup(fd, 0);
+ sys->pctl(Sys->NEWFD, 0::1::2::nil);
+ sync <-= 1;
+ cmd->init(nil, Cmd->PATH :: "-s" :: "-m" :: string sz :: nil);
+}
diff --git a/appl/lib/mkfile b/appl/lib/mkfile
new file mode 100644
index 00000000..210e2f74
--- /dev/null
+++ b/appl/lib/mkfile
@@ -0,0 +1,223 @@
+<../../mkconfig
+
+DIRS=\
+ convcs\
+ crypt\
+ ecmascript\
+ encoding\
+ print\
+ spki\
+ strokes\
+ styxconv\
+ usb\
+ w3c\
+
+TARG=\
+ arg.dis\
+ asn1.dis\
+ attrdb.dis\
+ attrhash.dis\
+ auth.dis\
+ auth9.dis\
+ bloomfilter.dis\
+ bufio.dis\
+ cfg.dis\
+ cfgfile.dis\
+ chanfill.dis\
+ crc.dis\
+ daytime.dis\
+ db.dis\
+ dbm.dis\
+ dbsrv.dis\
+ debug.dis\
+ deflate.dis\
+ devpointer.dis\
+ dhcpclient.dis\
+ dialog.dis\
+ dict.dis\
+ dis.dis\
+ diskblocks.dis\
+ disks.dis\
+ dividers.dis\
+ env.dis\
+ ether.dis\
+ exception.dis\
+ factotum.dis\
+ filepat.dis\
+ format.dis\
+ fsfilter.dis\
+ fslib.dis\
+ fsproto.dis\
+ gamer.dis\
+ hash.dis\
+ html.dis\
+ imageremap.dis\
+ inflate.dis\
+ ip.dis\
+ ipattr.dis\
+ ir.dis\
+ irsage.dis\
+ irsim.dis\
+ itslib.dis\
+ keyset.dis\
+ libc.dis\
+ libc0.dis\
+ lock.dis\
+ login.dis\
+ mpeg.dis\
+ nametree.dis\
+ names.dis\
+ newns.dis\
+ palm.dis\
+ palmdb.dis\
+ palmfile.dis\
+ parseman.dis\
+ plumbmsg.dis\
+ plumbing.dis\
+ pop3.dis\
+ popup.dis\
+ powerman.dis\
+ profile.dis\
+ pslib.dis\
+ quicktime.dis\
+ rand.dis\
+ random.dis\
+ readdir.dis\
+ readgif.dis\
+ readjpg.dis\
+ readpicfile.dis\
+ readpng.dis\
+ readxbitmap.dis\
+ regex.dis\
+ regexutils.dis\
+ registries.dis\
+ riff.dis\
+ scoretable.dis\
+ scsiio.dis\
+ secstore.dis\
+ selectfile.dis\
+ sets.dis\
+ sets32.dis\
+ sexprs.dis\
+ slip.dis\
+ smtp.dis\
+ sort.dis\
+ ssl.dis\
+ string.dis\
+ strinttab.dis\
+ styx.dis\
+ styxlib.dis\
+ styxpersist.dis\
+ styxservers.dis\
+ tables.dis\
+ tabs.dis\
+ tftp.dis\
+ timers.dis\
+ tcl_utils.dis\
+# tcl_tk.dis\
+ tcl_symhash.dis\
+ tcl_string.dis\
+ tcl_strhash.dis\
+ tcl_stack.dis\
+ tcl_modhash.dis\
+ tcl_list.dis\
+ tcl_io.dis\
+ tcl_inthash.dis\
+ tcl_core.dis\
+ tcl_calc.dis\
+ tkclient.dis\
+ titlebar.dis\
+ translate.dis\
+ ubfa.dis\
+ url.dis\
+ venti.dis\
+ virgil.dis\
+ volume.dis\
+ wait.dis\
+ watchvars.dis\
+ winplace.dis\
+ wmclient.dis\
+ wmlib.dis\
+ wmsrv.dis\
+ workdir.dis\
+ writegif.dis\
+ xml.dis\
+
+MODULES=
+
+SYSMODULES= \
+ bufio.m\
+ cci.m\
+ daytime.m\
+ db.m\
+ debug.m\
+ devpointer.m\
+ dict.m\
+ draw.m\
+ env.m\
+ exception.m\
+ filepat.m\
+ filter.m\
+ fslib.m\
+ hash.m\
+ html.m\
+ imagefile.m\
+ inflate.m\
+ ir.m\
+ keyring.m\
+ lock.m\
+ man.m\
+ mpeg.m\
+ newns.m\
+ palmfile.m\
+ plumbmsg.m\
+ powerman.m\
+ prefab.m\
+ pslib.m\
+ quicktime.m\
+ rand.m\
+ readdir.m\
+ regex.m\
+ riff.m\
+ scoretable.m\
+ security.m\
+ sh.m\
+ smtp.m\
+ srv.m\
+ string.m\
+ styx.m\
+ styxservers.m\
+ sys.m\
+ tables.m\
+ url.m\
+ venti.m\
+ volume.m\
+ watchvars.m\
+ wmlib.m\
+
+DISBIN=$ROOT/dis/lib
+
+<$ROOT/mkfiles/mkdis
+<$ROOT/mkfiles/mksubdirs
+
+plumbing.dis:N: plumbing.m
+plumber.dis:N: plumbing.m
+
+ip.dis: $ROOT/module/ip.m
+ether.dis: $ROOT/module/ether.m
+attrdb.dis attrhash.dis: $ROOT/module/attrdb.m
+ipattr.dis: $ROOT/module/attrdb.m $ROOT/module/ip.m $ROOT/module/ipattr.m
+tftp.dis: $ROOT/module/tftp.m
+registries.dis: $ROOT/module/registries.m
+keyset.dis: $ROOT/module/keyset.m
+auth9.dis: $ROOT/module/auth9.m
+factotum.dis: $ROOT/module/factotum.m
+sexprs.dis: $ROOT/module/sexprs.m
+dbm.dis: $ROOT/module/dbm.m
+names.dis: $ROOT/module/names.m
+disks.dis: $ROOT/module/disks.m
+scsiio.dis: $ROOT/module/scsiio.m
+dhcpclient.dis: $ROOT/module/dhcp.m
+ubfa.dis: $ROOT/module/ubfa.m
+secstore.dis: $ROOT/module/secstore.m
+ida.dis: $ROOT/module/ida.m
diff --git a/appl/lib/mpeg.b b/appl/lib/mpeg.b
new file mode 100644
index 00000000..f1cd00f0
--- /dev/null
+++ b/appl/lib/mpeg.b
@@ -0,0 +1,148 @@
+implement Mpeg;
+
+include "sys.m";
+sys: Sys;
+FD, Connection: import Sys;
+include "draw.m";
+draw: Draw;
+Display, Rect, Image: import draw;
+include "mpeg.m";
+
+Chroma: con 16r05;
+
+getenv()
+{
+ if(sys != nil)
+ return;
+
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+}
+
+copy(files: list of string, notify: chan of string, mpctl, mpdata: ref FD)
+{
+ ok, n: int;
+ c: Connection;
+ name: list of string;
+
+ while(files != nil) {
+ file := hd files;
+ (n, name) = sys->tokenize(file, "@");
+ m : ref FD;
+ case n {
+ 1 =>
+ m = sys->open(file, sys->OREAD);
+ if(m == nil) {
+ notify <-= "mpeg open:" + file;
+ return;
+ }
+ 2 =>
+ (ok, c) = sys->dial(hd tl name, nil);
+ if(ok < 0) {
+ notify <-= "dial:" + hd tl name;
+ return;
+ }
+ sys->fprint(c.dfd, "%s\n", hd name);
+ c.cfd = nil;
+ m = c.dfd;
+ * =>
+ notify <-= "bad file:"+hd name;
+ return;
+ }
+ sys->stream(m, mpdata, 64*1024);
+ files = tl files;
+ }
+ sys->fprint(mpctl, "stop");
+ sys->fprint(mpctl, "window 0 0 0 0");
+ notify <-= "";
+}
+
+play(display: ref Display, w: ref Image, paint: int, r: Rect, file: string, notify: chan of string): string
+{
+ i, j: int;
+ line: string;
+ cfg: array of byte;
+ buf := array[1024] of byte;
+ arg, words, files: list of string;
+
+ getenv();
+
+ mpdata := sys->open("/dev/mpeg", sys->OWRITE);
+ if(mpdata == nil)
+ return sys->sprint("can't open /dev/mpeg: %r");
+
+ obj := sys->open(file, sys->OREAD);
+ if(obj == nil)
+ return "open failed:"+file;
+
+ n := sys->read(obj, buf, len buf);
+ if(n < 0)
+ return "mpeg object: read error";
+
+ mpctl := sys->open("/dev/mpegctl", sys->OWRITE);
+ if(mpctl == nil)
+ return "open mpeg ctl file";
+
+ # Parse into lines
+ (n, arg) = sys->tokenize(string buf[0:n], "\n");
+ for(i = 0; i < n; i++) {
+ # Parse into words
+ line = hd arg;
+ (j, words) = sys->tokenize(line, " \t");
+
+ # Pass device config lines through to the ctl file
+ if(hd words == "files")
+ files = tl words;
+ else {
+ cfg = array of byte line;
+ if(sys->write(mpctl, cfg, len cfg) < 0)
+ return "invalid device config:"+line;
+ }
+ arg = tl arg;
+ }
+
+ if(files == nil)
+ return "no file to play";
+
+ # now the driver is configured initialize the dsp's
+ # and set up the trident overlay
+ sys->fprint(mpctl, "init");
+ sys->fprint(mpctl, "window %d %d %d %d",
+ r.min.x, r.min.y, r.max.x, r.max.y);
+
+ # paint the window with the chroma key color
+ if(paint)
+ w.draw(r, keycolor(display), nil, r.min);
+
+ if(notify != nil) {
+ spawn copy(files, notify, mpctl, mpdata);
+ return "";
+ }
+ notify = chan of string;
+ spawn copy(files, notify, mpctl, mpdata);
+ return <-notify;
+}
+
+ctl(msg: string): int
+{
+ mpc: ref FD;
+
+ getenv();
+
+ mpc = sys->open("/dev/mpegctl", sys->OWRITE);
+ if(mpc == nil)
+ return -1;
+
+ b := array of byte msg;
+ n := sys->write(mpc, b, len b);
+ if(n != len b)
+ n = -1;
+
+ return n;
+}
+
+keycolor(display: ref Display): ref Image
+{
+ getenv();
+ return display.color(Chroma);
+}
diff --git a/appl/lib/names.b b/appl/lib/names.b
new file mode 100644
index 00000000..e15ce189
--- /dev/null
+++ b/appl/lib/names.b
@@ -0,0 +1,152 @@
+implement Names;
+
+include "sys.m";
+
+include "names.m";
+
+# return name rewritten to compress /+, eliminate ., and interpret ..
+
+cleanname(name: string): string
+{
+ if(name == nil)
+ return ".";
+
+ p := rooted := name[0]=='/';
+ if(name[0] == '#'){ # special
+ if(len name < 2)
+ return name;
+ p += 2; # character after # whatever it is, is the name (including /)
+ for(; p < len name; p++)
+ if(name[p] == '/')
+ break;
+ rooted = p;
+ }
+ dotdot := rooted;
+
+ #
+ # invariants:
+ # p points at beginning of path element we're considering.
+ # out records up to the last path element (no trailing slash unless root or #/).
+ # dotdot points in out just past the point where .. cannot backtrack
+ # any further (no slash).
+ #
+ out := name[0:rooted];
+ while(p < len name){
+ for(q := p; p < len name && name[p] != '/'; p++){
+ # skip
+ }
+ n := name[q:p]; # path element
+ p++;
+ case n {
+ "" or "." =>
+ ; # null effect
+ ".." =>
+ if(len out > dotdot){ # can backtrack
+ for(q = len out; --q > dotdot && out[q] != '/';)
+ ;
+ out = out[:q];
+ }else if(!rooted){ # /.. is / but ./../ is ..
+ if(out != nil)
+ out += "/..";
+ else
+ out += "..";
+ dotdot = len out;
+ }
+ * =>
+ if(rooted > 1 || len out > rooted)
+ out[len out] = '/';
+ out += n;
+ }
+ }
+ if(out == nil)
+ return ".";
+ return out;
+}
+
+dirname(name: string): string
+{
+ for(i := len name; --i >= 0;)
+ if(name[i] == '/')
+ break;
+ if(i < 0)
+ return nil;
+ d := name[0:i];
+ if(d != nil)
+ return d;
+ if(name[0] == '/')
+ return "/";
+ return nil;
+}
+
+basename(name: string, suffix: string): string
+{
+ for(i := len name; --i >= 0;)
+ if(name[i] == '/')
+ break;
+ if(i >= 0)
+ name = name[i+1:];
+ if(suffix != nil){
+ o := len name - len suffix;
+ if(o >= 0 && name[o:] == suffix)
+ return name[0:o];
+ }
+ return name;
+}
+
+relative(name: string, root: string): string
+{
+ if(root == nil || name == nil)
+ return name;
+ if(isprefix(root, name)){
+ name = name[len root:];
+ while(name != nil && name[0] == '/')
+ name = name[1:];
+ }
+ return name;
+}
+
+rooted(root: string, name: string): string
+{
+ if(name == nil)
+ return root;
+ if(root == nil || name[0] == '/' || name[0] == '#')
+ return name;
+ if(root[len root-1] != '/' && name[0] != '/')
+ return root+"/"+name;
+ return root+name;
+}
+
+isprefix(a: string, b: string): int
+{
+ la := len a;
+ while(la > 1 && a[la-1] == '/')
+ a = a[0:--la];
+ lb := len b;
+ if(la > lb)
+ return 0;
+ if(la == lb)
+ return a == b;
+ return a == b[0:la] && b[la] == '/';
+}
+
+elements(name: string): list of string
+{
+ sys := load Sys Sys->PATH;
+ (nil, fld) := sys->tokenize(name, "/");
+ if(name != nil && name[0] == '/')
+ fld = "/" :: fld;
+ return fld;
+}
+
+pathname(els: list of string): string
+{
+ name: string;
+ sl := els != nil && hd els == "/";
+ for(; els != nil; els = tl els){
+ if(!sl)
+ name += "/";
+ name += hd els;
+ sl = 0;
+ }
+ return name;
+}
diff --git a/appl/lib/nametree.b b/appl/lib/nametree.b
new file mode 100644
index 00000000..b9975567
--- /dev/null
+++ b/appl/lib/nametree.b
@@ -0,0 +1,278 @@
+implement Nametree;
+include "sys.m";
+ sys: Sys;
+include "styx.m";
+include "styxservers.m";
+ Navop: import Styxservers;
+ Enotfound, Eexists: import Styxservers;
+
+Fholder: adt {
+ parentqid: big;
+ d: Sys->Dir;
+ child: cyclic ref Fholder;
+ sibling: cyclic ref Fholder;
+ hash: cyclic ref Fholder;
+};
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+start(): (ref Tree, chan of ref Styxservers->Navop)
+{
+ fs := ref Tree(chan of ref Treeop, chan of string);
+ c := chan of ref Styxservers->Navop;
+ spawn fsproc(c, fs.c);
+ return (fs, c);
+}
+
+Tree.quit(t: self ref Tree)
+{
+ t.c <-= nil;
+}
+
+Tree.create(t: self ref Tree, parentq: big, d: Sys->Dir): string
+{
+ t.c <-= ref Treeop.Create(t.reply, parentq, d);
+ return <-t.reply;
+}
+
+Tree.remove(t: self ref Tree, q: big): string
+{
+ t.c <-= ref Treeop.Remove(t.reply, q);
+ return <-t.reply;
+}
+
+Tree.wstat(t: self ref Tree, q: big, d: Sys->Dir): string
+{
+ t.c <-= ref Treeop.Wstat(t.reply, q, d);
+ return <-t.reply;
+}
+
+Tree.getpath(t: self ref Tree, q: big): string
+{
+ t. c <-= ref Treeop.Getpath(t.reply, q);
+ return <-t.reply;
+}
+
+fsproc(c: chan of ref Styxservers->Navop, fsc: chan of ref Treeop)
+{
+ tab := array[23] of ref Fholder;
+ starttime := 0;
+
+ for (;;) alt {
+ grq := <-c =>
+ if (grq == nil)
+ exit;
+ (q, reply) := (grq.path, grq.reply);
+ fh := findfile(tab, q);
+ if (fh == nil) {
+ reply <-= (nil, Enotfound);
+ continue;
+ }
+ pick rq := grq {
+ Stat =>
+ reply <-= (ref fh.d, nil);
+ Walk =>
+ d := fswalk(tab, fh, rq.name);
+ if (d == nil)
+ reply <-= (nil, Enotfound);
+ else
+ reply <-= (d, nil);
+ Readdir =>
+ (start, end) := (rq.offset, rq.offset + rq.count);
+ fh = fh.child;
+ for (i := 0; i < end && fh != nil; i++) {
+ if (i >= start)
+ reply <-= (ref fh.d, nil);
+ fh = fh.sibling;
+ }
+ reply <-= (nil, nil);
+ * =>
+ panic(sys->sprint("unknown op %d\n", tagof(grq)));
+ }
+ grq := <-fsc =>
+ if (grq == nil)
+ exit;
+ (q, reply) := (grq.q, grq.reply);
+ pick rq := grq {
+ Create =>
+ reply <-= fscreate(tab, q, rq.d);
+ Remove =>
+ reply <-= fsremove(tab, q);
+ Wstat =>
+ reply <-= fswstat(tab, q, rq.d);
+ Getpath =>
+ reply <-= fsgetpath(tab, q);
+ * =>
+ panic(sys->sprint("unknown fs op %d\n", tagof(grq)));
+ }
+ }
+}
+
+hashfn(q: big, n: int): int
+{
+ h := int (q % big n);
+ if (h < 0)
+ h += n;
+ return h;
+}
+
+findfile(tab: array of ref Fholder, q: big): ref Fholder
+{
+ for (fh := tab[hashfn(q, len tab)]; fh != nil; fh = fh.hash)
+ if (fh.d.qid.path == q)
+ return fh;
+ return nil;
+}
+
+fsgetpath(tab: array of ref Fholder, q: big): string
+{
+ fh := findfile(tab, q);
+ if (fh == nil)
+ return nil;
+ s := fh.d.name;
+ while (fh.parentqid != fh.d.qid.path) {
+ fh = findfile(tab, fh.parentqid);
+ if (fh == nil)
+ return nil;
+ s = fh.d.name + "/" + s;
+ }
+ return s;
+}
+
+fswalk(tab: array of ref Fholder, fh: ref Fholder, name: string): ref Sys->Dir
+{
+ if (name == "..")
+ return ref findfile(tab, fh.parentqid).d;
+ for (fh = fh.child; fh != nil; fh = fh.sibling)
+ if (fh.d.name == name)
+ return ref fh.d;
+ return nil;
+}
+
+fsremove(tab: array of ref Fholder, q: big): string
+{
+ prev: ref Fholder;
+
+ # remove from hash table
+ slot := hashfn(q, len tab);
+ for (fh := tab[slot]; fh != nil; fh = fh.hash) {
+ if (fh.d.qid.path == q)
+ break;
+ prev = fh;
+ }
+ if (fh == nil)
+ return Enotfound;
+ if (prev == nil)
+ tab[slot] = fh.hash;
+ else
+ prev.hash = fh.hash;
+ fh.hash = nil;
+
+ # remove from parent's children
+ parent := findfile(tab, fh.parentqid);
+ if (parent != nil) {
+ prev = nil;
+ for (sfh := parent.child; sfh != nil; sfh = sfh.sibling) {
+ if (sfh == fh)
+ break;
+ prev = sfh;
+ }
+ if (sfh == nil)
+ panic("child not found in parent");
+ if (prev == nil)
+ parent.child = fh.sibling;
+ else
+ prev.sibling = fh.sibling;
+ }
+ fh.sibling = nil;
+
+ # now remove any descendents
+ sibling: ref Fholder;
+ for (sfh := fh.child; sfh != nil; sfh = sibling) {
+ sibling = sfh.sibling;
+ sfh.parentqid = sfh.d.qid.path; # make sure it doesn't disrupt things.
+ fsremove(tab, sfh.d.qid.path);
+ }
+ return nil;
+}
+
+fscreate(tab: array of ref Fholder, q: big, d: Sys->Dir): string
+{
+ parent := findfile(tab, q);
+ if (findfile(tab, d.qid.path) != nil)
+ return Eexists;
+ # allow creation of a root directory only if its parent is itself
+ if (parent == nil && d.qid.path != q)
+ return Enotfound;
+ fh: ref Fholder;
+ if (parent == nil)
+ fh = ref Fholder(q, d, nil, nil, nil);
+ else {
+ if (fswalk(tab, parent, d.name) != nil)
+ return Eexists;
+ fh = ref Fholder(parent.d.qid.path, d, nil, nil, nil);
+ fh.sibling = parent.child;
+ parent.child = fh;
+ }
+ slot := hashfn(d.qid.path, len tab);
+ fh.hash = tab[slot];
+ tab[slot] = fh;
+ return nil;
+}
+
+fswstat(tab: array of ref Fholder, q: big, d: Sys->Dir): string
+{
+ fh := findfile(tab, q);
+ if (fh == nil)
+ return Enotfound;
+
+ d = applydir(d, fh.d);
+
+ # if renaming a file, check for duplicates
+ if (d.name != fh.d.name) {
+ parent := findfile(tab, fh.parentqid);
+ if (parent != nil && parent != fh && fswalk(tab, parent, d.name) != nil)
+ return Eexists;
+ }
+ fh.d = d;
+ fh.d.qid.path = q; # ensure the qid can't be changed
+ return nil;
+}
+
+applydir(d: Sys->Dir, onto: Sys->Dir): Sys->Dir
+{
+ if (d.name != nil)
+ onto.name = d.name;
+ if (d.uid != nil)
+ onto.uid = d.uid;
+ if (d.gid != nil)
+ onto.gid = d.gid;
+ if (d.muid != nil)
+ onto.muid = d.muid;
+ if (d.qid.vers != ~0)
+ onto.qid.vers = d.qid.vers;
+ if (d.qid.qtype != ~0)
+ onto.qid.qtype = d.qid.qtype;
+ if (d.mode != ~0)
+ onto.mode = d.mode;
+ if (d.atime != ~0)
+ onto.atime = d.atime;
+ if (d.mtime != ~0)
+ onto.mtime = d.mtime;
+ if (d.length != ~big 0)
+ onto.length = d.length;
+ if (d.dtype != ~0)
+ onto.dtype = d.dtype;
+ if (d.dev != ~0)
+ onto.dev = d.dev;
+ return onto;
+}
+
+panic(s: string)
+{
+ sys->fprint(sys->fildes(2), "panic: %s\n", s);
+ raise "panic";
+}
diff --git a/appl/lib/newns.b b/appl/lib/newns.b
new file mode 100644
index 00000000..7bca18dc
--- /dev/null
+++ b/appl/lib/newns.b
@@ -0,0 +1,434 @@
+implement Newns;
+#
+# Build a new namespace from a file
+#
+# new create a new namespace from current directory (use cd)
+# fork split the namespace before modification
+# nodev disallow device attaches
+# bind [-abrci] from to
+# mount [-abrci9] [net!]machine[!svc] to [spec]
+# import [-abrci9] [net!]machine[!svc] [remotedir] dir
+# unmount [-i] [from] to
+# cd directory
+#
+# -i to bind/mount/unmount means continue in the face of errors
+#
+include "sys.m";
+ sys: Sys;
+ FD, FileIO, Connection: import Sys;
+ stderr: ref FD;
+
+include "draw.m";
+
+include "bufio.m";
+ bio: Bufio;
+ Iobuf: import bio;
+
+include "newns.m";
+
+#include "sh.m";
+
+include "keyring.m";
+ kr: Keyring;
+
+include "security.m";
+ au: Auth;
+
+include "factotum.m";
+
+include "arg.m";
+ arg: Arg;
+
+newns(user: string, file: string): string
+{
+ sys = load Sys Sys->PATH;
+ kr = load Keyring Keyring->PATH;
+ stderr = sys->fildes(2);
+
+ # Could do some authentication here, and bail if no good FIXME
+ if(user == nil)
+ ;
+ bio = load Bufio Bufio->PATH;
+ if(bio == nil)
+ return sys->sprint("cannot load %s: %r", Bufio->PATH);
+
+ arg = load Arg Arg->PATH;
+ if (arg == nil)
+ return sys->sprint("cannot load %s: %r", Arg->PATH);
+
+ au = load Auth Auth->PATH;
+ if(au == nil)
+ return sys->sprint("cannot load %s: %r", Auth->PATH);
+ err := au->init();
+ if(err != nil)
+ return "Auth->init: "+err;
+
+ if(file == nil){
+ file = "namespace";
+ if(sys->stat(file).t0 < 0)
+ file = "/lib/namespace";
+ }
+
+ mfp := bio->open(file, bio->OREAD);
+ if(mfp==nil)
+ return sys->sprint("cannot open %q: %r", file);
+
+ if(0 && user != nil){
+ sys->pctl(Sys->FORKENV, nil);
+ setenv("user", user);
+ setenv("home", "/usr/"+user);
+ }
+
+ facfd := sys->open("/mnt/factotum/rpc", Sys->ORDWR);
+ return nsfile(mfp, facfd);
+}
+
+nsfile(b: ref Iobuf, facfd: ref Sys->FD): string
+{
+ e := "";
+ while((l := b.gets('\n')) != nil){
+ (n, slist) := sys->tokenize(l, " \t\n\r"); # should use str->unquote?
+ if(n <= 0)
+ continue;
+ e = nsop(expand(slist), facfd);
+ if(e != "")
+ break;
+ }
+ return e;
+}
+
+expand(l: list of string): list of string
+{
+ nl: list of string;
+ for(; l != nil; l = tl l){
+ s := hd l;
+ for(i := 0; i < len s; i++)
+ if(s[i] == '$'){
+ for(j := i+1; j < len s; j++)
+ if((c := s[j]) == '.' || c == '/' || c == '$')
+ break;
+ if(j > i+1){
+ (ok, v) := getenv(s[i+1:j]);
+ if(!ok)
+ return nil;
+ s = s[0:i] + v + s[j:];
+ i = i + len v;
+ }
+ }
+ nl = s :: nl;
+ }
+ l = nil;
+ for(; nl != nil; nl = tl nl)
+ l = hd nl :: l;
+ return l;
+}
+
+nsop(argv: list of string, facfd: ref Sys->FD): string
+{
+ # ignore comments
+ if(argv == nil || (hd argv)[0] == '#')
+ return nil;
+
+ e := "";
+ c := 0;
+ cmdstr := hd argv;
+ case cmdstr {
+ "." =>
+ if(tl argv == nil)
+ return ".: needs a filename";
+ nsf := hd tl argv;
+ mfp := bio->open(nsf, bio->OREAD);
+ if(mfp==nil)
+ return sys->sprint("can't open %q for read %r", nsf);
+ e = nsfile(mfp, facfd);
+ "new" =>
+ c = Sys->NEWNS | Sys->FORKENV;
+ "clear" =>
+ if(sys->pctl(Sys->FORKNS, nil) < 0 ||
+ sys->bind("#/", "/", Sys->MREPL) < 0 ||
+ sys->chdir("/") < 0 ||
+ sys->pctl(Sys->NEWNS, nil) < 0)
+ return sys->sprint("%r");
+ return nil;
+ "fork" =>
+ c = Sys->FORKNS;
+ "nodev" =>
+ c = Sys->NODEVS;
+ "bind" =>
+ e = bind(argv);
+ "mount" =>
+ e = mount(argv, facfd);
+ "unmount" =>
+ e = unmount(argv);
+ "import" =>
+ e = import9(argv, facfd);
+ "cd" =>
+ if(len argv != 2)
+ return "cd: must have one argument";
+ if(sys->chdir(hd tl argv) < 0)
+ return sys->sprint("%r");
+ * =>
+ e = "invalid namespace command";
+ }
+ if(c != 0) {
+ if(sys->pctl(c, nil) < 0)
+ return sys->sprint("%r");
+ }
+ return e;
+}
+
+Moptres: adt {
+ argv: list of string;
+ flags: int;
+ alg: string;
+ keyfile: string;
+ ignore: int;
+ use9: int;
+};
+
+mopt(argv: list of string): (ref Moptres, string)
+{
+ r := ref Moptres(nil, 0, "none", nil, 0, 0);
+
+ arg->init(argv);
+ while ((opt := arg->opt()) != 0) {
+ case opt {
+ 'i' => r.ignore = 1;
+ 'a' => r.flags |= sys->MAFTER;
+ 'b' => r.flags |= sys->MBEFORE;
+ 'c' => r.flags |= sys->MCREATE;
+ 'r' => r.flags |= sys->MREPL;
+ 'k' =>
+ if((r.keyfile = arg->arg()) == nil)
+ return (nil, "mount: missing arg to -k option");
+ 'C' =>
+ if((r.alg = arg->arg()) == nil)
+ return (nil, "mount: missing arg to -C option");
+ '9' =>
+ r.use9 = 1;
+ * =>
+ return (nil, sys->sprint("mount: bad option -%c", opt));
+ }
+ }
+ if((r.flags & (Sys->MAFTER|Sys->MBEFORE)) == 0)
+ r.flags |= Sys->MREPL;
+
+ r.argv = arg->argv();
+ return (r, nil);
+}
+
+bind(argv: list of string): string
+{
+ (r, err) := mopt(argv);
+ if(err != nil)
+ return err;
+
+ if(len r.argv < 2)
+ return "bind: too few args";
+
+ from := hd r.argv;
+ r.argv = tl r.argv;
+ todir := hd r.argv;
+ if(sys->bind(from, todir, r.flags) < 0)
+ return ig(r, sys->sprint("bind %s %s: %r", from, todir));
+
+ return nil;
+}
+
+mount(argv: list of string, facfd: ref Sys->FD): string
+{
+ fd: ref Sys->FD;
+
+ (r, err) := mopt(argv);
+ if(err != nil)
+ return err;
+
+ if(len r.argv < 2)
+ return ig(r, "mount: too few args");
+
+ addr := hd r.argv;
+ r.argv = tl r.argv;
+ dest := netmkaddr(addr, "net", "styx");
+ dir := hd r.argv;
+ r.argv = tl r.argv;
+ if(r.argv != nil)
+ spec := hd r.argv;
+
+ (ok, c) := sys->dial(dest, nil);
+ if(ok < 0)
+ return ig(r, sys->sprint("dial: %s: %r", dest));
+
+ if(r.use9){
+ factotum := load Factotum Factotum->PATH;
+ if(factotum == nil)
+ return ig(r, sys->sprint("cannot load %s: %r", Factotum->PATH));
+ factotum->init();
+ afd := sys->fauth(fd, spec);
+ ai := factotum->proxy(afd, facfd, "proto=p9any role=client");
+ if(sys->mount(fd, afd, dir, r.flags, spec) < 0)
+ return ig(r, sys->sprint("mount %q %q: %r", addr, dir));
+ return nil;
+ }
+
+ user := user();
+ kd := "/usr/" + user + "/keyring/";
+ cert: string;
+ if (r.keyfile != nil) {
+ cert = r.keyfile;
+ if (cert[0] != '/')
+ cert = kd + cert;
+ (ok, nil) = sys->stat(cert);
+ if (ok<0)
+ return ig(r, sys->sprint("cannot find certificate %q: %r", cert));
+ } else {
+ cert = kd + addr;
+ (ok, nil) = sys->stat(cert);
+ if(ok < 0){
+ cert = kd + "default";
+ (ok, nil) = sys->stat(cert);
+ }
+ }
+ ai := kr->readauthinfo(cert);
+ if(ai == nil)
+ return ig(r, sys->sprint("cannot read certificate from %q: %r", cert));
+
+ err = au->init();
+ if (err != nil)
+ return ig(r, sys->sprint("auth->init: %r"));
+ (fd, err) = au->client(r.alg, ai, c.dfd);
+ if(fd == nil)
+ return ig(r, sys->sprint("auth: %r"));
+
+ if(sys->mount(fd, nil, dir, r.flags, spec) < 0)
+ return ig(r, sys->sprint("mount %q %q: %r", addr, dir));
+
+ return nil;
+}
+
+import9(argv: list of string, facfd: ref Sys->FD): string
+{
+ (r, err) := mopt(argv);
+ if(err != nil)
+ return err;
+
+ if(len r.argv < 2)
+ return "import: too few args";
+ if(facfd == nil)
+ return ig(r, "import: no factotum");
+ factotum := load Factotum Factotum->PATH;
+ if(factotum == nil)
+ return ig(r, sys->sprint("cannot load %s: %r", Factotum->PATH));
+ factotum->init();
+ addr := hd r.argv;
+ r.argv = tl r.argv;
+ rdir := hd r.argv;
+ r.argv = tl r.argv;
+ dir := rdir;
+ if(r.argv != nil)
+ dir = hd r.argv;
+ dest := netmkaddr(addr, "net", "17007"); # exportfs; might not be in inferno's ndb yet
+ (ok, c) := sys->dial(dest, nil);
+ if(ok < 0)
+ return ig(r, sys->sprint("import: %s: %r", dest));
+ fd := c.dfd;
+ if(factotum->proxy(fd, facfd, "proto=p9any role=client") == nil)
+ return ig(r, sys->sprint("import: %s: %r", dest));
+ if(sys->fprint(fd, "%s", rdir) < 0)
+ return ig(r, sys->sprint("import: %s: %r", dest));
+ buf := array[256] of byte;
+ if((n := sys->read(fd, buf, len buf)) != 2 || buf[0] != byte 'O' || buf[1] != byte 'K'){
+ if(n >= 4)
+ sys->werrstr(string buf[0:n]);
+ return ig(r, sys->sprint("import: %s: %r", dest));
+ }
+ # TO DO: new style: impo aan|nofilter clear|ssl|tls\n
+ afd := sys->fauth(fd, "");
+ ai := factotum->proxy(afd, facfd, "proto=p9any role=client");
+ if(sys->mount(fd, afd, dir, r.flags, "") < 0)
+ return ig(r, sys->sprint("import %q %q: %r", addr, dir));
+ return nil;
+}
+
+unmount(argv: list of string): string
+{
+ (r, err) := mopt(argv);
+ if(err != nil)
+ return err;
+
+ from, tu: string;
+ case len r.argv {
+ * =>
+ return "unmount: takes 1 or 2 args";
+ 1 =>
+ from = nil;
+ tu = hd r.argv;
+ 2 =>
+ from = hd r.argv;
+ tu = hd tl r.argv;
+ }
+
+ if(sys->unmount(from, tu) < 0)
+ return ig(r, sys->sprint("unmount: %r"));
+
+ return nil;
+}
+
+ig(r: ref Moptres, e: string): string
+{
+ if(r.ignore)
+ return nil;
+ return e;
+}
+
+user(): string
+{
+ sys = load Sys Sys->PATH;
+
+ fd := sys->open("/dev/user", sys->OREAD);
+ if(fd == nil)
+ return "";
+
+ buf := array[Sys->NAMEMAX] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return "";
+
+ return string buf[0:n];
+}
+
+getenv(name: string): (int, string)
+{
+ fd := sys->open("#e/"+name, Sys->OREAD);
+ if(fd == nil)
+ return (0, nil);
+ b := array[256] of byte;
+ n := sys->read(fd, b, len b);
+ if(n <= 0)
+ return (1, "");
+ for(i := 0; i < n; i++)
+ if(b[i] == byte 0 || b[i] == byte '\n')
+ break;
+ return (1, string b[0:i]);
+}
+
+setenv(name: string, val: string)
+{
+ fd := sys->create("#e/"+name, Sys->OWRITE, 8r664);
+ if(fd != nil)
+ sys->fprint(fd, "%s", val);
+}
+
+netmkaddr(addr, net, svc: string): string
+{
+ if(net == nil)
+ net = "net";
+ (n, nil) := sys->tokenize(addr, "!");
+ if(n <= 1){
+ if(svc== nil)
+ return sys->sprint("%s!%s", net, addr);
+ return sys->sprint("%s!%s!%s", net, addr, svc);
+ }
+ if(svc == nil || n > 2)
+ return addr;
+ return sys->sprint("%s!%s", addr, svc);
+}
diff --git a/appl/lib/palm.b b/appl/lib/palm.b
new file mode 100644
index 00000000..d5a8ce34
--- /dev/null
+++ b/appl/lib/palm.b
@@ -0,0 +1,504 @@
+implement Palm;
+
+#
+# Copyright © 2001-2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+# Based on ``Palm® File Format Specification'', Document Number 3008-004, 1 May 2001, by Palm Inc.
+# Doc compression based on description by Paul Lucas, 18 August 1998
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "daytime.m";
+ daytime: Daytime;
+
+include "palm.m";
+
+# Exact value of "Jan 1, 1970 0:00:00 GMT" - "Jan 1, 1904 0:00:00 GMT"
+Epochdelta: con 2082844800;
+tzoff := 0;
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ daytime = load Daytime Daytime->PATH;
+ if(daytime == nil)
+ return "can't load required module";
+ tzoff = daytime->local(0).tzoff;
+ return nil;
+}
+
+Record.new(id: int, attr: int, cat: int, size: int): ref Record
+{
+ return ref Record(id, attr, cat, array[size] of byte);
+}
+
+Resource.new(name: int, id: int, size: int): ref Resource
+{
+ return ref Resource(name, id, array[size] of byte);
+}
+
+Doc.open(m: Palmdb, file: ref Palmdb->PDB): (ref Doc, string)
+{
+ info := m->file.db.stat();
+ if(info.dtype != "TEXt" || info.creator != "REAd")
+ return (nil, "not a Doc file: wrong type or creator");
+ r := m->file.read(0);
+ if(r == nil)
+ return (nil, sys->sprint("not a valid Doc file: %r"));
+ a := r.data;
+ if(len a < 16)
+ return (nil, sys->sprint("not a valid Doc file: bad length: %d", len a));
+ maxrec := m->file.db.nentries()-1;
+ d := ref Doc;
+ d.m = m;
+ d.file = file;
+ d.version = get2(a);
+ err := "unknown";
+ if(d.version != 1 && d.version != 2)
+ err = "unknown Docfile version";
+ # a[2:] is spare
+ d.length = get4(a[4:]);
+ d.nrec = get2(a[8:]);
+ if(maxrec >= 0 && d.nrec > maxrec){
+ d.nrec = maxrec;
+ err = "invalid record count";
+ }
+ d.recsize = get2(a[10:]);
+ d.position = get4(a[12:]);
+ return (d, sys->sprint("unexpected Doc file format: %s", err));
+}
+
+Doc.iscompressed(d: self ref Doc): int
+{
+ return (d.version&7) == 2; # high-order bits are sometimes used, ignore them
+}
+
+Doc.read(doc: self ref Doc, index: int): (string, string)
+{
+ m := doc.m;
+ DB, PDB: import m;
+ r := doc.file.read(index+1);
+ if(r == nil)
+ return (nil, sys->sprint("%r"));
+ (s, serr) := doc.unpacktext(r.data);
+ if(s == nil)
+ return (nil, serr);
+ return (s, nil);
+}
+
+Doc.unpacktext(doc: self ref Doc, a: array of byte): (string, string)
+{
+ nb := len a;
+ s: string;
+ if(!doc.iscompressed()){
+ for(i := 0; i < nb; i++)
+ s[len s] = int a[i]; # assumes Latin-1
+ return (s, nil);
+ }
+ o := 0;
+ for(i := 0; i < nb;){
+ c := int a[i++];
+ if(c >= 9 && c <= 16r7F || c == 0)
+ s[o++] = c;
+ else if(c >= 1 && c <= 8){
+ if(i+c > nb)
+ return (nil, "missing data in record");
+ while(--c >= 0)
+ s[o++] = int a[i++];
+ }else if(c >= 16rC0 && c <= 16rFF){
+ s[o] = ' ';
+ s[o+1] = c & 16r7F;
+ o += 2;
+ }else{ # c >= 0x80 && c <= 16rBF
+ v := int a[i++];
+ m := ((c & 16r3F)<<5)|(v>>3);
+ n := (v&7) + 3;
+ if(m == 0 || m > o)
+ return (nil, sys->sprint("data is corrupt: m=%d n=%d o=%d", m, n, o));
+ for(; --n >= 0; o++)
+ s[o] = s[o-m];
+ }
+ }
+ return (s, nil);
+}
+
+Doc.textlength(doc: self ref Doc, a: array of byte): int
+{
+ nb := len a;
+ if(!doc.iscompressed())
+ return nb;
+ o := 0;
+ for(i := 0; i < nb;){
+ c := int a[i++];
+ if(c >= 9 && c <= 16r7F || c == 0)
+ o++;
+ else if(c >= 1 && c <= 8){
+ if(i+c > nb)
+ return -1;
+ o += c;
+ i += c;
+ }else if(c >= 16rC0 && c <= 16rFF){
+ o += 2;
+ }else{ # c >= 0x80 && c <= 16rBF
+ v := int a[i++];
+ m := ((c & 16r3F)<<5)|(v>>3);
+ n := (v&7) + 3;
+ if(m == 0 || m > o)
+ return -1;
+ o += n;
+ }
+ }
+ return o;
+}
+
+id2s(i: int): string
+{
+ if(i == 0)
+ return "";
+ return sys->sprint("%c%c%c%c", (i>>24)&16rFF, (i>>16)&16rFF, (i>>8)&16rFF, i&16rFF);
+}
+
+s2id(s: string): int
+{
+ n := 0;
+ for(i := 0; i < 4; i++){
+ c := 0;
+ if(i < len s)
+ c = s[i] & 16rFF;
+ n = (n<<8) | c;
+ }
+ return n;
+}
+
+DBInfo.new(name: string, attr: int, dtype: string, version: int, creator: string): ref DBInfo
+{
+ info := ref DBInfo;
+ info.name = name;
+ info.attr = attr;
+ info.version = version;
+ info.ctime = daytime->now();
+ info.mtime = daytime->now();
+ info.btime = 0;
+ info.modno = 0;
+ info.dtype = dtype;
+ info.creator = creator;
+ info.uidseed = 0;
+ info.index = 0;
+ return info;
+}
+
+Categories.new(labels: array of string): ref Categories
+{
+ c := ref Categories;
+ c.renamed = 0;
+ c.lastuid = 0;
+ c.labels = array[16] of string;
+ c.uids = array[] of {0 to 15 => 0};
+ for(i := 0; i < len labels && i < 16; i++){
+ c.labels[i] = labels[i];
+ c.lastuid = 16r80 + i;
+ c.uids[i] = c.lastuid;
+ }
+ return c;
+}
+
+Categories.unpack(a: array of byte): ref Categories
+{
+ if(len a < 16r114)
+ return nil; # doesn't match the structure
+ c := ref Categories;
+ c.renamed = get2(a);
+ c.labels = array[16] of string;
+ c.uids = array[16] of int;
+ j := 2;
+ for(i := 0; i < 16; i++){
+ c.labels[i] = latin1(a[j:j+16], 0);
+ j += 16;
+ c.uids[i] = int a[16r102+i];
+ }
+ c.lastuid = int a[16r112];
+ # one byte of padding is shown on p. 26, but
+ # two more are invariably used in practice
+ # before application specific data.
+ if(len a > 16r116)
+ c.appdata = a[16r116:];
+ return c;
+}
+
+Categories.pack(c: self ref Categories): array of byte
+{
+ a := array[16r116 + len c.appdata] of byte;
+ put2(a, c.renamed);
+ j := 2;
+ for(i := 0; i < 16; i++){
+ puts(a[j:j+16], c.labels[i]);
+ j += 16;
+ a[16r102+i] = byte c.uids[i];
+ }
+ a[16r112] = byte c.lastuid;
+ a[16r113] = byte 0; # pad shown on p. 26
+ a[16r114] = byte 0; # extra two bytes of padding used in practice
+ a[16r115] = byte 0;
+ if(c.appdata != nil)
+ a[16r116:] = c.appdata;
+ return a;
+}
+
+Categories.mkidmap(c: self ref Categories): array of int
+{
+ a := array[256] of {* => 0};
+ for(i := 0; i < len c.uids; i++)
+ a[c.uids[i]] = i;
+ return a;
+}
+
+#
+# because PalmOS treats all times as local times, and doesn't associate
+# them with time zones, we'll convert using local time on Plan 9 and Inferno
+#
+
+pilot2epoch(t: int): int
+{
+ if(t == 0)
+ return 0; # we'll assume it's not set
+ return t - Epochdelta + tzoff;
+}
+
+epoch2pilot(t: int): int
+{
+ if(t == 0)
+ return t;
+ return t - tzoff + Epochdelta;
+}
+
+#
+# map Palm name to string, assuming iso-8859-1,
+# but remap space and /
+#
+latin1(a: array of byte, remap: int): string
+{
+ s := "";
+ for(i := 0; i < len a; i++){
+ c := int a[i];
+ if(c == 0)
+ break;
+ if(remap){
+ if(c == ' ')
+ c = 16r00A0; # unpaddable space
+ else if(c == '/')
+ c = 16r2215; # division /
+ }
+ s[len s] = c;
+ }
+ return s;
+}
+
+#
+# map from Unicode to Palm name
+#
+filename(name: string): string
+{
+ s := "";
+ for(i := 0; i < len name; i++){
+ c := name[i];
+ if(c == ' ')
+ c = 16r00A0; # unpaddable space
+ else if(c == '/')
+ c = 16r2215; # division solidus
+ s[len s] = c;
+ }
+ return s;
+}
+
+dbname(name: string): string
+{
+ s := "";
+ for(i := 0; i < len name; i++){
+ c := name[i];
+ case c {
+ 0 => c = ' '; # unlikely, but just in case
+ 16r2215 => c = '/';
+ 16r00A0 => c = ' ';
+ }
+ s[len s] = c;
+ }
+ return s;
+}
+
+#
+# string conversion: can't use (string a) because
+# the bytes are Latin1, not Unicode
+#
+gets(a: array of byte): string
+{
+ s := "";
+ for(i := 0; i < len a; i++)
+ s[len s] = int a[i];
+ return s;
+}
+
+puts(a: array of byte, s: string)
+{
+ for(i := 0; i < len a-1 && i < len s; i++)
+ a[i] = byte s[i];
+ for(; i < len a; i++)
+ a[i] = byte 0;
+}
+
+#
+# big-endian packing
+#
+
+get4(p: array of byte): int
+{
+ return (((((int p[0] << 8) | int p[1]) << 8) | int p[2]) << 8) | int p[3];
+}
+
+get3(p: array of byte): int
+{
+ return (((int p[0] << 8) | int p[1]) << 8) | int p[2];
+}
+
+get2(p: array of byte): int
+{
+ return (int p[0]<<8) | int p[1];
+}
+
+put4(p: array of byte, v: int)
+{
+ p[0] = byte (v>>24);
+ p[1] = byte (v>>16);
+ p[2] = byte (v>>8);
+ p[3] = byte (v & 16rFF);
+}
+
+put3(p: array of byte, v: int)
+{
+ p[0] = byte (v>>16);
+ p[1] = byte (v>>8);
+ p[2] = byte (v & 16rFF);
+}
+
+put2(p: array of byte, v: int)
+{
+ p[0] = byte (v>>8);
+ p[1] = byte (v & 16rFF);
+}
+
+#
+# DL protocol argument wrapping, based on conventions
+# extracted from include/Core/System/DLCommon.h in SDK 5
+#
+# tiny arguments
+# id: byte
+# size: byte # excluding this header
+# data: byte[]
+#
+# small arguments
+# id: byte # with 16r80 flag
+# pad: byte
+# size: byte[2]
+# data: byte[]
+#
+# long arguments
+# id: byte # with 16r40 flag
+# pad: byte
+# size: byte[4]
+# data: byte[]
+
+# wrapper format flag in request/response argument ID
+ShortWrap: con 16r80; # 2-byte count
+LongWrap: con 16r40; # 4-byte count
+
+Eshort: con "response shorter than expected";
+
+#
+# set the system error string
+#
+e(s: string): string
+{
+ if(s != nil)
+ sys->werrstr(s);
+ return s;
+}
+
+argsize(args: array of (int, array of byte)): int
+{
+ totnb := 0;
+ for(i := 0; i < len args; i++){
+ (nil, a) := args[i];
+ n := len a;
+ if(n > 65535)
+ totnb += 6; # long wrap
+ else if(n > 255)
+ totnb += 4; # short
+ else
+ totnb += 2; # tiny
+ totnb += n;
+ }
+ return totnb;
+}
+
+packargs(out: array of byte, args: array of (int, array of byte)): array of byte
+{
+ for(i := 0; i < len args; i++){
+ (id, a) := args[i];
+ n := len a;
+ if(n > 65535){
+ out[0] = byte (LongWrap|ShortWrap|id);
+ out[1] = byte 0;
+ put4(out[2:], n);
+ out = out[6:];
+ }else if(n > 255){
+ out[0] = byte (ShortWrap|id);
+ out[1] = byte 0;
+ put2(out[2:], n);
+ out = out[4:];
+ }else{
+ out[0] = byte id;
+ out[1] = byte n;
+ out = out[2:];
+ }
+ out[0:] = a;
+ out = out[n:];
+ }
+ return out;
+}
+
+unpackargs(argc: int, reply: array of byte): (array of (int, array of byte), string)
+{
+ replies := array[argc] of (int, array of byte);
+ o := 0;
+ for(i := 0; i < len replies; i++){
+ o = (o+1)&~1; # each argument starts at even offset
+ a := reply[o:];
+ if(len a < 2)
+ return (nil, e(Eshort));
+ rid := int a[0];
+ l: int;
+ if(rid & LongWrap){
+ if(len a < 6)
+ return (nil, e(Eshort));
+ l = get4(a[2:]);
+ a = a[6:];
+ o += 6;
+ }else if(rid & ShortWrap){
+ if(len a < 4)
+ return (nil, e(Eshort));
+ l = get2(a[2:]);
+ a = a[4:];
+ o += 4;
+ }else{
+ l = int a[1];
+ a = a[2:];
+ o += 2;
+ }
+ if(len a < l)
+ return (nil, e(Eshort));
+ replies[i] = (rid &~ 16rC0, a[0:l]);
+ o += l;
+ }
+ return (replies, nil);
+}
diff --git a/appl/lib/palmdb.b b/appl/lib/palmdb.b
new file mode 100644
index 00000000..d734e529
--- /dev/null
+++ b/appl/lib/palmdb.b
@@ -0,0 +1,576 @@
+implement Palmdb;
+
+#
+# Copyright © 2001-2002 Vita Nuova Holdings Limited. All rights reserved.
+#
+# Based on ``Palm® File Format Specification'', Document Number 3008-004, 1 May 2001, by Palm Inc.
+# Doc compression based on description by Paul Lucas, 18 August 1998
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "daytime.m";
+ daytime: Daytime;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "palm.m";
+ palm: Palm;
+ DBInfo, Record, Resource, get2, get3, get4, put2, put3, put4, gets, puts: import palm;
+ filename, dbname: import palm;
+
+Entry: adt {
+ id: int; # resource: id; record: unique ID
+ offset: int;
+ size: int;
+ name: int; # resource entry only
+ attr: int; # record entry only
+};
+
+Ofile: adt {
+ fname: string;
+ f: ref Iobuf;
+ mode: int;
+ info: ref DBInfo;
+ appinfo: array of byte;
+ sortinfo: array of int;
+ uidseed: int;
+ entries: array of ref Entry;
+};
+
+files: array of ref Ofile;
+
+Dbhdrlen: con 72+6;
+Datahdrsize: con 4+1+3;
+Resourcehdrsize: con 4+2+4;
+
+# Exact value of "Jan 1, 1970 0:00:00 GMT" - "Jan 1, 1904 0:00:00 GMT"
+Epochdelta: con 2082844800;
+tzoff := 0;
+
+init(m: Palm): string
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ daytime = load Daytime Daytime->PATH;
+ if(bufio == nil || daytime == nil)
+ return "can't load required module";
+ palm = m;
+ tzoff = daytime->local(0).tzoff;
+ return nil;
+}
+
+Eshort: con "file format error: too small";
+
+DB.open(name: string, mode: int): (ref DB, string)
+{
+ if(mode != Sys->OREAD)
+ return (nil, "invalid mode");
+ fd := sys->open(name, mode);
+ if(fd == nil)
+ return (nil, sys->sprint("%r"));
+ (ok, d) := sys->fstat(fd);
+ if(ok < 0)
+ return (nil, sys->sprint("%r"));
+ length := int d.length;
+ if(length == 0)
+ return (nil, "empty file");
+ (pf, ofile, fx) := mkpfile(name, mode);
+
+ f := bufio->fopen(fd, mode); # automatically closed if open fails
+
+ p := array[Dbhdrlen] of byte;
+ if(f.read(p, Dbhdrlen) != Dbhdrlen)
+ return (nil, "invalid file header: too short");
+
+ ip := ofile.info;
+ ip.name = gets(p[0:32]);
+ ip.attr = get2(p[32:]);
+ ip.version = get2(p[34:]);
+ ip.ctime = pilot2epoch(get4(p[36:]));
+ ip.mtime = pilot2epoch(get4(p[40:]));
+ ip.btime = pilot2epoch(get4(p[44:]));
+ ip.modno = get4(p[48:]);
+ appinfo := get4(p[52:]);
+ sortinfo := get4(p[56:]);
+ if(appinfo < 0 || sortinfo < 0 || (appinfo|sortinfo)&1)
+ return (nil, "invalid header: bad offset");
+ ip.dtype = xs(get4(p[60:]));
+ ip.creator = xs(get4(p[64:]));
+ ofile.uidseed = ip.uidseed = get4(p[68:]);
+
+ if(get4(p[72:]) != 0)
+ return (nil, "chained headers not supported"); # Palm says to reject such files
+ nrec := get2(p[76:]);
+ if(nrec < 0)
+ return (nil, sys->sprint("invalid header: bad record count: %d", nrec));
+
+ esize := Datahdrsize;
+ if(ip.attr & Palm->Fresource)
+ esize = Resourcehdrsize;
+
+ dataoffset := length;
+ ofile.entries = array[nrec] of ref Entry;
+ if(nrec > 0){
+ laste: ref Entry;
+ buf := array[esize] of byte;
+ for(i := 0; i < nrec; i++){
+ if(f.read(buf, len buf) != len buf)
+ return (nil, Eshort);
+ e := ref Entry;
+ if(ip.attr & Palm->Fresource){
+ # resource entry: type[4], id[2], offset[4]
+ e.name = get4(buf);
+ e.id = get2(buf[4:]);
+ e.offset = get4(buf[6:]);
+ e.attr = 0;
+ }else{
+ # record entry: offset[4], attr[1], id[3]
+ e.offset = get4(buf);
+ e.attr = int buf[4];
+ e.id = get3(buf[5:]);
+ e.name = 0;
+ }
+ if(laste != nil)
+ laste.size = e.offset - laste.offset;
+ laste = e;
+ ofile.entries[i] = e;
+ }
+ if(laste != nil)
+ laste.size = length - laste.offset;
+ dataoffset = ofile.entries[0].offset;
+ }else{
+ if(f.read(p, 2) != 2)
+ return (nil, Eshort); # discard placeholder bytes
+ }
+
+ n := 0;
+ if(appinfo > 0){
+ n = appinfo - int f.offset();
+ while(--n >= 0)
+ f.getb();
+ if(sortinfo)
+ n = sortinfo - appinfo;
+ else
+ n = dataoffset - appinfo;
+ ofile.appinfo = array[n] of byte;
+ if(f.read(ofile.appinfo, n) != n)
+ return (nil, Eshort);
+ }
+ if(sortinfo > 0){
+ n = sortinfo - int f.offset();
+ while(--n >= 0)
+ f.getb();
+ n = (dataoffset-sortinfo)/2;
+ ofile.sortinfo = array[n] of int;
+ tmp := array[2*n] of byte;
+ if(f.read(tmp, len tmp) != len tmp)
+ return (nil, Eshort);
+ for(i := 0; i < n; i++)
+ ofile.sortinfo[i] = get2(tmp[2*i:]);
+ }
+ ofile.f = f; # safe to save open file reference
+ files[fx] = ofile;
+ return (pf, nil);
+}
+
+DB.close(db: self ref DB): string
+{
+ ofile := files[db.x];
+ if(ofile.f != nil){
+ ofile.f.close();
+ ofile.f = nil;
+ }
+ files[db.x] = nil;
+ return nil;
+}
+
+DB.stat(db: self ref DB): ref DBInfo
+{
+ return ref *files[db.x].info;
+}
+
+DB.create(name: string, mode: int, perm: int, info: ref DBInfo): (ref DB, string)
+{
+ return (nil, "DB.create not implemented");
+}
+
+DB.wstat(db: self ref DB, ip: ref DBInfo, flags: int)
+{
+ raise "DB.wstat not implemented";
+}
+
+#DB.wstat(db: self ref DB, ip: ref DBInfo): string
+#{
+# ofile := files[db.x];
+# if(ofile.mode != Sys->OWRITE)
+# return "not open for writing";
+# if((ip.attr & Palm->Fresource) != (ofile.info.attr & Palm->Fresource))
+# return "cannot change file type";
+# # copy only a subset
+# ofile.info.name = ip.name;
+# ofile.info.attr = ip.attr;
+# ofile.info.version = ip.version;
+# ofile.info.ctime = ip.ctime;
+# ofile.info.mtime = ip.mtime;
+# ofile.info.btime = ip.btime;
+# ofile.info.modno = ip.modno;
+# ofile.info.dtype = ip.dtype;
+# ofile.info.creator = ip.creator;
+# return nil;
+#}
+
+DB.rdappinfo(db: self ref DB): (array of byte, string)
+{
+ return (files[db.x].appinfo, nil);
+}
+
+DB.wrappinfo(db: self ref DB, data: array of byte): string
+{
+ ofile := files[db.x];
+ if(ofile.mode != Sys->OWRITE)
+ return "not open for writing";
+ ofile.appinfo = array[len data] of byte;
+ ofile.appinfo[0:] = data;
+ return nil;
+}
+
+DB.rdsortinfo(db: self ref DB): (array of int, string)
+{
+ return (files[db.x].sortinfo, nil);
+}
+
+DB.wrsortinfo(db: self ref DB, sort: array of int): string
+{
+ ofile := files[db.x];
+ if(ofile.mode != Sys->OWRITE)
+ return "not open for writing";
+ ofile.sortinfo = array[len sort] of int;
+ ofile.sortinfo[0:] = sort;
+ return nil;
+}
+
+DB.readidlist(db: self ref DB, nil: int): array of int
+{
+ ent := files[db.x].entries;
+ a := array[len ent] of int;
+ for(i := 0; i < len a; i++)
+ a[i] = ent[i].id;
+ return a;
+}
+
+DB.nentries(db: self ref DB): int
+{
+ return len files[db.x].entries;
+}
+
+DB.resetsyncflags(db: self ref DB): string
+{
+ raise "DB.resetsyncflags not implemented";
+}
+
+DB.records(db: self ref DB): ref PDB
+{
+ if(db == nil || db.attr & Palm->Fresource)
+ return nil;
+ return ref PDB(db);
+}
+
+DB.resources(db: self ref DB): ref PRC
+{
+ if(db == nil || (db.attr & Palm->Fresource) == 0)
+ return nil;
+ return ref PRC(db);
+}
+
+PDB.read(pdb: self ref PDB, i: int): ref Record
+{
+ ofile := files[pdb.db.x];
+ if(i < 0 || i >= len ofile.entries){
+ if(i == len ofile.entries)
+ return nil; # treat as end-of-file
+ #return "index out of range";
+ return nil;
+ }
+ e := ofile.entries[i];
+ nb := e.size;
+ r := ref Record(e.id, e.attr & 16rF0, e.attr & 16r0F, array[nb] of byte);
+ ofile.f.seek(big e.offset, 0);
+ if(ofile.f.read(r.data, nb) != nb)
+ return nil;
+ return r;
+}
+
+PDB.readid(pdb: self ref PDB, id: int): (ref Record, int)
+{
+ ofile := files[pdb.db.x];
+ ent := ofile.entries;
+ for(i := 0; i < len ent; i++)
+ if((e := ent[i]).id == id){
+ nb := e.size;
+ r := ref Record(e.id, e.attr & 16rF0, e.attr & 16r0F, array[e.size] of byte);
+ ofile.f.seek(big e.offset, 0);
+ if(ofile.f.read(r.data, nb) != nb)
+ return (nil, -1);
+ return (r, id);
+ }
+ sys->werrstr("ID not found");
+ return (nil, -1);
+}
+
+PDB.resetnext(db: self ref PDB): int
+{
+ raise "PDB.resetnext not implemented";
+}
+
+PDB.readnextmod(db: self ref PDB): (ref Record, int)
+{
+ raise "PDB.readnextmod not implemented";
+}
+
+PDB.write(db: self ref PDB, r: ref Record): string
+{
+ return "PDB.write not implemented";
+}
+
+PDB.truncate(db: self ref PDB): string
+{
+ return "PDB.truncate not implemented";
+}
+
+PDB.delete(db: self ref PDB, id: int): string
+{
+ return "PDB.delete not implemented";
+}
+
+PDB.deletecat(db: self ref PDB, cat: int): string
+{
+ return "PDB.deletecat not implemented";
+}
+
+PDB.purge(db: self ref PDB): string
+{
+ return "PDB.purge not implemented";
+}
+
+PDB.movecat(db: self ref PDB, old: int, new: int): string
+{
+ return "PDB.movecat not implemented";
+}
+
+PRC.read(db: self ref PRC, index: int): ref Resource
+{
+ return nil;
+}
+
+PRC.readtype(db: self ref PRC, name: int, id: int): (ref Resource, int)
+{
+ return (nil, -1);
+}
+
+PRC.write(db: self ref PRC, r: ref Resource): string
+{
+ return "PRC.write not implemented";
+}
+
+PRC.truncate(db: self ref PRC): string
+{
+ return "PRC.truncate not implemented";
+}
+
+PRC.delete(db: self ref PRC, name: int, id: int): string
+{
+ return "PRC.delete not implemented";
+}
+
+#
+# internal function to extend entry list if necessary, and return a
+# pointer to the next available slot
+#
+entryensure(db: ref DB, i: int): ref Entry
+{
+ ofile := files[db.x];
+ if(i < len ofile.entries)
+ return ofile.entries[i];
+ e := ref Entry(0, -1, 0, 0, 0);
+ n := len ofile.entries;
+ if(n == 0)
+ n = 64;
+ else
+ n = (i+63) & ~63;
+ a := array[n] of ref Entry;
+ a[0:] = ofile.entries;
+ a[i] = e;
+ ofile.entries = a;
+ return e;
+}
+
+writefilehdr(db: ref DB, mode: int, perm: int): string
+{
+ ofile := files[db.x];
+ if(len ofile.entries >= 64*1024)
+ return "too many records for Palm file"; # is there a way to extend it?
+
+ if((f := bufio->create(ofile.fname, mode, perm)) == nil)
+ return sys->sprint("%r");
+
+ ip := ofile.info;
+
+ esize := Datahdrsize;
+ if(ip.attr & Palm->Fresource)
+ esize = Resourcehdrsize;
+ offset := Dbhdrlen + esize*len ofile.entries + 2;
+ offset += 2; # placeholder bytes or gap bytes
+ appinfo := 0;
+ if(len ofile.appinfo > 0){
+ appinfo = offset;
+ offset += len ofile.appinfo;
+ }
+ sortinfo := 0;
+ if(len ofile.sortinfo > 0){
+ sortinfo = offset;
+ offset += 2*len ofile.sortinfo; # 2-byte entries
+ }
+ p := array[Dbhdrlen] of byte; # bigger than any entry as well
+ puts(p[0:32], ip.name);
+ put2(p[32:], ip.attr);
+ put2(p[34:], ip.version);
+ put4(p[36:], epoch2pilot(ip.ctime));
+ put4(p[40:], epoch2pilot(ip.mtime));
+ put4(p[44:], epoch2pilot(ip.btime));
+ put4(p[48:], ip.modno);
+ put4(p[52:], appinfo);
+ put4(p[56:], sortinfo);
+ put4(p[60:], sx(ip.dtype));
+ put4(p[64:], sx(ip.creator));
+ put4(p[68:], ofile.uidseed);
+ put4(p[72:], 0); # next record list ID
+ put2(p[76:], len ofile.entries);
+
+ if(f.write(p, Dbhdrlen) != Dbhdrlen)
+ return ewrite(f);
+ if(len ofile.entries > 0){
+ for(i := 0; i < len ofile.entries; i++) {
+ e := ofile.entries[i];
+ e.offset = offset;
+ if(ip.attr & Palm->Fresource) {
+ put4(p, e.name);
+ put2(p[4:], e.id);
+ put4(p[6:], e.offset);
+ } else {
+ put4(p, e.offset);
+ p[4] = byte e.attr;
+ put3(p[5:], e.id);
+ }
+ if(f.write(p, esize) != esize)
+ return ewrite(f);
+ offset += e.size;
+ }
+ }
+
+ f.putb(byte 0); # placeholder bytes (figure 1.4) or gap bytes (p. 15)
+ f.putb(byte 0);
+
+ if(appinfo != 0){
+ if(f.write(ofile.appinfo, len ofile.appinfo) != len ofile.appinfo)
+ return ewrite(f);
+ }
+
+ if(sortinfo != 0){
+ tmp := array[2*len ofile.sortinfo] of byte;
+ for(i := 0; i < len ofile.sortinfo; i++)
+ put2(tmp[2*i:], ofile.sortinfo[i]);
+ if(f.write(tmp, len tmp) != len tmp)
+ return ewrite(f);
+ }
+
+ if(f.flush() != 0)
+ return ewrite(f);
+
+ return nil;
+}
+
+ewrite(f: ref Iobuf): string
+{
+ e := sys->sprint("write error: %r");
+ f.close();
+ return e;
+}
+
+xs(i: int): string
+{
+ if(i == 0)
+ return "";
+ if(i & int 16r80808080)
+ return sys->sprint("%8.8ux", i);
+ return sys->sprint("%c%c%c%c", (i>>24)&16rFF, (i>>16)&16rFF, (i>>8)&16rFF, i&16rFF);
+}
+
+sx(s: string): int
+{
+ n := 0;
+ for(i := 0; i < 4; i++){
+ c := 0;
+ if(i < len s)
+ c = s[i] & 16rFF;
+ n = (n<<8) | c;
+ }
+ return n;
+}
+
+mkpfile(name: string, mode: int): (ref DB, ref Ofile, int)
+{
+ ofile := ref Ofile(name, nil, mode, DBInfo.new(name, 0, nil, 0, nil),
+ array[0] of byte, array[0] of int, 0, nil);
+ for(x := 0; x < len files; x++)
+ if(files[x] == nil)
+ return (ref DB(x, mode, 0), ofile, x);
+ a := array[x] of ref Ofile;
+ a[0:] = files;
+ files = a;
+ return (ref DB(x, mode, 0), ofile, x);
+}
+
+#
+# because PalmOS treats all times as local times, and doesn't associate
+# them with time zones, we'll convert using local time on Plan 9 and Inferno
+#
+
+pilot2epoch(t: int): int
+{
+ if(t == 0)
+ return 0; # we'll assume it's not set
+ return t - Epochdelta + tzoff;
+}
+
+epoch2pilot(t: int): int
+{
+ if(t == 0)
+ return t;
+ return t - tzoff + Epochdelta;
+}
+
+#
+# map Palm name to string, assuming iso-8859-1,
+# but remap space and /
+#
+latin1(a: array of byte, remap: int): string
+{
+ s := "";
+ for(i := 0; i < len a; i++){
+ c := int a[i];
+ if(c == 0)
+ break;
+ if(remap){
+ if(c == ' ')
+ c = 16r00A0; # unpaddable space
+ else if(c == '/')
+ c = 16r2215; # division /
+ }
+ s[len s] = c;
+ }
+ return s;
+}
diff --git a/appl/lib/palmfile.b b/appl/lib/palmfile.b
new file mode 100644
index 00000000..b59bbd6f
--- /dev/null
+++ b/appl/lib/palmfile.b
@@ -0,0 +1,703 @@
+implement Palmfile;
+
+#
+# Copyright © 2001-2002 Vita Nuova Holdings Limited. All rights reserved.
+#
+# Based on ``Palm® File Format Specification'', Document Number 3008-004, 1 May 2001, by Palm Inc.
+# Doc compression based on description by Paul Lucas, 18 August 1998
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "daytime.m";
+ daytime: Daytime;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "palmfile.m";
+
+
+Dbhdrlen: con 72+6;
+Datahdrsize: con 4+1+3;
+Resourcehdrsize: con 4+2+4;
+
+# Exact value of "Jan 1, 1970 0:00:00 GMT" - "Jan 1, 1904 0:00:00 GMT"
+Epochdelta: con 2082844800;
+tzoff := 0;
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ daytime = load Daytime Daytime->PATH;
+ if(bufio == nil || daytime == nil)
+ return "can't load required module";
+ tzoff = daytime->local(0).tzoff;
+ return nil;
+}
+
+Eshort: con "file format error: too small";
+
+Pfile.open(name: string, mode: int): (ref Pfile, string)
+{
+ if(mode != Sys->OREAD)
+ return (nil, "invalid mode");
+ fd := sys->open(name, mode);
+ if(fd == nil)
+ return (nil, sys->sprint("%r"));
+ pf := mkpfile(name, mode);
+ (ok, d) := sys->fstat(fd);
+ if(ok < 0)
+ return (nil, sys->sprint("%r"));
+ length := int d.length;
+ if(length == 0)
+ return (nil, "empty file");
+
+ f := bufio->fopen(fd, mode); # automatically closed if open fails
+
+ p := array[Dbhdrlen] of byte;
+ if(f.read(p, Dbhdrlen) != Dbhdrlen)
+ return (nil, "invalid file header: too short");
+
+ ip := pf.info;
+ ip.name = gets(p[0:32]);
+ ip.attr = get2(p[32:]);
+ ip.version = get2(p[34:]);
+ ip.ctime = pilot2epoch(get4(p[36:]));
+ ip.mtime = pilot2epoch(get4(p[40:]));
+ ip.btime = pilot2epoch(get4(p[44:]));
+ ip.modno = get4(p[48:]);
+ ip.appinfo = get4(p[52:]);
+ ip.sortinfo = get4(p[56:]);
+ if(ip.appinfo < 0 || ip.sortinfo < 0 || (ip.appinfo|ip.sortinfo)&1)
+ return (nil, "invalid header: bad offset");
+ ip.dtype = xs(get4(p[60:]));
+ ip.creator = xs(get4(p[64:]));
+ pf.uidseed = ip.uidseed = get4(p[68:]);
+
+ if(get4(p[72:]) != 0)
+ return (nil, "chained headers not supported"); # Palm says to reject such files
+ nrec := get2(p[76:]);
+ if(nrec < 0)
+ return (nil, sys->sprint("invalid header: bad record count: %d", nrec));
+
+ esize := Datahdrsize;
+ if(ip.attr & Fresource)
+ esize = Resourcehdrsize;
+
+ dataoffset := length;
+ pf.entries = array[nrec] of ref Entry;
+ if(nrec > 0){
+ laste: ref Entry;
+ buf := array[esize] of byte;
+ for(i := 0; i < nrec; i++){
+ if(f.read(buf, len buf) != len buf)
+ return (nil, Eshort);
+ e := ref Entry;
+ if(ip.attr & Fresource){
+ # resource entry: type[4], id[2], offset[4]
+ e.name = get4(buf);
+ e.id = get2(buf[4:]);
+ e.offset = get4(buf[6:]);
+ e.attr = 0;
+ }else{
+ # record entry: offset[4], attr[1], id[3]
+ e.offset = get4(buf);
+ e.attr = int buf[4];
+ e.id = get3(buf[5:]);
+ e.name = 0;
+ }
+ if(laste != nil)
+ laste.size = e.offset - laste.offset;
+ laste = e;
+ pf.entries[i] = e;
+ }
+ if(laste != nil)
+ laste.size = length - laste.offset;
+ dataoffset = pf.entries[0].offset;
+ }else{
+ if(f.read(p, 2) != 2)
+ return (nil, Eshort); # discard placeholder bytes
+ }
+
+ n := 0;
+ if(ip.appinfo > 0){
+ n = ip.appinfo - int f.offset();
+ while(--n >= 0)
+ f.getb();
+ if(ip.sortinfo)
+ n = ip.sortinfo - ip.appinfo;
+ else
+ n = dataoffset - ip.appinfo;
+ pf.appinfo = array[n] of byte;
+ if(f.read(pf.appinfo, n) != n)
+ return (nil, Eshort);
+ }
+ if(ip.sortinfo > 0){
+ n = ip.sortinfo - int f.offset();
+ while(--n >= 0)
+ f.getb();
+ n = (dataoffset-ip.sortinfo)/2;
+ pf.sortinfo = array[n] of int;
+ tmp := array[2*n] of byte;
+ if(f.read(tmp, len tmp) != len tmp)
+ return (nil, Eshort);
+ for(i := 0; i < n; i++)
+ pf.sortinfo[i] = get2(tmp[2*i:]);
+ }
+ pf.f = f; # safe to save open file reference
+ return (pf, nil);
+}
+
+Pfile.close(pf: self ref Pfile): int
+{
+ if(pf.f != nil){
+ pf.f.close();
+ pf.f = nil;
+ }
+ return 0;
+}
+
+Pfile.stat(pf: self ref Pfile): ref DBInfo
+{
+ return ref *pf.info;
+}
+
+Pfile.read(pf: self ref Pfile, i: int): (ref Record, string)
+{
+ if(i < 0 || i >= len pf.entries){
+ if(i == len pf.entries)
+ return (nil, nil); # treat as end-of-file
+ return (nil, "index out of range");
+ }
+ e := pf.entries[i];
+ r := ref Record;
+ r.index = i;
+ nb := e.size;
+ r.data = array[nb] of byte;
+ pf.f.seek(big e.offset, 0);
+ if(pf.f.read(r.data, nb) != nb)
+ return (nil, sys->sprint("%r"));
+ r.cat = e.attr & 16r0F;
+ r.attr = e.attr & 16rF0;
+ r.id = e.id;
+ r.name = e.name;
+ return (r, nil);
+}
+
+#Pfile.create(name: string, info: ref DBInfo): ref Pfile
+#{
+#}
+
+#Pfile.wstat(pf: self ref Pfile, ip: ref DBInfo): string
+#{
+# if(pf.mode != Sys->OWRITE)
+# return "not open for writing";
+# if((ip.attr & Fresource) != (pf.info.attr & Fresource))
+# return "cannot change file type";
+# # copy only a subset
+# pf.info.name = ip.name;
+# pf.info.attr = ip.attr;
+# pf.info.version = ip.version;
+# pf.info.ctime = ip.ctime;
+# pf.info.mtime = ip.mtime;
+# pf.info.btime = ip.btime;
+# pf.info.modno = ip.modno;
+# pf.info.dtype = ip.dtype;
+# pf.info.creator = ip.creator;
+# return nil;
+#}
+
+#Pfile.setappinfo(pf: self ref Pfile, data: array of byte): string
+#{
+# if(pf.mode != Sys->OWRITE)
+# return "not open for writing";
+# pf.appinfo = array[len data] of byte;
+# pf.appinfo[0:] = data;
+#}
+
+#Pfile.setsortinfo(pf: self ref Pfile, sort: array of int): string
+#{
+# if(pf.mode != Sys->OWRITE)
+# return "not open for writing";
+# pf.sortinfo = array[len sort] of int;
+# pf.sortinfo[0:] = sort;
+#}
+
+#
+# internal function to extend entry list if necessary, and return a
+# pointer to the next available slot
+#
+entryensure(pf: ref Pfile, i: int): ref Entry
+{
+ if(i < len pf.entries)
+ return pf.entries[i];
+ e := ref Entry(0, -1, 0, 0, 0);
+ n := len pf.entries;
+ if(n == 0)
+ n = 64;
+ else
+ n = (i+63) & ~63;
+ a := array[n] of ref Entry;
+ a[0:] = pf.entries;
+ a[i] = e;
+ pf.entries = a;
+ return e;
+}
+
+writefilehdr(pf: ref Pfile, mode: int, perm: int): string
+{
+ if(len pf.entries >= 64*1024)
+ return "too many records for Palm file"; # is there a way to extend it?
+
+ if((f := bufio->create(pf.fname, mode, perm)) == nil)
+ return sys->sprint("%r");
+
+ ip := pf.info;
+
+ esize := Datahdrsize;
+ if(ip.attr & Fresource)
+ esize = Resourcehdrsize;
+ offset := Dbhdrlen + esize*len pf.entries + 2;
+ offset += 2; # placeholder bytes or gap bytes
+ ip.appinfo = 0;
+ if(len pf.appinfo > 0){
+ ip.appinfo = offset;
+ offset += len pf.appinfo;
+ }
+ ip.sortinfo = 0;
+ if(len pf.sortinfo > 0){
+ ip.sortinfo = offset;
+ offset += 2*len pf.sortinfo; # 2-byte entries
+ }
+ p := array[Dbhdrlen] of byte; # bigger than any entry as well
+ puts(p[0:32], ip.name);
+ put2(p[32:], ip.attr);
+ put2(p[34:], ip.version);
+ put4(p[36:], epoch2pilot(ip.ctime));
+ put4(p[40:], epoch2pilot(ip.mtime));
+ put4(p[44:], epoch2pilot(ip.btime));
+ put4(p[48:], ip.modno);
+ put4(p[52:], ip.appinfo);
+ put4(p[56:], ip.sortinfo);
+ put4(p[60:], sx(ip.dtype));
+ put4(p[64:], sx(ip.creator));
+ put4(p[68:], pf.uidseed);
+ put4(p[72:], 0); # next record list ID
+ put2(p[76:], len pf.entries);
+
+ if(f.write(p, Dbhdrlen) != Dbhdrlen)
+ return ewrite(f);
+ if(len pf.entries > 0){
+ for(i := 0; i < len pf.entries; i++) {
+ e := pf.entries[i];
+ e.offset = offset;
+ if(ip.attr & Fresource) {
+ put4(p, e.name);
+ put2(p[4:], e.id);
+ put4(p[6:], e.offset);
+ } else {
+ put4(p, e.offset);
+ p[4] = byte e.attr;
+ put3(p[5:], e.id);
+ }
+ if(f.write(p, esize) != esize)
+ return ewrite(f);
+ offset += e.size;
+ }
+ }
+
+ f.putb(byte 0); # placeholder bytes (figure 1.4) or gap bytes (p. 15)
+ f.putb(byte 0);
+
+ if(ip.appinfo != 0){
+ if(f.write(pf.appinfo, len pf.appinfo) != len pf.appinfo)
+ return ewrite(f);
+ }
+
+ if(ip.sortinfo != 0){
+ tmp := array[2*len pf.sortinfo] of byte;
+ for(i := 0; i < len pf.sortinfo; i++)
+ put2(tmp[2*i:], pf.sortinfo[i]);
+ if(f.write(tmp, len tmp) != len tmp)
+ return ewrite(f);
+ }
+
+ if(f.flush() != 0)
+ return ewrite(f);
+
+ return nil;
+}
+
+ewrite(f: ref Iobuf): string
+{
+ e := sys->sprint("write error: %r");
+ f.close();
+ return e;
+}
+
+Doc.open(file: ref Pfile): (ref Doc, string)
+{
+ if(file.info.dtype != "TEXt" || file.info.creator != "REAd")
+ return (nil, "not a Doc file: wrong type or creator");
+ (r, err) := file.read(0);
+ if(r == nil){
+ if(err == nil)
+ err = "no directory record";
+ return (nil, sys->sprint("not a valid Doc file: %s", err));
+ }
+ a := r.data;
+ if(len a < 16)
+ return (nil, sys->sprint("not a valid Doc file: bad length: %d", len a));
+ maxrec := len file.entries-1;
+ d := ref Doc;
+ d.file = file;
+ d.version = get2(a);
+ if(d.version != 1 && d.version != 2)
+ err = "unknown Docfile version";
+ # a[2:] is spare
+ d.length = get4(a[4:]);
+ d.nrec = get2(a[8:]);
+ if(maxrec >= 0 && d.nrec > maxrec){
+ d.nrec = maxrec;
+ err = "invalid record count";
+ }
+ d.recsize = get2(a[10:]);
+ d.position = get4(a[12:]);
+ return (d, sys->sprint("unexpected Doc file format: %s", err));
+}
+
+Doc.iscompressed(d: self ref Doc): int
+{
+ return (d.version&7) == 2; # high-order bits are sometimes used, ignore them
+}
+
+Doc.read(doc: self ref Doc, index: int): (string, string)
+{
+ (r, err) := doc.file.read(index+1);
+ if(r == nil)
+ return (nil, err);
+ (s, serr) := doc.unpacktext(r.data);
+ if(s == nil)
+ return (nil, serr);
+ return (s, nil);
+}
+
+Doc.unpacktext(doc: self ref Doc, a: array of byte): (string, string)
+{
+ nb := len a;
+ s: string;
+ if(!doc.iscompressed()){
+ for(i := 0; i < nb; i++)
+ s[len s] = int a[i]; # assumes Latin-1
+ return (s, nil);
+ }
+ o := 0;
+ for(i := 0; i < nb;){
+ c := int a[i++];
+ if(c >= 9 && c <= 16r7F || c == 0)
+ s[o++] = c;
+ else if(c >= 1 && c <= 8){
+ if(i+c > nb)
+ return (nil, "missing data in record");
+ while(--c >= 0)
+ s[o++] = int a[i++];
+ }else if(c >= 16rC0 && c <= 16rFF){
+ s[o] = ' ';
+ s[o+1] = c & 16r7F;
+ o += 2;
+ }else{ # c >= 0x80 && c <= 16rBF
+ v := int a[i++];
+ m := ((c & 16r3F)<<5)|(v>>3);
+ n := (v&7) + 3;
+ if(m == 0 || m > o)
+ return (nil, sys->sprint("data is corrupt: m=%d n=%d o=%d", m, n, o));
+ for(; --n >= 0; o++)
+ s[o] = s[o-m];
+ }
+ }
+ return (s, nil);
+}
+
+Doc.textlength(doc: self ref Doc, a: array of byte): int
+{
+ nb := len a;
+ if(!doc.iscompressed())
+ return nb;
+ o := 0;
+ for(i := 0; i < nb;){
+ c := int a[i++];
+ if(c >= 9 && c <= 16r7F || c == 0)
+ o++;
+ else if(c >= 1 && c <= 8){
+ if(i+c > nb)
+ return -1;
+ o += c;
+ i += c;
+ }else if(c >= 16rC0 && c <= 16rFF){
+ o += 2;
+ }else{ # c >= 0x80 && c <= 16rBF
+ v := int a[i++];
+ m := ((c & 16r3F)<<5)|(v>>3);
+ n := (v&7) + 3;
+ if(m == 0 || m > o)
+ return -1;
+ o += n;
+ }
+ }
+ return o;
+}
+
+xs(i: int): string
+{
+ if(i == 0)
+ return "";
+ if(i & int 16r80808080)
+ return sys->sprint("%8.8ux", i);
+ return sys->sprint("%c%c%c%c", (i>>24)&16rFF, (i>>16)&16rFF, (i>>8)&16rFF, i&16rFF);
+}
+
+sx(s: string): int
+{
+ n := 0;
+ for(i := 0; i < 4; i++){
+ c := 0;
+ if(i < len s)
+ c = s[i] & 16rFF;
+ n = (n<<8) | c;
+ }
+ return n;
+}
+
+mkpfile(name: string, mode: int): ref Pfile
+{
+ pf := ref Pfile;
+ pf.mode = mode;
+ pf.fname = name;
+ pf.appinfo = array[0] of byte; # making it non-nil saves having to check each access
+ pf.sortinfo = array[0] of int;
+ pf.uidseed = 0;
+ pf.info = DBInfo.new(name, 0, nil, 0, nil);
+ return pf;
+}
+
+DBInfo.new(name: string, attr: int, dtype: string, version: int, creator: string): ref DBInfo
+{
+ info := ref DBInfo;
+ info.name = name;
+ info.attr = attr;
+ info.version = version;
+ info.ctime = daytime->now();
+ info.mtime = daytime->now();
+ info.btime = 0;
+ info.modno = 0;
+ info.appinfo = 0;
+ info.sortinfo = 0;
+ info.dtype = dtype;
+ info.creator = creator;
+ info.uidseed = 0;
+ info.index = 0;
+ info.more = 0;
+ return info;
+}
+
+Categories.new(labels: array of string): ref Categories
+{
+ c := ref Categories;
+ c.renamed = 0;
+ c.lastuid = 0;
+ c.labels = array[16] of string;
+ c.uids = array[] of {0 to 15 => 0};
+ for(i := 0; i < len labels && i < 16; i++){
+ c.labels[i] = labels[i];
+ c.lastuid = 16r80 + i;
+ c.uids[i] = c.lastuid;
+ }
+ return c;
+}
+
+Categories.unpack(a: array of byte): ref Categories
+{
+ if(len a < 16r114)
+ return nil; # doesn't match the structure
+ c := ref Categories;
+ c.renamed = get2(a);
+ c.labels = array[16] of string;
+ c.uids = array[16] of int;
+ j := 2;
+ for(i := 0; i < 16; i++){
+ c.labels[i] = latin1(a[j:j+16], 0);
+ j += 16;
+ c.uids[i] = int a[16r102+i];
+ }
+ c.lastuid = int a[16r112];
+ # one byte of padding is shown on p. 26, but
+ # two more are invariably used in practice
+ # before application specific data.
+ if(len a > 16r116)
+ c.appdata = a[16r116:];
+ return c;
+}
+
+Categories.pack(c: self ref Categories): array of byte
+{
+ a := array[16r116 + len c.appdata] of byte;
+ put2(a, c.renamed);
+ j := 2;
+ for(i := 0; i < 16; i++){
+ puts(a[j:j+16], c.labels[i]);
+ j += 16;
+ a[16r102+i] = byte c.uids[i];
+ }
+ a[16r112] = byte c.lastuid;
+ a[16r113] = byte 0; # pad shown on p. 26
+ a[16r114] = byte 0; # extra two bytes of padding used in practice
+ a[16r115] = byte 0;
+ if(c.appdata != nil)
+ a[16r116:] = c.appdata;
+ return a;
+}
+
+Categories.mkidmap(c: self ref Categories): array of int
+{
+ a := array[256] of {* => 0};
+ for(i := 0; i < len c.uids; i++)
+ a[c.uids[i]] = i;
+ return a;
+}
+
+#
+# because PalmOS treats all times as local times, and doesn't associate
+# them with time zones, we'll convert using local time on Plan 9 and Inferno
+#
+
+pilot2epoch(t: int): int
+{
+ if(t == 0)
+ return 0; # we'll assume it's not set
+ return t - Epochdelta + tzoff;
+}
+
+epoch2pilot(t: int): int
+{
+ if(t == 0)
+ return t;
+ return t - tzoff + Epochdelta;
+}
+
+#
+# map Palm name to string, assuming iso-8859-1,
+# but remap space and /
+#
+latin1(a: array of byte, remap: int): string
+{
+ s := "";
+ for(i := 0; i < len a; i++){
+ c := int a[i];
+ if(c == 0)
+ break;
+ if(remap){
+ if(c == ' ')
+ c = 16r00A0; # unpaddable space
+ else if(c == '/')
+ c = 16r2215; # division /
+ }
+ s[len s] = c;
+ }
+ return s;
+}
+
+#
+# map from Unicode to Palm name
+#
+filename(name: string): string
+{
+ s := "";
+ for(i := 0; i < len name; i++){
+ c := name[i];
+ if(c == ' ')
+ c = 16r00A0; # unpaddable space
+ else if(c == '/')
+ c = 16r2215; # division solidus
+ s[len s] = c;
+ }
+ return s;
+}
+
+dbname(name: string): string
+{
+ s := "";
+ for(i := 0; i < len name; i++){
+ c := name[i];
+ case c {
+ 0 => c = ' '; # unlikely, but just in case
+ 16r2215 => c = '/';
+ 16r00A0 => c = ' ';
+ }
+ s[len s] = c;
+ }
+ return s;
+}
+
+#
+# string conversion: can't use (string a) because
+# the bytes are Latin1, not Unicode
+#
+gets(a: array of byte): string
+{
+ s := "";
+ for(i := 0; i < len a; i++)
+ s[len s] = int a[i];
+ return s;
+}
+
+puts(a: array of byte, s: string)
+{
+ for(i := 0; i < len a-1 && i < len s; i++)
+ a[i] = byte s[i];
+ for(; i < len a; i++)
+ a[i] = byte 0;
+}
+
+#
+# big-endian packing
+#
+
+get4(p: array of byte): int
+{
+ return (((((int p[0] << 8) | int p[1]) << 8) | int p[2]) << 8) | int p[3];
+}
+
+get3(p: array of byte): int
+{
+ return (((int p[0] << 8) | int p[1]) << 8) | int p[2];
+}
+
+get2(p: array of byte): int
+{
+ return (int p[0]<<8) | int p[1];
+}
+
+put4(p: array of byte, v: int)
+{
+ p[0] = byte (v>>24);
+ p[1] = byte (v>>16);
+ p[2] = byte (v>>8);
+ p[3] = byte (v & 16rFF);
+}
+
+put3(p: array of byte, v: int)
+{
+ p[0] = byte (v>>16);
+ p[1] = byte (v>>8);
+ p[2] = byte (v & 16rFF);
+}
+
+put2(p: array of byte, v: int)
+{
+ p[0] = byte (v>>8);
+ p[1] = byte (v & 16rFF);
+}
diff --git a/appl/lib/parseman.b b/appl/lib/parseman.b
new file mode 100644
index 00000000..3b682587
--- /dev/null
+++ b/appl/lib/parseman.b
@@ -0,0 +1,805 @@
+implement Parseman;
+
+include "sys.m";
+include "bufio.m";
+include "man.m";
+
+sys: Sys;
+bufio: Bufio;
+Iobuf: import bufio;
+
+FONT_LITERAL: con -1;
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ if (bufio == nil)
+ return sys->sprint("cannot load module: %r");
+ return nil;
+}
+
+ParseState: adt[T]
+ for{
+ T =>
+ textwidth: fn(t: self T, text: Text): int;
+ }{
+ metrics: Metrics;
+ ql: int; # quote Literal text
+ margin: int;
+ mstack: list of int;
+ istack: list of int;
+ indent: int;
+ ntlsetindent: int; #copy prevailindent to indent on n.t.l
+ prevailindent: int;
+ curfont: int;
+ curattr: int;
+ verbatim: int;
+ pspace: int;
+ curline: list of (int, Text); # most recent first
+ curwidth: int;
+ newpara: int;
+ heading: int;
+ igto: string;
+ link: string;
+ viewer: T;
+ setline: chan of list of (int, Text);
+
+ # addstring() is simply an addtext() of the current font
+ addstring: fn(s: self ref ParseState, s: string);
+ addtext: fn(s: self ref ParseState, t: list of Text);
+ brk: fn(s: self ref ParseState);
+ paragraph: fn( s: self ref ParseState);
+};
+
+parseman[T](fd: ref Sys->FD, metrics: Metrics, ql: int, viewer: T, setline: chan of list of (int, Text))
+ for{
+ T =>
+ textwidth: fn(t: self T, text: Text): int;
+ }
+{
+ iob := bufio->fopen(fd, Sys->OREAD);
+ state := ref ParseState[T](metrics, ql, 0, nil, nil, 0, 0, metrics.indent, FONT_ROMAN, 0, 0, 1, nil, 0, 1, 0, "", nil, viewer, setline);
+ while ((l := iob.gets('\n')) != nil) {
+ if (l[len l -1] == '\n')
+ l = l[0: len l - 1];
+ if (state.igto != nil && state.igto != l)
+ continue;
+ state.igto = nil;
+ parseline(state, l);
+ }
+ state.pspace = 2;
+ state.pspace = 1;
+ state.paragraph();
+ footer := Text(FONT_ROMAN, 0, "Inferno Manual", 0, nil);
+ textw := state.viewer.textwidth(footer);
+#should do 'center' in addtext (state.justify = CENTER)
+ state.indent = (state.metrics.pagew - textw) / 2;
+ state.addtext(footer::nil);
+ state.brk();
+ setline <- = nil;
+}
+
+parseline[T](state: ref ParseState[T], t: string)
+ for{
+ T =>
+ textwidth: fn(t: self T, text: Text): int;
+ }
+{
+ if (t == nil) {
+ if (state.verbatim) {
+ blank := Text(state.curfont, state.curattr, "", 0, "");
+ state.setline <- = (0, blank)::nil;
+ } else
+ state.paragraph();
+ return;
+ }
+ ntlsetindent := state.ntlsetindent;
+ state.ntlsetindent = 0;
+ if (t[0] == '.' || t[0] == '\'')
+ parsemacro(state, t[1:]);
+ else {
+ state.addtext(parsetext(state, t));
+ if (state.verbatim)
+ state.brk();
+ }
+ if (ntlsetindent) {
+ state.indent = state.prevailindent;
+ if (state.curwidth + state.metrics.en > state.indent + state.margin)
+ state.brk();
+ }
+}
+
+parsemacro[T](state: ref ParseState[T], t: string)
+ for{
+ T =>
+ textwidth: fn(t: self T, text: Text): int;
+ }
+{
+ for (n := 0; n < len t && n < 2; n++)
+ if (t[n] == ' ' || t[n] == '\t')
+ break;
+ macro := t[0:n];
+ params: list of string;
+ quote := 0;
+ param := 0;
+ esc := 0;
+ p := "";
+ for (; n < len t; n++) {
+ if (esc)
+ esc = 0;
+ else {
+ case t[n] {
+ ' ' or '\t' =>
+ if (!quote) {
+ if (param) {
+ params = p :: params;
+ p = "";
+ param = 0;
+ }
+ continue;
+ }
+ '"' =>
+ param = 1;
+ quote = !quote;
+ continue;
+ '\\' =>
+ esc = 1;
+ }
+ }
+ param = 1;
+ p[len p] = t[n];
+ }
+ if (param)
+ params = p :: params;
+ plist: list of string;
+ for (; params != nil; params = tl params)
+ plist = hd params :: plist;
+ params = plist;
+
+ case macro {
+ "ig" =>
+ igto := "..";
+ if (params != nil)
+ igto = "." + hd params;
+ state.brk();
+ state.igto = igto;
+ "sp" =>
+ sp := "1";
+ if(params != nil)
+ sp = hd params;
+ d := tval(state.metrics, sp, 'v');
+ gap := d / state.metrics.V;
+ if (gap < 1)
+ gap = 1;
+ while (gap--)
+ state.paragraph();
+ "br" =>
+ state.brk();
+ "nf" =>
+ state.verbatim = 1;
+ "fi" =>
+ state.verbatim = 0;
+ "ti" =>
+ state.brk();
+ #i := 0;
+ #if(params != nil)
+ # i = tval(state.metrics, hd params, 'n');
+ #state.ntlsetindent = 1;
+ #state.prevailindent = i;
+ "in" =>
+ state.brk();
+ #i := 0;
+ #if(params != nil)
+ # i = tval(state.metrics, hd params, 'n');
+ #state.indent = i;
+ #state.prevailindent = state.indent;
+ "1C" =>
+ state.brk();
+ # not implemented
+ "2C" =>
+ state.brk();
+ # not implemented
+ "BI" =>
+ altattr(state, FONT_BOLD, FONT_ITALIC, params);
+ "BR" =>
+ altattr(state, FONT_BOLD, FONT_ROMAN, params);
+ "IB" =>
+ altattr(state, FONT_ITALIC, FONT_BOLD, params);
+ "IR" =>
+ # need to determine link if params of valid form
+ state.link = convlink(params);;
+ altattr(state, FONT_ITALIC, FONT_ROMAN, params);
+ state.link = nil;
+ "RB" =>
+ altattr(state, FONT_ROMAN, FONT_BOLD, params);
+ "RI" =>
+ altattr(state, FONT_ROMAN, FONT_ITALIC, params);
+ "B" =>
+ state.curfont = FONT_BOLD;
+ if (params != nil) {
+ for (; params != nil; params = tl params) {
+ textl := parsetext(state, hd params);
+ for (; textl != nil; textl = tl textl)
+ state.addtext(hd textl::nil);
+ }
+ state.curfont = FONT_ROMAN;
+ }
+ "I" =>
+ state.curfont = FONT_ITALIC;
+ if (params != nil) {
+ for (; params != nil; params = tl params) {
+ textl := parsetext(state, hd params);
+ for (; textl != nil; textl = tl textl)
+ state.addtext(hd textl::nil);
+ }
+ state.curfont = FONT_ROMAN;
+ }
+ "SM"=>
+ state.curattr |= ATTR_SMALL;
+ if (params != nil) {
+ for (; params != nil; params = tl params)
+ state.addstring(hd params);
+ state.curattr &= ~ATTR_SMALL;
+ }
+ "L" =>
+ state.curfont = FONT_LITERAL;
+ if (params != nil) {
+ str := "`";
+ for (pl := params; pl != nil;) {
+ str += hd pl;
+ if ((pl = tl pl) != nil)
+ str += " ";
+ else
+ break;
+ }
+ str += "'";
+ state.addstring(str);
+ state.curfont = FONT_ROMAN;
+ }
+ "LR" =>
+ if (params != nil) {
+ l := Text(FONT_LITERAL, state.curattr, hd params, 0, nil);
+ t: list of Text;
+ params = tl params;
+ if (params == nil)
+ t = l :: nil;
+ else {
+ r := Text(FONT_ROMAN, state.curattr, hd params, 0, nil);
+ t = l :: r :: nil;
+ }
+ state.addtext(t);
+ }
+ "RL" =>
+ if (params != nil) {
+ r := Text(FONT_ROMAN, state.curattr, hd params, 0, nil);
+ t: list of Text;
+ params = tl params;
+ if (params == nil)
+ t = r :: nil;
+ else {
+ l := Text(FONT_LITERAL, state.curattr, hd params, 0, nil);
+ t = r :: l :: nil;
+ }
+ state.addtext(t);
+ }
+ "DT" =>
+ # not yet supported
+ ;
+ "EE" =>
+ state.brk();
+ state.verbatim = 0;
+ state.curfont = FONT_ROMAN;
+ "EX" =>
+ state.brk();
+ state.verbatim = 1;
+ state.curfont = FONT_BOLD;
+ "HP" =>
+ state.paragraph();
+ i := state.metrics.indent;
+ if (params != nil)
+ i = tval(state.metrics, hd params, 'n');
+ state.prevailindent = state.indent + i;
+ "IP" =>
+ state.paragraph();
+ i := state.metrics.indent;
+ if (params != nil) {
+ tag := hd params;
+ params = tl params;
+ state.addtext(parsetext(state, tag));
+ if (params != nil)
+ i = tval(state.metrics, hd params, 'n');
+ }
+ state.indent = state.metrics.indent + i;
+ state.prevailindent = state.indent;
+ "PD" =>
+ state.pspace = 1;
+ if (params != nil) {
+ v := tval(state.metrics, hd params, 'v') / state.metrics.V;
+ state.pspace = v;
+ }
+ "LP" or "PP" =>
+ state.paragraph();
+ state.prevailindent = state.indent;
+ "RE" =>
+ state.brk();
+ if (state.mstack == nil || state.istack == nil)
+ break;
+
+ state.margin = hd state.mstack;
+ state.mstack = tl state.mstack;
+ state.prevailindent = hd state.istack;
+ state.indent = state.prevailindent;
+ state.istack = tl state.istack;
+ "RS" =>
+ state.brk();
+ i := state.prevailindent - state.metrics.indent;
+ if (params != nil)
+ i = tval(state.metrics, hd params, 'n');
+ state.mstack = state.margin :: state.mstack;
+ state.istack = state.prevailindent :: state.istack;
+ state.margin += i;
+ state.indent = 2 * state.metrics.indent;
+ state.prevailindent = state.indent;
+ "SH" =>
+ state.paragraph();
+ state.prevailindent = state.indent;
+ state.curfont = FONT_ROMAN;
+ state.curattr = 0;
+ state.indent = 0;
+ state.heading = 1;
+ state.verbatim = 0;
+
+ for (pl := params; pl != nil; pl = tl pl)
+ state.addstring(hd pl);
+
+ state.heading = 0;
+ state.brk();
+ state.newpara = 1;
+ state.pspace = 1;
+ "SS" =>
+ state.paragraph();
+ state.prevailindent = state.indent;
+ state.curfont = FONT_ROMAN;
+ state.curattr = 0;
+ state.indent = state.metrics.ssindent;
+ state.heading = 2;
+
+ for (pl := params; pl != nil; pl = tl pl)
+ state.addstring(hd pl);
+
+ state.heading = 0;
+ state.brk();
+ state.newpara = 1;
+ state.pspace = 1;
+
+ "TF" =>
+ state.brk();
+ state.pspace = 0;
+ i := state.metrics.indent;
+ if (params != nil) {
+ str := hd params;
+ text := Text(FONT_BOLD, 0, str, 0, nil);
+ w := state.viewer.textwidth(text) + 2*state.metrics.em;
+ if (w > i)
+ i = w;
+ }
+ state.indent = state.metrics.indent;;
+ state.prevailindent = state.indent + i;
+ "TH" =>
+ state.brk();
+ if (len params < 2)
+ break;
+ str := hd params + "(" + hd tl params + ")";
+ txt := Text(FONT_ROMAN, 0, str, 0, nil);
+ txtw := state.viewer.textwidth(txt);
+ state.indent = 0;
+ state.addtext(txt::nil);
+ state.indent = state.metrics.pagew - txtw;
+ state.addtext(txt::nil);
+ state.indent = 0;
+ state.brk();
+ "TP" =>
+ state.paragraph();
+ if (state.prevailindent == state.metrics.indent)
+ state.prevailindent += state.metrics.indent;
+ state.indent = state.metrics.indent;
+ state.ntlsetindent = 1;
+ if (params != nil) {
+ i := tval(state.metrics, hd params, 'n');
+ if (i == 0)
+ i = state.metrics.indent;
+ state.prevailindent = state.indent + i;
+ }
+ * =>
+ ;
+ }
+ if (state.verbatim)
+ state.brk();
+}
+
+parsetext[T](state: ref ParseState[T], t: string): list of Text
+ for{
+ T =>
+ textwidth: fn(t: self T, text: Text): int;
+ }
+{
+ # need to do better here - spot inline font changes etc
+ # we also currently cannot support troff tab stops
+ textl: list of Text;
+ line := "";
+ curfont := state.curfont;
+ prevfont := state.curfont; # should perhaps be in State
+ step := 1;
+ for (i := 0; i < len t; i += step) {
+ step = 1;
+ ch := t[i];
+ if (ch == '\\') {
+ i++;
+ width := len t - i;
+ if (width <= 0)
+ break;
+ case t[i] {
+ '-' or '.' or '\\' =>
+ ch = t[i];
+ ' ' =>
+ ch = ' ';
+ 'e' =>
+ ch = '\\';
+ '|' or '&' =>
+ continue;
+ '(' =>
+ if (width > 3)
+ width = 3;
+ step = width;
+ if (step != 3)
+ continue;
+ case t[i+1:i+3] {
+ "bu" =>
+ ch = '•';
+ "em" =>
+ ch = '—';
+ "mi" =>
+ ch = '-';
+ "mu" =>
+ ch = '×';
+ "*m" =>
+ ch = 'µ';
+ "*G" =>
+ ch = 'Γ';
+ "*p" =>
+ ch = 'π';
+ "*b" =>
+ ch = 'β';
+ "<=" =>
+ ch = '≤';
+ "->" =>
+ ch = '→';
+ * =>
+ continue;
+ }
+
+ 'f' =>
+ if (width == 1)
+ continue;
+ if (t[i+1] == '(') {
+ if (width > 4)
+ width = 4;
+ step = width;
+ continue;
+ }
+ i++;
+ case t[i] {
+ '0' or 'R' =>
+ curfont = FONT_ROMAN;
+ '1' or 'I' =>
+ curfont = FONT_ITALIC;
+ '2' =>
+ # should be bold but our 'bold' font is constant width
+ curfont = FONT_ROMAN;
+ '5' or 'L' =>
+ curfont = FONT_BOLD;
+ 'P' =>
+ curfont = prevfont;
+ }
+ continue;
+ '*' =>
+ if (width == 1)
+ continue;
+ case t[i+1] {
+ 'R' =>
+ step = 2;
+ ch = '®';
+ '(' =>
+ if (width > 4)
+ width = 4;
+ step = width;
+ continue;
+ }
+ * =>
+ i--;
+ }
+ }
+ if (curfont != state.curfont) {
+ if (line != "") {
+ txt := Text(state.curfont, state.curattr, line, state.heading, state.link);
+ line = "";
+ textl = txt :: textl;
+ }
+ prevfont = state.curfont;
+ state.curfont = curfont;
+ }
+ line[len line] = ch;
+ }
+ if (line != "") {
+ txt := Text(state.curfont, state.curattr, line, state.heading, state.link);
+ textl = txt :: textl;
+ }
+ state.curfont = curfont;
+
+ r: list of Text;
+ for (; textl != nil; textl = tl textl)
+ r = hd textl :: r;
+ return r;
+}
+
+ParseState[T].addstring(state: self ref ParseState[T], s: string)
+{
+ t := Text(state.curfont, state.curattr, s, state.heading, state.link);
+ state.addtext(t::nil);
+}
+
+ParseState[T].addtext(state: self ref ParseState[T], t: list of Text)
+{
+#dumptextlist(t);
+ # on setting a line copy state.prevailindent to state.indent
+ #
+ # always make sure that current indent is achieved
+ #
+ # if FONT_LITERAL and state.ql then convert to FONT_BOLD and
+ # quote the text before any other processing
+
+ state.newpara = 0;
+ addspace := 1;
+ while (t != nil) {
+ # this scheme is inadequate...
+ # results in mixed formatting at end of line getting split up
+ # e.g.
+ # .IR man (1)
+ # can get split at the '('
+
+ indent := 0;
+ spacew := 0;
+ text := hd t;
+ t = tl t;
+ if (state.indent + state.margin > state.curwidth || state.curline == nil) {
+ indent = state.indent + state.margin;
+ state.curwidth = indent;
+ addspace = 0;
+ if (!state.verbatim) {
+ text.text = trim(text.text);
+ while (text.text == "" && t != nil) {
+ text = hd t;
+ t = tl t;
+ text.text = trim(text.text);
+ }
+ }
+ }
+
+ if (text.font == FONT_LITERAL) {
+ if (state.ql)
+ text.text = "`" + text.text + "'";
+ text.font = FONT_BOLD;
+ }
+ if (addspace) {
+ (nil, previtem) := hd state.curline;
+ if (previtem.text[len previtem.text -1] == ' ')
+ addspace = 0;
+ else {
+ space := Text(previtem.font, previtem.attr, " ", 0, nil);
+ spacew = state.viewer.textwidth(space);
+ }
+ }
+ # it doesn't fit - try to word wrap...
+ t2 := text;
+ end := len text.text;
+ prevend := end;
+ nextstart := 0;
+ while (end > 0) {
+ t2.text = text.text[0:end];
+ tlen := state.viewer.textwidth(t2);
+ if (state.verbatim || state.curwidth + spacew + tlen <= state.metrics.pagew) {
+ # easy - just add it!
+ state.curwidth += spacew+tlen;
+ if (addspace) {
+ t2.text = " " + t2.text;
+ addspace = 0;
+ }
+ state.curline = (indent, t2) :: state.curline;
+ indent = 0;
+ break;
+ }
+ prevend = end;
+ for (; end > 0; end--) {
+ if (t2.text[end-1] == ' ') {
+ nextstart = end;
+ for (; end >0 && t2.text[end-1] == ' '; end--)
+ ;
+ break;
+ }
+ }
+ }
+ if (end != len text.text) {
+ # couldn't fit whole item onto line
+ if (state.curline == nil) {
+ # couldn't fit (sub)item on empty line - add it anyway
+ # as there is nowhere else to put it
+ end = prevend;
+ t2.text = text.text[0:end];
+ state.curline = (indent, t2) :: state.curline;
+ if (nextstart != 0) {
+ text.text = text.text[nextstart:];
+ t = text :: t;
+ }
+ } else {
+ # already stuff on line and we have consumed upto nexstart of
+ # the current item
+ if (end != 0)
+ text.text = text.text[nextstart:];
+ t = text :: t;
+ }
+ state.brk();
+ }
+ addspace = 0;
+ }
+}
+
+trim(s: string): string
+{
+ for (spi :=0; spi < len s && s[spi] == ' '; spi++)
+ ;
+ return s[spi:];
+}
+
+ParseState[T].brk(state: self ref ParseState)
+{
+ if (state.curline != nil) {
+ line: list of (int, Text);
+ for (l := state.curline; l != nil; l = tl l)
+ line = hd l :: line;
+ state.setline <- = line;
+ state.curline = nil;
+ state.curwidth = 0;
+ }
+ state.indent = state.prevailindent;
+}
+
+ParseState[T].paragraph(state: self ref ParseState)
+{
+ state.brk();
+ if (state.newpara == 0) {
+ blank := Text(state.curfont, state.curattr, "", 0, "");
+ for (i := 0; i < state.pspace; i++)
+ state.setline <- = (0, blank)::nil;
+ state.newpara = 1;
+ }
+ state.curattr = 0;
+ state.curfont = FONT_ROMAN;
+ state.indent = state.metrics.indent;
+# state.prevailindent = state.indent;
+ state.ntlsetindent = 0;
+}
+
+# convert troff 'values' into output 'dots'
+tval(m: Metrics, v: string, defunits: int): int
+{
+ if (v == nil)
+ return 0;
+ units := v[len v -1];
+ val: real;
+
+ case units {
+ 'i' or
+ 'c' or
+ 'P' or
+ 'm' or
+ 'n' or
+ 'p' or
+ 'u' or
+ 'v' =>
+ val = real v[0:len v - 1];
+ * =>
+ val = real v;
+ units = defunits;
+ }
+ r := 0;
+ case units {
+ 'i' =>
+ r = int (real m.dpi * val);
+ 'c' =>
+ r = int ((real m.dpi * val)/2.54);
+ 'P' =>
+ r = int ((real m.dpi * val)/ 6.0);
+ 'm' =>
+ r = int (real m.em * val);
+ 'n' =>
+ r = int (real m.en * val);
+ 'p' =>
+ r = int ((real m.dpi * val)/72.0);
+ 'u' =>
+ r = int val;
+ 'v' =>
+ r = int (real m.V * val);
+ }
+ return r;
+}
+
+altattr[T](state: ref ParseState[T], f1, f2: int, strs: list of string)
+ for{
+ T =>
+ textwidth: fn(t: self T, text: Text): int;
+ }
+{
+ index := 0;
+ textl: list of Text;
+
+ prevfont := state.curfont;
+ for (; strs != nil; strs = tl strs) {
+ str := hd strs;
+ f := f1;
+ if (index++ & 1)
+ f = f2;
+ state.curfont = f;
+ newtext := parsetext(state, str);
+ for (; newtext != nil; newtext = tl newtext)
+ textl = hd newtext :: textl;
+ }
+ orderedtext: list of Text;
+ for (; textl != nil; textl = tl textl)
+ orderedtext = hd textl :: orderedtext;
+ state.addtext(orderedtext);
+ state.curfont = prevfont;
+}
+
+dumptextlist(t: list of Text)
+{
+ sys->print("textlist[");
+ for (; t != nil; t = tl t) {
+ s := hd t;
+ sys->print("(%s)", s.text);
+ }
+ sys->print("]\n");
+}
+
+convlink(params: list of string): string
+{
+ # merge the texts
+ s := "";
+ for (; params != nil; params = tl params)
+ s = s + (hd params);
+
+ for (i := 0; i < len s; i ++)
+ if (s[i] == '(')
+ break;
+ if (i+1 >= len s)
+ return nil;
+ cmd := s[0:i];
+ i++;
+ s = s[i:];
+ for (i = 0; i < len s; i++)
+ if (s[i] == ')')
+ break;
+ section := s[0:i];
+ if (section == nil || !isint(section))
+ return nil;
+
+ return section + " " + cmd;
+}
+
+isint(s: string): int
+{
+ for (i := 0; i < len s; i++)
+ if (s[i] != '.' && (s[i] < '0' || s[i] > '9'))
+ return 0;
+ return 1;
+}
diff --git a/appl/lib/plumbing.b b/appl/lib/plumbing.b
new file mode 100644
index 00000000..77f378f1
--- /dev/null
+++ b/appl/lib/plumbing.b
@@ -0,0 +1,254 @@
+implement Plumbing;
+
+include "sys.m";
+ sys: Sys;
+
+include "regex.m";
+ regex: Regex;
+
+include "plumbing.m";
+
+init(regexmod: Regex, args: list of string): (list of ref Rule, string)
+{
+ sys = load Sys Sys->PATH;
+ regex = regexmod;
+
+ if(args == nil){
+ user := readfile("/dev/user");
+ if(user == nil)
+ return (nil, sys->sprint("can't read /dev/user: %r"));
+ filename := "/usr/"+user+"/plumbing";
+ (rc, nil) := sys->stat(filename);
+ if(rc < 0)
+ filename = "/usr/"+user+"/lib/plumbing";
+ args = filename :: nil;
+ }
+ r, rules: list of ref Rule;
+ err: string;
+ while(args != nil){
+ filename := hd args;
+ args = tl args;
+ file := readfile(filename);
+ if(file == nil)
+ return (nil, sys->sprint("can't read %s: %r", filename));
+ (r, err) = parse(filename, file);
+ if(err != nil)
+ return (nil, err);
+ while(r != nil){
+ rules = hd r :: rules;
+ r = tl r;
+ }
+ }
+ # reverse the rules
+ r = nil;
+ while(rules != nil){
+ r = hd rules :: r;
+ rules = tl rules;
+ }
+ return (r, nil);
+}
+
+readfile(filename: string): string
+{
+ fd := sys->open(filename, Sys->OREAD);
+ if(fd == nil)
+ return nil;
+ (ok, dir) := sys->fstat(fd);
+ if(ok < 0)
+ return nil;
+ size := int dir.length;
+ if(size == 0) # devices have length 0 sometimes
+ size = 1000;
+ b := array[size] of byte;
+ n := sys->read(fd, b, len b);
+ if(n <= 0)
+ return nil;
+ return string b[0:n];
+}
+
+parse(filename, file: string): (list of ref Rule, string)
+{
+ line: string;
+ lineno := 0;
+ i := 0;
+ pats: list of ref Pattern;
+ rules: list of ref Rule;
+ while(i < len file){
+ lineno++;
+ (line, i) = nextline(file, i);
+ (pat, err) := pattern(line);
+ if(err != nil)
+ return (nil, sys->sprint("%s:%d: %s", filename, lineno, err));
+ if(pat == nil){
+ if(pats==nil || !blank(line)) # comment line
+ continue;
+ (rul, err1) := rule(pats);
+ if(err1 != nil)
+ return (nil, sys->sprint("%s:%d: %s", filename, lineno-1, err1));
+ rules = rul :: rules;
+ pats = nil;
+ }else
+ pats = pat :: pats;
+ }
+ if(pats != nil){
+ (rul, err1) := rule(pats);
+ if(err1 != nil)
+ return (nil, sys->sprint("%s:%d: %s", filename, lineno-1, err1));
+ rules = rul :: rules;
+ }
+ # reverse the rules
+ r: list of ref Rule;
+ while(rules != nil){
+ r = hd rules :: r;
+ rules = tl rules;
+ }
+ return (r, nil);
+}
+
+nextline(file: string, i: int): (string, int)
+{
+ for(j:=i; j<len file; j++)
+ if(file[j] == '\n')
+ return (file[i:j], j+1);
+ return (file[i:], len file);
+}
+
+blank(line: string): int
+{
+ for(i:=0; i<len line; i++)
+ if(line[i]!=' ' && line[i]!='\t')
+ return 0;
+ return 1;
+}
+
+pattern(line: string): (ref Pattern, string)
+{
+ expand := 0;
+ for(i:=0; i<len line; i++)
+ if(line[i] == '$'){
+ expand = 1;
+ break;
+ }
+ (w, err) := words(line);
+ if(err != nil)
+ return (nil, err);
+ if(w == nil)
+ return (nil, nil);
+ if(len w < 3)
+ return (nil, "syntax error: too few words on line");
+ pat := ref Pattern;
+ pat.field = hd w;
+ pat.pred = hd tl w;
+ pat.arg = hd tl tl w;
+ pat.extra = tl tl tl w;
+ pat.expand = expand;
+ return (pat, nil);
+}
+
+rule(pats: list of ref Pattern): (ref Rule, string)
+{
+ # pats is in reverse order on arrival
+ actionpred := list of {"alwaysstart", "start", "to"};
+ patternpred := list of {"is", "isdir", "isfile", "matches", "set"};
+ npats := 0;
+ nacts := 0;
+ haveto := 0;
+ for(l:=pats; l!=nil; l=tl l){
+ pat := hd l;
+ pred := pat.pred;
+ noextra := 1;
+ case pat.field {
+ "plumb" =>
+ nacts++;
+ if(!oneof(pred, actionpred))
+ return (nil, "illegal predicate "+pred+" in action");
+ case pred {
+ "to" or "alwaysstart" =>
+ if(len pat.arg == 0)
+ return (nil, "\"plumb "+pred+"\" must have non-empty target");
+ haveto = 1;
+ "start" =>
+ noextra = 0;
+ }
+ if(npats != 0)
+ return (nil, "actions must follow patterns in rule");
+ "src" or "dst" or "dir" or "kind" or "attr" or "data" =>
+ if(!oneof(pred, patternpred))
+ return (nil, "illegal predicate "+pred+" in pattern");
+ if(pred == "matches"){
+ (pat.regex, nil) = regex->compile(pat.arg, 1);
+ if(pat.regex == nil)
+ return (nil, sys->sprint("error in regular expression '%s'", pat.arg));
+ }
+ npats++;
+ }
+ if(noextra && pat.extra != nil)
+ return (nil, sys->sprint("too many words in '%s' pattern", pat.field));
+ }
+ if(haveto == 0)
+ return (nil, "rule must have \"plumb to\" action");
+ rule := ref Rule;
+ rule.action = array[nacts] of ref Pattern;
+ for(i:=nacts; --i>=0; ){
+ rule.action[i] = hd pats;
+ pats = tl pats;
+ }
+ rule.pattern = array[npats] of ref Pattern;
+ for(i=npats; --i>=0; ){
+ rule.pattern[i] = hd pats;
+ pats = tl pats;
+ }
+ return (rule, nil);
+}
+
+oneof(word: string, words: list of string): int
+{
+ while(words != nil){
+ if(word == hd words)
+ return 1;
+ words = tl words;
+ }
+ return 0;
+}
+
+words(line: string): (list of string, string)
+{
+ ws: list of string;
+ i := 0;
+ for(;;){
+ # not in word; find beginning of word
+ while(i<len line && (line[i]==' ' || line[i]=='\t'))
+ i++;
+ if(i==len line || line[i]=='#')
+ break;
+ # i is first character of word; is it quoted?
+ if(line[i] == '\''){
+ word := "";
+ i++;
+ while(i < len line){
+ c := line[i++];
+ if(c=='\''){
+ if(i==len line || line[i]!='\'')
+ break;
+ # else it's a literal quote
+ if(i < len line)
+ i++;
+ }
+ word[len word] = c;
+ }
+ ws = word :: ws;
+ continue;
+ }
+ # regular word; continue until white space or end
+ start := i;
+ while(i<len line && (line[i]!=' ' && line[i]!='\t'))
+ i++;
+ ws = line[start:i] :: ws;
+ }
+ r: list of string;
+ while(ws != nil){
+ r = hd ws :: r;
+ ws = tl ws;
+ }
+ return (r, nil);
+}
diff --git a/appl/lib/plumbing.m b/appl/lib/plumbing.m
new file mode 100644
index 00000000..a00937d5
--- /dev/null
+++ b/appl/lib/plumbing.m
@@ -0,0 +1,23 @@
+Plumbing: module
+{
+
+ PATH: con "/dis/lib/plumbing.dis";
+
+ Pattern: adt
+ {
+ field: string;
+ pred: string;
+ arg: string;
+ extra: list of string;
+ expand: int;
+ regex: Regex->Re;
+ };
+
+ Rule: adt
+ {
+ pattern: array of ref Pattern;
+ action: array of ref Pattern;
+ };
+
+ init: fn(regexmod: Regex, args: list of string): (list of ref Rule, string);
+};
diff --git a/appl/lib/plumbmsg.b b/appl/lib/plumbmsg.b
new file mode 100644
index 00000000..ef9928e1
--- /dev/null
+++ b/appl/lib/plumbmsg.b
@@ -0,0 +1,190 @@
+implement Plumbmsg;
+
+include "sys.m";
+ sys: Sys;
+
+include "plumbmsg.m";
+
+input: ref Sys->FD;
+port: ref Sys->FD;
+portname: string;
+maxdatasize: int;
+
+init(doinput: int, rcvport: string, maxdata: int): int
+{
+ sys = load Sys Sys->PATH;
+
+ if(!doinput && rcvport == nil) # server, not client
+ return 1;
+ input = sys->open("/chan/plumb.input", Sys->OWRITE);
+ if(input == nil)
+ return -1;
+ if(rcvport == nil) # sending messages but never receiving them
+ return 1;
+ port = sys->open("/chan/plumb."+rcvport, Sys->OREAD);
+ if(port == nil){
+ input = nil;
+ return -1;
+ }
+ maxdatasize = maxdata;
+ portname = rcvport;
+ msg := ref Msg;
+ msg.src = portname;
+ msg.dst = "plumb";
+ msg.kind = "text";
+ msg.data = array of byte "start";
+ if(msg.send() < 0){
+ port = nil;
+ input = nil;
+ return -1;
+ }
+ return 1;
+}
+
+shutdown()
+{
+ msg := ref Msg;
+ msg.src = portname;
+ msg.dst = "plumb";
+ msg.kind = "text";
+ msg.data = array of byte "stop";
+ msg.send();
+}
+
+Msg.send(msg: self ref Msg): int
+{
+ hdr :=
+ msg.src+"\n"+
+ msg.dst+"\n"+
+ msg.dir+"\n"+
+ msg.kind+"\n"+
+ msg.attr+"\n"+
+ string len msg.data+"\n";
+ ahdr := array of byte hdr;
+ b := array[len ahdr+len msg.data] of byte;
+ b[0:] = ahdr;
+ b[len ahdr:] = msg.data;
+ return sys->write(input, b, len b);
+}
+
+Msg.recv(): ref Msg
+{
+ b := array[maxdatasize+1000] of byte;
+ n := sys->read(port, b, len b);
+ if(n <= 0)
+ return nil;
+ return Msg.unpack(b[0:n]);
+}
+
+Msg.unpack(b: array of byte): ref Msg
+{
+ (hdr, data) := unpack(b, 6);
+ if(hdr == nil)
+ return nil;
+
+ msg := ref Msg;
+ msg.src = hdr[0];
+ msg.dst = hdr[1];
+ msg.dir = hdr[2];
+ msg.kind = hdr[3];
+ msg.attr = hdr[4];
+ msg.data = data;
+
+ return msg;
+}
+
+Msg.pack(msg: self ref Msg): array of byte
+{
+ hdr :=
+ msg.src+"\n"+
+ msg.dst+"\n"+
+ msg.dir+"\n"+
+ msg.kind+"\n"+
+ msg.attr+"\n"+
+ string len msg.data+"\n";
+ ahdr := array of byte hdr;
+ b := array[len ahdr+len msg.data] of byte;
+ b[0:] = ahdr;
+ b[len ahdr:] = msg.data;
+ return b;
+}
+
+# unpack message from array of bytes. last string in message
+# is number of bytes in data portion of message
+unpack(b: array of byte, ns: int): (array of string, array of byte)
+{
+ i := 0;
+ a := array[ns] of string;
+ for(n:=0; n<ns; n++){
+ (i, a[n]) = unpackstring(b, i);
+ if(i < 0)
+ return (nil, nil);
+ }
+ nb := int a[ns-1];
+ if((len b)-i != nb){
+ sys->print("unpack: bad message format: wrong nbytes\n");
+ return (nil, nil);
+ }
+ # copy data so b can be reused or freed
+ data := array[nb] of byte;
+ data[0:] = b[i:];
+ return (a, data);
+}
+
+unpackstring(b: array of byte, i: int): (int, string)
+{
+ starti := i;
+ while(i < len b){
+ if(b[i] == byte '\n')
+ return (i+1, string b[starti:i]);
+ i++;
+ }
+ return (-1, nil);
+}
+
+string2attrs(s: string): list of ref Attr
+{
+ (nil, pairs) := sys->tokenize(s, "\t");
+ if(pairs == nil)
+ return nil;
+ attrs: list of ref Attr;
+ while(pairs != nil){
+ pair := hd pairs;
+ pairs = tl pairs;
+ a := ref Attr;
+ for(i:=0; i<len pair; i++)
+ if(pair[i] == '='){
+ a.name = pair[0:i];
+ if(++i < len pair)
+ a.val = pair[i:];
+ break;
+ }
+ attrs = a :: attrs;
+ }
+ return attrs;
+}
+
+attrs2string(l: list of ref Attr): string
+{
+ s := "";
+ while(l != nil){
+ a := hd l;
+ l = tl l;
+ if(s == "")
+ s = a.name + "=" + a.val;
+ else
+ s += "\t" + a.name + "=" + a.val;
+ }
+ return s;
+}
+
+lookup(attrs: list of ref Attr, name: string): (int, string)
+{
+ while(attrs != nil){
+ a := hd attrs;
+ attrs = tl attrs;
+ if(a.name == name)
+ return (1, a.val);
+ }
+ return (0, nil);
+}
diff --git a/appl/lib/pop3.b b/appl/lib/pop3.b
new file mode 100644
index 00000000..18e90525
--- /dev/null
+++ b/appl/lib/pop3.b
@@ -0,0 +1,298 @@
+implement Pop3;
+
+include "sys.m";
+ sys : Sys;
+include "draw.m";
+include "bufio.m";
+ bufio : Bufio;
+include "pop3.m";
+
+FD, Connection: import sys;
+Iobuf : import bufio;
+
+ibuf, obuf : ref Bufio->Iobuf;
+conn : int = 0;
+inited : int = 0;
+
+rpid : int = -1;
+cread : chan of (int, string);
+
+DEBUG : con 0;
+
+open(user, password, server : string): (int, string)
+{
+ s : string;
+
+ if (!inited) {
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ inited = 1;
+ }
+ if (conn)
+ return (-1, "connection is already open");
+ if (server == nil) {
+ server = defaultserver();
+ if (server == nil)
+ return (-1, "no default mail server");
+ }
+ (ok, c) := sys->dial ("tcp!" + server + "!110", nil);
+ if (ok < 0)
+ return (-1, "dialup failed");
+ ibuf = bufio->fopen(c.dfd, Bufio->OREAD);
+ obuf = bufio->fopen(c.dfd, Bufio->OWRITE);
+ if (ibuf == nil || obuf == nil)
+ return (-1, "failed to open bufio");
+ cread = chan of (int, string);
+ spawn mreader(cread);
+ (rpid, nil) = <- cread;
+ (ok, s) = mread();
+ if (ok < 0)
+ return (-1, s);
+ (ok, s) = mcmd("USER " + user);
+ if (ok < 0)
+ return (-1, s);
+ (ok, s) = mcmd("PASS " + password);
+ if (ok < 0)
+ return (-1, s);
+ conn = 1;
+ return (1, nil);
+}
+
+stat() : (int, string, int, int)
+{
+ if (!conn)
+ return (-1, "not connected", 0, 0);
+ (ok, s) := mcmd("STAT");
+ if (ok < 0)
+ return (-1, s, 0, 0);
+ (n, ls) := sys->tokenize(s, " ");
+ if (n == 3)
+ return (1, nil, int hd tl ls, int hd tl tl ls);
+ return (-1, "stat failed", 0, 0);
+}
+
+msglist() : (int, string, list of (int, int))
+{
+ ls : list of (int, int);
+
+ if (!conn)
+ return (-1, "not connected", nil);
+ (ok, s) := mcmd("LIST");
+ if (ok < 0)
+ return (-1, s, nil);
+ for (;;) {
+ (ok, s) = mread();
+ if (ok < 0)
+ return (-1, s, nil);
+ if (len s < 3) {
+ if (len s > 0 && s[0] == '.')
+ return (1, nil, rev2(ls));
+ else
+ return (-1, s, nil);
+ }
+ else {
+ (n, sl) := sys->tokenize(s, " ");
+ if (n == 2)
+ ls = (int hd sl, int hd tl sl) :: ls;
+ else
+ return (-1, "bad list format", nil);
+ }
+ }
+}
+
+msgnolist() : (int, string, list of int)
+{
+ ls : list of int;
+
+ if (!conn)
+ return (-1, "not connected", nil);
+ (ok, s) := mcmd("LIST");
+ if (ok < 0)
+ return (-1, s, nil);
+ for (;;) {
+ (ok, s) = mread();
+ if (ok < 0)
+ return (-1, s, nil);
+ if (len s < 3) {
+ if (len s > 0 && s[0] == '.')
+ return (1, nil, rev1(ls));
+ else
+ return (-1, s, nil);
+ }
+ else {
+ (n, sl) := sys->tokenize(s, " ");
+ if (n == 2)
+ ls = int hd sl :: ls;
+ else
+ return (-1, "bad list format", nil);
+ }
+ }
+}
+
+top(m : int) : (int, string, string)
+{
+ if (!conn)
+ return (-1, "not connected", nil);
+ (ok, s) := mcmd("TOP " + string m + " 1");
+ if (ok < 0)
+ return (-1, s, nil);
+ return getbdy();
+}
+
+get(m : int) : (int, string, string)
+{
+ if (!conn)
+ return (-1, "not connected", nil);
+ (ok, s) := mcmd("RETR " + string m);
+ if (ok < 0)
+ return (-1, s, nil);
+ return getbdy();
+}
+
+getbdy() : (int, string, string)
+{
+ b : string;
+
+ for (;;) {
+ (ok, s) := mread();
+ if (ok < 0)
+ return (-1, s, nil);
+ if (s == ".")
+ break;
+ if (len s > 1 && s[0] == '.' && s[1] == '.')
+ s = s[1:];
+ b = b + s + "\n";
+ }
+ return (1, nil, b);
+}
+
+delete(m : int) : (int, string)
+{
+ if (!conn)
+ return (-1, "not connected");
+ return mcmd("DELE " + string m);
+}
+
+close(): (int, string)
+{
+ if (!conn)
+ return (-1, "connection not open");
+ ok := mwrite("QUIT");
+ kill(rpid);
+ ibuf.close();
+ obuf.close();
+ conn = 0;
+ if (ok < 0)
+ return (-1, "failed to close connection");
+ return (1, nil);
+}
+
+SLPTIME : con 100;
+MAXSLPTIME : con 10000;
+
+mread() : (int, string)
+{
+ t := 0;
+ while (t < MAXSLPTIME) {
+ alt {
+ (ok, s) := <- cread =>
+ return (ok, s);
+ * =>
+ t += SLPTIME;
+ sys->sleep(SLPTIME);
+ }
+ }
+ kill(rpid);
+ return (-1, "smtp timed out\n");
+}
+
+mreader(c : chan of (int, string))
+{
+ c <- = (sys->pctl(0, nil), nil);
+ for (;;) {
+ line := ibuf.gets('\n');
+ if (DEBUG)
+ sys->print("mread : %s", line);
+ if (line == nil) {
+ c <- = (-1, "could not read response from server");
+ continue;
+ }
+ l := len line;
+ if (line[l-1] == '\n')
+ l--;
+ if (line[l-1] == '\r')
+ l--;
+ c <- = (1, line[0:l]);
+ }
+}
+
+mwrite(s : string): int
+{
+ s += "\r\n";
+ if (DEBUG)
+ sys->print("mwrite : %s", s);
+ b := array of byte s;
+ l := len b;
+ nb := obuf.write(b, l);
+ obuf.flush();
+ if (nb != l)
+ return -1;
+ return 1;
+}
+
+mcmd(s : string) : (int, string)
+{
+ ok : int;
+ r : string;
+
+ ok = mwrite(s);
+ if (ok < 0)
+ return (-1, err(s) + " send failed");
+ (ok, r) = mread();
+ if (ok < 0)
+ return (-1, err(s) + " receive failed (" + r + ")");
+ if (len r > 1 && r[0] == '+')
+ return (1, r);
+ return (-1, r);
+}
+
+defaultserver() : string
+{
+ return "$pop3";
+}
+
+rev1(l1 : list of int) : list of int
+{
+ l2 : list of int;
+
+ for ( ; l1 != nil; l1 = tl l1)
+ l2 = hd l1 :: l2;
+ return l2;
+}
+
+rev2(l1 : list of (int, int)) : list of (int, int)
+{
+ l2 : list of (int, int);
+
+ for ( ; l1 != nil; l1 = tl l1)
+ l2 = hd l1 :: l2;
+ return l2;
+}
+
+err(s : string) : string
+{
+ for (i := 0; i < len s; i++)
+ if (s[i] == ' ' || s[i] == ':')
+ return s[0:i];
+ return s;
+}
+
+kill(pid : int) : int
+{
+ if (pid < 0)
+ return 0;
+ fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE);
+ if (fd == nil || sys->fprint(fd, "kill") < 0)
+ return -1;
+ return 0;
+}
diff --git a/appl/lib/popup.b b/appl/lib/popup.b
new file mode 100644
index 00000000..78eef27e
--- /dev/null
+++ b/appl/lib/popup.b
@@ -0,0 +1,124 @@
+implement Popup;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ Point: import Draw;
+include "tk.m";
+ tk: Tk;
+include "popup.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ tk = load Tk Tk->PATH;
+}
+
+post(win: ref Tk->Toplevel, p: Point, a: array of string, n: int): chan of int
+{
+ rc := chan of int;
+ spawn postproc(win, p, a, n, rc);
+ return rc;
+}
+
+postproc(win: ref Tk->Toplevel, p: Point, a: array of string, n: int, rc: chan of int)
+{
+ c := chan of string;
+ tk->namechan(win, c, "c.popup");
+ mkpopupmenu(win, a);
+ cmd(win, ".popup entryconfigure " + string n + " -state active");
+ cmd(win, "bind .popup <Unmap> {send c.popup unmap}");
+
+ dy := ypos(win, n) - ypos(win, 0);
+ p.y -= dy;
+ cmd(win, ".popup post " + string p.x + " " + string p.y +
+ ";grab set .popup");
+ n = -1;
+ while ((e := <-c) != "unmap")
+ n = int e;
+
+ cmd(win, "destroy .popup");
+ rc <-= n;
+}
+
+mkpopupmenu(win: ref Tk->Toplevel, a: array of string)
+{
+ cmd(win, "menu .popup");
+ for (i := 0; i < len a; i++) {
+ cmd(win, ".popup add command -command {send c.popup " + string i +
+ "} -text '" + a[i]);
+ }
+}
+
+Blank: con "-----";
+
+# XXX what should we do about popups containing no items.
+mkbutton(win: ref Tk->Toplevel, w: string, a: array of string, n: int): chan of string
+{
+ c := chan of string;
+ if (len a == 0) {
+ cmd(win, "label " + w + " -bd 2 -relief raised -text '" + Blank);
+ return c;
+ }
+ tk->namechan(win, c, "c" + w);
+ mkpopupmenu(win, a);
+ cmd(win, "label " + w + " -bd 2 -relief raised -width [.popup cget -width] -text '" + a[n]);
+ cmd(win, "bind " + w + " <Button-1> {send c" + w + " " + w + "}");
+ cmd(win, "destroy .popup");
+ return c;
+}
+
+changebutton(win: ref Tk->Toplevel, w: string, a: array of string, n: int)
+{
+ if (len a > 0) {
+ mkpopupmenu(win, a);
+ cmd(win, w + " configure -width [.popup cget -width] -text '" + a[n]);
+ cmd(win, "bind " + w + " <Button-1> {send c" + w + " " + w + "}");
+ cmd(win, "destroy .popup");
+ } else {
+ cmd(win, w + " configure -text '" + Blank);
+ cmd(win, "bind " + w + " <Button-1> {}");
+ }
+}
+
+add(a: array of string, s: string): (array of string, int)
+{
+ for (i := 0; i < len a; i++)
+ if (s == a[i])
+ return (a, i);
+ na := array[len a + 1] of string;
+ na[0:] = a;
+ na[len a] = s;
+ return (na, len a);
+}
+
+#event(win: ref Tk->Toplevel, e: string, a: array of string): int
+#{
+# w := e;
+# p := Point(int cmd(win, w + " cget -actx"), int cmd(win, w + " cget -acty"));
+# s := cmd(win, w + " cget -text");
+# for (i := 0; i < len a; i++)
+# if (s == a[i])
+# break;
+# if (i == len a)
+# i = 0;
+#
+# n := post(win, p, a, i);
+# if (n != -1) {
+# cmd(win, w + " configure -text '" + a[n]);
+# i = n;
+# }
+# return i;
+#}
+
+ypos(win: ref Tk->Toplevel, n: int): int
+{
+ return int cmd(win, ".popup yposition " + string n);
+}
+
+cmd(win: ref Tk->Toplevel, s: string): string
+{
+ r := tk->cmd(win, s);
+ if (len r > 0 && r[0] == '!')
+ sys->print("error executing '%s': %s\n", s, r[1:]);
+ return r;
+}
diff --git a/appl/lib/powerman.b b/appl/lib/powerman.b
new file mode 100644
index 00000000..5b343726
--- /dev/null
+++ b/appl/lib/powerman.b
@@ -0,0 +1,59 @@
+implement Powerman;
+
+#
+# Copyright © 2001 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "powerman.m";
+
+pid := 0;
+
+init(file: string, events: chan of string): int
+{
+ if(file == nil)
+ file = "/dev/powerdata";
+ fd := sys->open(file, Sys->OREAD);
+ if(fd == nil)
+ return -1;
+ pidc := chan of int;
+ spawn reader(fd, events, pidc);
+ return pid = <-pidc;
+}
+
+reader(fd: ref Sys->FD, events: chan of string, pidc: chan of int)
+{
+ pidc <-= sys->pctl(0, nil);
+ buf := array[128] of byte;
+ while((n := sys->read(fd, buf, len buf)) > 0){
+ if(buf[n-1] == byte '\n')
+ n--;
+ events <-= string buf[0:n];
+ }
+ events <-= "error";
+}
+
+stop()
+{
+ if(pid != 0){
+ fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);
+ if(fd != nil)
+ sys->fprint(fd, "kill");
+ pid = 0;
+ }
+}
+
+ack(op: string)
+{
+ ctl("ack "+op);
+}
+
+ctl(op: string): string
+{
+ fd := sys->open("/dev/powerctl", Sys->OWRITE);
+ if(fd != nil && sys->fprint(fd, "%s", op) >= 0)
+ return nil;
+ return sys->sprint("%r");
+}
diff --git a/appl/lib/print/hp_driver.b b/appl/lib/print/hp_driver.b
new file mode 100644
index 00000000..15fb2eb7
--- /dev/null
+++ b/appl/lib/print/hp_driver.b
@@ -0,0 +1,1536 @@
+implement Pdriver;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Display, Font, Rect, Point, Image, Screen: import draw;
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "print.m";
+ Printer: import Print;
+include "scaler.m";
+ scaler: Scaler;
+
+
+K: con 0;
+C: con 1;
+M: con 2;
+Y: con 3;
+Clight: con 4;
+Mlight: con 5;
+
+HPTRUE: con 1;
+HPFALSE: con 0;
+TRUE: con 1;
+FALSE: con 0;
+
+# RGB pixel
+
+RGB: adt {
+ r, g, b: byte;
+};
+
+
+# KCMY pixel
+
+KCMY: adt {
+ k, c, m, y: byte;
+};
+
+
+
+DitherParms: adt {
+ fNumPix: int;
+ fInput: array of byte;
+ fErr: array of int;
+ fSymmetricFlag: int;
+ fFEDRes: array of int;
+ fRasterEvenOrOdd: int;
+ fHifipe: int;
+ fOutput1, fOutput2, fOutput3: array of byte;
+};
+
+# magic and wondrous HP colour maps
+map1: array of KCMY;
+map2: array of KCMY;
+
+ABSOLUTE: con 1;
+RELATIVE: con 0;
+
+Compression := 1;
+
+DEBUG := 0;
+stderr: ref Sys->FD;
+outbuf: ref Iobuf;
+
+ESC: con 27;
+
+# Palettes for Simple_Color
+
+PALETTE_RGB: con 3;
+PALETTE_CMY: con -3;
+PALETTE_KCMY: con -4;
+PALETTE_K: con 1;
+
+
+# Initialization
+
+init(debug: int)
+{
+ sys = load Sys Sys->PATH;
+ stderr = sys->fildes(2);
+ draw = load Draw Draw->PATH;
+ bufio = load Bufio Bufio->PATH;
+ scaler = load Scaler Scaler->PATH;
+ if (scaler == nil) fatal("Failed to load Scaler module");
+ DEBUG = debug;
+}
+
+
+# Return printable area in pixels
+
+printable_pixels(p: ref Printer): (int, int)
+{
+ HMARGIN: con 0.6;
+ WMARGIN: con 0.3;
+ winches := p.popt.paper.width_inches - 2.0*WMARGIN;
+ hinches := p.popt.paper.height_inches - 2.0*HMARGIN;
+ wres := real p.popt.mode.resx;
+ hres := real p.popt.mode.resy;
+
+ (x, y) := (int (winches*wres), int (hinches*hres));
+
+ if (p.popt.orientation == Print->PORTRAIT)
+ return (x, y);
+ return (y, x);
+}
+
+
+
+# Send image to printer
+
+MASK := array[] of {byte 1, byte 3, byte 15, byte 255, byte 255};
+SHIFT := array[] of {7, 6, 4, 0};
+GSFACTOR := array[] of {255.0, 255.0/3.0, 255.0/7.0, 1.0, 1.0};
+lastp : ref Printer;
+
+Refint: adt {
+ value: int;
+};
+
+watchdog(cancel: chan of int, cancelled: ref Refint)
+{
+ <- cancel;
+ cancelled.value = 1;
+}
+
+sendimage(p: ref Printer, pfd: ref Sys->FD, display: ref Draw->Display, im: ref Draw->Image, width: int, lmargin: int, cancel: chan of int): int
+{
+ grppid := sys->pctl(Sys->NEWPGRP, nil);
+ cancelled := ref Refint(0);
+ spawn watchdog(cancel, cancelled);
+
+ outopen(pfd);
+ dbg(sys->sprint("image depth=%d from %d,%d to %d,%d\n", im.depth, im.r.min.x, im.r.min.y, im.r.max.x, im.r.max.y));
+ if (p != lastp) {
+ (map1, map2) = readmaps(p);
+ lastp = p;
+ }
+
+ bpp := im.depth;
+ linechan := chan of array of int;
+ if (p.popt.orientation == Print->PORTRAIT)
+ InputWidth := im.r.max.x-im.r.min.x;
+ else
+ InputWidth = im.r.max.y-im.r.min.y;
+ AdjustedInputWidth := (InputWidth+7) - ((InputWidth+7) % 8);
+ dbg(sys->sprint("bpp=%d, InputWidth=%d, AdjustedInputWidth=%d\n",
+ bpp, InputWidth, AdjustedInputWidth));
+ if (p.popt.orientation == Print->PORTRAIT)
+ spawn row_by_row(im, linechan, AdjustedInputWidth);
+ else
+ spawn rotate(im, linechan, AdjustedInputWidth);
+ DesiredOutputWidth := AdjustedInputWidth;
+ if (width > AdjustedInputWidth)
+ DesiredOutputWidth = width;
+ ScaledWidth := 8*((DesiredOutputWidth)/8);
+ mode := p.popt.mode;
+ Nplanes := 4;
+ if (map2 != nil)
+ Nplanes += 2;
+ Contone := array[Nplanes] of array of byte;
+ ColorDepth := array[Nplanes] of int;
+ ColorDepth[K] = mode.blackdepth;
+ for (col:=1; col<Nplanes; col++)
+ ColorDepth[col] = mode.coldepth;
+ OutputWidth := array[Nplanes] of int;
+ fDitherParms := array[Nplanes] of DitherParms;
+ ErrBuff := array[Nplanes] of array of int;
+ ColorPlane := array[Nplanes] of array of array of array of byte;
+ MixedRes := 0;
+ BaseResX := mode.resx;
+ BaseResY := mode.resy;
+ ResBoost := BaseResX / BaseResY;
+ ResolutionX := array[Nplanes] of int;
+ ResolutionY := array[Nplanes] of int;
+ ResolutionX[K] = mode.resx*mode.blackresmult;
+ ResolutionY[K] = mode.resy*mode.blackresmult;
+ for (col=1; col<Nplanes; col++) {
+ ResolutionX[col] = mode.resx;
+ ResolutionY[col] = mode.resy;
+ }
+ NumRows := array[Nplanes] of int;
+ for (j:=0; j<Nplanes; j++) {
+ if (ResolutionX[j] != ResolutionX[K])
+ MixedRes++;
+ if (MixedRes)
+ # means res(K) !+ res(C,M,Y)
+ NumRows[j] = ResolutionX[j] / BaseResX;
+ else
+ NumRows[j]=1;
+ OutputWidth[j]= ScaledWidth * NumRows[j] * ResBoost;
+ PlaneSize:= OutputWidth[j]/8;
+ Contone[j] = array[OutputWidth[j]] of byte;
+ ColorPlane[j] = array[NumRows[j]] of array of array of byte;
+ for (jj:=0; jj<NumRows[j]; jj++) {
+ ColorPlane[j][jj] = array[ColorDepth[j]] of array of byte;
+ for (jjj:=0; jjj<ColorDepth[j]; jjj++) {
+ ColorPlane[j][jj][jjj] = array[PlaneSize] of byte;
+ }
+ }
+ ErrBuff[j] = array[OutputWidth[j]+2] of {* => 0};
+ }
+
+ pcl_startjob(p);
+ if (p.popt.paper.hpcode != "")
+ PCL_Page_Size(p.popt.paper.hpcode);
+ PCL_Move_CAP_H_Units(lmargin*300/BaseResX, ABSOLUTE);
+ PCL_Configure_Raster_Data4(BaseResX, BaseResY, ColorDepth);
+ PCL_Source_Raster_Width(ScaledWidth);
+ PCL_Compression_Method(Compression);
+ PCL_Start_Raster(1);
+ cmap1 := setup_color_map(display, map1, im.depth);
+ if (map2 != nil)
+ cmap2 := setup_color_map(display, map2, im.depth);
+ numerator, denominator: int;
+ if ((ScaledWidth % AdjustedInputWidth)==0) {
+ numerator = ScaledWidth / AdjustedInputWidth;
+ denominator = 1;
+ } else {
+ numerator = ScaledWidth;
+ denominator = AdjustedInputWidth;
+ }
+ rs := scaler->init(DEBUG, AdjustedInputWidth, numerator, denominator);
+ rasterno := 0;
+ col_row: array of int;
+ eof := 0;
+
+ while (!eof) {
+ col_row = <- linechan;
+ if (col_row == nil)
+ eof++;
+ scaler->rasterin(rs, col_row);
+ while ((scaled_col_row := scaler->rasterout(rs)) != nil) {
+ rasterno++;
+ fRasterOdd := rasterno & 1;
+ kcmy_row := SimpleColorMatch(cmap1, scaled_col_row);
+ if (DEBUG) {
+ dbg("Scaled Raster line:");
+ for (q:=0; q<len scaled_col_row; q++) {
+ (r, g, b) := display.cmap2rgb(scaled_col_row[q]);
+ dbg(sys->sprint("%d rgb=(%d,%d,%d) kcmy=(%d,%d,%d,%d)\n", int scaled_col_row[q],
+ r, g, b, int kcmy_row[q].k, int kcmy_row[q].c, int kcmy_row[q].m, int kcmy_row[q].y));
+ }
+ dbg("\n");
+ }
+ Contone_K := Contone[K];
+ Contone_C := Contone[C];
+ Contone_M := Contone[M];
+ Contone_Y := Contone[Y];
+ for (ii:=0; ii<len Contone[K]; ii++) {
+ kcmy := kcmy_row[ii];
+ Contone_K[ii] = kcmy.k;
+ Contone_C[ii] = kcmy.c;
+ Contone_M[ii] = kcmy.m;
+ Contone_Y[ii] = kcmy.y;
+ }
+ if (map2 != nil) { # For lighter inks
+ kcmy_row_light := SimpleColorMatch(cmap2, scaled_col_row);
+ Contone_Clight := Contone[Clight];
+ Contone_Mlight := Contone[Mlight];
+ for (ii=0; ii<len Contone[Clight]; ii++) {
+ kcmy := kcmy_row_light[ii];
+ Contone_Clight[ii] = kcmy.c;
+ Contone_Mlight[ii] = kcmy.m;
+ }
+ }
+
+ for (i:=0; i< Nplanes; i++) {
+# Pixel multiply here!!
+ fDitherParms[i].fNumPix = OutputWidth[i];
+ fDitherParms[i].fInput = Contone[i];
+ fDitherParms[i].fErr = ErrBuff[i];
+# fDitherParms[i].fErr++; // serpentine (?)
+ fDitherParms[i].fSymmetricFlag = 1;
+# if (i == K)
+# fDitherParms[i].fFEDResPtr = fBlackFEDResPtr;
+# else
+# fDitherParms[i].fFEDResPtr = fColorFEDResPtr;
+ fDitherParms[i].fFEDRes = FEDarray;
+ fDitherParms[i].fRasterEvenOrOdd = fRasterOdd;
+ fDitherParms[i].fHifipe = ColorDepth[i] > 1;
+ for (j=0; j < NumRows[i]; j++) {
+ fDitherParms[i].fOutput1 = ColorPlane[i][j][0];
+ if (fDitherParms[i].fHifipe)
+ fDitherParms[i].fOutput2 = ColorPlane[i][j][1];
+# dbg(sys->sprint("Dither for Row %d ColorPlane[%d][%d]\n", rasterno, i, j));
+ Dither(fDitherParms[i]);
+ }
+ }
+
+ FINALPLANE: con 3;
+# NfinalPlanes := 4;
+ for (i=0; i<=FINALPLANE; i++) {
+ cp_i := ColorPlane[i];
+ coldepth_i := ColorDepth[i];
+ finalrow := NumRows[i]-1;
+ for (j=0; j<=finalrow; j++) {
+ cp_i_j := cp_i[j];
+ for (k:=0; k<coldepth_i; k++) {
+ if (i == FINALPLANE && j == finalrow && k == coldepth_i-1)
+ PCL_Transfer_Raster_Row(cp_i_j[k]);
+ else
+ PCL_Transfer_Raster_Plane(cp_i_j[k]);
+ if (cancelled.value) {
+ PCL_Reset();
+ outclose();
+ killgrp(grppid);
+ return -1;
+ }
+ }
+ }
+ }
+ }
+ }
+ PCL_End_Raster();
+ PCL_Reset();
+ outclose();
+ killgrp(grppid);
+ if (cancelled.value)
+ return -1;
+#sys->print("dlen %d, clen %d overruns %d\n", dlen, clen, overruns);
+ return 0;
+}
+
+
+# Send text to printer
+
+sendtextfd(p: ref Print->Printer, pfd, tfd: ref Sys->FD, pointsize: real, proportional: int, wrap: int): int
+{
+ outopen(pfd);
+ pcl_startjob(p);
+ if (wrap) PCL_End_of_Line_Wrap(0);
+ LATIN1: con "0N";
+ PCL_Font_Symbol_Set(LATIN1);
+ if (proportional) PCL_Font_Spacing(1);
+ if (pointsize > 0.0) {
+ PCL_Font_Height(pointsize);
+ pitch := 10.0*12.0/pointsize;
+ PCL_Font_Pitch(pitch);
+ spacing := int (6.0*12.0/pointsize);
+ PCL_Line_Spacing(spacing);
+ dbg(sys->sprint("Text: pointsize %f pitch %f spacing %d\n", pointsize, pitch, spacing));
+ }
+ PCL_Line_Termination(3);
+ inbuf := bufio->fopen(tfd, Bufio->OREAD);
+ while ((line := inbuf.gets('\n')) != nil) {
+ ob := array of byte line;
+ outwrite(ob, len ob);
+ }
+ PCL_Reset();
+ outclose();
+ return 0;
+}
+
+
+
+# Common PCL start
+
+pcl_startjob(p: ref Printer)
+{
+ PCL_Reset();
+ if (p.popt.duplex) {
+ esc("%-12345X@PJL DEFAULT DUPLEX=ON\n");
+ esc("%-12345X");
+ }
+ if (p.popt.paper.hpcode != "")
+ PCL_Page_Size(p.popt.paper.hpcode);
+ PCL_Orientation(p.popt.orientation);
+ PCL_Duplex(p.popt.duplex);
+}
+
+
+# Spawned to return sequence of rotated image rows
+
+rotate(im: ref Draw->Image, linechan: chan of array of int, adjwidth: int)
+{
+ xmin := im.r.min.x;
+ xmax := im.r.max.x;
+ InputWidth := xmax - xmin;
+ rawchan := chan of array of int;
+ spawn row_by_row(im, rawchan, InputWidth);
+ r_image := array[InputWidth] of {* => array [adjwidth] of {* => 0}};
+ r_row := 0;
+ while ((col_row := <- rawchan) != nil) {
+ endy := len col_row - 1;
+ for (i:=0; i<len col_row; i++)
+ r_image[endy - i][r_row] = col_row[i];
+ r_row++;
+ }
+ for (i:=0; i<len r_image; i++)
+ linechan <-= r_image[i];
+ linechan <-= nil;
+}
+
+
+# Spawned to return sequence of image rows
+
+row_by_row(im: ref Draw->Image, linechan: chan of array of int, adjwidth: int)
+{
+ xmin := im.r.min.x;
+ ymin := im.r.min.y;
+ xmax := im.r.max.x;
+ ymax := im.r.max.y;
+ InputWidth := xmax - xmin;
+ bpp := im.depth;
+ ld := ldepth(im.depth);
+ bytesperline := (InputWidth*bpp+7)/8;
+ rdata := array[bytesperline+10] of byte;
+ pad0 := array [7] of { * => 0};
+ for (y:=ymin; y<ymax; y++) {
+ col_row := array[adjwidth] of int;
+ rect := Rect((xmin, y), (xmax, y+1));
+ np := im.readpixels(rect, rdata);
+ if (np < 0)
+ fatal("Error reading image\n");
+ dbg(sys->sprint("Input Raster line %d: np=%d\n ", y, np));
+ ind := 0;
+ mask := MASK[ld];
+ shift := SHIFT[ld];
+ col_row[adjwidth-7:] = pad0; # Pad to adjusted width with white
+ data := rdata[ind];
+ for (q:=0; q<InputWidth; q++) {
+ col := int ((data >> shift) & mask);
+ shift -= bpp;
+ if (shift < 0) {
+ shift = SHIFT[ld];
+ ind++;
+ data = rdata[ind];
+ }
+ col_row[q] = col;
+ }
+ linechan <-= col_row;
+ }
+ linechan <-= nil;
+}
+
+
+# PCL output routines
+
+
+PCL_Reset()
+{
+ esc("E");
+}
+
+
+PCL_Orientation(value: int)
+{
+ esc(sys->sprint("&l%dO", value));
+}
+
+PCL_Duplex(value: int)
+{
+ esc(sys->sprint("&l%dS", value));
+}
+
+
+PCL_Left_Margin(value: int)
+{
+ esc(sys->sprint("&a%dL", value));
+}
+
+PCL_Page_Size(value: string)
+{
+ esc(sys->sprint("&l%sA", value));
+}
+
+
+PCL_End_of_Line_Wrap(value: int)
+{
+ esc(sys->sprint("&s%dC", value));
+}
+
+PCL_Line_Termination(value: int)
+{
+ esc(sys->sprint("&k%dG", value));
+}
+
+
+PCL_Font_Symbol_Set(value: string)
+{
+ esc(sys->sprint("(%s", value));
+}
+
+
+PCL_Font_Pitch(value: real)
+{
+ esc(sys->sprint("(s%2.2fH", value));
+}
+
+PCL_Font_Spacing(value: int)
+{
+ esc(sys->sprint("(s%dP", value));
+}
+
+PCL_Font_Height(value: real)
+{
+ esc(sys->sprint("(s%2.2fV", value));
+}
+
+PCL_Line_Spacing(value: int)
+{
+ esc(sys->sprint("&l%dD", value));
+}
+
+
+
+PCL_Start_Raster(current: int)
+{
+ flag := 0;
+ if (current) flag = 1;
+ esc(sys->sprint("*r%dA", flag));
+}
+
+
+
+PCL_End_Raster()
+{
+ esc("*rC");
+}
+
+
+PCL_Raster_Resolution(ppi: int)
+{
+ esc(sys->sprint("*t%dR", ppi));
+}
+
+
+PCL_Source_Raster_Width(pixels: int)
+{
+ esc(sys->sprint("*r%dS", pixels));
+}
+
+
+PCL_Simple_Color(palette: int)
+{
+ esc(sys->sprint("*r%dU", palette));
+}
+
+PCL_Compression_Method(ctype: int)
+{
+ esc(sys->sprint("*b%dM", ctype));
+
+}
+
+
+PCL_Move_CAP_V_Rows(pos: int, absolute: int)
+{
+ plus := "";
+ if (!absolute && pos > 0) plus = "+";
+ esc(sys->sprint("&a%s%dR", plus, pos));
+}
+
+PCL_Move_CAP_H_Cols(pos: int, absolute: int)
+{
+ plus := "";
+ if (!absolute && pos > 0) plus = "+";
+ esc(sys->sprint("&a%s%dC", plus, pos));
+}
+
+# These Units are 1/300 of an inch.
+
+PCL_Move_CAP_H_Units(pos: int, absolute: int)
+{
+ plus := "";
+ if (!absolute && pos > 0) plus = "+";
+ esc(sys->sprint("*p%s%dX", plus, pos));
+}
+
+
+
+PCL_Move_CAP_V_Units(pos: int, absolute: int)
+{
+ plus := "";
+ if (!absolute && pos > 0) plus = "+";
+ esc(sys->sprint("*p%s%dY", plus, pos));
+}
+
+
+
+PCL_Configure_Raster_Data4(hres, vres: int, ColorDepth: array of int)
+{
+ ncomponents := 4;
+ msg := array[ncomponents*6 + 2] of byte;
+ i := 0;
+ msg[i++] = byte 2; # Format
+ msg[i++] = byte ncomponents; # KCMY
+ for (c:=0; c<ncomponents; c++) {
+ msg[i++] = byte (hres/256);
+ msg[i++] = byte (hres%256);
+ msg[i++] = byte (vres/256);
+ msg[i++] = byte (vres%256);
+
+ depth := 1 << ColorDepth[c];
+ msg[i++] = byte (depth/256);
+ msg[i++] = byte (depth%256);
+ }
+ if (DEBUG) {
+ dbg("CRD: ");
+ for (ii:=0; ii<len msg; ii++) dbg(sys->sprint("%d(%x) ", int msg[ii], int msg[ii]));
+ dbg("\n");
+ }
+ esc(sys->sprint("*g%dW", len msg));
+ outwrite(msg, len msg);
+}
+
+dlen := 0;
+clen := 0;
+overruns := 0;
+PCL_Transfer_Raster_Plane(data: array of byte)
+{
+ if (DEBUG) {
+ dbg("Transfer_Raster_Plane:");
+ for (i:=0; i<len data; i++) dbg(sys->sprint(" %x", int data[i]));
+ dbg("\n");
+ }
+ if (Compression) {
+d := len data;
+dlen += d;
+ data = compress(data);
+c := len data;
+clen += c;
+if (c > d)
+ overruns += c-d;
+ if (DEBUG) {
+ dbg("Compressed Transfer_Raster_Plane:");
+ for (i:=0; i<len data; i++) dbg(sys->sprint(" %x", int data[i]));
+ dbg("\n");
+ }
+ }
+ esc(sys->sprint("*b%dV", len data));
+ outwrite(data, len data);
+}
+
+
+PCL_Transfer_Raster_Row(data: array of byte)
+{
+ if (DEBUG) {
+ dbg("Transfer_Raster_Row:");
+ for (i:=0; i<len data; i++) dbg(sys->sprint(" %x", int data[i]));
+ dbg("\n");
+ }
+ if (Compression) {
+ data = compress(data);
+ if (DEBUG) {
+ dbg("Compressed Transfer_Raster_Row:");
+ for (i:=0; i<len data; i++) dbg(sys->sprint(" %x", int data[i]));
+ dbg("\n");
+ }
+ }
+ esc(sys->sprint("*b%dW", len data));
+ outwrite(data, len data);
+}
+
+
+outopen(fd: ref Sys->FD)
+{
+ outbuf = bufio->fopen(fd, Bufio->OWRITE);
+ if (outbuf == nil) sys->fprint(stderr, "Failed to open output fd: %r\n");
+}
+
+outclose()
+{
+ outbuf.close();
+}
+
+
+# Write to output using buffered io
+
+outwrite(data: array of byte, length: int)
+{
+ outbuf.write(data, length);
+}
+
+
+# Send escape code to printer
+
+esc(s: string)
+{
+ os := sys->sprint("%c%s", ESC, s);
+ ob := array of byte os;
+ outwrite(ob, len ob);
+}
+
+
+# Read all the maps
+readmaps(p: ref Printer): (array of KCMY, array of KCMY)
+{
+
+ mapfile := p.ptype.hpmapfile;
+ mapf1 := Pdriver->DATAPREFIX + mapfile + ".map";
+ m1 := read_map(mapf1);
+ if (m1 == nil) fatal("Failed to read map file");
+ mapf2 := Pdriver->DATAPREFIX + mapfile + "_2.map";
+ m2 := read_map(mapf2);
+ return (m1, m2);
+}
+
+
+# Read a map file
+
+read_map(mapfile: string) : array of KCMY
+{
+ mf := bufio->open(mapfile, bufio->OREAD);
+ if (mf == nil) return nil;
+ CUBESIZE: con 9*9*9;
+ marray := array[CUBESIZE] of KCMY;
+ i := 0;
+ while (i <CUBESIZE && (lstr := bufio->mf.gets('\n')) != nil) {
+ (n, toks) := sys->tokenize(lstr, " \t");
+ if (n >= 4) {
+ marray[i].k = byte int hd toks;
+ toks = tl toks;
+ marray[i].c = byte int hd toks;
+ toks = tl toks;
+ marray[i].m = byte int hd toks;
+ toks = tl toks;
+ marray[i].y = byte int hd toks;
+ i++;
+ }
+ }
+ return marray;
+}
+
+
+
+
+# Big interpolation routine
+
+# static data
+prev := RGB (byte 255, byte 255, byte 255);
+result: KCMY;
+offset := array[] of { 0, 1, 9, 10, 81, 82, 90, 91 };
+
+
+Interpolate(map: array of KCMY, start: int, rgb: RGB, firstpixel: int): KCMY
+{
+ cyan := array[8] of int;
+ magenta := array[8] of int;
+ yellow := array[8] of int;
+ black := array[8] of int;
+
+ if (firstpixel || prev.r != rgb.r || prev.g != rgb.g || prev.b != rgb.b) {
+ prev = rgb;
+ for (j:=0; j<8; j++) {
+ ioff := start+offset[j];
+ cyan[j] = int map[ioff].c;
+ magenta[j] = int map[ioff].m;
+ yellow[j] = int map[ioff].y;
+ black[j] = int map[ioff].k;
+ }
+
+ diff_red := int rgb.r & 16r1f;
+ diff_green := int rgb.g & 16r1f;
+ diff_blue := int rgb.b & 16r1f;
+
+
+ result.c = byte (((cyan[0] + ( ( (cyan[4] - cyan[0] ) * diff_red) >> 5)) + ( ( ((cyan[2] + ( ( (cyan[6] - cyan[2] ) * diff_red) >> 5)) -(cyan[0] + ( ( (cyan[4] - cyan[0] ) * diff_red) >> 5)) ) * diff_green) >> 5)) + ( ( (((cyan[1] + ( ( (cyan[5] - cyan[1] ) * diff_red) >> 5)) + ( ( ((cyan[3] + ( ( (cyan[7] - cyan[3] ) * diff_red) >> 5)) -(cyan[1] + ( ( (cyan[5] - cyan[1] ) * diff_red) >> 5)) ) * diff_green) >> 5)) -((cyan[0] + ( ( (cyan[4] - cyan[0] ) * diff_red) >> 5)) + ( ( ((cyan[2] + ( ( (cyan[6] - cyan[2] ) * diff_red) >> 5)) -(cyan[0] + ( ( (cyan[4] - cyan[0] ) * diff_red) >> 5)) ) * diff_green) >> 5)) ) * diff_blue) >> 5));
+
+ result.m = byte (((magenta[0] + ( ( (magenta[4] - magenta[0] ) * diff_red) >> 5)) + ( ( ((magenta[2] + ( ( (magenta[6] - magenta[2] ) * diff_red) >> 5)) -(magenta[0] + ( ( (magenta[4] - magenta[0] ) * diff_red) >> 5)) ) * diff_green) >> 5)) + ( ( (((magenta[1] + ( ( (magenta[5] - magenta[1] ) * diff_red) >> 5)) + ( ( ((magenta[3] + ( ( (magenta[7] - magenta[3] ) * diff_red) >> 5)) -(magenta[1] + ( ( (magenta[5] - magenta[1] ) * diff_red) >> 5)) ) * diff_green) >> 5)) -((magenta[0] + ( ( (magenta[4] - magenta[0] ) * diff_red) >> 5)) + ( ( ((magenta[2] + ( ( (magenta[6] - magenta[2] ) * diff_red) >> 5)) -(magenta[0] + ( ( (magenta[4] - magenta[0] ) * diff_red) >> 5)) ) * diff_green) >> 5)) ) * diff_blue) >> 5));
+
+ result.y = byte (((yellow[0] + ( ( (yellow[4] - yellow[0] ) * diff_red) >> 5)) + ( ( ((yellow[2] + ( ( (yellow[6] - yellow[2] ) * diff_red) >> 5)) -(yellow[0] + ( ( (yellow[4] - yellow[0] ) * diff_red) >> 5)) ) * diff_green) >> 5)) + ( ( (((yellow[1] + ( ( (yellow[5] - yellow[1] ) * diff_red) >> 5)) + ( ( ((yellow[3] + ( ( (yellow[7] - yellow[3] ) * diff_red) >> 5)) -(yellow[1] + ( ( (yellow[5] - yellow[1] ) * diff_red) >> 5)) ) * diff_green) >> 5)) -((yellow[0] + ( ( (yellow[4] - yellow[0] ) * diff_red) >> 5)) + ( ( ((yellow[2] + ( ( (yellow[6] - yellow[2] ) * diff_red) >> 5)) -(yellow[0] + ( ( (yellow[4] - yellow[0] ) * diff_red) >> 5)) ) * diff_green) >> 5)) ) * diff_blue) >> 5));
+
+ result.k = byte (((black[0] + ( ( (black[4] - black[0] ) * diff_red) >> 5)) + ( ( ((black[2] + ( ( (black[6] - black[2] ) * diff_red) >> 5)) -(black[0] + ( ( (black[4] - black[0] ) * diff_red) >> 5)) ) * diff_green) >> 5)) + ( ( (((black[1] + ( ( (black[5] - black[1] ) * diff_red) >> 5)) + ( ( ((black[3] + ( ( (black[7] - black[3] ) * diff_red) >> 5)) -(black[1] + ( ( (black[5] - black[1] ) * diff_red) >> 5)) ) * diff_green) >> 5)) -((black[0] + ( ( (black[4] - black[0] ) * diff_red) >> 5)) + ( ( ((black[2] + ( ( (black[6] - black[2] ) * diff_red) >> 5)) -(black[0] + ( ( (black[4] - black[0] ) * diff_red) >> 5)) ) * diff_green) >> 5)) ) * diff_blue) >> 5));
+
+ }
+ return result;
+}
+
+# Colour RGB to KCMY convertor
+
+ColorMatch(map: array of KCMY, row: array of RGB): array of KCMY
+{
+ kcmy := array[len row] of KCMY;
+ first := 1;
+ for (i:=0; i<len row; i++) {
+ r := int row[i].r;
+ g := int row[i].g;
+ b := int row[i].b;
+ start := ((r & 16re0) << 1) + ((r & 16re0) >> 1) + (r >> 5) +
+ ((g & 16re0) >> 2) + (g >> 5) + (b >> 5);
+ kcmy[i] = Interpolate(map, start, row[i], first);
+# dbg(sys->sprint("+++ for (%d,%d,%d) Interpolate returned (%d,%d,%d,%d)\n", r, g, b, int kcmy[i].k, int kcmy[i].c, int kcmy[i].m, int kcmy[i].y));
+ first = 0;
+ }
+ return kcmy;
+}
+
+
+# Simple version of above to lookup precalculated values
+
+SimpleColorMatch(cmap: array of KCMY, colrow: array of int): array of KCMY
+{
+ ncolrow := len colrow;
+ kcmy_row := array[ncolrow] of KCMY;
+ for (i:=0; i<ncolrow; i++)
+ kcmy_row[i] = cmap[colrow[i]];
+ return kcmy_row;
+}
+
+
+ldepth(d: int): int
+{
+ if(d & (d-1) || d >= 16)
+ return 4;
+ for(i := 0; i < 3; i++)
+ if(d <= (1<<i))
+ break;
+ return i;
+}
+
+
+# Set up color map once and for all
+
+setup_color_map(display: ref Display, map: array of KCMY, depth: int): array of KCMY
+{
+ gsfactor := GSFACTOR[ldepth(depth)];
+ bpp := depth;
+ max := 1 << bpp;
+ rgb_row := array[max] of RGB;
+ for (i:=0; i<max; i++) {
+ if (depth >= 8) {
+ (r, g, b) := display.cmap2rgb(i);
+ rgb_row[i] = RGB (byte r, byte g, byte b);
+ } else { # BW or Greyscale
+ grey := byte (255-int (real i * gsfactor));
+ rgb_row[i] = RGB (grey, grey, grey);
+ }
+ }
+ kcmy_row := ColorMatch(map, rgb_row);
+
+ return kcmy_row;
+}
+
+
+
+# Dithering
+
+tmpShortStore: int;
+diffusionErrorPtr := 1; # for serpentine??
+errPtr: array of int;
+rasterByte1 := 0;
+rasterByte2 := 0;
+
+rand8 := array [8] of int;
+pad8 := array [8] of {* => 0};
+
+Dither(ditherParms: DitherParms)
+{
+ errPtr = ditherParms.fErr;
+ numLoop := ditherParms.fNumPix;
+ inputPtr := 0;
+ fedResTbl := ditherParms.fFEDRes;
+ symmetricFlag := ditherParms.fSymmetricFlag;
+ doNext8Pixels : int;
+ hifipe := ditherParms.fHifipe;
+ outputPtr1 := 0;
+ outputPtr2 := 0;
+ diffusionErrorPtr = 1;
+ fInput := ditherParms.fInput;
+
+ if(ditherParms.fRasterEvenOrOdd) {
+ tmpShortStore = errPtr[diffusionErrorPtr];
+ errPtr[diffusionErrorPtr] = 0;
+
+ for (pixelCount := numLoop + 8; (pixelCount -= 8) > 0; ) {
+ if (pixelCount > 16) {
+ # if next 16 pixels are white, skip 8
+# doNext8Pixels = Forward16PixelsNonWhite(fInput, inputPtr);
+ doNext8Pixels = 0;
+ lim := inputPtr + 16;
+ for (i := inputPtr; i < lim; i++) {
+ if (fInput[i] != byte 0) {
+ doNext8Pixels = 1;
+ break;
+ }
+ }
+ } else {
+ doNext8Pixels = 1;
+ }
+ if (doNext8Pixels) {
+FORWARD_FED8(fInput, inputPtr, fedResTbl);
+inputPtr += 8;
+# HPRand8();
+# FORWARD_FED(rand8[0], 16r80, fInput[inputPtr++], fedResTbl);
+# FORWARD_FED(rand8[1], 16r40, fInput[inputPtr++], fedResTbl);
+# FORWARD_FED(rand8[2], 16r20, fInput[inputPtr++], fedResTbl);
+# FORWARD_FED(rand8[3], 16r10, fInput[inputPtr++], fedResTbl);
+# FORWARD_FED(rand8[4], 16r08, fInput[inputPtr++], fedResTbl);
+# FORWARD_FED(rand8[5], 16r04, fInput[inputPtr++], fedResTbl);
+# FORWARD_FED(rand8[6], 16r02, fInput[inputPtr++], fedResTbl);
+# FORWARD_FED(rand8[7], 16r01, fInput[inputPtr++], fedResTbl);
+
+ ditherParms.fOutput1[outputPtr1++] = byte rasterByte1;
+ rasterByte1 = 0;
+
+ if (hifipe) {
+ ditherParms.fOutput2[outputPtr2++] = byte rasterByte2;
+ rasterByte2 = 0;
+ }
+ } else {
+ # Do white space skipping
+ inputPtr += 8;
+ ditherParms.fOutput1[outputPtr1++] = byte 0;
+ if (hifipe) {
+ ditherParms.fOutput2[outputPtr2++] = byte 0;
+ }
+ errPtr[diffusionErrorPtr:] = pad8;
+ diffusionErrorPtr += 8;
+
+ rasterByte1 = 0;
+ rasterByte2 = 0;
+ tmpShortStore = 0;
+ }
+ } # for pixelCount
+ } else {
+ rasterByte1 = 0;
+ rasterByte2 = 0;
+ inputPtr += ( numLoop-1 );
+ outputPtr1 += ( numLoop/8 - 1 );
+ outputPtr2 += ( numLoop/8 - 1 );
+ diffusionErrorPtr += ( numLoop-1 );
+
+ tmpShortStore = errPtr[diffusionErrorPtr];
+ errPtr[diffusionErrorPtr] = 0;
+
+ for (pixelCount := numLoop + 8; (pixelCount -= 8) > 0; ) {
+ if (pixelCount > 16) {
+ # if next 16 pixels are white, skip 8
+# doNext8Pixels = Backward16PixelsNonWhite(fInput, inputPtr);
+ doNext8Pixels = 0;
+ lim := inputPtr - 16;
+ for (i := inputPtr; i > lim; i--) {
+ if (fInput[i] != byte 0) {
+ doNext8Pixels = 1;
+ break;
+ }
+ }
+ } else {
+ doNext8Pixels = HPTRUE;
+ }
+
+ if (doNext8Pixels) {
+ BACKWARD_FED8(fInput, inputPtr, fedResTbl);
+ inputPtr -= 8;
+# HPRand8();
+# BACKWARD_FED(rand8[0], 16r01, fInput[inputPtr--], fedResTbl);
+# BACKWARD_FED(rand8[1], 16r02, fInput[inputPtr--], fedResTbl);
+# BACKWARD_FED(rand8[2], 16r04, fInput[inputPtr--], fedResTbl);
+# BACKWARD_FED(rand8[3], 16r08, fInput[inputPtr--], fedResTbl);
+# BACKWARD_FED(rand8[4], 16r10, fInput[inputPtr--], fedResTbl);
+# BACKWARD_FED(rand8[5], 16r20, fInput[inputPtr--], fedResTbl);
+# BACKWARD_FED(rand8[6], 16r40, fInput[inputPtr--], fedResTbl);
+# BACKWARD_FED(rand8[7], 16r80, fInput[inputPtr--], fedResTbl);
+
+ ditherParms.fOutput1[outputPtr1-- ]= byte rasterByte1;
+ rasterByte1 = 0;
+
+ if (hifipe) {
+ ditherParms.fOutput2[outputPtr2--] = byte rasterByte2;
+ rasterByte2 = 0;
+ }
+ } else {
+ # Do white space skipping
+ inputPtr -= 8;
+ ditherParms.fOutput1[outputPtr1--] = byte 0;
+ if (hifipe) {
+ ditherParms.fOutput2[outputPtr2--] = byte 0;
+ }
+ diffusionErrorPtr -= 8;
+ errPtr[diffusionErrorPtr:] = pad8;
+
+ rasterByte1 = 0;
+ rasterByte2 = 0;
+ tmpShortStore = 0;
+ }
+ }
+ }
+}
+
+
+
+# Take a step back
+
+Backward16PixelsNonWhite(ba: array of byte, inputPtr: int): int
+{
+ lim := inputPtr - 16;
+ for (i := inputPtr; i > lim; i--) {
+ if (ba[i] != byte 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+# Take a step forward
+
+Forward16PixelsNonWhite(ba: array of byte, inputPtr: int): int
+{
+ lim := inputPtr + 16;
+ for (i := inputPtr; i < lim; i++) {
+ if (ba[i] != byte 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+FORWARD_FED8(input: array of byte, ix: int, fedResTbl: array of int)
+{
+ HPRand8();
+ randix := 0;
+
+ for (bitMask := 16r80; bitMask; bitMask >>= 1) {
+ tone := int input[ix++];
+ fedResPtr := tone << 2;
+ level := fedResTbl[fedResPtr];
+ if (tone != 0) {
+ tone = ( tmpShortStore + int fedResTbl[fedResPtr+1] );
+ if (tone >= rand8[randix++]) {
+ tone -= 255;
+ level++;
+ }
+ case (level) {
+ 0=>
+ break;
+ 1=>
+ rasterByte1 |= bitMask;
+ break;
+ 2=>
+ rasterByte2 |= bitMask;
+ break;
+ 3=>
+ rasterByte2 |= bitMask; rasterByte1 |= bitMask;
+ break;
+ 4=>
+ break;
+ 5=>
+ rasterByte1 |= bitMask;
+ break;
+ 6=>
+ rasterByte2 |= bitMask;
+ break;
+ 7=>
+ rasterByte2 |= bitMask; rasterByte1 |= bitMask;
+ break;
+ }
+ } else {
+ tone = tmpShortStore;
+ }
+ halftone := tone >> 1;
+ errPtr[diffusionErrorPtr++] = halftone;
+ tmpShortStore = errPtr[diffusionErrorPtr] + (tone - halftone);
+ }
+}
+
+#FORWARD_FED(thresholdValue: int, bitMask: int, toneb: byte, fedResTbl : array of int)
+#{
+# tone := int toneb;
+# fedResPtr := (tone << 2);
+# level := fedResTbl[fedResPtr];
+# if (tone != 0) {
+# tone = ( tmpShortStore + int fedResTbl[fedResPtr+1] );
+# if (tone >= thresholdValue) {
+# tone -= 255;
+# level++;
+# }
+# case (level) {
+# 0=>
+# break;
+# 1=>
+# rasterByte1 |= bitMask;
+# break;
+# 2=>
+# rasterByte2 |= bitMask;
+# break;
+# 3=>
+# rasterByte2 |= bitMask; rasterByte1 |= bitMask;
+# break;
+# 4=>
+# break;
+# 5=>
+# rasterByte1 |= bitMask;
+# break;
+# 6=>
+# rasterByte2 |= bitMask;
+# break;
+# 7=>
+# rasterByte2 |= bitMask; rasterByte1 |= bitMask;
+# break;
+# }
+# } else {
+# tone = tmpShortStore;
+# }
+# halftone := tone >> 1;
+# errPtr[diffusionErrorPtr++] = halftone;
+# tmpShortStore = errPtr[diffusionErrorPtr] + (tone - halftone);
+## dbg(sys->sprint("FORWARD_FED: thresh %d bitMask %x toneb %d => rasterbytes %d,%d,%d\n", thresholdValue, bitMask, int toneb, rasterByte1, rasterByte2));
+#}
+
+BACKWARD_FED8(input: array of byte, ix: int, fedResTbl: array of int)
+{
+ HPRand8();
+ randix := 0;
+
+ for (bitMask := 16r01; bitMask <16r100; bitMask <<= 1) {
+ tone := int input[ix--];
+ fedResPtr := (tone << 2);
+ level := fedResTbl[fedResPtr];
+ if (tone != 0) {
+ tone = ( tmpShortStore + int fedResTbl[fedResPtr+1] );
+ if (tone >= rand8[randix++]) {
+ tone -= 255;
+ level++;
+ }
+ case (level) {
+ 0=>
+ break;
+ 1=>
+ rasterByte1 |= bitMask;
+ break;
+ 2=>
+ rasterByte2 |= bitMask;
+ break;
+ 3=>
+ rasterByte2 |= bitMask; rasterByte1 |= bitMask;
+ break;
+ 4=>
+ break;
+ 5=>
+ rasterByte1 |= bitMask;
+ break;
+ 6=>
+ rasterByte2 |= bitMask;
+ break;
+ 7=>
+ rasterByte2 |= bitMask; rasterByte1 |= bitMask;
+ break;
+ }
+ } else {
+ tone = tmpShortStore;
+ }
+ halftone := tone >> 1;
+ errPtr[diffusionErrorPtr--] = halftone;
+ tmpShortStore = errPtr[diffusionErrorPtr] + (tone - halftone);
+ }
+}
+
+
+#BACKWARD_FED(thresholdValue: int, bitMask: int, toneb: byte, fedResTbl : array of int)
+#{
+# tone := int toneb;
+# fedResPtr := (tone << 2);
+# level := fedResTbl[fedResPtr];
+# if (tone != 0) {
+# tone = ( tmpShortStore + int fedResTbl[fedResPtr+1] );
+# if (tone >= thresholdValue) {
+# tone -= 255;
+# level++;
+# }
+# case (level) {
+# 0=>
+# break;
+# 1=>
+# rasterByte1 |= bitMask;
+# break;
+# 2=>
+# rasterByte2 |= bitMask;
+# break;
+# 3=>
+# rasterByte2 |= bitMask; rasterByte1 |= bitMask;
+# break;
+# 4=>
+# break;
+# 5=>
+# rasterByte1 |= bitMask;
+# break;
+# 6=>
+# rasterByte2 |= bitMask;
+# break;
+# 7=>
+# rasterByte2 |= bitMask; rasterByte1 |= bitMask;
+# break;
+# }
+# } else {
+# tone = tmpShortStore;
+# }
+# halftone := tone >> 1;
+# errPtr[diffusionErrorPtr--] = halftone;
+# tmpShortStore = errPtr[diffusionErrorPtr] + (tone - halftone);
+## dbg(sys->sprint("BACWARD_FED: thresh %d bitMask %x toneb %d => rasterbytes %d,%d,%d\n", thresholdValue, bitMask, int toneb, rasterByte1, rasterByte2));
+#}
+
+
+# Pixel replication
+
+pixrep(in: array of RGB): array of RGB
+{
+ out := array[2*len in] of RGB;
+ for (i:=0; i<len in; i++) {
+ out[i*2] = in[i];
+ out[i*2+1] = in[i];
+ }
+ return out;
+}
+
+
+
+
+
+
+# Random numbers
+
+IM: con 139968;
+IA: con 3877;
+IC: con 29573;
+
+last := 42;
+
+# Use a really simple and quick random number generator
+
+HPRand(): int
+{
+ return (74 * (last = (last* IA + IC) % IM) / IM ) + 5;
+}
+
+HPRand8()
+{
+ for (i:= 0; i < 8; i++)
+ rand8[i] = (74 * (last = (last* IA + IC) % IM) / IM ) + 5;
+}
+
+# Compression
+
+compress(rawdata: array of byte): array of byte
+{
+ nraw := len rawdata;
+ comp := array [2*nraw] of byte; # worst case
+ ncomp := 0;
+ for (i:=0; i<nraw;) {
+ rpt := 0;
+ val := rawdata[i++];
+ while (i<nraw && rpt < 255 && rawdata[i] == val) {
+ rpt++;
+ i++;
+ }
+ comp[ncomp++] = byte rpt;
+ comp[ncomp++] = val;
+ }
+ return comp[0:ncomp];
+}
+
+
+
+# Print error message and exit
+
+fatal(s: string)
+{
+ sys->fprint(stderr, "%s\n", s);
+ exit;
+}
+
+killgrp(pid: int)
+{
+ sys->fprint(sys->open("/prog/" + string pid +"/ctl", Sys->OWRITE), "killgrp");
+}
+
+
+dbg(s: string)
+{
+ if (DEBUG) sys->fprint(stderr, "%s", s);
+}
+
+
+
+# Uninteresting constants
+
+FEDarray := array[1024] of
+{
+ 0 , 0 , 0 , 0 ,
+ 0 , 0 , 0 , 0 ,
+ 0 , 2 , 0 , 0 ,
+ 0 , 3 , 0 , 0 ,
+ 0 , 4 , 0 , 0 ,
+ 0 , 5 , 0 , 0 ,
+ 0 , 6 , 0 , 0 ,
+ 0 , 7 , 0 , 0 ,
+ 0 , 8 , 0 , 0 ,
+ 0 , 9 , 0 , 0 ,
+ 0 , 10 , 0 , 0 ,
+ 0 , 11 , 0 , 0 ,
+ 0 , 12 , 0 , 0 ,
+ 0 , 13 , 0 , 0 ,
+ 0 , 14 , 0 , 0 ,
+ 0 , 15 , 0 , 0 ,
+ 0 , 16 , 0 , 0 ,
+ 0 , 17 , 0 , 0 ,
+ 0 , 18 , 0 , 0 ,
+ 0 , 19 , 0 , 0 ,
+ 0 , 20 , 0 , 0 ,
+ 0 , 21 , 0 , 0 ,
+ 0 , 22 , 0 , 0 ,
+ 0 , 23 , 0 , 0 ,
+ 0 , 24 , 0 , 0 ,
+ 0 , 25 , 0 , 0 ,
+ 0 , 26 , 0 , 0 ,
+ 0 , 27 , 0 , 0 ,
+ 0 , 28 , 0 , 0 ,
+ 0 , 29 , 0 , 0 ,
+ 0 , 30 , 0 , 0 ,
+ 0 , 31 , 0 , 0 ,
+ 0 , 32 , 0 , 0 ,
+ 0 , 33 , 0 , 0 ,
+ 0 , 34 , 0 , 0 ,
+ 0 , 35 , 0 , 0 ,
+ 0 , 36 , 0 , 0 ,
+ 0 , 37 , 0 , 0 ,
+ 0 , 38 , 0 , 0 ,
+ 0 , 39 , 0 , 0 ,
+ 0 , 40 , 0 , 0 ,
+ 0 , 41 , 0 , 0 ,
+ 0 , 42 , 0 , 0 ,
+ 0 , 43 , 0 , 0 ,
+ 0 , 44 , 0 , 0 ,
+ 0 , 45 , 0 , 0 ,
+ 0 , 46 , 0 , 0 ,
+ 0 , 47 , 0 , 0 ,
+ 0 , 48 , 0 , 0 ,
+ 0 , 49 , 0 , 0 ,
+ 0 , 50 , 0 , 0 ,
+ 0 , 51 , 0 , 0 ,
+ 0 , 52 , 0 , 0 ,
+ 0 , 53 , 0 , 0 ,
+ 0 , 54 , 0 , 0 ,
+ 0 , 55 , 0 , 0 ,
+ 0 , 56 , 0 , 0 ,
+ 0 , 57 , 0 , 0 ,
+ 0 , 58 , 0 , 0 ,
+ 0 , 59 , 0 , 0 ,
+ 0 , 60 , 0 , 0 ,
+ 0 , 61 , 0 , 0 ,
+ 0 , 62 , 0 , 0 ,
+ 0 , 63 , 0 , 0 ,
+ 0 , 64 , 0 , 0 ,
+ 0 , 65 , 0 , 0 ,
+ 0 , 66 , 0 , 0 ,
+ 0 , 67 , 0 , 0 ,
+ 0 , 68 , 0 , 0 ,
+ 0 , 69 , 0 , 0 ,
+ 0 , 70 , 0 , 0 ,
+ 0 , 71 , 0 , 0 ,
+ 0 , 72 , 0 , 0 ,
+ 0 , 73 , 0 , 0 ,
+ 0 , 74 , 0 , 0 ,
+ 0 , 75 , 0 , 0 ,
+ 0 , 76 , 0 , 0 ,
+ 0 , 77 , 0 , 0 ,
+ 0 , 78 , 0 , 0 ,
+ 0 , 79 , 0 , 0 ,
+ 0 , 80 , 0 , 0 ,
+ 0 , 81 , 0 , 0 ,
+ 0 , 82 , 0 , 0 ,
+ 0 , 83 , 0 , 0 ,
+ 0 , 84 , 0 , 0 ,
+ 0 , 85 , 0 , 0 ,
+ 0 , 86 , 0 , 0 ,
+ 0 , 87 , 0 , 0 ,
+ 0 , 88 , 0 , 0 ,
+ 0 , 89 , 0 , 0 ,
+ 0 , 90 , 0 , 0 ,
+ 0 , 91 , 0 , 0 ,
+ 0 , 92 , 0 , 0 ,
+ 0 , 93 , 0 , 0 ,
+ 0 , 94 , 0 , 0 ,
+ 0 , 95 , 0 , 0 ,
+ 0 , 96 , 0 , 0 ,
+ 0 , 97 , 0 , 0 ,
+ 0 , 98 , 0 , 0 ,
+ 0 , 99 , 0 , 0 ,
+ 0 , 100 , 0 , 0 ,
+ 0 , 101 , 0 , 0 ,
+ 0 , 102 , 0 , 0 ,
+ 0 , 103 , 0 , 0 ,
+ 0 , 104 , 0 , 0 ,
+ 0 , 105 , 0 , 0 ,
+ 0 , 106 , 0 , 0 ,
+ 0 , 107 , 0 , 0 ,
+ 0 , 108 , 0 , 0 ,
+ 0 , 109 , 0 , 0 ,
+ 0 , 110 , 0 , 0 ,
+ 0 , 111 , 0 , 0 ,
+ 0 , 112 , 0 , 0 ,
+ 0 , 113 , 0 , 0 ,
+ 0 , 114 , 0 , 0 ,
+ 0 , 115 , 0 , 0 ,
+ 0 , 116 , 0 , 0 ,
+ 0 , 117 , 0 , 0 ,
+ 0 , 118 , 0 , 0 ,
+ 0 , 119 , 0 , 0 ,
+ 0 , 120 , 0 , 0 ,
+ 0 , 121 , 0 , 0 ,
+ 0 , 122 , 0 , 0 ,
+ 0 , 123 , 0 , 0 ,
+ 0 , 124 , 0 , 0 ,
+ 0 , 125 , 0 , 0 ,
+ 0 , 126 , 0 , 0 ,
+ 0 , 127 , 0 , 0 ,
+ 0 , 128 , 0 , 0 ,
+ 0 , 129 , 0 , 0 ,
+ 0 , 130 , 0 , 0 ,
+ 0 , 131 , 0 , 0 ,
+ 0 , 132 , 0 , 0 ,
+ 0 , 133 , 0 , 0 ,
+ 0 , 134 , 0 , 0 ,
+ 0 , 135 , 0 , 0 ,
+ 0 , 136 , 0 , 0 ,
+ 0 , 137 , 0 , 0 ,
+ 0 , 138 , 0 , 0 ,
+ 0 , 139 , 0 , 0 ,
+ 0 , 140 , 0 , 0 ,
+ 0 , 141 , 0 , 0 ,
+ 0 , 142 , 0 , 0 ,
+ 0 , 143 , 0 , 0 ,
+ 0 , 144 , 0 , 0 ,
+ 0 , 145 , 0 , 0 ,
+ 0 , 146 , 0 , 0 ,
+ 0 , 147 , 0 , 0 ,
+ 0 , 148 , 0 , 0 ,
+ 0 , 149 , 0 , 0 ,
+ 0 , 150 , 0 , 0 ,
+ 0 , 151 , 0 , 0 ,
+ 0 , 152 , 0 , 0 ,
+ 0 , 153 , 0 , 0 ,
+ 0 , 154 , 0 , 0 ,
+ 0 , 155 , 0 , 0 ,
+ 0 , 156 , 0 , 0 ,
+ 0 , 157 , 0 , 0 ,
+ 0 , 158 , 0 , 0 ,
+ 0 , 159 , 0 , 0 ,
+ 0 , 160 , 0 , 0 ,
+ 0 , 161 , 0 , 0 ,
+ 0 , 162 , 0 , 0 ,
+ 0 , 163 , 0 , 0 ,
+ 0 , 164 , 0 , 0 ,
+ 0 , 165 , 0 , 0 ,
+ 0 , 166 , 0 , 0 ,
+ 0 , 167 , 0 , 0 ,
+ 0 , 168 , 0 , 0 ,
+ 0 , 169 , 0 , 0 ,
+ 0 , 170 , 0 , 0 ,
+ 0 , 171 , 0 , 0 ,
+ 0 , 172 , 0 , 0 ,
+ 0 , 173 , 0 , 0 ,
+ 0 , 174 , 0 , 0 ,
+ 0 , 175 , 0 , 0 ,
+ 0 , 176 , 0 , 0 ,
+ 0 , 177 , 0 , 0 ,
+ 0 , 178 , 0 , 0 ,
+ 0 , 179 , 0 , 0 ,
+ 0 , 180 , 0 , 0 ,
+ 0 , 181 , 0 , 0 ,
+ 0 , 182 , 0 , 0 ,
+ 0 , 183 , 0 , 0 ,
+ 0 , 184 , 0 , 0 ,
+ 0 , 185 , 0 , 0 ,
+ 0 , 186 , 0 , 0 ,
+ 0 , 187 , 0 , 0 ,
+ 0 , 188 , 0 , 0 ,
+ 0 , 189 , 0 , 0 ,
+ 0 , 190 , 0 , 0 ,
+ 0 , 191 , 0 , 0 ,
+ 0 , 192 , 0 , 0 ,
+ 0 , 193 , 0 , 0 ,
+ 0 , 194 , 0 , 0 ,
+ 0 , 195 , 0 , 0 ,
+ 0 , 196 , 0 , 0 ,
+ 0 , 197 , 0 , 0 ,
+ 0 , 198 , 0 , 0 ,
+ 0 , 199 , 0 , 0 ,
+ 0 , 200 , 0 , 0 ,
+ 0 , 201 , 0 , 0 ,
+ 0 , 202 , 0 , 0 ,
+ 0 , 203 , 0 , 0 ,
+ 0 , 204 , 0 , 0 ,
+ 0 , 205 , 0 , 0 ,
+ 0 , 206 , 0 , 0 ,
+ 0 , 207 , 0 , 0 ,
+ 0 , 208 , 0 , 0 ,
+ 0 , 209 , 0 , 0 ,
+ 0 , 210 , 0 , 0 ,
+ 0 , 211 , 0 , 0 ,
+ 0 , 212 , 0 , 0 ,
+ 0 , 213 , 0 , 0 ,
+ 0 , 214 , 0 , 0 ,
+ 0 , 215 , 0 , 0 ,
+ 0 , 216 , 0 , 0 ,
+ 0 , 217 , 0 , 0 ,
+ 0 , 218 , 0 , 0 ,
+ 0 , 219 , 0 , 0 ,
+ 0 , 220 , 0 , 0 ,
+ 0 , 221 , 0 , 0 ,
+ 0 , 222 , 0 , 0 ,
+ 0 , 223 , 0 , 0 ,
+ 0 , 224 , 0 , 0 ,
+ 0 , 225 , 0 , 0 ,
+ 0 , 226 , 0 , 0 ,
+ 0 , 227 , 0 , 0 ,
+ 0 , 228 , 0 , 0 ,
+ 0 , 229 , 0 , 0 ,
+ 0 , 230 , 0 , 0 ,
+ 0 , 231 , 0 , 0 ,
+ 0 , 232 , 0 , 0 ,
+ 0 , 233 , 0 , 0 ,
+ 0 , 234 , 0 , 0 ,
+ 0 , 235 , 0 , 0 ,
+ 0 , 236 , 0 , 0 ,
+ 0 , 237 , 0 , 0 ,
+ 0 , 238 , 0 , 0 ,
+ 0 , 239 , 0 , 0 ,
+ 0 , 240 , 0 , 0 ,
+ 0 , 241 , 0 , 0 ,
+ 0 , 242 , 0 , 0 ,
+ 0 , 243 , 0 , 0 ,
+ 0 , 244 , 0 , 0 ,
+ 0 , 245 , 0 , 0 ,
+ 0 , 246 , 0 , 0 ,
+ 0 , 247 , 0 , 0 ,
+ 0 , 248 , 0 , 0 ,
+ 0 , 249 , 0 , 0 ,
+ 0 , 250 , 0 , 0 ,
+ 0 , 251 , 0 , 0 ,
+ 0 , 252 , 0 , 0 ,
+ 0 , 253 , 0 , 0 ,
+ 0 , 254 , 0 , 0 ,
+ 0 , 254 , 0 , 0
+};
diff --git a/appl/lib/print/mkfile b/appl/lib/print/mkfile
new file mode 100644
index 00000000..786daf06
--- /dev/null
+++ b/appl/lib/print/mkfile
@@ -0,0 +1,26 @@
+
+<../../../mkconfig
+
+TARG=\
+ print.dis \
+ hp_driver.dis \
+ scaler.dis \
+
+
+MODULES=\
+ scaler.m \
+
+
+SYSMODULES= \
+ bufio.m\
+ draw.m\
+ string.m\
+ sys.m\
+ print.m \
+
+
+DISBIN=$ROOT/dis/lib/print
+
+<$ROOT/mkfiles/mkdis
+# force compilation or it's very slow
+LIMBOFLAGS= -c $LIMBOFLAGS
diff --git a/appl/lib/print/print.b b/appl/lib/print/print.b
new file mode 100644
index 00000000..090136a6
--- /dev/null
+++ b/appl/lib/print/print.b
@@ -0,0 +1,625 @@
+implement Print;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Display, Font, Rect, Point, Image, Screen: import draw;
+include "bufio.m";
+ bufio: Bufio;
+include "string.m";
+ str: String;
+
+include "print.m";
+
+MAXNAME: con 80;
+DEFMODE: con 8r664;
+
+PAPER_CONFIG: con CONFIG_PATH + "paper.cfg";
+PTYPE_CONFIG: con CONFIG_PATH + "ptype.cfg";
+PMODE_CONFIG: con CONFIG_PATH + "pmode.cfg";
+POPT_CONFIG: con CONFIG_PATH + "popt.cfg";
+PRINTER_CONFIG: con CONFIG_PATH + "printer.cfg";
+DEFPRINTER: con CONFIG_PATH + "defprinter";
+
+
+Cfg: adt {
+ name: string;
+ pairs: list of (string, string);
+};
+
+DEBUG :=0;
+
+
+all_papers: list of ref Paper;
+all_pmodes: list of ref Pmode;
+all_ptypes: list of ref Ptype;
+all_popts: list of ref Popt;
+all_printers: list of ref Printer;
+default_printer: ref Printer;
+stderr: ref Sys->FD;
+printfd: ref Sys->FD;
+
+# Initialization
+
+init(): int
+{
+ sys = load Sys Sys->PATH;
+ stderr = sys->fildes(2);
+ draw = load Draw Draw->PATH;
+ bufio = load Bufio Bufio->PATH;
+ str = load String String->PATH;
+ all_papers = read_paper_config();
+ if (all_papers == nil) return 1;
+ all_pmodes = read_pmode_config();
+ if (all_pmodes == nil) return 1;
+ all_ptypes = read_ptype_config();
+ if (all_ptypes == nil) return 1;
+ all_printers = read_printer_config();
+ if (all_printers == nil) return 1;
+ all_popts = read_popt_config();
+ for (pl:=all_printers; pl!=nil; pl=tl pl) {
+ p := hd pl;
+ opt := find_popt(all_popts, p.name);
+ if (opt != nil) p.popt = opt;
+ else {
+ p.popt = ref Popt (p.name, hd all_pmodes, hd all_papers, 0, 0);
+ all_popts = p.popt :: all_popts;
+ }
+ }
+ return 0;
+}
+
+# Set printer FD
+
+set_printfd(fd: ref Sys->FD)
+{
+ printfd = fd;
+}
+
+
+# Get default printer
+
+get_defprinter(): ref Printer
+{
+ if (len all_printers == 1) return hd all_printers; # If there's only 1 printer
+ df := sys->open(DEFPRINTER, Sys->OREAD);
+ if (df == nil) {
+ if (all_printers != nil) return hd all_printers;
+ else return nil;
+ }
+ a := array[MAXNAME] of byte;
+ nb := sys->read(df, a, MAXNAME);
+ if (nb < 2) return nil;
+ name := string a[:nb-1];
+ def := find_printer(all_printers, name);
+ if (def != nil) return def;
+ else return hd all_printers;
+}
+
+# Set default printer
+
+set_defprinter(p: ref Printer)
+{
+ df := sys->create(DEFPRINTER, Sys->OWRITE, DEFMODE);
+ if (df == nil) return;
+ sys->fprint(df, "%s\n", p.name);
+}
+
+# Set paper size
+
+get_size(p: ref Printer): (int, int, int) # dpi, xpixels, ypixels
+{
+ if (p == nil) return (0, 0, 0);
+ load_driver(p);
+ dpi := p.popt.mode.resx;
+ (xpix, ypix) := p.pdriver->printable_pixels(p); # This takes account of orientation
+ return (dpi, xpix, ypix);
+}
+
+
+
+# Get list of all printers
+
+get_printers(): list of ref Printer
+{
+ return all_printers;
+}
+
+# Return list of printer types
+
+get_ptypes(): list of ref Ptype
+{
+ return all_ptypes;
+}
+
+# Return list of print modes
+
+get_pmodes(): list of ref Pmode
+{
+ return all_pmodes;
+}
+
+# Return list of paper types
+
+get_papers(): list of ref Paper
+{
+ return all_papers;
+}
+
+# Return list of print options
+
+get_popts(): list of ref Popt
+{
+ return all_popts;
+}
+
+# Save option settings
+
+save_settings(): int
+{
+ return write_popt_config(all_popts);
+
+}
+
+
+# Print an image
+
+print_image(p: ref Printer, display: ref Draw->Display, im: ref Draw->Image, pcwidth: int, cancel: chan of int): int
+{
+ if (p == nil || im == nil) return 1;
+ load_driver(p);
+ popen(p);
+ (xpix, ypix) := p.pdriver->printable_pixels(p);
+ imwidth := im.r.max.x - im.r.min.x;
+ imheight := im.r.max.y - im.r.min.y;
+ if (pcwidth > 0) pixwidth := int (real xpix * real pcwidth/100.0);
+ else pixwidth = imwidth;
+ lmar := (xpix - pixwidth)/2;
+ fpixwidth := pixwidth;
+ if (p.popt.orientation != PORTRAIT) {
+ lmar += pixwidth;
+ fpixwidth = pixwidth*imheight/imwidth;
+ }
+ if (lmar < 0) lmar = 0;
+ return p.pdriver->sendimage(p, printfd, display, im, fpixwidth, lmar, cancel);
+}
+
+# Print text
+
+print_textfd(p: ref Printer, fd: ref Sys->FD, ps: real, pr: int, wrap: int): int
+{
+ load_driver(p);
+ popen(p);
+ return p.pdriver->sendtextfd(p, printfd, fd, ps, pr, wrap);
+
+}
+
+
+# Open printer device if necessary
+
+popen(p: ref Printer)
+{
+ if (printfd != nil) return;
+ printfd = sys->create(p.device, Sys->OWRITE, DEFMODE);
+}
+
+# Find printer item
+
+find_printer(all: list of ref Printer, name: string): ref Printer
+{
+ for (p:=all; p!=nil; p=tl p) if ((hd p).name == name) return hd p;
+ return nil;
+}
+
+# Find popt item
+
+find_popt(all: list of ref Popt, name: string): ref Popt
+{
+ for (p:=all; p!=nil; p=tl p) if ((hd p).name == name) return hd p;
+ return nil;
+}
+
+
+# Find paper item
+
+find_paper(all: list of ref Paper, name: string): ref Paper
+{
+ for (p:=all; p!=nil; p=tl p) if ((hd p).name == name) return hd p;
+ return nil;
+}
+
+# Find pmode item
+
+find_pmode(all: list of ref Pmode, name: string): ref Pmode
+{
+ for (p:=all; p!=nil; p=tl p) if ((hd p).name == name) return hd p;
+ return nil;
+}
+
+# Find ptype item
+
+find_ptype(all: list of ref Ptype, name: string): ref Ptype
+{
+ for (p:=all; p!=nil; p=tl p) if ((hd p).name == name) return hd p;
+ return nil;
+}
+
+
+# Read paper config file
+
+read_paper_config(): list of ref Paper
+{
+ (clist, aliases) := read_config(PAPER_CONFIG);
+ rlist: list of ref Paper;
+ while (clist != nil) {
+ this := hd clist;
+ clist = tl clist;
+ item := ref Paper(this.name, "", 0.0, 0.0);
+ for (pairs:= this.pairs; pairs != nil; pairs = tl pairs) {
+ (name, value) := hd pairs;
+ case (name) {
+ "hpcode" =>
+ item.hpcode = value;
+
+ "width_inches" =>
+ item.width_inches = real value;
+
+ "height_inches" =>
+ item.height_inches = real value;
+
+ * =>
+ sys->fprint(stderr, "Unknown paper config file option: %s\n", name);
+ }
+ }
+ rlist =item :: rlist;
+ }
+ for (al:=aliases; al!=nil; al=tl al) {
+ (new, old) := hd al;
+ olda := find_paper(rlist, old);
+ if (olda == nil) sys->fprint(stderr, "Paper alias %s not found\n", old);
+ else {
+ newa := ref *olda;
+ newa.name = new;
+ rlist = newa :: rlist;
+ }
+ }
+ return rlist;
+}
+
+
+# Read pmode config file
+
+read_pmode_config(): list of ref Pmode
+{
+ (clist, aliases) := read_config(PMODE_CONFIG);
+ rlist: list of ref Pmode;
+ while (clist != nil) {
+ this := hd clist;
+ clist = tl clist;
+ item := ref Pmode(this.name, "", 0, 0, 1, 1, 1);
+ for (pairs:= this.pairs; pairs != nil; pairs = tl pairs) {
+ (name, value) := hd pairs;
+ case (name) {
+ "desc" =>
+ item.desc = value;
+
+ "resx" =>
+ item.resx = int value;
+
+ "resy" =>
+ item.resy = int value;
+
+ "coldepth" =>
+ item.coldepth = int value;
+
+ "blackdepth" =>
+ item.blackdepth = int value;
+
+ "blackresmult" =>
+ item.blackresmult = int value;
+
+ * =>
+ sys->fprint(stderr, "Unknown pmode config file option: %s\n", name);
+
+ }
+ }
+ rlist =item :: rlist;
+ }
+ for (al:=aliases; al!=nil; al=tl al) {
+ (new, old) := hd al;
+ olda := find_pmode(rlist, old);
+ if (olda == nil) sys->fprint(stderr, "Pmode alias %s not found\n", old);
+ else {
+ newa := ref *olda;
+ newa.name = new;
+ rlist = newa :: rlist;
+ }
+ }
+ return rlist;
+}
+
+
+
+
+# Readp Ptype config file
+
+read_ptype_config(): list of ref Ptype
+{
+ (clist, aliases) := read_config(PTYPE_CONFIG);
+ rlist: list of ref Ptype;
+ while (clist != nil) {
+ this := hd clist;
+ clist = tl clist;
+ item := ref Ptype(this.name, "", nil, "", "");
+ for (pairs:= this.pairs; pairs != nil; pairs = tl pairs) {
+ (name, value) := hd pairs;
+ case (name) {
+ "desc" =>
+ item.desc = value;
+
+ "driver" =>
+ item.driver = value;
+
+ "hpmapfile" =>
+ item.hpmapfile = value;
+
+ "modes" =>
+ item.modes = make_pmode_list(value);
+
+ * =>
+ sys->fprint(stderr, "Unknown ptype config file option: %s\n", name);
+ }
+ }
+ if (item.modes == nil) {
+ sys->fprint(stderr, "No print modes for ptype %s\n", item.name);
+ continue;
+ }
+ rlist = item :: rlist;
+ }
+ for (al:=aliases; al!=nil; al=tl al) {
+ (new, old) := hd al;
+ olda := find_ptype(rlist, old);
+ if (olda == nil) sys->fprint(stderr, "Ptype alias %s not found\n", old);
+ else {
+ newa := ref *olda;
+ newa.name = new;
+ rlist = newa :: rlist;
+ }
+ }
+ return rlist;
+}
+
+
+# Make a list of pmodes from a string
+
+make_pmode_list(sl: string): list of ref Pmode
+{
+ pml: list of ref Pmode;
+ (n, toks) := sys->tokenize(sl, " \t");
+ if (n == 0) return nil;
+ for (i:=0; i<n; i++) {
+ pms := hd toks;
+ toks = tl toks;
+ pm := find_pmode(all_pmodes, pms);
+ if (pm == nil) {
+ sys->fprint(stderr, "unknown pmode: %s\n", pms);
+ continue;
+ }
+ pml = pm :: pml;
+ }
+ return pml;
+}
+
+
+# Read popt config file
+
+read_popt_config(): list of ref Popt
+{
+ (clist, aliases) := read_config(POPT_CONFIG);
+ rlist: list of ref Popt;
+ while (clist != nil) {
+ this := hd clist;
+ clist = tl clist;
+ item := ref Popt(this.name, nil, nil, 0, 0);
+ for (pairs:= this.pairs; pairs != nil; pairs = tl pairs) {
+ (name, value) := hd pairs;
+ case (name) {
+
+ "mode" =>
+ item.mode = find_pmode(all_pmodes, value);
+ if (item.mode == nil) sys->fprint(stderr, "Config error: Pmode not found: %s\n", value);
+
+ "paper" =>
+ item.paper = find_paper(all_papers, value);
+ if (item.paper == nil) sys->fprint(stderr, "Config error: paper not found: %s\n", value);
+
+ "orientation" =>
+ item.orientation = int value;
+ "duplex" =>
+ item.duplex = int value;
+
+ * =>
+ sys->fprint(stderr, "Unknown popt config file option: %s\n", name);
+ }
+ }
+ if (item.mode == nil) {
+ sys->fprint(stderr, "No print mode for printer %s\n", item.name);
+ continue;
+ }
+ if (item.paper == nil) {
+ sys->fprint(stderr, "No paper size for printer %s\n", item.name);
+ continue;
+ }
+ rlist = item :: rlist;
+ }
+ for (al:=aliases; al!=nil; al=tl al) {
+ (new, old) := hd al;
+ olda := find_popt(rlist, old);
+ if (olda == nil) sys->fprint(stderr, "Popt alias %s not found\n", old);
+ else {
+ newa := ref *olda;
+ newa.name = new;
+ rlist = newa :: rlist;
+ }
+ }
+ return rlist;
+}
+
+
+
+
+# Read printer config file
+
+read_printer_config(): list of ref Printer
+{
+ (clist, aliases) := read_config(PRINTER_CONFIG);
+ rlist: list of ref Printer;
+ while (clist != nil) {
+ this := hd clist;
+ clist = tl clist;
+ item := ref Printer(this.name, nil, "", nil, nil);
+ for (pairs:= this.pairs; pairs != nil; pairs = tl pairs) {
+ (name, value) := hd pairs;
+ case (name) {
+ "ptype" =>
+ item.ptype = find_ptype(all_ptypes, value);
+ if (item.ptype == nil) sys->fprint(stderr, "Config error: Ptype not found: %s\n", value);
+
+ "device" =>
+ item.device = value;
+
+ * =>
+ sys->fprint(stderr, "Unknown printer config file option: %s\n", name);
+ }
+ }
+ if (item.ptype == nil) {
+ sys->fprint(stderr, "No printer type for printer %s\n", item.name);
+ continue;
+ }
+ rlist = item :: rlist;
+ }
+ for (al:=aliases; al!=nil; al=tl al) {
+ (new, old) := hd al;
+ olda := find_printer(rlist, old);
+ if (olda == nil) sys->fprint(stderr, "Ptype alias %s not found\n", old);
+ else {
+ newa := ref *olda;
+ newa.name = new;
+ rlist = newa :: rlist;
+ }
+ }
+ return rlist;
+}
+
+# Write opt config file
+
+write_popt_config(plist: list of ref Popt): int
+{
+ cfl: list of Cfg;
+ for (pl:=plist; pl!=nil; pl=tl pl) {
+ po := hd pl;
+ cf := Cfg(po.name, nil);
+ cf.pairs = ("mode", po.mode.name) :: cf.pairs;
+ cf.pairs = ("paper", po.paper.name) :: cf.pairs;
+ cf.pairs = ("orientation", sys->sprint("%d", po.orientation)) :: cf.pairs;
+ cf.pairs = ("duplex", sys->sprint("%d", po.duplex)) :: cf.pairs;
+ cfl = cf :: cfl;
+ }
+ return write_config(POPT_CONFIG, cfl, nil);
+}
+
+
+write_config(fspec: string, clist: list of Cfg, aliases: list of (string, string)): int
+{
+ fd := sys->create(fspec, Sys->OWRITE, DEFMODE);
+ if (fd == nil) {
+ sys->fprint(stderr, "Failed to write to config file %s: %r\n", fspec);
+ return 1;
+ }
+ for (cfl:=clist; cfl!=nil; cfl=tl cfl) {
+ cf := hd cfl;
+ sys->fprint(fd, "%s=\n", cf.name);
+ for (pl:=cf.pairs; pl!=nil; pl=tl pl) {
+ (name, value) := hd pl;
+ if (sys->fprint(fd, "\t%s=%s\n", name, value) < 0) return 2;
+ }
+ }
+ for (al:=aliases; al!=nil; al=tl al) {
+ (new, old) := hd al;
+ if (sys->fprint(fd, "%s=%s\n", new, old)) return 2;
+ }
+ return 0;
+}
+
+
+# Read in a config file and return list of items and aliases
+
+read_config(fspec: string): (list of Cfg, list of (string, string))
+{
+ ib := bufio->open(fspec, Bufio->OREAD);
+ if (ib == nil) {
+ sys->fprint(stderr, "Failed to open config file %s: %r\n", fspec);
+ return (nil, nil);
+ }
+ clist: list of Cfg;
+ plist: list of (string, string);
+ section := "";
+ aliases : list of (string, string);
+ while ((line := bufio->ib.gets('\n')) != nil) {
+ if (line[0] == '#') continue;
+ if (line[len line-1] == '\n') line = line[:len line-1];
+ if (len line == 0) continue;
+ if (line[0] != ' ' && line[0] != '\t') {
+ if (section != "") clist = Cfg (section, plist) :: clist;
+ section = "";
+ plist = nil;
+ sspec := strip(line);
+ (n, toks) := sys->tokenize(sspec, "=");
+ if (n == 0) continue;
+ if (n > 2) {
+ sys->fprint(stderr, "Error in config file %s\n", fspec);
+ continue;
+ }
+ if (n == 2) {
+ asection := hd toks;
+ toks = tl toks;
+ alias := hd toks;
+ aliases = (asection, alias) :: aliases;
+ continue;
+ }
+ section = hd toks;
+ } else {
+ (n, toks) := sys->tokenize(line, "=");
+ if (n == 2) {
+ name := strip(hd toks);
+ toks = tl toks;
+ value := strip(hd toks);
+ plist = (name, value) :: plist;
+ }
+ }
+ }
+ if (section != "") clist = Cfg (section, plist) :: clist;
+ return (clist, aliases);
+}
+
+
+# Load printer driver if necessary
+load_driver(p: ref Printer)
+{
+ if (p.pdriver != nil) return;
+ modpath := Pdriver->PATHPREFIX + p.ptype.driver;
+ p.pdriver = load Pdriver modpath;
+ if (p.pdriver == nil) sys->fprint(stderr, "Failed to load driver %s: %r\n", modpath);
+ p.pdriver->init(DEBUG);
+}
+
+
+# Strip leading/trailing spaces
+
+strip(s: string): string
+{
+ (dummy1, s1) := str->splitl(s, "^ \t");
+ (s2, dummy2) := str->splitr(s1, "^ \t");
+ return s2;
+}
diff --git a/appl/lib/print/scaler.b b/appl/lib/print/scaler.b
new file mode 100644
index 00000000..fd33b591
--- /dev/null
+++ b/appl/lib/print/scaler.b
@@ -0,0 +1,186 @@
+implement Scaler;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "print.m";
+include "scaler.m";
+
+DEBUG := 0;
+
+# Scaler initialisation
+
+init(debug: int, WidthInPixels, ScaleFactorMultiplier, ScaleFactorDivisor: int): ref RESSYNSTRUCT
+{
+ DEBUG = debug;
+ ScaleFactor := real ScaleFactorMultiplier / real ScaleFactorDivisor;
+ ScaleBound := int ScaleFactor;
+ if (ScaleFactor > real ScaleBound) ScaleBound++;
+ ResSynStruct := ref RESSYNSTRUCT (
+ WidthInPixels+2, # add 2 for edges
+ ScaleFactorMultiplier,
+ ScaleFactorDivisor,
+ ScaleFactor,
+ int ((real WidthInPixels / real ScaleFactorDivisor))*ScaleFactorMultiplier + 1,
+ ScaleFactorMultiplier != ScaleFactorDivisor,
+ ScaleFactor < 2.0,
+ (ScaleFactorMultiplier * 256 / ScaleFactorDivisor)
+ - ((ScaleFactorMultiplier/ScaleFactorDivisor) * 256),
+ 0,
+ 0,
+ array[NUMBER_RASTERS] of array of int,
+ array[ScaleBound] of array of int,
+ 0,
+ 0
+ );
+ if (ResSynStruct.ScaleFactor > real ScaleBound) ScaleBound++;
+ for (i:=0; i<len ResSynStruct.Buffer; i++) ResSynStruct.Buffer[i] = array[WidthInPixels*NUMBER_RASTERS] of int;
+ for (i=0; i<len ResSynStruct.oBuffer; i++) ResSynStruct.oBuffer[i] = array[ResSynStruct.iOutputWidth] of int;
+ return ResSynStruct;
+}
+
+
+# Input a raster line to the scaler
+
+rasterin(rs: ref RESSYNSTRUCT, inraster: array of int)
+{
+ if (!rs.scaling) { # Just copy to output buffer
+ if (inraster == nil) return;
+ rs.oBuffer[0] = inraster;
+ rs.nready = 1;
+ rs.ndelivered = 0;
+ return;
+ }
+
+ if (rs.ReplicateOnly) { # for scaling between 1 and 2
+# for (i:=0; i<len inraster; i++) rs.oBuffer[0][i] = inraster[i];
+ rs.oBuffer[0][:] = inraster[0:];
+ create_out(rs, 1);
+ return;
+ }
+
+ if (rs.RastersinBuffer == 0) { # First time through
+ if (inraster == nil) return;
+ for (i:=0; i<2; i++) {
+ rs.Buffer[i][0] = inraster[0];
+# for (j:=1; j<rs.Width-1; j++) rs.Buffer[i][j] = inraster[j-1];
+ rs.Buffer[i][1:] = inraster[0:rs.Width-2];
+ rs.Buffer[i][rs.Width-1] = inraster[rs.Width-3];
+ }
+ rs.RastersinBuffer = 2;
+ return;
+ }
+
+ if (rs.RastersinBuffer == 2) { # Just two buffers in so far
+ if (inraster != nil) {
+ i := 2;
+ rs.Buffer[i][0] = inraster[0];
+# for (j:=1; j<rs.Width-1; j++) rs.Buffer[i][j] = inraster[j-1];
+ rs.Buffer[i][1:] = inraster[0:rs.Width-2];
+ rs.Buffer[i][rs.Width-1] = inraster[rs.Width-3];
+ rs.RastersinBuffer = 3;
+ } else { # nil means end of image
+ rez_synth(rs, rs.oBuffer[0], rs.oBuffer[1]);
+ create_out(rs, 0);
+ }
+ return;
+ }
+ if (rs.RastersinBuffer == 3) { # All three buffers are full
+ (rs.Buffer[0], rs.Buffer[1], rs.Buffer[2]) = (rs.Buffer[1], rs.Buffer[2], rs.Buffer[0]);
+ if (inraster != nil) {
+ i := 2;
+ rs.Buffer[i][0] = inraster[0];
+# for (j:=1; j<rs.Width-1; j++) rs.Buffer[i][j] = inraster[j-1];
+ rs.Buffer[i][1:] = inraster[0:rs.Width-2];
+ rs.Buffer[i][rs.Width-1] = inraster[rs.Width-3];
+ } else { # nil means end of image
+# for (j:=0; j<len rs.Buffer[1]; j++) rs.Buffer[2][j] = rs.Buffer[1][j];
+ rs.Buffer[2][:] = rs.Buffer[1];
+ rs.RastersinBuffer = 0;
+
+ }
+ rez_synth(rs, rs.oBuffer[0], rs.oBuffer[1]);
+ create_out(rs, 0);
+ }
+
+}
+
+
+# Get a raster output line from the scaler
+
+rasterout(rs: ref RESSYNSTRUCT): array of int
+{
+ if (rs.nready-- > 0) {
+ return rs.oBuffer[rs.ndelivered++][:rs.iOutputWidth-1];
+ } else return nil;
+}
+
+
+
+# Create output raster
+
+create_out(rs: ref RESSYNSTRUCT, simple: int)
+{
+ factor: int;
+ if (simple) factor = 1;
+ else factor = 2;
+
+ out_width := (rs.Width-2) * rs.ScaleFactorMultiplier / rs.ScaleFactorDivisor;
+ number_out := rs.ScaleFactorMultiplier / rs.ScaleFactorDivisor;
+ if (number_out == 2 && !(rs.ScaleFactorMultiplier % rs.ScaleFactorDivisor) ) {
+ rs.nready = 2;
+ rs.ndelivered = 0;
+ return;
+ }
+
+ if (rs.ScaleFactorMultiplier % rs.ScaleFactorDivisor)
+ {
+ rs.Remainder = rs.Remainder + rs.Repeat;
+
+ if (rs.Remainder >= 256) # send extra raster
+ {
+ number_out++;
+ rs.Remainder = rs.Remainder - 256;
+ }
+ }
+ # set up pointers into the output buffer
+ output_raster := array[number_out] of array of int;
+ output_raster[:] = rs.oBuffer[0:number_out];
+
+ ScaleFactorMultiplier := rs.ScaleFactorMultiplier;
+ ScaleFactorDivisor := rs.ScaleFactorDivisor;
+ sf := factor * ScaleFactorDivisor;
+
+ # Convert the input data by starting at the bottom right hand corner and move left + up
+ for (i:=(number_out-1); i>=0; i--) {
+ y_index := i*sf/ScaleFactorMultiplier;
+ orast_i := output_raster[i];
+ orast_y := output_raster[y_index];
+ for (lx := out_width-1; lx>=0; --lx) {
+ x_index := lx*sf/ScaleFactorMultiplier;
+ orast_i[lx] = orast_y[x_index];
+ }
+ }
+
+ rs.nready = number_out;
+ rs.ndelivered = 0;
+ return;
+}
+
+
+# Synthesise raster line
+
+rez_synth(rs: ref RESSYNSTRUCT, output_raster0, output_raster1: array of int)
+{
+
+ i := 1;
+ Buffer := rs.Buffer[i];
+ h_offset := 0;
+ for (j:=1; j<rs.Width-1; j++) {
+ rgb := Buffer[j];
+ output_raster0[h_offset] = rgb;
+ output_raster1[h_offset++] = rgb;
+ output_raster0[h_offset] = rgb;
+ output_raster1[h_offset++] = rgb;
+ }
+}
diff --git a/appl/lib/print/scaler.m b/appl/lib/print/scaler.m
new file mode 100644
index 00000000..e73dfdb4
--- /dev/null
+++ b/appl/lib/print/scaler.m
@@ -0,0 +1,30 @@
+Scaler: module
+{
+ PATH: con "/dis/lib/print/scaler.dis";
+
+ init: fn(debug: int, WidthInPixels, ScaleFactorMultiplier, ScaleFactorDivisor: int): ref RESSYNSTRUCT;
+ rasterin: fn(rs: ref RESSYNSTRUCT, inraster: array of int);
+ rasterout: fn(rs: ref RESSYNSTRUCT ): array of int;
+
+ RESSYNSTRUCT: adt {
+ Width: int;
+ ScaleFactorMultiplier: int;
+ ScaleFactorDivisor: int;
+ ScaleFactor: real;
+ iOutputWidth: int;
+ scaling: int;
+ ReplicateOnly: int;
+ Repeat: int;
+ RastersinBuffer: int;
+ Remainder: int;
+ Buffer: array of array of int;
+ oBuffer: array of array of int;
+ nready: int;
+ ndelivered: int;
+ };
+
+
+};
+
+
+NUMBER_RASTERS: con 3; # no of rasters to buffer
diff --git a/appl/lib/profile.b b/appl/lib/profile.b
new file mode 100644
index 00000000..20ad94b3
--- /dev/null
+++ b/appl/lib/profile.b
@@ -0,0 +1,1230 @@
+implement Profile;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "workdir.m";
+ workdir: Workdir;
+include "debug.m";
+ debug: Debug;
+ Sym: import debug;
+include "dis.m";
+ dism: Dis;
+include "profile.m";
+
+# merge common code
+
+PROF: con "/prof";
+CTL: con "ctl";
+NAME: con "name";
+MPATH: con "path";
+HISTOGRAM: con "histogram";
+
+inited: int;
+modl: string;
+lasterr: string;
+
+bspath := array[] of
+{
+ ("/dis/", "/appl/cmd/"),
+ ("/dis/", "/appl/"),
+};
+
+error(s: string)
+{
+ lasterr = sys->sprint("%s: %r", s);
+}
+
+error0(s: string)
+{
+ lasterr = s;
+}
+
+cleare()
+{
+ lasterr = nil;
+}
+
+lasterror(): string
+{
+ return lasterr;
+}
+
+init(): int
+{
+ cleare();
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ debug = load Debug Debug->PATH;
+ if(debug == nil){
+ error("cannot load Debug module");
+ return -1;
+ }
+ debug->init();
+ (ok, nil) := sys->stat(PROF + "/ctl");
+ if (ok == -1) {
+ if(sys->bind("#P", PROF, Sys->MREPL|Sys->MCREATE) < 0){
+ error(sys->sprint("cannot bind prof device to /prof"));
+ return -1;
+ }
+ }
+ inited = 1;
+ return 0;
+}
+
+end(): int
+{
+ cleare();
+ inited = 0;
+ modl = nil;
+ if(write(mkpath(PROF, CTL), "end") < 0)
+ return -1;
+ return 0;
+}
+
+start(): int
+{
+ cleare();
+ if(!inited && init() < 0)
+ return -1;
+ if(write(mkpath(PROF, CTL), "module " + modl) < 0)
+ return -1;
+ if(write(mkpath(PROF, CTL), "start") < 0)
+ return -1;
+ return 0;
+}
+
+cpstart(pid: int): int
+{
+ cleare();
+ if(!inited && init() < 0)
+ return -1;
+ if(write(mkpath(PROF, CTL), "module " + modl) < 0)
+ return -1;
+ if(write(mkpath(PROF, CTL), "startcp " + string pid) < 0)
+ return -1;
+ return 0;
+}
+
+memstart(m: int): int
+{
+ cleare();
+ if(!inited && init() < 0)
+ return -1;
+ if(modl != nil && write(mkpath(PROF, CTL), "module " + modl) < 0)
+ return -1;
+ start := "startmp";
+ if(m == 0)
+ m = MAIN|HEAP|IMAGE;
+ if(m&MAIN)
+ start += "1";
+ if(m&HEAP)
+ start += "2";
+ if(m&IMAGE)
+ start += "3";
+ if(write(mkpath(PROF, CTL), start) < 0)
+ return -1;
+ return 0;
+}
+
+stop(): int
+{
+ cleare();
+ if(!inited && init() < 0)
+ return -1;
+ if(write(mkpath(PROF, CTL), "stop") < 0)
+ return -1;
+ return 0;
+}
+
+sample(i: int): int
+{
+ cleare();
+ if(i <= 0){
+ error0(sys->sprint("bad sample rate %d", i));
+ return -1;
+ }
+ if(write(mkpath(PROF, CTL), "interval " + string i) < 0)
+ return -1;
+ return 0;
+}
+
+profile(m: string): int
+{
+ cleare();
+ modl = m + " " + modl;
+ return 0;
+}
+
+stats(): Prof
+{
+ mp: Modprof;
+ p: Prof;
+ mpl: list of Modprof;
+
+ cleare();
+ fd := sys->open(PROF, Sys->OREAD);
+ if(fd == nil){
+ error(sys->sprint("cannot open %s for reading", PROF));
+ return (nil, 0, nil);
+ }
+ total := 0;
+ for(;;){
+ (nr, d) := sys->dirread(fd);
+ if(nr <= 0)
+ break;
+ for(i := 0; i < nr; i++){
+ if(d[i].name == CTL)
+ continue;
+ dn := mkpath(PROF, d[i].name);
+ mp.name = read(mkpath(dn, NAME));
+ mp.path = read(mkpath(dn, MPATH));
+ fdh := sys->open(mkpath(dn, HISTOGRAM), Sys->OREAD);
+ if(fdh == nil)
+ continue;
+ (mp.srcpath, mp.linetab, mp.funtab, mp.total) = tprofile(fdh, mp.path);
+ if((sp := getb(mp.path)) != nil)
+ mp.srcpath = sp;
+ if(mp.total != 0){
+ mpl = mp :: mpl;
+ total += mp.total;
+ }
+ }
+ }
+ p.mods = mpl;
+ p.total = total;
+ return p;
+}
+
+cpstats(rec: int, v: int): Prof
+{
+ m: string;
+ mp: Modprof;
+ p: Prof;
+ mpl: list of Modprof;
+
+ cleare();
+ fd := sys->open(PROF, Sys->OREAD);
+ if(fd == nil){
+ error(sys->sprint("cannot open %s for reading", PROF));
+ return (nil, 0, nil);
+ }
+ total := 0;
+ for(;;){
+ (nr, d) := sys->dirread(fd);
+ if(nr <= 0)
+ break;
+ for(i:=0; i<nr; i++){
+ if(d[i].name == CTL)
+ continue;
+ dn := mkpath(PROF, d[i].name);
+ mp.name = read(mkpath(dn, NAME));
+ mp.path = read(mkpath(dn, MPATH));
+ fdh := sys->open(mkpath(dn, HISTOGRAM), Sys->OREAD);
+ if(fdh == nil)
+ continue;
+ (m, mp.srcpath, mp.rawtab, mp.linetab, mp.rngtab, mp.total, mp.coverage) = cprofile(fdh, mp.path, rec, v);
+ if(mp.name == nil)
+ mp.name = m;
+ if((sp := getb(mp.path)) != nil)
+ mp.srcpath = sp;
+ if(len mp.rawtab > 0){
+ mpl = mp :: mpl;
+ total += mp.total;
+ }
+ }
+ }
+ p.mods = mpl;
+ p.total = total;
+ return p;
+}
+
+cpfstats(v: int): Prof
+{
+ mp: Modprof;
+ p: Prof;
+ mpl: list of Modprof;
+
+ cleare();
+ total := 0;
+ (nil, l) := sys->tokenize(modl, " ");
+ for( ; l != nil; l = tl l){
+ s := hd l;
+ suf := suff(s);
+ if(suf == nil)
+ s += ".dis";
+ else
+ s = repsuff(s, "."+suf, ".dis");
+ if(!exists(s) && s[0] != '/' && s[0:2] != "./")
+ s = "/dis/"+s;
+ mp.path = s;
+ (mp.name, mp.srcpath, mp.rawtab, mp.linetab, mp.rngtab, mp.total, mp.coverage) = cprofile(nil, mp.path, 1, v);
+ if((sp := getb(mp.path)) != nil)
+ mp.srcpath = sp;
+ if(len mp.rawtab > 0){
+ mpl = mp :: mpl;
+ total += mp.total;
+ }
+ }
+ p.mods = mpl;
+ p.total = total;
+ return p;
+}
+
+memstats(): Prof
+{
+ mp: Modprof;
+ p: Prof;
+ mpl: list of Modprof;
+
+ cleare();
+ fd := sys->open(PROF, Sys->OREAD);
+ if(fd == nil){
+ error(sys->sprint("cannot open %s for reading", PROF));
+ return (nil, 0, nil);
+ }
+ total := totale := 0;
+ for(;;){
+ (nr, d) := sys->dirread(fd);
+ if(nr <= 0)
+ break;
+ for(i:=0; i<nr; i++){
+ if(d[i].name == CTL)
+ continue;
+ dn := mkpath(PROF, d[i].name);
+ mp.name = read(mkpath(dn, NAME));
+ mp.path = read(mkpath(dn, MPATH));
+ fdh := sys->open(mkpath(dn, HISTOGRAM), Sys->OREAD);
+ if(fdh == nil)
+ continue;
+ mp.totals = array[1] of int;
+ (mp.srcpath, mp.linetab, mp.funtab, mp.total, mp.totals[0]) = mprofile(fdh, mp.path);
+ if((sp := getb(mp.path)) != nil)
+ mp.srcpath = sp;
+ if(mp.total != 0 || mp.totals[0] != 0){
+ mpl = mp :: mpl;
+ total += mp.total;
+ totale += mp.totals[0];
+ }
+ }
+ }
+ p.mods = mpl;
+ p.total = total;
+ p.totals = array[1] of int;
+ p.totals[0] = totale;
+ return p;
+}
+
+tprofile(fd: ref Sys->FD, dis: string): (string, array of int, array of Funprof, int)
+{
+ sbl := findsbl(dis);
+ if(sbl == nil){
+ error0(sys->sprint("cannot locate symbol table file for %s", dis));
+ return (nil, nil, nil, 0);
+ }
+ (sym, err) := debug->sym(sbl);
+ if(sym == nil){
+ error0(sys->sprint("bad symbol table file: %s", err));
+ return (nil, nil, nil, 0);
+ }
+ nlines := 0;
+ nl := len sym.src;
+ for(i := 0; i < nl; i++){
+ if((l := sym.src[i].stop.line) > nlines)
+ nlines = l;
+ }
+ name := sym.src[0].start.file;
+ line := array[nlines+1] of int;
+ for(i = 0; i <= nlines; i++)
+ line[i] = 0;
+ nf := len sym.fns;
+ fun := array[nf] of Funprof;
+ for(i = 0; i < nf; i++){
+ fun[i].name = sym.fns[i].name;
+ # src seems to be always nil
+ # fun[i].file = sym.fns[i].src.start.file;
+ # fun[i].line = (sym.fns[i].src.start.line+sym.fns[i].src.stop.line)/2;
+ src := sym.pctosrc(sym.fns[i].offset);
+ if(src != nil)
+ fun[i].line = src.start.line;
+ else
+ fun[i].line = 0;
+ fun[i].count = 0;
+ }
+ buf := array[32] of byte;
+ # pc := 0;
+ tot := 0;
+ fi := 0;
+# for(i=0; i < nl; i++) sys->print("%d -> %d\n", i, sym.pctosrc(i).start.line);
+ while((m := sys->read(fd, buf, len buf)) > 0){
+ (nw, lw) := sys->tokenize(string buf[0:m], " ");
+ if(nw != 2){
+ error0("bad histogram data");
+ return (nil, nil, nil, 0);
+ }
+ pc := int hd lw;
+ f := int hd tl lw;
+ rpc := pc-1;
+ src := sym.pctosrc(rpc);
+ if(src == nil)
+ continue;
+ l1 := src.start.line;
+ l2 := src.stop.line;
+ if(l1 == 0 || l2 == 0)
+ continue;
+ if((nl = l2-l1+1) == 1)
+ line[l1] += f;
+ else{
+ q := f/nl;
+ r := f-q*nl;
+ for(i = l1; i <= l2; i++)
+ line[i] += q+(r-->0);
+ }
+ if(fi < nf){
+ if(rpc >= sym.fns[fi].offset && rpc < sym.fns[fi].stoppc)
+ fun[fi].count += f;
+ else{
+ while(fi < nf && rpc >= sym.fns[fi].stoppc)
+ fi++;
+ # fi++;
+ if(fi >= nf && f != 0)
+ error0(sys->sprint("bad fn index"));
+ if(fi < nf)
+ fun[fi].count += f;
+ }
+ }
+ tot += f;
+# sys->print("pc %d count %d l1 %d l2 %d\n", rpc, f, l1, l2);
+ }
+ return (name, line, fun, tot);
+}
+
+cprofile(fd: ref Sys->FD, dis: string, rec: int, v: int): (string, string, array of (int, int), array of int, array of ref Range, int, int)
+{
+ freq := v&FREQUENCY;
+ sbl := findsbl(dis);
+ if(sbl == nil){
+ error0(sys->sprint("cannot locate symbol table file for %s", dis));
+ return (nil, nil, nil, nil, nil, 0, 0);
+ }
+ (sym, err) := debug->sym(sbl);
+ if(sym == nil){
+ error0(sys->sprint("bad symbol table file: %s", err));
+ return (nil, nil, nil, nil, nil, 0, 0);
+ }
+ nlines := 0;
+ nl := len sym.src;
+ for(i := 0; i < nl; i++){
+ if((l := sym.src[i].start.line) > nlines)
+ nlines = l;
+ if((l = sym.src[i].stop.line) > nlines)
+ nlines = l;
+ }
+ name := sym.src[0].start.file;
+ line := array[nlines+1] of int;
+ for(i = 0; i <= nlines; i++){
+ if(freq)
+ line[i] = -1;
+ else
+ line[i] = 0;
+ }
+ rng := array[nlines+1] of ref Range;
+ for(i = 0; i < nl; i++)
+ cover(i, -1, sym, line, rng, freq);
+ buf := array[32] of byte;
+ nr := 0;
+ r := array[1024] of (int, int);
+ while((m := sys->read(fd, buf, len buf)) > 0){
+ (nw, lw) := sys->tokenize(string buf[0:m], " ");
+ if(nw != 2){
+ error0("bad histogram data");
+ return (nil, nil, nil, nil, nil, 0, 0);
+ }
+ (r, nr) = add(r, nr, int hd lw, int hd tl lw);
+ }
+ r = clip(r, nr);
+ if(rec){
+ wt := nr > 0;
+ prf := repsuff(sbl, ".sbl", ".prf");
+ if(exists(prf)){
+ if(stamp(sbl) > stamp(prf)){
+ error0(sys->sprint("%s later than %s", sbl, prf));
+ return (nil, nil, nil, nil, nil, 0, 0);
+ }
+ r = mergeprof(r, readprof(prf));
+ nr = len r;
+ }
+ if(wt && writeprof(prf, r) < 0){
+ error0(sys->sprint("cannot write profile file %s", prf));
+ return (nil, nil, nil, nil, nil, 0, 0);
+ }
+ }
+ tot := 0;
+ lpc := 0;
+ dise := dist := 0;
+ for(i = 0; i < nr; i++){
+ (pc, f) := r[i];
+ for( ; lpc < pc; lpc++){
+ cover(lpc, 0, sym, line, rng, freq);
+ dist++;
+ }
+ cover(pc, f, sym, line, rng, freq);
+ dist++;
+ if(f != 0)
+ dise++;
+ tot += f;
+ lpc = pc+1;
+ }
+ for( ; lpc < nl; lpc++){
+ cover(lpc, 0, sym, line, rng, freq);
+ dist++;
+ }
+ if(dist == 0)
+ dist = 1;
+ return (sym.name, name, r, line, rng, tot, (100*dise)/dist);
+}
+
+show(p: Prof, v: int): int
+{
+ i: int;
+
+ cleare();
+ tot := p.total;
+ if(tot == 0)
+ return 0;
+ verbose := v&VERBOSE;
+ fullhdr := v&FULLHDR;
+ for(ml := p.mods; ml != nil; ml = tl ml){
+ mp := hd ml;
+ if(mp.total == 0)
+ continue;
+ if((b := getb(mp.path)) == nil)
+ continue;
+ sys->print("\nModule: %s(%s)\n\n", mp.name, mp.path);
+ line := mp.linetab;
+ if(v&FUNCTION){
+ fun := mp.funtab;
+ nf := len fun;
+ for(i = 0; i < nf; i++)
+ if(verbose || fun[i].count != 0){
+ if(fullhdr)
+ sys->print("%s:", b);
+ sys->print("%d\t%.2f\t%s()\n", fun[i].line, 100.0*(real fun[i].count)/(real tot), fun[i].name);
+ }
+ sys->print("\n**** module sampling points %d ****\n\n", mp.total);
+ if(v&LINE)
+ sys->print("\n");
+ }
+ if(v&LINE){
+ bio := bufio->open(b, Bufio->OREAD);
+ if(bio == nil){
+ error(sys->sprint("cannot open %s for reading", b));
+ continue;
+ }
+ i = 1;
+ ll := len line;
+ while((s := bio.gets('\n')) != nil){
+ f := 0;
+ if(i < ll)
+ f = line[i];
+ if(verbose || f != 0){
+ if(fullhdr)
+ sys->print("%s:", b);
+ sys->print("%d\t%.2f\t%s", i, 100.0*(real f)/(real tot), s);
+ }
+ i++;
+ }
+ sys->print("\n**** module sampling points %d ****\n\n", mp.total);
+ }
+ }
+ if(p.mods != nil && tl p.mods != nil)
+ sys->print("\n**** total sampling points %d ****\n\n", p.total);
+ return 0;
+}
+
+cpshow(p: Prof, v: int): int
+{
+ i: int;
+
+ cleare();
+ tot := p.total;
+ fullhdr := v&FULLHDR;
+ freq := v&FREQUENCY;
+ for(ml := p.mods; ml != nil; ml = tl ml){
+ mp := hd ml;
+ if((b := getb(mp.path)) == nil)
+ continue;
+ sys->print("\nModule: %s(%s)", mp.name, mp.path);
+ sys->print("\t%d%% coverage\n\n", mp.coverage);
+ if(mp.coverage == 100 && !freq)
+ continue;
+ line := mp.linetab;
+ rng := mp.rngtab;
+ bio := bufio->open(b, Bufio->OREAD);
+ if(bio == nil){
+ error(sys->sprint("cannot open %s for reading", b));
+ continue;
+ }
+ i = 1;
+ ll := len line;
+ while((s := bio.gets('\n')) != nil){
+ f := 0;
+ if(i < ll)
+ f = line[i];
+ if(fullhdr)
+ sys->print("%s:", b);
+ sys->print("%d\t", i);
+ if(rng != nil && i < ll && (r := rng[i]) != nil && multirng(r)){
+ for( ; r != nil; r = r.n){
+ sys->print("%s", trans(r.f, freq));
+ if(r.n != nil)
+ sys->print("|");
+ }
+ }
+ else
+ sys->print("%s", trans(f, freq));
+ sys->print("\t%s", s);
+ i++;
+ }
+ sys->print("\n**** module dis instructions %d ****\n\n", mp.total);
+ }
+ if(p.mods != nil && tl p.mods != nil)
+ sys->print("\n**** total number dis instructions %d ****\n\n", p.total);
+ return 0;
+}
+
+coverage(p: Prof, v: int): Coverage
+{
+ i: int;
+ clist: Coverage;
+
+ cleare();
+ freq := v&FREQUENCY;
+ for(ml := p.mods; ml != nil; ml = tl ml){
+ mp := hd ml;
+ if((b := getb(mp.path)) == nil)
+ continue;
+ line := mp.linetab;
+ rng := mp.rngtab;
+ bio := bufio->open(b, Bufio->OREAD);
+ if(bio == nil){
+ error(sys->sprint("cannot open %s for reading", b));
+ continue;
+ }
+ i = 1;
+ ll := len line;
+ llist: list of (list of (int, int, int), string);
+ while((s := bio.gets('\n')) != nil){
+ f := 0;
+ if(i < ll)
+ f = line[i];
+ rlist: list of (int, int, int);
+ if(rng != nil && i < ll && (r := rng[i]) != nil){
+ for( ; r != nil; r = r.n){
+ if(r.u == ∞)
+ r.u = len s - 1;
+ if(freq){
+ if(r.f > 0)
+ rlist = (r.l, r.u, r.f) :: rlist;
+ }
+ else{
+ if(r.f&NEX)
+ rlist = (r.l, r.u, (r.f&EXE)==EXE) :: rlist;
+ }
+ }
+ }
+ else{
+ if(freq){
+ if(f > 0)
+ rlist = (0, len s - 1, f) :: rlist;
+ }
+ else{
+ if(f&NEX)
+ rlist = (0, len s - 1, (f&EXE)==EXE) :: nil;
+ }
+ }
+ llist = (rlist, s) :: llist;
+ i++;
+ }
+ if(freq)
+ n := mp.total;
+ else
+ n = mp.coverage;
+ clist = (b, n, rev(llist)) :: clist;
+ }
+ return clist;
+}
+
+∞: con 1<<30;
+
+DIS: con 1;
+EXE: con 2;
+NEX: con 4;
+
+cover(pc: int, f: int, sym: ref Debug->Sym, line: array of int, rng: array of ref Range, freq: int)
+{
+ v: int;
+
+ src := sym.pctosrc(pc);
+ if(src == nil)
+ return;
+ l1 := src.start.line;
+ l2 := src.stop.line;
+ if(l1 == 0 || l2 == 0)
+ return;
+ c1 := src.start.pos;
+ c2 := src.stop.pos;
+ if(freq){
+ v = 0;
+ if(f > 0)
+ v = f;
+ }
+ else{
+ v = DIS;
+ if(f > 0)
+ v = EXE;
+ else if(f == 0)
+ v = NEX;
+ }
+ for(i := l1; i <= l2; i++){
+ r1 := 0;
+ r2 := ∞;
+ if(i == l1)
+ r1 = c1;
+ if(i == l2)
+ r2 = c2;
+ if(rng != nil)
+ rng[i] = mrgrng(addrng(rng[i], r1, r2, v, freq));
+ if(freq){
+ if(v > line[i])
+ line[i] = v;
+ }
+ else
+ line[i] |= v;
+ # if(i==123) sys->print("%d %d-%d %d %d\n", i, r1, r2, v, pc);
+ }
+}
+
+arng(c1: int, c2: int, f: int, tr: ref Range, lr: ref Range, r: ref Range): ref Range
+{
+ nr := ref Range(c1, c2, f, tr);
+ if(lr == nil)
+ r = nr;
+ else
+ lr.n = nr;
+ return r;
+}
+
+addrng(r: ref Range, c1: int, c2: int, f: int, freq: int): ref Range
+{
+ lr: ref Range;
+
+ if(c1 > c2)
+ return r;
+ for(tr := r; tr != nil; tr = tr.n){
+ r1 := tr.l;
+ r2 := tr.u;
+ if(c1 < r1){
+ if(c2 < r1)
+ return arng(c1, c2, f, tr, lr, r);
+ else if(c2 <= r2){
+ r = addrng(r, c1, r1-1, f, freq);
+ return addrng(r, r1, c2, f, freq);
+ }
+ else{
+ r = addrng(r, c1, r1-1, f, freq);
+ r = addrng(r, r1, r2, f, freq);
+ return addrng(r, r2+1, c2, f, freq);
+ }
+ }
+ else if(c1 <= r2){
+ if(c2 <= r2){
+ v := tr.f;
+ tr.l = c1;
+ tr.u = c2;
+ if(freq){
+ if(f > tr.f)
+ tr.f = f;
+ }
+ else
+ tr.f |= f;
+ r = addrng(r, r1, c1-1, v, freq);
+ return addrng(r, c2+1, r2, v, freq);
+ }
+ else{
+ r = addrng(r, c1, r2, f, freq);
+ return addrng(r, r2+1, c2, f, freq);
+ }
+ }
+ lr = tr;
+ }
+ return arng(c1, c2, f, nil, lr, r);
+}
+
+mrgrng(r: ref Range): ref Range
+{
+ lr: ref Range;
+
+ for(tr := r; tr != nil; tr = tr.n){
+ if(lr != nil && lr.u >= tr.l)
+ sys->print("ERROR %d %d\n", lr.u, tr.l);
+ if(lr != nil && lr.f == tr.f && lr.u+1 == tr.l){
+ lr.u = tr.u;
+ lr.n = tr.n;
+ }
+ else
+ lr = tr;
+ }
+ return r;
+}
+
+multirng(r: ref Range): int
+{
+ f := r.f;
+ for(tr := r; tr != nil; tr = tr.n)
+ if(tr.f != f)
+ return 1;
+ return 0;
+}
+
+add(r: array of (int, int), nr: int, pc: int, f: int): (array of (int, int), int)
+{
+ l := len r;
+ if(nr == l){
+ s := array[2*l] of (int, int);
+ s[0:] = r[0: nr];
+ r = s;
+ }
+ r[nr++] = (pc, f);
+ return (r, nr);
+}
+
+clip(r: array of (int, int), nr: int): array of (int, int)
+{
+ l := len r;
+ if(nr < l){
+ s := array[nr] of (int, int);
+ s[0:] = r[0: nr];
+ r = s;
+ }
+ return r;
+}
+
+readprof(f: string): array of (int, int)
+{
+ b := bufio->open(f, Bufio->OREAD);
+ if(b == nil)
+ return nil;
+ nr := 0;
+ r := array[1024] of (int, int);
+ while((buf := b.gets('\n')) != nil){
+ (nw, lw) := sys->tokenize(buf, " ");
+ if(nw != 2){
+ error0("bad raw data");
+ return nil;
+ }
+ (r, nr) = add(r, nr, int hd lw, int hd tl lw);
+ }
+ r = clip(r, nr);
+ return r;
+}
+
+mergeprof(r1, r2: array of (int, int)): array of (int, int)
+{
+ nr := 0;
+ r := array[1024] of (int, int);
+ l1 := len r1;
+ l2 := len r2;
+ for((i, j) := (0, 0); i < l1 || j < l2; ){
+ if(i < l1)
+ (pc1, f1) := r1[i];
+ else
+ pc1 = ∞;
+ if(j < l2)
+ (pc2, f2) := r2[j];
+ else
+ pc2 = ∞;
+ if(pc1 < pc2){
+ (r, nr) = add(r, nr, pc1, f1);
+ i++;
+ }
+ else if(pc1 > pc2){
+ (r, nr) = add(r, nr, pc2, f2);
+ j++;
+ }
+ else{
+ (r, nr) = add(r, nr, pc1, f1+f2);
+ i++;
+ j++;
+ }
+ }
+ r = clip(r, nr);
+ return r;
+}
+
+writeprof(f: string, r: array of (int, int)): int
+{
+ fd := sys->create(f, Sys->OWRITE, 8r664);
+ if(fd == nil)
+ return -1;
+ l := len r;
+ for(i := 0; i < l; i++){
+ (pc, fr) := r[i];
+ sys->fprint(fd, "%d %d\n", pc, fr);
+ }
+ return 0;
+}
+
+trans(f: int, freq: int): string
+{
+ if(freq)
+ return transf(f);
+ else
+ return transc(f);
+}
+
+transf(f: int): string
+{
+ if(f < 0)
+ return " ";
+ return string f;
+}
+
+transc(f: int): string
+{
+ c := "";
+ case(f){
+ 0 => c = " ";
+ DIS|EXE => c = "+";
+ DIS|NEX => c = "-";
+ DIS|EXE|NEX => c = "?";
+ * =>
+ error(sys->sprint("bad code %d\n", f));
+ }
+ return c;
+}
+
+getb(dis: string): string
+{
+ b := findb(dis);
+ if(b == nil){
+ error0(sys->sprint("cannot locate source file for %s\n", dis));
+ return nil;
+ }
+ if(stamp(b) > stamp(dis)){
+ error0(sys->sprint("%s later than %s", b, dis));
+ return nil;
+ }
+ return b;
+}
+
+mkpath(d: string, f: string): string
+{
+ return d+"/"+f;
+}
+
+suff(s: string): string
+{
+ (n, l) := sys->tokenize(s, ".");
+ if(n > 1){
+ while(tl l != nil)
+ l = tl l;
+ return hd l;
+ }
+ return nil;
+}
+
+repsuff(s: string, old: string, new: string): string
+{
+ lo := len old;
+ ls := len s;
+ if(lo <= ls && s[ls-lo:ls] == old)
+ return s[0:ls-lo]+new;
+ return s;
+}
+
+read(f: string): string
+{
+ if((fd := sys->open(f, Sys->OREAD)) == nil){
+ error(sys->sprint("cannot open %s for reading", f));
+ return nil;
+ }
+ buf := array[128] of byte;
+ n := sys->read(fd, buf, len buf);
+ return string buf[0:n];
+}
+
+write(f: string, s: string): int
+{
+ if((fd := sys->open(f, Sys->OWRITE)) == nil){
+ error(sys->sprint("cannot open %s for writing", f));
+ return -1;
+ }
+ b := array of byte s;
+ if((n := sys->write(fd, b, len b)) != len b){
+ error(sys->sprint("cannot write %s to file %s", s, f));
+ return -1;
+ }
+ return 0;
+}
+
+exists(f: string): int
+{
+ return sys->open(f, Sys->OREAD) != nil;
+}
+
+stamp(f: string): int
+{
+ (ok, d) := sys->stat(f);
+ if(ok < 0)
+ return 0;
+ return d.mtime;
+}
+
+findb(dis: string): string
+{
+ if(dism == nil){
+ dism = load Dis Dis->PATH;
+ if(dism != nil)
+ dism->init();
+ }
+ if(dism != nil && (b := dism->src(dis)) != nil && exists(b))
+ return b;
+ return findfile(repsuff(dis, ".dis", ".b"));
+}
+
+findsbl(dis: string): string
+{
+ b := findb(dis);
+ if(b != nil){
+ sbl := repsuff(b, ".b", ".sbl");
+ if(exists(sbl))
+ return sbl;
+ return findfile(sbl);
+ }
+ return findfile(repsuff(dis, ".dis", ".sbl"));
+}
+
+findfile(s: string): string
+{
+ if(exists(s))
+ return s;
+ if(s != nil && s[0] != '/'){
+ if(workdir == nil)
+ workdir = load Workdir Workdir->PATH;
+ if(workdir == nil){
+ error("cannot load Workdir module");
+ return nil;
+ }
+ s = workdir->init() + "/" + s;
+ }
+ (d, f) := split(s, '/');
+ (fp, nil) := split(f, '.');
+ if(fp != nil)
+ fp = fp[0: len fp - 1];
+ for(k := 0; k < 2; k++){
+ if(k == 0)
+ str := s;
+ else
+ str = d;
+ ls := len str;
+ for(i := 0; i < len bspath; i++){
+ (dis, src) := bspath[i];
+ ld := len dis;
+ if(ls >= ld && str[:ld] == dis){
+ if(k == 0)
+ ns := src + str[ld:];
+ else
+ ns = src + str[ld:] + fp + "/" + f;
+ if(exists(ns))
+ return ns;
+ }
+ }
+ }
+ return nil;
+}
+
+split(s: string, c: int): (string, string)
+{
+ for(i := len s - 1; i >= 0; --i)
+ if(s[i] == c)
+ break;
+ return (s[0:i+1], s[i+1:]);
+}
+
+rev(llist: list of (list of (int, int, int), string)): list of (list of (int, int, int), string)
+{
+ r: list of (list of (int, int, int), string);
+
+ for(l := llist; l != nil; l = tl l)
+ r = hd l :: r;
+ return r;
+}
+
+mprofile(fd: ref Sys->FD, dis: string): (string, array of int, array of Funprof, int, int)
+{
+ sbl := findsbl(dis);
+ if(sbl == nil){
+ error0(sys->sprint("cannot locate symbol table file for %s", dis));
+ return (nil, nil, nil, 0, 0);
+ }
+ (sym, err) := debug->sym(sbl);
+ if(sym == nil){
+ error0(sys->sprint("bad symbol table file: %s", err));
+ return (nil, nil, nil, 0, 0);
+ }
+ nlines := 0;
+ nl := len sym.src;
+ for(i := 0; i < nl; i++){
+ if((l := sym.src[i].stop.line) > nlines)
+ nlines = l;
+ }
+ name := sym.src[0].start.file;
+ nl0 := 2*(nlines+1);
+ line := array[nl0] of int;
+ for(i = 0; i < nl0; i++)
+ line[i] = 0;
+ nf := len sym.fns;
+ fun := array[nf] of Funprof;
+ for(i = 0; i < nf; i++){
+ fun[i].name = sym.fns[i].name;
+ # src seems to be always nil
+ # fun[i].file = sym.fns[i].src.start.file;
+ # fun[i].line = (sym.fns[i].src.start.line+sym.fns[i].src.stop.line)/2;
+ src := sym.pctosrc(sym.fns[i].offset);
+ if(src != nil)
+ fun[i].line = src.start.line;
+ else
+ fun[i].line = 0;
+ fun[i].count = fun[i].counte = 0;
+ }
+ buf := array[32] of byte;
+ # pc := 0;
+ ktot := ktot1 := 0;
+ fi := 0;
+# for(i=0; i < nl; i++) sys->print("%d -> %d\n", i, sym.pctosrc(i).start.line);
+ while((m := sys->read(fd, buf, len buf)) > 0){
+ (nw, lw) := sys->tokenize(string buf[0:m], " ");
+ if(nw != 2){
+ error0("bad histogram data");
+ return (nil, nil, nil, 0, 0);
+ }
+ pc := int hd lw;
+ f := int hd tl lw;
+ if(pc == 0){
+ ktot = f;
+ continue;
+ }
+ if(pc == 1){
+ ktot1 = f;
+ continue;
+ }
+ pc -= 2;
+ t := pc&1;
+ pc /= 2;
+ rpc := pc-1;
+ src := sym.pctosrc(rpc);
+ if(src == nil)
+ continue;
+ l1 := src.start.line;
+ l2 := src.stop.line;
+ if(l1 == 0 || l2 == 0)
+ continue;
+ if((nl = l2-l1+1) == 1)
+ line[2*l1+t] += f;
+ else{
+ q := f/nl;
+ r := f-q*nl;
+ for(i = l1; i <= l2; i++)
+ line[2*i+t] += q+(r-->0);
+ }
+ if(fi < nf){
+ if(rpc >= sym.fns[fi].offset && rpc < sym.fns[fi].stoppc){
+ if(t)
+ fun[fi].counte += f;
+ else
+ fun[fi].count += f;
+ }
+ else{
+ while(fi < nf && rpc >= sym.fns[fi].stoppc)
+ fi++;
+ # fi++;
+ if(fi >= nf && f != 0)
+ error0(sys->sprint("bad fn index"));
+ if(fi < nf){
+ if(t)
+ fun[fi].counte += f;
+ else
+ fun[fi].count += f;
+ }
+ }
+ }
+# sys->print("pc %d count %d l1 %d l2 %d\n", rpc, f, l1, l2);
+ }
+ return (name, line, fun, ktot, ktot1);
+}
+
+memshow(p: Prof, v: int): int
+{
+ i: int;
+
+ cleare();
+ tot := p.total;
+ if(p.total == 0 && p.totals[0] == 0)
+ return 0;
+ verbose := v&VERBOSE;
+ fullhdr := v&FULLHDR;
+ for(ml := p.mods; ml != nil; ml = tl ml){
+ mp := hd ml;
+ if(mp.total == 0 && mp.totals[0] == 0)
+ continue;
+ if((b := getb(mp.path)) == nil)
+ continue;
+ sys->print("\nModule: %s(%s)\n\n", mp.name, mp.path);
+ line := mp.linetab;
+ if(v&LINE){
+ bio := bufio->open(b, Bufio->OREAD);
+ if(bio == nil){
+ error(sys->sprint("cannot open %s for reading", b));
+ continue;
+ }
+ i = 1;
+ ll := len line/2;
+ while((s := bio.gets('\n')) != nil){
+ f := g := 0;
+ if(i < ll){
+ f = line[2*i];
+ g = line[2*i+1];
+ }
+ if(verbose || f != 0 || g != 0){
+ if(fullhdr)
+ sys->print("%s:", b);
+ sys->print("%d\t%d\t%d\t%s", i, f, g, s);
+ }
+ i++;
+ }
+ if(v&(FUNCTION|MODULE))
+ sys->print("\n");
+ }
+ if(v&FUNCTION){
+ fun := mp.funtab;
+ nf := len fun;
+ for(i = 0; i < nf; i++)
+ if(verbose || fun[i].count != 0 || fun[i].counte != 0){
+ if(fullhdr)
+ sys->print("%s:", b);
+ sys->print("%d\t%d\t%d\t%s()\n", fun[i].line, fun[i].count, fun[i].counte, fun[i].name);
+ }
+ if(v&MODULE)
+ sys->print("\n");
+ }
+ if(v&MODULE)
+ sys->print("Module totals\t%d\t%d\n\n", mp.total, mp.totals[0]);
+ }
+ if(p.mods != nil && tl p.mods != nil)
+ sys->print("Grand totals\t%d\t%d\n\n", p.total, p.totals[0]);
+ return 0;
+}
diff --git a/appl/lib/pslib.b b/appl/lib/pslib.b
new file mode 100644
index 00000000..a929fc8a
--- /dev/null
+++ b/appl/lib/pslib.b
@@ -0,0 +1,714 @@
+implement Pslib;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+ draw : Draw;
+Image, Display,Rect,Point : import draw;
+
+include "bufio.m";
+ bufmod : Bufio;
+
+include "tk.m";
+ tk: Tk;
+ Toplevel: import tk;
+
+Iobuf : import bufmod;
+
+include "string.m";
+ str : String;
+
+include "daytime.m";
+ time : Daytime;
+
+include "pslib.m";
+
+# old module declaration.
+# this whole thing needs a revamp, so almost all the old external
+# linkages have been removed until there's time to do it properly.
+#Pslib : module
+#{
+# PATH: con "/dis/lib/pslib.dis";
+#
+# init: fn(env: ref Draw->Context, t: ref Tk->Toplevel, boxes: int, deb: int): string;
+# getfonts: fn(input: string): string;
+# preamble: fn(ioutb: ref Bufio->Iobuf, bbox: Draw->Rect): string;
+# trailer: fn(ioutb: ref Bufio->Iobuf, pages: int): string;
+# printnewpage: fn(pagenum: int, end: int, ioutb: ref Bufio->Iobuf);
+# parseTkline: fn(ioutb: ref Bufio->Iobuf, input: string): string;
+# stats: fn(): (int, int, int);
+# deffont: fn(): string;
+# image2psfile: fn(ioutb: ref Bufio->Iobuf, im: ref Draw->Image, dpi: int) : string;
+#};
+
+ASCII,RUNE,IMAGE : con iota;
+
+Iteminfo : adt
+{
+ itype: int;
+ offset: int; # offset from the start of line.
+ width: int; # width....
+ ascent: int; # ascent of the item
+ font: int; # font
+ line : int; # line its on
+ buf : string;
+};
+
+Lineinfo : adt
+{
+ xorg: int;
+ yorg: int;
+ width: int;
+ height: int;
+ ascent: int;
+};
+
+
+font_arr := array[256] of {* => (-1,"")};
+remap := array[20] of (string,string);
+
+PXPI : con 100;
+PTPI : con 100;
+
+boxes: int;
+debug: int;
+totitems: int;
+totlines: int;
+curfont: int;
+def_font: string;
+def_font_type: int;
+curfonttype: int;
+pagestart: int;
+ctxt: ref Draw->Context;
+t: ref Toplevel;
+
+nomod(s: string)
+{
+ sys->print("pslib: cannot load %s: %r\n", s);
+ raise "fail:bad module";
+}
+
+init(bufio: Bufio)
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+ tk= load Tk Tk->PATH;
+ if (tk == nil)
+ nomod(Tk->PATH);
+ str = load String String->PATH;
+ if (str == nil)
+ nomod(String->PATH);
+ bufmod = bufio;
+}
+
+
+oldinit(env: ref Draw->Context, d: ref Toplevel, nil: int,deb: int): string
+{
+ sys = load Sys Sys->PATH;
+ str = load String String->PATH;
+ draw = load Draw Draw->PATH;
+ tk= load Tk Tk->PATH;
+ bufmod = load Bufio Bufio->PATH;
+ ctxt=env;
+ t=d;
+ debug = deb;
+ totlines=0;
+ totitems=0;
+ pagestart=0;
+ boxes=0; #box;
+ curfont=0;
+ e := loadfonts();
+ if (e != "")
+ return e;
+ return "";
+}
+
+stats(): (int,int,int)
+{
+ return (totitems,totlines,curfont);
+}
+
+loadfonts() : string
+{
+ input : string;
+ iob:=bufmod->open("/fonts/psrename",bufmod->OREAD);
+ if (iob==nil)
+ return sys->sprint("can't open /fonts/psrename: %r");
+ i:=0;
+ while((input=iob.gets('\n'))!=nil){
+ (tkfont,psfont):=str->splitl(input," ");
+ psfont=psfont[1:len psfont -1];
+ remap[i]=(tkfont,psfont);
+ i++;
+ }
+ return "";
+}
+
+preamble(ioutb: ref Iobuf, bb: Rect)
+{
+ time = load Daytime Daytime->PATH;
+ username := "";
+ fd := sys->open("/dev/user", sys->OREAD);
+ if(fd != nil) {
+ b := array[128] of byte;
+ n := sys->read(fd, b, len b);
+ b=b[0:n];
+ username = string b;
+ fd = nil;
+ }
+ if(bb.max.x == 0 && bb.max.y == 0) {
+ bb.max.x = 612;
+ bb.max.y = 792;
+ }
+ ioutb.puts("%!PS-Adobe-3.0\n");
+ ioutb.puts(sys->sprint("%%%%Creator: Pslib 1.0 (%s)\n",username));
+ ioutb.puts(sys->sprint("%%%%CreationDate: %s\n",time->time()));
+ ioutb.puts("%%Pages: (atend) \n");
+ ioutb.puts(sys->sprint("%%%%BoundingBox: %d %d %d %d\n", bb.min.x, bb.min.y, bb.max.x, bb.max.y));
+ ioutb.puts("%%EndComments\n");
+ ioutb.puts("%%BeginProlog\n");
+ ioutb.puts("/doimage {\n");
+ ioutb.puts("/bps exch def\n");
+ ioutb.puts("/width exch def\n");
+ ioutb.puts("/height exch def\n");
+ ioutb.puts("/xstart exch def\n");
+ ioutb.puts("/ystart exch def\n");
+ ioutb.puts("/iwidth exch def\n");
+ ioutb.puts("/ascent exch def\n");
+ ioutb.puts("/iheight exch def\n");
+ ioutb.puts("gsave\n");
+ if(boxes)
+ ioutb.puts("xstart ystart iwidth iheight rectstroke\n");
+ # if bps==8, use inferno colormap; else (bps < 8) it's grayscale
+ ioutb.puts("bps 8 eq\n");
+ ioutb.puts("{\n");
+ ioutb.puts("[/Indexed /DeviceRGB 255 \n");
+ ioutb.puts("<ffffff ffffaa ffff55 ffff00 ffaaff ffaaaa ffaa55 ffaa00 ff55ff ff55aa ff5555 ff5500\n");
+ ioutb.puts("ff00ff ff00aa ff0055 ff0000 ee0000 eeeeee eeee9e eeee4f eeee00 ee9eee ee9e9e ee9e4f\n");
+ ioutb.puts("ee9e00 ee4fee ee4f9e ee4f4f ee4f00 ee00ee ee009e ee004f dd0049 dd0000 dddddd dddd93\n");
+ ioutb.puts("dddd49 dddd00 dd93dd dd9393 dd9349 dd9300 dd49dd dd4993 dd4949 dd4900 dd00dd dd0093\n");
+ ioutb.puts("cc0088 cc0044 cc0000 cccccc cccc88 cccc44 cccc00 cc88cc cc8888 cc8844 cc8800 cc44cc\n");
+ ioutb.puts("cc4488 cc4444 cc4400 cc00cc aaffaa aaff55 aaff00 aaaaff bbbbbb bbbb5d bbbb00 aa55ff\n");
+ ioutb.puts("bb5dbb bb5d5d bb5d00 aa00ff bb00bb bb005d bb0000 aaffff 9eeeee 9eee9e 9eee4f 9eee00\n");
+ ioutb.puts("9e9eee aaaaaa aaaa55 aaaa00 9e4fee aa55aa aa5555 aa5500 9e00ee aa00aa aa0055 aa0000\n");
+ ioutb.puts("990000 93dddd 93dd93 93dd49 93dd00 9393dd 999999 99994c 999900 9349dd 994c99 994c4c\n");
+ ioutb.puts("994c00 9300dd 990099 99004c 880044 880000 88cccc 88cc88 88cc44 88cc00 8888cc 888888\n");
+ ioutb.puts("888844 888800 8844cc 884488 884444 884400 8800cc 880088 55ff55 55ff00 55aaff 5dbbbb\n");
+ ioutb.puts("5dbb5d 5dbb00 5555ff 5d5dbb 777777 777700 5500ff 5d00bb 770077 770000 55ffff 55ffaa\n");
+ ioutb.puts("4fee9e 4fee4f 4fee00 4f9eee 55aaaa 55aa55 55aa00 4f4fee 5555aa 666666 666600 4f00ee\n");
+ ioutb.puts("5500aa 660066 660000 4feeee 49dddd 49dd93 49dd49 49dd00 4993dd 4c9999 4c994c 4c9900\n");
+ ioutb.puts("4949dd 4c4c99 555555 555500 4900dd 4c0099 550055 550000 440000 44cccc 44cc88 44cc44\n");
+ ioutb.puts("44cc00 4488cc 448888 448844 448800 4444cc 444488 444444 444400 4400cc 440088 440044\n");
+ ioutb.puts("00ff00 00aaff 00bbbb 00bb5d 00bb00 0055ff 005dbb 007777 007700 0000ff 0000bb 000077\n");
+ ioutb.puts("333333 00ffff 00ffaa 00ff55 00ee4f 00ee00 009eee 00aaaa 00aa55 00aa00 004fee 0055aa\n");
+ ioutb.puts("006666 006600 0000ee 0000aa 000066 222222 00eeee 00ee9e 00dd93 00dd49 00dd00 0093dd\n");
+ ioutb.puts("009999 00994c 009900 0049dd 004c99 005555 005500 0000dd 000099 000055 111111 00dddd\n");
+ ioutb.puts("00cccc 00cc88 00cc44 00cc00 0088cc 008888 008844 008800 0044cc 004488 004444 004400\n");
+ ioutb.puts("0000cc 000088 000044 000000>\n");
+ ioutb.puts("] setcolorspace\n");
+ ioutb.puts("/decodemat [0 255] def\n");
+ ioutb.puts("}\n");
+ # else, bps != 8
+ ioutb.puts("{\n");
+ ioutb.puts("[/DeviceGray] setcolorspace\n");
+ ioutb.puts("/decodemat [1 0] def\n");
+ ioutb.puts("}\n");
+ ioutb.puts("ifelse\n");
+ ioutb.puts("xstart ystart translate \n");
+ ioutb.puts("iwidth iheight scale \n");
+ ioutb.puts("<<\n");
+ ioutb.puts("/ImageType 1\n");
+ ioutb.puts("/Width width \n");
+ ioutb.puts("/Height height \n");
+ ioutb.puts("/BitsPerComponent bps %bits/sample\n");
+ ioutb.puts("/Decode decodemat % Inferno cmap or DeviceGray value\n");
+ ioutb.puts("/ImageMatrix [width 0 0 height neg 0 height]\n");
+ ioutb.puts("/DataSource currentfile /ASCII85Decode filter\n");
+ ioutb.puts(">> \n");
+ ioutb.puts("image\n");
+ ioutb.puts("grestore\n");
+ ioutb.puts("} def\n");
+ ioutb.puts("%%EndProlog\n");
+}
+
+trailer(ioutb : ref Iobuf,pages : int)
+{
+ ioutb.puts("%%Trailer\n%%Pages: "+string pages+"\n%%EOF\n");
+}
+
+
+printnewpage(pagenum : int,end : int, ioutb : ref Iobuf)
+{
+ pnum:=string pagenum;
+ if (end){
+ # bounding box
+ if (boxes){
+ ioutb.puts("18 18 moveto 594 18 lineto 594 774 lineto 18 774 lineto"+
+ " closepath stroke\n");
+ }
+ ioutb.puts("showpage\n%%EndPage "+pnum+" "+pnum+"\n");
+ } else
+ ioutb.puts("%%Page: "+pnum+" "+pnum+"\n");
+}
+
+printimage(ioutb: ref Iobuf, line: Lineinfo, imag: Iteminfo): (string,string)
+{
+ RM:=612-18;
+ class:=tk->cmd(t,"winfo class "+imag.buf);
+#sys->print("Looking for [%s] of type [%s]\n",imag.buf,class);
+ if (line.xorg+imag.offset+imag.width>RM)
+ imag.width=RM-line.xorg-imag.offset;
+ case class {
+ "button" or "menubutton" =>
+ # try to get the text out and print it....
+ ioutb.puts(sys->sprint("%d %d moveto\n",line.xorg+imag.offset,
+ line.yorg));
+ msg:=tk->cmd(t,sys->sprint("%s cget -text",imag.buf));
+ ft:=tk->cmd(t,sys->sprint("%s cget -font",imag.buf));
+ sys->print("font is [%s]\n",ft);
+ ioutb.puts(sys->sprint("%d %d %d %d rectstroke\n",
+ line.xorg+imag.offset,line.yorg,imag.width,
+ line.height));
+ return (class,msg);
+ "label" =>
+ (im,im2,err) := tk->getimage(t,imag.buf);
+ if (im!=nil){
+ bps := im.depth;
+ ioutb.puts(sys->sprint("%d %d %d %d %d %d %d %d doimage\n",
+ im.r.dy(),line.ascent,im.r.dx(),line.yorg,
+ line.xorg+imag.offset,im.r.dy(), im.r.dx(), bps));
+ imagebits(ioutb,im);
+ }
+ return (class,"");
+ "entry" =>
+ ioutb.puts(sys->sprint("%d %d moveto\n",line.xorg+imag.offset,
+ line.yorg));
+ ioutb.puts(sys->sprint("%d %d %d %d rectstroke\n",
+ line.xorg+imag.offset,line.yorg,imag.width,
+ line.height));
+ return (class,"");
+ * =>
+ sys->print("Unhandled class [%s]\n",class);
+ return (class,"Error");
+
+ }
+ return ("","");
+}
+
+printline(ioutb: ref Iobuf,line : Lineinfo,items : array of Iteminfo)
+{
+ xstart:=line.xorg;
+ wid:=xstart;
+ # items
+ if (len items == 0) return;
+ for(j:=0;j<len items;j++){
+ msg:="";
+ class:="";
+ if (items[j].itype==IMAGE)
+ (class,msg)=printimage(ioutb,line,items[j]);
+ if (items[j].itype!=IMAGE || class=="button"|| class=="menubutton"){
+ setfont(ioutb,items[j].font);
+ if (msg!=""){
+ # position the text in the center of the label
+ # moveto curpoint
+ # (msg) stringwidth pop xstart sub 2 div
+ ioutb.puts(sys->sprint("%d %d moveto\n",xstart+items[j].offset,
+ line.yorg+line.height-line.ascent));
+ ioutb.puts(sys->sprint("(%s) dup stringwidth pop 2 div",
+ msg));
+ ioutb.puts(" 0 rmoveto show\n");
+ }
+ else {
+ ioutb.puts(sys->sprint("%d %d moveto\n",
+ xstart+items[j].offset,line.yorg+line.height
+ -line.ascent));
+ ioutb.puts(sys->sprint("(%s) show\n",items[j].buf));
+ }
+ }
+ wid=xstart+items[j].offset+items[j].width;
+ }
+ if (boxes)
+ ioutb.puts(sys->sprint("%d %d %d %d rectstroke\n",line.xorg,line.yorg,
+ wid,line.height));
+}
+
+setfont(ioutb: ref Iobuf, font: int)
+{
+ ftype : int;
+ fname : string;
+ if ((curfonttype & font) != curfonttype){
+ for(f:=0;f<curfont;f++){
+ (ftype,fname)=font_arr[f];
+ if ((ftype&font)==ftype)
+ break;
+ }
+ if (f==curfont){
+ fname=def_font;
+ ftype=def_font_type;
+ }
+ ioutb.puts(sys->sprint("%s setfont\n",fname));
+ curfonttype=ftype;
+ }
+}
+
+parseTkline(ioutb: ref Iobuf, input: string): string
+{
+ thisline : Lineinfo;
+ PS:=792-18-18; # page size in points
+ TM:=792-18; # top margin in points
+ LM:=18; # left margin 1/4 in. in
+ BM:=18; # bottom margin 1/4 in. in
+ x : int;
+ (x,input)=str->toint(input,10);
+ thisline.xorg=(x*PTPI)/PXPI;
+ (x,input)=str->toint(input,10);
+ thisline.yorg=(x*PTPI)/PXPI;
+ (x,input)=str->toint(input,10);
+ thisline.width=(x*PTPI)/PXPI;
+ (x,input)=str->toint(input,10);
+ thisline.height=(x*PTPI)/PXPI;
+ (x,input)=str->toint(input,10);
+ thisline.ascent=(x*PTPI)/PXPI;
+ (x,input)=str->toint(input,10);
+ # thisline.numitems=x;
+ if (thisline.width==0 || thisline.height==0)
+ return "";
+ if (thisline.yorg+thisline.height-pagestart>PS){
+ pagestart=thisline.yorg;
+ return "newpage";
+ # must resend this line....
+ }
+ thisline.yorg=TM-thisline.yorg-thisline.height+pagestart;
+ thisline.xorg+=LM;
+ (items, err) :=getline(totlines,input);
+ if(err != nil)
+ return err;
+ totitems+=len items;
+ totlines++;
+ printline(ioutb,thisline,items);
+ return "";
+}
+
+getfonts(input: string) : string
+{
+ tkfont,psfont : string;
+ j : int;
+ retval := "";
+ if (input[0]=='%')
+ return "";
+ # get a line of the form
+ # 5::/fonts/lucida/moo.16.font
+ # translate it to...
+ # 32 f32.16
+ # where 32==1<<5 and f32.16 is a postscript function that loads the
+ # appropriate postscript font (from remap)
+ # and writes it to fonts....
+ (bits,font):=str->toint(input,10);
+ if (bits!=-1)
+ bits=1<<bits;
+ else{
+ bits=1;
+ def_font_type=bits;
+ curfonttype=def_font_type;
+ }
+ font=font[2:];
+ for(i:=0;i<len remap;i++){
+ (tkfont,psfont)=remap[i];
+ if (tkfont==font)
+ break;
+ }
+ if (i==len remap)
+ psfont="Times-Roman";
+ (font,nil)=str->splitr(font,".");
+ (nil,font)=str->splitr(font[0:len font-1],".");
+ (fsize,nil):=str->toint(font,10);
+ fsize=(PTPI*3*fsize)/(2*PXPI);
+ enc_font:="f"+string bits+"."+string fsize;
+ ps_func:="/"+enc_font+" /"+psfont+" findfont "+string fsize+
+ " scalefont def\n";
+ sy_font:="sy"+string fsize;
+ xtra_func:="/"+sy_font+" /Symbol findfont "+string fsize+
+ " scalefont def\n";
+ for(i=0;i<len font_arr;i++){
+ (j,font)=font_arr[i];
+ if (j==-1) break;
+ }
+ if (j==len font_arr)
+ return "Error";
+ font_arr[i]=(bits,enc_font);
+ if (bits==1)
+ def_font=enc_font;
+ curfont++;
+ retval+= ps_func;
+ retval+= xtra_func;
+ return retval;
+}
+
+deffont() : string
+{
+ return def_font;
+}
+
+getline(k : int, input : string) : (array of Iteminfo, string)
+{
+ lineval,args : string;
+ j, nb : int;
+ lw:=0;
+ wid:=0;
+ flags:=0;
+ item_arr := array[32] of {* => Iteminfo(-1,-1,-1,-1,-1,-1,"")};
+ curitem:=0;
+ while(input!=nil){
+ (nil,input)=str->splitl(input,"[");
+ if (input==nil)
+ break;
+ com:=input[1];
+ input=input[2:];
+ case com {
+ 'A' =>
+ nb=0;
+ # get the width of the item
+ (wid,input)=str->toint(input,10);
+ wid=(wid*PTPI)/PXPI;
+ if (input[0]!='{')
+ return (nil, sys->sprint(
+ "line %d item %d Bad Syntax : '{' expected",
+ k,curitem));
+ # get the args.
+ (args,input)=str->splitl(input,"}");
+ # get the flags.
+ # assume there is only one int flag..
+ (flags,args)=str->toint(args[1:],16);
+ if (args!=nil && debug){
+ sys->print("line %d item %d extra flags=%s\n",
+ k,curitem,args);
+ }
+ if (flags<1024) flags=1;
+ item_arr[curitem].font=flags;
+ item_arr[curitem].offset=lw;
+ item_arr[curitem].width=wid;
+ lw+=wid;
+ for(j=1;j<len input;j++){
+ if ((input[j]==')')||(input[j]=='('))
+ lineval[len lineval]='\\';
+ if (input[j]=='[')
+ nb++;
+ if (input[j]==']')
+ if (nb==0)
+ break;
+ else
+ nb--;
+ lineval[len lineval]=input[j];
+ }
+ if (j<len input)
+ input=input[j:];
+ item_arr[curitem].buf=lineval;
+ item_arr[curitem].line=k;
+ item_arr[curitem].itype=ASCII;
+ curitem++;
+ lineval="";
+ 'R' =>
+ nb=0;
+ # get the width of the item
+ (wid,input)=str->toint(input,10);
+ wid=(wid*PTPI)/PXPI;
+ if (input[0]!='{')
+ return (nil, "Bad Syntax : '{' expected");
+ # get the args.
+ (args,input)=str->splitl(input,"}");
+ # get the flags.
+ # assume there is only one int flag..
+ (flags,args)=str->toint(args[1:],16);
+ if (args!=nil && debug){
+ sys->print("line %d item %d Bad Syntax args=%s",
+ k,curitem,args);
+ }
+ item_arr[curitem].font=flags;
+ item_arr[curitem].offset=lw;
+ item_arr[curitem].width=wid;
+ lw+=wid;
+ for(j=1;j<len input;j++){
+ if (input[j]=='[')
+ nb++;
+ if (input[j]==']')
+ if (nb==0)
+ break;
+ else
+ nb--;
+ case input[j] {
+ 8226 => # bullet
+ lineval+="\\267 ";
+ 169 => # copyright
+ lineval+="\\251 ";
+ curitem++;
+ * =>
+ lineval[len lineval]=input[j];
+ }
+ }
+ if (j>len input)
+ input=input[j:];
+ item_arr[curitem].buf=lineval;
+ item_arr[curitem].line=k;
+ item_arr[curitem].itype=RUNE;
+ curitem++;
+ lineval="";
+ 'N' or 'C'=>
+ # next item
+ for(j=0;j<len input;j++)
+ if (input[j]==']')
+ break;
+ if (j>len input)
+ input=input[j:];
+ 'T' =>
+ (wid,input)=str->toint(input,10);
+ wid=(wid*PTPI)/PXPI;
+ item_arr[curitem].offset=lw;
+ item_arr[curitem].width=wid;
+ lw+=wid;
+ lineval[len lineval]='\t';
+ # next item
+ for(j=0;j<len input;j++)
+ if (input[j]==']')
+ break;
+ if (j>len input)
+ input=input[j:];
+ item_arr[curitem].buf=lineval;
+ item_arr[curitem].line=k;
+ item_arr[curitem].itype=ASCII;
+ curitem++;
+ lineval="";
+ 'W' =>
+ (wid,input)=str->toint(input,10);
+ wid=(wid*PTPI)/PXPI;
+ item_arr[curitem].offset=lw;
+ item_arr[curitem].width=wid;
+ item_arr[curitem].itype=IMAGE;
+ lw+=wid;
+ # next item
+ for(j=1;j<len input;j++){
+ if (input[j]==']')
+ break;
+ lineval[len lineval]=input[j];
+ }
+ item_arr[curitem].buf=lineval;
+ if (j>len input)
+ input=input[j:];
+ curitem++;
+ lineval="";
+ * =>
+ # next item
+ for(j=0;j<len input;j++)
+ if (input[j]==']')
+ break;
+ if (j>len input)
+ input=input[j:];
+
+ }
+ }
+ return (item_arr[0:curitem], "");
+}
+
+writeimage(ioutb: ref Iobuf, im: ref Draw->Image, dpi: int)
+{
+ r := im.r;
+ width := r.dx();
+ height := r.dy();
+ iwidth := width * 72 / dpi;
+ iheight := height * 72 / dpi;
+ xstart := 72;
+ ystart := 720 - iheight;
+ bbox := Rect((xstart,ystart), (xstart+iwidth,ystart+iheight));
+ preamble(ioutb, bbox);
+ ioutb.puts("%%Page: 1\n%%BeginPageSetup\n");
+ ioutb.puts("/pgsave save def\n");
+ ioutb.puts("%%EndPageSetup\n");
+ bps := im.depth;
+ ioutb.puts(sys->sprint("%d 0 %d %d %d %d %d %d doimage\n", iheight, iwidth, ystart, xstart, height, width, bps));
+ imagebits(ioutb, im);
+ ioutb.puts("pgsave restore\nshowpage\n");
+ trailer(ioutb, 1);
+ ioutb.flush();
+}
+
+imagebits(ioutb: ref Iobuf, im: ref Draw->Image)
+{
+ if(debug)
+ sys->print("imagebits, r=%d %d %d %d, depth=%d\n",
+ im.r.min.x, im.r.min.y, im.r.max.x, im.r.max.y, im.depth);
+ width:=im.r.dx();
+ height:=im.r.dy();
+ bps:=im.depth; # bits per sample
+ spb := 1; # samples per byte
+ bitoff := 0; # bit offset of beginning sample within first byte
+ linebytes := width;
+ if(bps < 8) {
+ spb=8/bps;
+ bitoff=(im.r.min.x % spb) * bps;
+ linebytes=(bitoff + (width-1)*bps) / 8 + 1;
+ }
+ arr:=array[linebytes*height] of byte;
+ n:=im.readpixels(im.r,arr);
+ if(debug)
+ sys->print("linebytes=%d, height=%d, readpixels returned %d\n",
+ linebytes, height, n);
+ if(n < 0) {
+ n = len arr;
+ for(i := 0; i < n; i++)
+ arr[i] = byte 0;
+ }
+ if(bitoff != 0) {
+ # Postscript image wants beginning of line at beginning of byte
+ pslinebytes := (width-1)*bps + 1;
+ if(debug)
+ sys->print("bitoff=%d, pslinebytes=%d\n", bitoff, pslinebytes);
+ old:=arr;
+ n = pslinebytes*height;
+ arr=array[n] of byte;
+ a0 := 0;
+ o0 := 0;
+ for(y := 0; y < height; y++) {
+ for(i:=0; i < pslinebytes; i++)
+ arr[a0+i] = (old[o0+i]<<bitoff) | (old[o0+i+1]>>(8-bitoff));
+ a0 += pslinebytes;
+ o0 += linebytes;
+ }
+ }
+ lsf:=0;
+ n4 := (n/4)*4;
+ for(i:=0;i<n4;i+=4){
+ s:=cmap2ascii85(arr[i:i+4]);
+ lsf+=len s;
+ ioutb.puts(s);
+ if (lsf>74){
+ ioutb.puts("\n");
+ lsf=0;
+ }
+ }
+ nrest:=n-n4;
+ if(nrest!=0){
+ foo:=array[4] of {* => byte 0};
+ foo[0:]=arr[n4:n];
+ s:=cmap2ascii85(foo);
+ if(s=="z")
+ s="!!!!!";
+ ioutb.puts(s[0:nrest+1]);
+ }
+ ioutb.puts("~>\n");
+ ioutb.flush();
+}
+
+
+cmap2ascii85(arr : array of byte) : string
+{
+ b := array[4] of {* => big 0};
+ for(i:=0;i<4;i++)
+ b[i]=big arr[i];
+ i1:=(b[0]<<24)+(b[1]<<16)+(b[2]<<8)+b[3];
+ c1:=sys->sprint("%c%c%c%c%c",'!'+int ((i1/big (85*85*85*85))%big 85),
+ '!'+int ((i1/big (85*85*85))%big 85),
+ '!'+int ((i1/big (85*85))% big 85),
+ '!'+int ((i1/big 85)% big 85),'!'+int(i1% big 85));
+ if (c1=="!!!!!") c1="z";
+ return c1;
+}
diff --git a/appl/lib/quicktime.b b/appl/lib/quicktime.b
new file mode 100644
index 00000000..4163c578
--- /dev/null
+++ b/appl/lib/quicktime.b
@@ -0,0 +1,205 @@
+implement QuickTime;
+
+include "sys.m";
+
+sys: Sys;
+
+include "quicktime.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+open(file: string): (ref QD, string)
+{
+ fd := sys->open(file, sys->OREAD);
+ if(fd == nil)
+ return (nil, "open failed");
+
+ r := ref QD;
+ r.fd = fd;
+ r.buf = array[DEFBUF] of byte;
+
+ (hdr, l) := r.atomhdr();
+ if(hdr != "mdat")
+ return (nil, "not a QuickTime movie file");
+
+ #
+ # We are expecting a unified file with .data then .rsrc
+ #
+ r.skipatom(l);
+
+ return (r, nil);
+}
+
+QD.atomhdr(r: self ref QD): (string, int)
+{
+ b := array[8] of byte;
+
+ if(r.readn(b, 8) != 8)
+ return (nil, -1);
+
+for(i := 0; i < 8; i++)
+sys->print("%.2ux ", int b[i]);
+sys->print(" %s %d\n", string b[4:8], bedword(b, 0));
+
+ return (string b[4:8], bedword(b, 0));
+}
+
+QD.skipatom(r: self ref QD, l: int): int
+{
+ return r.skip(l - AtomHDR);
+}
+
+QD.mvhd(q: self ref QD, l: int): string
+{
+ l -= AtomHDR;
+ if(l != MvhdrSIZE)
+ return "mvhd atom funny size";
+
+ b := array[l] of byte;
+ if(q.readn(b, l) != l)
+ return "short read in mvhd";
+
+ mvhdr := ref Mvhdr;
+
+ mvhdr.version = bedword(b, 0);
+ mvhdr.create = bedword(b, 4);
+ mvhdr.modtime = bedword(b, 8);
+ mvhdr.timescale = bedword(b, 12);
+ mvhdr.duration = bedword(b, 16);
+ mvhdr.rate = bedword(b, 20);
+ mvhdr.vol = beword(b, 24);
+ mvhdr.r1 = bedword(b, 26);
+ mvhdr.r2 = bedword(b, 30);
+
+ mvhdr.matrix = array[9] of int;
+ for(i :=0; i<9; i++)
+ mvhdr.matrix[i] = bedword(b, 34+i*4);
+
+ mvhdr.r3 = beword(b, 70);
+ mvhdr.r4 = bedword(b, 72);
+ mvhdr.pvtime = bedword(b, 76);
+ mvhdr.posttime = bedword(b, 80);
+ mvhdr.seltime = bedword(b, 84);
+ mvhdr.seldurat = bedword(b, 88);
+ mvhdr.curtime = bedword(b, 92);
+ mvhdr.nxttkid = bedword(b, 96);
+
+ q.mvhdr = mvhdr;
+ return nil;
+}
+
+QD.trak(q: self ref QD, l: int): string
+{
+ (tk, tkl) := q.atomhdr();
+ if(tk != "tkhd")
+ return "missing track header atom";
+
+ l -= tkl;
+ tkl -= AtomHDR;
+ b := array[tkl] of byte;
+ if(q.readn(b, tkl) != tkl)
+ return "short read in tkhd";
+
+ tkhdr := ref Tkhdr;
+
+ tkhdr.version = bedword(b, 0);
+ tkhdr.creation = bedword(b, 4);
+ tkhdr.modtime = bedword(b, 8);
+ tkhdr.trackid = bedword(b, 12);
+ tkhdr.timescale = bedword(b, 16);
+ tkhdr.duration = bedword(b, 20);
+ tkhdr.timeoff = bedword(b, 24);
+ tkhdr.priority = bedword(b, 28);
+ tkhdr.layer = beword(b, 32);
+ tkhdr.altgrp = beword(b, 34);
+ tkhdr.volume = beword(b, 36);
+
+ tkhdr.matrix = array[9] of int;
+ for(i := 0; i < 9; i++)
+ tkhdr.matrix[i] = bedword(b, 38+i*4);
+
+ tkhdr.width = bedword(b, 74);
+ tkhdr.height = bedword(b, 78);
+
+ (md, mdl) := q.atomhdr();
+ if(md != "mdia")
+ return "missing media atom";
+
+ while(mdl != AtomHDR) {
+ (atom, atoml) := q.atomhdr();
+sys->print("\t%s %d\n", atom, atoml);
+ q.skipatom(atoml);
+
+ mdl -= atoml;
+ }
+
+ return nil;
+}
+
+QD.readn(r: self ref QD, b: array of byte, l: int): int
+{
+ if(r.nbyte < l) {
+ c := 0;
+ if(r.nbyte != 0) {
+ b[0:] = r.buf[r.ptr:];
+ l -= r.nbyte;
+ c += r.nbyte;
+ b = b[r.nbyte:];
+ }
+ bsize := len r.buf;
+ while(l != 0) {
+ r.nbyte = sys->read(r.fd, r.buf, bsize);
+ if(r.nbyte <= 0) {
+ r.nbyte = 0;
+ return -1;
+ }
+ n := l;
+ if(n > bsize)
+ n = bsize;
+
+ r.ptr = 0;
+ b[0:] = r.buf[0:n];
+ b = b[n:];
+ r.nbyte -= n;
+ r.ptr += n;
+ l -= n;
+ c += n;
+ }
+ return c;
+ }
+ b[0:] = r.buf[r.ptr:r.ptr+l];
+ r.nbyte -= l;
+ r.ptr += l;
+ return l;
+}
+
+QD.skip(r: self ref QD, size: int): int
+{
+ if(r.nbyte != 0) {
+ n := size;
+ if(n > r.nbyte)
+ n = r.nbyte;
+ r.ptr += n;
+ r.nbyte -= n;
+ size -= n;
+ if(size == 0)
+ return 0;
+ }
+ return int sys->seek(r.fd, big size, sys->SEEKRELA);
+}
+
+beword(b: array of byte, o: int): int
+{
+ return (int b[o] << 8) | int b[o+1];
+}
+
+bedword(b: array of byte, o: int): int
+{
+ return (int b[o] << 24) |
+ (int b[o+1] << 16) |
+ (int b[o+2] << 8) |
+ int b[o+3];
+}
diff --git a/appl/lib/rand.b b/appl/lib/rand.b
new file mode 100644
index 00000000..3409cdec
--- /dev/null
+++ b/appl/lib/rand.b
@@ -0,0 +1,29 @@
+implement Rand;
+
+include "rand.m";
+
+rsalt: big;
+
+init(seed: int)
+{
+ rsalt = big seed;
+}
+
+MASK: con (big 1<<63)-(big 1);
+
+rand(modulus: int): int
+{
+ rsalt = rsalt * big 1103515245 + big 12345;
+ if(modulus <= 0)
+ return 0;
+ return int (((rsalt&MASK)>>10) % big modulus);
+}
+
+# 0 < modulus < 2^53
+bigrand(modulus: big): big
+{
+ rsalt = rsalt * big 1103515245 + big 12345;
+ if(modulus <= big 0)
+ return big 0;
+ return ((rsalt&MASK)>>10) % modulus;
+}
diff --git a/appl/lib/random.b b/appl/lib/random.b
new file mode 100644
index 00000000..16c7c1ef
--- /dev/null
+++ b/appl/lib/random.b
@@ -0,0 +1,50 @@
+implement Random;
+
+include "sys.m";
+include "draw.m";
+include "keyring.m";
+include "security.m";
+
+sys: Sys;
+
+randfd(which: int): ref sys->FD
+{
+ file: string;
+
+ sys = load Sys Sys->PATH;
+ case(which){
+ ReallyRandom =>
+ file = "/dev/random";
+ NotQuiteRandom =>
+ file = "/dev/notquiterandom";
+ }
+ fd := sys->open(file, sys->OREAD);
+ if(fd == nil){
+ sys->print("can't open /dev/random\n");
+ return nil;
+ }
+ return fd;
+}
+
+randomint(which: int): int
+{
+ fd := randfd(which);
+ if(fd == nil)
+ return 0;
+ buf := array[4] of byte;
+ sys->read(fd, buf, 4);
+ rand := 0;
+ for(i := 0; i < 4; i++)
+ rand = (rand<<8) | int buf[i];
+ return rand;
+}
+
+randombuf(which, n: int): array of byte
+{
+ buf := array[n] of byte;
+ fd := randfd(which);
+ if(fd == nil)
+ return buf;
+ sys->read(fd, buf, n);
+ return buf;
+}
diff --git a/appl/lib/readdir.b b/appl/lib/readdir.b
new file mode 100644
index 00000000..8b8ad276
--- /dev/null
+++ b/appl/lib/readdir.b
@@ -0,0 +1,123 @@
+implement Readdir;
+
+include "sys.m";
+ sys: Sys;
+ Dir: import sys;
+include "readdir.m";
+
+init(path: string, sortkey: int): (array of ref Dir, int)
+{
+ sys = load Sys Sys->PATH;
+ fd := sys->open(path, sys->OREAD);
+ if(fd == nil)
+ return (nil, -1);
+ return readall(fd, sortkey);
+}
+
+readall(fd: ref Sys->FD, sortkey: int): (array of ref Dir, int)
+{
+ sys = load Sys Sys->PATH;
+ dl: list of array of Dir;
+ n := 0;
+ for(;;){
+ (nr, b) := sys->dirread(fd);
+ if(nr <= 0){
+ # any error makes the whole directory unreadable
+ if(nr < 0)
+ return (nil, -1);
+ break;
+ }
+ dl = b :: dl;
+ n += nr;
+ }
+ rl := dl;
+ for(dl = nil; rl != nil; rl = tl rl)
+ dl = hd rl :: dl;
+ a := makerefs(dl, n, sortkey & COMPACT);
+ sortkey &= ~COMPACT;
+ if((sortkey & ~DESCENDING) == NONE)
+ return (a, len a);
+ return sortdir(a, sortkey);
+}
+
+makerefs(dl: list of array of Dir, n: int, compact: int): array of ref Dir
+{
+ a := array[n] of ref Dir;
+ ht: array of list of string;
+ if(compact)
+ ht = array[41] of list of string;
+ j := 0;
+ for(; dl != nil; dl = tl dl){
+ d := hd dl;
+ for(i := 0; i < len d; i++)
+ if(ht == nil || hashadd(ht, d[i].name))
+ a[j++] = ref d[i];
+ }
+ if(j != n)
+ a = a[0:j];
+ return a;
+}
+
+sortdir(a: array of ref Dir, key: int): (array of ref Dir, int)
+{
+ mergesort(a, array[len a] of ref Dir, key);
+ return (a, len a);
+}
+
+# mergesort because it's stable.
+mergesort(a, b: array of ref Dir, key: int)
+{
+ r := len a;
+ if (r > 1) {
+ m := (r-1)/2 + 1;
+ mergesort(a[0:m], b[0:m], key);
+ mergesort(a[m:], b[m:], key);
+ b[0:] = a;
+ for ((i, j, k) := (0, m, 0); i < m && j < r; k++) {
+ if (greater(b[i], b[j], key))
+ a[k] = b[j++];
+ else
+ a[k] = b[i++];
+ }
+ if (i < m)
+ a[k:] = b[i:m];
+ else if (j < r)
+ a[k:] = b[j:r];
+ }
+}
+
+greater(x, y: ref Dir, sortkey: int): int
+{
+ case (sortkey) {
+ NAME => return(x.name > y.name);
+ ATIME => return(x.atime < y.atime);
+ MTIME => return(x.mtime < y.mtime);
+ SIZE => return(x.length > y.length);
+ NAME|DESCENDING => return(x.name < y.name);
+ ATIME|DESCENDING => return(x.atime > y.atime);
+ MTIME|DESCENDING => return(x.mtime > y.mtime);
+ SIZE|DESCENDING => return(x.length < y.length);
+ }
+ return 0;
+}
+
+# from tcl_strhash.b
+hashfn(key: string, n : int): int
+{
+ h := 0;
+ for(i := 0; i < len key; i++){
+ h = 10*h + key[i];
+ h = h%n;
+ }
+ return h%n;
+}
+
+hashadd(ht: array of list of string, nm: string): int
+{
+ idx := hashfn(nm, len ht);
+ for (ent := ht[idx]; ent != nil; ent = tl ent)
+ if (hd ent == nm)
+ return 0;
+ ht[idx] = nm :: ht[idx];
+ return 1;
+}
diff --git a/appl/lib/readgif.b b/appl/lib/readgif.b
new file mode 100644
index 00000000..3c3bb486
--- /dev/null
+++ b/appl/lib/readgif.b
@@ -0,0 +1,442 @@
+implement RImagefile;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "imagefile.m";
+
+Header: adt
+{
+ fd: ref Iobuf;
+ buf: array of byte;
+ vers: string;
+ screenw: int;
+ screenh: int;
+ fields: int;
+ bgrnd: int;
+ aspect: int;
+ transp: int;
+ trindex: byte;
+};
+
+Entry: adt
+{
+ prefix: int;
+ exten: int;
+};
+
+tbl: array of Entry;
+
+init(iomod: Bufio)
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ bufio = iomod;
+}
+read(fd: ref Iobuf): (ref Rawimage, string)
+{
+ (a, err) := readarray(fd, 0);
+ if(a != nil)
+ return (a[0], err);
+ return (nil, err);
+}
+
+readmulti(fd: ref Iobuf): (array of ref Rawimage, string)
+{
+ return readarray(fd, 1);
+}
+
+readarray(fd: ref Iobuf, multi: int): (array of ref Rawimage, string)
+{
+ inittbl();
+
+ buf := array[3*256] of byte;
+
+ (header, err) := readheader(fd, buf);
+ if(header == nil)
+ return (nil, err);
+
+ globalcmap: array of byte;
+ if(header.fields & 16r80){
+ (globalcmap, err) = readcmap(header, (header.fields&7)+1);
+ if(globalcmap == nil)
+ return (nil, err);
+ }
+
+ images: array of ref Rawimage;
+ new: ref Rawimage;
+
+ Loop:
+ for(;;){
+ case c := fd.getb(){
+ Bufio->EOF =>
+ if(err == "")
+ err = "ReadGIF: premature EOF";
+ break Loop;
+ Bufio->ERROR =>
+ err = sys->sprint("ReadGIF: read error: %r");
+ return (nil, err);
+ 16r21 => # Extension (ignored)
+ err = skipextension(header);
+ if(err != nil)
+ return (nil, err);
+
+ 16r2C => # Image Descriptor
+ if(!multi && images!=nil) # why read the rest?
+ break Loop;
+ (new, err) = readimage(header);
+ if(new == nil)
+ return (nil ,err);
+ if(new.fields & 16r80){
+ (new.cmap, err) = readcmap(header, (new.fields&7)+1);
+ if(new.cmap == nil)
+ return (nil, err);
+ }else
+ new.cmap = globalcmap;
+ (new.chans[0], err) = decode(header, new);
+ if(new.chans[0] == nil)
+ return (nil, err);
+ if(new.fields & 16r40)
+ interlace(new);
+ new.transp = header.transp;
+ new.trindex = header.trindex;
+ nimages := array[len images+1] of ref Rawimage;
+ nimages[0:] = images[0:];
+ nimages[len images] = new;
+ images = nimages;
+
+ 16r3B => # Trailer
+ break Loop;
+
+ * =>
+ err = sys->sprint("ReadGIF: unknown block type: %x", c);
+ break Loop;
+ }
+ }
+
+ if(images==nil || images[0].chans[0] == nil){
+ if(err == nil)
+ err = "ReadGIF: no picture in file";
+ return (nil, err);
+ }
+
+ return (images, err);
+}
+
+readheader(fd: ref Iobuf, buf: array of byte): (ref Header, string)
+{
+ if(fd.read(buf, 13) != 13){
+ err := sys->sprint("ReadGIF: can't read header: %r");
+ return (nil, err);
+ }
+ h := ref Header;
+ h.vers = string buf[0:6];
+ if(h.vers!="GIF87a" && h.vers!="GIF89a"){
+ err := sys->sprint("ReadGIF: can't recognize format %s", h.vers);
+ return (nil, err);
+ }
+ h.screenw = int buf[6]+(int buf[7]<<8);
+ h.screenh = int buf[8]+(int buf[9]<<8);
+ h.fields = int buf[10];
+ h.bgrnd = int buf[11];
+ h.aspect = int buf[12];
+ h.fd = fd;
+ h.buf = buf;
+ h.transp = 0;
+ return (h, "");
+}
+
+readcmap(h: ref Header, size: int): (array of byte,string)
+{
+ size = 3*(1<<size);
+ map := array[size] of byte;
+ if(h.fd.read(map, size) != size)
+ return (nil, "ReadGIF: short read on color map");
+ return (map, "");
+}
+
+readimage(h: ref Header): (ref Rawimage, string)
+{
+ if(h.fd.read(h.buf, 9) != 9){
+ err := sys->sprint("ReadGIF: can't read image descriptor: %r");
+ return (nil, err);
+ }
+ i := ref Rawimage;
+ left := int h.buf[0]+(int h.buf[1]<<8);
+ top := int h.buf[2]+(int h.buf[3]<<8);
+ width := int h.buf[4]+(int h.buf[5]<<8);
+ height := int h.buf[6]+(int h.buf[7]<<8);
+ i.fields = int h.buf[8];
+ i.r.min.x = left;
+ i.r.min.y = top;
+ i.r.max.x = left+width;
+ i.r.max.y = top+height;
+ i.nchans = 1;
+ i.chans = array[1] of array of byte;
+ i.chandesc = CRGB1;
+ return (i, "");
+}
+
+readdata(h: ref Header, ch: chan of (array of byte, string))
+{
+ err: string;
+
+ # send nil for error, buffer of length 0 for EOF
+ for(;;){
+ nbytes := h.fd.getb();
+ if(nbytes < 0){
+ err = sys->sprint("ReadGIF: can't read data: %r");
+ ch <-= (nil, err);
+ return;
+ }
+ d := array[nbytes] of byte;
+ if(nbytes == 0){
+ ch <-= (d, "");
+ return;
+ }
+ n := h.fd.read(d, nbytes);
+ if(n != nbytes){
+ if(n > 0){
+ ch <-= (d[0:n], nil);
+ ch <-= (d[0:0], "ReadGIF: short data subblock");
+ }else
+ ch <-= (nil, sys->sprint("ReadGIF: can't read data: %r"));
+ return;
+ }
+ ch <-= (d, "");
+ }
+}
+
+readerr: con "ReadGIF: can't read extension: %r";
+
+skipextension(h: ref Header): string
+{
+ fmterr: con "ReadGIF: bad extension format";
+
+ hsize := 0;
+ hasdata := 0;
+
+ case XX := h.fd.getb(){
+ Bufio->ERROR or Bufio->EOF =>
+ return sys->sprint(readerr);
+ 16r01 => # Plain Text Extension
+ hsize = 13;
+ hasdata = 1;
+ 16rF9 => # Graphic Control Extension
+ return graphiccontrol(h);
+ 16rFE => # Comment Extension
+ hasdata = 1;
+ 16rFF => # Application Extension
+ hsize = h.fd.getb();
+ # standard says this must be 11, but Adobe likes to put out 10-byte ones,
+ # so we pay attention to the field.
+ hasdata = 1;
+ * =>
+ return "ReadGIF: unknown extension";
+ }
+ if(hsize>0 && h.fd.read(h.buf, hsize) != hsize)
+ return sys->sprint(readerr);
+ if(!hasdata){
+ if(int h.buf[hsize-1] != 0)
+ return fmterr;
+ }else{
+ ch := chan of (array of byte, string);
+ spawn readdata(h, ch);
+ for(;;){
+ (data, err) := <-ch;
+ if(data == nil)
+ return err;
+ if(len data == 0)
+ break;
+ }
+ }
+ return "";
+}
+
+graphiccontrol(h: ref Header): string
+{
+ if(h.fd.read(h.buf, 5+1) != 5+1)
+ return sys->sprint(readerr);
+ if(int h.buf[1] & 1){
+ h.transp = 1;
+ h.trindex = h.buf[4];
+ }
+ return "";
+}
+
+inittbl()
+{
+ tbl = array[4096] of Entry;
+ for(i:=0; i<258; i++) {
+ tbl[i].prefix = -1;
+ tbl[i].exten = i;
+ }
+}
+
+decode(h: ref Header, i: ref Rawimage): (array of byte, string)
+{
+ c, incode: int;
+
+ err := "";
+ if(h.fd.read(h.buf, 1) != 1){
+ err = sys->sprint("ReadGIF: can't read data: %r");
+ return (nil, err);
+ }
+ codesize := int h.buf[0];
+ if(codesize>8 || 0>codesize){
+ err = sys->sprint("ReadGIF: can't handle codesize %d", codesize);
+ return (nil, err);
+ }
+ err1 := "";
+ if(i.cmap!=nil && len i.cmap!=3*(1<<codesize)
+ && (codesize!=2 || len i.cmap!=3*2)) # peculiar GIF bitmap files...
+ err1 = sys->sprint("ReadGIF: codesize %d doesn't match color map 3*%d", codesize, len i.cmap/3);
+
+ ch := chan of (array of byte, string);
+
+ spawn readdata(h, ch);
+
+ CTM :=1<<codesize;
+ EOD := CTM+1;
+
+ pic := array[(i.r.max.x-i.r.min.x)*(i.r.max.y-i.r.min.y)] of byte;
+ pici := 0;
+ data := array[0] of byte;
+ datai := 0;
+
+ nbits := 0;
+ sreg := 0;
+ stack := array[4096] of byte;
+ stacki: int;
+ fc := 0;
+
+Init:
+ for(;;){
+ csize := codesize+1;
+ nentry := EOD+1;
+ maxentry := (1<<csize)-1;
+ first := 1;
+ ocode := -1;
+
+ for(;; ocode = incode) {
+ while(nbits < csize) {
+ if(datai == len data){
+ (data, err) = <-ch;
+ if(data == nil)
+ return (nil, err);
+ if(err!="" && err1=="")
+ err1 = err;
+ if(len data == 0)
+ break Init;
+ datai = 0;
+ }
+ c = int data[datai++];
+ sreg |= c<<nbits;
+ nbits += 8;
+ }
+ code := sreg & ((1<<csize) - 1);
+ sreg >>= csize;
+ nbits -= csize;
+
+ if(code == EOD){
+ (data, err) = <-ch;
+ if(len data != 0)
+ err = "ReadGIF: unexpected data past EOD";
+ if(err!="" && err1=="")
+ err1 = err;
+ break Init;
+ }
+
+ if(code == CTM)
+ continue Init;
+
+ stacki = len stack-1;
+
+ incode = code;
+
+ # special case for KwKwK
+ if(code == nentry) {
+ stack[stacki--] = byte fc;
+ code = ocode;
+ }
+
+ if(code > nentry) {
+ err = sys->sprint("ReadGIF: bad code %x %x", code, nentry);
+ return (nil, err);
+ }
+
+ for(c=code; c>=0; c=tbl[c].prefix)
+ stack[stacki--] = byte tbl[c].exten;
+
+ nb := len stack-(stacki+1);
+ if(pici+nb > len pic){
+ if(err1 == "")
+ err1 = "ReadGIF: data overflows picture";
+ }else{
+ pic[pici:] = stack[stacki+1:];
+ pici += nb;
+ }
+
+ fc = int stack[stacki+1];
+
+ if(first){
+ first = 0;
+ continue;
+ }
+ early:=0; # peculiar tiff feature here for reference
+ if(nentry == maxentry-early) {
+ if(csize >= 12)
+ continue;
+ csize++;
+ maxentry = (1<<csize);
+ if(csize < 12)
+ maxentry--;
+ }
+ tbl[nentry].prefix = ocode;
+ tbl[nentry].exten = fc;
+ nentry++;
+ }
+ }
+ return (pic, err1);
+}
+
+interlace(image: ref Rawimage)
+{
+ pic := image.chans[0];
+ r := image.r;
+ dx := r.max.x-r.min.x;
+ ipic := array[dx*(r.max.y-r.min.y)] of byte;
+
+ # Group 1: every 8th row, starting with row 0
+ yy := 0;
+ for(y:=r.min.y; y<r.max.y; y+=8){
+ ipic[y*dx:] = pic[yy*dx:(yy+1)*dx];
+ yy++;
+ }
+
+ # Group 2: every 8th row, starting with row 4
+ for(y=r.min.y+4; y<r.max.y; y+=8){
+ ipic[y*dx:] = pic[yy*dx:(yy+1)*dx];
+ yy++;
+ }
+
+ # Group 3: every 4th row, starting with row 2
+ for(y=r.min.y+2; y<r.max.y; y+=4){
+ ipic[y*dx:] = pic[yy*dx:(yy+1)*dx];
+ yy++;
+ }
+
+ # Group 4: every 2nd row, starting with row 1
+ for(y=r.min.y+1; y<r.max.y; y+=2){
+ ipic[y*dx:] = pic[yy*dx:(yy+1)*dx];
+ yy++;
+ }
+
+ image.chans[0] = ipic;
+}
diff --git a/appl/lib/readjpg.b b/appl/lib/readjpg.b
new file mode 100644
index 00000000..d47c15d7
--- /dev/null
+++ b/appl/lib/readjpg.b
@@ -0,0 +1,973 @@
+implement RImagefile;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "imagefile.m";
+
+# Constants, all preceded by byte 16rFF
+SOF: con byte 16rC0; # Start of Frame
+SOF2: con byte 16rC2; # Start of Frame; progressive Huffman
+JPG: con byte 16rC8; # Reserved for JPEG extensions
+DHT: con byte 16rC4; # Define Huffman Tables
+DAC: con byte 16rCC; # Arithmetic coding conditioning
+RST: con byte 16rD0; # Restart interval termination
+RST7: con byte 16rD7; # Restart interval termination (highest value)
+SOI: con byte 16rD8; # Start of Image
+EOI: con byte 16rD9; # End of Image
+SOS: con byte 16rDA; # Start of Scan
+DQT: con byte 16rDB; # Define quantization tables
+DNL: con byte 16rDC; # Define number of lines
+DRI: con byte 16rDD; # Define restart interval
+DHP: con byte 16rDE; # Define hierarchical progression
+EXP: con byte 16rDF; # Expand reference components
+APPn: con byte 16rE0; # Reserved for application segments
+JPGn: con byte 16rF0; # Reserved for JPEG extensions
+COM: con byte 16rFE; # Comment
+
+Header: adt
+{
+ fd: ref Iobuf;
+ ch: chan of (ref Rawimage, string);
+ # variables in i/o routines
+ sr: int; # shift register, right aligned
+ cnt: int; # # bits in right part of sr
+ buf: array of byte;
+ bufi: int;
+ nbuf: int;
+
+ Nf: int;
+ comp: array of Framecomp;
+ mode: byte;
+ X,Y: int;
+ qt: array of array of int; # quantization tables
+ dcht: array of ref Huffman;
+ acht: array of ref Huffman;
+ sf: array of byte; # start of frame; do better later
+ ss: array of byte; # start of scan; do better later
+ ri: int;
+};
+
+NBUF: con 16*1024;
+
+Huffman: adt
+{
+ bits: array of int;
+ size: array of int;
+ code: array of int;
+ val: array of int;
+ mincode: array of int;
+ maxcode: array of int;
+ valptr: array of int;
+ # fast lookup
+ value: array of int;
+ shift: array of int;
+};
+
+Framecomp: adt # Frame component specifier from SOF marker
+{
+ C: int;
+ H: int;
+ V: int;
+ Tq: int;
+};
+
+zerobytes: array of byte;
+zeroints: array of int;
+zeroreals: array of real;
+clamp: array of byte;
+NCLAMP: con 1000;
+CLAMPOFF: con 300;
+
+init(iomod: Bufio)
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ bufio = iomod;
+ zerobytes = array[8*8] of byte;
+ zeroints = array[8*8] of int;
+ zeroreals = array[8*8] of real;
+ for(k:=0; k<8*8; k++){
+ zerobytes[k] = byte 0;
+ zeroints[k] = 0;
+ zeroreals[k] = 0.0;
+ }
+ clamp = array[NCLAMP] of byte;
+ for(k=0; k<CLAMPOFF; k++)
+ clamp[k] = byte 0;
+ for(; k<CLAMPOFF+256; k++)
+ clamp[k] = byte(k-CLAMPOFF);
+ for(; k<NCLAMP; k++)
+ clamp[k] = byte 255;
+}
+
+read(fd: ref Iobuf): (ref Rawimage, string)
+{
+ # spawn a subprocess so I/O errors can clean up easily
+
+ ch := chan of (ref Rawimage, string);
+ spawn readslave(fd, ch);
+
+ return <-ch;
+}
+
+readmulti(fd: ref Iobuf): (array of ref Rawimage, string)
+{
+ (i, err) := read(fd);
+ if(i != nil){
+ a := array[1] of { i };
+ return (a, err);
+ }
+ return (nil, err);
+}
+
+readslave(fd: ref Iobuf, ch: chan of (ref Rawimage, string))
+{
+ image: ref Rawimage;
+
+ (header, err) := soiheader(fd, ch);
+ if(header == nil){
+ ch <-= (nil, err);
+ exit;
+ }
+ buf := header.buf;
+ nseg := 0;
+
+ Loop:
+ while(err == ""){
+ m: int;
+ b: array of byte;
+ nseg++;
+ (m, b, err) = readsegment(header);
+ case m{
+ -1 =>
+ break Loop;
+
+ int APPn+0 =>
+ if(nseg==1 && string b[0:4]=="JFIF"){ # JFIF header; check version
+ vers0 := int b[5];
+ vers1 := int b[6];
+ if(vers0>1 || vers1>2)
+ err = sys->sprint("ReadJPG: can't handle JFIF version %d.%2d", vers0, vers1);
+ }
+
+ int APPn+1 to int APPn+15 =>
+ ;
+
+ int DQT =>
+ err = quanttables(header, b);
+
+ int SOF =>
+ header.Y = int2(b, 1);
+ header.X = int2(b, 3);
+ header.Nf = int b[5];
+ header.comp = array[header.Nf] of Framecomp;
+ for(i:=0; i<header.Nf; i++){
+ header.comp[i].C = int b[6+3*i+0];
+ (H, V) := nibbles(b[6+3*i+1]);
+ header.comp[i].H = H;
+ header.comp[i].V = V;
+ header.comp[i].Tq = int b[6+3*i+2];
+ }
+ header.mode = SOF;
+ header.sf = b;
+
+ int SOF2 =>
+ err = sys->sprint("ReadJPG: can't handle progressive Huffman mode");
+ break Loop;
+
+ int SOS =>
+ header.ss = b;
+ (image, err) = decodescan(header);
+ if(err != "")
+ break Loop;
+
+ # BUG: THIS SHOULD USE THE LOOP TO FINISH UP
+ x := nextbyte(header, 1);
+ if(x != 16rFF)
+ err = sys->sprint("ReadJPG: didn't see marker at end of scan; saw %x", x);
+ else{
+ x = nextbyte(header, 1);
+ if(x != int EOI)
+ err = sys->sprint("ReadJPG: expected EOI saw %x", x);
+ }
+ break Loop;
+
+ int DHT =>
+ err = huffmantables(header, b);
+
+ int DRI =>
+ header.ri = int2(b, 0);
+
+ int COM =>
+ ;
+
+ int EOI =>
+ break Loop;
+
+ * =>
+ err = sys->sprint("ReadJPG: unknown marker %.2x", m);
+ }
+ }
+ ch <-= (image, err);
+}
+
+readerror(): string
+{
+ return sys->sprint("ReadJPG: read error: %r");
+}
+
+marker(buf: array of byte, n: int): byte
+{
+ if(buf[n] != byte 16rFF)
+ return byte 0;
+ return buf[n+1];
+}
+
+int2(buf: array of byte, n: int): int
+{
+ return (int buf[n]<<8)+(int buf[n+1]);
+}
+
+nibbles(b: byte): (int, int)
+{
+ i := int b;
+ return (i>>4, i&15);
+}
+
+soiheader(fd: ref Iobuf, ch: chan of (ref Rawimage, string)): (ref Header, string)
+{
+ # 1+ for restart preamble (see nextbyte), +1 for sentinel
+ buf := array[1+NBUF+1] of byte;
+ if(fd.read(buf, 2) != 2)
+ return (nil, sys->sprint("ReadJPG: can't read header: %r"));
+ if(marker(buf, 0) != SOI)
+ return (nil, sys->sprint("ReadJPG: unrecognized marker in header"));
+ h := ref Header;
+ h.buf = buf;
+ h.bufi = 0;
+ h.nbuf = 0;
+ h.fd = fd;
+ h.ri = 0;
+ h.ch = ch;
+ return (h, nil);
+}
+
+readsegment(h: ref Header): (int, array of byte, string)
+{
+ if(h.fd.read(h.buf, 2) != 2)
+ return (-1, nil, readerror());
+ m := int marker(h.buf, 0);
+ case m{
+ int EOI =>
+ return (m, nil, nil);
+ 0 =>
+ err := sys->sprint("ReadJPG: expecting marker; saw %.2x%.2x)",
+ int h.buf[0], int h.buf[1]);
+ return (-1, nil, err);
+ }
+ if(h.fd.read(h.buf, 2) != 2)
+ return (-1, nil, readerror());
+ n := int2(h.buf, 0);
+ if(n < 2)
+ return (-1, nil, readerror());
+ n -= 2;
+# if(n > len h.buf){
+# h.buf = array[n+1] of byte; # +1 for sentinel
+# #h.nbuf = n;
+# }
+ b := array[n] of byte;
+ if(h.fd.read(b, n) != n)
+ return (-1, nil, readerror());
+ return (m, b, nil);
+}
+
+huffmantables(h: ref Header, b: array of byte): string
+{
+ if(h.dcht == nil){
+ h.dcht = array[4] of ref Huffman;
+ h.acht = array[4] of ref Huffman;
+ }
+ err: string;
+ mt: int;
+ for(l:=0; l<len b; l+=17+mt){
+ (mt, err) = huffmantable(h, b[l:]);
+ if(err != nil)
+ return err;
+ }
+ return nil;
+}
+
+huffmantable(h: ref Header, b: array of byte): (int, string)
+{
+ t := ref Huffman;
+ (Tc, th) := nibbles(b[0]);
+ if(Tc > 1)
+ return (0, sys->sprint("ReadJPG: unknown Huffman table class %d", Tc));
+ if(th>3 || (h.mode==SOF && th>1))
+ return (0, sys->sprint("ReadJPG: unknown Huffman table index %d", th));
+ if(Tc == 0)
+ h.dcht[th] = t;
+ else
+ h.acht[th] = t;
+
+ # flow chart C-2
+ nsize := 0;
+ for(i:=0; i<16; i++)
+ nsize += int b[1+i];
+ t.size = array[nsize+1] of int;
+ k := 0;
+ for(i=1; i<=16; i++){
+ n := int b[i];
+ for(j:=0; j<n; j++)
+ t.size[k++] = i;
+ }
+ t.size[k] = 0;
+
+ # initialize HUFFVAL
+ t.val = array[nsize] of int;
+ for(i=0; i<nsize; i++){
+ t.val[i] = int b[17+i];
+ }
+
+ # flow chart C-3
+ t.code = array[nsize+1] of int;
+ k = 0;
+ code := 0;
+ si := t.size[0];
+ for(;;){
+ do
+ t.code[k++] = code++;
+ while(t.size[k] == si);
+ if(t.size[k] == 0)
+ break;
+ do{
+ code <<= 1;
+ si++;
+ }while(t.size[k] != si);
+ }
+
+ # flow chart F-25
+ t.mincode = array[17] of int;
+ t.maxcode = array[17] of int;
+ t.valptr = array[17] of int;
+ i = 0;
+ j := 0;
+ F25:
+ for(;;){
+ for(;;){
+ i++;
+ if(i > 16)
+ break F25;
+ if(int b[i] != 0)
+ break;
+ t.maxcode[i] = -1;
+ }
+ t.valptr[i] = j;
+ t.mincode[i] = t.code[j];
+ j += int b[i]-1;
+ t.maxcode[i] = t.code[j];
+ j++;
+ }
+
+ # create byte-indexed fast path tables
+ t.value = array[256] of int;
+ t.shift = array[256] of int;
+ maxcode := t.maxcode;
+ # stupid startup algorithm: just run machine for each byte value
+ Bytes:
+ for(v:=0; v<256; v++){
+ cnt := 7;
+ m := 1<<7;
+ code = 0;
+ sr := v;
+ i = 1;
+ for(;;i++){
+ if(sr & m)
+ code |= 1;
+ if(code <= maxcode[i])
+ break;
+ code <<= 1;
+ m >>= 1;
+ if(m == 0){
+ t.shift[v] = 0;
+ t.value[v] = -1;
+ continue Bytes;
+ }
+ cnt--;
+ }
+ t.shift[v] = 8-cnt;
+ t.value[v] = t.val[t.valptr[i]+(code-t.mincode[i])];
+ }
+
+ return (nsize, nil);
+}
+
+quanttables(h: ref Header, b: array of byte): string
+{
+ if(h.qt == nil)
+ h.qt = array[4] of array of int;
+ err: string;
+ n: int;
+ for(l:=0; l<len b; l+=1+n){
+ (n, err) = quanttable(h, b[l:]);
+ if(err != nil)
+ return err;
+ }
+ return nil;
+}
+
+quanttable(h: ref Header, b: array of byte): (int, string)
+{
+ (pq, tq) := nibbles(b[0]);
+ if(pq > 1)
+ return (0, sys->sprint("ReadJPG: unknown quantization table class %d", pq));
+ if(tq > 3)
+ return (0, sys->sprint("ReadJPG: unknown quantization table index %d", tq));
+ q := array[64] of int;
+ h.qt[tq] = q;
+ for(i:=0; i<64; i++){
+ if(pq == 0)
+ q[i] = int b[1+i];
+ else
+ q[i] = int2(b, 1+2*i);
+ }
+ return (64*(1+pq), nil);
+}
+
+zig := array[64] of {
+ 0, 1, 8, 16, 9, 2, 3, 10, 17, # 0-7
+ 24, 32, 25, 18, 11, 4, 5, # 8-15
+ 12, 19, 26, 33, 40, 48, 41, 34, # 16-23
+ 27, 20, 13, 6, 7, 14, 21, 28, # 24-31
+ 35, 42, 49, 56, 57, 50, 43, 36, # 32-39
+ 29, 22, 15, 23, 30, 37, 44, 51, # 40-47
+ 58, 59, 52, 45, 38, 31, 39, 46, # 48-55
+ 53, 60, 61, 54, 47, 55, 62, 63 # 56-63
+};
+
+decodescan(h: ref Header): (ref Rawimage, string)
+{
+ ss := h.ss;
+ Ns := int ss[0];
+ if((Ns!=3 && Ns!=1) || Ns!=h.Nf)
+ return (nil, "ReadJPG: can't handle scan not 3 components");
+
+ image := ref Rawimage;
+ image.r = ((0, 0), (h.X, h.Y));
+ image.cmap = nil;
+ image.transp = 0;
+ image.trindex = byte 0;
+ image.fields = 0;
+ image.chans = array[h.Nf] of array of byte;
+ if(Ns == 3)
+ image.chandesc = CRGB;
+ else
+ image.chandesc = CY;
+ image.nchans = h.Nf;
+ for(k:=0; k<h.Nf; k++)
+ image.chans[k] = array[h.X*h.Y] of byte;
+
+ # build per-component arrays
+ Td := array[Ns] of int;
+ Ta := array[Ns] of int;
+ data := array[Ns] of array of array of real;
+ H := array[Ns] of int;
+ V := array[Ns] of int;
+ DC := array[Ns] of int;
+
+ # compute maximum H and V
+ Hmax := 0;
+ Vmax := 0;
+ for(comp:=0; comp<Ns; comp++){
+ if(h.comp[comp].H > Hmax)
+ Hmax = h.comp[comp].H;
+ if(h.comp[comp].V > Vmax)
+ Vmax = h.comp[comp].V;
+ }
+
+ # initialize data structures
+ allHV1 := 1;
+ for(comp=0; comp<Ns; comp++){
+ # JPEG requires scan components to be in same order as in frame,
+ # so if both have 3 we know scan is Y Cb Cr and there's no need to
+ # reorder
+ cs := int ss[1+2*comp];
+ (Td[comp], Ta[comp]) = nibbles(ss[2+2*comp]);
+ H[comp] = h.comp[comp].H;
+ V[comp] = h.comp[comp].V;
+ nblock := H[comp]*V[comp];
+ if(nblock != 1)
+ allHV1 = 0;
+ data[comp] = array[nblock] of array of real;
+ DC[comp] = 0;
+ for(m:=0; m<nblock; m++)
+ data[comp][m] = array[8*8] of real;
+ }
+
+ ri := h.ri;
+
+ h.buf[0] = byte 16rFF; # see nextbyte()
+ h.cnt = 0;
+ h.sr = 0;
+ nacross := ((h.X+(8*Hmax-1))/(8*Hmax));
+ nmcu := ((h.Y+(8*Vmax-1))/(8*Vmax))*nacross;
+ zz := array[64] of real;
+ err := "";
+ for(mcu:=0; mcu<nmcu; ){
+ for(comp=0; comp<Ns; comp++){
+ dcht := h.dcht[Td[comp]];
+ acht := h.acht[Ta[comp]];
+ qt := h.qt[h.comp[comp].Tq];
+
+ for(block:=0; block<H[comp]*V[comp]; block++){
+ # F-22
+ t := decode(h, dcht);
+ diff := receive(h, t);
+ DC[comp] += diff;
+
+ # F-23
+ zz[0:] = zeroreals;
+ zz[0] = real (qt[0]*DC[comp]);
+ k = 1;
+ for(;;){
+ rs := decode(h, acht);
+ (rrrr, ssss) := nibbles(byte rs);
+ if(ssss == 0){
+ if(rrrr != 15)
+ break;
+ k += 16;
+ }else{
+ k += rrrr;
+ z := receive(h, ssss);
+ zz[zig[k]] = real (z*qt[k]);
+ if(k == 63)
+ break;
+ k++;
+ }
+ }
+
+ idct(zz, data[comp][block]);
+ }
+ }
+
+ # rotate colors to RGB and assign to bytes
+ if(Ns == 1) # very easy
+ colormap1(h, image, data[0][0], mcu, nacross);
+ else if(allHV1) # fairly easy
+ colormapall1(h, image, data[0][0], data[1][0], data[2][0], mcu, nacross);
+ else # miserable general case
+ colormap(h, image, data[0], data[1], data[2], mcu, nacross, Hmax, Vmax, H, V);
+
+ # process restart marker, if present
+ mcu++;
+ if(ri>0 && mcu<nmcu-1 && mcu%ri==0){
+ restart := mcu/ri-1;
+ rst, nskip: int;
+ nskip = 0;
+ do{
+ do{
+ rst = nextbyte(h, 1);
+ nskip++;
+ }while(rst>=0 && rst!=16rFF);
+ if(rst == 16rFF){
+ rst = nextbyte(h, 1);
+ nskip++;
+ }
+ }while(rst>=0 && (rst&~7)!=int RST);
+ if(nskip != 2)
+ err = sys->sprint("skipped %d bytes at restart %d\n", nskip-2, restart);
+ if(rst < 0)
+ return (nil, readerror());
+ if((rst&7) != (restart&7))
+ return (nil, sys->sprint("ReadJPG: expected RST%d got %d", restart&7, int rst&7));
+ h.cnt = 0;
+ h.sr = 0;
+ for(comp=0; comp<Ns; comp++)
+ DC[comp] = 0;
+ }
+ }
+ return (image, err);
+}
+
+colormap1(h: ref Header, image: ref Rawimage, data: array of real, mcu, nacross: int)
+{
+ pic := image.chans[0];
+ minx := 8*(mcu%nacross);
+ dx := 8;
+ if(minx+dx > h.X)
+ dx = h.X-minx;
+ miny := 8*(mcu/nacross);
+ dy := 8;
+ if(miny+dy > h.Y)
+ dy = h.Y-miny;
+ pici := miny*h.X+minx;
+ k := 0;
+ for(y:=0; y<dy; y++){
+ for(x:=0; x<dx; x++){
+ r := clamp[int (data[k+x]+128.)+CLAMPOFF];
+ pic[pici+x] = r;
+ }
+ pici += h.X;
+ k += 8;
+ }
+}
+
+colormapall1(h: ref Header, image: ref Rawimage, data0, data1, data2: array of real, mcu, nacross: int)
+{
+ rpic := image.chans[0];
+ gpic := image.chans[1];
+ bpic := image.chans[2];
+ minx := 8*(mcu%nacross);
+ dx := 8;
+ if(minx+dx > h.X)
+ dx = h.X-minx;
+ miny := 8*(mcu/nacross);
+ dy := 8;
+ if(miny+dy > h.Y)
+ dy = h.Y-miny;
+ pici := miny*h.X+minx;
+ k := 0;
+ for(y:=0; y<dy; y++){
+ for(x:=0; x<dx; x++){
+ Y := data0[k+x]+128.;
+ Cb := data1[k+x];
+ Cr := data2[k+x];
+ r := int (Y+1.402*Cr);
+ g := int (Y-0.34414*Cb-0.71414*Cr);
+ b := int (Y+1.772*Cb);
+ rpic[pici+x] = clamp[r+CLAMPOFF];
+ gpic[pici+x] = clamp[g+CLAMPOFF];
+ bpic[pici+x] = clamp[b+CLAMPOFF];
+ }
+ pici += h.X;
+ k += 8;
+ }
+}
+
+colormap(h: ref Header, image: ref Rawimage, data0, data1, data2: array of array of real, mcu, nacross, Hmax, Vmax: int, H, V: array of int)
+{
+ rpic := image.chans[0];
+ gpic := image.chans[1];
+ bpic := image.chans[2];
+ minx := 8*Hmax*(mcu%nacross);
+ dx := 8*Hmax;
+ if(minx+dx > h.X)
+ dx = h.X-minx;
+ miny := 8*Vmax*(mcu/nacross);
+ dy := 8*Vmax;
+ if(miny+dy > h.Y)
+ dy = h.Y-miny;
+ pici := miny*h.X+minx;
+ H0 := H[0];
+ H1 := H[1];
+ H2 := H[2];
+ for(y:=0; y<dy; y++){
+ t := y*V[0];
+ b0 := H0*(t/(8*Vmax));
+ y0 := 8*((t/Vmax)&7);
+ t = y*V[1];
+ b1 := H1*(t/(8*Vmax));
+ y1 := 8*((t/Vmax)&7);
+ t = y*V[2];
+ b2 := H2*(t/(8*Vmax));
+ y2 := 8*((t/Vmax)&7);
+ x0 := 0;
+ x1 := 0;
+ x2 := 0;
+ for(x:=0; x<dx; x++){
+ Y := data0[b0][y0+x0++*H0/Hmax]+128.;
+ Cb := data1[b1][y1+x1++*H1/Hmax];
+ Cr := data2[b2][y2+x2++*H2/Hmax];
+ if(x0*H0/Hmax >= 8){
+ x0 = 0;
+ b0++;
+ }
+ if(x1*H1/Hmax >= 8){
+ x1 = 0;
+ b1++;
+ }
+ if(x2*H2/Hmax >= 8){
+ x2 = 0;
+ b2++;
+ }
+ r := int (Y+1.402*Cr);
+ g := int (Y-0.34414*Cb-0.71414*Cr);
+ b := int (Y+1.772*Cb);
+ rpic[pici+x] = clamp[r+CLAMPOFF];
+ gpic[pici+x] = clamp[g+CLAMPOFF];
+ bpic[pici+x] = clamp[b+CLAMPOFF];
+ }
+ pici += h.X;
+ }
+}
+
+# decode next 8-bit value from entropy-coded input. chart F-26
+decode(h: ref Header, t: ref Huffman): int
+{
+ maxcode := t.maxcode;
+ if(h.cnt < 8)
+ nextbyte(h, 0);
+ # fast lookup
+ code := (h.sr>>(h.cnt-8))&16rFF;
+ v := t.value[code];
+ if(v >= 0){
+ h.cnt -= t.shift[code];
+ return v;
+ }
+
+ h.cnt -= 8;
+ if(h.cnt == 0)
+ nextbyte(h, 0);
+ h.cnt--;
+ cnt := h.cnt;
+ m := 1<<cnt;
+ sr := h.sr;
+ code <<= 1;
+ i := 9;
+ for(;;i++){
+ if(sr & m)
+ code |= 1;
+ if(code <= maxcode[i])
+ break;
+ code <<= 1;
+ m >>= 1;
+ if(m == 0){
+ sr = nextbyte(h, 0);
+ m = 16r80;
+ cnt = 8;
+ }
+ cnt--;
+ }
+ h.cnt = cnt;
+ return t.val[t.valptr[i]+(code-t.mincode[i])];
+}
+
+#
+# load next byte of input
+# we should really just call h.fd.getb(), but it's faster just to use Bufio
+# to load big chunks and manage our own byte-at-a-time input.
+#
+nextbyte(h: ref Header, marker: int): int
+{
+ b := int h.buf[h.bufi++];
+ if(b == 16rFF){
+ # check for sentinel at end of buffer
+ if(h.bufi >= h.nbuf){
+ underflow := (h.bufi > h.nbuf);
+ h.nbuf = h.fd.read(h.buf, NBUF);
+ if(h.nbuf <= 0){
+ h.ch <-= (nil, readerror());
+ exit;
+ }
+ h.buf[h.nbuf] = byte 16rFF;
+ h.bufi = 0;
+ if(underflow) # if ran off end of buffer, just restart
+ return nextbyte(h, marker);
+ }
+ if(marker)
+ return b;
+ b2 := h.buf[h.bufi++];
+ if(b2 != byte 0){
+ if(b2 == DNL){
+ h.ch <-= (nil, "ReadJPG: DNL marker unimplemented");
+ exit;
+ }else if(b2<RST && RST7<b2){
+ h.ch <-= (nil, sys->sprint("ReadJPG: unrecognized marker %x", int b2));
+ exit;
+ }
+ # decode is reading into restart marker; satisfy it and restore state
+ if(h.bufi < 2){
+ # misery: must shift up buffer
+ h.buf[1:] = h.buf[0:h.nbuf+1];
+ h.nbuf++;
+ h.buf[0] = byte 16rFF;
+ h.bufi -= 1;
+ }else
+ h.bufi -= 2;
+ b = 16rFF;
+ }
+ }
+ h.cnt += 8;
+ h.sr = (h.sr<<8)|b;
+ return b;
+}
+
+# return next s bits of input, MSB first, and level shift it
+receive(h: ref Header, s: int): int
+{
+ while(h.cnt < s)
+ nextbyte(h, 0);
+ v := h.sr >> (h.cnt-s);
+ m := (1<<s);
+ v &= m-1;
+ h.cnt -= s;
+ # level shift
+ if(v < (m>>1))
+ v += ~(m-1)+1;
+ return v;
+}
+
+# IDCT based on Arai, Agui, and Nakajima, using flow chart Figure 4.8
+# of Pennebaker & Mitchell, JPEG: Still Image Data Compression Standard.
+# Remember IDCT is reverse of flow of DCT.
+
+a0: con 1.414;
+a1: con 0.707;
+a2: con 0.541;
+a3: con 0.707;
+a4: con 1.307;
+a5: con -0.383;
+
+# scaling factors from eqn 4-35 of P&M
+s1: con 1.0196;
+s2: con 1.0823;
+s3: con 1.2026;
+s4: con 1.4142;
+s5: con 1.8000;
+s6: con 2.6131;
+s7: con 5.1258;
+
+# overall normalization of 1/16, folded into premultiplication on vertical pass
+scale: con 0.0625;
+
+idct(zin: array of real, zout: array of real)
+{
+ x, y: int;
+
+ r := array[8*8] of real;
+
+ # transform horizontally
+ for(y=0; y<8; y++){
+ eighty := y<<3;
+ # if all non-DC components are zero, just propagate the DC term
+ if(zin[eighty+1]==0.)
+ if(zin[eighty+2]==0. && zin[eighty+3]==0.)
+ if(zin[eighty+4]==0. && zin[eighty+5]==0.)
+ if(zin[eighty+6]==0. && zin[eighty+7]==0.){
+ v := zin[eighty]*a0;
+ r[eighty+0] = v;
+ r[eighty+1] = v;
+ r[eighty+2] = v;
+ r[eighty+3] = v;
+ r[eighty+4] = v;
+ r[eighty+5] = v;
+ r[eighty+6] = v;
+ r[eighty+7] = v;
+ continue;
+ }
+
+ # step 5
+ in1 := s1*zin[eighty+1];
+ in3 := s3*zin[eighty+3];
+ in5 := s5*zin[eighty+5];
+ in7 := s7*zin[eighty+7];
+ f2 := s2*zin[eighty+2];
+ f3 := s6*zin[eighty+6];
+ f5 := (in1+in7);
+ f7 := (in5+in3);
+
+ # step 4
+ g2 := f2-f3;
+ g4 := (in5-in3);
+ g6 := (in1-in7);
+ g7 := f5+f7;
+
+ # step 3.5
+ t := (g4+g6)*a5;
+
+ # step 3
+ f0 := a0*zin[eighty+0];
+ f1 := s4*zin[eighty+4];
+ f3 += f2;
+ f2 = a1*g2;
+
+ # step 2
+ g0 := f0+f1;
+ g1 := f0-f1;
+ g3 := f2+f3;
+ g4 = t-a2*g4;
+ g5 := a3*(f5-f7);
+ g6 = a4*g6+t;
+
+ # step 1
+ f0 = g0+g3;
+ f1 = g1+f2;
+ f2 = g1-f2;
+ f3 = g0-g3;
+ f5 = g5-g4;
+ f6 := g5+g6;
+ f7 = g6+g7;
+
+ # step 6
+ r[eighty+0] = (f0+f7);
+ r[eighty+1] = (f1+f6);
+ r[eighty+2] = (f2+f5);
+ r[eighty+3] = (f3-g4);
+ r[eighty+4] = (f3+g4);
+ r[eighty+5] = (f2-f5);
+ r[eighty+6] = (f1-f6);
+ r[eighty+7] = (f0-f7);
+ }
+
+ # transform vertically
+ for(x=0; x<8; x++){
+ # step 5
+ in1 := scale*s1*r[x+8];
+ in3 := scale*s3*r[x+24];
+ in5 := scale*s5*r[x+40];
+ in7 := scale*s7*r[x+56];
+ f2 := scale*s2*r[x+16];
+ f3 := scale*s6*r[x+48];
+ f5 := (in1+in7);
+ f7 := (in5+in3);
+
+ # step 4
+ g2 := f2-f3;
+ g4 := (in5-in3);
+ g6 := (in1-in7);
+ g7 := f5+f7;
+
+ # step 3.5
+ t := (g4+g6)*a5;
+
+ # step 3
+ f0 := scale*a0*r[x];
+ f1 := scale*s4*r[x+32];
+ f3 += f2;
+ f2 = a1*g2;
+
+ # step 2
+ g0 := f0+f1;
+ g1 := f0-f1;
+ g3 := f2+f3;
+ g4 = t-a2*g4;
+ g5 := a3*(f5-f7);
+ g6 = a4*g6+t;
+
+ # step 1
+ f0 = g0+g3;
+ f1 = g1+f2;
+ f2 = g1-f2;
+ f3 = g0-g3;
+ f5 = g5-g4;
+ f6 := g5+g6;
+ f7 = g6+g7;
+
+ # step 6
+ zout[x] = (f0+f7);
+ zout[x+8] = (f1+f6);
+ zout[x+16] = (f2+f5);
+ zout[x+24] = (f3-g4);
+ zout[x+32] = (f3+g4);
+ zout[x+40] = (f2-f5);
+ zout[x+48] = (f1-f6);
+ zout[x+56] = (f0-f7);
+ }
+}
diff --git a/appl/lib/readpicfile.b b/appl/lib/readpicfile.b
new file mode 100644
index 00000000..a8983d24
--- /dev/null
+++ b/appl/lib/readpicfile.b
@@ -0,0 +1,164 @@
+implement RImagefile;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "imagefile.m";
+
+Header: adt
+{
+ fd: ref Iobuf;
+ ch: chan of (ref Rawimage, string);
+ # variables in i/o routines
+ buf: array of byte;
+ bufi: int;
+ nbuf: int;
+
+ TYPE: string;
+ CHAN: string;
+ NCHAN: string;
+ CMAP: int;
+
+ dx: int;
+ dy: int;
+};
+
+NBUF: con 8*1024;
+
+init(iomod: Bufio)
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ bufio = iomod;
+}
+
+read(fd: ref Iobuf): (ref Rawimage, string)
+{
+ # spawn a subprocess so I/O errors can clean up easily
+
+ ch := chan of (ref Rawimage, string);
+ spawn readslave(fd, ch);
+
+ return <-ch;
+}
+
+readmulti(fd: ref Iobuf): (array of ref Rawimage, string)
+{
+ (i, err) := read(fd);
+ if(i != nil){
+ a := array[1] of { i };
+ return (a, err);
+ }
+ return (nil, err);
+}
+
+readslave(fd: ref Iobuf, ch: chan of (ref Rawimage, string))
+{
+ (header, err) := header(fd, ch);
+ if(header == nil){
+ ch <-= (nil, err);
+ exit;
+ }
+
+ ch <-= image(header);
+}
+
+readerror(): string
+{
+ return sys->sprint("ReadPIC: read error: %r");
+}
+
+header(fd: ref Iobuf, ch: chan of (ref Rawimage, string)): (ref Header, string)
+{
+ h := ref Header;
+
+ h.fd = fd;
+ h.ch = ch;
+ h.CMAP = 0;
+ h.dx = 0;
+ h.dy = 0;
+ cantparse := "ReadPIC: can't parse header";
+ for(;;){
+ s := fd.gets('\n');
+ if(s==nil || s[len s-1]!='\n')
+ return (nil, cantparse);
+ if(s == "\n")
+ break;
+ addfield(h, s[0:len s-1]);
+ }
+ if(h.dx<=0 || h.dy<=0)
+ return (nil, "ReadPIC: empty picture or WINDOW not set");
+ return (h, nil);
+}
+
+addfield(h: ref Header, s: string)
+{
+ baddata := "ReadPIC: not a PIC header";
+ for(i:=0; i<len s; i++){
+ if(s[i] == '=')
+ break;
+ if(s[i]==0 || s[i]>16r7f){
+ h.ch <-= (nil, baddata);
+ exit;
+ }
+ }
+ if(i == len s){
+ h.ch <-= (nil, baddata);
+ exit;
+ }
+ case s[0:i]{
+ "TYPE" =>
+ h.TYPE = s[i+1:];
+ "CHAN" =>
+ h.CHAN = s[i+1:];
+ "NCHAN" =>
+ h.NCHAN = s[i+1:];
+ "CMAP" =>
+ h.CMAP = 1;
+ "WINDOW" =>
+ (n, l) := sys->tokenize(s[i+1:], " ");
+ if(n != 4){
+ h.ch <-= (nil, "ReadPIC: bad WINDOW specification");
+ exit;
+ }
+ x0 := int hd l;
+ l = tl l;
+ y0 := int hd l;
+ l = tl l;
+ h.dx = int hd l - x0;
+ l = tl l;
+ h.dy = int hd l - y0;
+ }
+}
+
+image(h: ref Header): (ref Rawimage, string)
+{
+ if(h.TYPE!="dump" || h.CHAN!="rgb" || h.NCHAN!="3" || h.CMAP)
+ return (nil, "ReadPIC: can't handle this type of picture");
+
+ i := ref Rawimage;
+ i.r = ((0,0), (h.dx, h.dy));
+ i.cmap = nil;
+ i.transp = 0;
+ i.trindex = byte 0;
+ i.nchans = int h.NCHAN;
+ i.chans = array[i.nchans] of array of byte;
+ for(j:=0; j<i.nchans; j++)
+ i.chans[j] = array[h.dx*h.dy] of byte;
+ i.chandesc = CRGB;
+ n := h.dx*h.dy;
+ b := array[i.nchans*n] of byte;
+ if(h.fd.read(b, len b) != len b)
+ return (nil, "ReadPIC: file too short");
+ l := 0;
+ for(j=0; j<n; j++)
+ for(k:=0; k<i.nchans; k++)
+ i.chans[k][j] = b[l++];
+ return (i, nil);
+}
diff --git a/appl/lib/readpng.b b/appl/lib/readpng.b
new file mode 100644
index 00000000..9cff27ec
--- /dev/null
+++ b/appl/lib/readpng.b
@@ -0,0 +1,823 @@
+implement RImagefile;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+ draw: Draw;
+ Point: import Draw;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "imagefile.m";
+
+include "crc.m";
+ crc: Crc;
+ CRCstate: import Crc;
+
+include "filter.m";
+ inflate: Filter;
+
+Chunk: adt {
+ size : int;
+ typ: string;
+ crc_state: ref CRCstate;
+};
+
+Png: adt {
+ depth: int;
+ filterbpp: int;
+ colortype: int;
+ compressionmethod: int;
+ filtermethod: int;
+ interlacemethod: int;
+ # tRNS
+ PLTEsize: int;
+ tRNS: array of byte;
+ # state for managing unpacking
+ alpha: int;
+ done: int;
+ error: string;
+ row, rowstep, colstart, colstep: int;
+ phase: int;
+ phasecols: int;
+ phaserows: int;
+ rowsize: int;
+ rowbytessofar: int;
+ thisrow: array of byte;
+ lastrow: array of byte;
+};
+
+init(iomod: Bufio)
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ if(crc == nil)
+ crc = load Crc Crc->PATH;
+ if(inflate == nil)
+ inflate = load Filter "/dis/lib/inflate.dis";
+ inflate->init();
+ bufio = iomod;
+}
+
+readmulti(fd: ref Iobuf): (array of ref Rawimage, string)
+{
+ (i, err) := read(fd);
+ if(i != nil){
+ a := array[1] of { i };
+ return (a, err);
+ }
+ return (nil, err);
+}
+
+read(fd: ref Iobuf): (ref Rawimage, string)
+{
+ chunk := ref Chunk;
+ png := ref Png;
+ raw := ref Rawimage;
+
+ chunk.crc_state = crc->init(0, int 16rffffffff);
+# Check it's a PNG
+ if (!get_signature(fd))
+ return (nil, "not a PNG");
+# Get the IHDR
+ if (!get_chunk_header(fd, chunk))
+ return (nil, "duff header");
+ if (chunk.typ != "IHDR")
+ return (nil, "IHDR must come first");
+ if (chunk.size != 13)
+ return (nil, "IHDR wrong size");
+ raw.r.max.x = get_int(fd, chunk.crc_state);
+ if (raw.r.max.x <= 0)
+ return (nil, "invalid width");
+ raw.r.max.y = get_int(fd, chunk.crc_state);
+ if (raw.r.max.y <= 0)
+ return (nil, "invalid height");
+ png.depth = get_byte(fd, chunk.crc_state);
+ case png.depth {
+ 1 or 2 or 4 or 8 or 16 =>
+ ;
+ * =>
+ return (nil, "invalid depth");
+ }
+ png.colortype = get_byte(fd, chunk.crc_state);
+
+ okcombo : int;
+
+ case png.colortype {
+ 0 =>
+ okcombo = 1;
+ raw.nchans = 1;
+ raw.chandesc = RImagefile->CY;
+ png.alpha = 0;
+ 2 =>
+ okcombo = (png.depth == 8 || png.depth == 16);
+ raw.nchans = 3;
+ raw.chandesc = RImagefile->CRGB;
+ png.alpha = 0;
+ 3 =>
+ okcombo = (png.depth != 16);
+ raw.nchans = 1;
+ raw.chandesc = RImagefile->CRGB1;
+ png.alpha = 0;
+ 4 =>
+ okcombo = (png.depth == 8 || png.depth == 16);
+ raw.nchans = 1;
+ raw.chandesc = RImagefile->CY;
+ png.alpha = 1;
+ 6 =>
+ okcombo = (png.depth == 8 || png.depth == 16);
+ raw.nchans = 3;
+ raw.chandesc = RImagefile->CRGB;
+ png.alpha = 1;
+ * =>
+ return (nil, "invalid colortype");
+ }
+ if (!okcombo)
+ return (nil, "invalid depth/colortype combination");
+ png.compressionmethod = get_byte(fd, chunk.crc_state);
+ if (png.compressionmethod != 0)
+ return (nil, "invalid compression method " + string png.compressionmethod);
+ png.filtermethod = get_byte(fd, chunk.crc_state);
+ if (png.filtermethod != 0)
+ return (nil, "invalid filter method");
+ png.interlacemethod = get_byte(fd, chunk.crc_state);
+ if (png.interlacemethod != 0 && png.interlacemethod != 1)
+ return (nil, "invalid interlace method");
+ if(0)
+ sys->print("width %d height %d depth %d colortype %d interlace %d\n",
+ raw.r.max.x, raw.r.max.y, png.depth, png.colortype, png.interlacemethod);
+ if (!get_crc_and_check(fd, chunk))
+ return (nil, "invalid CRC");
+# Stash some detail in raw
+ raw.r.min = Point(0, 0);
+ raw.transp = 0;
+ raw.chans = array[raw.nchans] of array of byte;
+ {
+ for (r:= 0; r < raw.nchans; r++)
+ raw.chans[r] = array[raw.r.max.x * raw.r.max.y] of byte;
+ }
+# Get the next chunk
+ seenPLTE := 0;
+ seenIDAT := 0;
+ seenLastIDAT := 0;
+ inflateFinished := 0;
+ seenIEND := 0;
+ seentRNS := 0;
+ rq: chan of ref Filter->Rq;
+
+ png.error = nil;
+ rq = nil;
+ while (png.error == nil) {
+ if (!get_chunk_header(fd, chunk)) {
+ if (!seenIEND)
+ png.error = "duff header";
+ break;
+ }
+ if (seenIEND) {
+ png.error = "rubbish at eof";
+ break;
+ }
+ case (chunk.typ) {
+ "IEND" =>
+ seenIEND = 1;
+ "PLTE" =>
+ if (seenPLTE) {
+ png.error = "too many PLTEs";
+ break;
+ }
+ if (seentRNS) {
+ png.error = "tRNS before PLTE";
+ break;
+ }
+ if (seenIDAT) {
+ png.error = "PLTE too late";
+ break;
+ }
+ if (chunk.size % 3 || chunk.size < 1 * 3 || chunk.size > 256 * 3) {
+ png.error = "PLTE strange size";
+ break;
+ }
+ if (png.colortype == 0 || png.colortype == 4) {
+ png.error = "superfluous PLTE";
+ break;
+ }
+ raw.cmap = array[256 * 3] of byte;
+ png.PLTEsize = chunk.size / 3;
+ if (!get_bytes(fd, chunk.crc_state, raw.cmap, chunk.size)) {
+ png.error = "eof in PLTE";
+ break;
+ }
+# {
+# x: int;
+# sys->print("Palette:\n");
+# for (x = 0; x < chunk.size; x += 3)
+# sys->print("%3d: (%3d, %3d, %3d)\n",
+# x / 3, int raw.cmap[x], int raw.cmap[x + 1], int raw.cmap[x + 2]);
+# }
+ seenPLTE = 1;
+ "tRNS" =>
+ if (seenIDAT) {
+ png.error = "tRNS too late";
+ break;
+ }
+ case png.colortype {
+ 0 =>
+ if (chunk.size != 2) {
+ png.error = "tRNS wrong size";
+ break;
+ }
+ level := get_ushort(fd, chunk.crc_state);
+ if (level < 0) {
+ png.error = "eof in tRNS";
+ break;
+ }
+ if (png.depth != 16) {
+ raw.transp = 1;
+ raw.trindex = byte level;
+ }
+ 2 =>
+ # a legitimate coding, but we can't use the information
+ if (!skip_bytes(fd, chunk.crc_state, chunk.size))
+ png.error = "eof in skipped tRNS chunk";
+ break;
+ 3 =>
+ if (!seenPLTE) {
+ png.error = "tRNS too early";
+ break;
+ }
+ if (chunk.size > png.PLTEsize) {
+ png.error = "tRNS too big";
+ break;
+ }
+ png.tRNS = array[png.PLTEsize] of byte;
+ for (x := chunk.size; x < png.PLTEsize; x++)
+ png.tRNS[x] = byte 255;
+ if (!get_bytes(fd, chunk.crc_state, png.tRNS, chunk.size)) {
+ png.error = "eof in tRNS";
+ break;
+ }
+# {
+# sys->print("tRNS:\n");
+# for (x = 0; x < chunk.size; x++)
+# sys->print("%3d: (%3d)\n", x, int png.tRNS[x]);
+# }
+ if (png.error == nil) {
+ # analyse the tRNS chunk to see if it contains a single transparent index
+ # translucent entries are treated as opaque
+ for (x = 0; x < chunk.size; x++)
+ if (png.tRNS[x] == byte 0) {
+ raw.trindex = byte x;
+ if (raw.transp) {
+ raw.transp = 0;
+ break;
+ }
+ raw.transp = 1;
+ }
+# if (raw.transp)
+# sys->print("selected index %d\n", int raw.trindex);
+ }
+ 4 or 6 =>
+ png.error = "tRNS invalid when alpha present";
+ }
+ seentRNS = 1;
+ "IDAT" =>
+ if (seenLastIDAT) {
+ png.error = "non contiguous IDATs";
+ break;
+ }
+ if (inflateFinished) {
+ png.error = "too many IDATs";
+ break;
+ }
+ remaining := 0;
+ if (!seenIDAT) {
+ # open channel to inflate filter
+ if (!processdatainit(png, raw))
+ break;
+ rq = inflate->start(nil);
+ skip_bytes(fd, chunk.crc_state, 2);
+ remaining = chunk.size - 2;
+ }
+ else
+ remaining = chunk.size;
+ while (remaining && png.error == nil) {
+ pick m := <- rq {
+ Fill =>
+# sys->print("Fill(%d) remaining %d\n", len m.buf, remaining);
+ toget := len m.buf;
+ if (toget > remaining)
+ toget = remaining;
+ if (!get_bytes(fd, chunk.crc_state, m.buf, toget)) {
+ m.reply <-= -1;
+ png.error = "eof during IDAT";
+ break;
+ }
+ m.reply <-= toget;
+ remaining -= toget;
+ Result =>
+# sys->print("Result(%d)\n", len m.buf);
+ m.reply <-= 0;
+ processdata(png, raw, m.buf);
+ Info =>
+# sys->print("Info(%s)\n", m.msg);
+ Finished =>
+ inflateFinished = 1;
+# sys->print("Finished\n");
+ Error =>
+ return (nil, "inflate error\n");
+ }
+ }
+ seenIDAT = 1;
+ * =>
+ # skip the blighter
+ if (!skip_bytes(fd, chunk.crc_state, chunk.size))
+ png.error = "eof in skipped chunk";
+ }
+ if (png.error != nil)
+ break;
+ if (!get_crc_and_check(fd, chunk))
+ return (nil, "invalid CRC");
+ if (chunk.typ != "IDAT" && seenIDAT)
+ seenLastIDAT = 1;
+ }
+ # can only get here if IEND was last chunk, or png.error set
+
+ if (png.error == nil && !seenIDAT) {
+ png.error = "no IDAT!";
+ inflateFinished = 1;
+ }
+ while (rq != nil && !inflateFinished) {
+ pick m := <-rq {
+ Fill =>
+# sys->print("Fill(%d)\n", len m.buf);
+ png.error = "eof in zlib stream";
+ m.reply <-= -1;
+ inflateFinished = 1;
+ Result =>
+# sys->print("Result(%d)\n", len m.buf);
+ if (png.error != nil) {
+ m.reply <-= -1;
+ inflateFinished = 1;
+ }
+ else {
+ m.reply <-= 0;
+ processdata(png, raw, m.buf);
+ }
+ Info =>
+# sys->print("Info(%s)\n", m.msg);
+ Finished =>
+# sys->print("Finished\n");
+ inflateFinished = 1;
+ break;
+ Error =>
+ png.error = "inflate error\n";
+ inflateFinished = 1;
+ }
+
+ }
+ if (png.error == nil && !png.done)
+ png.error = "insufficient data";
+ return (raw, png.error);
+}
+
+phase2stepping(phase: int): (int, int, int, int)
+{
+ case phase {
+ 0 =>
+ return (0, 1, 0, 1);
+ 1 =>
+ return (0, 8, 0, 8);
+ 2 =>
+ return (0, 8, 4, 8);
+ 3 =>
+ return (4, 8, 0, 4);
+ 4 =>
+ return (0, 4, 2, 4);
+ 5 =>
+ return (2, 4, 0, 2);
+ 6 =>
+ return (0, 2, 1, 2);
+ 7 =>
+ return (1, 2, 0, 1);
+ * =>
+ return (-1, -1, -1, -1);
+ }
+}
+
+processdatainitphase(png: ref Png, raw: ref Rawimage)
+{
+ (png.row, png.rowstep, png.colstart, png.colstep) = phase2stepping(png.phase);
+ if (raw.r.max.x > png.colstart)
+ png.phasecols = (raw.r.max.x - png.colstart + png.colstep - 1) / png.colstep;
+ else
+ png.phasecols = 0;
+ if (raw.r.max.y > png.row)
+ png.phaserows = (raw.r.max.y - png.row + png.rowstep - 1) / png.rowstep;
+ else
+ png.phaserows = 0;
+ png.rowsize = png.phasecols * (raw.nchans + png.alpha) * png.depth;
+ png.rowsize = (png.rowsize + 7) / 8;
+ png.rowsize++; # for the filter byte
+ png.rowbytessofar = 0;
+ png.thisrow = array[png.rowsize] of byte;
+ png.lastrow = array[png.rowsize] of byte;
+# sys->print("init phase %d: r (%d, %d, %d) c (%d, %d, %d) (%d)\n",
+# png.phase, png.row, png.rowstep, png.phaserows,
+# png.colstart, png.colstep, png.phasecols, png.rowsize);
+}
+
+processdatainit(png: ref Png, raw: ref Rawimage): int
+{
+ if (raw.nchans != 1&& raw.nchans != 3) {
+ png.error = "only 1 or 3 channels supported";
+ return 0;
+ }
+# if (png.interlacemethod != 0) {
+# png.error = "only progressive supported";
+# return 0;
+# }
+ if (png.colortype == 3 && raw.cmap == nil) {
+ png.error = "PLTE chunk missing";
+ return 0;
+ }
+ png.done = 0;
+ png.filterbpp = (png.depth * (raw.nchans + png.alpha) + 7) / 8;
+ png.phase = png.interlacemethod;
+
+ processdatainitphase(png, raw);
+
+ return 1;
+}
+
+upconvert(out: array of byte, outstride: int, in: array of byte, pixels: int, bpp: int)
+{
+ b: byte;
+ bits := pixels * bpp;
+ lim := bits / 8;
+ mask := byte ((1 << bpp) - 1);
+ outx := 0;
+ inx := 0;
+ for (x := 0; x < lim; x++) {
+ b = in[inx];
+ for (s := 8 - bpp; s >= 0; s -= bpp) {
+ pixel := (b >> s) & mask;
+ ucp := pixel;
+ for (y := bpp; y < 8; y += bpp)
+ ucp |= pixel << y;
+ out[outx] = ucp;
+ outx += outstride;
+ }
+ inx++;
+ }
+ residue := (bits % 8) / bpp;
+ if (residue) {
+ b = in[inx];
+ for (s := 8 - bpp; s >= 0; s -= bpp) {
+ pixel := (b >> s) & mask;
+ ucp := pixel;
+ for (y := bpp; y < 8; y += bpp)
+ ucp |= pixel << y;
+ out[outx] = ucp;
+ outx += outstride;
+ if (--residue <= 0)
+ break;
+ }
+ }
+}
+
+# expand (1 or 2 or 4) bit to 8 bit without scaling (for palletized stuff)
+
+expand(out: array of byte, outstride: int, in: array of byte, pixels: int, bpp: int)
+{
+ b: byte;
+ bits := pixels * bpp;
+ lim := bits / 8;
+ mask := byte ((1 << bpp) - 1);
+ outx := 0;
+ inx := 0;
+ for (x := 0; x < lim; x++) {
+ b = in[inx];
+ for (s := 8 - bpp; s >= 0; s -= bpp) {
+ out[outx] = (b >> s) & mask;
+ outx += outstride;
+ }
+ inx++;
+ }
+ residue := (bits % 8) / bpp;
+ if (residue) {
+ b = in[inx];
+ for (s := 8 - bpp; s >= 0; s -= bpp) {
+ out[outx] = (b >> s) & mask;
+ outx += outstride;
+ if (--residue <= 0)
+ break;
+ }
+ }
+}
+
+copybytes(out: array of byte, outstride: int, in: array of byte, instride: int, pixels: int)
+{
+ inx := 0;
+ outx := 0;
+ for (x := 0; x < pixels; x++) {
+ out[outx] = in[inx];
+ inx += instride;
+ outx += outstride;
+ }
+}
+
+outputrow(png: ref Png, raw: ref Rawimage, row: array of byte)
+{
+ offset := png.row * raw.r.max.x;
+ case raw.nchans {
+ 1 =>
+ case (png.depth) {
+ * =>
+ png.error = "depth not supported";
+ return;
+ 1 or 2 or 4 =>
+ if (raw.chandesc == RImagefile->CRGB1)
+ expand(raw.chans[0][offset + png.colstart:], png.colstep, row, png.phasecols, png.depth);
+ else
+ upconvert(raw.chans[0][offset + png.colstart:], png.colstep, row, png.phasecols, png.depth);
+ 8 or 16 =>
+ # might have an Alpha channel to ignore!
+ stride := (png.alpha + 1) * png.depth / 8;
+ copybytes(raw.chans[0][offset + png.colstart:], png.colstep, row, stride, png.phasecols);
+ }
+ 3 =>
+ case (png.depth) {
+ * =>
+ png.error = "depth not supported (2)";
+ return;
+ 8 or 16 =>
+ # split rgb into three channels
+ bytespc := png.depth / 8;
+ stride := (3 + png.alpha) * bytespc;
+ copybytes(raw.chans[0][offset + png.colstart:], png.colstep, row, stride, png.phasecols);
+ copybytes(raw.chans[1][offset + png.colstart:], png.colstep, row[bytespc:], stride, png.phasecols);
+ copybytes(raw.chans[2][offset + png.colstart:], png.colstep, row[bytespc * 2:], stride, png.phasecols);
+ }
+ }
+}
+
+filtersub(png: ref Png)
+{
+ subx := 1;
+ for (x := int png.filterbpp + 1; x < png.rowsize; x++) {
+ png.thisrow[x] += png.thisrow[subx];
+ subx++;
+ }
+}
+
+filterup(png: ref Png)
+{
+ if (png.row == 0)
+ return;
+ for (x := 1; x < png.rowsize; x++)
+ png.thisrow[x] += png.lastrow[x];
+}
+
+filteraverage(png: ref Png)
+{
+ for (x := 1; x < png.rowsize; x++) {
+ a: int;
+ if (x > png.filterbpp)
+ a = int png.thisrow[x - png.filterbpp];
+ else
+ a = 0;
+ if (png.row != 0)
+ a += int png.lastrow[x];
+ png.thisrow[x] += byte (a / 2);
+ }
+}
+
+filterpaeth(png: ref Png)
+{
+ a, b, c: byte;
+ p, pa, pb, pc: int;
+ for (x := 1; x < png.rowsize; x++) {
+ if (x > png.filterbpp)
+ a = png.thisrow[x - png.filterbpp];
+ else
+ a = byte 0;
+ if (png.row == 0) {
+ b = byte 0;
+ c = byte 0;
+ } else {
+ b = png.lastrow[x];
+ if (x > png.filterbpp)
+ c = png.lastrow[x - png.filterbpp];
+ else
+ c = byte 0;
+ }
+ p = int a + int b - int c;
+ pa = p - int a;
+ if (pa < 0)
+ pa = -pa;
+ pb = p - int b;
+ if (pb < 0)
+ pb = -pb;
+ pc = p - int c;
+ if (pc < 0)
+ pc = -pc;
+ if (pa <= pb && pa <= pc)
+ png.thisrow[x] += a;
+ else if (pb <= pc)
+ png.thisrow[x] += b;
+ else
+ png.thisrow[x] += c;
+ }
+}
+
+phaseendcheck(png: ref Png, raw: ref Rawimage): int
+{
+ if (png.row >= raw.r.max.y || png.rowsize <= 1) {
+ # this phase is over
+ if (png.phase == 0) {
+ png.done = 1;
+ }
+ else {
+ png.phase++;
+ if (png.phase > 7)
+ png.done = 1;
+ else
+ processdatainitphase(png, raw);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+processdata(png: ref Png, raw: ref Rawimage, buf: array of byte)
+{
+#sys->print("processdata(%d)\n", len buf);
+ if (png.error != nil)
+ return;
+ i := 0;
+ while (i < len buf) {
+ if (png.done) {
+ png.error = "too much data";
+ return;
+ }
+ if (phaseendcheck(png, raw))
+ continue;
+ tocopy := (png.rowsize - png.rowbytessofar);
+ if (tocopy > (len buf - i))
+ tocopy = len buf - i;
+ png.thisrow[png.rowbytessofar :] = buf[i : i + tocopy];
+ i += tocopy;
+ png.rowbytessofar += tocopy;
+ if (png.rowbytessofar >= png.rowsize) {
+ # a new row has arrived
+ # apply filter here
+#sys->print("phase %d row %d\n", png.phase, png.row);
+ case int png.thisrow[0] {
+ 0 =>
+ ;
+ 1 =>
+ filtersub(png);
+ 2 =>
+ filterup(png);
+ 3 =>
+ filteraverage(png);
+ 4 =>
+ filterpaeth(png);
+ * =>
+# sys->print("implement filter method %d\n", int png.thisrow[0]);
+ png.error = "filter method unsupported";
+ return;
+ }
+ # output row
+ if (png.row >= raw.r.max.y) {
+ png.error = "too much data";
+ return;
+ }
+ outputrow(png, raw, png.thisrow[1 :]);
+ png.row += png.rowstep;
+ save := png.lastrow;
+ png.lastrow = png.thisrow;
+ png.thisrow = save;
+ png.rowbytessofar = 0;
+ }
+ }
+ phaseendcheck(png, raw);
+}
+
+get_signature(fd: ref Iobuf): int
+{
+ sig := array[8] of { byte 137, byte 80, byte 78, byte 71, byte 13, byte 10, byte 26, byte 10 };
+ x: int;
+ for (x = 0; x < 8; x++)
+ if (fd.getb() != int sig[x])
+ return 0;
+ return 1;
+}
+
+get_bytes(fd: ref Iobuf, crc_state: ref CRCstate, buf: array of byte, n: int): int
+{
+ if (buf == nil) {
+ fd.seek(big n, bufio->SEEKRELA);
+ return 1;
+ }
+ if (fd.read(buf, n) != n)
+ return 0;
+ if (crc_state != nil)
+ crc->crc(crc_state, buf, n);
+ return 1;
+}
+
+skip_bytes(fd: ref Iobuf, crc_state: ref CRCstate, n: int): int
+{
+ buf := array[1024] of byte;
+ while (n) {
+ thistime: int = 1024;
+ if (thistime > n)
+ thistime = n;
+ if (!get_bytes(fd, crc_state, buf, thistime))
+ return 0;
+ n -= thistime;
+ }
+ return 1;
+}
+
+get_4(fd: ref Iobuf, crc_state: ref CRCstate, signed: int): (int, int)
+{
+ buf := array[4] of byte;
+ if (!get_bytes(fd, crc_state, buf, 4))
+ return (0, 0);
+ if (signed && int buf[0] & 16r80)
+ return (0, 0);
+ r:int = (int buf[0] << 24) | (int buf[1] << 16) | (int buf[2] << 8) | (int buf[3]);
+# sys->print("got int %d\n", r);
+ return (1, r);
+}
+
+get_int(fd: ref Iobuf, crc_state: ref CRCstate): int
+{
+ ok, r: int;
+ (ok, r) = get_4(fd, crc_state, 1);
+ if (ok)
+ return r;
+ return -1;
+}
+
+get_ushort(fd: ref Iobuf, crc_state: ref CRCstate): int
+{
+ buf := array[2] of byte;
+ if (!get_bytes(fd, crc_state, buf, 2))
+ return -1;
+ return (int buf[0] << 8) | int buf[1];
+}
+
+get_crc_and_check(fd: ref Iobuf, chunk: ref Chunk): int
+{
+ crc, ok: int;
+ (ok, crc) = get_4(fd, nil, 0);
+ if (!ok)
+ return 0;
+# sys->print("crc: computed %.8ux expected %.8ux\n", chunk.crc_state.crc, crc);
+ if (chunk.crc_state.crc != crc)
+ return 1;
+ return 1;
+}
+
+get_byte(fd: ref Iobuf, crc_state: ref CRCstate): int
+{
+ buf := array[1] of byte;
+ if (!get_bytes(fd, crc_state, buf, 1))
+ return -1;
+# sys->print("got byte %d\n", int buf[0]);
+ return int buf[0];
+}
+
+get_type(fd: ref Iobuf, crc_state: ref CRCstate): string
+{
+ x: int;
+ buf := array[4] of byte;
+ if (!get_bytes(fd, crc_state, buf, 4))
+ return nil;
+ for (x = 0; x < 4; x++) {
+ c: int;
+ c = int buf[x];
+ if (c == bufio->EOF || (c < 65 || c > 90 && c < 97) || c > 122)
+ return nil;
+ }
+ return string buf;
+}
+
+get_chunk_header(fd: ref Iobuf, chunk: ref Chunk): int
+{
+ chunk.size = get_int(fd, nil);
+ if (chunk.size < 0)
+ return 0;
+ crc->reset(chunk.crc_state);
+ chunk.typ = get_type(fd, chunk.crc_state);
+ if (chunk.typ == nil)
+ return 0;
+# sys->print("%s(%d)\n", chunk.typ, chunk.size);
+ return 1;
+}
diff --git a/appl/lib/readxbitmap.b b/appl/lib/readxbitmap.b
new file mode 100644
index 00000000..2c32b822
--- /dev/null
+++ b/appl/lib/readxbitmap.b
@@ -0,0 +1,131 @@
+implement RImagefile;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+ draw: Draw;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "imagefile.m";
+
+init(iomod: Bufio)
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ bufio = iomod;
+}
+
+readmulti(fd: ref Iobuf): (array of ref Rawimage, string)
+{
+ (i, err) := read(fd);
+ if(i != nil){
+ a := array[1] of { i };
+ return (a, err);
+ }
+ return (nil, err);
+}
+
+read(fd: ref Iobuf): (ref Rawimage, string)
+{
+ width, height, fnd: int;
+ (fnd, width) = get_define(fd);
+ if(fnd)
+ (fnd, height) = get_define(fd);
+ if(!fnd)
+ return (nil, "xbitmap doesn't start with width and height");
+ if(height <= 0 || width <= 0)
+ return (nil, "xbitmap has bad width or height");
+ # now, optional x_hot, y_hot
+ (fnd, nil) = get_define(fd);
+ if(fnd)
+ (fnd, nil) = get_define(fd);
+ # now expect 'static char x...x_bits[] = {'
+ if(!get_to_char(fd, '{'))
+ return (nil, "xbitmap premature eof");
+
+ bytesperline := (width+7) / 8;
+ pixels := array[width*height] of byte;
+ pixi := 0;
+ for(i := 0; i < height; i++) {
+ for(j := 0; j < bytesperline; j++) {
+ (vfnd, v) := get_hexbyte(fd);
+ if(!vfnd)
+ return (nil, "xbitmap premature eof");
+ kend := 7;
+ if(j == bytesperline-1)
+ kend = (width-1)%8;
+ for(k := 0; k <= kend; k++) {
+ if(v & (1<<k))
+ pixels[pixi] = byte 0;
+ else
+ pixels[pixi] = byte 1;
+ pixi++;
+ }
+ }
+ }
+ cmap := array[6] of {byte 0, byte 0, byte 0,
+ byte 255, byte 255, byte 255};
+ chans := array[1] of {pixels};
+ ans := ref Rawimage(Draw->Rect((0,0),(width,height)), cmap, 0, byte 0, 1, chans, CRGB1, 0);
+ return (ans, "");
+}
+
+# get a line, which should be of form
+# '#define fieldname val'
+# and return (found, integer rep of val)
+get_define(fd: ref Iobuf) : (int, int)
+{
+ c := fd.getc();
+ if(c != '#') {
+ fd.ungetc();
+ return (0, 0);
+ }
+ line := fd.gets('\n');
+ for(i := len line -1; i >= 0; i--)
+ if(line[i] == ' ')
+ break;
+ val := int line[i+1:];
+ return (1, val);
+}
+
+# read fd until get char cterm; return 1 if found
+get_to_char(fd: ref Iobuf, cterm: int) : int
+{
+ for(;;) {
+ c := fd.getc();
+ if(c < 0)
+ return c;
+ if(c == cterm)
+ return 1;
+ }
+}
+
+# read fd until get xDD, were DD are hex digits.
+# return (found, value of DD as integer)
+get_hexbyte(fd: ref Iobuf) : (int, int)
+{
+ if(!get_to_char(fd, 'x'))
+ return (0, 0);
+ n1 := hexdig(fd.getc());
+ n2 := hexdig(fd.getc());
+ if(n1 < 0 || n2 < 0)
+ return (0, 0);
+ return (1, (n1<<4) | n2);
+}
+
+hexdig(c: int) : int
+{
+ if('0' <= c && c <= '9')
+ c -= '0';
+ else if('a' <= c && c <= 'f')
+ c += 10 - 'a';
+ else if('A' <= c && c <= 'F')
+ c += 10 - 'A';
+ else
+ c = -1;
+ return c;
+}
diff --git a/appl/lib/regex.b b/appl/lib/regex.b
new file mode 100644
index 00000000..3e94df3f
--- /dev/null
+++ b/appl/lib/regex.b
@@ -0,0 +1,389 @@
+implement Regex;
+
+include "regex.m";
+
+# syntax
+
+# RE ALT regular expression
+# NUL
+# ALT CAT alternation
+# CAT | ALT
+#
+# CAT DUP catenation
+# DUP CAT
+#
+# DUP PRIM possibly duplicated primary
+# PCLO
+# CLO
+# OPT
+#
+# PCLO PRIM + 1 or more
+# CLO PRIM * 0 or more
+# OPT PRIM ? 0 or 1
+#
+# PRIM ( RE )
+# ()
+# DOT any character
+# CHAR a single character
+# ESC escape sequence
+# [ SET ] character set
+# NUL null string
+# HAT beginning of string
+# DOL end of string
+#
+
+NIL : con -1; # a refRex constant
+NONE: con -2; # ditto, for an un-set value
+BAD: con 1<<16; # a non-character
+HUGE: con (1<<31) - 1;
+
+# the data structures of re.m would like to be ref-linked, but are
+# circular (see fn walk), thus instead of pointers we use indexes
+# into an array (arena) of nodes of the syntax tree of a regular expression.
+# from a storage-allocation standpoint, this replaces many small
+# allocations of one size with one big one of variable size.
+
+ReStr: adt {
+ s : string;
+ i : int; # cursor postion
+ n : int; # number of chars left; -1 on error
+ peek : fn(s: self ref ReStr): int;
+ next : fn(s: self ref ReStr): int;
+};
+
+ReStr.peek(s: self ref ReStr): int
+{
+ if(s.n <= 0)
+ return BAD;
+ return s.s[s.i];
+}
+
+ReStr.next(s: self ref ReStr): int
+{
+ if(s.n <= 0)
+ return BAD;
+ s.n--;
+ return s.s[s.i++];
+}
+
+newRe(kind: int, left, right: refRex, set: ref Set, ar: ref Arena, pno: int): refRex
+{
+ ar.rex[ar.ptr] = Rex(kind, left, right, set, pno);
+ return ar.ptr++;
+}
+
+# parse a regex by recursive descent to get a syntax tree
+
+re(s: ref ReStr, ar: ref Arena): refRex
+{
+ left := cat(s, ar);
+ if(left==NIL || s.peek()!='|')
+ return left;
+ s.next();
+ right := re(s, ar);
+ if(right == NIL)
+ return NIL;
+ return newRe(ALT, left, right, nil, ar, 0);
+}
+
+cat(s: ref ReStr, ar: ref Arena): refRex
+{
+ left := dup(s, ar);
+ if(left == NIL)
+ return left;
+ right := cat(s, ar);
+ if(right == NIL)
+ return left;
+ return newRe(CAT, left, right, nil, ar, 0);
+}
+
+dup(s: ref ReStr, ar: ref Arena): refRex
+{
+ case s.peek() {
+ BAD or ')' or ']' or '|' or '?' or '*' or '+' =>
+ return NIL;
+ }
+ prim: refRex;
+ case kind:=s.next() {
+ '(' => if(ar.pno < 0) {
+ if(s.peek() == ')') {
+ s.next();
+ prim = newRe(NUL, NONE, NONE, nil, ar, 0);
+ } else {
+ prim = re(s, ar);
+ if(prim==NIL || s.next()!=')')
+ s.n = -1;
+ }
+ } else {
+ pno := ++ar.pno;
+ lp := newRe(LPN, NONE, NONE, nil, ar, pno);
+ rp := newRe(RPN, NONE, NONE, nil, ar, pno);
+ if(s.peek() == ')') {
+ s.next();
+ prim = newRe(CAT, lp, rp, nil, ar, 0);
+
+ } else {
+ prim = re(s, ar);
+ if(prim==NIL || s.next()!=')')
+ s.n = -1;
+ else {
+ prim = newRe(CAT, prim, rp, nil, ar, 0);
+ prim = newRe(CAT, lp, prim, nil, ar, 0);
+ }
+ }
+ }
+ '[' => prim = newRe(SET, NONE, NONE, newSet(s), ar, 0);
+ * => case kind {
+ '.' => kind = DOT;
+ '^' => kind = HAT;
+ '$' => kind = DOL;
+ }
+ prim = newRe(esc(s, kind), NONE, NONE, nil, ar, 0);
+ }
+ case s.peek() {
+ '*' => kind = CLO;
+ '+' => kind = PCLO;
+ '?' => kind = OPT;
+ * => return prim;
+ }
+ s.next();
+ return newRe(kind, prim, NONE, nil, ar, 0);
+}
+
+esc(s: ref ReStr, char: int): int
+{
+ if(char == '\\') {
+ char = s.next();
+ case char {
+ BAD => s.n = -1;
+ 'n' => char = '\n';
+ }
+ }
+ return char;
+}
+
+# walk the tree adjusting pointers to refer to
+# next state of the finite state machine
+
+walk(r: refRex, succ: refRex, ar: ref Arena)
+{
+ if(r==NONE)
+ return;
+ rex := ar.rex[r];
+ case rex.kind {
+ ALT => walk(rex.left, succ, ar);
+ walk(rex.right, succ, ar);
+ return;
+ CAT => walk(rex.left, rex.right, ar);
+ walk(rex.right, succ, ar);
+ ar.rex[r] = ar.rex[rex.left]; # optimization
+ return;
+ CLO or PCLO =>
+ end := newRe(OPT, r, succ, nil, ar, 0); # here's the circularity
+ walk(rex.left, end, ar);
+ OPT => walk(rex.left, succ, ar);
+ }
+ ar.rex[r].right = succ;
+}
+
+compile(e: string, flag: int): (Re, string)
+{
+ if(e == nil)
+ return (nil, "missing expression");
+ s := ref ReStr(e, 0, len e);
+ ar := ref Arena(array[2*s.n] of Rex, 0, 0, (flag&1)-1);
+ start := ar.start = re(s, ar);
+ if(start==NIL || s.n!=0)
+ return (nil, "invalid regular expression");
+ walk(start, NIL, ar);
+ if(ar.pno < 0)
+ ar.pno = 0;
+ return (ar, nil);
+}
+
+# todo1, todo2: queues for epsilon and advancing transitions
+Gaz: adt {
+ pno: int;
+ beg: int;
+ end: int;
+};
+Trace: adt {
+ cre: refRex; # cursor in Re
+ beg: int; # where this trace began;
+ gaz: list of Gaz;
+};
+Queue: adt {
+ ptr: int;
+ q: array of Trace;
+};
+
+execute(re: Re, s: string): array of (int, int)
+{
+ return executese(re, s, (-1,-1), 1, 1);
+}
+
+executese(re: Re, s: string, range: (int, int), bol: int, eol: int): array of (int,int)
+{
+ if(re==nil)
+ return nil;
+ (s0, s1) := range;
+ if(s0 < 0)
+ s0 = 0;
+ if(s1 < 0)
+ s1 = len s;
+ gaz : list of Gaz;
+ (beg, end) := (-1, -1);
+ todo1 := ref Queue(0, array[re.ptr] of Trace);
+ todo2 := ref Queue(0, array[re.ptr] of Trace);
+ for(i:=s0; i<=s1; i++) {
+ small2 := HUGE; # earliest possible match if advance
+ if(beg == -1) # no leftmost match yet
+ todo1.q[todo1.ptr++] = Trace(re.start, i, nil);
+ for(k:=0; k<todo1.ptr; k++) {
+ q := todo1.q[k];
+ rex := re.rex[q.cre];
+ next1 := next2 := NONE;
+ case rex.kind {
+ NUL =>
+ next1 = rex.right;
+ DOT =>
+ if(i<len s && s[i]!='\n')
+ next2 = rex.right;
+ HAT =>
+ if(i == s0 && bol)
+ next1 = rex.right;
+ DOL =>
+ if(i == s1 && eol)
+ next1 = rex.right;
+ SET =>
+ if(i<len s && member(s[i], rex.set))
+ next2 = rex.right;
+ CAT or
+ PCLO =>
+ next1 = rex.left;
+ ALT or
+ CLO or
+ OPT =>
+ next1 = rex.right;
+ k = insert(rex.left, q.beg, q.gaz, todo1, k);
+ LPN =>
+ next1 = rex.right;
+ q.gaz = Gaz(rex.pno,i,-1)::q.gaz;
+ RPN =>
+ next1 = rex.right;
+ for(r:=q.gaz; ; r=tl r) {
+ (pno,beg1,end1) := hd r;
+ if(rex.pno==pno && end1==-1) {
+ q.gaz = Gaz(pno,beg1,i)::q.gaz;
+ break;
+ }
+ }
+ * =>
+ if(i<len s && rex.kind==s[i])
+ next2 = rex.right;
+ }
+ if(next1 != NONE) {
+ if(next1 != NIL)
+ k =insert(next1, q.beg, q.gaz, todo1, k);
+ else if(better(q.beg, i, beg, end))
+ (gaz, beg, end) = (q.gaz, q.beg, i);
+ }
+ if(next2 != NONE) {
+ if(next2 != NIL) {
+ if(q.beg < small2)
+ small2 = q.beg;
+ insert(next2, q.beg, q.gaz, todo2, 0);
+ } else if(better(q.beg, i+1, beg, end))
+ (gaz, beg, end) = (q.gaz, q.beg, i+1);
+ }
+
+ }
+ if(beg!=-1 && beg<small2) # nothing better possible
+ break;
+ (todo1,todo2) = (todo2, todo1);
+ todo2.ptr = 0;
+ }
+ if(beg == -1)
+ return nil;
+ result := array[re.pno+1] of { 0 => (beg,end), * => (-1,-1) };
+ for( ; gaz!=nil; gaz=tl gaz) {
+ (pno, beg1, end1) := hd gaz;
+ (rbeg, nil) := result[pno];
+ if(rbeg==-1 && (beg1|end1)!=-1)
+ result[pno] = (beg1,end1);
+ }
+ return result;
+}
+
+better(newbeg, newend, oldbeg, oldend: int): int
+{
+ return oldbeg==-1 || newbeg<oldbeg ||
+ newbeg==oldbeg && newend>oldend;
+}
+
+insert(next: refRex, tbeg: int, tgaz: list of Gaz, todo: ref Queue, k: int): int
+{
+ for(j:=0; j<todo.ptr; j++)
+ if(todo.q[j].cre == next)
+ if(todo.q[j].beg <= tbeg)
+ return k;
+ else
+ break;
+ if(j < k)
+ k--;
+ if(j < todo.ptr)
+ todo.ptr--;
+ for( ; j<todo.ptr; j++)
+ todo.q[j] = todo.q[j+1];
+ todo.q[todo.ptr++] = Trace(next, tbeg, tgaz);
+ return k;
+}
+
+ASCII : con 128;
+WORD : con 32;
+
+member(char: int, set: ref Set): int
+{
+ if(char < 128)
+ return ((set.ascii[char/WORD]>>char%WORD)&1)^set.neg;
+ for(l:=set.unicode; l!=nil; l=tl l) {
+ (beg, end) := hd l;
+ if(char>=beg && char<=end)
+ return !set.neg;
+ }
+ return set.neg;
+}
+
+newSet(s: ref ReStr): ref Set
+{
+ set := ref Set(0, array[ASCII/WORD] of {* => 0}, nil);
+ if(s.peek() == '^') {
+ set.neg = 1;
+ s.next();
+ }
+ while(s.n > 0) {
+ char1 := s.next();
+ if(char1 == ']')
+ return set;
+ char1 = esc(s, char1);
+ char2 := char1;
+ if(s.peek() == '-') {
+ s.next();
+ char2 = s.next();
+ if(char2 == ']')
+ break;
+ char2 = esc(s, char2);
+ if(char2 < char1)
+ break;
+ }
+ for( ; char1<=char2; char1++)
+ if(char1 < ASCII)
+ set.ascii[char1/WORD] |= 1<<char1%WORD;
+ else {
+ set.unicode = (char1,char2)::set.unicode;
+ break;
+ }
+ }
+ s.n = -1;
+ return nil;
+}
diff --git a/appl/lib/regexutils.b b/appl/lib/regexutils.b
new file mode 100644
index 00000000..4040f0bf
--- /dev/null
+++ b/appl/lib/regexutils.b
@@ -0,0 +1,65 @@
+implement RegexUtils;
+
+# matching and substitution functions
+# evb@lucent.com
+
+include "sys.m";
+ sys: Sys;
+
+include "regexutils.m";
+
+init()
+{
+ if (sys == nil)
+ sys = load Sys Sys->PATH;
+
+ regex = load Regex Regex->PATH;
+ if (regex == nil)
+ raise "fail: Regex not loaded";
+}
+
+match(pattern: Regex->Re, s: string): string
+{
+ pos := regex->execute(pattern, s);
+ if (pos == nil)
+ return "";
+ (beg, end) := pos[0];
+
+ return s[beg:end];
+}
+
+match_mult(pattern: Regex->Re, s: string): array of (int, int)
+{
+ return regex->execute(pattern, s);
+}
+
+sub(text, pattern, new: string): string
+{
+ return sub_re(text, regex->compile(pattern, 0).t0, new);
+}
+
+sub_re(text: string, pattern: Regex->Re, new: string): string
+{
+ pos := regex->execute(pattern, text);
+ if (pos == nil)
+ return text;
+
+ (beg, end) := pos[0];
+ newline := text[:beg] + new + text[end:];
+ return newline;
+}
+
+subg(text, pattern, new: string): string
+{
+ return subg_re(text, regex->compile(pattern, 0).t0, new);
+}
+
+subg_re(text: string, pattern: Regex->Re, new: string): string
+{
+ oldtext := text;
+ while ( (text = sub_re(text, pattern, new)) != oldtext) {
+ oldtext = text;
+ }
+
+ return text;
+}
diff --git a/appl/lib/registries.b b/appl/lib/registries.b
new file mode 100644
index 00000000..2feb83e3
--- /dev/null
+++ b/appl/lib/registries.b
@@ -0,0 +1,288 @@
+implement Registries;
+
+include "sys.m";
+ sys: Sys;
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "string.m";
+ str: String;
+include "keyring.m";
+ keyring: Keyring;
+include "security.m";
+ auth: Auth;
+include "keyset.m";
+ keyset: Keyset;
+include "registries.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ bufio = checkload(load Bufio Bufio->PATH, Bufio->PATH);
+ keyring = checkload(load Keyring Keyring->PATH, Keyring->PATH);
+ str = checkload(load String String->PATH, String->PATH);
+ keyset = checkload(load Keyset Keyset->PATH, Keyset->PATH);
+ auth = checkload(load Auth Auth->PATH, Auth->PATH);
+ e := keyset->init();
+ if(e != nil)
+ raise sys->sprint("can't init Keyset: %s", e);
+ e = auth->init();
+ if(e != nil)
+ raise sys->sprint("can't init Auth: %s", e);
+}
+
+checkload[T](x: T, s: string): T
+{
+ if(x == nil)
+ raise sys->sprint("can't load %s: %r", s);
+ return x;
+}
+
+Registry.new(dir: string): ref Registry
+{
+ if(dir == nil)
+ dir = "/mnt/registry";
+ r := ref Registry;
+ r.dir = dir;
+ r.indexfd = sys->open(dir + "/index", Sys->OREAD);
+ if(r.indexfd == nil)
+ return nil;
+ return r;
+}
+
+Registry.connect(svc: ref Service, user, keydir: string): ref Registry
+{
+ # XXX broadcast for local registries here.
+ if(svc == nil)
+ # svc = ref Service("net!$registry!registry", Attributes.new(("auth", "infpk1") :: nil));
+ svc = ref Service("net!$registry!registry", Attributes.new(("auth", "none") :: nil));
+ a := svc.attach(user, keydir);
+ if(a == nil)
+ return nil;
+ if(sys->mount(a.fd, nil, "/mnt/registry", Sys->MREPL, nil) == -1){
+ sys->werrstr(sys->sprint("mount failed: %r"));
+ return nil;
+ }
+ return Registry.new("/mnt/registry");
+}
+
+Registry.services(r: self ref Registry): (list of ref Service, string)
+{
+ sys->seek(r.indexfd, big 0, Sys->SEEKSTART);
+ iob := bufio->fopen(r.indexfd, Sys->OREAD);
+ if(iob == nil)
+ return (nil, sys->sprint("%r"));
+ return (readservices(iob), nil);
+}
+
+Registry.find(r: self ref Registry, a: list of (string, string)): (list of ref Service, string)
+{
+ fd := sys->open(r.dir + "/find", Sys->ORDWR); # could keep it open if it's a bottleneck
+ if(fd == nil)
+ return (nil, sys->sprint("%r"));
+ s := "";
+ if(a != nil){
+ for(; a != nil; a = tl a){
+ (n, v) := hd a;
+ s += sys->sprint(" %q %q", n, v);
+ }
+ s = s[1:];
+ }
+ if(sys->fprint(fd, "%s", s) == -1)
+ return (nil, sys->sprint("%r"));
+ sys->seek(fd, big 0, Sys->SEEKSTART);
+ iob := bufio->fopen(fd, Sys->OREAD);
+ return (readservices(iob), nil);
+}
+
+readservices(iob: ref Iobuf): list of ref Service
+{
+ services: list of ref Service;
+ while((s := qgets(iob, '\n')) != nil){
+ toks := str->unquoted(s);
+ if(toks == nil || len toks % 2 != 1)
+ continue;
+ svc := ref Service(hd toks, nil);
+ attrs, rattrs: list of (string, string);
+ for(toks = tl toks; toks != nil; toks = tl tl toks)
+ rattrs = (hd toks, hd tl toks) :: rattrs;
+ for(; rattrs != nil; rattrs = tl rattrs)
+ attrs = hd rattrs :: attrs;
+ svc.attrs = ref Attributes(attrs);
+ services = svc :: services;
+ }
+ return rev(services);
+}
+
+rev[T](l: list of T): list of T
+{
+ rl: list of T;
+ for(; l != nil; l = tl l)
+ rl = hd l :: rl;
+ return rl;
+}
+
+Registry.register(r: self ref Registry, addr: string, attrs: ref Attributes, persist: int): (ref Registered, string)
+{
+ fd := sys->open(r.dir + "/new", Sys->OWRITE);
+ if(fd == nil)
+ return (nil, sys->sprint("%r"));
+ s := sys->sprint("%q", addr);
+ for(a := attrs.attrs; a != nil; a = tl a)
+ s += sys->sprint(" %q %q", (hd a).t0, (hd a).t1);
+ if(persist)
+ s += " persist 1";
+ if(sys->fprint(fd, "%s", s) == -1)
+ return (nil, sys->sprint("%r"));
+ return (ref Registered(addr, r, fd), nil);
+}
+
+Registry.unregister(r: self ref Registry, addr: string): string
+{
+ if(sys->remove(r.dir + "/" + addr) == -1)
+ return sys->sprint("%r");
+ return nil;
+}
+
+Attributes.new(attrs: list of (string, string)): ref Attributes
+{
+ return ref Attributes(attrs);
+}
+
+Attributes.set(a: self ref Attributes, attr, val: string)
+{
+ for(al := a.attrs; al != nil; al = tl al)
+ if((hd al).t0 == attr)
+ break;
+ if(al == nil){
+ a.attrs = (attr, val) :: a.attrs;
+ return;
+ }
+ attrs := (attr, val) :: tl al;
+ for(al = a.attrs; al != nil; al = tl al){
+ if((hd al).t0 == attr)
+ break;
+ attrs = hd al :: attrs;
+ }
+ a.attrs = attrs;
+}
+
+Attributes.get(a: self ref Attributes, attr: string): string
+{
+ for(al := a.attrs; al != nil; al = tl al)
+ if((hd al).t0 == attr)
+ return (hd al).t1;
+ return nil;
+}
+
+qgets(iob: ref Iobuf, eoc: int): string
+{
+ inq := 0;
+ s := "";
+ while((c := iob.getc()) >= 0){
+ s[len s] = c;
+ if(inq){
+ if(c == '\''){
+ c = iob.getc();
+ if(c == '\'')
+ s[len s] = c;
+ else{
+ iob.ungetc();
+ inq = 0;
+ }
+ }
+ }else{
+ if(c == eoc)
+ return s;
+ if(c == '\'')
+ inq = 1;
+ }
+ }
+ return s;
+}
+
+Service.attach(svc: self ref Service, localuser, keydir: string): ref Attached
+{
+ # attributes used:
+ # auth type of authentication to perform (auth, none)
+ # auth.crypt type of encryption to push (as accepted by ssl(3)'s "alg" operation)
+ # auth.signer hash of service's certificate's signer's public key
+
+ (ok, c) := sys->dial(svc.addr, nil);
+ if(ok == -1){
+ sys->werrstr(sys->sprint("cannot dial: %r"));
+ return nil;
+ }
+ attached := ref Attached;
+ authkind := svc.attrs.get("auth");
+ case authkind {
+ "auth" or # old
+ "infpk1" =>
+ cryptalg := svc.attrs.get("auth.crypt");
+ if(cryptalg == nil)
+ cryptalg = "none";
+ ca := svc.attrs.get("auth.signer");
+ kf: string;
+ if(ca != nil){
+ (kfl, err) := keyset->keysforsigner(nil, ca, nil, keydir);
+ if(kfl == nil){
+ s := "no matching keys found";
+ if(err != nil)
+ s += ": "+err;
+ sys->werrstr(s);
+ return nil;
+ }
+ if(localuser == nil)
+ kf = (hd kfl).t0;
+ else{
+ for(; kfl != nil; kfl = tl kfl)
+ if((hd kfl).t1 == localuser)
+ break;
+ if(kfl == nil){
+ sys->werrstr("no matching user found");
+ return nil;
+ }
+ kf = (hd kfl).t0;
+ }
+ } else {
+ user := readname("/dev/user");
+ if(user == nil)
+ kf = "/lib/keyring/default";
+ else
+ kf = "/usr/" + user + "/keyring/default";
+ }
+ info := keyring->readauthinfo(kf);
+ if(info == nil){
+ sys->werrstr(sys->sprint("cannot read key: %r"));
+ return nil;
+ }
+ (fd, ue) := auth->client(cryptalg, info, c.dfd);
+ if(fd == nil){
+ sys->werrstr(sys->sprint("cannot authenticate: %r"));
+ return nil;
+ }
+ attached.signerpkhash = keyset->pkhash(keyring->pktostr(info.spk));
+ attached.localuser = info.mypk.owner;
+ attached.remoteuser = ue;
+ attached.fd = fd;
+ "" or
+ "none" =>
+ attached.fd = c.dfd;
+ * =>
+ sys->werrstr(sys->sprint("unknown authentication type %q", authkind));
+ return nil;
+ }
+ return attached;
+}
+
+readname(s: string): string
+{
+ fd := sys->open(s, Sys->OREAD);
+ if(fd == nil)
+ return nil;
+ buf := array[Sys->NAMEMAX] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n <= 0)
+ return nil;
+ return string buf[0:n];
+}
diff --git a/appl/lib/riff.b b/appl/lib/riff.b
new file mode 100644
index 00000000..a746486e
--- /dev/null
+++ b/appl/lib/riff.b
@@ -0,0 +1,225 @@
+implement Riff;
+
+include "sys.m";
+
+sys: Sys;
+
+include "riff.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+open(file: string): (ref RD, string)
+{
+ fd := sys->open(file, sys->OREAD);
+ if(fd == nil)
+ return (nil, "open failed");
+
+ r := ref RD;
+ r.fd = fd;
+ r.buf = array[DEFBUF] of byte;
+ r.ptr = 0;
+ r.nbyte = 0;
+
+ (hdr, l) := r.gethdr();
+ if(hdr != "RIFF")
+ return (nil, "not a RIFF file");
+
+ return (r, nil);
+}
+
+RD.gethdr(r: self ref RD): (string, int)
+{
+ b := array[8] of byte;
+
+ if(r.readn(b, 8) != 8)
+ return (nil, -1);
+
+ return (string b[0:4], ledword(b, 4));
+}
+
+RD.check4(r: self ref RD, code: string): string
+{
+ b := array[4] of byte;
+
+ if(r.readn(b, 4) != 4)
+ return "file i/o error";
+ if(string b != code)
+ return "bad four code header information";
+ return nil;
+}
+
+RD.avihdr(r: self ref RD): (ref AVIhdr, string)
+{
+ (s, l) := r.gethdr();
+ if(s == nil || s != "avih")
+ return (nil, "missing/malformed avih");
+
+ b := array[AVImainhdr] of byte;
+ if(r.readn(b, AVImainhdr) != AVImainhdr)
+ return (nil, "short read in avih");
+
+ h := ref AVIhdr;
+
+ h.usecperframe = ledword(b, 0);
+ h.bytesec = ledword(b, 4);
+ h.flag = ledword(b, 12);
+ h.frames = ledword(b, 16);
+ h.initframes = ledword(b, 20);
+ h.streams = ledword(b, 24);
+ h.bufsize = ledword(b, 28);
+ h.width = ledword(b, 32);
+ h.height = ledword(b, 36);
+
+ return (h, nil);
+}
+
+RD.streaminfo(r: self ref RD): (ref AVIstream, string)
+{
+ (h, l) := r.gethdr();
+ if(h != "LIST")
+ return (nil, "streaminfo expected LIST");
+
+ err := r.check4("strl");
+ if(err != nil)
+ return (nil, err);
+
+ (strh, sl) := r.gethdr();
+ if(strh != "strh")
+ return (nil, "streaminfo expected strh");
+
+ b := array[sl] of byte;
+ if(r.readn(b, sl) != sl)
+ return (nil, "streaminfo strl short read");
+
+ s := ref AVIstream;
+
+ s.stype = string b[0:4];
+ s.handler = string b[4:8];
+ s.flags = ledword(b, 8);
+ s.priority = ledword(b, 12);
+ s.initframes = ledword(b, 16);
+ s.scale = ledword(b, 20);
+ s.rate = ledword(b, 24);
+ s.start = ledword(b, 28);
+ s.length = ledword(b, 32);
+ s.bufsize = ledword(b, 36);
+ s.quality = ledword(b, 40);
+ s.samplesz = ledword(b, 44);
+
+ (strf, sf) := r.gethdr();
+ if(strf != "strf")
+ return (nil, "streaminfo expected strf");
+
+ s.fmt = array[sf] of byte;
+ if(r.readn(s.fmt, sf) != sf)
+ return (nil, "streaminfo strf short read");
+
+ return (s, nil);
+}
+
+RD.readn(r: self ref RD, b: array of byte, l: int): int
+{
+ if(r.nbyte < l) {
+ c := 0;
+ if(r.nbyte != 0) {
+ b[0:] = r.buf[r.ptr:];
+ l -= r.nbyte;
+ c += r.nbyte;
+ b = b[r.nbyte:];
+ }
+ bsize := len r.buf;
+ while(l != 0) {
+ r.nbyte = sys->read(r.fd, r.buf, bsize);
+ if(r.nbyte <= 0) {
+ r.nbyte = 0;
+ return -1;
+ }
+ n := l;
+ if(n > bsize)
+ n = bsize;
+
+ r.ptr = 0;
+ b[0:] = r.buf[0:n];
+ b = b[n:];
+ r.nbyte -= n;
+ r.ptr += n;
+ l -= n;
+ c += n;
+ }
+ return c;
+ }
+ b[0:] = r.buf[r.ptr:r.ptr+l];
+ r.nbyte -= l;
+ r.ptr += l;
+ return l;
+}
+
+RD.skip(r: self ref RD, size: int): int
+{
+ if(r.nbyte != 0) {
+ n := size;
+ if(n > r.nbyte)
+ n = r.nbyte;
+ r.ptr += n;
+ r.nbyte -= n;
+ size -= n;
+ if(size == 0)
+ return 0;
+ }
+ return int sys->seek(r.fd, big size, sys->SEEKRELA);
+}
+
+AVIstream.fmt2binfo(a: self ref AVIstream): string
+{
+ if(len a.fmt < Binfosize)
+ return "format is wrong size for BITMAPINFO";
+
+ b := ref Bitmapinfo;
+
+ # Pull out the bitmap info
+ b.width = ledword(a.fmt, 4);
+ b.height = ledword(a.fmt, 8);
+ b.planes = leword(a.fmt, 12);
+ b.bitcount = leword(a.fmt, 14);
+ b.compression = ledword(a.fmt, 16);
+ b.sizeimage = ledword(a.fmt, 20);
+ b.xpelpermeter = ledword(a.fmt, 24);
+ b.ypelpermeter = ledword(a.fmt, 28);
+ b.clrused = ledword(a.fmt, 32);
+ b.clrimportant = ledword(a.fmt, 36);
+
+ # Parse out the color map
+ ncolor := len a.fmt - Binfosize;
+ if(ncolor & 3)
+ return "wrong size color map";
+ ncolor /= 4;
+
+ b.cmap = array[ncolor] of RGB;
+ idx := 40;
+ for(i := 0; i < ncolor; i++) {
+ b.cmap[i].r = int a.fmt[idx+2];
+ b.cmap[i].g = int a.fmt[idx+1];
+ b.cmap[i].b = int a.fmt[idx+0];
+ idx += 4;
+ }
+
+ a.fmt = nil;
+ a.binfo = b;
+ return nil;
+}
+
+leword(b: array of byte, o: int): int
+{
+ return (int b[o+1] << 8) | int b[o];
+}
+
+ledword(b: array of byte, o: int): int
+{
+ return (int b[o+3] << 24) |
+ (int b[o+2] << 16) |
+ (int b[o+1] << 8) |
+ int b[o];
+}
diff --git a/appl/lib/scoretable.b b/appl/lib/scoretable.b
new file mode 100644
index 00000000..a466763b
--- /dev/null
+++ b/appl/lib/scoretable.b
@@ -0,0 +1,150 @@
+# Copyright © 1999 Roger Peppe. All rights reserved.
+implement Scoretable;
+include "sys.m";
+ sys: Sys;
+ stderr: ref Sys->FD;
+include "draw.m";
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "scoretable.m";
+
+# this is the cut-down version - it doesn't bother
+# with score table locking at all; there is such a version,
+# but it needs a lock server, so is often more hassle than
+# it's worth. if you want a distributed score file, contact
+# rog@vitanuova.com
+# currently this module is only used by tetris - the interface
+# will probably change in the future.
+
+scorefile: string;
+username: string;
+
+MAXSCORES: con 10;
+
+init(port: int, user, name: string, sfile: string): (int, string)
+{
+ if (sys == nil) {
+ sys = load Sys Sys->PATH;
+ stderr = sys->fildes(2);
+ bufio = load Bufio Bufio->PATH;
+ if (bufio == nil) {
+ sys = nil;
+ return (-1, sys->sprint("cannot load %s: %r", Bufio->PATH));
+ }
+ }
+ username = user;
+ lock();
+ scorefd: ref Sys->FD;
+ if ((scorefd = sys->open(sfile, Sys->ORDWR)) == nil
+ && (scorefd = sys->create(sfile, Sys->ORDWR, 8r666)) == nil) {
+ unlock();
+ return (-1, sys->sprint("cannot open %s: %r", sfile));
+ }
+ unlock();
+ scorefile = sfile;
+ return (0, nil);
+}
+
+lock()
+{
+}
+
+unlock()
+{
+}
+
+scores(): list of Score
+{
+ lock();
+ sl := readscores();
+ unlock();
+ return sl;
+}
+
+readscores(): list of Score
+{
+ sl: list of Score;
+ iob := bufio->open(scorefile, Sys->OREAD);
+ if (iob == nil)
+ return nil;
+ iob.seek(big 0, Bufio->SEEKSTART);
+ while ((s := iob.gets('\n')) != nil) {
+ (n, toks) := sys->tokenize(s, " \t\n");
+ if (toks == nil)
+ continue;
+ if (n < 2) {
+ sys->fprint(stderr, "bad line in score table: %s", s);
+ continue;
+ }
+ score: Score;
+ (score.user, toks) = (hd toks, tl toks);
+ (score.score, toks) = (int hd toks, tl toks);
+ score.other = nil;
+ while (toks != nil) {
+ score.other += hd toks;
+ if (tl toks != nil)
+ score.other += " ";
+ toks = tl toks;
+ }
+ sl = score :: sl;
+ }
+ iob.close();
+ nl: list of Score;
+ while (sl != nil) {
+ nl = hd sl :: nl;
+ sl = tl sl;
+ }
+ return nl;
+}
+
+writescores(sl: list of Score)
+{
+ scoreiob := bufio->open(scorefile, Sys->OWRITE|Sys->OTRUNC);
+ if (scoreiob == nil) {
+ sys->fprint(stderr, "scoretable: cannot write score file '%s': %r\n", scorefile);
+ return;
+ }
+ scoreiob.seek(big 0, Bufio->SEEKSTART);
+ n := 0;
+ while (sl != nil && n < MAXSCORES) {
+ s := hd sl;
+ scoreiob.puts(sys->sprint("%s %d %s\n", s.user, s.score, s.other));
+ n++;
+ sl = tl sl;
+ }
+ scoreiob.close();
+}
+
+setscore(score: int, other: string): int
+{
+ lock();
+ sl := readscores();
+ nl: list of Score;
+ done := 0;
+ n := rank := 0;
+ while (sl != nil) {
+ s := hd sl;
+ if (score > s.score && !done) {
+ nl = Score(username, score, other) :: nl;
+ rank = n;
+ done = 1;
+ }
+ nl = s :: nl;
+ sl = tl sl;
+ n++;
+ }
+ if (!done) {
+ nl = Score(username, score, other) :: nl;
+ rank = n;
+ }
+ sl = nil;
+ while (nl != nil) {
+ sl = hd nl :: sl;
+ nl = tl nl;
+ }
+ writescores(sl);
+ unlock();
+ # XXX minor race condition in returning the rank, not our idea of the rank.
+ return rank;
+}
diff --git a/appl/lib/scsiio.b b/appl/lib/scsiio.b
new file mode 100644
index 00000000..152a22dd
--- /dev/null
+++ b/appl/lib/scsiio.b
@@ -0,0 +1,317 @@
+implement ScsiIO;
+
+# adapted from /sys/src/libdisk on Plan 9: subject to Lucent Public License 1.02
+
+include "sys.m";
+ sys: Sys;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "daytime.m";
+ daytime: Daytime;
+
+include "scsiio.m";
+
+scsiverbose := 0;
+
+Codefile: con "/lib/scsicodes";
+
+Code: adt {
+ v: int; # (asc<<8) | ascq
+ s: string;
+};
+codes: array of Code;
+
+init(verbose: int)
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ daytime = load Daytime Daytime->PATH;
+
+ scsiverbose = verbose;
+ getcodes();
+}
+
+getcodes()
+{
+ fd := bufio->open(Codefile, Sys->OREAD);
+ if(fd == nil)
+ return;
+
+ codes = array[256] of Code;
+ nc := 0;
+ while((s := fd.gets('\n')) != nil){
+ if(s[0] == '#' || s[0] == '\n')
+ continue;
+ s = s[0: len s-1]; # trim '\n'
+ m: string;
+ for(i := 0; i < len s; i++)
+ if(s[i] == ' '){
+ m = s[i+1:];
+ break;
+ }
+ c := Code(tohex(s), m);
+ if(nc >= len codes){
+ ct := array[nc + 20] of Code;
+ ct[0:] = codes;
+ codes = ct;
+ }
+ codes[nc++] = c;
+ }
+ codes = codes[0:nc];
+}
+
+tohex(s: string): int
+{
+ n := 0;
+ j := 0;
+ for(i := 0; i < len s && j < 4; i++){
+ if(s[i] == '/')
+ continue;
+ d := hex(s[i]);
+ if(d < 0)
+ return -1;
+ n = (n<<4) | d;
+ j++;
+ }
+ return n;
+}
+
+hex(c: int): int
+{
+ if(c >= '0' && c <= '9')
+ return c-'0';
+ if(c >= 'A' && c <= 'F')
+ return c-'A' + 10;
+ if(c >= 'a' && c <= 'f')
+ return c-'a' + 10;
+ return -1;
+}
+
+scsierror(asc: int, ascq: int): string
+{
+ t := -1;
+ for(i := 0; i < len codes; i++){
+ if(codes[i].v == ((asc<<8) | ascq))
+ return codes[i].s;
+ if(codes[i].v == (asc<<8))
+ t = i;
+ }
+ if(t >= 0)
+ return sys->sprint("(ascq #%.2ux) %s", ascq, codes[t].s);
+ return sys->sprint("scsi #%.2ux %.2ux", asc, ascq);
+}
+
+_scsicmd(s: ref Scsi, cmd: array of byte, data: array of byte, io: int, dolock: int): int
+{
+ if(dolock)
+ qlock(s);
+ dcount := len data;
+ if(sys->write(s.rawfd, cmd, len cmd) != len cmd) {
+ sys->werrstr("cmd write: %r");
+ if(dolock)
+ qunlock(s);
+ return -1;
+ }
+
+ n: int;
+ resp := array[16] of byte;
+ case io {
+ Sread =>
+ n = sys->read(s.rawfd, data, dcount);
+ if(n < 0 && scsiverbose)
+ sys->fprint(sys->fildes(2), "dat read: %r: cmd %#2.2uX\n", int cmd[0]);
+ Swrite =>
+ n = sys->write(s.rawfd, data, dcount);
+ if(n != dcount && scsiverbose)
+ sys->fprint(sys->fildes(2), "dat write: %r: cmd %#2.2uX\n", int cmd[0]);
+ Snone or * =>
+ n = sys->write(s.rawfd, resp, 0);
+ if(n != 0 && scsiverbose)
+ sys->fprint(sys->fildes(2), "none write: %r: cmd %#2.2uX\n", int cmd[0]);
+ }
+
+ m := sys->read(s.rawfd, resp, len resp);
+ if(dolock)
+ qunlock(s);
+ if(m < 0){
+ sys->werrstr("resp read: %r\n");
+ return -1;
+ }
+ status := int string resp[0:m];
+ if(status == 0)
+ return n;
+
+ sys->werrstr(sys->sprint("cmd %2.2uX: status %uX dcount %d n %d", int cmd[0], status, dcount, n));
+ return -1;
+}
+
+Scsi.rawcmd(s: self ref Scsi, cmd: array of byte, data: array of byte, io: int): int
+{
+ return _scsicmd(s, cmd, data, io, 1);
+}
+
+_scsiready(s: ref Scsi, dolock: int): int
+{
+ if(dolock)
+ qlock(s);
+ for(i:=0; i<3; i++) {
+ cmd := array[6] of {0 => byte 16r00, * => byte 0}; # test unit ready
+ if(sys->write(s.rawfd, cmd, len cmd) != len cmd) {
+ if(scsiverbose)
+ sys->fprint(sys->fildes(2), "ur cmd write: %r\n");
+ continue;
+ }
+ resp := array[16] of byte;
+ sys->write(s.rawfd, resp, 0);
+ m := sys->read(s.rawfd, resp, len resp);
+ if(m < 0){
+ if(scsiverbose)
+ sys->fprint(sys->fildes(2), "ur resp read: %r\n");
+ continue; # retry
+ }
+ status := int string resp[0:m];
+ if(status == 0 || status == 16r02) {
+ if(dolock)
+ qunlock(s);
+ return 0;
+ }
+ if(scsiverbose)
+ sys->fprint(sys->fildes(2), "target: bad status: %x\n", status);
+ }
+ if(dolock)
+ qunlock(s);
+ return -1;
+}
+
+Scsi.ready(s: self ref Scsi): int
+{
+ return _scsiready(s, 1);
+}
+
+Scsi.cmd(s: self ref Scsi, cmd: array of byte, data: array of byte, io: int): int
+{
+ dcount := len data;
+ code := 0;
+ key := 0;
+ qlock(s);
+ sense: array of byte;
+ for(tries:=0; tries<2; tries++) {
+ n := _scsicmd(s, cmd, data, io, 0);
+ if(n >= 0) {
+ qunlock(s);
+ return n;
+ }
+
+ #
+ # request sense
+ #
+ sense = array[255] of {* => byte 16rFF}; # TO DO: usb mass storage devices might inist on less
+ req := array[6] of {0 => byte 16r03, 4 => byte len sense, * => byte 0};
+ if((n=_scsicmd(s, req, sense, Sread, 0)) < 14)
+ if(scsiverbose)
+ sys->fprint(sys->fildes(2), "reqsense scsicmd %d: %r\n", n);
+
+ if(_scsiready(s, 0) < 0)
+ if(scsiverbose)
+ sys->fprint(sys->fildes(2), "unit not ready\n");
+
+ key = int sense[2];
+ code = int sense[12];
+ if(code == 16r17 || code == 16r18) { # recovered errors
+ qunlock(s);
+ return dcount;
+ }
+ if(code == 16r28 && int cmd[0] == 16r43) { # get info and media changed
+ s.nchange++;
+ s.changetime = daytime->now();
+ continue;
+ }
+ }
+
+ # drive not ready, or medium not present
+ if(cmd[0] == byte 16r43 && key == 2 && (code == 16r3a || code == 16r04)) {
+ s.changetime = 0;
+ qunlock(s);
+ return -1;
+ }
+ qunlock(s);
+
+ if(cmd[0] == byte 16r43 && key == 5 && code == 16r24) # blank media
+ return -1;
+
+ p := scsierror(code, int sense[13]);
+
+ sys->werrstr(sys->sprint("cmd #%.2ux: %s", int cmd[0], p));
+
+ if(scsiverbose)
+ sys->fprint(sys->fildes(2), "scsi cmd #%.2ux: %.2ux %.2ux %.2ux: %s\n", int cmd[0], key, code, int sense[13], p);
+
+# if(key == 0)
+# return dcount;
+ return -1;
+}
+
+Scsi.open(dev: string): ref Scsi
+{
+ rawfd := sys->open(dev+"/raw", Sys->ORDWR);
+ if(rawfd == nil)
+ return nil;
+ ctlfd := sys->open(dev+"/ctl", Sys->ORDWR);
+ if(ctlfd == nil)
+ return nil;
+
+ buf := array[512] of byte;
+ n := readn(ctlfd, buf, len buf);
+ if(n < 8){
+ if(n >= 0)
+ sys->werrstr("error reading ctl file");
+ return nil;
+ }
+ ctlfd = nil;
+
+ for(i := 0; i < n; i++)
+ if(buf[i] == byte '\n')
+ break;
+ inq := string buf[0:i];
+ if(i >= n || inq[0:8] != "inquiry "){
+ sys->werrstr("invalid inquiry string");
+ return nil;
+ }
+ s := ref Scsi;
+ s.lock = chan[1] of int;
+ s.rawfd = rawfd;
+ s.inquire = inq[8:];
+ s.changetime = daytime->now();
+
+ if(s.ready() < 0)
+ return nil;
+
+ return s;
+}
+
+qlock(s: ref Scsi)
+{
+ s.lock <-= 1;
+}
+
+qunlock(s: ref Scsi)
+{
+ <-s.lock;
+}
+
+readn(fd: ref Sys->FD, buf: array of byte, nb: int): int
+{
+ for(nr := 0; nr < nb;){
+ n := sys->read(fd, buf[nr:], nb-nr);
+ if(n <= 0){
+ if(nr == 0)
+ return n;
+ break;
+ }
+ nr += n;
+ }
+ return nr;
+}
diff --git a/appl/lib/secstore.b b/appl/lib/secstore.b
new file mode 100644
index 00000000..c5ff24bc
--- /dev/null
+++ b/appl/lib/secstore.b
@@ -0,0 +1,474 @@
+implement Secstore;
+
+#
+# interact with the Plan 9 secstore
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "keyring.m";
+ kr: Keyring;
+ DigestState, IPint: import kr;
+ AESbsize, AESstate: import kr;
+
+include "security.m";
+ ssl: SSL;
+
+include "encoding.m";
+ base64: Encoding;
+
+include "secstore.m";
+
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ kr = load Keyring Keyring->PATH;
+ ssl = load SSL SSL->PATH;
+ base64 = load Encoding Encoding->BASE64PATH;
+ initPAKparams();
+}
+
+privacy(): int
+{
+ fd := sys->open("#p/"+string sys->pctl(0, nil)+"/ctl", Sys->OWRITE);
+ if(fd == nil || sys->fprint(fd, "private") < 0)
+ return 0;
+ return 1;
+}
+
+connect(addr: string, user: string, pwhash: array of byte): (ref Sys->Connection, string, string)
+{
+ conn := dial(addr);
+ if(conn == nil){
+ sys->werrstr(sys->sprint("can't dial %s: %r", addr));
+ return (nil, nil, sys->sprint("%r"));
+ }
+ (sname, diag) := auth(conn, user, pwhash);
+ if(sname == nil){
+ sys->werrstr(sys->sprint("can't authenticate: %s", diag));
+ return (nil, nil, sys->sprint("%r"));
+ }
+ return (conn, sname, diag);
+}
+
+dial(netaddr: string): ref Sys->Connection
+{
+ if(netaddr == nil)
+ netaddr = "net!$auth!secstore";
+ (ok, conn) := sys->dial(netaddr, nil);
+ if(ok < 0)
+ return nil;
+ (err, sslconn) := ssl->connect(conn.dfd);
+ if(err != nil)
+ sys->werrstr(err);
+ return sslconn;
+}
+
+auth(conn: ref Sys->Connection, user: string, pwhash: array of byte): (string, string)
+{
+ sname := PAKclient(conn, user, pwhash);
+ if(sname == nil)
+ return (nil, sys->sprint("%r"));
+ s := readstr(conn.dfd);
+ if(s == "STA")
+ return (sname, "need pin");
+ if(s != "OK"){
+ if(s != nil)
+ sys->werrstr(s);
+ return (nil, sys->sprint("%r"));
+ }
+ return (sname, nil);
+}
+
+cansecstore(netaddr: string, user: string): int
+{
+ conn := dial(netaddr);
+ if(conn == nil)
+ return 0;
+ if(sys->fprint(conn.dfd, "secstore\tPAK\nC=%s\nm=0\n", user) < 0)
+ return 0;
+ buf := array[128] of byte;
+ n := sys->read(conn.dfd, buf, len buf);
+ if(n <= 0)
+ return 0;
+ return string buf[0:n] == "!account exists";
+}
+
+sendpin(conn: ref Sys->Connection, pin: string): int
+{
+ if(sys->fprint(conn.dfd, "STA%s", pin) < 0)
+ return -1;
+ s := readstr(conn.dfd);
+ if(s != "OK"){
+ if(s != nil)
+ sys->werrstr(s);
+ return -1;
+ }
+ return 0;
+}
+
+files(conn: ref Sys->Connection): list of (string, int, string, string, array of byte)
+{
+ file := getfile(conn, ".", 0);
+ if(file == nil)
+ return nil;
+ rl: list of (string, int, string, string, array of byte);
+ for(linelist := lines(file); linelist != nil; linelist = tl linelist){
+ s := string hd linelist;
+ # factotum\t2552 Dec 9 13:04:49 GMT 2005 n9wSk45SPDxgljOIflGQoXjOkjs=
+ for(i := 0; i < len s && s[i] != '\t' && s[i] != ' '; i++){} # can be trailing spaces
+ name := s[0:i];
+ for(; i < len s && (s[i] == ' ' || s[i] == '\t'); i++){}
+ for(j := i; j < len s && s[j] != ' '; j++){}
+ size := int s[i+1:j];
+ for(i = j; i < len s && s[i] == ' '; i++){}
+ date := s[i:i+24];
+ i += 24+1;
+ for(j = i; j < len s && s[j] != '\n'; j++){}
+ sha1 := s[i:j];
+ rl = (name, int size, date, sha1, base64->dec(sha1)) :: rl;
+ }
+ l: list of (string, int, string, string, array of byte);
+ for(; rl != nil; rl = tl rl)
+ l = hd rl :: l;
+ return l;
+}
+
+getfile(conn: ref Sys->Connection, name: string, maxsize: int): array of byte
+{
+ fd := conn.dfd;
+ if(maxsize <= 0)
+ maxsize = Maxfilesize;
+ if(sys->fprint(fd, "GET %s\n", name) < 0 ||
+ (s := readstr(fd)) == nil){
+ sys->werrstr(sys->sprint("can't get %q: %r", name));
+ return nil;
+ }
+ nb := int s;
+ if(nb == -1){
+ sys->werrstr(sys->sprint("remote file %q does not exist", name));
+ return nil;
+ }
+ if(nb < 0 || nb > maxsize){
+ sys->werrstr(sys->sprint("implausible file size %d for %q", nb, name));
+ return nil;
+ }
+ file := array[nb] of byte;
+ for(nr := 0; nr < nb;){
+ n := sys->read(fd, file[nr:], nb-nr);
+ if(n < 0){
+ sys->werrstr(sys->sprint("error reading %q: %r", name));
+ return nil;
+ }
+ if(n == 0){
+ sys->werrstr(sys->sprint("empty file chunk reading %q at offset %d", name, nr));
+ return nil;
+ }
+ nr += n;
+ }
+ return file;
+}
+
+remove(conn: ref Sys->Connection, name: string): int
+{
+ if(sys->fprint(conn.dfd, "RM %s\n", name) < 0)
+ return -1;
+
+ return 0;
+}
+
+bye(conn: ref Sys->Connection)
+{
+ if(conn != nil){
+ if(conn.dfd != nil)
+ sys->fprint(conn.dfd, "BYE");
+ conn.dfd = nil;
+ conn.cfd = nil;
+ }
+}
+
+mkseckey(s: string): array of byte
+{
+ key := array of byte s;
+ skey := array[Keyring->SHA1dlen] of byte;
+ kr->sha1(key, len key, skey, nil);
+ erasekey(key);
+ return skey;
+}
+
+Checkpat: con "XXXXXXXXXXXXXXXX"; # it's what Plan 9's aescbc uses
+Checklen: con len Checkpat;
+
+mkfilekey(s: string): array of byte
+{
+ key := array of byte s;
+ skey := array[Keyring->SHA1dlen] of byte;
+ sha := kr->sha1(array of byte "aescbc file", 11, nil, nil);
+ kr->sha1(key, len key, skey, sha);
+ erasekey(key);
+ erasekey(skey[AESbsize:]);
+ return skey[0:AESbsize];
+}
+
+decrypt(file: array of byte, key: array of byte): array of byte
+{
+ length := len file;
+ if(length == 0)
+ return file;
+ if(length < AESbsize+Checklen)
+ return nil;
+ state := kr->aessetup(key, file[0:AESbsize]);
+ if(state == nil){
+ sys->werrstr("can't set AES state");
+ return nil;
+ }
+ kr->aescbc(state, file[AESbsize:], length-AESbsize, Keyring->Decrypt);
+ if(string file[length-Checklen:] != Checkpat){
+ sys->werrstr("file did not decrypt correctly");
+ return nil;
+ }
+ return file[AESbsize: length-Checklen];
+}
+
+lines(file: array of byte): list of array of byte
+{
+ rl: list of array of byte;
+ for(i := 0; i < len file;){
+ for(j := i; j < len file; j++)
+ if(file[j] == byte '\n'){
+ j++;
+ break;
+ }
+ rl = file[i:j] :: rl;
+ i = j;
+ }
+ l: list of array of byte;
+ for(; rl != nil; rl = tl rl)
+ l = (hd rl) :: l;
+ return l;
+}
+
+readstr(fd: ref Sys->FD): string
+{
+ buf := array[500] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return nil;
+ s := string buf[0:n];
+ if(s[0] == '!'){
+ sys->werrstr(s[1:]);
+ return nil;
+ }
+ return s;
+}
+
+writerr(fd: ref Sys->FD, s: string)
+{
+ sys->fprint(fd, "!%s", s);
+ sys->werrstr(s);
+}
+
+setsecret(conn: ref Sys->Connection, sigma: array of byte, direction: int): string
+{
+ secretin := array[Keyring->SHA1dlen] of byte;
+ secretout := array[Keyring->SHA1dlen] of byte;
+ if(direction != 0){
+ kr->hmac_sha1(sigma, len sigma, array of byte "one", secretout, nil);
+ kr->hmac_sha1(sigma, len sigma, array of byte "two", secretin, nil);
+ }else{
+ kr->hmac_sha1(sigma, len sigma, array of byte "two", secretout, nil);
+ kr->hmac_sha1(sigma, len sigma, array of byte "one", secretin, nil);
+ }
+ return ssl->secret(conn, secretin, secretout);
+}
+
+erasekey(a: array of byte)
+{
+ for(i := 0; i < len a; i++)
+ a[i] = byte 0;
+}
+
+#
+# the following must only be used to talk to a Plan 9 secstore
+#
+
+VERSION: con "secstore";
+
+PAKparams: adt {
+ q: ref IPint;
+ p: ref IPint;
+ r: ref IPint;
+ g: ref IPint;
+};
+
+pak: ref PAKparams;
+
+# from seed EB7B6E35F7CD37B511D96C67D6688CC4DD440E1E
+
+initPAKparams()
+{
+ if(pak != nil)
+ return;
+ lpak := ref PAKparams;
+ lpak.q = IPint.strtoip("E0F0EF284E10796C5A2A511E94748BA03C795C13", 16);
+ lpak.p = IPint.strtoip("C41CFBE4D4846F67A3DF7DE9921A49D3B42DC33728427AB159CEC8CBB"+
+ "DB12B5F0C244F1A734AEB9840804EA3C25036AD1B61AFF3ABBC247CD4B384224567A86"+
+ "3A6F020E7EE9795554BCD08ABAD7321AF27E1E92E3DB1C6E7E94FAAE590AE9C48F96D9"+
+ "3D178E809401ABE8A534A1EC44359733475A36A70C7B425125062B1142D", 16);
+ lpak.r = IPint.strtoip("DF310F4E54A5FEC5D86D3E14863921E834113E060F90052AD332B3241"+
+ "CEF2497EFA0303D6344F7C819691A0F9C4A773815AF8EAECFB7EC1D98F039F17A32A7E"+
+ "887D97251A927D093F44A55577F4D70444AEBD06B9B45695EC23962B175F266895C67D"+
+ "21C4656848614D888A4", 16);
+ lpak.g = IPint.strtoip("2F1C308DC46B9A44B52DF7DACCE1208CCEF72F69C743ADD4D23271734"+
+ "44ED6E65E074694246E07F9FD4AE26E0FDDD9F54F813C40CB9BCD4338EA6F242AB94CD"+
+ "410E676C290368A16B1A3594877437E516C53A6EEE5493A038A017E955E218E7819734"+
+ "E3E2A6E0BAE08B14258F8C03CC1B30E0DDADFCF7CEDF0727684D3D255F1", 16);
+ pak = lpak; # atomic store
+}
+
+# H = (sha(ver,C,sha(passphrase)))^r mod p,
+# a hash function expensive to attack by brute force.
+
+longhash(ver: string, C: string, passwd: array of byte): ref IPint
+{
+ aver := array of byte ver;
+ aC := array of byte C;
+ Cp := array[len aver + len aC + len passwd] of byte;
+ Cp[0:] = aver;
+ Cp[len aver:] = aC;
+ Cp[len aver+len aC:] = passwd;
+ buf := array[7*Keyring->SHA1dlen] of byte;
+ for(i := 0; i < 7; i++){
+ key := array[] of { byte('A'+i) };
+ kr->hmac_sha1(Cp, len Cp, key, buf[i*Keyring->SHA1dlen:], nil);
+ }
+ erasekey(Cp);
+ return mod(IPint.bebytestoip(buf), pak.p).expmod(pak.r, pak.p); # H
+}
+
+mod(a, b: ref IPint): ref IPint
+{
+ return a.div(b).t1;
+}
+
+shaz(s: string, digest: array of byte, state: ref DigestState): ref DigestState
+{
+ a := array of byte s;
+ state = kr->sha1(a, len a, digest, state);
+ erasekey(a);
+ return state;
+}
+
+# Hi = H^-1 mod p
+PAK_Hi(C: string, passhash: array of byte): (string, ref IPint, ref IPint)
+{
+ H := longhash(VERSION, C, passhash);
+ Hi := H.invert(pak.p);
+ return (Hi.iptostr(64), H, Hi);
+}
+
+# another, faster, hash function for each party to
+# confirm that the other has the right secrets.
+
+shorthash(mess: string, C: string, S: string, m: string, mu: string, sigma: string, Hi: string): array of byte
+{
+ state := shaz(mess, nil, nil);
+ state = shaz(C, nil, state);
+ state = shaz(S, nil, state);
+ state = shaz(m, nil, state);
+ state = shaz(mu, nil, state);
+ state = shaz(sigma, nil, state);
+ state = shaz(Hi, nil, state);
+ state = shaz(mess, nil, state);
+ state = shaz(C, nil, state);
+ state = shaz(S, nil, state);
+ state = shaz(m, nil, state);
+ state = shaz(mu, nil, state);
+ state = shaz(sigma, nil, state);
+ digest := array[Keyring->SHA1dlen] of byte;
+ shaz(Hi, digest, state);
+ return digest;
+}
+
+#
+# On input, conn provides an open channel to the server;
+# C is the name this client calls itself;
+# pass is the user's passphrase
+# On output, session secret has been set in conn
+# (unless return code is negative, which means failure).
+#
+PAKclient(conn: ref Sys->Connection, C: string, pwhash: array of byte): string
+{
+ dfd := conn.dfd;
+
+ (hexHi, H, nil) := PAK_Hi(C, pwhash);
+
+ # random 1<=x<=q-1; send C, m=g**x H
+ x := mod(IPint.random(240, 240), pak.q);
+ if(x.eq(IPint.inttoip(0)))
+ x = IPint.inttoip(1);
+ m := mod(pak.g.expmod(x, pak.p).mul(H), pak.p);
+ hexm := m.iptostr(64);
+
+ if(sys->fprint(dfd, "%s\tPAK\nC=%s\nm=%s\n", VERSION, C, hexm) < 0)
+ return nil;
+
+ # recv g**y, S, check hash1(g**xy)
+ s := readstr(dfd);
+ if(s == nil){
+ e := sys->sprint("%r");
+ writerr(dfd, "couldn't read g**y");
+ sys->werrstr(e);
+ return nil;
+ }
+ # should be: "mu=%s\nk=%s\nS=%s\n"
+ (nf, flds) := sys->tokenize(s, "\n");
+ if(nf != 3){
+ writerr(dfd, "verifier syntax error");
+ return nil;
+ }
+ hexmu := ex("mu=", hd flds); flds = tl flds;
+ ks := ex("k=", hd flds); flds = tl flds;
+ S := ex("S=", hd flds);
+ if(hexmu == nil || ks == nil || S == nil){
+ writerr(dfd, "verifier syntax error");
+ return nil;
+ }
+ mu := IPint.strtoip(hexmu, 64);
+ sigma := mu.expmod(x, pak.p);
+ hexsigma := sigma.iptostr(64);
+ digest := shorthash("server", C, S, hexm, hexmu, hexsigma, hexHi);
+ kc := base64->enc(digest);
+ if(ks != kc){
+ writerr(dfd, "verifier didn't match");
+ return nil;
+ }
+
+ # send hash2(g**xy)
+ digest = shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi);
+ kc = base64->enc(digest);
+ if(sys->fprint(dfd, "k'=%s\n", kc) < 0)
+ return nil;
+
+ # set session key
+ digest = shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi);
+ for(i := 0; i < len hexsigma; i++)
+ hexsigma[i] = 0;
+
+ err := setsecret(conn, digest, 0);
+ if(err != nil)
+ return nil;
+ erasekey(digest);
+ if(sys->fprint(conn.cfd, "alg sha1 rc4_128") < 0)
+ return nil;
+ return S;
+}
+
+ex(tag: string, s: string): string
+{
+ if(len s < len tag || s[0:len tag] != tag)
+ return nil;
+ return s[len tag:];
+}
diff --git a/appl/lib/selectfile.b b/appl/lib/selectfile.b
new file mode 100644
index 00000000..15c55766
--- /dev/null
+++ b/appl/lib/selectfile.b
@@ -0,0 +1,624 @@
+implement Selectfile;
+
+include "sys.m";
+ sys: Sys;
+ Dir: import sys;
+
+include "draw.m";
+ draw: Draw;
+ Screen, Rect, Point: import draw;
+
+include "tk.m";
+ tk: Tk;
+
+include "string.m";
+ str: String;
+
+include "tkclient.m";
+ tkclient: Tkclient;
+
+include "workdir.m";
+
+include "readdir.m";
+ readdir: Readdir;
+
+include "filepat.m";
+ filepat: Filepat;
+
+include "selectfile.m";
+
+Browser: adt {
+ top: ref Tk->Toplevel;
+ ncols: int;
+ colwidth: int;
+ w: string;
+ init: fn(top: ref Tk->Toplevel, w: string, colwidth: string): (ref Browser, chan of string);
+
+ addcol: fn(c: self ref Browser, t: string, d: array of string);
+ delete: fn(c: self ref Browser, colno: int);
+ selection: fn(c: self ref Browser, cno: int): string;
+ select: fn(b: self ref Browser, cno: int, e: string);
+ entries: fn(b: self ref Browser, cno: int): array of string;
+ resize: fn(c: self ref Browser);
+};
+
+BState: adt {
+ b: ref Browser;
+ bpath: string; # path currently displayed in browser
+ epath: string; # path entered by user
+ dirfetchpid: int;
+ dirfetchpath: string;
+};
+
+filename_config := array[] of {
+ "entry .e -bg white",
+ "frame .pf",
+ "entry .pf.e",
+ "label .pf.t -text {Filter:}",
+ "entry .pats",
+ "bind .e <Key> +{send ech key}",
+ "bind .e <Key-\n> {send ech enter}",
+ "bind .e {<Key-\t>} {send ech expand}",
+ "bind .pf.e <Key-\n> {send ech setpat}",
+ "bind . <Configure> {send ech config}",
+ "pack .b -side top -fill both -expand 1",
+ "pack .pf.t -side left",
+ "pack .pf.e -side top -fill x",
+ "pack .pf -side top -fill x",
+ "pack .e -side top -fill x",
+ "pack propagate . 0",
+};
+
+debugging := 0;
+STEP: con 20;
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+ tk = load Tk Tk->PATH;
+ tkclient = load Tkclient Tkclient->PATH;
+ tkclient->init();
+ str = load String String->PATH;
+ readdir = load Readdir Readdir->PATH;
+ filepat = load Filepat Filepat->PATH;
+ return nil;
+}
+
+filename(ctxt: ref Draw->Context, parent: ref Draw->Image,
+ title: string,
+ pats: list of string,
+ dir: string): string
+{
+ patstr: string;
+
+ if (dir == nil || dir == ".") {
+ wd := load Workdir Workdir->PATH;
+ if ((dir = wd->init()) != nil) {
+ (ok, nil) := sys->stat(dir);
+ if (ok == -1)
+ dir = nil;
+ }
+ wd = nil;
+ }
+ if (dir == nil)
+ dir = "/";
+ (pats, patstr) = makepats(pats);
+ where := localgeom(parent);
+ if (title == nil)
+ title = "Open";
+ (top, wch) := tkclient->toplevel(ctxt, where+" -bd 1", # -font /fonts/misc/latin1.6x13.font",
+ title, Tkclient->Popup|Tkclient->Resize|Tkclient->OK);
+ (b, colch) := Browser.init(top, ".b", "16w");
+ entrych := chan of string;
+ tk->namechan(top, entrych, "ech");
+ tkcmds(top, filename_config);
+ cmd(top, ". configure -width " + string (b.colwidth * 3) + " -height 20h");
+ cmd(top, ".e insert 0 '" + dir);
+ cmd(top, ".pf.e insert 0 '" + patstr);
+ s := ref BState(b, nil, dir, -1, nil);
+ s.b.resize();
+ dfch := chan of (string, array of ref Sys->Dir);
+ if (parent == nil)
+ centre(top);
+ tkclient->onscreen(top, nil);
+ tkclient->startinput(top, "kbd" :: "ptr" :: nil);
+loop: for (;;) {
+ if (debugging) {
+ sys->print("filename: before sync, bpath: '%s'; epath: '%s'\n",
+ s.bpath, s.epath);
+ }
+ bsync(s, dfch, pats);
+ if (debugging) {
+ sys->print("filename: after sync, bpath: '%s'; epath: '%s'", s.bpath, s.epath);
+ if (s.dirfetchpid == -1)
+ sys->print("\n");
+ else
+ sys->print("; fetching '%s' (pid %d)\n", s.dirfetchpath, s.dirfetchpid);
+ }
+ cmd(top, "focus .e");
+ cmd(top, "update");
+ alt {
+ c := <-top.ctxt.kbd =>
+ tk->keyboard(top, c);
+ p := <-top.ctxt.ptr =>
+ tk->pointer(top, *p);
+ c := <-top.ctxt.ctl or
+ c = <-top.wreq =>
+ tkclient->wmctl(top, c);
+ c := <-colch =>
+ double := c[0] == 'd';
+ c = c[1:];
+ (bpath, nbpath, elem) := (s.bpath, "", "");
+ for (cno := 0; cno <= int c; cno++) {
+ (elem, bpath) = nextelem(bpath);
+ nbpath = pathcat(nbpath, elem);
+ }
+ nsel := s.b.selection(int c);
+ if (nsel != nil)
+ nbpath = pathcat(nbpath, nsel);
+ s.epath = nbpath;
+ cmd(top, ".e delete 0 end");
+ cmd(top, ".e insert 0 '" + s.epath);
+ if (double)
+ break loop;
+ c := <-entrych =>
+ case c {
+ "enter" =>
+ break loop;
+ "config" =>
+ s.b.resize();
+ "key" =>
+ s.epath = cmdget(top, ".e get");
+ "expand" =>
+ cmd(top, ".e delete 0 end");
+ cmd(top, ".e insert 0 '" + s.bpath);
+ s.epath = s.bpath;
+ "setpat" =>
+ patstr = cmdget(top, ".pf.e get");
+ if (patstr == " debug ")
+ debugging = !debugging;
+ else {
+ (nil, pats) = sys->tokenize(patstr, " ");
+ s.b.delete(0);
+ s.bpath = nil;
+ }
+ }
+ c := <-wch =>
+ if (c == "ok")
+ break loop;
+ if (c == "exit") {
+ s.epath = nil;
+ break loop;
+ }
+ tkclient->wmctl(top, c);
+ (t, d) := <-dfch =>
+ ds := array[len d] of string;
+ for (i := 0; i < len d; i++) {
+ n := d[i].name;
+ if ((d[i].mode & Sys->DMDIR) != 0)
+ n[len n] = '/';
+ ds[i] = n;
+ }
+ s.b.addcol(t, ds);
+ ds = nil;
+ d = nil;
+ s.bpath = s.dirfetchpath;
+ s.dirfetchpid = -1;
+ }
+ }
+ if (s.dirfetchpid != -1)
+ kill(s.dirfetchpid);
+ return s.epath;
+}
+
+bsync(s: ref BState, dfch: chan of (string, array of ref Sys->Dir), pats: list of string)
+{
+ (epath, bpath) := (s.epath, s.bpath);
+ cno := 0;
+ prefix, e1, e2: string = "";
+
+ # find maximal prefix of epath and bpath.
+ for (;;) {
+ p1, p2: string;
+ (e1, p1) = nextelem(epath);
+ (e2, p2) = nextelem(bpath);
+ if (e1 == nil || e1 != e2)
+ break;
+ prefix = pathcat(prefix, e1);
+ (epath, bpath) = (p1, p2);
+ cno++;
+ }
+
+ if (epath == nil) {
+ if (bpath != nil) {
+ s.b.delete(cno);
+ s.b.select(cno - 1, nil);
+ s.bpath = prefix;
+ }
+ return;
+ }
+
+ # if the paths have no prefix in common then we're starting
+ # at a different root - don't do anything until
+ # we know we have at least one full element.
+ # even then, if it's not a directory, we have to ignore it.
+ if (cno == 0 && islastelem(epath))
+ return;
+
+ if (e1 != nil && islastelem(epath)) {
+ # find first prefix-matching entry.
+ match := "";
+ for ((i, ents) := (0, s.b.entries(cno - 1)); i < len ents; i++) {
+ m := ents[i];
+ if (len m >= len e1 && m[0:len e1] == e1) {
+ match = deslash(m);
+ break;
+ }
+ }
+ if (match != nil) {
+ if (match == e2 && islastelem(bpath))
+ return;
+
+ epath = pathcat(match, epath[len e1:]);
+ e1 = match;
+ if (e1 == e2)
+ cno++;
+ } else {
+ s.b.delete(cno);
+ s.bpath = prefix;
+ return;
+ }
+ }
+
+ s.b.delete(cno);
+ s.b.select(cno - 1, e1);
+ np := pathcat(prefix, e1);
+ if (s.dirfetchpid != -1) {
+ if (np == s.dirfetchpath)
+ return;
+ kill(s.dirfetchpid);
+ s.dirfetchpid = -1;
+ }
+ (ok, dir) := sys->stat(np);
+ if (ok != -1 && (dir.mode & Sys->DMDIR) != 0) {
+ sync := chan of int;
+ spawn dirfetch(np, e1, sync, dfch, pats);
+ s.dirfetchpid = <-sync;
+ s.dirfetchpath = np;
+ } else if (ok != -1)
+ s.bpath = np;
+ else
+ s.bpath = prefix;
+}
+
+dirfetch(p: string, t: string, sync: chan of int,
+ dfch: chan of (string, array of ref Sys->Dir),
+ pats: list of string)
+{
+ sync <-= sys->pctl(0, nil);
+ (a, e) := readdir->init(p, Readdir->NAME|Readdir->COMPACT);
+ if (e != -1) {
+ j := 0;
+ for (i := 0; i < len a; i++) {
+ pl := pats;
+ if ((a[i].mode & Sys->DMDIR) == 0) {
+ for (; pl != nil; pl = tl pl)
+ if (filepat->match(hd pl, a[i].name))
+ break;
+ }
+ if (pl != nil || pats == nil)
+ a[j++] = a[i];
+ }
+ a = a[0:j];
+ }
+ dfch <-= (t, a);
+}
+
+dist(top: ref Tk->Toplevel, s: string): int
+{
+ cmd(top, "frame .xxxx -width " + s);
+ d := int cmd(top, ".xxxx cget -width");
+ cmd(top, "destroy .xxxx");
+ return d;
+}
+
+Browser.init(top: ref Tk->Toplevel, w: string, colwidth: string): (ref Browser, chan of string)
+{
+ b := ref Browser;
+ b.top = top;
+ b.ncols = 0;
+ b.colwidth = dist(top, colwidth);
+ b.w = w;
+ cmd(b.top, "frame " + b.w);
+ cmd(b.top, "canvas " + b.w + ".c -width 0 -height 0 -xscrollcommand {" + b.w + ".s set}");
+ cmd(b.top, "frame " + b.w + ".c.f -bd 0");
+ cmd(b.top, "pack propagate " + b.w + ".c.f 0");
+ cmd(b.top, b.w + ".c create window 0 0 -tags win -window " + b.w + ".c.f -anchor nw");
+ cmd(b.top, "scrollbar "+b.w+".s -command {"+b.w+".c xview} -orient horizontal");
+ cmd(b.top, "bind "+b.w+".c <Configure> {"+b.w+".c itemconfigure win -height ["+b.w+".c cget -actheight]}");
+ cmd(b.top, "pack "+b.w+".c -side top -fill both -expand 1");
+ cmd(b.top, "pack "+b.w+".s -side top -fill x");
+ ch := chan of string;
+ tk->namechan(b.top, ch, "colch");
+ return (b, ch);
+}
+
+xview(top: ref Tk->Toplevel, w: string): (real, real)
+{
+ s := tk->cmd(top, w + " xview");
+ if (s != nil && s[0] != '!') {
+ (n, v) := sys->tokenize(s, " ");
+ if (n == 2)
+ return (real hd v, real hd tl v);
+ }
+ return (0.0, 0.0);
+}
+
+setscrollregion(b: ref Browser)
+{
+ (w, h) := (b.colwidth * (b.ncols + 1), int cmd(b.top, b.w + ".c cget -actheight"));
+ cmd(b.top, b.w+".c.f configure -width " + string w + " -height " + string h);
+# w := int cmd(b.top, b.w+".c.f cget -actwidth");
+# w += int cmd(b.top, b.w+".c cget -actwidth") - b.colwidth;
+# h := int cmd(b.top, b.w+".c.f cget -actheight");
+ if (w > 0 && h > 0)
+ cmd(b.top, b.w + ".c configure -scrollregion {0 0 " + string w + " " + string h + "}");
+ (start, end) := xview(b.top, b.w+".c");
+ if (end > 1.0)
+ cmd(b.top, b.w+".c xview scroll left 0 units");
+}
+
+Browser.addcol(b: self ref Browser, title: string, d: array of string)
+{
+ ncol := string b.ncols++;
+
+ f := b.w + ".c.f.d" + ncol;
+ cmd(b.top, "frame " + f + " -bg green -width " + string b.colwidth);
+
+ t := f + ".t";
+ cmd(b.top, "label " + t + " -text " + tk->quote(title) + " -bg black -fg white");
+
+ sb := f + ".s";
+ lb := f + ".l";
+ cmd(b.top, "scrollbar " + sb +
+ " -command {" + lb + " yview}");
+
+ cmd(b.top, "listbox " + lb +
+ " -selectmode browse" +
+ " -yscrollcommand {" + sb + " set}" +
+ " -bd 2");
+
+ cmd(b.top, "bind " + lb + " <ButtonRelease-1> +{send colch s " + ncol + "}");
+ cmd(b.top, "bind " + lb + " <Double-Button-1> +{send colch d " + ncol + "}");
+ cmd(b.top, "pack propagate " + f + " 0");
+ cmd(b.top, "pack " + t + " -side top -fill x");
+ cmd(b.top, "pack " + sb + " -side left -fill y");
+ cmd(b.top, "pack " + lb + " -side left -fill both -expand 1");
+ cmd(b.top, "pack " + f + " -side left -fill y");
+ for (i := 0; i < len d; i++)
+ cmd(b.top, lb + " insert end '" + d[i]);
+ setscrollregion(b);
+ seecol(b, b.ncols - 1);
+}
+
+Browser.resize(b: self ref Browser)
+{
+ if (b.ncols == 0)
+ return;
+ setscrollregion(b);
+}
+
+seecol(b: ref Browser, cno: int)
+{
+ w := b.w + ".c.f.d" + string cno;
+ min := int cmd(b.top, w + " cget -actx");
+ max := min + int cmd(b.top, w + " cget -actwidth") +
+ 2 * int cmd(b.top, w + " cget -bd");
+ min = int cmd(b.top, b.w+".c canvasx " + string min);
+ max = int cmd(b.top, b.w +".c canvasx " + string max);
+
+ # see first the right edge; then the left edge, to ensure
+ # that the start of a column is visible, even if the window
+ # is narrower than one column.
+ cmd(b.top, b.w + ".c see " + string max + " 0");
+ cmd(b.top, b.w + ".c see " + string min + " 0");
+}
+
+Browser.delete(b: self ref Browser, colno: int)
+{
+ while (b.ncols > colno)
+ cmd(b.top, "destroy " + b.w+".c.f.d" + string --b.ncols);
+ setscrollregion(b);
+}
+
+Browser.selection(b: self ref Browser, cno: int): string
+{
+ if (cno >= b.ncols || cno < 0)
+ return nil;
+ l := b.w+".c.f.d" + string cno + ".l";
+ sel := cmd(b.top, l + " curselection");
+ if (sel == nil)
+ return nil;
+ return cmdget(b.top, l + " get " + sel);
+}
+
+Browser.select(b: self ref Browser, cno: int, e: string)
+{
+ if (cno < 0 || cno >= b.ncols)
+ return;
+ l := b.w+".c.f.d" + string cno + ".l";
+ cmd(b.top, l + " selection clear 0 end");
+ if (e == nil)
+ return;
+ ents := b.entries(cno);
+ for (i := 0; i < len ents; i++) {
+ if (deslash(ents[i]) == e) {
+ cmd(b.top, l + " selection set " + string i);
+ cmd(b.top, l + " see " + string i);
+ return;
+ }
+ }
+}
+
+Browser.entries(b: self ref Browser, cno: int): array of string
+{
+ if (cno < 0 || cno >= b.ncols)
+ return nil;
+ l := b.w+".c.f.d" + string cno + ".l";
+ nent := int cmd(b.top, l + " index end") + 1;
+ ents := array[nent] of string;
+ for (i := 0; i < len ents; i++)
+ ents[i] = cmdget(b.top, l + " get " + string i);
+ return ents;
+}
+
+# turn each pattern of the form "*.b (Limbo files)" into "*.b".
+# ignore '*' as it's a hangover from a past age.
+makepats(pats: list of string): (list of string, string)
+{
+ np: list of string;
+ s := "";
+ for (; pats != nil; pats = tl pats) {
+ p := hd pats;
+ for (i := 0; i < len p; i++)
+ if (p[i] == ' ')
+ break;
+ pat := p[0:i];
+ if (p != "*") {
+ np = p[0:i] :: np;
+ s += hd np;
+ if (tl pats != nil)
+ s[len s] = ' ';
+ }
+ }
+ return (np, s);
+}
+
+widgetwidth(top: ref Tk->Toplevel, w: string): int
+{
+ return int cmd(top, w + " cget -width") + 2 * int cmd(top, w + " cget -bd");
+}
+
+skipslash(path: string): string
+{
+ for (i := 0; i < len path; i++)
+ if (path[i] != '/')
+ return path[i:];
+ return nil;
+}
+
+nextelem(path: string): (string, string)
+{
+ if (path == nil)
+ return (nil, nil);
+ if (path[0] == '/')
+ return ("/", skipslash(path));
+ for (i := 0; i < len path; i++)
+ if (path[i] == '/')
+ break;
+ return (path[0:i], skipslash(path[i:]));
+}
+
+islastelem(path: string): int
+{
+ for (i := 0; i < len path; i++)
+ if (path[i] == '/')
+ return 0;
+ return 1;
+}
+
+pathcat(path, elem: string): string
+{
+ if (path != nil && path[len path - 1] != '/')
+ path[len path] = '/';
+ return path + elem;
+}
+
+# remove a possible trailing slash
+deslash(s: string): string
+{
+ if (len s > 0 && s[len s - 1] == '/')
+ s = s[0:len s - 1];
+ return s;
+}
+
+#
+# find upper left corner for subsidiary child window (always at constant
+# position relative to parent)
+#
+localgeom(im: ref Draw->Image): string
+{
+ if (im == nil)
+ return nil;
+
+ return sys->sprint("-x %d -y %d", im.r.min.x+STEP, im.r.min.y+STEP);
+}
+
+centre(t: ref Tk->Toplevel)
+{
+ org: Point;
+ org.x = t.screenr.dx() / 2 - int cmd(t, ". cget -width") / 2;
+ org.y = t.screenr.dy() / 3 - int cmd(t, ". cget -height") / 2;
+ if (org.y < 0)
+ org.y = 0;
+ cmd(t, ". configure -x " + string org.x + " -y " + string org.y);
+}
+
+tkcmds(top: ref Tk->Toplevel, a: array of string)
+{
+ n := len a;
+ for(i := 0; i < n; i++)
+ tk->cmd(top, a[i]);
+}
+
+topopts := array[] of {
+ "font"
+# , "bd" # Wait for someone to ask for these
+# , "relief" # Note: colors aren't inherited, it seems
+};
+
+opts(top: ref Tk->Toplevel) : string
+{
+ if (top == nil)
+ return nil;
+ opts := "";
+ for ( i := 0; i < len topopts; i++ ) {
+ cfg := tk->cmd(top, ". cget " + topopts[i]);
+ if ( cfg != "" && cfg[0] != '!' )
+ opts += " -" + topopts[i] + " " + tk->quote(cfg);
+ }
+ return opts;
+}
+
+kill(pid: int): int
+{
+ fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE);
+ if (fd == nil)
+ return -1;
+ if (sys->write(fd, array of byte "kill", 4) != 4)
+ return -1;
+ return 0;
+}
+Showtk: con 0;
+
+cmd(top: ref Tk->Toplevel, s: string): string
+{
+ if (Showtk)
+ sys->print("%s\n", s);
+ e := tk->cmd(top, s);
+ if (e != nil && e[0] == '!')
+ sys->fprint(sys->fildes(2), "tkclient: tk error %s on '%s'\n", e, s);
+ return e;
+}
+
+cmdget(top: ref Tk->Toplevel, s: string): string
+{
+ if (Showtk)
+ sys->print("%s\n", s);
+ tk->cmd(top, "variable lasterror");
+ e := tk->cmd(top, s);
+ lerr := tk->cmd(top, "variable lasterror");
+ if (lerr != nil) sys->fprint(sys->fildes(2), "tkclient: tk error %s on '%s'\n", e, s);
+ return e;
+}
diff --git a/appl/lib/sets.b b/appl/lib/sets.b
new file mode 100644
index 00000000..22f25d7b
--- /dev/null
+++ b/appl/lib/sets.b
@@ -0,0 +1,329 @@
+implement Sets;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sets.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+BPW: con 32;
+SHIFT: con 5;
+MASK: con 31;
+
+# Set adt contains:
+# a - array holding membership of set s for n (0 ≤ n < len a * BPW).
+# ∀ n: 0≤n<(len a*BPW), (s.a[n >> SHIFT] & (1 << (n & MASK)) != 0) iff s ∋ n
+# m - "most significant bits", extrapolate membership for n >= len a * BPW.
+# m is 0 if members are excluded by default,
+# or ~0 if members are included by default.
+
+swapops := array[16] of {
+ byte 2r0000, byte 2r0001, byte 2r0100, byte 2r0101,
+ byte 2r0010, byte 2r0011, byte 2r0110, byte 2r0111,
+ byte 2r1000, byte 2r1001, byte 2r1100, byte 2r1101,
+ byte 2r1010, byte 2r1011, byte 2r1110, byte 2r1111,
+};
+
+Set.X(s1: self Set, o: int, s2: Set): Set
+{
+ if (len s1.a > len s2.a) {
+ (s1, s2) = (s2, s1);
+ o = int swapops[o & 2r1111];
+ }
+ r := Set(0, array[len s2.a] of int);
+ for (i := 0; i < len s1.a; i++)
+ r.a[i] = op(o, s1.a[i], s2.a[i]);
+ for (; i < len s2.a; i++)
+ r.a[i] = op(o, s1.m, s2.a[i]);
+ r.m = op(o, s1.m, s2.m);
+ return r;
+}
+
+Set.invert(s: self Set): Set
+{
+ r := Set(~s.m, array[len s.a] of int);
+ for (i := 0; i < len s.a; i++)
+ r.a[i] = ~s.a[i];
+ return r;
+}
+
+# copy s, ensuring that the copy is big enough to hold n.
+copy(s: Set, n: int): Set
+{
+ if (n >= 0) {
+ req := (n >> SHIFT) + 1;
+ if (req > len s.a) {
+ a := array[req] of int;
+ a[0:] = s.a;
+ for (i := len s.a; i < len a; i++)
+ a[i] = s.m;
+ return (s.m, a);
+ }
+ }
+ a: array of int;
+ if (len s.a > 0) {
+ a = array[len s.a] of int;
+ a[0:] = s.a;
+ }
+ return (s.m, a);
+}
+
+Set.add(s: self Set, n: int): Set
+{
+ d := n >> SHIFT;
+ if (s.m && d >= len s.a)
+ return s;
+ r := copy(s, n);
+ r.a[d] |= 1<< (n & MASK);
+ return r;
+}
+
+Set.addlist(s: self Set, ns: list of int): Set
+{
+ r: Set;
+ if (s.m == 0) {
+ max := -1;
+ for (l := ns; l != nil; l = tl l)
+ if (hd l > max)
+ max = hd l;
+ r = copy(s, max);
+ } else
+ r = copy(s, -1);
+ for (; ns != nil; ns = tl ns) {
+ n := hd ns;
+ d := n >> SHIFT;
+ if (d < len r.a)
+ r.a[d] |= 1 << (n & MASK);
+ }
+ return r;
+}
+
+
+Set.del(s: self Set, n: int): Set
+{
+ d := n >> SHIFT;
+ if (!s.m && d >= len s.a)
+ return s;
+ r := copy(s, n);
+ r.a[d] &= ~(1 << (n & MASK));
+ return r;
+}
+
+Set.holds(s: self Set, n: int): int
+{
+ d := n >> SHIFT;
+ if (d >= len s.a)
+ return s.m;
+ return s.a[d] & (1 << (n & MASK));
+}
+
+Set.limit(s: self Set): int
+{
+ for (i := len s.a - 1; i >= 0; i--)
+ if (s.a[i] != s.m)
+ return (i<<SHIFT) + topbit(s.m ^ s.a[i]);
+ return 0;
+}
+
+Set.eq(s1: self Set, s2: Set): int
+{
+ if (len s1.a > len s2.a)
+ (s1, s2) = (s2, s1);
+ for (i := 0; i < len s1.a; i++)
+ if (s1.a[i] != s2.a[i])
+ return 0;
+ for (; i < len s2.a; i++)
+ if (s1.m != s2.a[i])
+ return 0;
+ return s1.m == s2.m;
+}
+
+Set.isempty(s: self Set): int
+{
+ return Set(0, nil).eq(s);
+}
+
+Set.msb(s: self Set): int
+{
+ return s.m != 0;
+}
+
+Set.bytes(s: self Set, n: int): array of byte
+{
+ m := (s.limit() >> 3) + 1;
+ if(m > n)
+ n = m;
+ d := array[n] of byte;
+ # XXX this could proably be made substantially faster by unrolling the
+ # loop a little.
+ for(i := 0; i < len d; i++){
+ j := i >> 2;
+ if(j >= len s.a)
+ d[i] = byte s.m;
+ else
+ d[i] = byte (s.a[j] >> ((i & 3) << 3));
+ }
+ return d;
+}
+
+bytes2set(d: array of byte): Set
+{
+ if(len d == 0)
+ return (0, nil);
+ a := array[(len d + 3) >> 2] of int; # round up
+ n := len d >> 2;
+ for(i := 0; i < n; i++){
+ j := i << 2;
+ a[i] = int d[j] + (int d[j+1] << 8) + (int d[j+2] << 16) + (int d[j+3] << 24);
+ }
+ msb := ~(int (d[len d - 1] >> 7) - 1);
+ j := i << 2;
+ case len d & 3 {
+ 0 =>
+ ;
+ 1 =>
+ a[i] = int d[j] | (msb & int 16rffffff00);
+ 2 =>
+ a[i] = int d[j] | (int d[j+1] << 8) | (msb & int 16rffff0000);
+ 3 =>
+ a[i] = int d[j] | (int d[j+1] << 8) | (int d[j+2] << 16) | (msb & int 16rff000000);
+ }
+ return (msb, a);
+}
+
+Set.str(s: self Set): string
+{
+ str: string;
+
+ # discard all top bits that are the same as msb.
+ sig := 0;
+loop:
+ for (i := len s.a - 1; i >= 0; i--) {
+ t := 16rf << (BPW - 4);
+ sig = 8;
+ while (t != 0) {
+ if ((s.m & t) != (s.a[i] & t))
+ break loop;
+ sig--;
+ t = (t >> 4) & 16r0fffffff; # logical shift right
+ }
+ }
+ if (i >= 0) {
+ top := s.a[i];
+ if (sig < 8) # shifting left by 32 bits is undefined.
+ top &= (1 << (sig << 2)) - 1;
+ str = sys->sprint("%.*ux", sig, top);
+ for (i--; i >= 0; i--)
+ str += sys->sprint("%.8ux", s.a[i]);
+ }
+ return str + ":" + string (s.m & 1);
+}
+
+str2set(str: string): Set
+{
+ n := len str;
+ if (n < 2 || str[n - 2] != ':')
+ return (0, nil);
+ c := str[n - 1];
+ if (c != '0' && c != '1')
+ return (0, nil);
+ msb := ~(c - '1');
+
+ n -= 2;
+ if (n == 0)
+ return (msb, nil);
+ req := ((n * 4 - 1) >> SHIFT) + 1;
+ a := array[req] of int;
+ d := 0;
+ for (i := n; i > 0; ) {
+ j := i - 8;
+ if (j < 0)
+ j = 0;
+ a[d++] = hex2int(str[j:i], msb);
+ i = j;
+ }
+ return (msb, a);
+}
+
+Set.debugstr(s: self Set): string
+{
+ str: string;
+ for (i := len s.a - 1; i >= 0; i--)
+ str += sys->sprint("%ux:", s.a[i]);
+ str += sys->sprint(":%ux", s.m);
+ return str;
+}
+
+set(): Set
+{
+ return (0, nil);
+}
+
+hex2int(s: string, fill: int): int
+{
+ n := fill;
+ for (i := 0; i < len s; i++) {
+ c := s[i];
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'a' && c <= 'f')
+ c -= 'a' - 10;
+ else if (c >= 'A' && c <= 'F')
+ c -= 'A' - 10;
+ else
+ c = 0;
+ n = (n << 4) | c;
+ }
+ return n;
+}
+
+op(o: int, a, b: int): int
+{
+ case o & 2r1111 {
+ 2r0000 => return 0;
+ 2r0001 => return ~(a | b);
+ 2r0010 => return a & ~b;
+ 2r0011 => return ~b;
+ 2r0100 => return ~a & b;
+ 2r0101 => return ~a;
+ 2r0110 => return a ^ b;
+ 2r0111 => return ~(a & b);
+ 2r1000 => return a & b;
+ 2r1001 => return ~(a ^ b);
+ 2r1010 => return a;
+ 2r1011 => return a | ~b;
+ 2r1100 => return b;
+ 2r1101 => return ~(a | b);
+ 2r1110 => return a | b;
+ 2r1111 => return ~0;
+ }
+ return 0;
+}
+
+topbit(v: int): int
+{
+ if (v == 0)
+ return 0;
+ (b, n, mask) := (1, 16, int 16rffff0000);
+ while (n != 0) {
+ if (v & mask) {
+ b += n;
+ v >>= n; # could return if v==0 here if we thought it worth it
+ }
+ n >>= 1;
+ mask >>= n;
+ }
+ return b;
+}
+
+nbits(n: int): int
+{
+ n = ((n >> 1) & 16r55555555) + (n & 16r55555555) ;
+ n = ((n >> 2) & 16r33333333) + (n & 16r33333333) ;
+ n = ((n >> 4) + n) & 16r0F0F0F0F ;
+ n = ((n >> 8) + n) ;
+ return ((n >> 16) + n) & 16rFF ;
+}
diff --git a/appl/lib/sets32.b b/appl/lib/sets32.b
new file mode 100644
index 00000000..01edea46
--- /dev/null
+++ b/appl/lib/sets32.b
@@ -0,0 +1,226 @@
+implement Sets;
+include "sys.m";
+ sys: Sys;
+include "sets32.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+set(): Set
+{
+ return Set(0);
+}
+
+BITS: con 32;
+MSB: con 1 << (BITS - 1);
+
+Set.X(s1: self Set, o: int, s2: Set): Set
+{
+ return Set(op(o, s1.s, s2.s));
+}
+
+Set.invert(s: self Set): Set
+{
+ return Set(~s.s);
+}
+
+Set.add(s: self Set, n: int): Set
+{
+ return Set(s.s | (1 << n));
+}
+
+Set.del(s: self Set, n: int): Set
+{
+ return Set(s.s & ~(1 << n));
+}
+
+Set.addlist(s: self Set, ns: list of int): Set
+{
+ for (; ns != nil; ns = tl ns)
+ s.s |= (1 << hd ns);
+ return s;
+}
+
+Set.holds(s: self Set, n: int): int
+{
+ return s.s & (1 << n);
+}
+
+Set.str(s: self Set): string
+{
+ msb := s.s >> (BITS - 1);
+
+ # discard all top bits that are the same as msb
+ t := 16rf << (BITS - 4);
+ sig := 8;
+ while (t != 0) {
+ if ((msb & t) != (s.s & t))
+ break;
+ sig--;
+ t = (t >> 4) & 16r0fffffff; # logical shift right
+ }
+ str: string;
+ if (sig > 0) {
+ top := ~MSB & s.s;
+ if (sig < 8) # shifting left by 32 bits is undefined.
+ top &= (1 << (sig << 2)) - 1;
+ str = sys->sprint("%.*ux", sig, top);
+ }
+ return str + ":" + string (msb & 1);
+}
+
+Set.bytes(s: self Set, n: int): array of byte
+{
+ m := (s.limit() >> 3) + 1;
+ if(m > n)
+ n = m;
+ d := array[n] of byte;
+ case len d {
+ 1 =>
+ d[0] = byte s.s;
+ 2 =>
+ d[0] = byte s.s;
+ d[1] = byte (s.s >> 8);
+ 3 =>
+ d[0] = byte s.s;
+ d[1] = byte (s.s >> 8);
+ d[2] = byte (s.s >> 16);
+ 4 =>
+ d[0] = byte s.s;
+ d[1] = byte (s.s >> 8);
+ d[2] = byte (s.s >> 16);
+ d[3] = byte (s.s >> 24);
+ * =>
+ d[0] = byte s.s;
+ d[1] = byte (s.s >> 8);
+ d[2] = byte (s.s >> 16);
+ d[3] = byte (s.s >> 24);
+ msb := byte (s.s >> (BITS - 1)); # sign extension
+ for(i := 4; i < len d; i++)
+ d[i] = msb;
+ }
+ return d;
+}
+
+bytes2set(d: array of byte): Set
+{
+ if(len d == 0)
+ return Set(0);
+ msb := ~(int (d[len d - 1] >> 7) - 1);
+ v: int;
+ case len d {
+ 1 =>
+ v = int d[0] | (msb & int 16rffffff00);
+ 2 =>
+ v = int d[0] | (int d[1] << 8) | (msb & int 16rffff0000);
+ 3 =>
+ v = int d[0] | (int d[1] << 8) | (int d[2] << 16) | (msb & int 16rff000000);
+ * or # XXX could raise (or return) an error for len d > 4
+ 4 =>
+ v = int d[0] | (int d[1] << 8) | (int d[2] << 16) | (int d[3] << 24);
+ }
+ return Set(v);
+}
+
+
+Set.debugstr(s: self Set): string
+{
+ return sys->sprint("%ux", s.s);
+}
+
+Set.eq(s1: self Set, s2: Set): int
+{
+ return s1.s == s2.s;
+}
+
+Set.isempty(s: self Set): int
+{
+ return s.s == 0;
+}
+
+Set.msb(s: self Set): int
+{
+ return (s.s & MSB) != 0;
+}
+
+Set.limit(s: self Set): int
+{
+ m := s.s >> (BITS - 1); # sign extension
+ return topbit(s.s ^ m);
+}
+
+topbit(v: int): int
+{
+ if (v == 0)
+ return 0;
+ (b, n, mask) := (1, 16, int 16rffff0000);
+ while (n != 0) {
+ if (v & mask) {
+ b += n;
+ v >>= n; # could return if v==0 here if we thought it worth it
+ }
+ n >>= 1;
+ mask >>= n;
+ }
+ return b;
+}
+
+
+str2set(str: string): Set
+{
+ n := len str;
+ if (n < 2 || str[n - 2] != ':')
+ return Set(0);
+ c := str[n - 1];
+ if (c != '0' && c != '1')
+ return Set(0);
+ n -= 2;
+ msb := ~(c - '1');
+ # XXX should we give some sort of error if there
+ # are more bits than we can hold?
+ return Set((hex2int(str[0:n], msb) & ~MSB) | (msb & MSB));
+}
+
+hex2int(s: string, fill: int): int
+{
+ n := fill;
+ for (i := 0; i < len s; i++) {
+ c := s[i];
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'a' && c <= 'f')
+ c -= 'a' - 10;
+ else if (c >= 'A' && c <= 'F')
+ c -= 'A' - 10;
+ else
+ c = 0;
+ n = (n << 4) | c;
+ }
+ return n;
+}
+
+
+op(o: int, a, b: int): int
+{
+ case o & 2r1111 {
+ 2r0000 => return 0;
+ 2r0001 => return ~(a | b);
+ 2r0010 => return a & ~b;
+ 2r0011 => return ~b;
+ 2r0100 => return ~a & b;
+ 2r0101 => return ~a;
+ 2r0110 => return a ^ b;
+ 2r0111 => return ~(a & b);
+ 2r1000 => return a & b;
+ 2r1001 => return ~(a ^ b);
+ 2r1010 => return a;
+ 2r1011 => return a | ~b;
+ 2r1100 => return b;
+ 2r1101 => return ~(a | b);
+ 2r1110 => return a | b;
+ 2r1111 => return ~0;
+ }
+ return 0;
+}
diff --git a/appl/lib/sexprs.b b/appl/lib/sexprs.b
new file mode 100644
index 00000000..7c42dd35
--- /dev/null
+++ b/appl/lib/sexprs.b
@@ -0,0 +1,638 @@
+implement Sexprs;
+
+#
+# full SDSI/SPKI S-expression reader
+#
+# Copyright © 2003-2004 Vita Nuova Holdings Limited
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "encoding.m";
+ base64: Encoding;
+ base16: Encoding;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "sexprs.m";
+
+Maxtoken: con 1024*1024; # should be more than enough
+
+Syntax: exception(string, big);
+Here: con big -1;
+
+Rd: adt[T]
+ for {
+ T =>
+ getb: fn(nil: self T): int;
+ ungetb: fn(nil: self T): int;
+ offset: fn(nil: self T): big;
+ }
+{
+ t: T;
+
+ parseitem: fn(rd: self ref Rd[T]): ref Sexp raises (Syntax);
+ ws: fn(rd: self ref Rd[T]): int;
+ simplestring: fn(rd: self ref Rd[T], c: int, hint: string): ref Sexp raises (Syntax);
+ toclosing: fn(rd: self ref Rd[T], c: int): string raises (Syntax);
+ unquote: fn(rd: self ref Rd[T]): string raises (Syntax);
+};
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ base64 = load Encoding Encoding->BASE64PATH;
+ base16 = load Encoding Encoding->BASE16PATH;
+ bufio = load Bufio Bufio->PATH;
+ bufio->sopen("");
+}
+
+Sexp.read[T](t: T): (ref Sexp, string)
+ for {
+ T =>
+ getb: fn(nil: self T): int;
+ ungetb: fn(nil: self T): int;
+ offset: fn(nil: self T): big;
+ }
+{
+ {
+ rd := ref Rd[T](t);
+ e := rd.parseitem();
+ return (e, nil);
+ }exception e {
+ Syntax =>
+ (diag, pos) := e;
+ if(pos < big 0)
+ pos += t.offset();
+ return (nil, sys->sprint("%s at offset %bd", diag, pos));
+ }
+}
+
+Sexp.parse(s: string): (ref Sexp, string, string)
+{
+ f := bufio->sopen(s);
+ (e, diag) := Sexp.read(f);
+ pos := int f.offset();
+ return (e, s[pos:], diag);
+}
+
+Sexp.unpack(a: array of byte): (ref Sexp, array of byte, string)
+{
+ f := bufio->aopen(a);
+ (e, diag) := Sexp.read(f);
+ pos := int f.offset();
+ return (e, a[pos:], diag);
+}
+
+Rd[T].parseitem(rd: self ref Rd[T]): ref Sexp raises (Syntax)
+{
+ p0 := rd.t.offset();
+ {
+ c := rd.ws();
+ if(c < 0)
+ return nil;
+ case c {
+ '{' =>
+ a := rd.toclosing('}');
+ f := bufio->aopen(base64->dec(a));
+ ht: type Rd[ref Iobuf];
+ nr := ref ht(f);
+ return nr.parseitem();
+ '(' =>
+ lists: list of ref Sexp;
+ while((c = rd.ws()) != ')'){
+ if(c < 0)
+ raise Syntax("unclosed '('", p0);
+ rd.t.ungetb();
+ e := rd.parseitem(); # we'll catch missing ) at top of loop
+ lists = e :: lists;
+ }
+ rl := lists;
+ lists = nil;
+ for(; rl != nil; rl = tl rl)
+ lists = hd rl :: lists;
+ return ref Sexp.List(lists);
+ '[' =>
+ # display hint
+ e := rd.simplestring(rd.t.getb(), nil);
+ c = rd.ws();
+ if(c != ']'){
+ if(c >= 0)
+ rd.t.ungetb();
+ raise Syntax("missing ] in display hint", p0);
+ }
+ pick r := e {
+ String =>
+ return rd.simplestring(rd.ws(), r.s);
+ * =>
+ raise Syntax("illegal display hint", Here);
+ }
+ * =>
+ return rd.simplestring(c, nil);
+ }
+ }exception{
+ Syntax => raise;
+ }
+}
+
+# skip white space
+Rd[T].ws(rd: self ref Rd[T]): int
+{
+ while(isspace(c := rd.t.getb()))
+ {}
+ return c;
+}
+
+isspace(c: int): int
+{
+ return c == ' ' || c == '\r' || c == '\t' || c == '\n';
+}
+
+Rd[T].simplestring(rd: self ref Rd[T], c: int, hint: string): ref Sexp raises (Syntax)
+{
+ dec := -1;
+ decs: string;
+ if(c >= '0' && c <= '9'){
+ for(dec = 0; c >= '0' && c <= '9'; c = rd.t.getb()){
+ dec = dec*10 + c-'0';
+ decs[len decs] = c;
+ }
+ if(dec < 0 || dec > Maxtoken)
+ raise Syntax("implausible token length", Here);
+ }
+ {
+ case c {
+ '"' =>
+ text := rd.unquote();
+ return ref Sexp.String(text, hint);
+ '|' =>
+ return sform(base64->dec(rd.toclosing(c)), hint);
+ '#' =>
+ return sform(base16->dec(rd.toclosing(c)), hint);
+ * =>
+ if(c == ':' && dec >= 0){ # raw bytes
+ a := array[dec] of byte;
+ for(i := 0; i < dec; i++){
+ c = rd.t.getb();
+ if(c < 0)
+ raise Syntax("missing bytes in raw token", Here);
+ a[i] = byte c;
+ }
+ return sform(a, hint);
+ }
+ #s := decs;
+ if(decs != nil)
+ raise Syntax("token can't start with a digit", Here);
+ s: string; # <token> by definition is always printable; never utf-8
+ while(istokenc(c)){
+ s[len s] = c;
+ c = rd.t.getb();
+ }
+ if(s == nil)
+ raise Syntax("missing token", Here); # consume c to ensure progress on error
+ if(c >= 0)
+ rd.t.ungetb();
+ return ref Sexp.String(s, hint);
+ }
+ }exception{
+ Syntax => raise;
+ }
+}
+
+sform(a: array of byte, hint: string): ref Sexp
+{
+ if(istextual(a))
+ return ref Sexp.String(string a, hint);
+ return ref Sexp.Binary(a, hint);
+}
+
+Rd[T].toclosing(rd: self ref Rd[T], end: int): string raises (Syntax)
+{
+ s: string;
+ p0 := rd.t.offset();
+ while((c := rd.t.getb()) != end){
+ if(c < 0)
+ raise Syntax(sys->sprint("missing closing '%c'", end), p0);
+ s[len s] = c;
+ }
+ return s;
+}
+
+hex(c: int): int
+{
+ if(c >= '0' && c <= '9')
+ return c-'0';
+ if(c >= 'a' && c <= 'f')
+ return 10+(c-'a');
+ if(c >= 'A' && c <= 'F')
+ return 10+(c-'A');
+ return -1;
+}
+
+Rd[T].unquote(rd: self ref Rd[T]): string raises (Syntax)
+{
+ os: string;
+
+ p0 := rd.t.offset();
+ while((c := rd.t.getb()) != '"'){
+ if(c < 0)
+ raise Syntax("unclosed quoted string", p0);
+ if(c == '\\'){
+ e0 := rd.t.offset();
+ c = rd.t.getb();
+ if(c < 0)
+ break;
+ case c {
+ '\r' =>
+ c = rd.t.getb();
+ if(c != '\n')
+ rd.t.ungetb();
+ continue;
+ '\n' =>
+ c = rd.t.getb();
+ if(c != '\r')
+ rd.t.ungetb();
+ continue;
+ 'b' =>
+ c = '\b';
+ 'f' =>
+ c = '\f';
+ 'n' =>
+ c = '\n';
+ 'r' =>
+ c = '\r';
+ 't' =>
+ c = '\t';
+ 'v' =>
+ c = '\v';
+ '0' to '7' =>
+ oct := 0;
+ for(i := 0;;){
+ if(!(c >= '0' && c <= '7'))
+ raise Syntax("illegal octal escape", e0);
+ oct = (oct<<3) | (c-'0');
+ if(++i == 3)
+ break;
+ c = rd.t.getb();
+ }
+ c = oct & 16rFF;
+ 'x' =>
+ c0 := hex(rd.t.getb());
+ c1 := hex(rd.t.getb());
+ if(c0 < 0 || c1 < 0)
+ raise Syntax("illegal hex escape", e0);
+ c = (c0<<4) | c1;
+ * =>
+ ; # as-is
+ }
+ }
+ os[len os] = c;
+ }
+ return os;
+}
+
+hintlen(s: string): int
+{
+ if(s == nil)
+ return 0;
+ n := len array of byte s;
+ return len sys->aprint("[%d:]", n) + n;
+}
+
+Sexp.packedsize(e: self ref Sexp): int
+{
+ if(e == nil)
+ return 0;
+ pick r := e{
+ String =>
+ n := len array of byte r.s;
+ return hintlen(r.hint) + len sys->aprint("%d:", n) + n;
+ Binary =>
+ n := len r.data;
+ return hintlen(r.hint) + len sys->aprint("%d:", n) + n;
+ List =>
+ n := 1; # '('
+ for(l := r.l; l != nil; l = tl l)
+ n += (hd l).packedsize();
+ return n+1; # + ')'
+ }
+}
+
+packbytes(a: array of byte, b: array of byte): array of byte
+{
+ n := len b;
+ c := sys->aprint("%d:", n);
+ a[0:] = c;
+ a[len c:] = b;
+ return a[len c+n:];
+}
+
+packhint(a: array of byte, s: string): array of byte
+{
+ if(s == nil)
+ return a;
+ a[0] = byte '[';
+ a = packbytes(a[1:], array of byte s);
+ a[0] = byte ']';
+ return a[1:];
+}
+
+pack(e: ref Sexp, a: array of byte): array of byte
+{
+ if(e == nil)
+ return array[0] of byte;
+ pick r := e{
+ String =>
+ if(r.hint != nil)
+ a = packhint(a, r.hint);
+ return packbytes(a, array of byte r.s);
+ Binary =>
+ if(r.hint != nil)
+ a = packhint(a, r.hint);
+ return packbytes(a, r.data);
+ List =>
+ a[0] = byte '(';
+ a = a[1:];
+ for(l := r.l; l != nil; l = tl l)
+ a = pack(hd l, a);
+ a[0] = byte ')';
+ return a[1:];
+ }
+}
+
+Sexp.pack(e: self ref Sexp): array of byte
+{
+ a := array[e.packedsize()] of byte;
+ pack(e, a);
+ return a;
+}
+
+Sexp.b64text(e: self ref Sexp): string
+{
+ return "{" + base64->enc(e.pack()) + "}";
+}
+
+Sexp.text(e: self ref Sexp): string
+{
+ if(e == nil)
+ return "";
+ pick r := e{
+ String =>
+ s := quote(r.s);
+ if(r.hint == nil)
+ return s;
+ return "["+quote(r.hint)+"]"+s;
+ Binary =>
+ h := r.hint;
+ if(h != nil)
+ h = "["+quote(h)+"]";
+ if(len r.data <= 4)
+ return sys->sprint("%s#%s#", h, base16->enc(r.data));
+ return sys->sprint("%s|%s|", h, base64->enc(r.data));
+ List =>
+ s := "(";
+ for(l := r.l; l != nil; l = tl l){
+ s += (hd l).text();
+ if(tl l != nil)
+ s += " ";
+ }
+ return s+")";
+ }
+}
+
+#An octet string that meets the following conditions may be given
+#directly as a "token".
+#
+# -- it does not begin with a digit
+#
+# -- it contains only characters that are
+# -- alphabetic (upper or lower case),
+# -- numeric, or
+# -- one of the eight "pseudo-alphabetic" punctuation marks:
+# - . / _ : * + =
+# (Note: upper and lower case are not equivalent.)
+# (Note: A token may begin with punctuation, including ":").
+
+istokenc(c: int): int
+{
+ return c >= '0' && c <= '9' ||
+ c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' ||
+ c == '-' || c == '.' || c == '/' || c == '_' || c == ':' || c == '*' || c == '+' || c == '=';
+}
+
+istoken(s: string): int
+{
+ if(s == nil)
+ return 0;
+ for(i := 0; i < len s; i++)
+ case s[i] {
+ '0' to '9' =>
+ if(i == 0)
+ return 0;
+ 'a' to 'z' or 'A' to 'Z' or
+ '-' or '.' or '/' or '_' or ':' or '*' or '+' or '=' =>
+ break;
+ * =>
+ return 0;
+ }
+ return 1;
+}
+
+# should the data qualify as binary or text?
+# the if(0) version accepts valid Unicode sequences
+# could use [display] to control character set?
+istextual(a: array of byte): int
+{
+ for(i := 0; i < len a;){
+ if(0){
+ (c, n, ok) := sys->byte2char(a, i);
+ if(!ok || c < ' ' && !isspace(c) || c >= 16r7F)
+ return 0;
+ i += n;
+ }else{
+ c := int a[i++];
+ if(c < ' ' && !isspace(c) || c >= 16r7F)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+esc(c: int): string
+{
+ case c {
+ '"' => return "\\\"";
+ '\\' => return "\\\\";
+ '\b' => return "\\b";
+ '\f' => return "\\f";
+ '\n' => return "\\n";
+ '\t' => return "\\t";
+ '\r' => return "\\r";
+ '\v' => return "\\v";
+ * =>
+ if(c < ' ' || c >= 16r7F)
+ return sys->sprint("\\x%.2ux", c & 16rFF);
+ }
+ return nil;
+}
+
+quote(s: string): string
+{
+ if(istoken(s))
+ return s;
+ for(i := 0; i < len s; i++)
+ if((v := esc(s[i])) != nil){
+ os := "\"" + s[0:i] + v;
+ while(++i < len s){
+ if((v = esc(s[i])) != nil)
+ os += v;
+ else
+ os[len os] = s[i];
+ }
+ os[len os] = '"';
+ return os;
+ }
+ return "\""+s+"\"";
+}
+
+#
+# other S expression operations
+#
+Sexp.islist(e: self ref Sexp): int
+{
+ return e != nil && tagof e == tagof Sexp.List;
+}
+
+Sexp.els(e: self ref Sexp): list of ref Sexp
+{
+ if(e == nil)
+ return nil;
+ pick s := e {
+ List =>
+ return s.l;
+ * =>
+ return nil;
+ }
+}
+
+Sexp.op(e: self ref Sexp): string
+{
+ if(e == nil)
+ return nil;
+ pick s := e {
+ String =>
+ return s.s;
+ Binary =>
+ return nil;
+ List =>
+ if(s.l == nil)
+ return nil;
+ pick t := hd s.l {
+ String =>
+ return t.s;
+ * =>
+ return nil;
+ }
+ }
+ return nil;
+}
+
+Sexp.args(e: self ref Sexp): list of ref Sexp
+{
+ if((l := e.els()) != nil)
+ return tl l;
+ return nil;
+}
+
+Sexp.asdata(e: self ref Sexp): array of byte
+{
+ if(e == nil)
+ return nil;
+ pick s := e {
+ List =>
+ return nil;
+ String =>
+ return array of byte s.s;
+ Binary =>
+ return s.data;
+ }
+}
+
+Sexp.astext(e: self ref Sexp): string
+{
+ if(e == nil)
+ return nil;
+ pick s := e {
+ List =>
+ return nil;
+ String =>
+ return s.s;
+ Binary =>
+ return string s.data; # questionable; should possibly treat it as latin-1
+ }
+}
+
+Sexp.eq(e1: self ref Sexp, e2: ref Sexp): int
+{
+ if(e1 == e2)
+ return 1;
+ if(e1 == nil || e2 == nil || tagof e1 != tagof e2)
+ return 0;
+ pick s1 := e1 {
+ List =>
+ pick s2 := e2 {
+ List =>
+ l1 := s1.l;
+ l2 := s2.l;
+ for(; l1 != nil; l1 = tl l1){
+ if(l2 == nil || !(hd l1).eq(hd l2))
+ return 0;
+ l2 = tl l2;
+ }
+ return l2 == nil;
+ }
+ String =>
+ pick s2 := e2 {
+ String =>
+ return s1.s == s2.s && s1.hint == s2.hint;
+ }
+ Binary =>
+ pick s2 := e2 {
+ Binary =>
+ if(len s1.data != len s2.data || s1.hint != s2.hint)
+ return 0;
+ for(i := 0; i < len s1.data; i++)
+ if(s1.data[i] != s2.data[i])
+ return 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+Sexp.copy(e: self ref Sexp): ref Sexp
+{
+ if(e == nil)
+ return nil;
+ pick r := e {
+ List =>
+ rl: list of ref Sexp;
+ for(l := r.l; l != nil; l = tl l)
+ rl = (hd l).copy() :: rl;
+ for(l = nil; rl != nil; rl = tl rl)
+ l = hd rl :: l;
+ return ref Sexp.List(l);
+ String =>
+ return ref *r; # safe because .s and .hint are strings, immutable
+ Binary =>
+ b: array of byte;
+ if((a := r.data) != nil){
+ b = array[len a] of byte;
+ b[0:] = a;
+ }
+ return ref Sexp.Binary(b, r.hint);
+ }
+}
diff --git a/appl/lib/slip.b b/appl/lib/slip.b
new file mode 100644
index 00000000..da904015
--- /dev/null
+++ b/appl/lib/slip.b
@@ -0,0 +1,113 @@
+implement Filter;
+
+include "sys.m";
+
+include "filter.m";
+
+End: con byte 8r300;
+Esc: con byte 8r333;
+Eend: con byte 8r334; # encoded End byte
+Eesc: con byte 8r335; # encoded Esc byte
+
+init()
+{
+}
+
+start(param: string): chan of ref Rq
+{
+ req := chan of ref Rq;
+ if(param == "encode")
+ spawn encode(req);
+ else
+ spawn decode(req);
+ return req;
+}
+
+encode(reqs: chan of ref Rq)
+{
+ sys := load Sys Sys->PATH;
+ reqs <-= ref Rq.Start(sys->pctl(0, nil));
+ buf := array[8192] of byte;
+ rc := chan of int;
+ do{
+ reqs <-= ref Rq.Fill(buf, rc);
+ if((n := <-rc) <= 0){
+ if(n == 0)
+ reqs <-= ref Rq.Finished(nil);
+ break;
+ }
+ b := array[2*n + 2] of byte; # optimise time not space
+ o := 1;
+ b[0] = End;
+ for(i := 0; i < n; i++){
+ if((c := buf[i]) == End || c == Esc){
+ b[o++] = Esc;
+ c = byte (Eend + (c& byte 1));
+ }
+ b[o++] = c;
+ }
+ b[o++] = End;
+ if(o != len b)
+ b = b[0:o];
+ reqs <-= ref Rq.Result(b, rc);
+ }while(<-rc != -1);
+}
+
+Slipesc, Slipend: con (1<<8) + iota;
+Slipsize: con 1006; # rfc's suggestion
+
+slipin(c: byte, esc: int): int
+{
+ if(esc == Slipesc){ # last byte was Esc
+ if(c == Eend)
+ c = End;
+ else if(c == Eesc)
+ c = Esc;
+ }else{
+ if(c == Esc)
+ return Slipesc;
+ if(c == End)
+ return Slipend;
+ }
+ return int c;
+}
+
+decode(reqs: chan of ref Rq)
+{
+ sys := load Sys Sys->PATH;
+ reqs <-= ref Rq.Start(sys->pctl(0, nil));
+ buf := array[8192] of byte;
+ b := array[Slipsize] of byte;
+ rc := chan of int;
+ c := 0;
+ o := 0;
+ for(;;){
+ reqs <-= ref Rq.Fill(buf, rc);
+ if((n := <-rc) <= 0){
+ if(n < 0)
+ exit;
+ break;
+ }
+ for(i := 0; i < n; i++){
+ c = slipin(buf[i], c);
+ if(c == Slipend){
+ if(o != 0){
+ reqs <-= ref Rq.Result(b[0:o], rc);
+ if(<-rc == -1)
+ exit;
+ b = array[Slipsize] of byte;
+ o = 0;
+ }
+ }else if(c != Slipesc){
+ if(o >= len b){
+ t := array[3*len b/2] of byte;
+ t[0:] = b;
+ b = t;
+ }
+ b[o++] = byte c;
+ }
+ }
+ }
+ # partial block discarded
+ reqs <-= ref Rq.Finished(nil);
+}
diff --git a/appl/lib/smtp.b b/appl/lib/smtp.b
new file mode 100644
index 00000000..a1d92b8e
--- /dev/null
+++ b/appl/lib/smtp.b
@@ -0,0 +1,241 @@
+implement Smtp;
+
+include "sys.m";
+ sys : Sys;
+include "bufio.m";
+ bufio : Bufio;
+include "smtp.m";
+
+FD, Connection: import sys;
+Iobuf : import bufio;
+
+ibuf, obuf : ref Bufio->Iobuf;
+conn : int = 0;
+init : int = 0;
+
+rpid : int = -1;
+cread : chan of (int, string);
+
+DEBUG : con 0;
+
+open(server : string): (int, string)
+{
+ s : string;
+
+ if (!init) {
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ init = 1;
+ }
+ if (conn)
+ return (-1, "connection is already open");
+ if (server == nil)
+ server = "$smtp";
+ (ok, c) := sys->dial ("tcp!" + server + "!25", nil);
+ if (ok < 0)
+ return (-1, "dialup failed");
+ ibuf = bufio->fopen(c.dfd, Bufio->OREAD);
+ obuf = bufio->fopen(c.dfd, Bufio->OWRITE);
+ if (ibuf == nil || obuf == nil)
+ return (-1, "failed to open bufio");
+ cread = chan of (int, string);
+ spawn mreader(cread);
+ (rpid, nil) = <- cread;
+ (ok, s) = mread();
+ if (ok < 0)
+ return (-1, s);
+ conn = 1;
+ return (1, nil);
+}
+
+sendmail (fromwho : string, towho : list of string, cc : list of string, mlist: list of string): (int, string)
+{
+ ok : int;
+ s, t, line : string;
+
+ if (!conn)
+ return (-1, "connection is not open");
+ (ok, s) = mcmd("RSET");
+ if (ok < 0)
+ return (-1, s);
+ (user, dom) := split(fromwho, '@');
+ if (fromwho == nil || user == nil)
+ return (-1, "no 'from' name");
+ if (towho == nil)
+ return (-1, "no 'to' name");
+ if (dom == nil)
+ return (-1, "no domain name");
+ (ok, s) = mcmd("HELO " + dom);
+ if (ok < 0)
+ return (-1, s);
+ (ok, s) = mcmd("MAIL FROM:<" + fromwho + ">");
+ if (ok < 0)
+ return (-1, s);
+ all := concat(towho, cc);
+ t = nil;
+ for ( ; all != nil; all = tl all) {
+ (ok, s) = mcmd("RCPT TO:<" + hd all + ">");
+ if (ok < 0)
+ t += " " + s;
+ }
+ if (t != nil)
+ return (-1, t);
+ (ok, s) = mcmd("DATA");
+ if (ok < 0)
+ return (-1, s);
+ for ( ; mlist != nil; mlist = tl mlist) {
+ for (msg := hd mlist; msg != nil; ) {
+ (line, msg) = split(msg, '\n'); # BUG: too much copying for larger messages
+ if (putline(line) < 0)
+ return (-1, sys->sprint("write to server failed: %r"));
+ }
+ }
+ obuf.flush();
+ (ok, s) = mcmd(".");
+ if (ok < 0)
+ return (-1, s);
+ return (1, nil);
+}
+
+putline(line: string): int
+{
+ ln := len line;
+ if (ln > 0 && line[ln-1] == '\r')
+ line = line[0:ln-1];
+ if (line != nil && line[0] == '.'){
+ if(obuf.putb(byte '.') < 0)
+ return -1;
+ }
+ if(line != nil && obuf.puts(line) < 0)
+ return -1;
+ return obuf.puts("\r\n");
+}
+
+close(): (int, string)
+{
+ ok : int;
+
+ if (!conn)
+ return (-1, "connection is not open");
+ ok = mwrite("QUIT");
+ kill(rpid);
+ ibuf.close();
+ obuf.close();
+ conn = 0;
+ if (ok < 0)
+ return (-1, "failed to close connection");
+ return (1, nil);
+}
+
+SLPTIME : con 100;
+MAXSLPTIME : con 10000;
+
+mread() : (int, string)
+{
+ t := 0;
+ while (t < MAXSLPTIME) {
+ alt {
+ (ok, s) := <- cread =>
+ return (ok, s);
+ * =>
+ t += SLPTIME;
+ sys->sleep(SLPTIME);
+ }
+ }
+ kill(rpid);
+ return (-1, "smtp timed out\n");
+}
+
+mreader(c : chan of (int, string))
+{
+ c <- = (sys->pctl(0, nil), nil);
+ for (;;) {
+ line := ibuf.gets('\n');
+ if (DEBUG)
+ sys->print("mread : %s", line);
+ if (line == nil) {
+ c <- = (-1, "could not read response from server");
+ continue;
+ }
+ l := len line;
+ if (line[l-1] == '\n')
+ l--;
+ if (line[l-1] == '\r')
+ l--;
+ if (l < 3) {
+ c <- = (-1, "short response from server");
+ continue;
+ }
+ if (l > 0 && (line[0] == '1' || line[0] == '2' || line[0] == '3')) {
+ c <- = (1, nil);
+ continue;
+ }
+ c <- = (-1, line[3:l]);
+ }
+}
+
+mwrite(s : string): int
+{
+ s += "\r\n";
+ if (DEBUG)
+ sys->print("mwrite : %s", s);
+ b := array of byte s;
+ l := len b;
+ nb := obuf.write(b, l);
+ obuf.flush();
+ if (nb != l)
+ return -1;
+ return 1;
+}
+
+mcmd(s : string) : (int, string)
+{
+ ok : int;
+ r : string;
+
+ ok = mwrite(s);
+ if (ok < 0)
+ return (-1, err(s) + " send failed");
+ (ok, r) = mread();
+ if (ok < 0)
+ return (-1, err(s) + " receive failed (" + r + ")");
+ return (1, nil);
+}
+
+split(s : string, c : int) : (string, string)
+{
+ for (i := 0; i < len s; i++)
+ if (s[i] == c)
+ return (s[0:i], s[i+1:]);
+ return (s, nil);
+}
+
+concat(l1, l2 : list of string) : list of string
+{
+ ls : list of string;
+
+ ls = nil;
+ for (l := l1; l != nil; l = tl l)
+ ls = hd l :: ls;
+ for (l = l2; l != nil; l = tl l)
+ ls = hd l :: ls;
+ return ls;
+}
+
+err(s : string) : string
+{
+ for (i := 0; i < len s; i++)
+ if (s[i] == ' ' || s[i] == ':')
+ return s[0:i];
+ return s;
+}
+
+kill(pid : int) : int
+{
+ if (pid < 0)
+ return 0;
+ fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE);
+ if (fd == nil || sys->fprint(fd, "kill") < 0)
+ return -1;
+ return 0;
+}
diff --git a/appl/lib/sort.b b/appl/lib/sort.b
new file mode 100644
index 00000000..a425845e
--- /dev/null
+++ b/appl/lib/sort.b
@@ -0,0 +1,36 @@
+implement Sort;
+include "sort.m";
+
+sort[S, T](s: S, a: array of T)
+ for{
+ S =>
+ gt: fn(s: self S, x, y: T): int;
+ }
+{
+ mergesort(s, a, array[len a] of T);
+}
+
+mergesort[S, T](s: S, a, b: array of T)
+ for{
+ S =>
+ gt: fn(s: self S, x, y: T): int;
+ }
+{
+ r := len a;
+ if (r > 1) {
+ m := (r-1)/2 + 1;
+ mergesort(s, a[0:m], b[0:m]);
+ mergesort(s, a[m:], b[m:]);
+ b[0:] = a;
+ for ((i, j, k) := (0, m, 0); i < m && j < r; k++) {
+ if(s.gt(b[i], b[j]))
+ a[k] = b[j++];
+ else
+ a[k] = b[i++];
+ }
+ if (i < m)
+ a[k:] = b[i:m];
+ else if (j < r)
+ a[k:] = b[j:r];
+ }
+}
diff --git a/appl/lib/spki/mkfile b/appl/lib/spki/mkfile
new file mode 100644
index 00000000..9d096625
--- /dev/null
+++ b/appl/lib/spki/mkfile
@@ -0,0 +1,21 @@
+<../../../mkconfig
+
+TARG=\
+ spki.dis\
+ verifier.dis\
+
+MODULES=
+
+SYSMODULES= \
+ sys.m\
+ daytime.m\
+ keyring.m\
+ security.m\
+ bufio.m\
+ sexprs.m\
+ spki.m\
+ encoding.m\
+
+DISBIN=$ROOT/dis/lib/spki
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/lib/spki/spki.b b/appl/lib/spki/spki.b
new file mode 100644
index 00000000..302eb1aa
--- /dev/null
+++ b/appl/lib/spki/spki.b
@@ -0,0 +1,2109 @@
+implement SPKI;
+
+#
+# Copyright © 2004 Vita Nuova Holdings Limited
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "daytime.m";
+ daytime: Daytime;
+
+include "keyring.m";
+ kr: Keyring;
+ IPint, Certificate, PK, SK: import kr;
+
+include "security.m";
+
+include "bufio.m";
+
+include "sexprs.m";
+ sexprs: Sexprs;
+ Sexp: import sexprs;
+
+include "spki.m";
+
+include "encoding.m";
+ base16: Encoding;
+ base64: Encoding;
+
+debug: con 0;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ kr = load Keyring Keyring->PATH;
+ daytime = load Daytime Daytime->PATH;
+ sexprs = load Sexprs Sexprs->PATH;
+ base16 = load Encoding Encoding->BASE16PATH;
+ base64 = load Encoding Encoding->BASE64PATH;
+
+ sexprs->init();
+}
+
+#
+# parse SPKI structures
+#
+
+parse(e: ref Sexp): (ref Toplev, string)
+{
+ if(e == nil)
+ return (nil, "nil expression");
+ if(!e.islist())
+ return (nil, "list expected");
+ case e.op() {
+ "cert" =>
+ if((c := parsecert(e)) != nil)
+ return (ref Toplev.C(c), nil);
+ return (nil, "bad certificate syntax");
+ "signature" =>
+ if((s := parsesig(e)) != nil)
+ return (ref Toplev.Sig(s), nil);
+ return (nil, "bad signature syntax");
+ "public-key" =>
+ if((k := parsekey(e)) != nil)
+ return (ref Toplev.K(k), nil);
+ return (nil, "bad public-key syntax");
+ "sequence" =>
+ if((els := parseseq(e)) != nil)
+ return (ref Toplev.Seq(els), nil);
+ return (nil, "bad sequence syntax");
+ * =>
+ return (nil, sys->sprint("unknown operation: %#q", e.op()));
+ }
+}
+
+parseseq(e: ref Sexp): list of ref Seqel
+{
+ l := mustbe(e, "sequence");
+ if(l == nil)
+ return nil;
+ rl: list of ref Seqel;
+ for(; l != nil; l = tl l){
+ se := hd l;
+ case se.op() {
+ "cert" =>
+ cert := parsecert(se);
+ if(cert == nil)
+ return nil;
+ rl = ref Seqel.C(cert) :: rl;
+ "do" =>
+ el := se.args();
+ if(el == nil)
+ return nil;
+ op := (hd el).astext();
+ if(op == nil)
+ return nil;
+ rl = ref Seqel.O(op, tl el) :: rl;
+ "public-key" =>
+ k := parsekey(se);
+ if(k == nil)
+ return nil;
+ rl = ref Seqel.K(k) :: rl;
+ "signature" =>
+ sig := parsesig(se);
+ if(sig == nil)
+ return nil;
+ rl = ref Seqel.S(sig) :: rl;
+ * =>
+ rl = ref Seqel.E(se) :: rl;
+ }
+ }
+ return rev(rl);
+}
+
+parsecert(e: ref Sexp): ref Cert
+{
+ # "(" "cert" <version>? <cert-display>? <issuer> <issuer-loc>? <subject> <subject-loc>?
+ # <deleg>? <tag> <valid>? <comment>? ")"
+ # elements can appear in any order in a top-level item, though the one above is conventional
+
+ l := mustbe(e, "cert");
+ if(l == nil)
+ return nil;
+ delegate := 0;
+ issuer: ref Name;
+ subj: ref Subject;
+ tag: ref Sexp;
+ valid: ref Valid;
+ for(; l != nil; l = tl l){
+ t := (hd l).op();
+ case t {
+ "version" or "display" or "issuer-info" or "subject-info" or "comment" =>
+ ; # skip
+ "issuer" =>
+ # <principal> | <name> [via issuer-name]
+ if(issuer != nil)
+ return nil;
+ ie := onlyarg(hd l);
+ if(ie == nil)
+ return nil;
+ issuer = parsecompound(ie);
+ if(issuer == nil)
+ return nil;
+ "subject" =>
+ # <subject>:: "(" "subject" <subj-obj> ")" ;
+ if(subj != nil)
+ return nil;
+ se := onlyarg(hd l);
+ if(se == nil)
+ return nil;
+ subj = parsesubjobj(se);
+ if(subj == nil)
+ return nil;
+ "propagate" =>
+ if(delegate)
+ return nil;
+ delegate = 1;
+ "tag" =>
+ if(tag != nil)
+ return nil;
+ tag = maketag(hd l); # can safely leave (tag ...) operation in place
+ "valid" =>
+ if(valid != nil)
+ return nil;
+ valid = parsevalid(hd l);
+ if(valid == nil)
+ return nil;
+ * =>
+ sys->print("cert component: %q unknown/ignored\n", t);
+ }
+ }
+ if(issuer == nil || subj == nil)
+ return nil;
+ pick s := subj {
+ KH =>
+ return ref Cert.KH(e, issuer, subj, valid, delegate, tag);
+ O =>
+ return ref Cert.O(e, issuer, subj, valid, delegate, tag);
+ * =>
+ if(issuer.isprincipal())
+ return ref Cert.A(e, issuer, subj, valid, delegate, tag);
+ return ref Cert.N(e, issuer, subj, valid);
+ }
+}
+
+parsesubjobj(e: ref Sexp): ref Subject
+{
+ # <subj-obj>:: <principal> | <name> | <obj-hash> | <keyholder> | <subj-thresh> ;
+ case e.op() {
+ "name" or "hash" or "public-key" =>
+ name := parsecompound(e);
+ if(name == nil)
+ return nil;
+ if(name.names == nil)
+ return ref Subject.P(name.principal);
+ return ref Subject.N(name);
+
+ "object-hash" =>
+ e = onlyarg(e);
+ if(e == nil)
+ return nil;
+ hash := parsehash(e);
+ if(hash == nil)
+ return nil;
+ return ref Subject.O(hash);
+
+ "keyholder" =>
+ e = onlyarg(e);
+ if(e == nil)
+ return nil;
+ name := parsecompound(e);
+ if(name == nil)
+ return nil;
+ return ref Subject.KH(name);
+
+ "k-of-n" =>
+ el := e.args();
+ m := len el;
+ if(m < 2)
+ return nil;
+ k := intof(hd el);
+ n := intof(hd tl el);
+ if(k < 0 || n < 0 || k > n || n != m-2)
+ return nil;
+ el = tl tl el;
+ sl: list of ref Subject;
+ for(; el != nil; el = tl el){
+ o := parsesubjobj(hd el);
+ if(o == nil)
+ return nil;
+ sl = o :: sl;
+ }
+ return ref Subject.T(k, n, rev(sl));
+
+ * =>
+ return nil;
+ }
+}
+
+parsesig(e: ref Sexp): ref Signature
+{
+ # <signature>:: "(" "signature" <hash> <principal> <sig-val> ")"
+ # <sig-val>:: "(" <pub-sig-alg-id> <sig-params> ")"
+ # <pub-sig-alg-id>:: "rsa-pkcs1-md5" | "rsa-pkcs1-sha1" | "rsa-pkcs1" | "dsa-sha1" | <uri>
+ # <sig-params>:: <byte-string> | <s-expr>+
+
+ l := mustbe(e, "signature");
+ if(len l < 3)
+ return nil;
+ # signature hash key sig
+ hash := parsehash(hd l);
+ k := parseprincipal(hd tl l);
+ if(hash == nil || k == nil)
+ return nil;
+ val := hd tl tl l;
+ if(!val.islist()){ # not in grammar but examples paper uses it
+ sigalg: string;
+ if(k != nil)
+ sigalg = k.sigalg();
+ return ref Signature(hash, k, sigalg, (nil, val.asdata()) :: nil);
+ }
+ sigalg := val.op();
+ if(sigalg == nil)
+ return nil;
+ rl: list of (string, array of byte);
+ for(els := val.args(); els != nil; els = tl els){
+ g := hd els;
+ if(g.islist()){
+ arg := onlyarg(g);
+ if(arg == nil)
+ return nil;
+ rl = (g.op(), arg.asdata()) :: rl;
+ }else
+ rl = (nil, g.asdata()) :: rl;
+ }
+ return ref Signature(hash, k, sigalg, revt(rl));
+}
+
+parsecompound(e: ref Sexp): ref Name
+{
+ if(e == nil)
+ return nil;
+ case t := e.op() {
+ "name" =>
+ return parsename(e);
+ "public-key" or "hash" =>
+ k := parseprincipal(e);
+ if(k == nil)
+ return nil;
+ return ref Name(k, nil);
+ * =>
+ return nil;
+ }
+}
+
+parsename(e: ref Sexp): ref Name
+{
+ l := mustbe(e, "name");
+ if(l == nil)
+ return nil;
+ k: ref Key;
+ if((hd l).islist()){ # must be principal: pub key or hash of key
+ k = parseprincipal(hd l);
+ if(k == nil)
+ return nil;
+ l = tl l;
+ }
+ names: list of string;
+ for(; l != nil; l = tl l){
+ s := (hd l).astext();
+ if(s == nil)
+ return nil;
+ names = s :: names;
+ }
+ return ref Name(k, rev(names));
+}
+
+parseprincipal(e: ref Sexp): ref Key
+{
+ case e.op() {
+ "public-key" =>
+ return parsekey(e);
+ "hash" =>
+ hash := parsehash(e);
+ if(hash == nil)
+ return nil;
+ return ref Key(nil, nil, 0, hash.alg, hash);
+ * =>
+ return nil;
+ }
+}
+
+parsekey(e: ref Sexp): ref Key
+{
+ l := mustbe(e, "public-key");
+ if(l == nil)
+ return nil;
+ kind := (hd l).op();
+ (nf, fld) := sys->tokenize(kind, "-");
+ if(nf < 1)
+ return nil;
+ alg := hd fld;
+ if(nf > 1)
+ enc := hd tl fld; # signature hash encoding
+ if(nf > 2)
+ mha := hd tl tl fld; # signature hash algorithm
+ kha := "md5"; # could be sha1
+ kl := (hd l).args();
+ if(kl == nil)
+ return nil;
+ els: list of (string, ref IPint);
+ for(; kl != nil; kl = tl kl){
+ t := (hd kl).op();
+ a := onlyarg(hd kl).asdata();
+ if(a == nil)
+ return nil;
+ ip := IPint.bebytestoip(a);
+ if(ip == nil)
+ return nil;
+ els = (t, ip) :: els;
+ }
+ krp := ref Keyrep.PK(alg, "sdsi", els);
+ (pk, nbits) := krp.mkpk();
+ if(pk == nil){
+ sys->print("can't convert key\n");
+ return nil;
+ }
+#(ref Key(pk,nil,kha,nil)).hashed(kha); # TEST
+ return ref Key(pk, nil, nbits, kha, ref Hash(kha, nil));
+}
+
+parsehash(e: ref Sexp): ref Hash
+{
+ # "(" "hash" <hash-alg-name> <hash-value> <uris>? ")"
+ l := mustbe(e, "hash");
+ if(len l < 2)
+ return nil;
+ return ref Hash((hd l).astext(), (hd tl l).asdata());
+}
+
+parsevalid(e: ref Sexp): ref Valid
+{
+ l := mustbe(e, "valid");
+ if(l == nil)
+ return nil;
+ el: list of ref Sexp;
+ notbefore, notafter: string;
+ (el, l) = isita(l, "not-before");
+ if(el != nil && (notafter = ckdate((hd el).astext())) == nil)
+ return nil;
+ (el, l) = isita(l, "not-after");
+ if(el != nil && (notafter = ckdate((hd el).astext())) == nil)
+ return nil;
+ for(;;){
+ (el, l) = isita(l, "online");
+ if(el == nil)
+ break;
+ }
+ if(el != nil)
+ return nil;
+ return ref Valid(notbefore, notafter);
+}
+
+isnumeric(s: string): int
+{
+ for(i := 0; i < len s; i++)
+ if(!(s[i]>='0' && s[i]<='9'))
+ return 0;
+ return s != nil;
+}
+
+ckdate(s: string): string
+{
+ if(date2epoch(s) < 0) # TO DO: prefix/suffix tests
+ return nil;
+ return s;
+}
+
+Seqel.text(se: self ref Seqel): string
+{
+ pick r := se {
+ C =>
+ return r.c.text();
+ K =>
+ return r.k.text();
+ O =>
+ e := ref Sexp.List(ref Sexp.String("do",nil) :: ref Sexp.String(r.op,nil) :: r.args);
+ return e.text();
+ S =>
+ return r.sig.text();
+ E =>
+ return r.exp.text();
+ * =>
+ return "unsupported";
+ }
+}
+
+isita(l: list of ref Sexp, s: string): (list of ref Sexp, list of ref Sexp)
+{
+ if(l == nil)
+ return (nil, nil);
+ e := hd l;
+ if(e.islist() && e.op() == s)
+ return (e.args(), tl l);
+ return (nil, l);
+}
+
+intof(e: ref Sexp): int
+{
+ # int should be plenty; don't need big
+ pick s := e {
+ List =>
+ return -1;
+ Binary =>
+ if(len s.data > 4)
+ return -1;
+ v := 0;
+ for(i := 0; i < len s.data; i++)
+ v = (v<<8) | int s.data[i];
+ return v;
+ String =>
+ if(s.s == nil || !(s.s[0]>='0' && s.s[0]<='9'))
+ return -1;
+ return int s.s;
+ }
+}
+
+onlyarg(e: ref Sexp): ref Sexp
+{
+ l := e.args();
+ if(l == nil || tl l != nil)
+ return nil;
+ return hd l;
+}
+
+mustbe(e: ref Sexp, kind: string): list of ref Sexp
+{
+ if(e != nil && e.islist() && e.op() == kind)
+ return e.args();
+ return nil;
+}
+
+checksig(c: ref Cert, sig: ref Signature): string
+{
+ if(c.e == nil)
+ return "missing S-expression for certificate";
+ if(sig.key == nil)
+ return "missing key for signature";
+ if(sig.hash == nil)
+ return "missing hash for signature";
+ if(sig.sig == nil)
+ return "missing signature value";
+ pk := sig.key.pk;
+ if(pk == nil)
+ return "missing Keyring->PK for signature"; # TO DO
+#rsacomp((hd sig.sig).t1, sig.key);
+#sys->print("nbits= %d\n", sig.key.nbits);
+ (alg, enc, hashalg) := sig.algs();
+ if(alg == nil)
+ return "unspecified signature algorithm";
+ if(hashalg == nil)
+ hashalg = "md5"; # TO DO
+ if(enc == nil)
+ h := hashbytes(c.e.pack(), hashalg);
+ else if(enc == "pkcs" || enc == "pkcs1")
+ h = pkcs1_encode(hashalg, c.e.pack(), (sig.key.nbits+7)/8);
+ else
+ return "unknown encoding algorithm "+enc;
+ if(h == nil)
+ return "unknown hash algorithm "+hashalg;
+ ip := IPint.bebytestoip(h);
+ isig := sig2icert(sig, "sdsi", 0);
+ if(isig == nil)
+ return "couldn't convert SPKI signature to Keyring form";
+ if(!kr->verifym(pk, isig, ip))
+ return "signature does not match";
+ return nil;
+}
+
+hashexp(e: ref Sexp, alg: string): array of byte
+{
+ a := e.pack();
+#dump("inp a", a);
+ hash := hashbytes(a, alg);
+#dump(alg, hash);
+#sys->print("%s = |%s|\n", alg, base64->enc(hash));
+ return hash;
+}
+
+hashbytes(a: array of byte, alg: string): array of byte
+{
+ hash: array of byte;
+ case alg {
+ "md5" =>
+ hash = array[Keyring->MD5dlen] of byte;
+ kr->md5(a, len a, hash, nil);
+ "sha" or "sha1" =>
+ hash = array[Keyring->SHA1dlen] of byte;
+ kr->sha1(a, len a, hash, nil);
+ * =>
+ return nil;
+ }
+ return hash;
+}
+
+pre0(a: array of byte): array of byte
+{
+ for(i:=0; i<len a-1; i++)
+ if(a[i] != a[i+1] && (a[i] != byte 0 || (int a[i+1] & 16r80) != 0))
+ break;
+ if(i > 0)
+ a = a[i:];
+ if(len a < 1 || (int a[0] & 16r80) == 0)
+ return a;
+ b := array[len a + 1] of byte;
+ b[0] = byte 0;
+ b[1:] = a;
+ return b;
+}
+
+dump(s: string, a: array of byte)
+{
+ s = sys->sprint("%s [%d]: ", s, len a);
+ for(i := 0; i < len a; i++)
+ s += sys->sprint(" %.2ux", int a[i]);
+ sys->print("%s\n", s);
+}
+
+Signature.algs(sg: self ref Signature): (string, string, string)
+{
+ (nf, flds) := sys->tokenize(sg.sa, "-");
+ if(nf >= 3)
+ return (hd flds, hd tl flds, hd tl tl flds);
+ if(nf >= 2)
+ return (hd flds, nil, hd tl flds);
+ if(nf >= 1)
+ return (hd flds, nil, nil);
+ return (nil, nil, nil);
+}
+
+Signature.sexp(sg: self ref Signature): ref Sexp
+{
+ sv: ref Sexp;
+ if(len sg.sig != 1){
+ l: list of ref Sexp;
+ for(els := sg.sig; els != nil; els = tl els){
+ (op, val) := hd els;
+ if(op != nil)
+ l = ref Sexp.List(ref Sexp.String(op,nil) :: ref Sexp.Binary(val,nil) :: nil) :: l;
+ else
+ l = ref Sexp.Binary(val,nil) :: l;
+ }
+ sv = ref Sexp.List(rev(l));
+ }else
+ sv = ref Sexp.Binary((hd sg.sig).t1, nil);
+ if(sg.sa != nil)
+ sv = ref Sexp.List(ref Sexp.String(sg.sa,nil) :: sv :: nil);
+ return ref Sexp.List(ref Sexp.String("signature",nil) :: sg.hash.sexp() :: sg.key.sexp() ::
+ sv :: nil);
+}
+
+Signature.text(sg: self ref Signature): string
+{
+ if(sg == nil)
+ return nil;
+ return sg.sexp().text();
+}
+
+Hash.sexp(h: self ref Hash): ref Sexp
+{
+ return ref Sexp.List(ref Sexp.String("hash",nil) ::
+ ref Sexp.String(h.alg, nil) :: ref Sexp.Binary(h.hash,nil) :: nil);
+}
+
+Hash.text(h: self ref Hash): string
+{
+ return h.sexp().text();
+}
+
+Hash.eq(h1: self ref Hash, h2: ref Hash): int
+{
+ if(h1 == h2)
+ return 1;
+ if(h1 == nil || h2 == nil || h1.alg != h2.alg)
+ return 0;
+ return cmpbytes(h1.hash, h2.hash) == 0;
+}
+
+Valid.intersect(a: self Valid, b: Valid): (int, Valid)
+{
+ c: Valid;
+ if(a.notbefore < b.notbefore)
+ c.notbefore = b.notbefore;
+ else
+ c.notbefore = a.notbefore;
+ if(a.notafter == nil)
+ c.notafter = b.notafter;
+ else if(b.notafter == nil || a.notafter < b.notafter)
+ c.notafter = a.notafter;
+ else
+ c.notafter = b.notafter;
+ if(c.notbefore > c.notafter)
+ return (0, (nil, nil));
+ return (1, c);
+}
+
+Valid.text(a: self Valid): string
+{
+ na, nb: string;
+ if(a.notbefore != nil)
+ nb = " (not-before \""+a.notbefore+"\")";
+ if(a.notafter != nil)
+ na = " (not-after \""+a.notafter+"\")";
+ return sys->sprint("(valid%s%s)", nb, na);
+}
+
+Valid.sexp(a: self Valid): ref Sexp
+{
+ nb, na: ref Sexp;
+ if(a.notbefore != nil)
+ nb = ref Sexp.List(ref Sexp.String("not-before",nil) :: ref Sexp.String(a.notbefore,nil) :: nil);
+ if(a.notafter != nil)
+ na = ref Sexp.List(ref Sexp.String("not-after",nil) :: ref Sexp.String(a.notafter,nil) :: nil);
+ if(nb == nil && na == nil)
+ return nil;
+ return ref Sexp.List(ref Sexp.String("valid",nil) :: nb :: na :: nil);
+}
+
+Cert.text(c: self ref Cert): string
+{
+ if(c == nil)
+ return "nil";
+ v: string;
+ pick d := c {
+ A or KH or O =>
+ if(d.tag != nil)
+ v += " "+d.tag.text();
+ }
+ if(c.valid != nil)
+ v += " "+(*c.valid).text();
+ return sys->sprint("(cert (issuer %s) (subject %s)%s)", c.issuer.text(), c.subject.text(), v);
+}
+
+Cert.sexp(c: self ref Cert): ref Sexp
+{
+ if(c == nil)
+ return nil;
+ ds, tag: ref Sexp;
+ pick d := c {
+ N =>
+ A or KH or O =>
+ if(d.delegate)
+ ds = ref Sexp.List(ref Sexp.String("propagate",nil) :: nil);
+ tag = d.tag;
+ }
+ if(c.valid != nil)
+ vs := (*c.valid).sexp();
+ s := ref Sexp.List(ref Sexp.String("cert",nil) ::
+ ref Sexp.List(ref Sexp.String("issuer",nil) :: c.issuer.sexp() :: nil) ::
+ c.subject.sexp() ::
+ ds ::
+ tag ::
+ vs ::
+ nil);
+ return s;
+}
+
+Subject.principal(s: self ref Subject): ref Key
+{
+ pick r := s {
+ P =>
+ return r.key;
+ N =>
+ return r.name.principal;
+ * =>
+ return nil; # TO DO
+ }
+}
+
+Subject.text(s: self ref Subject): string
+{
+ pick r := s {
+ P =>
+ return r.key.text();
+ N =>
+ return r.name.text();
+ KH =>
+ return sys->sprint("(keyholder %s)", r.holder.text());
+ O =>
+ return sys->sprint("(object-hash %s)", r.hash.text());
+ T =>
+ return s.sexp().text(); # easy way out
+ }
+}
+
+Subject.sexp(s: self ref Subject): ref Sexp
+{
+ e: ref Sexp;
+ pick r := s {
+ P =>
+ e = r.key.sexp();
+ N =>
+ e = r.name.sexp();
+ KH =>
+ e = ref Sexp.List(ref Sexp.String("keyholder",nil) :: r.holder.sexp() :: nil);
+ O =>
+ e = ref Sexp.List(ref Sexp.String("object-hash",nil) :: r.hash.sexp() :: nil);
+ T =>
+ sl: list of ref Sexp;
+ for(subs := r.subs; subs != nil; subs = tl subs)
+ sl = (hd subs).sexp() :: sl;
+ e = ref Sexp.List(ref Sexp.String("k-of-n",nil) ::
+ ref Sexp.String(string r.k,nil) :: ref Sexp.String(string r.n,nil) :: rev(sl));
+ * =>
+ return nil;
+ }
+ return ref Sexp.List(ref Sexp.String("subject",nil) :: e :: nil);
+}
+
+Subject.eq(s1: self ref Subject, s2: ref Subject): int
+{
+ if(s1 == s2)
+ return 1;
+ if(s1 == nil || s2 == nil || tagof s1 != tagof s2)
+ return 0;
+ pick r1 := s1 {
+ P =>
+ pick r2 := s2 {
+ P =>
+ return r1.key.eq(r2.key);
+ }
+ N =>
+ pick r2 := s2 {
+ N =>
+ return r1.name.eq(r2.name);
+ }
+ O =>
+ pick r2 := s2 {
+ O =>
+ return r1.hash.eq(r2.hash);
+ }
+ KH =>
+ pick r2 := s2 {
+ KH =>
+ return r1.holder.eq(r2.holder);
+ }
+ T =>
+ pick r2 := s2 {
+ T =>
+ if(r1.k != r2.k || r1.n != r2.n)
+ return 0;
+ l2 := r2.subs;
+ for(l1 := r1.subs; l1 != nil; l1 = tl l1){
+ if(l2 == nil || !(hd l1).eq(hd l2))
+ return 0;
+ l2 = tl l2;
+ }
+ }
+ }
+ return 0;
+}
+
+Name.isprincipal(n: self ref Name): int
+{
+ return n.names == nil;
+}
+
+Name.local(n: self ref Name): ref Name
+{
+ if(n.names == nil || tl n.names == nil)
+ return n;
+ return ref Name(n.principal, hd n.names :: nil);
+}
+
+Name.islocal(n: self ref Name): int
+{
+ return n.names == nil || tl n.names == nil;
+}
+
+Name.isprefix(n1: self ref Name, n2: ref Name): int
+{
+ if(n1 == nil)
+ return n2 == nil;
+ if(!n1.principal.eq(n2.principal))
+ return 0;
+ s1 := n1.names;
+ s2 := n2.names;
+ for(; s1 != nil; s1 = tl s1){
+ if(s2 == nil || hd s2 != hd s1)
+ return 0;
+ s2 = tl s2;
+ }
+ return 1;
+}
+
+Name.text(n: self ref Name): string
+{
+ if(n.principal == nil)
+ s := "$self";
+ else
+ s = n.principal.text();
+ for(nl := n.names; nl != nil; nl = tl nl)
+ s += " " + hd nl;
+ return "(name "+s+")";
+}
+
+Name.sexp(n: self ref Name): ref Sexp
+{
+ ns: list of ref Sexp;
+
+ if(n.principal != nil)
+ is := n.principal.sexp();
+ else
+ is = ref Sexp.String("$self",nil);
+ if(n.names == nil)
+ return is;
+ for(nl := n.names; nl != nil; nl = tl nl)
+ ns = ref Sexp.String(hd nl,nil) :: ns;
+ return ref Sexp.List(ref Sexp.String("name",nil) :: is :: rev(ns));
+}
+
+Name.eq(a: self ref Name, b: ref Name): int
+{
+ if(a == b)
+ return 1;
+ if(a == nil || b == nil)
+ return 0;
+ if(!a.principal.eq(b.principal))
+ return 0;
+ nb := b.names;
+ for(na := a.names; na != nil; na = tl na){
+ if(nb == nil || hd nb != hd na)
+ return 0;
+ nb = tl nb;
+ }
+ return nb == nil;
+}
+
+Key.hashed(key: self ref Key, alg: string): array of byte
+{
+ if(key.hash != nil && key.halg == alg && key.hash.hash != nil)
+ return key.hash.hash;
+ krp := Keyrep.pk(key.pk);
+ if(krp == nil)
+ return nil;
+ n := krp.getb("n");
+ e := krp.getb("e");
+ if(n == nil || e == nil)
+ return nil;
+ ex := ref Sexp.List(
+ ref Sexp.String("public-key", nil) ::
+ ref Sexp.List(
+ ref Sexp.String("rsa-pkcs1-"+alg, nil) ::
+ ref Sexp.List(ref Sexp.String("e", nil) :: ref Sexp.Binary(e, nil) :: nil) ::
+ ref Sexp.List(ref Sexp.String("n", nil) :: ref Sexp.Binary(n, nil) :: nil)
+ :: nil)
+ :: nil);
+# sys->print("=> %q %s\n", hd tl tl flds, ex.text());
+ hash := hashexp(ex, alg);
+ if((key.hash == nil || key.hash.hash == nil) && (key.halg == alg || key.halg == nil)){
+ key.halg = alg;
+ key.hash = ref Hash(alg, hash);
+ }
+ return hash;
+}
+
+Key.sigalg(k: self ref Key): string
+{
+ if(k.pk == nil || k.pk.sa == nil)
+ return nil;
+ halg := "";
+ if(k.halg != nil)
+ halg = "-"+k.halg;
+ n := k.pk.sa.name;
+ if(n == "rsa" || n == "dsa")
+ return n+"-pkcs1"+halg;
+ return n+halg;
+}
+
+Key.text(k: self ref Key): string
+{
+ e := k.sexp();
+ if(e == nil)
+ return sys->sprint("(public-key unknown)");
+ return e.text();
+}
+
+Key.sexp(k: self ref Key): ref Sexp
+{
+ if(k.hash != nil && k.hash.hash != nil)
+ return k.hash.sexp();
+ krp := Keyrep.pk(k.pk);
+ if(krp == nil)
+ return nil;
+ rl: list of ref Sexp;
+ for(el := krp.els; el != nil; el = tl el){
+ (n, v) := hd el;
+ a := pre0(v.iptobebytes());
+ rl = ref Sexp.List(ref Sexp.String(n,nil) :: ref Sexp.Binary(a,nil) :: nil) :: rl;
+ }
+ return ref Sexp.List(ref Sexp.String("public-key", nil) ::
+ ref Sexp.List(ref Sexp.String(k.sigalg(),nil) :: rev(rl)) :: nil);
+}
+
+Key.eq(k1: self ref Key, k2: ref Key): int
+{
+ if(k1 == k2)
+ return 1;
+ if(k1 == nil || k2 == nil)
+ return 0;
+ if(k1.hash != nil && k2.hash != nil && k1.hash.eq(k2.hash))
+ return 1;
+ if(k1.pk != nil && k2.pk != nil)
+ return kr->pktostr(k1.pk) == kr->pktostr(k2.pk); # TO DO
+ return 0;
+}
+
+dec(s: string, i: int, l: int): (int, int)
+{
+ l += i;
+ n := 0;
+ for(; i < l; i++){
+ c := s[i];
+ if(!(c >= '0' && c <= '9'))
+ return (-1, 0);
+ n = n*10 + (c-'0');
+ }
+ return (n, l);
+}
+
+# TO DO: any valid prefix of a date
+
+date2epoch(t: string): int
+{
+ if(len t != 19)
+ return -1;
+ tm := ref Daytime->Tm;
+ i: int;
+ (tm.year, i) = dec(t, 0, 4);
+ if(tm.year < 0 || t[i++] != '-')
+ return -1;
+ tm.year -= 1900;
+ (tm.mon, i) = dec(t, i, 2);
+ if(tm.mon <= 0 || t[i++] != '-' || tm.mon > 12)
+ return -1;
+ tm.mon--;
+ (tm.mday, i) = dec(t, i, 2);
+ if(tm.mday <= 0 || t[i++] != '_' || tm.mday >= 31)
+ return -1;
+ (tm.hour, i) = dec(t, i, 2);
+ if(tm.hour < 0 || t[i++] != ':' || tm.hour > 23)
+ return -1;
+ (tm.min, i) = dec(t, i, 2);
+ if(tm.min < 0 || t[i++] != ':' || tm.min > 59)
+ return -1;
+ (tm.sec, i) = dec(t, i, 2);
+ if(tm.sec < 0 || tm.sec > 59) # leap second(s)?
+ return -1;
+ tm.tzoff = 0;
+ return daytime->tm2epoch(tm);
+}
+
+epoch2date(t: int): string
+{
+ tm := daytime->gmt(t);
+ return sys->sprint("%.4d-%.2d-%.2d_%.2d:%.2d:%.2d",
+ tm.year+1900, tm.mon+1, tm.mday, tm.hour, tm.min, tm.sec);
+}
+
+# could use a delta-time function
+
+time2secs(s: string): int
+{
+ if(len s != 8) # HH:MM:SS
+ return -1;
+ hh, mm, ss, i: int;
+ (hh, i) = dec(s, 0, 2);
+ if(hh < 0 || hh > 24 || s[i++] != ':')
+ return -1;
+ (mm, i) = dec(s, i, 2);
+ if(mm < 0 || mm > 59 || s[i++] != ':')
+ return -1;
+ (ss, i) = dec(s, i, 2);
+ if(ss < 0 || ss > 59)
+ return -1;
+ return hh*3600 + mm*60 + ss;
+}
+
+secs2time(t: int): string
+{
+ hh := (t/60*60)%24;
+ mm := (t%3600)/60;
+ ss := t%60;
+ return sys->sprint("%.2d:%.2d:%.2d", hh, mm, ss);
+}
+
+#
+# auth tag intersection as defined by
+# ``A Formal Semantics for SPKI'', Jon Howell, David Kotz
+# its proof cases are marked by the roman numerals (I) ... (X)
+# with contributions from
+# ``A Note on SPKI's Authorisation Syntax'', Olav Bandmann, Mads Dam
+# its AIntersect cases are marked by arabic numerals
+
+maketag(e: ref Sexp): ref Sexp
+{
+ if(e == nil)
+ return e;
+ return remake(e.copy());
+}
+
+tagimplies(t1: ref Sexp, t2: ref Sexp): int
+{
+ e := tagintersect(t1, t2);
+ if(e == nil)
+ return 0;
+ return e.eq(t2);
+}
+
+Anull, Astar, Abytes, Aprefix, Asuffix, Arange, Alist, Aset: con iota;
+
+tagindex(s: ref Sexp): int
+{
+ if(s == nil)
+ return Anull;
+ pick r := s {
+ String =>
+ return Abytes;
+ Binary =>
+ return Abytes;
+ List =>
+ if(r.op() == "*"){
+ if(tl r.l == nil)
+ return Astar;
+ case (hd tl r.l).astext() {
+ "prefix" => return Aprefix;
+ "suffix" => return Asuffix;
+ "range" => return Arange;
+ "set" => return Aset;
+ * => return Anull; # unknown
+ }
+ }
+ return Alist;
+ * =>
+ return Anull; # not reached
+ }
+}
+
+#
+# 1 (*) x r = r
+# 2 r x (*) = r
+# 3 ⊥ x r = ⊥
+# 4 r x ⊥ = ⊥
+# 5 a x a = a (also a x a' = ⊥)
+# 6 a x b = a if a ∈ Val(b)
+# 7 a x b = ⊥ if a ∉ Val(b)
+# 8 a x (a' r1 ... rn)) = ⊥
+# 9 a x (* set r1 ... ri = a ... rn) = a
+# 10 a x (* set r1 ... ri = b ... rn) = a, if a ∈ Val(b)
+# 11 a x (* set r1 ... ri ... rn)) = ⊥, if neither of above two cases applies
+# 12 b x b' = b ∩ b'
+# 13 b x (a r1 ... rn) = ⊥
+# 14 b x (* set r1 ... rn) = (*set (b x r'[1]) ... (b x r'[m])), for atomic elements in r1, ..., rn
+# 15 (a r1 ... rn) x (a r'[1] ... r'[n] r'[n+1] ... r'[m]) = (a (r1 x r'[1]) ... (rn x r'[n]) r'[n+1] ... r'[m]) for m >= n
+# 16 (a r1 ... rn) x (a' r'[1] ... r'[m]) = ⊥
+# 17 (a r1 ... rn) x (* set r'[1] ... r'[i] ... r'[k]) = (a r1 ... rn) x r'[i], if r'[i] has tag a
+# 18 (a r1 ... rn) x (* set r'[1] ... r'[m]) = ⊥, if no r'[i] has tag a
+# 19 (* set r1 .. rn) x r, where r is (* set r1'[1] ... r'[m]) = (* set (r1 x r) (r2 x r) ... (rn x r))
+#
+# nil is used instead of ⊥, which works provided an incoming credential
+# with no tag has implicit tag (*)
+#
+
+# put operands in order of proof in FSS
+
+swaptag := array[] of {
+ (Abytes<<4) | Alist => (Alist<<4) | Abytes, # (IV)
+
+ (Abytes<<4) | Aset => (Aset<<4) | Abytes, # (VI)
+ (Aprefix<<4) | Aset => (Aset<<4) | Aprefix, # (VI)
+ (Arange<<4) | Aset => (Aset<<4) | Arange, # (VI)
+ (Alist<<4) | Aset => (Aset<<4) | Alist, # (VI)
+ (Asuffix<<4) | Aset => (Aset<<4) | Asuffix, # (VI) extension
+
+ (Aprefix<<4) | Abytes => (Abytes<<4) | Aprefix, # (VII)
+ (Arange<<4) | Abytes => (Abytes<<4) | Arange, # (VII)
+ (Asuffix<<4) | Abytes => (Abytes<<4) | Asuffix, # (VII) extension
+
+ * => 0,
+};
+
+tagintersect(t1, t2: ref Sexp): ref Sexp
+{
+ if(t1 == t2)
+ return t1;
+ if(t1 == nil || t2 == nil) # 3, 4; case (I)
+ return nil;
+ x1 := tagindex(t1);
+ x2 := tagindex(t2);
+ if(debug){
+ sys->print("%#q -> %d\n", t1.text(), x1);
+ sys->print("%#q -> %d\n", t2.text(), x2);
+ }
+ if(x1 == Astar) # 1; case (II)
+ return t2;
+ if(x2 == Astar) # 2; case (II)
+ return t1;
+ code := (x1 << 4) | x2; # (a[x]<<4) | a[y] in FSS
+ # reorder symmetric cases
+ if(code < len swaptag && swaptag[code]){
+ (t1, t2) = (t2, t1);
+ (x1, x2) = (x2, x1);
+ code = swaptag[code];
+ }
+ case code {
+ (Abytes<<4) | Abytes => # case (III); 5
+ if(t1.eq(t2))
+ return t1;
+
+ (Alist<<4) | Abytes => # case (IV)
+ return nil;
+
+ (Alist<<4) | Alist => # case (V); 15-16
+ if(t1.op() != t2.op())
+ return nil;
+ l1 := t1.els();
+ l2 := t2.els();
+ if(len l1 > len l2){
+ (t1, t2) = (t2, t1);
+ (l1, l2) = (l2, l1);
+ }
+ rl: list of ref Sexp;
+ for(; l1 != nil; l1 = tl l1){
+ x := tagintersect(hd l1, hd l2);
+ if(x == nil)
+ return nil;
+ rl = x :: rl;
+ l2 = tl l2;
+ }
+ for(; l2 != nil; l2 = tl l2)
+ rl = hd l2 :: rl;
+ return ref Sexp.List(rev(rl));
+
+ (Aset<<4) | Abytes => # case (VI); 9-11
+ for(el := setof(t1); el != nil; el = tl el){
+ e := hd el;
+ case tagindex(e) {
+ Abytes =>
+ if(e.eq(t2))
+ return t2;
+ Astar =>
+ return t2;
+ Arange =>
+ if(inrange(t2, e))
+ return t2;
+ Aprefix =>
+ if(isprefix(e, t2))
+ return t2;
+ Asuffix =>
+ if(issuffix(e, t2))
+ return t2;
+ }
+ }
+ # otherwise null
+
+ (Aset<<4) | Alist => # case (VI); 17-18
+ o := t2.op();
+ for(el := setof(t1); el != nil; el = tl el){
+ e := hd el;
+ if(e.islist() && e.op() == o || tagindex(e) == Astar)
+ return tagintersect(e, t2);
+ }
+ # otherwise null
+
+ (Aset<<4) | Aprefix or # case (VI); 14
+ (Aset<<4) | Arange or # case (VI); 14
+ # for Aprefix or Arange, could restrict els of t1 to atomic elements (sets A and B)
+ # here, following rule 14, but we'll let tagintersect sort it out in the general case below
+ (Aset<<4) | Aset => # case (VI); 19
+ rl: list of ref Sexp;
+ for(el := setof(t1); el != nil; el = tl el){
+ x := tagintersect(hd el, t2);
+ if(x != nil)
+ rl = x :: rl;
+ }
+ return mkset(rev(rl)); # null if empty
+
+ (Abytes<<4) | Aprefix => # case (VII)
+ if(isprefix(t2, t1))
+ return t1;
+ (Abytes<<4) | Arange => # case (VII)
+ if(inrange(t1, t2))
+ return t1;
+ (Abytes<<4) | Asuffix => # case (VII)
+ if(issuffix(t2, t1))
+ return t1;
+
+ (Aprefix<<4) | Aprefix => # case (VIII)
+ p1 := prefixof(t1);
+ p2 := prefixof(t2);
+ if(p1 == nil || p2 == nil)
+ return nil;
+ if(p1.nb < p2.nb){
+ (t1, t2) = (t2, t1);
+ (p1, p2) = (p2, p1);
+ }
+ if((*p2).isprefix(*p1))
+ return t1; # t1 is longer, thus more specific
+
+ (Asuffix<<4) | Asuffix => # case (VIII) extension
+ p1 := suffixof(t1);
+ p2 := suffixof(t2);
+ if(p1 == nil || p2 == nil)
+ return nil;
+ if(p1.nb < p2.nb){
+ (t1, t2) = (t2, t1);
+ (p1, p2) = (p2, p1);
+ }
+ if((*p2).issuffix(*p1))
+ return t1; # t1 is longer, thus more specific
+
+ (Arange<<4) | Aprefix => # case (IX)
+ return nil;
+ (Arange<<4) | Asuffix => # case (IX)
+ return nil;
+ (Arange<<4) | Arange => # case (IX)
+ v1 := rangeof(t1);
+ v2 := rangeof(t2);
+ if(v1 == nil || v2 == nil)
+ return nil; # invalid
+ (ok, v) := (*v1).intersect(*v2);
+ if(ok)
+ return mkrange(v);
+
+ (Alist<<4) | Arange or
+ (Alist<<4) | Aprefix => # case (X)
+ ;
+ }
+ return nil; # case (X), and default
+}
+
+isprefix(pat, subj: ref Sexp): int
+{
+ p := prefixof(pat);
+ if(p == nil)
+ return 0;
+ return (*p).isprefix(valof(subj));
+}
+
+issuffix(pat, subj: ref Sexp): int
+{
+ p := suffixof(pat);
+ if(p == nil)
+ return 0;
+ return (*p).issuffix(valof(subj));
+}
+
+inrange(t1, t2: ref Sexp): int
+{
+ v := valof(t1);
+ r := rangeof(t2);
+ if(r == nil)
+ return 0;
+ if(0)
+ sys->print("%s :: %s\n", v.text(), (*r).text());
+ pass := 0;
+ if(r.ge >= 0){
+ c := v.cmp(r.lb, r.order);
+ if(c < 0 || c == 0 && !r.ge)
+ return 0;
+ pass = 1;
+ }
+ if(r.le >= 0){
+ c := v.cmp(r.ub, r.order);
+ if(c > 0 || c == 0 && !r.le)
+ return 0;
+ pass = 1;
+ }
+ return pass;
+}
+
+addval(l: list of ref Sexp, s: string, v: Val): list of ref Sexp
+{
+ e: ref Sexp;
+ if(v.a != nil)
+ e = ref Sexp.Binary(v.a, v.hint);
+ else
+ e = ref Sexp.String(v.s, v.hint);
+ return ref Sexp.String(s, nil) :: e :: l;
+}
+
+mkrange(r: Vrange): ref Sexp
+{
+ l: list of ref Sexp;
+ if(r.le > 0)
+ l = addval(l, "le", r.ub);
+ else if(r.le == 0)
+ l = addval(l, "l", r.ub);
+ if(r.ge > 0)
+ l = addval(l, "ge", r.lb);
+ else if(r.ge == 0)
+ l = addval(l, "g", r.lb);
+ return ref Sexp.List(ref Sexp.String("*",nil) :: ref Sexp.String("range",nil) :: ref Sexp.String(r.otext(), nil) :: l);
+}
+
+valof(s: ref Sexp): Val
+{
+ pick r := s {
+ String =>
+ return Val.mk(r.s, nil, r.hint);
+ Binary =>
+ return Val.mk(nil, r.data, r.hint);
+ * =>
+ return Val.mk(nil, nil, nil); # can't happen
+ }
+}
+
+starop(s: ref Sexp, op: string): (string, list of ref Sexp)
+{
+ if(s == nil)
+ return (nil, nil);
+ pick r := s {
+ List =>
+ if(r.op() == "*" && tl r.l != nil){
+ pick t := hd tl r.l {
+ String =>
+ if(op != nil && t.s != op)
+ return (nil, nil);
+ return (t.s, tl tl r.l);
+ }
+ }
+ }
+ return (nil, nil);
+}
+
+isset(s: ref Sexp): (int, list of ref Sexp)
+{
+ (op, l) := starop(s, "set");
+ if(op != nil)
+ return (1, l);
+ return (0, l);
+}
+
+setof(s: ref Sexp): list of ref Sexp
+{
+ return starop(s, "set").t1;
+}
+
+prefixof(s: ref Sexp): ref Val
+{
+ return substrof(s, "prefix");
+}
+
+suffixof(s: ref Sexp): ref Val
+{
+ return substrof(s, "suffix");
+}
+
+substrof(s: ref Sexp, kind: string): ref Val
+{
+ l := starop(s, kind).t1;
+ if(l == nil)
+ return nil;
+ pick x := hd l{
+ String =>
+ return ref Val.mk(x.s, nil, x.hint);
+ Binary =>
+ return ref Val.mk(nil, x.data, x.hint);
+ }
+ return nil;
+}
+
+rangeof(s: ref Sexp): ref Vrange
+{
+ l := starop(s, "range").t1;
+ if(l == nil)
+ return nil;
+ ord: int;
+ case (hd l).astext() {
+ "alpha" => ord = Alpha;
+ "numeric" => ord = Numeric;
+ "binary" => ord = Binary;
+ "time" => ord = Time; # hh:mm:ss
+ "date" => ord = Date; # full date format
+ * => return nil;
+ }
+ l = tl l;
+ lb, ub: Val;
+ lt := -1;
+ gt := -1;
+ while(l != nil){
+ if(tl l == nil)
+ return nil;
+ o := (hd l).astext();
+ v: Val;
+ l = tl l;
+ if(l == nil)
+ return nil;
+ pick t := hd l {
+ String =>
+ v = Val.mk(t.s, nil, t.hint);
+ Binary =>
+ v = Val.mk(nil, t.data, t.hint);
+ * =>
+ return nil;
+ }
+ l = tl l;
+ case o {
+ "g" or "ge" =>
+ if(gt >= 0 || lt >= 0)
+ return nil;
+ gt = o == "ge";
+ lb = v;
+ "l" or "le" =>
+ if(lt >= 0)
+ return nil;
+ lt = o == "le";
+ ub = v;
+ * =>
+ return nil;
+ }
+ }
+ if(gt < 0 && lt < 0)
+ return nil;
+ return ref Vrange(ord, gt, lb, lt, ub);
+}
+
+Els: adt {
+ a: array of ref Sexp;
+ n: int;
+
+ add: fn(el: self ref Els, s: ref Sexp);
+ els: fn(el: self ref Els): array of ref Sexp;
+};
+
+Els.add(el: self ref Els, s: ref Sexp)
+{
+ if(el.n >= len el.a){
+ t := array[el.n+10] of ref Sexp;
+ if(el.a != nil)
+ t[0:] = el.a;
+ el.a = t;
+ }
+ el.a[el.n++] = s;
+}
+
+Els.els(el: self ref Els): array of ref Sexp
+{
+ if(el.n == 0)
+ return nil;
+ return el.a[0:el.n];
+}
+
+remake(s: ref Sexp): ref Sexp
+{
+ if(s == nil)
+ return nil;
+ pick r := s {
+ List =>
+ (is, mem) := isset(r);
+ if(is){
+ el := ref Els(array[10] of ref Sexp, 0);
+ members(mem, el);
+ if(debug)
+ sys->print("-- %#q\n", s.text());
+ y := mkset0(tolist(el.els()));
+ if(debug){
+ if(y == nil)
+ sys->print("\t=> EMPTY\n");
+ else
+ sys->print("\t=> %#q\n", y.text());
+ }
+ return y;
+ }
+ rl: list of ref Sexp;
+ for(l := r.l; l != nil; l = tl l){
+ e := remake(hd l);
+ if(e != hd l){
+ # structure changed, remake current node's list
+ for(il := r.l; il != l; il = tl il)
+ rl = hd il :: rl;
+ rl = e :: rl;
+ while((l = tl l) != nil)
+ rl = remake(hd l) :: rl;
+ return ref Sexp.List(rev(rl));
+ }
+ }
+ # unchanged
+ }
+ return s;
+}
+
+members(l: list of ref Sexp, el: ref Els)
+{
+ for(; l != nil; l = tl l){
+ e := hd l;
+ (is, mem) := isset(e);
+ if(is)
+ members(mem, el);
+ else
+ el.add(remake(e));
+ }
+}
+
+mkset(sl: list of ref Sexp): ref Sexp
+{
+ rl: list of ref Sexp;
+ for(l := sl; l != nil; l = tl l){
+ (is, mem) := isset(hd l);
+ if(is){
+ for(; mem != nil; mem = tl mem)
+ rl = hd mem :: rl;
+ }else
+ rl = hd l :: rl;
+ }
+ return mkset0(rev(rl));
+}
+
+mkset0(mem: list of ref Sexp): ref Sexp
+{
+ if(mem == nil)
+ return nil;
+ return ref Sexp.List(ref Sexp.String("*", nil) :: ref Sexp.String("set", nil) :: mem);
+}
+
+factor(a: array of ref Sexp): ref Sexp
+{
+ mergesort(a, array[len a] of ref Sexp);
+ for(i := 0; i < len a; i++){
+ case tagindex(a[i]) {
+ Astar =>
+ return a[i];
+ Alist =>
+ k := i+1;
+ if(k >= len a)
+ break;
+ if(a[k].islist() && (op := a[i].op()) != "*" && op == a[k].op()){
+ # ensure tag uniqueness within a set by: (* set (a L1) (a L2)) => (a (* set L1 L2))
+ ml := a[i].els();
+ n0 := hd ml;
+ rl := ref Sexp.List(tl ml) :: ref Sexp.String("set", nil) :: ref Sexp.String("*", nil) :: nil; # reversed
+ # gather tails of adjacent lists with op matching this one
+ for(; k < len a && a[k].islist() && a[k].op() == op; k++){
+ ml = tl a[k].els();
+ if(len ml == 1)
+ rl = hd ml :: rl;
+ else
+ rl = ref Sexp.List(ml) :: rl;
+ }
+ a[i] = ref Sexp.List(n0 :: remake(ref Sexp.List(rev(rl))) :: nil);
+ sys->print("common: %q [%d -> %d] -> %q\n", op, i, k-1, a[i].text());
+ if(k < len a)
+ a[i+1:] = a[k:];
+ a = a[0:i+1+(len a-k)];
+ }
+ }
+ }
+ return mkset0(tolist(a));
+}
+
+tolist(a: array of ref Sexp): list of ref Sexp
+{
+ l: list of ref Sexp;
+ for(i := len a; --i >= 0;)
+ l = a[i] :: l;
+ return l;
+}
+
+mergesort(a, b: array of ref Sexp)
+{
+ r := len a;
+ if(r > 1) {
+ m := (r-1)/2 + 1;
+ mergesort(a[0:m], b[0:m]);
+ mergesort(a[m:], b[m:]);
+ b[0:] = a;
+ for ((i, j, k) := (0, m, 0); i < m && j < r; k++) {
+ if(b[i].islist() || !b[j].islist() && b[i].op() > b[j].op()) # a list is greater than any atom
+ a[k] = b[j++];
+ else
+ a[k] = b[i++];
+ }
+ if(i < m)
+ a[k:] = b[i:m];
+ else if (j < r)
+ a[k:] = b[j:r];
+ }
+}
+
+Val: adt {
+ # only one of s or a is not nil
+ s: string;
+ a: array of byte;
+ hint: string;
+ nb: int; # size in bytes
+
+ mk: fn(s: string, a: array of byte, h: string): Val;
+ cmp: fn(a: self Val, b: Val, order: int): int;
+ isfloat: fn(a: self Val): int;
+ isprefix: fn(a: self Val, b: Val): int;
+ issuffix: fn(a: self Val, b: Val): int;
+ bytes: fn(a: self Val): array of byte;
+ text: fn(v: self Val): string;
+};
+
+Val.mk(s: string, a: array of byte, h: string): Val
+{
+ if(a != nil)
+ nb := len a;
+ else
+ nb = utflen(s);
+ return Val(s, a, h, nb);
+}
+
+Val.bytes(v: self Val): array of byte
+{
+ if(v.a != nil)
+ return v.a;
+ return array of byte v.s;
+}
+
+Val.isfloat(v: self Val): int
+{
+ if(v.a != nil)
+ return 0;
+ for(i := 0; i < len v.s; i++)
+ if(v.s[i] == '.')
+ return 1;
+ return 0;
+}
+
+Val.isprefix(a: self Val, b: Val): int
+{
+ if(a.hint != b.hint)
+ return 0;
+ # normalise to bytes
+ va := a.bytes();
+ vb := b.bytes();
+ for(i := 0; i < len va; i++)
+ if(i >= len vb || va[i] != vb[i])
+ return 0;
+ return 1;
+}
+
+Val.issuffix(a: self Val, b: Val): int
+{
+ if(a.hint != b.hint)
+ return 0;
+ # normalise to bytes
+ va := a.bytes();
+ vb := b.bytes();
+ for(i := 0; i < len va; i++)
+ if(i >= len vb || va[len va-i-1] != vb[len vb-i-1])
+ return 0;
+ return 1;
+}
+
+Val.cmp(a: self Val, b: Val, order: int): int
+{
+ if(a.hint != b.hint)
+ return -2;
+ case order {
+ Numeric => # TO DO: change this to use string comparisons
+ if(a.a != nil || b.a != nil)
+ return -2;
+ if(a.isfloat() || b.isfloat()){
+ fa := real a.s;
+ fb := real b.s;
+ if(fa < fb)
+ return -1;
+ if(fa > fb)
+ return 1;
+ return 0;
+ }
+ ia := big a.s;
+ ib := big b.s;
+ if(ia < ib)
+ return -1;
+ if(ia > ib)
+ return 1;
+ return 0;
+ Binary => # right-justified, unsigned binary values
+ av := a.a;
+ if(av == nil)
+ av = array of byte a.s;
+ bv := b.a;
+ if(bv == nil)
+ bv = array of byte b.s;
+ while(len av > len bv){
+ if(av[0] != byte 0)
+ return 1;
+ av = av[1:];
+ }
+ while(len bv > len av){
+ if(bv[0] != byte 0)
+ return -1;
+ bv = bv[1:];
+ }
+ return cmpbytes(av, bv);
+ }
+ # otherwise compare as strings
+ if(a.a != nil){
+ if(b.s != nil)
+ return cmpbytes(a.a, array of byte b.s);
+ return cmpbytes(a.a, b.a);
+ }
+ if(b.a != nil)
+ return cmpbytes(array of byte a.s, b.a);
+ if(a.s < b.s)
+ return -1;
+ if(a.s > b.s)
+ return 1;
+ return 0;
+}
+
+Val.text(v: self Val): string
+{
+ s: string;
+ if(v.hint != nil)
+ s = sys->sprint("[%s]", v.hint);
+ if(v.s != nil)
+ return s+v.s;
+ if(v.a != nil)
+ return sys->sprint("%s#%s#", s, base16->enc(v.a));
+ return sys->sprint("%s\"\"", s);
+}
+
+cmpbytes(a, b: array of byte): int
+{
+ n := len a;
+ if(n > len b)
+ n = len b;
+ for(i := 0; i < n; i++)
+ if(a[i] != b[i])
+ return int a[i] - int b[i];
+ return len a - len b;
+}
+
+Vrange: adt {
+ order: int;
+ ge: int;
+ lb: Val;
+ le: int;
+ ub: Val;
+
+ text: fn(v: self Vrange): string;
+ otext: fn(v: self Vrange): string;
+ intersect: fn(a: self Vrange, b: Vrange): (int, Vrange);
+};
+
+Alpha, Numeric, Time, Binary, Date: con iota; # Vrange.order
+
+Vrange.otext(r: self Vrange): string
+{
+ case r.order {
+ Alpha => return "alpha";
+ Numeric => return "numeric";
+ Time => return "time";
+ Binary => return "binary";
+ Date => return "date";
+ * => return sys->sprint("O%d", r.order);
+ }
+}
+
+Vrange.text(v: self Vrange): string
+{
+ s := sys->sprint("(* range %s", v.otext());
+ if(v.ge >= 0){
+ s += " g";
+ if(v.ge)
+ s += "e";
+ s += " "+v.lb.text();
+ }
+ if(v.le >= 0){
+ s += " l";
+ if(v.le)
+ s += "e";
+ s += " "+v.ub.text();
+ }
+ return s+")";
+}
+
+Vrange.intersect(v1: self Vrange, v2: Vrange): (int, Vrange)
+{
+ if(v1.order != v2.order)
+ return (0, v1); # incommensurate
+ v := v1;
+ if(v.ge < 0 || v2.ge >= 0 && v2.lb.cmp(v.lb, v.order) > 0)
+ v.lb = v2.lb;
+ if(v.le < 0 || v2.le >= 0 && v2.ub.cmp(v.ub, v.order) < 0)
+ v.ub = v2.ub;
+ if(v.lb.hint != v.ub.hint)
+ return (0, v1); # incommensurate
+ v.ge &= v2.ge;
+ v.le &= v2.le;
+ c := v.lb.cmp(v.ub, v.order);
+ if(c > 0 || c == 0 && !(v.ge && v.le))
+ return (0, v1); # empty range
+ return (1, v);
+}
+
+utflen(s: string): int
+{
+ return len array of byte s;
+}
+
+append[T](l1, l2: list of T): list of T
+{
+ rl1: list of T;
+ for(; l1 != nil; l1 = tl l1)
+ rl1 = hd l1 :: rl1;
+ for(; rl1 != nil; rl1 = tl rl1)
+ l2 = hd rl1 :: l2;
+ return l2;
+}
+
+rev[T](l: list of T): list of T
+{
+ rl: list of T;
+ for(; l != nil; l = tl l)
+ rl = hd l :: rl;
+ return rl;
+}
+
+revt[S,T](l: list of (S,T)): list of (S,T)
+{
+ rl: list of (S,T);
+ for(; l != nil; l = tl l)
+ rl = hd l :: rl;
+ return rl;
+}
+
+#
+# the following should probably be in a separate Limbo library module
+#
+
+Keyrep: adt {
+ alg: string;
+ owner: string;
+ els: list of (string, ref IPint);
+ pick{ # keeps a type distance between public and private keys
+ PK =>
+ SK =>
+ }
+
+ pk: fn(pk: ref Keyring->PK): ref Keyrep.PK;
+ sk: fn(sk: ref Keyring->SK): ref Keyrep.SK;
+ mkpk: fn(k: self ref Keyrep): (ref Keyring->PK, int);
+ mksk: fn(k: self ref Keyrep): ref Keyring->SK;
+ get: fn(k: self ref Keyrep, n: string): ref IPint;
+ getb: fn(k: self ref Keyrep, n: string): array of byte;
+ eq: fn(k1: self ref Keyrep, k2: ref Keyrep): int;
+};
+
+keyextract(flds: list of string, names: list of (string, int)): list of (string, ref IPint)
+{
+ a := array[len flds] of ref IPint;
+ for(i := 0; i < len a; i++){
+ a[i] = IPint.b64toip(hd flds);
+ flds = tl flds;
+ }
+ rl: list of (string, ref IPint);
+ for(; names != nil; names = tl names){
+ (n, p) := hd names;
+ if(p < len a)
+ rl = (n, a[p]) :: rl;
+ }
+ return revt(rl);
+}
+
+Keyrep.pk(pk: ref Keyring->PK): ref Keyrep.PK
+{
+ s := kr->pktostr(pk);
+ (nf, flds) := sys->tokenize(s, "\n");
+ if((nf -= 2) < 0)
+ return nil;
+ case hd flds {
+ "rsa" =>
+ return ref Keyrep.PK(hd flds, hd tl flds,
+ keyextract(tl tl flds, list of {("e",1), ("n",0)}));
+ "elgamal" or "dsa" =>
+ return ref Keyrep.PK(hd flds, hd tl flds,
+ keyextract(tl tl flds, list of {("p",0), ("alpha",1), ("key",2)}));
+ * =>
+ return nil;
+ }
+}
+
+Keyrep.sk(pk: ref Keyring->SK): ref Keyrep.SK
+{
+ s := kr->pktostr(pk);
+ (nf, flds) := sys->tokenize(s, "\n");
+ if((nf -= 2) < 0)
+ return nil;
+ case hd flds {
+ "rsa" =>
+ return ref Keyrep.SK(hd flds, hd tl flds,
+ keyextract(tl tl flds,list of {("e",1), ("n",0), ("!dk",2), ("!p",3), ("!q",4), ("!kp",5), ("!kq",6), ("!c2",7)}));
+ "elgamal" or "dsa" =>
+ return ref Keyrep.SK(hd flds, hd tl flds,
+ keyextract(tl tl flds, list of {("p",0), ("alpha",1), ("key",2), ("!secret",3)}));
+ * =>
+ return nil;
+ }
+}
+
+Keyrep.get(k: self ref Keyrep, n: string): ref IPint
+{
+ for(el := k.els; el != nil; el = tl el)
+ if((hd el).t0 == n)
+ return (hd el).t1;
+ return nil;
+}
+
+Keyrep.getb(k: self ref Keyrep, n: string): array of byte
+{
+ v := k.get(n);
+ if(v == nil)
+ return nil;
+ return pre0(v.iptobebytes());
+}
+
+Keyrep.mkpk(k: self ref Keyrep): (ref Keyring->PK, int)
+{
+ case k.alg {
+ "rsa" =>
+ e := k.get("e");
+ n := k.get("n");
+ return (kr->strtopk(sys->sprint("rsa\n%s\n%s\n%s\n", k.owner, n.iptob64(), e.iptob64())), n.bits());
+ * =>
+ raise "Keyrep: unknown algorithm";
+ }
+}
+
+Keyrep.mksk(k: self ref Keyrep): ref Keyring->SK
+{
+ case k.alg {
+ "rsa" =>
+ e := k.get("e");
+ n := k.get("n");
+ dk := k.get("!dk");
+ p := k.get("!p");
+ q := k.get("!q");
+ kp := k.get("!kp");
+ kq := k.get("!kq");
+ c12 := k.get("!c2");
+ return kr->strtosk(sys->sprint("rsa\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+ k.owner, n.iptob64(), e.iptob64(), dk.iptob64(), p.iptob64(), q.iptob64(),
+ kp.iptob64(), kq.iptob64(), c12.iptob64()));
+ * =>
+ raise "Keyrep: unknown algorithm";
+ }
+}
+
+Keyrep.eq(k1: self ref Keyrep, k2: ref Keyrep): int
+{
+ # n⁲ but n is small
+ for(l1 := k1.els; l1 != nil; l1 = tl l1){
+ (n, v1) := hd l1;
+ v2 := k2.get(n);
+ if(v2 == nil || !v1.eq(v2))
+ return 0;
+ }
+ for(l2 := k2.els; l2 != nil; l2 = tl l2)
+ if(k1.get((hd l2).t0) == nil)
+ return 0;
+ return 1;
+}
+
+sig2icert(sig: ref Signature, signer: string, exp: int): ref Keyring->Certificate
+{
+ if(sig.sig == nil)
+ return nil;
+ s := sys->sprint("%s\n%s\n%s\n%d\n%s\n", "rsa", sig.hash.alg, signer, exp, base64->enc((hd sig.sig).t1));
+#sys->print("alg %s *** %s\n", sig.sa, base64->enc((hd sig.sig).t1));
+ return kr->strtocert(s);
+}
+
+#
+# pkcs1 asn.1 DER encodings
+#
+
+pkcs1_md5_pfx := array[] of {
+ byte 16r30, byte 32, # SEQUENCE in 32 bytes
+ byte 16r30, byte 12, # SEQUENCE in 12 bytes
+ byte 6, byte 8, # OBJECT IDENTIFIER in 8 bytes
+ byte (40*1+2), # iso(1) member-body(2)
+ byte (16r80 + 6), byte 72, # US(840)
+ byte (16r80 + 6), byte (16r80 + 119), byte 13, # rsadsi(113549)
+ byte 2, # digestAlgorithm(2)
+ byte 5, # md5(5), end of OBJECT IDENTIFIER
+ byte 16r05, byte 0, # NULL parameter, end of SEQUENCE
+ byte 16r04, byte 16 #OCTET STRING in 16 bytes (MD5 length)
+} ;
+
+pkcs1_sha1_pfx := array[] of {
+ byte 16r30, byte 33, # SEQUENCE in 33 bytes
+ byte 16r30, byte 9, # SEQUENCE in 9 bytes
+ byte 6, byte 5, # OBJECT IDENTIFIER in 5 bytes
+ byte (40*1+3), # iso(1) member-body(3)
+ byte 14, # ??(14)
+ byte 3, # ??(3)
+ byte 2, # digestAlgorithm(2)
+ byte 26, # sha1(26), end of OBJECT IDENTIFIER
+ byte 16r05, byte 0, # NULL parameter, end of SEQUENCE
+ byte 16r40, byte 20 # OCTET STRING in 20 bytes (SHA1 length)
+};
+
+#
+# mlen should be key length in bytes
+#
+pkcs1_encode(ha: string, msg: array of byte, mlen: int): array of byte
+{
+ # apply hash function to message
+ hash: array of byte;
+ prefix: array of byte;
+ case ha {
+ "md5" =>
+ prefix = pkcs1_md5_pfx;
+ hash = array[Keyring->MD5dlen] of byte;
+ kr->md5(msg, len msg, hash, nil);
+ "sha" or "sha1" =>
+ prefix = pkcs1_sha1_pfx;
+ hash = array[Keyring->SHA1dlen] of byte;
+ kr->sha1(msg, len msg, hash, nil);
+ * =>
+ return nil;
+ }
+ tlen := len prefix + len hash;
+ if(mlen < tlen + 11)
+ return nil; # "intended encoded message length too short"
+ pslen := mlen - tlen - 3;
+ out := array[mlen] of byte;
+ out[0] = byte 0;
+ out[1] = byte 1;
+ for(i:=0; i<pslen; i++)
+ out[i+2] = byte 16rFF;
+ out[2+pslen] = byte 0;
+ out[2+pslen+1:] = prefix;
+ out[2+pslen+1+len prefix:] = hash;
+ return out;
+}
+
+#
+# for debugging
+#
+rsacomp(block: array of byte, akey: ref Key): array of byte
+{
+ key := Keyrep.pk(akey.pk);
+ x := kr->IPint.bebytestoip(block);
+ y := x.expmod(key.get("e"), key.get("n"));
+ ybytes := y.iptobebytes();
+#dump("rsacomp", ybytes);
+ k := 1024; # key.modlen;
+ ylen := len ybytes;
+ if(ylen < k) {
+ a := array[k] of { * => byte 0};
+ a[k-ylen:] = ybytes[0:];
+ ybytes = a;
+ }
+ else if(ylen > k) {
+ # assume it has leading zeros (mod should make it so)
+ a := array[k] of byte;
+ a[0:] = ybytes[ylen-k:];
+ ybytes = a;
+ }
+ return ybytes;
+}
diff --git a/appl/lib/spki/verifier.b b/appl/lib/spki/verifier.b
new file mode 100644
index 00000000..d712fd2a
--- /dev/null
+++ b/appl/lib/spki/verifier.b
@@ -0,0 +1,188 @@
+implement Verifier;
+
+#
+# Copyright © 2004 Vita Nuova Holdings Limited
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "keyring.m";
+ kr: Keyring;
+ IPint: import kr;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "sexprs.m";
+ sexprs: Sexprs;
+ Sexp: import sexprs;
+
+include "spki.m";
+ spki: SPKI;
+ Hash, Key, Cert, Name, Subject, Signature, Seqel, Toplev, Valid: import spki;
+ dump: import spki;
+
+include "encoding.m";
+ base64: Encoding;
+
+debug := 0;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ kr = load Keyring Keyring->PATH;
+ bufio = load Bufio Bufio->PATH;
+ sexprs = load Sexprs Sexprs->PATH;
+ spki = load SPKI SPKI->PATH;
+ base64 = load Encoding Encoding->BASE64PATH;
+
+ sexprs->init();
+ spki->init();
+}
+
+putkey(keys: list of ref Key, k: ref Key): list of ref Key
+{
+ for(kl := keys; kl != nil; kl = tl kl)
+ if(k.eq(hd kl))
+ return keys;
+ return k :: keys;
+}
+
+keybyhash(h: ref Hash, keys: list of ref Key): ref Key
+{
+ for(kl := keys; kl != nil; kl = tl kl){
+ k := hd kl;
+ if(k.hash != nil && h.eq(k.hash))
+ return k;
+ }
+ return nil;
+}
+
+verify(seq: list of ref Seqel): (ref Speaksfor, list of ref Seqel, string)
+{
+ stack: list of ref Seqel;
+ keys: list of ref Key;
+ n0: ref Name;
+ cn: ref Cert;
+ delegate := 1;
+ tag: ref Sexp;
+ val: ref Valid;
+ for(; seq != nil; seq = tl seq){
+ pick s := hd seq {
+ C =>
+ diag := checkcert(s.c);
+ if(diag != nil)
+ return (nil, seq, diag);
+ if(stack != nil){
+ pick h := hd stack {
+ C =>
+ if(!delegate)
+ return(nil, seq, "previous auth certificate did not delegate");
+ if(!h.c.subject.principal().eq(s.c.issuer.principal))
+ return (nil, seq, "certificate chain has mismatched principals");
+ if(debug)
+ sys->print("issuer %s ok\n", s.c.issuer.principal.text());
+ }
+ stack = tl stack;
+ }
+ stack = s :: stack;
+ if(n0 == nil)
+ n0 = s.c.issuer;
+ cn = s.c;
+ pick t := s.c {
+ A or KH or O =>
+ delegate = t.delegate;
+ if(tag != nil){
+ tag = spki->tagintersect(tag, t.tag);
+ if(tag == nil)
+ return (nil, seq, "certificate chain has null authority");
+ }else
+ tag = t.tag;
+ if(val != nil){
+ if(t.valid != nil){
+ (ok, iv) := (*val).intersect(*t.valid);
+ if(!ok)
+ return (nil, seq, "certificate chain is not currently valid");
+ *val = iv;
+ }
+ }else
+ val = t.valid;
+ }
+ K =>
+ stack = s :: stack;
+ O =>
+ if(s.op == "debug"){
+ debug = !debug;
+ continue;
+ }
+ if(s.op != "hash" || s.args == nil || tl s.args != nil)
+ return (nil, seq, "invalid operation to `do'");
+ alg := (hd s.args).astext();
+ if(alg != "md5" && alg != "sha1")
+ return (nil, seq, "invalid hash operation");
+ if(stack == nil)
+ return (nil, seq, "verification stack empty");
+ pick h := hd stack {
+ K =>
+ a := h.k.hashed(alg);
+ if(debug)
+ dump("do hash", a);
+ keys = putkey(keys, h.k);
+ stack = tl stack;
+ C =>
+ ;
+ * =>
+ return (nil, seq, "invalid type of operand for hash");
+ }
+ S =>
+ if(stack == nil)
+ return (nil, seq, "verification stack empty");
+ sig := s.sig;
+ if(sig.key == nil)
+ return (nil, seq, "neither hash nor key for signature");
+ if(sig.key.pk == nil){
+ k := keybyhash(sig.key.hash, keys);
+ if(k == nil)
+ return (nil, seq, "unknown key for signature");
+ sig.key = k;
+ }
+ pick c := hd stack {
+ C =>
+ if(c.c.e == nil)
+ return (nil, seq, "missing canonical expression for cert");
+ a := c.c.e.pack();
+ # verify signature ...
+ if(debug)
+ dump("cert a", a);
+ h := spki->hashbytes(a, "md5");
+ if(debug){
+ dump("hash cert", h);
+ sys->print("hash = %q\n", base64->enc(h));
+ }
+ failed := spki->checksig(c.c, sig);
+ if(debug)
+ sys->print("checksig: %q\n", failed);
+ if(failed != nil)
+ return (nil, seq, "signature verification failed: "+failed);
+ * =>
+ return (nil, seq, "invalid type of signature operand");
+ }
+ }
+ }
+ if(n0 != nil && cn != nil){
+ if(debug){
+ if(tag != nil)
+ auth := sys->sprint(" regarding %q", tag.text());
+ sys->print("%q speaks for %q%s\n", cn.subject.text(), n0.text(), auth);
+ }
+ return (ref Speaksfor(cn.subject, n0, tag, val), nil, nil);
+ }
+ return (nil, nil, nil);
+}
+
+checkcert(c: ref Cert): string
+{
+ return nil;
+}
diff --git a/appl/lib/ssl.b b/appl/lib/ssl.b
new file mode 100644
index 00000000..2828e269
--- /dev/null
+++ b/appl/lib/ssl.b
@@ -0,0 +1,90 @@
+implement SSL;
+
+include "sys.m";
+ sys: Sys;
+
+include "keyring.m";
+include "security.m";
+
+sslclone(): (ref Sys->Connection, string)
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ (rc, nil) := sys->stat("#D"); # only the local device will work, because local file descriptors are used
+ if(rc < 0)
+ return (nil, sys->sprint("cannot access SSL device #D: %r"));
+ c := ref Sys->Connection;
+ c.dir = "#D";
+ if(rc >= 0){
+ (rc, nil) = sys->stat("#D/ssl"); # another variant
+ if(rc >= 0)
+ c.dir = "#D/ssl";
+ }
+ clonef := c.dir+"/clone";
+ c.cfd = sys->open(clonef, Sys->ORDWR);
+ if(c.cfd == nil)
+ return (nil, sys->sprint("cannot open %s: %r", clonef));
+ s := readstring(c.cfd);
+ if(s == nil)
+ return (nil, sys->sprint("cannot read %s: %r", clonef));
+ c.dir += "/" + s;
+ return (c, nil);
+}
+
+connect(fd: ref Sys->FD): (string, ref Sys->Connection)
+{
+ (c, err) := sslclone();
+ if(c == nil)
+ return (err, nil);
+ c.dfd = sys->open(c.dir + "/data", Sys->ORDWR);
+ if(c.dfd == nil)
+ return (sys->sprint("cannot open data: %r"), nil);
+ if(sys->fprint(c.cfd, "fd %d", fd.fd) < 0)
+ return (sys->sprint("cannot push fd: %r"), nil);
+ return (nil, c);
+}
+
+secret(c: ref Sys->Connection, secretin, secretout: array of byte): string
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+
+ if(secretin != nil){
+ fd := sys->open(c.dir + "/secretin", Sys->ORDWR);
+ if(fd == nil)
+ return sys->sprint("cannot open %s: %r", c.dir + "/secretin");
+ if(sys->write(fd, secretin, len secretin) < 0)
+ return sys->sprint("cannot write %s: %r", c.dir + "/secretin");
+ }
+
+ if(secretout != nil){
+ fd := sys->open(c.dir + "/secretout", Sys->ORDWR);
+ if(fd == nil)
+ return sys->sprint("cannot open %s: %r", c.dir + "/secretout");
+ if(sys->write(fd, secretout, len secretout) < 0)
+ return sys->sprint("cannot open %s: %r", c.dir + "/secretout");
+ }
+ return nil;
+}
+
+algs(): (list of string, list of string)
+{
+ (c, err) := sslclone();
+ if(c == nil)
+ return (nil, nil);
+ c.dfd = nil;
+ (nil, encalgs) := sys->tokenize(readstring(sys->open(c.dir+"/encalgs", Sys->OREAD)), " \t\n");
+ (nil, hashalgs) := sys->tokenize(readstring(sys->open(c.dir+"/hashalgs", Sys->OREAD)), " \t\n");
+ return (encalgs, hashalgs);
+}
+
+readstring(fd: ref Sys->FD): string
+{
+ if(fd == nil)
+ return nil;
+ buf := array[256] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return nil;
+ return string buf[0:n];
+}
diff --git a/appl/lib/string.b b/appl/lib/string.b
new file mode 100644
index 00000000..16bc1940
--- /dev/null
+++ b/appl/lib/string.b
@@ -0,0 +1,358 @@
+implement String;
+include "string.m";
+
+splitl(s: string, cl: string): (string, string)
+{
+ n := len s;
+ for(j := 0; j < n; j++) {
+ if(in(s[j], cl))
+ return (s[0:j], s[j:n]);
+ }
+ return (s,"");
+}
+
+splitr(s: string, cl: string): (string, string)
+{
+ n := len s;
+ for(j := n-1; j >= 0; j--) {
+ if(in(s[j], cl))
+ return (s[0:j+1], s[j+1:n]);
+ }
+ return ("",s);
+}
+
+drop(s: string, cl: string): string
+{
+ n := len s;
+ for(j := 0; j < n; j++) {
+ if(!in(s[j], cl))
+ return (s[j:n]);
+ }
+ return "";
+}
+
+take(s: string, cl: string): string
+{
+ n := len s;
+ for(j := 0; j < n; j++) {
+ if(!in(s[j], cl))
+ return (s[0:j]);
+ }
+ return s;
+}
+
+in(c: int, s: string): int
+{
+ n := len s;
+ if(n == 0)
+ return 0;
+ ans := 0;
+ negate := 0;
+ if(s[0] == '^') {
+ negate = 1;
+ s = s[1:];
+ n--;
+ }
+ for(i := 0; i < n; i++) {
+ if(s[i] == '-' && i > 0 && i < n-1) {
+ if(c >= s[i-1] && c <= s[i+1]) {
+ ans = 1;
+ break;
+ }
+ i++;
+ }
+ else
+ if(c == s[i]) {
+ ans = 1;
+ break;
+ }
+ }
+ if(negate)
+ ans = !ans;
+ return ans;
+}
+
+splitstrl(s: string, t: string): (string, string)
+{
+ n := len s;
+ nt := len t;
+ if(nt == 0)
+ return ("", s);
+ c0 := t[0];
+ mainloop:
+ for(j := 0; j <= n-nt; j++) {
+ if(s[j] == c0) {
+ for(k := 1; k < nt; k++)
+ if(s[j+k] != t[k])
+ continue mainloop;
+ return(s[0:j], s[j:n]);
+ }
+ }
+ return (s,"");
+}
+
+splitstrr(s: string, t: string): (string, string)
+{
+ n := len s;
+ nt := len t;
+ if(nt == 0)
+ return (s, "");
+ c0 := t[0];
+ mainloop:
+ for(j := n-nt; j >= 0; j--) {
+ if(s[j] == c0) {
+ for(k := 1; k < nt; k++)
+ if(s[j+k] != t[k])
+ continue mainloop;
+ return(s[0:j+nt], s[j+nt:n]);
+ }
+ }
+ return ("",s);
+}
+
+prefix(pre: string, s: string): int
+{
+ ns := len s;
+ n := len pre;
+ if(ns < n)
+ return 0;
+ for(k := 0; k < n; k++) {
+ if(pre[k] != s[k])
+ return 0;
+ }
+ return 1;
+}
+
+tolower(s: string): string
+{
+ r := s;
+ for(i := 0; i < len r; i++) {
+ c := r[i];
+ if(c >= int 'A' && c <= int 'Z')
+ r[i] = r[i] + (int 'a' - int 'A');
+ }
+ return r;
+}
+
+toupper(s: string): string
+{
+ r := s;
+ for(i := 0; i < len r; i++) {
+ c := r[i];
+ if(c >= int 'a' && c <= int 'z')
+ r[i] = r[i] - (int 'a' - int 'A');
+ }
+ return r;
+}
+
+tobig(s: string, base: int): (big, string)
+{
+ if (s == nil || base < 0 || base > 36 || base == 1)
+ return (big 0, s);
+
+ # skip possible leading white space
+ c: int;
+ for (i := 0; i < len s; i++) {
+ c = s[i];
+ if(c != ' ' && c != '\t' && c != '\n')
+ break;
+ }
+
+ # skip possible sign character
+ neg := 0;
+ if (c == '-' || c == '+') {
+ if(c == '-')
+ neg = 1;
+ i++;
+ }
+
+ if (base == 0) {
+ # parse possible leading base designator
+ start := i;
+ base = -1;
+ for (; i < start+3 && i < len s; i++) {
+ c = s[i];
+ if (c == 'r' && i > start) {
+ base = int s[start:i];
+ i++;
+ break;
+ } else if (c < '0' || c > '9')
+ break;
+ }
+ if (base == -1) {
+ i = start;
+ base = 10;
+ } else if (base == 0 || base > 36)
+ return (big 0, s);
+ }
+
+ # parse number itself.
+ # perhaps this should check for overflow, and max out, as limbo op does?
+ start := i;
+ dig := '9';
+ if (base < 10)
+ dig = '0' + base - 1;
+ n := big 0;
+ for (; i < len s; i++) {
+ c = s[i];
+ if ('0' <= c && c <= dig)
+ n = (n * big base) + big(c - '0');
+ else if ('a' <= c && c < 'a' + base - 10)
+ n = (n * big base) + big(c - 'a' + 10);
+ else if ('A' <= c && c < 'A' + base - 10)
+ n = (n * big base) + big(c - 'A' + 10);
+ else
+ break;
+ }
+ if (i == start)
+ return (big 0, s);
+ if (neg)
+ return (-n, s[i:]);
+ return (n, s[i:]);
+}
+
+toint(s: string, base: int): (int, string)
+{
+ if (s == nil || base < 0 || base > 36 || base == 1)
+ return (0, s);
+
+ # skip possible leading white space
+ c: int;
+ for (i := 0; i < len s; i++) {
+ c = s[i];
+ if(c != ' ' && c != '\t' && c != '\n')
+ break;
+ }
+
+ # skip possible sign character
+ neg := 0;
+ if (c == '-' || c == '+') {
+ if(c == '-')
+ neg = 1;
+ i++;
+ }
+
+ if (base == 0) {
+ # parse possible leading base designator
+ start := i;
+ base = -1;
+ for (; i < start+3 && i < len s; i++) {
+ c = s[i];
+ if (c == 'r' && i > start) {
+ base = int s[start:i];
+ i++;
+ break;
+ } else if (c < '0' || c > '9')
+ break;
+ }
+ if (base == -1) {
+ i = start;
+ base = 10;
+ } else if (base == 0 || base > 36)
+ return (0, s);
+ }
+
+ # parse number itself.
+ # perhaps this should check for overflow, and max out, as limbo op does?
+ start := i;
+ dig := '9';
+ if (base < 10)
+ dig = '0' + base - 1;
+ n := 0;
+ for (; i < len s; i++) {
+ c = s[i];
+ if ('0' <= c && c <= dig)
+ n = (n * base) + (c - '0');
+ else if ('a' <= c && c < 'a' + base - 10)
+ n = (n * base) + (c - 'a' + 10);
+ else if ('A' <= c && c < 'A' + base - 10)
+ n = (n * base) + (c - 'A' + 10);
+ else
+ break;
+ }
+ if (i == start)
+ return (0, s);
+ if (neg)
+ return (-n, s[i:]);
+ return (n, s[i:]);
+}
+
+append(s: string, l: list of string): list of string
+{
+ t: list of string;
+
+ # Reverse l, prepend s, and reverse result.
+ while (l != nil) {
+ t = hd l :: t;
+ l = tl l;
+ }
+ t = s :: t;
+ do {
+ l = hd t :: l;
+ t = tl t;
+ } while (t != nil);
+ return l;
+}
+
+quoted(argv: list of string): string
+{
+ return quotedc(argv, nil);
+}
+
+quotedc(argv: list of string, cl: string): string
+{
+ s := "";
+ while(argv != nil){
+ arg := hd argv;
+ for(i := 0; i < len arg; i++){
+ c := arg[i];
+ if(c == ' ' || c == '\t' || c == '\n' || c == '\'' || in(c, cl))
+ break;
+ }
+ if(i < len arg || arg == nil){
+ s += "'" + arg[0:i];
+ for(; i < len arg; i++){
+ if (arg[i] == '\'')
+ s[len s] = '\'';
+ s[len s] = arg[i];
+ }
+ s[len s] = '\'';
+ }else
+ s += arg;
+ if(tl argv != nil)
+ s[len s] = ' ';
+ argv = tl argv;
+ }
+ return s;
+}
+
+unquoted(s: string): list of string
+{
+ args: list of string;
+ word: string;
+ inquote := 0;
+ for(j := len s; j > 0;){
+ c := s[j-1];
+ if(c == ' ' || c == '\t' || c == '\n'){
+ j--;
+ continue;
+ }
+ for(i := j-1; i >= 0 && ((c = s[i]) != ' ' && c != '\t' && c != '\n' || inquote); i--){ # collect word
+ if(c == '\''){
+ word = s[i+1:j] + word;
+ j = i;
+ if(!inquote || i == 0 || s[i-1] != '\'')
+ inquote = !inquote;
+ else
+ i--;
+ }
+ }
+ args = (s[i+1:j]+word) :: args;
+ word = nil;
+ j = i;
+ }
+ # if quotes were unbalanced, balance them and try again.
+ if(inquote)
+ return unquoted(s + "'");
+ return args;
+}
diff --git a/appl/lib/strinttab.b b/appl/lib/strinttab.b
new file mode 100644
index 00000000..a96d0f57
--- /dev/null
+++ b/appl/lib/strinttab.b
@@ -0,0 +1,28 @@
+implement StringIntTab;
+
+include "strinttab.m";
+
+lookup(t: array of StringInt, key: string) : (int, int)
+{
+ min := 0;
+ max := len t-1;
+ while(min <= max){
+ try := (min+max)/2;
+ if(t[try].key < key)
+ min = try+1;
+ else if(t[try].key > key)
+ max = try-1;
+ else
+ return (1, t[try].val);
+ }
+ return (0, 0);
+}
+
+revlookup(t: array of StringInt, val: int) : string
+{
+ n := len t;
+ for(i:=0; i < n; i++)
+ if(t[i].val == val)
+ return t[i].key;
+ return nil;
+}
diff --git a/appl/lib/strokes/buildstrokes.b b/appl/lib/strokes/buildstrokes.b
new file mode 100644
index 00000000..2d8a18c4
--- /dev/null
+++ b/appl/lib/strokes/buildstrokes.b
@@ -0,0 +1,260 @@
+implement Buildstrokes;
+
+#
+# this Limbo code is derived from C code that had the following
+# copyright notice, which i reproduce as requested
+#
+# li_strokesnizer.c
+#
+# Copyright 2000 Compaq Computer Corporation.
+# Copying or modifying this code for any purpose is permitted,
+# provided that this copyright notice is preserved in its entirety
+# in all copies or modifications.
+# COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR
+# IMPLIED, AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR
+#
+#
+# Adapted from cmu_strokesnizer.c by Jay Kistler.
+#
+# Where is the CMU copyright???? Gotta track it down - Jim Gettys
+#
+# Credit to Dean Rubine, Jim Kempf, and Ari Rapkin.
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "strokes.m";
+ strokes: Strokes;
+ Classifier, Penpoint, Stroke, Region: import strokes;
+ Rconvex, Rconcave, Rplain, Rpseudo: import Strokes;
+
+lidebug: con 0;
+stderr: ref Sys->FD;
+
+init(r: Strokes)
+{
+ sys = load Sys Sys->PATH;
+ if(lidebug)
+ stderr = sys->fildes(2);
+ strokes = r;
+}
+
+#
+# Implementation of the Li/Yeung recognition algorithm
+#
+
+# Pre-processing and canonicalization parameters
+CANONICAL_X: con 108;
+CANONICAL_Y: con 128;
+NCANONICAL: con 50;
+
+
+#
+# calculate canonical forms
+#
+
+canonical_example(nclasses: int, cnames: array of string, examples: array of list of ref Stroke): (string, array of ref Stroke, array of ref Stroke)
+{
+ canonex := array[nclasses] of ref Stroke;
+ dompts := array[nclasses] of ref Stroke;
+
+ # make canonical examples for each class.
+ for(i := 0; i < nclasses; i++){
+ if(lidebug)
+ sys->fprint(stderr, "canonical_example: class %s\n", cnames[i]);
+
+ # Make a copy of the examples.
+ pts: list of ref Stroke = nil;
+ nex := 0;
+ for(exl := examples[i]; exl != nil; exl = tl exl){
+ t := hd exl;
+ pts = t.copy() :: pts;
+ nex++;
+ }
+
+ # Canonicalize each example, and derive the max x and y ranges.
+ maxxrange := 0;
+ maxyrange := 0;
+ for(exl = pts; exl != nil; exl = tl exl){
+ e := hd exl;
+ ce := canonical_stroke(e);
+ if(ce == nil){
+ if(lidebug)
+ sys->fprint(stderr, "example discarded: can't make canonical form\n");
+ continue; # try the next one
+ }
+ *e = *ce;
+ if(e.xrange > maxxrange)
+ maxxrange = e.xrange;
+ if(e.yrange > maxyrange)
+ maxyrange = e.yrange;
+ }
+
+ # Normalise max ranges.
+ (maxxrange, maxyrange) = normalise(maxxrange, maxyrange, CANONICAL_X, CANONICAL_Y);
+
+ # Re-scale each example to max ranges.
+ for(exl = pts; exl != nil; exl = tl exl){
+ t := hd exl;
+ scalex, scaley: int;
+ if(t.xrange == 0)
+ scalex = 100;
+ else
+ scalex = (100*maxxrange + t.xrange/2) / t.xrange;
+ if(t.yrange == 0)
+ scaley = 100;
+ else
+ scaley = (100*maxyrange + t.yrange/2) / t.yrange;
+ t.translate(0, 0, scalex, scaley);
+ }
+
+ # Average the examples; leave average in first example.
+ avg := hd pts; # careful, aliasing
+ for(k := 0; k < NCANONICAL; k++){
+ xsum := 0;
+ ysum := 0;
+ for(exl = pts; exl != nil; exl = tl exl){
+ t := hd exl;
+ xsum += t.pts[k].x;
+ ysum += t.pts[k].y;
+ }
+ avg.pts[k].x = (xsum + nex/2) / nex;
+ avg.pts[k].y = (ysum + nex/2) / nex;
+ }
+
+ # rescale averaged stroke
+ avg.scaleup();
+
+ # Re-compute the x and y ranges and center the stroke.
+ avg.center();
+
+ canonex[i] = avg; # now it's the canonical representation
+
+ if(lidebug){
+ sys->fprint(stderr, "%s, avgpts = %d\n", cnames[i], avg.npts);
+ for(j := 0; j < avg.npts; j++){
+ p := avg.pts[j];
+ sys->fprint(stderr, " (%d %d)\n", p.x, p.y);
+ }
+ }
+
+ dompts[i] = avg.interpolate().dominant(); # dominant points of canonical representation
+ }
+
+ return (nil, canonex, dompts);
+}
+
+normalise(x, y: int, xrange, yrange: int): (int, int)
+{
+ if((100*x + xrange/2)/xrange > (100*y + yrange/2)/yrange){
+ y = (y*xrange + x/2)/x;
+ x = xrange;
+ }else{
+ x = (x*yrange + y/2)/y;
+ y = yrange;
+ }
+ return (x, y);
+}
+
+canonical_stroke(points: ref Stroke): ref Stroke
+{
+ points = points.filter();
+ if(points.npts < 2)
+ return nil;
+
+ # Scale up to avoid conversion errors.
+ points.scaleup();
+
+ # Compute an equivalent stroke with equi-distant points
+ points = compute_equipoints(points);
+ if(points == nil)
+ return nil;
+
+ # Re-translate the points to the origin.
+ (minx, miny, maxx, maxy) := points.bbox();
+ points.translate(minx, miny, 100, 100);
+
+ # Store the x and y ranges in the point list.
+ points.xrange = maxx - minx;
+ points.yrange = maxy - miny;
+
+ if(lidebug){
+ sys->fprint(stderr, "Canonical stroke: %d, %d, %d, %d\n", minx, miny, maxx, maxy);
+ for(i := 0; i < points.npts; i++){
+ p := points.pts[i];
+ sys->fprint(stderr, " (%d %d)\n", p.x, p.y);
+ }
+ }
+
+ return points;
+}
+
+compute_equipoints(points: ref Stroke): ref Stroke
+{
+ pathlen := points.length();
+ equidist := (pathlen + (NCANONICAL-1)/2) / (NCANONICAL-1);
+ equipoints := array[NCANONICAL] of Penpoint;
+ if(lidebug)
+ sys->fprint(stderr, "compute_equipoints: npts = %d, pathlen = %d, equidist = %d\n",
+ points.npts, pathlen, equidist);
+
+ # First original point is an equipoint.
+ equipoints[0] = points.pts[0];
+ nequipoints := 1;
+ dist_since_last_eqpt := 0;
+
+ for(i := 1; i < points.npts; i++){
+ dx1 := points.pts[i].x - points.pts[i-1].x;
+ dy1 := points.pts[i].y - points.pts[i-1].y;
+ endx := points.pts[i-1].x*100;
+ endy := points.pts[i-1].y*100;
+ remaining_seglen := strokes->sqrt(100*100 * (dx1*dx1 + dy1*dy1));
+ dist_to_next_eqpt := equidist - dist_since_last_eqpt;
+ while(remaining_seglen >= dist_to_next_eqpt){
+ if(dx1 == 0){
+ # x-coordinate stays the same
+ if(dy1 >= 0)
+ endy += dist_to_next_eqpt;
+ else
+ endy -= dist_to_next_eqpt;
+ }else{
+ slope := (100*dy1 + dx1/2) / dx1;
+ tmp := strokes->sqrt(100*100 + slope*slope);
+ dx := (100*dist_to_next_eqpt + tmp/2) / tmp;
+ dy := (slope*dx + 50)/100;
+ if(dy < 0)
+ dy = -dy;
+ if(dx1 >= 0)
+ endx += dx;
+ else
+ endx -= dx;
+ if(dy1 >= 0)
+ endy += dy;
+ else
+ endy -= dy;
+ }
+ equipoints[nequipoints].x = (endx + 50) / 100;
+ equipoints[nequipoints].y = (endy + 50) / 100;
+ nequipoints++;
+ #assert(nequipoints <= NCANONICAL);
+ dist_since_last_eqpt = 0;
+ remaining_seglen -= dist_to_next_eqpt;
+ dist_to_next_eqpt = equidist;
+ }
+ dist_since_last_eqpt += remaining_seglen;
+ }
+
+ # Take care of last equipoint.
+ if(nequipoints == NCANONICAL-1){
+ # Make last original point the last equipoint.
+ equipoints[nequipoints++] = points.pts[points.npts - 1];
+ }
+ if(nequipoints != NCANONICAL){ # fell short
+ if(lidebug)
+ sys->fprint(stderr,"compute_equipoints: nequipoints = %d\n", nequipoints);
+ # assert(false);
+ return nil;
+ }
+ return ref Stroke(NCANONICAL, equipoints, 0, 0);
+}
diff --git a/appl/lib/strokes/mkfile b/appl/lib/strokes/mkfile
new file mode 100644
index 00000000..e06e6327
--- /dev/null
+++ b/appl/lib/strokes/mkfile
@@ -0,0 +1,18 @@
+<../../../mkconfig
+
+TARG=\
+ buildstrokes.dis\
+ strokes.dis\
+ readstrokes.dis\
+ writestrokes.dis\
+
+MODULES=
+
+SYSMODULES= \
+ bufio.m\
+ strokes.m\
+ sys.m\
+
+DISBIN=$ROOT/dis/lib/strokes
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/lib/strokes/readstrokes.b b/appl/lib/strokes/readstrokes.b
new file mode 100644
index 00000000..c42bd48c
--- /dev/null
+++ b/appl/lib/strokes/readstrokes.b
@@ -0,0 +1,205 @@
+implement Readstrokes;
+
+#
+# read structures from stroke classifier files
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "strokes.m";
+ strokes: Strokes;
+ Classifier, Penpoint, Stroke, Region: import strokes;
+ buildstrokes: Buildstrokes;
+
+init(s: Strokes)
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ strokes = s;
+}
+
+getint(fp: ref Iobuf): (int, int)
+{
+ while((c := fp.getc()) == ' ' || c == '\t' || c == '\n')
+ ;
+ if(c < 0)
+ return (c, 0);
+ sign := 1;
+ if(c == '-')
+ sign = -1;
+ else if(c == '+')
+ ;
+ else
+ fp.ungetc();
+ rc := 0;
+ n := 0;
+ while((c = fp.getc()) >= '0' && c <= '9'){
+ n = n*10 + (c-'0');
+ rc = 1;
+ }
+ return (rc, n*sign);
+}
+
+getstr(fp: ref Iobuf): (int, string)
+{
+ while((c := fp.getc()) == ' ' || c == '\t' || c == '\n')
+ ;
+ if(c < 0)
+ return (c, nil);
+ fp.ungetc();
+ s := "";
+ while((c = fp.getc()) != ' ' && c != '\t' && c != '\n')
+ s[len s] = c;
+ return (0, s);
+}
+
+getpoint(fp: ref Iobuf): (int, Penpoint)
+{
+ (okx, x) := getint(fp);
+ (oky, y) := getint(fp);
+ if(okx <= 0 || oky <= 0)
+ return (-1, (0,0,0));
+ return (0, (x,y,0));
+}
+
+getpoints(fp: ref Iobuf): ref Stroke
+{
+ (ok, npts) := getint(fp);
+ if(ok <= 0 || npts < 0 || npts > 4000)
+ return nil;
+ pts := array[npts] of Penpoint;
+ for(i := 0; i < npts; i++){
+ (ok, pts[i]) = getpoint(fp);
+ if(ok < 0)
+ return nil;
+ }
+ return ref Stroke(npts, pts, 0, 0);
+}
+
+read_classifier_points(fp: ref Iobuf, nclass: int): (int, array of string, array of list of ref Stroke)
+{
+ names := array[nclass] of string;
+ examples := array[nclass] of list of ref Stroke;
+ for(k := 0; k < nclass; k++){
+ # read class name and number of examples
+ (ok, nex) := getint(fp);
+ if(ok <= 0)
+ return (-1, nil, nil);
+ (ok, names[k]) = getstr(fp);
+ if(ok < 0)
+ return (ok, nil, nil);
+
+ # read examples
+ for(i := 0; i < nex; i++){
+ pts := getpoints(fp);
+ if(pts == nil)
+ return (-1, nil, nil);
+ examples[k] = pts :: examples[k];
+ }
+ }
+ return (0, names, examples);
+}
+
+#
+# read a classifier, using its digest if that exists
+#
+read_classifier(file: string, build: int, needex: int): (string, ref Classifier)
+{
+ rc := ref Classifier;
+ l := len file;
+ digestfile: string;
+ if(l >= 4 && file[l-4:]==".clx")
+ digestfile = file;
+ else if(!needex && l >= 3 && file[l-3:]==".cl")
+ digestfile = file[0:l-3]+".clx"; # try the digest file first
+ err: string;
+ if(digestfile != nil){
+ fd := sys->open(digestfile, Sys->OREAD);
+ if(fd != nil){
+ (err, rc.cnames, rc.dompts) = read_digest(fd);
+ rc.nclasses = len rc.cnames;
+ if(rc.cnames == nil)
+ err = "empty digest file";
+ if(err == nil)
+ return (nil, rc);
+ }else
+ err = sys->sprint("%r");
+ if(!build)
+ return (sys->sprint("digest file: %s", err), nil);
+ }
+
+ if(buildstrokes == nil){
+ buildstrokes = load Buildstrokes Buildstrokes->PATH;
+ if(buildstrokes == nil)
+ return (sys->sprint("module %s: %r", Buildstrokes->PATH), nil);
+ buildstrokes->init(strokes);
+ }
+
+ fd := sys->open(file, Sys->OREAD);
+ if(fd == nil)
+ return (sys->sprint("%r"), nil);
+ (emsg, cnames, examples) := read_examples(fd);
+ if(emsg != nil)
+ return (emsg, nil);
+ rc.nclasses = len cnames;
+ (err, rc.canonex, rc.dompts) = buildstrokes->canonical_example(rc.nclasses, cnames, examples);
+ if(err != nil)
+ return ("failed to calculate canonical examples", nil);
+ rc.cnames = cnames;
+ if(needex)
+ rc.examples = examples;
+
+ return (nil, rc);
+}
+
+read_examples(fd: ref Sys->FD): (string, array of string, array of list of ref Strokes->Stroke)
+{
+ fp := bufio->fopen(fd, Bufio->OREAD);
+ (ok, nclasses) := getint(fp);
+ if(ok <= 0)
+ return ("missing number of classes", nil, nil);
+ (okc, cnames, examples) := read_classifier_points(fp, nclasses);
+ if(okc < 0)
+ return ("couldn't read examples", nil, nil);
+ return (nil, cnames, examples);
+}
+
+#
+# attempt to read the digest of a classifier,
+# and return its contents if successful;
+# return a diagnostic if not
+#
+read_digest(fd: ref Sys->FD): (string, array of string, array of ref Stroke)
+{
+ # Read-in the name and dominant points for each class.
+ fp := bufio->fopen(fd, Bufio->OREAD);
+ cnames := array[32] of string;
+ dompts := array[32] of ref Stroke;
+ for(nclasses := 0;; nclasses++){
+ if(nclasses >= len cnames){
+ a := array[nclasses+32] of string;
+ a[0:] = cnames;
+ cnames = a;
+ b := array[nclasses+32] of ref Stroke;
+ b[0:] = dompts;
+ dompts = b;
+ }
+ (okn, class) := getstr(fp);
+ if(okn == Bufio->EOF)
+ break;
+ if(class == nil)
+ return ("expected class name", nil, nil);
+ cnames[nclasses] = class;
+ dpts := getpoints(fp);
+ if(dpts == nil)
+ return ("bad points list", nil, nil);
+ strokes->compute_chain_code(dpts);
+ dompts[nclasses] = dpts;
+ }
+ return (nil, cnames[0:nclasses], dompts[0:nclasses]);
+}
diff --git a/appl/lib/strokes/strokes.b b/appl/lib/strokes/strokes.b
new file mode 100644
index 00000000..3797b3d1
--- /dev/null
+++ b/appl/lib/strokes/strokes.b
@@ -0,0 +1,793 @@
+implement Strokes;
+
+#
+# this Limbo code is derived from C code that had the following
+# copyright notice, which i reproduce as requested
+#
+# li_recognizer.c
+#
+# Copyright 2000 Compaq Computer Corporation.
+# Copying or modifying this code for any purpose is permitted,
+# provided that this copyright notice is preserved in its entirety
+# in all copies or modifications.
+# COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR
+# IMPLIED, AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR
+#
+#
+# Adapted from cmu_recognizer.c by Jay Kistler.
+#
+# Where is the CMU copyright???? Gotta track it down - Jim Gettys
+#
+# Credit to Dean Rubine, Jim Kempf, and Ari Rapkin.
+#
+#
+# the copyright notice really did end in the middle of the sentence
+#
+
+#
+# Limbo version for Inferno by forsyth@vitanuova.com, Vita Nuova, September 2001
+#
+
+#
+# the code is reasonably close to the algorithms described in
+# ``On-line Handwritten Alphanumeric Character Recognition Using Dominant Stroke in Strokes'',
+# Xiaolin Li and Dit-Yan Yueng, Department of Computer Science,
+# Hong Kong University of Science and Technology, Hong Kong (23 August 1996)
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "strokes.m";
+
+MAXINT: con 16r7FFFFFFF;
+
+# Dynamic programming parameters
+DP_BAND: con 3;
+SIM_THLD: con 60; # x100
+#DIST_THLD: con 3200; # x100
+DIST_THLD: con 3300; # x100
+
+# Low-pass filter parameters -- empirically derived
+LP_FILTER_WIDTH: con 6;
+LP_FILTER_ITERS: con 8;
+LP_FILTER_THLD: con 250; # x100
+LP_FILTER_MIN: con 5;
+
+# Pseudo-extrema parameters -- empirically derived
+PE_AL_THLD: con 1500; # x100
+PE_ATCR_THLD: con 135; # x100
+
+# Pre-processing and canonicalization parameters
+CANONICAL_X: con 108;
+CANONICAL_Y: con 128;
+DIST_SQ_THRESHOLD: con 3*3;
+
+# direction-code table; indexed by dx, dy
+dctbl := array[] of {array[] of {1, 0, 7}, array[] of {2, MAXINT, 6}, array[] of {3, 4, 5}};
+
+# low-pass filter weights
+lpfwts := array[2 * LP_FILTER_WIDTH + 1] of int;
+lpfconst := -1;
+
+lidebug: con 0;
+stderr: ref Sys->FD;
+
+# x := 0.04 * (i * i);
+# wtvals[i] = floor(100.0 * exp(x));
+wtvals := array[] of {100, 104, 117, 143, 189, 271, 422};
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ if(lidebug)
+ stderr = sys->fildes(2);
+ for(i := LP_FILTER_WIDTH; i >= 0; i--){
+ wt := wtvals[i];
+ lpfwts[LP_FILTER_WIDTH - i] = wt;
+ lpfwts[LP_FILTER_WIDTH + i] = wt;
+ }
+ lpfconst = 0;
+ for(i = 0; i < (2 * LP_FILTER_WIDTH + 1); i++)
+ lpfconst += lpfwts[i];
+}
+
+Stroke.new(n: int): ref Stroke
+{
+ return ref Stroke(n, array[n] of Penpoint, 0, 0);
+}
+
+Stroke.trim(ps: self ref Stroke, n: int)
+{
+ ps.npts = n;
+ ps.pts = ps.pts[0:n];
+}
+
+Stroke.copy(ps: self ref Stroke): ref Stroke
+{
+ n := ps.npts;
+ a := array[n] of Penpoint;
+ a[0:] = ps.pts[0:n];
+ return ref Stroke(n, a, ps.xrange, ps.yrange);
+}
+
+#
+# return the bounding box of a set of points
+# (note: unlike Draw->Rectangle, the region is closed)
+#
+Stroke.bbox(ps: self ref Stroke): (int, int, int, int)
+{
+ minx := maxx := ps.pts[0].x;
+ miny := maxy := ps.pts[0].y;
+ for(i := 1; i < ps.npts; i++){
+ pt := ps.pts[i];
+ if(pt.x < minx)
+ minx = pt.x;
+ if(pt.x > maxx)
+ maxx = pt.x;
+ if(pt.y < miny)
+ miny = pt.y;
+ if(pt.y > maxy)
+ maxy = pt.y;
+ }
+ return (minx, miny, maxx, maxy); # warning: closed interval
+}
+
+Stroke.center(ps: self ref Stroke)
+{
+ (minx, miny, maxx, maxy) := ps.bbox();
+ ps.xrange = maxx-minx;
+ ps.yrange = maxy-miny;
+ avgxoff := -((CANONICAL_X - ps.xrange + 1) / 2);
+ avgyoff := -((CANONICAL_Y - ps.yrange + 1) / 2);
+ ps.translate(avgxoff, avgyoff, 100, 100);
+}
+
+Stroke.scaleup(ps: self ref Stroke): int
+{
+ (minx, miny, maxx, maxy) := ps.bbox();
+ xrange := maxx - minx;
+ yrange := maxy - miny;
+ scale: int;
+ if(((100 * xrange + CANONICAL_X / 2) / CANONICAL_X) >
+ ((100 * yrange + CANONICAL_Y / 2) / CANONICAL_Y))
+ scale = (100 * CANONICAL_X + xrange / 2) / xrange;
+ else
+ scale = (100 * CANONICAL_Y + yrange / 2) / yrange;
+ ps.translate(minx, miny, scale, scale);
+ return scale;
+}
+
+# scalex and scaley are x 100.
+# Note that this does NOT update points.xrange and points.yrange!
+Stroke.translate(ps: self ref Stroke, minx: int, miny: int, scalex: int, scaley: int)
+{
+ for(i := 0; i < ps.npts; i++){
+ ps.pts[i].x = ((ps.pts[i].x - minx) * scalex + 50) / 100;
+ ps.pts[i].y = ((ps.pts[i].y - miny) * scaley + 50) / 100;
+ }
+}
+
+TAP_PATHLEN: con 10*100; # x100
+
+Classifier.match(rec: self ref Classifier, stroke: ref Stroke): (int, string)
+{
+ if(stroke.npts < 1)
+ return (-1, nil);
+
+ # Check for tap.
+
+ # First thing is to filter out ``close points.''
+ stroke = stroke.filter();
+
+ # Unfortunately, we don't have the actual time that each point
+ # was recorded (i.e., dt is invalid). Hence, we have to use a
+ # heuristic based on total distance and the number of points.
+ if(stroke.npts == 1 || stroke.length() < TAP_PATHLEN)
+ return (-1, "tap");
+
+ preprocess_stroke(stroke);
+
+ # Compute its dominant points.
+ dompts := stroke.interpolate().dominant();
+ best_dist := MAXDIST;
+ best_i := -1;
+ best_name: string;
+
+ # Score input stroke against every class in classifier.
+ for(i := 0; i < len rec.cnames; i++){
+ name := rec.cnames[i];
+ (sim, dist) := score_stroke(dompts, rec.dompts[i]);
+ if(dist < MAXDIST)
+ sys->fprint(stderr, " (%s, %d, %d)", name, sim, dist);
+ if(dist < DIST_THLD){
+ if(lidebug)
+ sys->fprint(stderr, " (%s, %d, %d)", name, sim, dist);
+ # Is it the best so far?
+ if(dist < best_dist){
+ best_dist = dist;
+ best_i = i;
+ best_name = name;
+ }
+ }
+ }
+
+ if(lidebug)
+ sys->fprint(stderr, "\n");
+ return (best_i, best_name);
+}
+
+preprocess_stroke(s: ref Stroke)
+{
+ # Filter out points that are too close.
+ # We did this earlier, when we checked for a tap.
+
+# s = s.filter();
+
+
+# assert(s.npts > 0);
+
+ # Scale up to avoid conversion errors.
+ s.scaleup();
+
+ # Center the stroke.
+ s.center();
+
+ if(lidebug){
+ (minx, miny, maxx, maxy) := s.bbox();
+ sys->fprint(stderr, "After pre-processing: [ %d %d %d %d]\n",
+ minx, miny, maxx, maxy);
+ printpoints(stderr, s, "\n");
+ }
+}
+
+#
+# return the dominant points of Stroke s, assuming s has been through interpolation
+#
+Stroke.dominant(s: self ref Stroke): ref Stroke
+{
+ regions := s.regions();
+
+ # Dominant points are: start, end, extrema of non plain regions, and midpoints of the preceding.
+ nonplain := 0;
+ for(r := regions; r != nil; r = r.next)
+ if(r.rtype != Rplain)
+ nonplain++;
+ dom := Stroke.new(1 + 2*nonplain + 2);
+
+ # Pick out dominant points.
+
+ # start point
+ dp := 0;
+ previx := 0;
+ dom.pts[dp++] = s.pts[previx];
+ currix: int;
+
+ cas := s.contourangles(regions);
+ for(r = regions; r != nil; r = r.next)
+ if(r.rtype != Rplain){
+ max_v := 0;
+ min_v := MAXINT;
+ max_ix := -1;
+ min_ix := -1;
+
+ for(i := r.start; i <= r.end; i++){
+ v := cas[i];
+ if(v > max_v){
+ max_v = v; max_ix = i;
+ }
+ if(v < min_v){
+ min_v = v; min_ix = i;
+ }
+ if(lidebug > 1)
+ sys->fprint(stderr, " %d\n", v);
+ }
+ if(r.rtype == Rconvex)
+ currix = max_ix;
+ else
+ currix = min_ix;
+
+ dom.pts[dp++] = s.pts[(previx+currix)/2]; # midpoint
+ dom.pts[dp++] = s.pts[currix]; # extreme
+
+ previx = currix;
+ }
+
+ # last midpoint, and end point
+ lastp := s.npts - 1;
+ dom.pts[dp++] = s.pts[(previx+lastp)/2];
+ dom.pts[dp++] = s.pts[lastp];
+ dom.trim(dp);
+
+ # Compute chain-code.
+ compute_chain_code(dom);
+
+ return dom;
+}
+
+Stroke.contourangles(s: self ref Stroke, regions: ref Region): array of int
+{
+ V := array[s.npts] of int;
+ V[0] = 18000;
+ for(r := regions; r != nil; r = r.next){
+ for(i := r.start; i <= r.end; i++){
+ if(r.rtype == Rplain){
+ V[i] = 18000;
+ }else{
+ # For now, simply choose the mid-point.
+ ismidpt := i == (r.start + r.end)/2;
+ if(ismidpt ^ (r.rtype!=Rconvex))
+ V[i] = 18000;
+ else
+ V[i] = 0;
+ }
+ }
+ }
+ V[s.npts - 1] = 18000;
+ return V;
+}
+
+Stroke.interpolate(s: self ref Stroke): ref Stroke
+{
+ # Compute an upper-bound on the number of interpolated points
+ maxpts := s.npts;
+ for(i := 0; i < s.npts - 1; i++){
+ a := s.pts[i];
+ b := s.pts[i+1];
+ maxpts += abs(a.x - b.x) + abs(a.y - b.y);
+ }
+
+ # Allocate an array of the maximum size
+ newpts := Stroke.new(maxpts);
+
+ # Interpolate each of the segments.
+ j := 0;
+ for(i = 0; i < s.npts - 1; i++){
+ j = bresline(s.pts[i], s.pts[i+1], newpts, j);
+ j--; # end point gets recorded as start point of next segment!
+ }
+
+ # Add-in last point and trim
+ newpts.pts[j++] = s.pts[s.npts - 1];
+ newpts.trim(j);
+
+ if(lidebug){
+ sys->fprint(stderr, "After interpolation:\n");
+ printpoints(stderr, newpts, "\n");
+ }
+
+ # Compute the chain code for P (the list of points).
+ compute_unit_chain_code(newpts);
+
+ return newpts;
+}
+
+# This implementation is due to an anonymous page on the net
+bresline(startpt: Penpoint, endpt: Penpoint, newpts: ref Stroke, j: int): int
+{
+ x0 := startpt.x;
+ x1 := endpt.x;
+ y0 := startpt.y;
+ y1 := endpt.y;
+
+ stepx := 1;
+ dx := x1-x0;
+ if(dx < 0){
+ dx = -dx;
+ stepx = -1;
+ }
+ dx <<= 1;
+ stepy := 1;
+ dy := y1-y0;
+ if(dy < 0){
+ dy = -dy;
+ stepy = -1;
+ }
+ dy <<= 1;
+ newpts.pts[j++] = (x0, y0, 0);
+ if(dx >= dy){
+ e := dy - (dx>>1);
+ while(x0 != x1){
+ if(e >= 0){
+ y0 += stepy;
+ e -= dx;
+ }
+ x0 += stepx;
+ e += dy;
+ newpts.pts[j++] = (x0, y0, 0);
+ }
+ }else{
+ e := dx - (dy>>1);
+ while(y0 != y1){
+ if(e >= 0){
+ x0 += stepx;
+ e -= dy;
+ }
+ y0 += stepy;
+ e += dx;
+ newpts.pts[j++] = (x0, y0, 0);
+ }
+ }
+ return j;
+}
+
+compute_chain_code(pts: ref Stroke)
+{
+ for(i := 0; i < pts.npts - 1; i++){
+ dx := pts.pts[i+1].x - pts.pts[i].x;
+ dy := pts.pts[i+1].y - pts.pts[i].y;
+ pts.pts[i].chaincode = (12 - quadr(likeatan(dy, dx))) % 8;
+ }
+}
+
+compute_unit_chain_code(pts: ref Stroke)
+{
+ for(i := 0; i < pts.npts - 1; i++){
+ dx := pts.pts[i+1].x - pts.pts[i].x;
+ dy := pts.pts[i+1].y - pts.pts[i].y;
+ pts.pts[i].chaincode = dctbl[dx+1][dy+1];
+ }
+}
+
+Stroke.regions(pts: self ref Stroke): ref Region
+{
+ # Allocate a 2 x pts.npts array for use in computing the (filtered) Angle set, A_n.
+ R := array[] of {0 to LP_FILTER_ITERS+1 => array[pts.npts] of int};
+ curr := R[0];
+
+ # Compute the Angle set, A, in the first element of array R.
+ # Values in R are in degrees, x 100.
+ curr[0] = 18000; # a_0
+ for(i := 1; i < pts.npts - 1; i++){
+ d_i := pts.pts[i].chaincode;
+ d_iminusone := pts.pts[i-1].chaincode;
+ if(d_iminusone < d_i)
+ d_iminusone += 8;
+ a_i := (d_iminusone - d_i) % 8;
+ # convert to degrees, x 100
+ curr[i] = ((12 - a_i) % 8) * 45 * 100;
+ }
+ curr[pts.npts-1] = 18000; # a_L-1
+
+ # Perform a number of filtering iterations.
+ next := R[1];
+ for(j := 0; j < LP_FILTER_ITERS; ){
+ for(i = 0; i < pts.npts; i++){
+ next[i] = 0;
+ for(k := i - LP_FILTER_WIDTH; k <= i + LP_FILTER_WIDTH; k++){
+ oldval: int;
+ if(k < 0 || k >= pts.npts)
+ oldval = 18000;
+ else
+ oldval = curr[k];
+ next[i] += oldval * lpfwts[k - (i - LP_FILTER_WIDTH)]; # overflow?
+ }
+ next[i] /= lpfconst;
+ }
+ j++;
+ curr = R[j];
+ next = R[j+1];
+ }
+
+ # Do final thresholding around PI.
+ # curr and next are set-up correctly at end of previous loop!
+ for(i = 0; i < pts.npts; i++)
+ if(abs(curr[i] - 18000) < LP_FILTER_THLD)
+ next[i] = 18000;
+ else
+ next[i] = curr[i];
+ curr = next;
+
+ # Debugging.
+ if(lidebug > 1){
+ for(i = 0; i < pts.npts; i++){
+ p := pts.pts[i];
+ sys->fprint(stderr, "%3d: (%d %d) %ud ",
+ i, p.x, p.y, p.chaincode);
+ for(j = 0; j < 2 + LP_FILTER_ITERS; j++)
+ sys->fprint(stderr, "%d ", R[j][i]);
+ sys->fprint(stderr, "\n");
+ }
+ }
+
+ # Do the region segmentation.
+ r := regions := ref Region(regiontype(curr[0]), 0, 0, nil);
+ for(i = 1; i < pts.npts; i++){
+ t := regiontype(curr[i]);
+ if(t != r.rtype){
+ r.end = i-1;
+ if(lidebug > 1)
+ sys->fprint(stderr, " (%d, %d) %d\n", r.start, r.end, r.rtype);
+ r.next = ref Region(t, i, 0, nil);
+ r = r.next;
+ }
+ }
+ r.end = i-1;
+ if(lidebug > 1)
+ sys->fprint(stderr, " (%d, %d), %d\n", r.start, r.end, r.rtype);
+
+ # Filter out convex/concave regions that are too short.
+ for(r = regions; r != nil; r = r.next)
+ if(r.rtype == Rplain){
+ while((nr := r.next) != nil && (nr.end - nr.start) < LP_FILTER_MIN){
+ # nr must not be plain, and it must be followed by a plain
+ # assert(nr.rtype != Rplain);
+ # assert(nr.next != nil && (nr.next).rtype == Rplain);
+ if(nr.next == nil){
+ sys->fprint(stderr, "recog: nr.next==nil\n"); # can't happen
+ break;
+ }
+ r.next = nr.next.next;
+ r.end = nr.next.end;
+ }
+ }
+
+ # Add-in pseudo-extremes.
+ for(r = regions; r != nil; r = r.next)
+ if(r.rtype == Rplain){
+ arclen := pts.pathlen(r.start, r.end);
+ dx := pts.pts[r.end].x - pts.pts[r.start].x;
+ dy := pts.pts[r.end].y - pts.pts[r.start].y;
+ chordlen := sqrt(100*100 * (dx*dx + dy*dy));
+ atcr := 0;
+ if(chordlen)
+ atcr = (100*arclen + chordlen/2) / chordlen;
+
+ if(lidebug)
+ sys->fprint(stderr, "%d, %d, %d\n", arclen, chordlen, atcr);
+
+ # Split region if necessary.
+ if(arclen >= PE_AL_THLD && atcr >= PE_ATCR_THLD){
+ mid := (r.start + r.end)/2;
+ end := r.end;
+ r.end = mid - 1;
+ r = r.next = ref Region(Rpseudo, mid, mid,
+ ref Region(Rplain, mid+1, end, r.next));
+ }
+ }
+
+ return regions;
+}
+
+regiontype(val: int): int
+{
+ if(val == 18000)
+ return Rplain;
+ if(val < 18000)
+ return Rconcave;
+ return Rconvex;
+}
+
+#
+# return the similarity of two strokes and,
+# if similar, the distance between them;
+# if dissimilar, the distance is MAXDIST)
+#
+score_stroke(a: ref Stroke, b: ref Stroke): (int, int)
+{
+ sim := compute_similarity(a, b);
+ if(sim < SIM_THLD)
+ return (sim, MAXDIST);
+ return (sim, compute_distance(a, b));
+}
+
+compute_similarity(A: ref Stroke, B: ref Stroke): int
+{
+ # A is the longer sequence, length N.
+ # B is the shorter sequence, length M.
+ if(A.npts < B.npts){
+ t := A; A = B; B = t;
+ }
+ N := A.npts;
+ M := B.npts;
+
+ # Allocate and initialize the Gain matrix, G.
+ # The size of G is M x (N + 1).
+ # Note that row 0 is unused.
+ # Similarities are x 10.
+ G := array[M] of array of int;
+ for(i := 1; i < M; i++){
+ G[i] = array[N+1] of int;
+ bcode := B.pts[i-1].chaincode;
+
+ G[i][0] = 0; # source column
+
+ for(j := 1; j < N; j++){
+ diff := abs(bcode - A.pts[j-1].chaincode);
+ if(diff > 4)
+ diff = 8 - diff; # symmetry
+ v := 0;
+ if(diff == 0)
+ v = 10;
+ else if(diff == 1)
+ v = 6;
+ G[i][j] = v;
+ }
+
+ G[i][N] = 0; # sink column
+ }
+
+ # Do the DP algorithm.
+ # Proceed in column order, from highest column to the lowest.
+ # Within each column, proceed from the highest row to the lowest.
+ # Skip the highest column.
+ for(j := N - 1; j >= 0; j--)
+ for(i = M - 1; i > 0; i--){
+ max := G[i][j + 1];
+ if(i < M-1){
+ t := G[i + 1][j + 1];
+ if(t > max)
+ max = t;
+ }
+ G[i][j] += max;
+ }
+
+ return (10*G[1][0] + (N-1)/2) / (N-1);
+}
+
+compute_distance(A: ref Stroke, B: ref Stroke): int
+{
+ # A is the longer sequence, length N.
+ # B is the shorter sequence, length M.
+ if(A.npts < B.npts){
+ t := A; A = B; B = t;
+ }
+ N := A.npts;
+ M := B.npts;
+
+ # Construct the helper vectors, BE and TE, which say for each column
+ # what are the ``bottom'' and ``top'' rows of interest.
+ BE := array[N+1] of int;
+ TE := array[N+1] of int;
+
+ for(j := 1; j <= N; j++){
+ bot := j + (M - DP_BAND);
+ if(bot > M) bot = M;
+ BE[j] = bot;
+
+ top := j - (N - DP_BAND);
+ if(top < 1) top = 1;
+ TE[j] = top;
+ }
+
+ # Allocate and initialize the Cost matrix, C.
+ # The size of C is (M + 1) x (N + 1).
+ # Note that row and column 0 are unused.
+ # Costs are x 100.
+ C := array[M+1] of array of int;
+ for(i := 1; i <= M; i++){
+ C[i] = array[N+1] of int;
+ bx := B.pts[i-1].x;
+ by := B.pts[i-1].y;
+
+ for(j = 1; j <= N; j++){
+ ax := A.pts[j-1].x;
+ ay := A.pts[j-1].y;
+ dx := bx - ax;
+ dy := by - ay;
+ dist := sqrt(10000 * (dx * dx + dy * dy));
+
+ C[i][j] = dist;
+ }
+ }
+
+ # Do the DP algorithm.
+ # Proceed in column order, from highest column to the lowest.
+ # Within each column, proceed from the highest row to the lowest.
+ for(j = N; j > 0; j--)
+ for(i = M; i > 0; i--){
+ min := MAXDIST;
+ if(i > BE[j] || i < TE[j] || (j == N && i == M))
+ continue;
+ if(j < N){
+ if(i >= TE[j+1]){
+ tmp := C[i][j+1];
+ if(tmp < min)
+ min = tmp;
+ }
+ if(i < M){
+ tmp := C[i+1][j+1];
+ if(tmp < min)
+ min = tmp;
+ }
+ }
+ if(i < BE[j]){
+ tmp := C[i+1][j];
+ if(tmp < min)
+ min = tmp;
+ }
+ C[i][j] += min;
+ }
+ return (C[1][1] + N / 2) / N; # dist
+}
+
+# Result is x 100.
+Stroke.length(s: self ref Stroke): int
+{
+ return s.pathlen(0, s.npts-1);
+}
+
+# Result is x 100.
+Stroke.pathlen(s: self ref Stroke, first: int, last: int): int
+{
+ l := 0;
+ for(i := first + 1; i <= last; i++){
+ dx := s.pts[i].x - s.pts[i-1].x;
+ dy := s.pts[i].y - s.pts[i-1].y;
+ l += sqrt(100*100 * (dx*dx + dy*dy));
+ }
+ return l;
+}
+
+# Note that this does NOT update points.xrange and points.yrange!
+Stroke.filter(s: self ref Stroke): ref Stroke
+{
+ pts := array[s.npts] of Penpoint;
+ pts[0] = s.pts[0];
+ npts := 1;
+ for(i := 1; i < s.npts; i++){
+ j := npts - 1;
+ dx := s.pts[i].x - pts[j].x;
+ dy := s.pts[i].y - pts[j].y;
+ magsq := dx * dx + dy * dy;
+ if(magsq >= DIST_SQ_THRESHOLD){
+ pts[npts] = s.pts[i];
+ npts++;
+ }
+ }
+ return ref Stroke(npts, pts[0:npts], 0, 0);
+}
+
+abs(a: int): int
+{
+ if(a < 0)
+ return -a;
+ return a;
+}
+
+# Code from Joseph Hall (jnhall@sat.mot.com).
+sqrt(n: int): int
+{
+ nn := n;
+ k0 := 2;
+ for(i := n; i > 0; i >>= 2)
+ k0 <<= 1;
+ nn <<= 2;
+ k1: int;
+ for(;;){
+ k1 = (nn / k0 + k0) >> 1;
+ if(((k0 ^ k1) & ~1) == 0)
+ break;
+ k0 = k1;
+ }
+ return (k1 + 1) >> 1;
+}
+
+# Helper routines from Mark Hayter.
+likeatan(tantop: int, tanbot: int): int
+{
+ # Use tan(theta)=top/bot -. order for t
+ # t in range 0..16r40000
+
+ if(tantop == 0 && tantop == 0)
+ return 0;
+ t := (tantop << 16) / (abs(tantop) + abs(tanbot));
+ if(tanbot < 0)
+ t = 16r20000 - t;
+ else if(tantop < 0)
+ t = 16r40000 + t;
+ return t;
+}
+
+quadr(t: int): int
+{
+ return (8 - (((t + 16r4000) >> 15) & 7)) & 7;
+}
+
+printpoints(fd: ref Sys->FD, pts: ref Stroke, sep: string)
+{
+ for(j := 0; j < pts.npts; j++){
+ p := pts.pts[j];
+ sys->fprint(fd, " (%d %d) %ud%s", p.x, p.y, pts.pts[j].chaincode, sep);
+ }
+}
diff --git a/appl/lib/strokes/writestrokes.b b/appl/lib/strokes/writestrokes.b
new file mode 100644
index 00000000..f5232466
--- /dev/null
+++ b/appl/lib/strokes/writestrokes.b
@@ -0,0 +1,68 @@
+implement Writestrokes;
+
+#
+# write structures to classifier files
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "strokes.m";
+ strokes: Strokes;
+ Penpoint, Stroke: import strokes;
+
+init(s: Strokes)
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ strokes = s;
+}
+
+write_examples(fd: ref Sys->FD, names: array of string, examples: array of list of ref Stroke): string
+{
+ fp := bufio->fopen(fd, Bufio->OWRITE);
+ nclass := len names;
+ fp.puts(sys->sprint("%d\n", nclass));
+ for(i := 0; i < nclass; i++){
+ exl := examples[i];
+ fp.puts(sys->sprint("%d %s\n", len exl, names[i]));
+ for(; exl != nil; exl = tl exl){
+ putpoints(fp, hd exl);
+ fp.putc('\n');
+ }
+ }
+ if(fp.flush() == Bufio->ERROR)
+ return sys->sprint("write error: %r");
+ fp.close();
+ return nil;
+}
+
+write_digest(fd: ref Sys->FD, cnames: array of string, dompts: array of ref Stroke): string
+{
+ fp := bufio->fopen(fd, Bufio->OWRITE);
+ n := len cnames;
+ for(i := 0; i < n; i++){
+ d := dompts[i];
+ npts := d.npts;
+ fp.puts(cnames[i]);
+ putpoints(fp, d);
+ fp.putc('\n');
+ }
+ if(fp.flush() == Bufio->ERROR)
+ return sys->sprint("write error: %r");
+ fp.close();
+ return nil;
+}
+
+putpoints(fp: ref Iobuf, d: ref Stroke)
+{
+ fp.puts(sys->sprint(" %d", d.npts));
+ for(j := 0; j < d.npts; j++){
+ p := d.pts[j];
+ fp.puts(sys->sprint(" %d %d", p.x, p.y));
+ }
+}
diff --git a/appl/lib/styx.b b/appl/lib/styx.b
new file mode 100644
index 00000000..f4534c6c
--- /dev/null
+++ b/appl/lib/styx.b
@@ -0,0 +1,934 @@
+implement Styx;
+
+include "sys.m";
+ sys: Sys;
+
+include "styx.m";
+
+STR: con BIT16SZ; # string length
+TAG: con BIT16SZ;
+FID: con BIT32SZ;
+QID: con BIT8SZ+BIT32SZ+BIT64SZ;
+LEN: con BIT16SZ; # stat and qid array lengths
+COUNT: con BIT32SZ;
+OFFSET: con BIT64SZ;
+
+H: con BIT32SZ+BIT8SZ+BIT16SZ; # minimum header length: size[4] type tag[2]
+
+#
+# the following array could be shorter if it were indexed by (type-Tversion)
+#
+hdrlen := array[Tmax] of
+{
+Tversion => H+COUNT+STR, # size[4] Tversion tag[2] msize[4] version[s]
+Rversion => H+COUNT+STR, # size[4] Rversion tag[2] msize[4] version[s]
+
+Tauth => H+FID+STR+STR, # size[4] Tauth tag[2] afid[4] uname[s] aname[s]
+Rauth => H+QID, # size[4] Rauth tag[2] aqid[13]
+
+Rerror => H+STR, # size[4] Rerror tag[2] ename[s]
+
+Tflush => H+TAG, # size[4] Tflush tag[2] oldtag[2]
+Rflush => H, # size[4] Rflush tag[2]
+
+Tattach => H+FID+FID+STR+STR, # size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s]
+Rattach => H+QID, # size[4] Rattach tag[2] qid[13]
+
+Twalk => H+FID+FID+LEN, # size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])
+Rwalk => H+LEN, # size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13])
+
+Topen => H+FID+BIT8SZ, # size[4] Topen tag[2] fid[4] mode[1]
+Ropen => H+QID+COUNT, # size[4] Ropen tag[2] qid[13] iounit[4]
+
+Tcreate => H+FID+STR+BIT32SZ+BIT8SZ, # size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1]
+Rcreate => H+QID+COUNT, # size[4] Rcreate tag[2] qid[13] iounit[4]
+
+Tread => H+FID+OFFSET+COUNT, # size[4] Tread tag[2] fid[4] offset[8] count[4]
+Rread => H+COUNT, # size[4] Rread tag[2] count[4] data[count]
+
+Twrite => H+FID+OFFSET+COUNT, # size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count]
+Rwrite => H+COUNT, # size[4] Rwrite tag[2] count[4]
+
+Tclunk => H+FID, # size[4] Tclunk tag[2] fid[4]
+Rclunk => H, # size[4] Rclunk tag[2]
+
+Tremove => H+FID, # size[4] Tremove tag[2] fid[4]
+Rremove => H, # size[4] Rremove tag[2]
+
+Tstat => H+FID, # size[4] Tstat tag[2] fid[4]
+Rstat => H+LEN, # size[4] Rstat tag[2] stat[n]
+
+Twstat => H+FID+LEN, # size[4] Twstat tag[2] fid[4] stat[n]
+Rwstat => H, # size[4] Rwstat tag[2]
+};
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+utflen(s: string): int
+{
+ # the domain is 16-bit unicode only, which is all that Inferno now implements
+ n := l := len s;
+ for(i:=0; i<l; i++)
+ if((c := s[i]) > 16r7F){
+ n++;
+ if(c > 16r7FF)
+ n++;
+ }
+ return n;
+}
+
+packdirsize(d: Sys->Dir): int
+{
+ return STATFIXLEN+utflen(d.name)+utflen(d.uid)+utflen(d.gid)+utflen(d.muid);
+}
+
+packdir(f: Sys->Dir): array of byte
+{
+ ds := packdirsize(f);
+ a := array[ds] of byte;
+ # size[2]
+ a[0] = byte (ds-LEN);
+ a[1] = byte ((ds-LEN)>>8);
+ # type[2]
+ a[2] = byte f.dtype;
+ a[3] = byte (f.dtype>>8);
+ # dev[4]
+ a[4] = byte f.dev;
+ a[5] = byte (f.dev>>8);
+ a[6] = byte (f.dev>>16);
+ a[7] = byte (f.dev>>24);
+ # qid.type[1]
+ # qid.vers[4]
+ # qid.path[8]
+ pqid(a, 8, f.qid);
+ # mode[4]
+ a[21] = byte f.mode;
+ a[22] = byte (f.mode>>8);
+ a[23] = byte (f.mode>>16);
+ a[24] = byte (f.mode>>24);
+ # atime[4]
+ a[25] = byte f.atime;
+ a[26] = byte (f.atime>>8);
+ a[27] = byte (f.atime>>16);
+ a[28] = byte (f.atime>>24);
+ # mtime[4]
+ a[29] = byte f.mtime;
+ a[30] = byte (f.mtime>>8);
+ a[31] = byte (f.mtime>>16);
+ a[32] = byte (f.mtime>>24);
+ # length[8]
+ p64(a, 33, big f.length);
+ # name[s]
+ i := pstring(a, 33+BIT64SZ, f.name);
+ i = pstring(a, i, f.uid);
+ i = pstring(a, i, f.gid);
+ i = pstring(a, i, f.muid);
+ if(i != len a)
+ raise "assertion: Styx->packdir: bad count"; # can't happen unless packedsize is wrong
+ return a;
+}
+
+pqid(a: array of byte, o: int, q: Sys->Qid): int
+{
+ a[o] = byte q.qtype;
+ v := q.vers;
+ a[o+1] = byte v;
+ a[o+2] = byte (v>>8);
+ a[o+3] = byte (v>>16);
+ a[o+4] = byte (v>>24);
+ v = int q.path;
+ a[o+5] = byte v;
+ a[o+6] = byte (v>>8);
+ a[o+7] = byte (v>>16);
+ a[o+8] = byte (v>>24);
+ v = int (q.path >> 32);
+ a[o+9] = byte v;
+ a[o+10] = byte (v>>8);
+ a[o+11] = byte (v>>16);
+ a[o+12] = byte (v>>24);
+ return o+QID;
+}
+
+pstring(a: array of byte, o: int, s: string): int
+{
+ sa := array of byte s; # could do conversion ourselves
+ n := len sa;
+ a[o] = byte n;
+ a[o+1] = byte (n>>8);
+ a[o+2:] = sa;
+ return o+LEN+n;
+}
+
+p32(a: array of byte, o: int, v: int): int
+{
+ a[o] = byte v;
+ a[o+1] = byte (v>>8);
+ a[o+2] = byte (v>>16);
+ a[o+3] = byte (v>>24);
+ return o+BIT32SZ;
+}
+
+p64(a: array of byte, o: int, b: big): int
+{
+ i := int b;
+ a[o] = byte i;
+ a[o+1] = byte (i>>8);
+ a[o+2] = byte (i>>16);
+ a[o+3] = byte (i>>24);
+ i = int (b>>32);
+ a[o+4] = byte i;
+ a[o+5] = byte (i>>8);
+ a[o+6] = byte (i>>16);
+ a[o+7] = byte (i>>24);
+ return o+BIT64SZ;
+}
+
+unpackdir(a: array of byte): (int, Sys->Dir)
+{
+ dir: Sys->Dir;
+
+ if(len a < STATFIXLEN)
+ return (0, dir);
+ # size[2]
+ sz := ((int a[1] << 8) | int a[0])+LEN; # bytes this packed dir should occupy
+ if(len a < sz)
+ return (0, dir);
+ # type[2]
+ dir.dtype = (int a[3]<<8) | int a[2];
+ # dev[4]
+ dir.dev = (((((int a[7] << 8) | int a[6]) << 8) | int a[5]) << 8) | int a[4];
+ # qid.type[1]
+ # qid.vers[4]
+ # qid.path[8]
+ dir.qid = gqid(a, 8);
+ # mode[4]
+ dir.mode = (((((int a[24] << 8) | int a[23]) << 8) | int a[22]) << 8) | int a[21];
+ # atime[4]
+ dir.atime = (((((int a[28] << 8) | int a[27]) << 8) | int a[26]) << 8) | int a[25];
+ # mtime[4]
+ dir.mtime = (((((int a[32] << 8) | int a[31]) << 8) | int a[30]) << 8) | int a[29];
+ # length[8]
+ v0 := (((((int a[36] << 8) | int a[35]) << 8) | int a[34]) << 8) | int a[33];
+ v1 := (((((int a[40] << 8) | int a[39]) << 8) | int a[38]) << 8) | int a[37];
+ dir.length = (big v1 << 32) | (big v0 & 16rFFFFFFFF);
+ # name[s], uid[s], gid[s], muid[s]
+ i: int;
+ (dir.name, i) = gstring(a, 41);
+ (dir.uid, i) = gstring(a, i);
+ (dir.gid, i) = gstring(a, i);
+ (dir.muid, i) = gstring(a, i);
+ if(i != sz)
+ return (0, dir);
+ return (i, dir);
+}
+
+gqid(f: array of byte, i: int): Sys->Qid
+{
+ qtype := int f[i];
+ vers := (((((int f[i+4] << 8) | int f[i+3]) << 8) | int f[i+2]) << 8) | int f[i+1];
+ i += BIT8SZ+BIT32SZ;
+ path0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
+ i += BIT32SZ;
+ path1 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
+ path := (big path1 << 32) | (big path0 & 16rFFFFFFFF);
+ return (path, vers, qtype);
+}
+
+g32(f: array of byte, i: int): int
+{
+ return (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
+}
+
+g64(f: array of byte, i: int): big
+{
+ b0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
+ b1 := (((((int f[i+7] << 8) | int f[i+6]) << 8) | int f[i+5]) << 8) | int f[i+4];
+ return (big b1 << 32) | (big b0 & 16rFFFFFFFF);
+}
+
+gstring(a: array of byte, o: int): (string, int)
+{
+ if(o < 0 || o+STR > len a)
+ return (nil, -1);
+ l := (int a[o+1] << 8) | int a[o];
+ o += STR;
+ e := o+l;
+ if(e > len a)
+ return (nil, -1);
+ return (string a[o:e], e);
+}
+
+ttag2type := array[] of {
+tagof Tmsg.Readerror => 0,
+tagof Tmsg.Version => Tversion,
+tagof Tmsg.Auth => Tauth,
+tagof Tmsg.Attach => Tattach,
+tagof Tmsg.Flush => Tflush,
+tagof Tmsg.Walk => Twalk,
+tagof Tmsg.Open => Topen,
+tagof Tmsg.Create => Tcreate,
+tagof Tmsg.Read => Tread,
+tagof Tmsg.Write => Twrite,
+tagof Tmsg.Clunk => Tclunk,
+tagof Tmsg.Stat => Tstat,
+tagof Tmsg.Remove => Tremove,
+tagof Tmsg.Wstat => Twstat,
+};
+
+Tmsg.mtype(t: self ref Tmsg): int
+{
+ return ttag2type[tagof t];
+}
+
+Tmsg.packedsize(t: self ref Tmsg): int
+{
+ mtype := ttag2type[tagof t];
+ if(mtype <= 0)
+ return 0;
+ ml := hdrlen[mtype];
+ pick m := t {
+ Version =>
+ ml += utflen(m.version);
+ Auth =>
+ ml += utflen(m.uname)+utflen(m.aname);
+ Attach =>
+ ml += utflen(m.uname)+utflen(m.aname);
+ Walk =>
+ for(i:=0; i<len m.names; i++)
+ ml += STR+utflen(m.names[i]);
+ Create =>
+ ml += utflen(m.name);
+ Write =>
+ ml += len m.data;
+ Wstat =>
+ ml += packdirsize(m.stat);
+ }
+ return ml;
+}
+
+Tmsg.pack(t: self ref Tmsg): array of byte
+{
+ if(t == nil)
+ return nil;
+ ds := t.packedsize();
+ if(ds <= 0)
+ return nil;
+ d := array[ds] of byte;
+ d[0] = byte ds;
+ d[1] = byte (ds>>8);
+ d[2] = byte (ds>>16);
+ d[3] = byte (ds>>24);
+ d[4] = byte ttag2type[tagof t];
+ d[5] = byte t.tag;
+ d[6] = byte (t.tag >> 8);
+ pick m := t {
+ Version =>
+ p32(d, H, m.msize);
+ pstring(d, H+COUNT, m.version);
+ Auth =>
+ p32(d, H, m.afid);
+ o := pstring(d, H+FID, m.uname);
+ pstring(d, o, m.aname);
+ Flush =>
+ v := m.oldtag;
+ d[H] = byte v;
+ d[H+1] = byte (v>>8);
+ Attach =>
+ p32(d, H, m.fid);
+ p32(d, H+FID, m.afid);
+ o := pstring(d, H+2*FID, m.uname);
+ pstring(d, o, m.aname);
+ Walk =>
+ d[H] = byte m.fid;
+ d[H+1] = byte (m.fid>>8);
+ d[H+2] = byte (m.fid>>16);
+ d[H+3] = byte (m.fid>>24);
+ d[H+FID] = byte m.newfid;
+ d[H+FID+1] = byte (m.newfid>>8);
+ d[H+FID+2] = byte (m.newfid>>16);
+ d[H+FID+3] = byte (m.newfid>>24);
+ n := len m.names;
+ d[H+2*FID] = byte n;
+ d[H+2*FID+1] = byte (n>>8);
+ o := H+2*FID+LEN;
+ for(i := 0; i < n; i++)
+ o = pstring(d, o, m.names[i]);
+ Open =>
+ p32(d, H, m.fid);
+ d[H+FID] = byte m.mode;
+ Create =>
+ p32(d, H, m.fid);
+ o := pstring(d, H+FID, m.name);
+ p32(d, o, m.perm);
+ d[o+BIT32SZ] = byte m.mode;
+ Read =>
+ p32(d, H, m.fid);
+ p64(d, H+FID, m.offset);
+ p32(d, H+FID+OFFSET, m.count);
+ Write =>
+ p32(d, H, m.fid);
+ p64(d, H+FID, m.offset);
+ n := len m.data;
+ p32(d, H+FID+OFFSET, n);
+ d[H+FID+OFFSET+COUNT:] = m.data;
+ Clunk or Remove or Stat =>
+ p32(d, H, m.fid);
+ Wstat =>
+ p32(d, H, m.fid);
+ stat := packdir(m.stat);
+ n := len stat;
+ d[H+FID] = byte n;
+ d[H+FID+1] = byte (n>>8);
+ d[H+FID+LEN:] = stat;
+ * =>
+ raise sys->sprint("assertion: Styx->Tmsg.pack: bad tag: %d", tagof t);
+ }
+ return d;
+}
+
+Tmsg.unpack(f: array of byte): (int, ref Tmsg)
+{
+ if(len f < H)
+ return (0, nil);
+ size := (int f[1] << 8) | int f[0];
+ size |= ((int f[3] << 8) | int f[2]) << 16;
+ if(len f != size){
+ if(len f < size)
+ return (0, nil); # need more data
+ f = f[0:size]; # trim to exact length
+ }
+ mtype := int f[4];
+ if(mtype >= len hdrlen || (mtype&1) != 0 || size < hdrlen[mtype])
+ return (-1, nil);
+
+ tag := (int f[6] << 8) | int f[5];
+ fid := 0;
+ if(hdrlen[mtype] >= H+FID)
+ fid = g32(f, H); # fid is always in same place: extract it once for all if there
+
+ # return out of each case body for a legal message;
+ # break out of the case for an illegal one
+
+Decode:
+ case mtype {
+ * =>
+ sys->print("styx: Tmsg.unpack: bad type %d\n", mtype);
+ Tversion =>
+ msize := fid;
+ (version, o) := gstring(f, H+COUNT);
+ if(o <= 0)
+ break;
+ return (o, ref Tmsg.Version(tag, msize, version));
+ Tauth =>
+ (uname, o1) := gstring(f, H+FID);
+ (aname, o2) := gstring(f, o1);
+ if(o2 <= 0)
+ break;
+ return (o2, ref Tmsg.Auth(tag, fid, uname, aname));
+ Tflush =>
+ oldtag := (int f[H+1] << 8) | int f[H];
+ return (H+TAG, ref Tmsg.Flush(tag, oldtag));
+ Tattach =>
+ afid := g32(f, H+FID);
+ (uname, o1) := gstring(f, H+2*FID);
+ (aname, o2) := gstring(f, o1);
+ if(o2 <= 0)
+ break;
+ return (o2, ref Tmsg.Attach(tag, fid, afid, uname, aname));
+ Twalk =>
+ newfid := g32(f, H+FID);
+ n := (int f[H+2*FID+1] << 8) | int f[H+2*FID];
+ if(n > MAXWELEM)
+ break;
+ o := H+2*FID+LEN;
+ names: array of string = nil;
+ if(n > 0){
+ names = array[n] of string;
+ for(i:=0; i<n; i++){
+ (names[i], o) = gstring(f, o);
+ if(o <= 0)
+ break Decode;
+ }
+ }
+ return (o, ref Tmsg.Walk(tag, fid, newfid, names));
+ Topen =>
+ return (H+FID+BIT8SZ, ref Tmsg.Open(tag, fid, int f[H+FID]));
+ Tcreate =>
+ (name, o) := gstring(f, H+FID);
+ if(o <= 0 || o+BIT32SZ+BIT8SZ > len f)
+ break;
+ perm := g32(f, o);
+ o += BIT32SZ;
+ mode := int f[o++];
+ return (o, ref Tmsg.Create(tag, fid, name, perm, mode));
+ Tread =>
+ offset := g64(f, H+FID);
+ count := g32(f, H+FID+OFFSET);
+ return (H+FID+OFFSET+COUNT, ref Tmsg.Read(tag, fid, offset, count));
+ Twrite =>
+ offset := g64(f, H+FID);
+ count := g32(f, H+FID+OFFSET);
+ O: con H+FID+OFFSET+COUNT;
+ if(count > len f-O)
+ break;
+ data := f[O:O+count];
+ return (O+count, ref Tmsg.Write(tag, fid, offset, data));
+ Tclunk =>
+ return (H+FID, ref Tmsg.Clunk(tag, fid));
+ Tremove =>
+ return (H+FID, ref Tmsg.Remove(tag, fid));
+ Tstat =>
+ return (H+FID, ref Tmsg.Stat(tag, fid));
+ Twstat =>
+ n := int (f[H+FID+1]<<8) | int f[H+FID];
+ if(len f < H+FID+LEN+n)
+ break;
+ (ds, stat) := unpackdir(f[H+FID+LEN:]);
+ if(ds != n){
+ sys->print("Styx->Tmsg.unpack: wstat count: %d/%d\n", ds, n); # temporary
+ break;
+ }
+ return (H+FID+LEN+n, ref Tmsg.Wstat(tag, fid, stat));
+ }
+ return (-1, nil); # illegal
+}
+
+tmsgname := array[] of {
+tagof Tmsg.Readerror => "Readerror",
+tagof Tmsg.Version => "Version",
+tagof Tmsg.Auth => "Auth",
+tagof Tmsg.Attach => "Attach",
+tagof Tmsg.Flush => "Flush",
+tagof Tmsg.Walk => "Walk",
+tagof Tmsg.Open => "Open",
+tagof Tmsg.Create => "Create",
+tagof Tmsg.Read => "Read",
+tagof Tmsg.Write => "Write",
+tagof Tmsg.Clunk => "Clunk",
+tagof Tmsg.Stat => "Stat",
+tagof Tmsg.Remove => "Remove",
+tagof Tmsg.Wstat => "Wstat",
+};
+
+Tmsg.text(t: self ref Tmsg): string
+{
+ if(t == nil)
+ return "nil";
+ s := sys->sprint("Tmsg.%s(%ud", tmsgname[tagof t], t.tag);
+ pick m:= t {
+ * =>
+ return s + ",ILLEGAL)";
+ Readerror =>
+ return s + sys->sprint(",\"%s\")", m.error);
+ Version =>
+ return s + sys->sprint(",%d,\"%s\")", m.msize, m.version);
+ Auth =>
+ return s + sys->sprint(",%ud,\"%s\",\"%s\")", m.afid, m.uname, m.aname);
+ Flush =>
+ return s + sys->sprint(",%ud)", m.oldtag);
+ Attach =>
+ return s + sys->sprint(",%ud,%ud,\"%s\",\"%s\")", m.fid, m.afid, m.uname, m.aname);
+ Walk =>
+ s += sys->sprint(",%ud,%ud", m.fid, m.newfid);
+ if(len m.names != 0){
+ s += ",array[] of {";
+ for(i := 0; i < len m.names; i++){
+ c := ",";
+ if(i == 0)
+ c = "";
+ s += sys->sprint("%s\"%s\"", c, m.names[i]);
+ }
+ s += "}";
+ }else
+ s += ",nil";
+ return s + ")";
+ Open =>
+ return s + sys->sprint(",%ud,%d)", m.fid, m.mode);
+ Create =>
+ return s + sys->sprint(",%ud,\"%s\",8r%uo,%d)", m.fid, m.name, m.perm, m.mode);
+ Read =>
+ return s + sys->sprint(",%ud,%bd,%ud)", m.fid, m.offset, m.count);
+ Write =>
+ return s + sys->sprint(",%ud,%bd,array[%d] of byte)", m.fid, m.offset, len m.data);
+ Clunk or
+ Remove or
+ Stat =>
+ return s + sys->sprint(",%ud)", m.fid);
+ Wstat =>
+ return s + sys->sprint(",%ud,%s)", m.fid, dir2text(m.stat));
+ }
+}
+
+Tmsg.read(fd: ref Sys->FD, msglim: int): ref Tmsg
+{
+ (msg, err) := readmsg(fd, msglim);
+ if(err != nil)
+ return ref Tmsg.Readerror(0, err);
+ if(msg == nil)
+ return nil;
+ (nil, m) := Tmsg.unpack(msg);
+ if(m == nil)
+ return ref Tmsg.Readerror(0, "bad Styx T-message format");
+ return m;
+}
+
+rtag2type := array[] of {
+tagof Rmsg.Version => Rversion,
+tagof Rmsg.Auth => Rauth,
+tagof Rmsg.Error => Rerror,
+tagof Rmsg.Flush => Rflush,
+tagof Rmsg.Attach => Rattach,
+tagof Rmsg.Walk => Rwalk,
+tagof Rmsg.Open => Ropen,
+tagof Rmsg.Create => Rcreate,
+tagof Rmsg.Read => Rread,
+tagof Rmsg.Write => Rwrite,
+tagof Rmsg.Clunk => Rclunk,
+tagof Rmsg.Remove => Rremove,
+tagof Rmsg.Stat => Rstat,
+tagof Rmsg.Wstat => Rwstat,
+};
+
+Rmsg.mtype(r: self ref Rmsg): int
+{
+ return rtag2type[tagof r];
+}
+
+Rmsg.packedsize(r: self ref Rmsg): int
+{
+ mtype := rtag2type[tagof r];
+ if(mtype <= 0)
+ return 0;
+ ml := hdrlen[mtype];
+ pick m := r {
+ Version =>
+ ml += utflen(m.version);
+ Error =>
+ ml += utflen(m.ename);
+ Walk =>
+ ml += QID*len m.qids;
+ Read =>
+ ml += len m.data;
+ Stat =>
+ ml += packdirsize(m.stat);
+ }
+ return ml;
+}
+
+Rmsg.pack(r: self ref Rmsg): array of byte
+{
+ if(r == nil)
+ return nil;
+ ps := r.packedsize();
+ if(ps <= 0)
+ return nil;
+ d := array[ps] of byte;
+ d[0] = byte ps;
+ d[1] = byte (ps>>8);
+ d[2] = byte (ps>>16);
+ d[3] = byte (ps>>24);
+ d[4] = byte rtag2type[tagof r];
+ d[5] = byte r.tag;
+ d[6] = byte (r.tag >> 8);
+ pick m := r {
+ Version =>
+ p32(d, H, m.msize);
+ pstring(d, H+BIT32SZ, m.version);
+ Auth =>
+ pqid(d, H, m.aqid);
+ Flush or
+ Clunk or
+ Remove or
+ Wstat =>
+ ; # nothing more required
+ Error =>
+ pstring(d, H, m.ename);
+ Attach =>
+ pqid(d, H, m.qid);
+ Walk =>
+ n := len m.qids;
+ d[H] = byte n;
+ d[H+1] = byte (n>>8);
+ o := H+LEN;
+ for(i:=0; i<n; i++){
+ pqid(d, o, m.qids[i]);
+ o += QID;
+ }
+ Create or
+ Open =>
+ pqid(d, H, m.qid);
+ p32(d, H+QID, m.iounit);
+ Read =>
+ v := len m.data;
+ d[H] = byte v;
+ d[H+1] = byte (v>>8);
+ d[H+2] = byte (v>>16);
+ d[H+3] = byte (v>>24);
+ d[H+4:] = m.data;
+ Write =>
+ v := m.count;
+ d[H] = byte v;
+ d[H+1] = byte (v>>8);
+ d[H+2] = byte (v>>16);
+ d[H+3] = byte (v>>24);
+ Stat =>
+ stat := packdir(m.stat);
+ v := len stat;
+ d[H] = byte v;
+ d[H+1] = byte (v>>8);
+ d[H+2:] = stat; # should avoid copy?
+ * =>
+ raise sys->sprint("assertion: Styx->Rmsg.pack: missed case: tag %d", tagof r);
+ }
+ return d;
+}
+
+Rmsg.unpack(f: array of byte): (int, ref Rmsg)
+{
+ if(len f < H)
+ return (0, nil);
+ size := (int f[1] << 8) | int f[0];
+ size |= ((int f[3] << 8) | int f[2]) << 16; # size includes itself
+ if(len f != size){
+ if(len f < size)
+ return (0, nil); # need more data
+ f = f[0:size]; # trim to exact length
+ }
+ mtype := int f[4];
+ if(mtype >= len hdrlen || (mtype&1) == 0 || size < hdrlen[mtype])
+ return (-1, nil);
+
+ tag := (int f[6] << 8) | int f[5];
+
+ # return out of each case body for a legal message;
+ # break out of the case for an illegal one
+
+ case mtype {
+ * =>
+ sys->print("Styx->Rmsg.unpack: bad type %d\n", mtype); # temporary
+ Rversion =>
+ msize := g32(f, H);
+ (version, o) := gstring(f, H+BIT32SZ);
+ if(o <= 0)
+ break;
+ return (o, ref Rmsg.Version(tag, msize, version));
+ Rauth =>
+ return (H+QID, ref Rmsg.Auth(tag, gqid(f, H)));
+ Rflush =>
+ return (H, ref Rmsg.Flush(tag));
+ Rerror =>
+ (ename, o) := gstring(f, H);
+ if(o <= 0)
+ break;
+ return (o, ref Rmsg.Error(tag, ename));
+ Rclunk =>
+ return (H, ref Rmsg.Clunk(tag));
+ Rremove =>
+ return (H, ref Rmsg.Remove(tag));
+ Rwstat=>
+ return (H, ref Rmsg.Wstat(tag));
+ Rattach =>
+ return (H+QID, ref Rmsg.Attach(tag, gqid(f, H)));
+ Rwalk =>
+ nqid := (int f[H+1] << 8) | int f[H];
+ if(len f < H+LEN+nqid*QID)
+ break;
+ o := H+LEN;
+ qids := array[nqid] of Sys->Qid;
+ for(i:=0; i<nqid; i++){
+ qids[i] = gqid(f, o);
+ o += QID;
+ }
+ return (o, ref Rmsg.Walk(tag, qids));
+ Ropen =>
+ return (H+QID+COUNT, ref Rmsg.Open(tag, gqid(f, H), g32(f, H+QID)));
+ Rcreate=>
+ return (H+QID+COUNT, ref Rmsg.Create(tag, gqid(f, H), g32(f, H+QID)));
+ Rread =>
+ count := g32(f, H);
+ if(len f < H+COUNT+count)
+ break;
+ data := f[H+COUNT:H+COUNT+count];
+ return (H+COUNT+count, ref Rmsg.Read(tag, data));
+ Rwrite =>
+ return (H+COUNT, ref Rmsg.Write(tag, g32(f, H)));
+ Rstat =>
+ n := (int f[H+1] << 8) | int f[H];
+ if(len f < H+LEN+n)
+ break;
+ (ds, d) := unpackdir(f[H+LEN:]);
+ if(ds <= 0)
+ break;
+ if(ds != n){
+ sys->print("Styx->Rmsg.unpack: stat count: %d/%d\n", ds, n); # temporary
+ break;
+ }
+ return (H+LEN+n, ref Rmsg.Stat(tag, d));
+ }
+ return (-1, nil); # illegal
+}
+
+rmsgname := array[] of {
+tagof Rmsg.Version => "Version",
+tagof Rmsg.Auth => "Auth",
+tagof Rmsg.Attach => "Attach",
+tagof Rmsg.Error => "Error",
+tagof Rmsg.Flush => "Flush",
+tagof Rmsg.Walk => "Walk",
+tagof Rmsg.Create => "Create",
+tagof Rmsg.Open => "Open",
+tagof Rmsg.Read => "Read",
+tagof Rmsg.Write => "Write",
+tagof Rmsg.Clunk => "Clunk",
+tagof Rmsg.Remove => "Remove",
+tagof Rmsg.Stat => "Stat",
+tagof Rmsg.Wstat => "Wstat",
+};
+
+Rmsg.text(r: self ref Rmsg): string
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ if(r == nil)
+ return "nil";
+ s := sys->sprint("Rmsg.%s(%ud", rmsgname[tagof r], r.tag);
+ pick m := r {
+ * =>
+ return s + "ERROR)";
+ Readerror =>
+ return s + sys->sprint(",\"%s\")", m.error);
+ Version =>
+ return s + sys->sprint(",%d,\"%s\")", m.msize, m.version);
+ Auth =>
+ return s+sys->sprint(",%s)", qid2text(m.aqid));
+ Error =>
+ return s+sys->sprint(",\"%s\")", m.ename);
+ Flush or
+ Clunk or
+ Remove or
+ Wstat =>
+ return s+")";
+ Attach =>
+ return s+sys->sprint(",%s)", qid2text(m.qid));
+ Walk =>
+ s += ",array[] of {";
+ for(i := 0; i < len m.qids; i++){
+ c := "";
+ if(i != 0)
+ c = ",";
+ s += sys->sprint("%s%s", c, qid2text(m.qids[i]));
+ }
+ return s+"})";
+ Create or
+ Open =>
+ return s+sys->sprint(",%s,%d)", qid2text(m.qid), m.iounit);
+ Read =>
+ return s+sys->sprint(",array[%d] of byte)", len m.data);
+ Write =>
+ return s+sys->sprint(",%d)", m.count);
+ Stat =>
+ return s+sys->sprint(",%s)", dir2text(m.stat));
+ }
+}
+
+Rmsg.read(fd: ref Sys->FD, msglim: int): ref Rmsg
+{
+ (msg, err) := readmsg(fd, msglim);
+ if(err != nil)
+ return ref Rmsg.Readerror(0, err);
+ if(msg == nil)
+ return nil;
+ (nil, m) := Rmsg.unpack(msg);
+ if(m == nil)
+ return ref Rmsg.Readerror(0, "bad Styx R-message format");
+ return m;
+}
+
+dir2text(d: Sys->Dir): string
+{
+ return sys->sprint("Dir(\"%s\",\"%s\",\"%s\",%s,8r%uo,%d,%d,%bd,16r%ux,%d)",
+ d.name, d.uid, d.gid, qid2text(d.qid), d.mode, d.atime, d.mtime, d.length, d.dtype, d.dev);
+}
+
+qid2text(q: Sys->Qid): string
+{
+ return sys->sprint("Qid(16r%ubx,%d,16r%.2ux)", q.path, q.vers, q.qtype);
+}
+
+readmsg(fd: ref Sys->FD, msglim: int): (array of byte, string)
+{
+ if(msglim <= 0)
+ msglim = MAXRPC;
+ sbuf := array[BIT32SZ] of byte;
+ if((n := readn(fd, sbuf, BIT32SZ)) != BIT32SZ){
+ if(n == 0)
+ return (nil, nil);
+ return (nil, sys->sprint("%r"));
+ }
+ ml := (int sbuf[1] << 8) | int sbuf[0];
+ ml |= ((int sbuf[3] << 8) | int sbuf[2]) << 16;
+ if(ml <= BIT32SZ)
+ return (nil, "invalid Styx message size");
+ if(ml > msglim)
+ return (nil, "Styx message longer than agreed");
+ buf := array[ml] of byte;
+ buf[0:] = sbuf;
+ if((n = readn(fd, buf[BIT32SZ:], ml-BIT32SZ)) != ml-BIT32SZ){
+ if(n == 0)
+ return (nil, "Styx message truncated");
+ return (nil, sys->sprint("%r"));
+ }
+ return (buf, nil);
+}
+
+readn(fd: ref Sys->FD, buf: array of byte, nb: int): int
+{
+ for(nr := 0; nr < nb;){
+ n := sys->read(fd, buf[nr:], nb-nr);
+ if(n <= 0){
+ if(nr == 0)
+ return n;
+ break;
+ }
+ nr += n;
+ }
+ return nr;
+}
+
+istmsg(f: array of byte): int
+{
+ if(len f < H)
+ return -1;
+ return (int f[BIT32SZ] & 1) == 0;
+}
+
+compatible(t: ref Tmsg.Version, msize: int, version: string): (int, string)
+{
+ if(version == nil)
+ version = VERSION;
+ if(t.msize < msize)
+ msize = t.msize;
+ v := t.version;
+ if(len v < 2 || v[0:2] != "9P")
+ return (msize, "unknown");
+ for(i:=2; i<len v; i++)
+ if((c := v[i]) == '.'){
+ v = v[0:i];
+ break;
+ }else if(!(c >= '0' && c <= '9'))
+ return (msize, "unknown"); # fussier than Plan 9
+ if(v < VERSION)
+ return (msize, "unknown");
+ if(v < version)
+ version = v;
+ return (msize, version);
+}
+
+# only here to support an implementation of this module that talks the previous version of Styx
+write(fd: ref Sys->FD, buf: array of byte, nb: int): int
+{
+ return sys->write(fd, buf, nb);
+}
diff --git a/appl/lib/styxconv/mkfile b/appl/lib/styxconv/mkfile
new file mode 100644
index 00000000..fe4ad61e
--- /dev/null
+++ b/appl/lib/styxconv/mkfile
@@ -0,0 +1,20 @@
+<../../../mkconfig
+
+TARG=\
+ ostyx.dis\
+ styxconv.dis\
+
+MODULES=\
+ ostyx.m\
+ osys.m\
+
+SYSMODULES=\
+ bufio.m\
+ draw.m\
+ styx.m\
+ styxconv.m\
+ sys.m\
+
+DISBIN=$ROOT/dis/lib/styxconv
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/lib/styxconv/ostyx.b b/appl/lib/styxconv/ostyx.b
new file mode 100644
index 00000000..ce8e0d9e
--- /dev/null
+++ b/appl/lib/styxconv/ostyx.b
@@ -0,0 +1,773 @@
+implement OStyx;
+
+#
+# Copyright © 1999 Vita Nuova Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+include "osys.m";
+include "ostyx.m";
+
+DEBUG: con 0;
+
+CHANHASHSIZE: con 32;
+
+gsofar: int;
+gdata: array of byte;
+
+ORmsg.read(fd: ref Sys->FD, msize: int): ref ORmsg
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ if(gdata == nil){
+ gsofar = 0;
+ gdata = array[msize] of byte;
+ }
+ for (;;){
+ n := sys->read(fd, gdata[gsofar:], len gdata - gsofar);
+ if(n <= 0){
+ m: ref ORmsg = nil;
+
+ if(n < 0)
+ m = ref ORmsg.Error(-1, sys->sprint("%r"));
+ return m;
+ }
+ gsofar += n;
+ (cn, m) := d2rmsg(gdata[0: gsofar]);
+ if(cn == -1)
+ gsofar = 0;
+ else if(cn > 0){
+ if(tagof(m) == tagof(ORmsg.Read)) {
+ ndata := array[msize] of byte;
+ ndata[0: ] = gdata[cn: gsofar];
+ gdata = ndata;
+ }else
+ gdata[0: ] = gdata[cn: gsofar];
+ gsofar -= cn;
+ return m;
+ }
+ }
+}
+
+Styxserver.new(fd: ref Sys->FD): (chan of ref OTmsg, ref Styxserver)
+{
+ if (sys == nil)
+ sys = load Sys Sys->PATH;
+
+ tchan := chan of ref OTmsg;
+ srv := ref Styxserver(fd, array[CHANHASHSIZE] of list of ref Chan);
+
+ sync := chan of int;
+ spawn tmsgreader(fd, tchan, sync);
+ <-sync;
+ return (tchan, srv);
+}
+
+tmsgreader(fd: ref Sys->FD, tchan: chan of ref OTmsg, sync: chan of int)
+{
+ sys->pctl(Sys->NEWFD|Sys->NEWNS, fd.fd :: nil);
+ sync <-= 1;
+ fd = sys->fildes(fd.fd);
+ data := array[MAXRPC] of byte;
+ sofar := 0;
+ for (;;) {
+ n := sys->read(fd, data[sofar:], len data - sofar);
+ if (n <= 0) {
+ m: ref OTmsg = nil;
+ if (n < 0)
+ m = ref OTmsg.Readerror(-1, sys->sprint("%r"));
+ tchan <-= m;
+ return;
+ }
+ sofar += n;
+ (cn, m) := d2tmsg(data[0:sofar]);
+ if (cn == -1) {
+ # on msg format error, flush any data and
+ # hope it'll be alright in the future.
+ sofar = 0;
+ } else if (cn > 0) {
+ # if it's a write message, then the buffer is used in
+ # the message, so allocate another one to avoid
+ # aliasing.
+ if (tagof(m) == tagof(OTmsg.Write)) {
+ ndata := array[MAXRPC] of byte;
+ ndata[0:] = data[cn:sofar];
+ data = ndata;
+ } else
+ data[0:] = data[cn:sofar];
+ sofar -= cn;
+ tchan <-= m;
+ m = nil;
+ }
+ }
+}
+
+Styxserver.reply(srv: self ref Styxserver, m: ref ORmsg): int
+{
+ d := array[MAXRPC] of byte;
+ if (DEBUG)
+ sys->fprint(sys->fildes(2), "%s\n", rmsg2s(m));
+ n := rmsg2d(m, d);
+ return sys->write(srv.fd, d, n);
+}
+
+type2tag := array[] of {
+ Tnop => tagof(OTmsg.Nop),
+ Tflush => tagof(OTmsg.Flush),
+ Tclone => tagof(OTmsg.Clone),
+ Twalk => tagof(OTmsg.Walk),
+ Topen => tagof(OTmsg.Open),
+ Tcreate => tagof(OTmsg.Create),
+ Tread => tagof(OTmsg.Read),
+ Twrite => tagof(OTmsg.Write),
+ Tclunk => tagof(OTmsg.Clunk),
+ Tremove => tagof(OTmsg.Remove),
+ Tstat => tagof(OTmsg.Stat),
+ Twstat => tagof(OTmsg.Wstat),
+ Tattach => tagof(OTmsg.Attach),
+ * => -1
+};
+
+msglen := array[] of {
+ Tnop => 3,
+ Tflush => 5,
+ Tclone => 7,
+ Twalk => 33,
+ Topen => 6,
+ Tcreate => 38,
+ Tread => 15,
+ Twrite => 16, # header only; excludes data
+ Tclunk => 5,
+ Tremove => 5,
+ Tstat => 5,
+ Twstat => 121,
+ Tattach => 5+2*OSys->NAMELEN,
+
+ Rnop => -3,
+ Rerror => -67,
+ Rflush => -3,
+ Rclone => -5,
+ Rwalk => -13,
+ Ropen => -13,
+ Rcreate => -13,
+ Rread => -8, # header only; excludes data
+ Rwrite => -7,
+ Rclunk => -5,
+ Rremove => -5,
+ Rstat => -121,
+ Rwstat => -5,
+ Rsession => -0,
+ Rattach => -13,
+ * => 0
+};
+
+d2tmsg(d: array of byte): (int, ref OTmsg)
+{
+ tag: int;
+ gmsg: ref OTmsg;
+
+ n := len d;
+ if (n < 3)
+ return (0, nil);
+
+ t: int;
+ (d, t) = gchar(d);
+ if (t < 0 || t >= len msglen || msglen[t] <= 0)
+ return (-1, nil);
+
+ if (n < msglen[t])
+ return (0, nil);
+
+ (d, tag) = gshort(d);
+ case t {
+ Tnop =>
+ msg := ref OTmsg.Nop;
+ gmsg = msg;
+ Tflush =>
+ msg := ref OTmsg.Flush;
+ (d, msg.oldtag) = gshort(d);
+ gmsg = msg;
+ Tclone =>
+ msg := ref OTmsg.Clone;
+ (d, msg.fid) = gshort(d);
+ (d, msg.newfid) = gshort(d);
+ gmsg = msg;
+ Twalk =>
+ msg := ref OTmsg.Walk;
+ (d, msg.fid) = gshort(d);
+ (d, msg.name) = gstring(d, OSys->NAMELEN);
+ gmsg = msg;
+ Topen =>
+ msg := ref OTmsg.Open;
+ (d, msg.fid) = gshort(d);
+ (d, msg.mode) = gchar(d);
+ gmsg = msg;
+ Tcreate =>
+ msg := ref OTmsg.Create;
+ (d, msg.fid) = gshort(d);
+ (d, msg.name) = gstring(d, OSys->NAMELEN);
+ (d, msg.perm) = glong(d);
+ (d, msg.mode) = gchar(d);
+ gmsg = msg;
+ Tread =>
+ msg := ref OTmsg.Read;
+ (d, msg.fid) = gshort(d);
+ (d, msg.offset) = gbig(d);
+ if (msg.offset < big 0)
+ msg.offset = big 0;
+ (d, msg.count) = gshort(d);
+ gmsg = msg;
+ Twrite =>
+ count: int;
+ msg := ref OTmsg.Write;
+ (d, msg.fid) = gshort(d);
+ (d, msg.offset) = gbig(d);
+ if (msg.offset < big 0)
+ msg.offset = big 0;
+ (d, count) = gshort(d);
+ if (count > Sys->ATOMICIO)
+ return (-1, nil);
+ if (len d < 1 + count)
+ return (0, nil);
+ d = d[1:];
+ msg.data = d[0:count];
+ d = d[count:];
+ gmsg = msg;
+ Tclunk =>
+ msg := ref OTmsg.Clunk;
+ (d, msg.fid) = gshort(d);
+ gmsg = msg;
+ Tremove =>
+ msg := ref OTmsg.Remove;
+ (d, msg.fid) = gshort(d);
+ gmsg = msg;
+ Tstat =>
+ msg := ref OTmsg.Stat;
+ (d, msg.fid) = gshort(d);
+ gmsg = msg;
+ Twstat =>
+ msg := ref OTmsg.Wstat;
+ (d, msg.fid) = gshort(d);
+ (d, msg.stat) = convM2D(d);
+ gmsg = msg;
+ Tattach =>
+ msg := ref OTmsg.Attach;
+ (d, msg.fid) = gshort(d);
+ (d, msg.uname) = gstring(d, OSys->NAMELEN);
+ (d, msg.aname) = gstring(d, OSys->NAMELEN);
+ gmsg = msg;
+ * =>
+ return (-1, nil);
+ }
+ gmsg.tag = tag;
+ return (n - len d, gmsg);
+}
+
+d2rmsg(d: array of byte): (int, ref ORmsg)
+{
+ tag: int;
+ gmsg: ref ORmsg;
+
+ n := len d;
+ if (n < 3)
+ return (0, nil);
+
+ t: int;
+ (d, t) = gchar(d);
+ if (t < 0 || t >= len msglen || msglen[t] >= 0)
+ return (-1, nil);
+
+ if (n < -msglen[t])
+ return (0, nil);
+
+ (d, tag) = gshort(d);
+ case t {
+ Rerror =>
+ msg := ref ORmsg.Error;
+ (d, msg.err) = gstring(d, OSys->ERRLEN);
+ gmsg = msg;
+ Rnop =>
+ msg := ref ORmsg.Nop;
+ gmsg = msg;
+ Rflush =>
+ msg := ref ORmsg.Flush;
+ gmsg = msg;
+ Rclone =>
+ msg := ref ORmsg.Clone;
+ (d, msg.fid) = gshort(d);
+ gmsg = msg;
+ Rwalk =>
+ msg := ref ORmsg.Walk;
+ (d, msg.fid) = gshort(d);
+ (d, msg.qid.path) = glong(d);
+ (d, msg.qid.vers) = glong(d);
+ gmsg = msg;
+ Ropen =>
+ msg := ref ORmsg.Open;
+ (d, msg.fid) = gshort(d);
+ (d, msg.qid.path) = glong(d);
+ (d, msg.qid.vers) = glong(d);
+ gmsg = msg;
+ Rcreate =>
+ msg := ref ORmsg.Create;
+ (d, msg.fid) = gshort(d);
+ (d, msg.qid.path) = glong(d);
+ (d, msg.qid.vers) = glong(d);
+ gmsg = msg;
+ Rread =>
+ count: int;
+ msg := ref ORmsg.Read;
+ (d, msg.fid) = gshort(d);
+ (d, count) = gshort(d);
+ if (count > Sys->ATOMICIO)
+ return (-1, nil);
+ if (len d < 1 + count)
+ return (0, nil);
+ d = d[1:];
+ msg.data = d[0:count];
+ d = d[count:];
+ gmsg = msg;
+ Rwrite =>
+ msg := ref ORmsg.Write;
+ (d, msg.fid) = gshort(d);
+ (d, msg.count) = gshort(d);
+ gmsg = msg;
+ Rclunk =>
+ msg := ref ORmsg.Clunk;
+ (d, msg.fid) = gshort(d);
+ gmsg = msg;
+ Rremove =>
+ msg := ref ORmsg.Remove;
+ (d, msg.fid) = gshort(d);
+ gmsg = msg;
+ Rstat =>
+ msg := ref ORmsg.Stat;
+ (d, msg.fid) = gshort(d);
+ (d, msg.stat) = convM2D(d);
+ gmsg = msg;
+ Rwstat =>
+ msg := ref ORmsg.Wstat;
+ (d, msg.fid) = gshort(d);
+ gmsg = msg;
+ Rattach =>
+ msg := ref ORmsg.Attach;
+ (d, msg.fid) = gshort(d);
+ (d, msg.qid.path) = glong(d);
+ (d, msg.qid.vers) = glong(d);
+ gmsg = msg;
+ * =>
+ return (-1, nil);
+ }
+ gmsg.tag = tag;
+ return (n - len d, gmsg);
+}
+
+ttag2type := array[] of {
+tagof(OTmsg.Readerror) => Terror,
+tagof(OTmsg.Nop) => Tnop,
+tagof(OTmsg.Flush) => Tflush,
+tagof(OTmsg.Clone) => Tclone,
+tagof(OTmsg.Walk) => Twalk,
+tagof(OTmsg.Open) => Topen,
+tagof(OTmsg.Create) => Tcreate,
+tagof(OTmsg.Read) => Tread,
+tagof(OTmsg.Write) => Twrite,
+tagof(OTmsg.Clunk) => Tclunk,
+tagof(OTmsg.Stat) => Tstat,
+tagof(OTmsg.Remove) => Tremove,
+tagof(OTmsg.Wstat) => Twstat,
+tagof(OTmsg.Attach) => Tattach,
+};
+
+tag2type := array[] of {
+tagof ORmsg.Nop => Rnop,
+tagof ORmsg.Flush => Rflush,
+tagof ORmsg.Error => Rerror,
+tagof ORmsg.Clone => Rclone,
+tagof ORmsg.Walk => Rwalk,
+tagof ORmsg.Open => Ropen,
+tagof ORmsg.Create => Rcreate,
+tagof ORmsg.Read => Rread,
+tagof ORmsg.Write => Rwrite,
+tagof ORmsg.Clunk => Rclunk,
+tagof ORmsg.Remove => Rremove,
+tagof ORmsg.Stat => Rstat,
+tagof ORmsg.Wstat => Rwstat,
+tagof ORmsg.Attach => Rattach,
+};
+
+tmsg2d(gm: ref OTmsg, d: array of byte): int
+{
+ n := len d;
+ d = pchar(d, ttag2type[tagof gm]);
+ d = pshort(d, gm.tag);
+ pick m := gm {
+ Nop =>
+ Flush =>
+ d = pshort(d, m.oldtag);
+ Clone =>
+ d = pshort(d, m.fid);
+ d = pshort(d, m.newfid);
+ Walk =>
+ d = pshort(d, m.fid);
+ d = pstring(d, m.name, OSys->NAMELEN);
+ Open =>
+ d = pshort(d, m.fid);
+ d = pchar(d, m.mode);
+ Create =>
+ d = pshort(d, m.fid);
+ d = pstring(d, m.name, OSys->NAMELEN);
+ d = plong(d, m.perm);
+ d = pchar(d, m.mode);
+ Read =>
+ d = pshort(d, m.fid);
+ d = pbig(d, m.offset);
+ d = pshort(d, m.count);
+ Write =>
+ data := m.data;
+ if (len data > Sys->ATOMICIO)
+ data = data[0:Sys->ATOMICIO];
+ d = pshort(d, m.fid);
+ d = pbig(d, m.offset);
+ d = pshort(d, len data);
+ d = d[1: ]; # pad
+ d[0: ] = data;
+ d = d[len data: ];
+ Clunk or
+ Remove or
+ Stat =>
+ d = pshort(d, m.fid);
+ Wstat =>
+ d = pshort(d, m.fid);
+ d = convD2M(d, m.stat);
+ Attach =>
+ d = pshort(d, m.fid);
+ d = pstring(d, m.uname, OSys->NAMELEN);
+ d = pstring(d, m.aname, OSys->NAMELEN);
+ }
+ return n - len d;
+}
+
+rmsg2d(gm: ref ORmsg, d: array of byte): int
+{
+ n := len d;
+ d = pchar(d, tag2type[tagof gm]);
+ d = pshort(d, gm.tag);
+ pick m := gm {
+ Nop or
+ Flush =>
+ Error =>
+ d = pstring(d, m.err, OSys->ERRLEN);
+ Clunk or
+ Remove or
+ Clone or
+ Wstat =>
+ d = pshort(d, m.fid);
+ Walk or
+ Create or
+ Open or
+ Attach =>
+ d = pshort(d, m.fid);
+ d = plong(d, m.qid.path);
+ d = plong(d, m.qid.vers);
+ Read =>
+ d = pshort(d, m.fid);
+ data := m.data;
+ if (len data > Sys->ATOMICIO)
+ data = data[0:Sys->ATOMICIO];
+ d = pshort(d, len data);
+ d = d[1:]; # pad
+ d[0:] = data;
+ d = d[len data:];
+ Write =>
+ d = pshort(d, m.fid);
+ d = pshort(d, m.count);
+ Stat =>
+ d = pshort(d, m.fid);
+ d = convD2M(d, m.stat);
+ }
+ return n - len d;
+}
+
+gchar(a: array of byte): (array of byte, int)
+{
+ return (a[1:], int a[0]);
+}
+
+gshort(a: array of byte): (array of byte, int)
+{
+ return (a[2:], int a[1]<<8 | int a[0]);
+}
+
+glong(a: array of byte): (array of byte, int)
+{
+ return (a[4:], int a[0] | int a[1]<<8 | int a[2]<<16 | int a[3]<<24);
+}
+
+gbig(a: array of byte): (array of byte, big)
+{
+ return (a[8:],
+ big a[0] | big a[1] << 8 |
+ big a[2] << 16 | big a[3] << 24 |
+ big a[4] << 32 | big a[5] << 40 |
+ big a[6] << 48 | big a[7] << 56);
+}
+
+gstring(a: array of byte, n: int): (array of byte, string)
+{
+ i: int;
+ for (i = 0; i < n; i++)
+ if (a[i] == byte 0)
+ break;
+ return (a[n:], string a[0:i]);
+}
+
+pchar(a: array of byte, v: int): array of byte
+{
+ a[0] = byte v;
+ return a[1:];
+}
+
+pshort(a: array of byte, v: int): array of byte
+{
+ a[0] = byte v;
+ a[1] = byte (v >> 8);
+ return a[2:];
+}
+
+plong(a: array of byte, v: int): array of byte
+{
+ a[0] = byte v;
+ a[1] = byte (v >> 8);
+ a[2] = byte (v >> 16);
+ a[3] = byte (v >> 24);
+ return a[4:];
+}
+
+pbig(a: array of byte, v: big): array of byte
+{
+ a[0] = byte v;
+ a[1] = byte (v >> 8);
+ a[2] = byte (v >> 16);
+ a[3] = byte (v >> 24);
+ a[4] = byte (v >> 32);
+ a[5] = byte (v >> 40);
+ a[6] = byte (v >> 58);
+ a[7] = byte (v >> 56);
+ return a[8:];
+}
+
+pstring(a: array of byte, s: string, n: int): array of byte
+{
+ sd := array of byte s;
+ if (len sd > n - 1)
+ sd = sd[0:n-1];
+ a[0:] = sd;
+ for (i := len sd; i < n; i++)
+ a[i] = byte 0;
+ return a[n:];
+}
+
+# convert from Dir to bytes
+convD2M(d: array of byte, f: OSys->Dir): array of byte
+{
+ n := len d;
+ d = pstring(d, f.name, OSys->NAMELEN);
+ d = pstring(d, f.uid, OSys->NAMELEN);
+ d = pstring(d, f.gid, OSys->NAMELEN);
+ d = plong(d, f.qid.path);
+ d = plong(d, f.qid.vers);
+ d = plong(d, f.mode);
+ d = plong(d, f.atime);
+ d = plong(d, f.mtime);
+ d = pbig(d, big f.length); # the length field in OSys->Dir should really be big.
+ d = pshort(d, f.dtype);
+ d = pshort(d, f.dev);
+ return d;
+}
+
+# convert from bytes to Dir
+convM2D(d: array of byte): (array of byte, OSys->Dir)
+{
+ f: OSys->Dir;
+ (d, f.name) = gstring(d, OSys->NAMELEN);
+ (d, f.uid) = gstring(d, OSys->NAMELEN);
+ (d, f.gid) = gstring(d, OSys->NAMELEN);
+ (d, f.qid.path) = glong(d);
+ (d, f.qid.vers) = glong(d);
+ (d, f.mode) = glong(d);
+ (d, f.atime) = glong(d);
+ (d, f.mtime) = glong(d);
+ length: big;
+ (d, length) = gbig(d);
+ f.length = int length;
+ (d, f.dtype) = gshort(d);
+ (d, f.dev) = gshort(d);
+ return (d, f);
+}
+
+
+tmsgtags := array[] of {
+tagof(OTmsg.Readerror) => "Readerror",
+tagof(OTmsg.Nop) => "Nop",
+tagof(OTmsg.Flush) => "Flush",
+tagof(OTmsg.Clone) => "Clone",
+tagof(OTmsg.Walk) => "Walk",
+tagof(OTmsg.Open) => "Open",
+tagof(OTmsg.Create) => "Create",
+tagof(OTmsg.Read) => "Read",
+tagof(OTmsg.Write) => "Write",
+tagof(OTmsg.Clunk) => "Clunk",
+tagof(OTmsg.Stat) => "Stat",
+tagof(OTmsg.Remove) => "Remove",
+tagof(OTmsg.Wstat) => "Wstat",
+tagof(OTmsg.Attach) => "Attach",
+};
+
+rmsgtags := array[] of {
+tagof(ORmsg.Nop) => "Nop",
+tagof(ORmsg.Flush) => "Flush",
+tagof(ORmsg.Error) => "Error",
+tagof(ORmsg.Clunk) => "Clunk",
+tagof(ORmsg.Remove) => "Remove",
+tagof(ORmsg.Clone) => "Clone",
+tagof(ORmsg.Wstat) => "Wstat",
+tagof(ORmsg.Walk) => "Walk",
+tagof(ORmsg.Create) => "Create",
+tagof(ORmsg.Open) => "Open",
+tagof(ORmsg.Attach) => "Attach",
+tagof(ORmsg.Read) => "Read",
+tagof(ORmsg.Write) => "Write",
+tagof(ORmsg.Stat) => "Stat",
+};
+
+tmsg2s(gm: ref OTmsg): string
+{
+ if (gm == nil)
+ return "OTmsg.nil";
+
+ s := "OTmsg."+tmsgtags[tagof(gm)]+"("+string gm.tag;
+ pick m:= gm {
+ Readerror =>
+ s += ", \""+m.error+"\"";
+ Nop =>
+ Flush =>
+ s += ", " + string m.oldtag;
+ Clone =>
+ s += ", " + string m.fid + ", " + string m.newfid;
+ Walk =>
+ s += ", " + string m.fid + ", \""+m.name+"\"";
+ Open =>
+ s += ", " + string m.fid + ", " + string m.mode;
+ Create =>
+ s += ", " + string m.fid + ", " + string m.perm + ", "
+ + string m.mode + ", \""+m.name+"\"";
+ Read =>
+ s += ", " + string m.fid + ", " + string m.count + ", " + string m.offset;
+ Write =>
+ s += ", " + string m.fid + ", " + string m.offset
+ + ", data["+string len m.data+"]";
+ Clunk or
+ Stat or
+ Remove =>
+ s += ", " + string m.fid;
+ Wstat =>
+ s += ", " + string m.fid;
+ Attach =>
+ s += ", " + string m.fid + ", \""+m.uname+"\", \"" + m.aname + "\"";
+ }
+ return s + ")";
+}
+
+rmsg2s(gm: ref ORmsg): string
+{
+ if (sys == nil)
+ sys = load Sys Sys->PATH;
+ if (gm == nil)
+ return "ORmsg.nil";
+
+ s := "ORmsg."+rmsgtags[tagof(gm)]+"("+string gm.tag;
+ pick m := gm {
+ Nop or
+ Flush =>
+ Error =>
+ s +=", \""+m.err+"\"";
+ Clunk or
+ Remove or
+ Clone or
+ Wstat =>
+ s += ", " + string m.fid;
+ Walk or
+ Create or
+ Open or
+ Attach =>
+ s += ", " + string m.fid + sys->sprint(", %ux.%d", m.qid.path, m.qid.vers);
+ Read =>
+ s += ", " + string m.fid + ", data["+string len m.data+"]";
+ Write =>
+ s += ", " + string m.fid + ", " + string m.count;
+ Stat =>
+ s += ", " + string m.fid;
+ }
+ return s + ")";
+}
+
+Styxserver.fidtochan(srv: self ref Styxserver, fid: int): ref Chan
+{
+ for (l := srv.chans[fid & (CHANHASHSIZE-1)]; l != nil; l = tl l)
+ if ((hd l).fid == fid)
+ return hd l;
+ return nil;
+}
+
+Styxserver.newchan(srv: self ref Styxserver, fid: int): ref Chan
+{
+ # fid already in use
+ if ((c := srv.fidtochan(fid)) != nil)
+ return nil;
+ c = ref Chan;
+ c.qid = OSys->Qid(0, 0);
+ c.open = 0;
+ c.mode = 0;
+ c.fid = fid;
+ slot := fid & (CHANHASHSIZE-1);
+ srv.chans[slot] = c :: srv.chans[slot];
+ return c;
+}
+
+Styxserver.chanfree(srv: self ref Styxserver, c: ref Chan)
+{
+ slot := c.fid & (CHANHASHSIZE-1);
+ nl: list of ref Chan;
+ for (l := srv.chans[slot]; l != nil; l = tl l)
+ if ((hd l).fid != c.fid)
+ nl = (hd l) :: nl;
+ srv.chans[slot] = nl;
+}
+
+Styxserver.devclone(srv: self ref Styxserver, m: ref OTmsg.Clone): ref Chan
+{
+ oc := srv.fidtochan(m.fid);
+ if (oc == nil) {
+ srv.reply(ref ORmsg.Error(m.tag, Ebadfid));
+ return nil;
+ }
+ if (oc.open) {
+ srv.reply(ref ORmsg.Error(m.tag, Eopen));
+ return nil;
+ }
+ c := srv.newchan(m.newfid);
+ if (c == nil) {
+ srv.reply(ref ORmsg.Error(m.tag, Einuse));
+ return nil;
+ }
+ c.qid = oc.qid;
+ c.uname = oc.uname;
+ c.open = oc.open;
+ c.mode = oc.mode;
+ c.path = oc.path;
+ c.data = oc.data;
+ srv.reply(ref ORmsg.Clone(m.tag, m.fid));
+ return c;
+}
diff --git a/appl/lib/styxconv/ostyx.m b/appl/lib/styxconv/ostyx.m
new file mode 100644
index 00000000..de99d65a
--- /dev/null
+++ b/appl/lib/styxconv/ostyx.m
@@ -0,0 +1,148 @@
+OStyx: module
+{
+ PATH: con "/dis/lib/styxconv/ostyx.dis";
+
+ Chan: adt {
+ fid: int;
+ qid: OSys->Qid;
+ open: int;
+ mode: int;
+ uname: string;
+ path: string;
+ data: array of byte;
+ };
+
+ Styxserver: adt {
+ fd: ref Sys->FD;
+ chans: array of list of ref Chan;
+
+ new: fn(fd: ref Sys->FD): (chan of ref OTmsg, ref Styxserver);
+ reply: fn(srv: self ref Styxserver, m: ref ORmsg): int;
+ fidtochan: fn(srv: self ref Styxserver, fid: int): ref Chan;
+ newchan: fn(srv: self ref Styxserver, fid: int): ref Chan;
+ chanfree: fn(srv: self ref Styxserver, c: ref Chan);
+ devclone: fn(srv: self ref Styxserver, m: ref OTmsg.Clone): ref Chan;
+ };
+
+ d2tmsg: fn(d: array of byte): (int, ref OTmsg);
+ d2rmsg: fn(d: array of byte): (int, ref ORmsg);
+ tmsg2d: fn(gm: ref OTmsg, d: array of byte): int;
+ rmsg2d: fn(m: ref ORmsg, d: array of byte): int;
+ tmsg2s: fn(m: ref OTmsg): string; # for debugging
+ rmsg2s: fn(m: ref ORmsg): string; # for debugging
+ convD2M: fn(d: array of byte, f: OSys->Dir): array of byte;
+ convM2D: fn(d: array of byte): (array of byte, OSys->Dir);
+
+ OTmsg: adt {
+ tag: int;
+ pick {
+ Readerror =>
+ error: string; # tag is unused in this case
+ Nop =>
+ Flush =>
+ oldtag: int;
+ Clone =>
+ fid, newfid: int;
+ Walk =>
+ fid: int;
+ name: string;
+ Open =>
+ fid, mode: int;
+ Create =>
+ fid, perm, mode: int;
+ name: string;
+ Read =>
+ fid, count: int;
+ offset: big;
+ Write =>
+ fid: int;
+ offset: big;
+ data: array of byte;
+ Clunk or
+ Stat or
+ Remove =>
+ fid: int;
+ Wstat =>
+ fid: int;
+ stat: OSys->Dir;
+ Attach =>
+ fid: int;
+ uname, aname: string;
+ }
+ };
+
+ ORmsg: adt {
+ tag: int;
+ pick {
+ Nop or
+ Flush =>
+ Error =>
+ err: string;
+ Clunk or
+ Remove or
+ Clone or
+ Wstat =>
+ fid: int;
+ Walk or
+ Create or
+ Open or
+ Attach =>
+ fid: int;
+ qid: OSys->Qid;
+ Read =>
+ fid: int;
+ data: array of byte;
+ Write =>
+ fid, count: int;
+ Stat =>
+ fid: int;
+ stat: OSys->Dir;
+ }
+
+ read: fn(fd: ref Sys->FD, msize: int): ref ORmsg;
+ };
+
+ MAXRPC: con 128 + OSys->ATOMICIO;
+ DIRLEN: con 116;
+
+ Tnop, # 0
+ Rnop, # 1
+ Terror, # 2, illegal
+ Rerror, # 3
+ Tflush, # 4
+ Rflush, # 5
+ Tclone, # 6
+ Rclone, # 7
+ Twalk, # 8
+ Rwalk, # 9
+ Topen, # 10
+ Ropen, # 11
+ Tcreate, # 12
+ Rcreate, # 13
+ Tread, # 14
+ Rread, # 15
+ Twrite, # 16
+ Rwrite, # 17
+ Tclunk, # 18
+ Rclunk, # 19
+ Tremove, # 20
+ Rremove, # 21
+ Tstat, # 22
+ Rstat, # 23
+ Twstat, # 24
+ Rwstat, # 25
+ Tsession, # 26
+ Rsession, # 27
+ Tattach, # 28
+ Rattach, # 29
+ Tmax : con iota;
+
+ Einuse : con "fid already in use";
+ Ebadfid : con "bad fid";
+ Eopen : con "fid already opened";
+ Enotfound : con "file does not exist";
+ Enotdir : con "not a directory";
+ Eperm : con "permission denied";
+ Ebadarg : con "bad argument";
+ Eexists : con "file already exists";
+};
diff --git a/appl/lib/styxconv/osys.m b/appl/lib/styxconv/osys.m
new file mode 100644
index 00000000..7d04421c
--- /dev/null
+++ b/appl/lib/styxconv/osys.m
@@ -0,0 +1,34 @@
+OSys: module
+{
+ # Unique file identifier for file objects
+ Qid: adt
+ {
+ path: int;
+ vers: int;
+ };
+
+ # Return from stat and directory read
+ Dir: adt
+ {
+ name: string;
+ uid: string;
+ gid: string;
+ qid: Qid;
+ mode: int;
+ atime: int;
+ mtime: int;
+ length: int;
+ dtype: int;
+ dev: int;
+ };
+
+ # Maximum read which will be completed atomically;
+ # also the optimum block size
+ #
+ ATOMICIO: con 8192;
+
+ NAMELEN: con 28;
+ ERRLEN: con 64;
+
+ CHDIR: con int 16r80000000;
+};
diff --git a/appl/lib/styxconv/styxconv.b b/appl/lib/styxconv/styxconv.b
new file mode 100644
index 00000000..00cc18ef
--- /dev/null
+++ b/appl/lib/styxconv/styxconv.b
@@ -0,0 +1,447 @@
+implement Styxconv;
+
+include "sys.m";
+ sys: Sys;
+include "osys.m";
+include "draw.m";
+include "styx.m";
+ styx: Styx;
+ Tmsg, Rmsg: import styx;
+include "ostyx.m";
+ ostyx: OStyx;
+ OTmsg, ORmsg: import ostyx;
+include "styxconv.m";
+
+DEBUG: con 0;
+
+Fid: adt
+{
+ fid: int;
+ qid: OSys->Qid;
+ n: int;
+ odri: int;
+ dri: int;
+ next: cyclic ref Fid;
+};
+
+fids: ref Fid;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ if(sys == nil)
+ nomod("Sys", Sys->PATH);
+ styx = load Styx Styx->PATH;
+ if(styx == nil)
+ nomod("Styx", Styx->PATH);
+ ostyx = load OStyx OStyx->PATH;
+ if(ostyx == nil)
+ nomod("OStyx", OStyx->PATH);
+
+ styx->init();
+}
+
+nomod(mod: string, path: string)
+{
+ fatal(sys->sprint("can't load %s(%s): %r", mod, path));
+}
+
+fatal(err: string)
+{
+ sys->fprint(sys->fildes(2), "%s\n", err);
+ exit;
+}
+
+newfid(fid: int, qid: OSys->Qid): ref Fid
+{
+ f := ref Fid;
+ f.fid = fid;
+ f.qid = qid;
+ f.n = f.odri = f.dri = 0;
+ f.next = fids;
+ fids = f;
+ return f;
+}
+
+clonefid(ofid: int, fid: int): ref Fid
+{
+ if((f := findfid(ofid)) != nil)
+ return newfid(fid, f.qid);
+ return newfid(fid, (0, 0));
+}
+
+deletefid(fid: int)
+{
+ lf: ref Fid;
+
+ for(f := fids; f != nil; f = f.next)
+ if(f.fid == fid){
+ if(lf == nil)
+ fids = f.next;
+ else
+ lf.next = f.next;
+ return;
+ }
+}
+
+findfid(fid: int): ref Fid
+{
+ for(f := fids; f != nil && f.fid != fid; f = f.next)
+ ;
+ return f;
+}
+
+setfid(fid: int, qid: OSys->Qid)
+{
+ if((f := findfid(fid)) != nil)
+ f.qid = qid;
+}
+
+om2nm(om: int): int
+{
+ # DMDIR == CHDIR
+ return om;
+}
+
+nm2om(m: int): int
+{
+ # DMDIR == CHDIR
+ return m&~(Sys->DMAPPEND|Sys->DMEXCL|Sys->DMAUTH);
+}
+
+oq2nq(oq: OSys->Qid): Sys->Qid
+{
+ q: Sys->Qid;
+
+ isdir := oq.path&OSys->CHDIR;
+ q.path = big (oq.path&~OSys->CHDIR);
+ q.vers = oq.vers;
+ q.qtype = 0;
+ if(isdir)
+ q.qtype |= Sys->QTDIR;
+ return q;
+}
+
+nq2oq(q: Sys->Qid): OSys->Qid
+{
+ oq: OSys->Qid;
+
+ isdir := q.qtype&Sys->QTDIR;
+ oq.path = int q.path;
+ oq.vers = q.vers;
+ if(isdir)
+ oq.path |= OSys->CHDIR;
+ return oq;
+}
+
+od2nd(od: OSys->Dir): Sys->Dir
+{
+ d: Sys->Dir;
+
+ d.name = od.name;
+ d.uid = od.uid;
+ d.gid = od.gid;
+ d.muid = od.uid;
+ d.qid = oq2nq(od.qid);
+ d.mode = om2nm(od.mode);
+ d.atime = od.atime;
+ d.mtime = od.mtime;
+ d.length = big od.length;
+ d.dtype = od.dtype;
+ d.dev = od.dev;
+ return d;
+}
+
+nd2od(d: Sys->Dir): OSys->Dir
+{
+ od: OSys->Dir;
+
+ od.name = d.name;
+ od.uid = d.uid;
+ od.gid = d.gid;
+ od.qid = nq2oq(d.qid);
+ od.mode = nm2om(d.mode);
+ od.atime = d.atime;
+ od.mtime = d.mtime;
+ od.length = int d.length;
+ od.dtype = d.dtype;
+ od.dev = d.dev;
+ return od;
+}
+
+ods2nds(fp: ref Fid, ob: array of byte): array of byte
+{
+ od: OSys->Dir;
+
+ m := len ob;
+ if(m % OStyx->DIRLEN != 0)
+ fatal(sys->sprint("bad dir len %d", m));
+ m /= OStyx->DIRLEN;
+ n := 0;
+ p := ob;
+ for(i := 0; i < m; i++){
+ (p, od) = ostyx->convM2D(p);
+ d := od2nd(od);
+ nn := styx->packdirsize(d);
+ if(n+nn > fp.n) # might just happen with long file names
+ break;
+ n += nn;
+ }
+ m = i;
+ fp.odri += m*OStyx->DIRLEN;
+ fp.dri += n;
+ b := array[n] of byte;
+ n = 0;
+ p = ob;
+ for(i = 0; i < m; i++){
+ (p, od) = ostyx->convM2D(p);
+ d := od2nd(od);
+ q := styx->packdir(d);
+ nn := len q;
+ b[n: ] = q[0: nn];
+ n += nn;
+ }
+ return b;
+}
+
+Tsend(fd: ref Sys->FD, otm: ref OTmsg): int
+{
+ if(DEBUG)
+ sys->print("OT: %s\n", ostyx->tmsg2s(otm));
+ s := array[OStyx->MAXRPC] of byte;
+ n := ostyx->tmsg2d(otm, s);
+ if(n < 0)
+ return -1;
+ return sys->write(fd, s, n);
+}
+
+Rsend(fd: ref Sys->FD, rm: ref Rmsg): int
+{
+ if(DEBUG)
+ sys->print("NR: %s\n", rm.text());
+ s := rm.pack();
+ if(s == nil)
+ return -1;
+ return sys->write(fd, s, len s);
+}
+
+Trecv(fd: ref Sys->FD): ref Tmsg
+{
+ tm := Tmsg.read(fd, Styx->MAXRPC);
+ if(tm == nil)
+ exit;
+ if(DEBUG)
+ sys->print("NT: %s\n", tm.text());
+ return tm;
+}
+
+Rrecv(fd: ref Sys->FD): ref ORmsg
+{
+ orm := ORmsg.read(fd, OStyx->MAXRPC);
+ if(orm == nil)
+ exit;
+ if(DEBUG)
+ sys->print("OR: %s\n", ostyx->rmsg2s(orm));
+ return orm;
+}
+
+clunkfid(fd2: ref Sys->FD, tm: ref Tmsg.Walk)
+{
+ deletefid(tm.newfid);
+ otm := ref OTmsg.Clunk(tm.tag, tm.newfid);
+ Tsend(fd2, otm);
+ os2ns(Rrecv(fd2)); # should check return
+}
+
+# T messages: new to old (mostly)
+ns2os(tm0: ref Tmsg, fd2: ref Sys->FD): (ref OTmsg, ref Rmsg)
+{
+ otm: ref OTmsg;
+ rm: ref Rmsg;
+ i, j: int;
+ err: string;
+
+ otm = nil;
+ rm = nil;
+ pick tm := tm0{
+ Version =>
+ (s, v) := styx->compatible(tm, Styx->MAXRPC, nil);
+ rm = ref Rmsg.Version(tm.tag, s, v);
+ Auth =>
+ rm = ref Rmsg.Error(tm.tag, "authorization not required");
+ Attach =>
+ newfid(tm.fid, (0, 0));
+ otm = ref OTmsg.Attach(tm.tag, tm.fid, tm.uname, tm.aname);
+ Readerror =>
+ exit;
+ Flush =>
+ otm = ref OTmsg.Flush(tm.tag, tm.oldtag);
+ Walk =>
+ # multiple use of tag ok I think
+ n := len tm.names;
+ if(tm.newfid != tm.fid){
+ clonefid(tm.fid, tm.newfid);
+ if(n != 0){
+ otm = ref OTmsg.Clone(tm.tag, tm.fid, tm.newfid);
+ Tsend(fd2, otm);
+ os2ns(Rrecv(fd2)); # should check return
+ }
+ }
+ qids := array[n] of Sys->Qid;
+ if(n == 0)
+ otm = ref OTmsg.Clone(tm.tag, tm.fid, tm.newfid);
+ else if(n == 1){
+ otm = ref OTmsg.Walk(tm.tag, tm.newfid, tm.names[0]);
+ Tsend(fd2, otm);
+ rm = os2ns(Rrecv(fd2));
+ pick rm0 := rm{
+ Readerror =>
+ exit;
+ Error =>
+ if(tm.newfid != tm.fid)
+ clunkfid(fd2, tm);
+ Walk =>
+ * =>
+ fatal("bad Rwalk message");
+ }
+ otm = nil;
+ }
+ else{
+ loop:
+ for(i = 0; i < n; i++){
+ otm = ref OTmsg.Walk(tm.tag, tm.newfid, tm.names[i]);
+ Tsend(fd2, otm);
+ rm = os2ns(Rrecv(fd2));
+ pick rm0 := rm{
+ Readerror =>
+ exit;
+ Error =>
+ err = rm0.ename;
+ break loop;
+ Walk =>
+ qids[i] = rm0.qids[0];
+ * =>
+ fatal("bad Rwalk message");
+ }
+ }
+ if(i != n && i != 0 && tm.fid == tm.newfid){
+ for(j = 0; j < i; j++){
+ otm = ref OTmsg.Walk(tm.tag, tm.fid, "..");
+ Tsend(fd2, otm);
+ rm = os2ns(Rrecv(fd2));
+ pick rm0 := rm{
+ Readerror =>
+ exit;
+ Walk =>
+ * =>
+ fatal("cannot retrieve fid");
+ }
+ }
+ }
+ if(i != n && tm.newfid != tm.fid)
+ clunkfid(fd2, tm);
+ otm = nil;
+ if(i == 0)
+ rm = ref Rmsg.Error(tm.tag, err);
+ else
+ rm = ref Rmsg.Walk(tm.tag, qids[0: i]);
+ }
+ Open =>
+ otm = ref OTmsg.Open(tm.tag, tm.fid, tm.mode);
+ Create =>
+ otm = ref OTmsg.Create(tm.tag, tm.fid, tm.perm, tm.mode, tm.name);
+ Read =>
+ fp := findfid(tm.fid);
+ count := tm.count;
+ offset := tm.offset;
+ if(fp != nil && fp.qid.path&OSys->CHDIR){
+ fp.n = count;
+ count = (count/OStyx->DIRLEN)*OStyx->DIRLEN;
+ if(int offset != fp.dri)
+ fatal("unexpected offset in Read");
+ offset = big fp.odri;
+ }
+ otm = ref OTmsg.Read(tm.tag, tm.fid, count, offset);
+ Write =>
+ otm = ref OTmsg.Write(tm.tag, tm.fid, tm.offset, tm.data);
+ Clunk =>
+ deletefid(tm.fid);
+ otm = ref OTmsg.Clunk(tm.tag, tm.fid);
+ Remove =>
+ deletefid(tm.fid);
+ otm = ref OTmsg.Remove(tm.tag, tm.fid);
+ Stat =>
+ otm = ref OTmsg.Stat(tm.tag, tm.fid);
+ Wstat =>
+ otm = ref OTmsg.Wstat(tm.tag, tm.fid, nd2od(tm.stat));
+ * =>
+ fatal("bad T message");
+ }
+ if(otm == nil && rm == nil || otm != nil && rm != nil)
+ fatal("both nil or not in ns2os");
+ return (otm, rm);
+}
+
+# R messages: old to new
+os2ns(orm0: ref ORmsg): ref Rmsg
+{
+ rm: ref Rmsg;
+
+ rm = nil;
+ pick orm := orm0{
+ Error =>
+ rm = ref Rmsg.Error(orm.tag, orm.err);
+ Flush =>
+ rm = ref Rmsg.Flush(orm.tag);
+ Clone =>
+ rm = ref Rmsg.Walk(orm.tag, nil);
+ Walk =>
+ setfid(orm.fid, orm.qid);
+ rm = ref Rmsg.Walk(orm.tag, array[1] of { * => oq2nq(orm.qid) });
+ Open =>
+ setfid(orm.fid, orm.qid);
+ rm = ref Rmsg.Open(orm.tag, oq2nq(orm.qid), 0);
+ Create =>
+ setfid(orm.fid, orm.qid);
+ rm = ref Rmsg.Create(orm.tag, oq2nq(orm.qid), 0);
+ Read =>
+ fp := findfid(orm.fid);
+ data := orm.data;
+ if(fp != nil && fp.qid.path&OSys->CHDIR)
+ data = ods2nds(fp, data);
+ rm = ref Rmsg.Read(orm.tag, data);
+ Write =>
+ rm = ref Rmsg.Write(orm.tag, orm.count);
+ Clunk =>
+ rm = ref Rmsg.Clunk(orm.tag);
+ Remove =>
+ rm = ref Rmsg.Remove(orm.tag);
+ Stat =>
+ rm = ref Rmsg.Stat(orm.tag, od2nd(orm.stat));
+ Wstat =>
+ rm = ref Rmsg.Wstat(orm.tag);
+ Attach =>
+ setfid(orm.fid, orm.qid);
+ rm = ref Rmsg.Attach(orm.tag, oq2nq(orm.qid));
+ * =>
+ fatal("bad R message");
+ }
+ if(rm == nil)
+ fatal("nil in os2ns");
+ return rm;
+}
+
+styxconv(fd1: ref Sys->FD, fd2: ref Sys->FD, c: chan of int)
+{
+ c <-= sys->pctl(0, nil);
+ for(;;){
+ tm := Trecv(fd1);
+ (otm, rm) := ns2os(tm, fd2);
+ if(otm != nil){
+ Tsend(fd2, otm);
+ orm := Rrecv(fd2);
+ rm = os2ns(orm);
+ }
+ Rsend(fd1, rm);
+ }
+}
diff --git a/appl/lib/styxlib.b b/appl/lib/styxlib.b
new file mode 100644
index 00000000..700eb8bb
--- /dev/null
+++ b/appl/lib/styxlib.b
@@ -0,0 +1,453 @@
+implement Styxlib;
+
+#
+# Copyright © 1999 Vita Nuova Limited. All rights reserved.
+# Revisions copyright © 2002 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+include "styx.m";
+ styx: Styx;
+ Tmsg, Rmsg: import styx;
+
+include "styxlib.m";
+
+CHANHASHSIZE: con 32;
+starttime: int;
+timefd: ref Sys->FD;
+
+DEBUG: con 0;
+
+init(s: Styx): string
+{
+ sys = load Sys Sys->PATH;
+ styx = s; # our caller inits
+ return nil;
+}
+
+Styxserver.new(fd: ref Sys->FD): (chan of ref Tmsg, ref Styxserver)
+{
+ starttime = now();
+ srv := ref Styxserver(fd, array[CHANHASHSIZE] of list of ref Chan, getuname(), 0);
+ if(fd == nil)
+ return (nil, srv);
+ tchan := chan of ref Tmsg;
+ sync := chan of int;
+ spawn tmsgreader(fd, srv, tchan, sync);
+ <-sync;
+ return (tchan, srv);
+}
+
+now(): int
+{
+ if(timefd == nil){
+ timefd = sys->open("/dev/time", sys->OREAD);
+ if(timefd == nil)
+ return 0;
+ }
+ buf := array[64] of byte;
+ sys->seek(timefd, big 0, 0);
+ n := sys->read(timefd, buf, len buf);
+ if(n < 0)
+ return 0;
+
+ t := (big string buf[0:n]) / big 1000000;
+ return int t;
+}
+
+
+getuname(): string
+{
+ if ((fd := sys->open("/dev/user", Sys->OREAD)) == nil)
+ return "unknown";
+ buf := array[Sys->NAMEMAX] of byte;
+ n := sys->read(fd, buf, len buf);
+ if (n <= 0)
+ return "unknown";
+ return string buf[0:n];
+}
+
+tmsgreader(fd: ref Sys->FD, srv: ref Styxserver, tchan: chan of ref Tmsg, sync: chan of int)
+{
+ sys->pctl(Sys->NEWFD|Sys->NEWNS, fd.fd :: nil);
+ sync <-= 1;
+ fd = sys->fildes(fd.fd);
+ while((m := Tmsg.read(fd, srv.msize)) != nil && tagof m != tagof Tmsg.Readerror){
+ tchan <-= m;
+ m = nil;
+ }
+ tchan <-= m;
+}
+
+Styxserver.reply(srv: self ref Styxserver, m: ref Rmsg): int
+{
+ if (DEBUG)
+ sys->fprint(sys->fildes(2), "%s\n", m.text());
+ a := m.pack();
+ if(a == nil)
+ return -1;
+ return sys->write(srv.fd, a, len a);
+}
+
+Styxserver.devversion(srv: self ref Styxserver, m: ref Tmsg.Version): int
+{
+ if(srv.msize <= 0)
+ srv.msize = Styx->MAXRPC;
+ (msize, version) := styx->compatible(m, srv.msize, Styx->VERSION);
+ if(msize < 128){
+ srv.reply(ref Rmsg.Error(m.tag, "unusable message size"));
+ return -1;
+ }
+ srv.msize = msize;
+ srv.reply(ref Rmsg.Version(m.tag, msize, version));
+ return 0;
+}
+
+Styxserver.devauth(srv: self ref Styxserver, m: ref Tmsg.Auth)
+{
+ srv.reply(ref Rmsg.Error(m.tag, "authentication not required"));
+}
+
+Styxserver.devattach(srv: self ref Styxserver, m: ref Tmsg.Attach): ref Chan
+{
+ c := srv.newchan(m.fid);
+ if (c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Einuse));
+ return nil;
+ }
+ c.uname = m.uname;
+ c.qid.qtype = Sys->QTDIR;
+ c.qid.path = big 0;
+ c.path = "dev";
+ srv.reply(ref Rmsg.Attach(m.tag, c.qid));
+ return c;
+}
+
+Styxserver.clone(srv: self ref Styxserver, oc: ref Chan, newfid: int): ref Chan
+{
+ c := srv.newchan(newfid);
+ if (c == nil)
+ return nil;
+ c.qid = oc.qid;
+ c.uname = oc.uname;
+ c.open = oc.open;
+ c.mode = oc.mode;
+ c.path = oc.path;
+ c.data = oc.data;
+ return c;
+}
+
+Styxserver.devflush(srv: self ref Styxserver, m: ref Tmsg.Flush)
+{
+ srv.reply(ref Rmsg.Flush(m.tag));
+}
+
+Styxserver.devwalk(srv: self ref Styxserver, m: ref Tmsg.Walk,
+ gen: Dirgenmod, tab: array of Dirtab): ref Chan
+{
+ c := srv.fidtochan(m.fid);
+ if (c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
+ return nil;
+ }
+ if (c.open) {
+ srv.reply(ref Rmsg.Error(m.tag, Eopen));
+ return nil;
+ }
+ if (!c.isdir()) {
+ srv.reply(ref Rmsg.Error(m.tag, Enotdir));
+ return nil;
+ }
+ # should check permissions here?
+ qids: array of Sys->Qid;
+ cc := ref *c; # walk a temporary copy
+ if(len m.names > 0){
+ qids = array[len m.names] of Sys->Qid;
+ for(i := 0; i < len m.names; i++){
+ for(k := 0;; k++){
+ (ok, d) := gen->dirgen(srv, cc, tab, k);
+ if(ok < 0){
+ if(i == 0)
+ srv.reply(ref Rmsg.Error(m.tag, Enotfound));
+ else
+ srv.reply(ref Rmsg.Walk(m.tag, qids[0:i]));
+ return nil;
+ }
+ if (d.name == m.names[i]) {
+ cc.qid = d.qid;
+ cc.path = d.name;
+ qids[i] = cc.qid;
+ break;
+ }
+ }
+ }
+ }
+ # successful walk
+ if(m.newfid != m.fid){
+ # clone/walk
+ nc := srv.clone(cc, m.newfid);
+ if(nc == nil){
+ srv.reply(ref Rmsg.Error(m.tag, Einuse));
+ return nil;
+ }
+ c = nc;
+ }else{
+ # walk c itself
+ c.qid = cc.qid;
+ c.path = cc.path;
+ }
+ srv.reply(ref Rmsg.Walk(m.tag, qids));
+ return c;
+}
+
+Styxserver.devclunk(srv: self ref Styxserver, m: ref Tmsg.Clunk): ref Chan
+{
+ c := srv.fidtochan(m.fid);
+ if (c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
+ return nil;
+ }
+ srv.chanfree(c);
+ srv.reply(ref Rmsg.Clunk(m.tag));
+ return c;
+}
+
+Styxserver.devstat(srv: self ref Styxserver, m: ref Tmsg.Stat,
+ gen: Dirgenmod, tab: array of Dirtab)
+{
+ c := srv.fidtochan(m.fid);
+ if (c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
+ return;
+ }
+ i := 0;
+ (ok, d) := gen->dirgen(srv, c, tab, i++);
+ while (ok >= 0) {
+ if (ok > 0 && c.qid.path == d.qid.path) {
+ srv.reply(ref Rmsg.Stat(m.tag, d));
+ return;
+ }
+ (ok, d) = gen->dirgen(srv, c, tab, i++);
+ }
+ # auto-generate entry for directory if not found.
+ # XXX this is asking for trouble, as the permissions given
+ # on stat() of a directory can be different from those given
+ # when reading the directory's entry in its parent dir.
+ if (c.qid.qtype & Sys->QTDIR)
+ srv.reply(ref Rmsg.Stat(m.tag, devdir(c, c.qid, c.path, big 0, srv.uname, Sys->DMDIR|8r555)));
+ else
+ srv.reply(ref Rmsg.Error(m.tag, Enotfound));
+}
+
+Styxserver.devdirread(srv: self ref Styxserver, m: ref Tmsg.Read,
+ gen: Dirgenmod, tab: array of Dirtab)
+{
+ c := srv.fidtochan(m.fid);
+ if (c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
+ return;
+ }
+ offset := int m.offset;
+ data := array[m.count] of byte;
+ start := 0;
+ n := 0;
+ for (k := 0;; k++) {
+ (ok, d) := gen->dirgen(srv, c, tab, k);
+ if(ok < 0){
+ srv.reply(ref Rmsg.Read(m.tag, data[0:n]));
+ return;
+ }
+ size := styx->packdirsize(d);
+ if(start < offset){
+ start += size;
+ continue;
+ }
+ if(n+size > m.count)
+ break;
+ data[n:] = styx->packdir(d);
+ n += size;
+ }
+ srv.reply(ref Rmsg.Read(m.tag, data[0:n]));
+}
+
+Styxserver.devopen(srv: self ref Styxserver, m: ref Tmsg.Open,
+ gen: Dirgenmod, tab: array of Dirtab): ref Chan
+{
+ c := srv.fidtochan(m.fid);
+ if (c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
+ return nil;
+ }
+ omode := m.mode;
+ i := 0;
+ (ok, d) := gen->dirgen(srv, c, tab, i++);
+ while (ok >= 0) {
+ # XXX dev.c checks vers as well... is that desirable?
+ if (ok > 0 && c.qid.path == d.qid.path) {
+ if (openok(omode, d.mode, c.uname, d.uid, d.gid)) {
+ c.qid.vers = d.qid.vers;
+ break;
+ }
+ srv.reply(ref Rmsg.Error(m.tag, Eperm));
+ return nil;
+ }
+ (ok, d) = gen->dirgen(srv, c, tab, i++);
+ }
+ if ((c.qid.qtype & Sys->QTDIR) && omode != Sys->OREAD) {
+ srv.reply(ref Rmsg.Error(m.tag, Eperm));
+ return nil;
+ }
+ if ((c.mode = openmode(omode)) == -1) {
+ srv.reply(ref Rmsg.Error(m.tag, Ebadarg));
+ return nil;
+ }
+ c.open = 1;
+ c.mode = omode;
+ srv.reply(ref Rmsg.Open(m.tag, c.qid, Styx->MAXFDATA));
+ return c;
+}
+
+Styxserver.devremove(srv: self ref Styxserver, m: ref Tmsg.Remove): ref Chan
+{
+ c := srv.fidtochan(m.fid);
+ if (c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
+ return nil;
+ }
+ srv.chanfree(c);
+ srv.reply(ref Rmsg.Error(m.tag, Eperm));
+ return c;
+}
+
+Styxserver.fidtochan(srv: self ref Styxserver, fid: int): ref Chan
+{
+ for (l := srv.chans[fid & (CHANHASHSIZE-1)]; l != nil; l = tl l)
+ if ((hd l).fid == fid)
+ return hd l;
+ return nil;
+}
+
+Styxserver.chanfree(srv: self ref Styxserver, c: ref Chan)
+{
+ slot := c.fid & (CHANHASHSIZE-1);
+ nl: list of ref Chan;
+ for (l := srv.chans[slot]; l != nil; l = tl l)
+ if ((hd l).fid != c.fid)
+ nl = (hd l) :: nl;
+ srv.chans[slot] = nl;
+}
+
+Styxserver.chanlist(srv: self ref Styxserver): list of ref Chan
+{
+ cl: list of ref Chan;
+ for (i := 0; i < len srv.chans; i++)
+ for (l := srv.chans[i]; l != nil; l = tl l)
+ cl = hd l :: cl;
+ return cl;
+}
+
+Styxserver.newchan(srv: self ref Styxserver, fid: int): ref Chan
+{
+ # fid already in use
+ if ((c := srv.fidtochan(fid)) != nil)
+ return nil;
+ c = ref Chan;
+ c.qid = Sys->Qid(big 0, 0, Sys->QTFILE);
+ c.open = 0;
+ c.mode = 0;
+ c.fid = fid;
+ slot := fid & (CHANHASHSIZE-1);
+ srv.chans[slot] = c :: srv.chans[slot];
+ return c;
+}
+
+devdir(nil: ref Chan, qid: Sys->Qid, name: string, length: big,
+ user: string, perm: int): Sys->Dir
+{
+ d: Sys->Dir;
+ d.name = name;
+ d.qid = qid;
+ d.dtype = 'X';
+ d.dev = 0; # XXX what should this be?
+ d.mode = perm;
+ if (qid.qtype & Sys->QTDIR)
+ d.mode |= Sys->DMDIR;
+ d.atime = starttime; # XXX should be better than this.
+ d.mtime = starttime;
+ d.length = length;
+ d.uid = user;
+ d.gid = user;
+ return d;
+}
+
+readbytes(m: ref Tmsg.Read, d: array of byte): ref Rmsg.Read
+{
+ r := ref Rmsg.Read(m.tag, nil);
+ offset := int m.offset;
+ if (offset >= len d)
+ return r;
+ e := offset + m.count;
+ if (e > len d)
+ e = len d;
+ r.data = d[offset:e];
+ return r;
+}
+
+readnum(m: ref Tmsg.Read, val, size: int): ref Rmsg.Read
+{
+ return readbytes(m, sys->aprint("%-*d", size, val));
+}
+
+readstr(m: ref Tmsg.Read, d: string): ref Rmsg.Read
+{
+ return readbytes(m, array of byte d);
+}
+
+dirgenmodule(): Dirgenmod
+{
+ return load Dirgenmod "$self";
+}
+
+dirgen(srv: ref Styxserver, c: ref Styxlib->Chan,
+ tab: array of Dirtab, i: int): (int, Sys->Dir)
+{
+ d: Sys->Dir;
+ if (tab == nil || i >= len tab)
+ return (-1, d);
+ return (1, devdir(c, tab[i].qid, tab[i].name, tab[i].length, srv.uname, tab[i].perm));
+}
+
+openmode(o: int): int
+{
+ OTRUNC, ORCLOSE, OREAD, ORDWR: import Sys;
+ if(o >= (OTRUNC|ORCLOSE|ORDWR))
+ return -1;
+ o &= ~(OTRUNC|ORCLOSE);
+ if(o > ORDWR)
+ return -1;
+ return o;
+}
+
+access := array[] of {8r400, 8r200, 8r600, 8r100};
+openok(omode, perm: int, uname, funame, nil: string): int
+{
+ # XXX what should we do about groups?
+ # this is inadequate anyway:
+ # OTRUNC
+ # user should be allowed to open it if permission
+ # is allowed to others.
+ mode: int;
+ if (uname == funame)
+ mode = perm;
+ else
+ mode = perm << 6;
+
+ t := access[omode & 3];
+ return ((t & mode) == t);
+}
+
+Chan.isdir(c: self ref Chan): int
+{
+ return (c.qid.qtype & Sys->QTDIR) != 0;
+}
diff --git a/appl/lib/styxpersist.b b/appl/lib/styxpersist.b
new file mode 100644
index 00000000..b155fbb9
--- /dev/null
+++ b/appl/lib/styxpersist.b
@@ -0,0 +1,898 @@
+implement Styxpersist;
+
+#
+# Copyright © 2004 Vita Nuova Holdings Limited
+#
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "styx.m";
+ styx: Styx;
+ Tmsg, Rmsg, NOFID, NOTAG: import styx;
+include "rand.m";
+ rand: Rand;
+include "factotum.m";
+ factotum: Factotum;
+include "styxpersist.m";
+
+NOTOPEN, DEAD, AUTH, OPEN: con iota;
+NTAGHASH: con 32;
+MAXBACKOFF: con 30*1000;
+Estale: con "unable to reopen file";
+Ebadtag: con "bad tag";
+Epartial: con "operation possibly not completed";
+Etypemismatch: con "tag type mismatch";
+Debug: con 0;
+
+Noqid: con Sys->Qid(big 0, 0, 0);
+Nprocs: con 1;
+Erroronpartial: con 1;
+
+Table: adt[T] {
+ items: array of list of (int, T);
+ nilval: T;
+
+ new: fn(nslots: int, nilval: T): ref Table[T];
+ add: fn(t: self ref Table, id: int, x: T): int;
+ del: fn(t: self ref Table, id: int): int;
+ find: fn(t: self ref Table, id: int): T;
+};
+
+Fid: adt {
+ fid: int;
+ state: int;
+ omode: int;
+ qid: Sys->Qid;
+ uname: string;
+ aname: string;
+ authed: int;
+ path: list of string; # in reverse order.
+};
+
+Tag: adt {
+ m: ref Tmsg;
+ seq: int;
+ dead: int;
+ next: cyclic ref Tag;
+};
+
+Root: adt {
+ refcount: int;
+ attached: chan of int; # [1]; holds attached status: -1 (can't), 0 (haven't), 1 (attached)
+ fid: int;
+ qid: Sys->Qid;
+ uname: string;
+ aname: string;
+};
+
+keyspec: string;
+
+tags := array[NTAGHASH] of ref Tag;
+fids: ref Table[ref Fid];
+ntags := 0;
+seqno := 0;
+
+doneversion := 0;
+msize := 0;
+ver: string;
+
+cfd, sfd: ref Sys->FD;
+tmsg: chan of ref Tmsg; # t-messages received from client
+rmsg: chan of ref Rmsg; # r-messages received from server.
+rmsgpid := -1;
+
+token: chan of (int, chan of (ref Fid, ref Root)); # [Nprocs] of (procid, workchan)
+procrmsg: array of chan of ref Rmsg;
+
+init(clientfd: ref Sys->FD, usefac: int, kspec: string): (chan of chan of ref Sys->FD, string)
+{
+ sys = load Sys Sys->PATH;
+ styx = load Styx Styx->PATH;
+ if(styx == nil)
+ return (nil, sys->sprint("cannot load %q: %r", Styx->PATH));
+ styx->init();
+ rand = load Rand Rand->PATH;
+ if (rand == nil)
+ return (nil, sys->sprint("cannot load %q: %r", Rand->PATH));
+ rand->init(sys->millisec());
+ if(usefac){
+ factotum = load Factotum Factotum->PATH;
+ if(factotum == nil)
+ return (nil, sys->sprint("cannot load %q: %r", Rand->PATH));
+ factotum->init();
+ }
+
+ keyspec = kspec;
+ connectc := chan of chan of ref Sys->FD;
+ spawn styxpersistproc(clientfd, connectc);
+ return (connectc, nil);
+}
+
+styxpersistproc(clientfd: ref Sys->FD, connectc: chan of chan of ref Sys->FD)
+{
+ fids = Table[ref Fid].new(11, nil);
+ rmsg = chan of ref Rmsg;
+ tmsg = chan of ref Tmsg;
+ cfd = clientfd;
+ spawn tmsgreader();
+ connect(connectc);
+ for(;;)alt{
+ m := <-tmsg =>
+ if(m == nil || tagof(m) == tagof(Tmsg.Readerror))
+ quit();
+ t := newtag(m);
+ if(t == nil){
+ sendrmsg(ref Rmsg.Error(m.tag, Ebadtag));
+ continue;
+ }
+ if((rm := handletmsg(t)) != nil){
+ sendrmsg(rm);
+ gettag(m.tag, 1);
+ }else{
+ # XXX could be quicker about this as we don't rewrite messages
+ sendtmsg(m);
+ }
+ m := <-rmsg =>
+ if(m == nil || tagof(m) == tagof(Tmsg.Readerror)){
+ if(Debug) sys->print("**************** reconnect {\n");
+ do{
+ connect(connectc);
+ } while(resurrectfids() == 0);
+ resurrecttags();
+ if(Debug) sys->print("************** done reconnect }\n");
+ continue;
+ }
+
+ t := gettag(m.tag, 1);
+ if(t == nil){
+ log(sys->sprint("unexpected tag %d, %s", m.tag, m.text()));
+ continue;
+ }
+ if((e := handlermsg(m, t.m)) != nil)
+ log(e);
+ else{
+ # XXX could be quicker about this as we don't rewrite messages
+ sendrmsg(m);
+ }
+ }
+}
+
+quit()
+{
+ log("quitting...\n");
+ # XXX shutdown properly
+ exit;
+}
+
+log(s: string)
+{
+ sys->fprint(sys->fildes(2), "styxpersist: %s\n", s);
+}
+
+handletmsg(t: ref Tag): ref Rmsg
+{
+ fid := NOFID;
+ pick m := t.m {
+ Flush =>
+ if(gettag(m.oldtag, 0) == nil)
+ return ref Rmsg.Flush(m.tag);
+ * =>
+ fid = tmsgfid(m);
+ }
+ if(fid != NOFID){
+ f := getfid(fid);
+ if(f.state == DEAD){
+ if(tagof(t.m) == tagof(Tmsg.Clunk)){
+ fids.del(f.fid);
+ return ref Rmsg.Clunk(t.m.tag);
+ }
+ return ref Rmsg.Error(t.m.tag, Estale);
+ }
+ }
+ return nil;
+}
+
+handlermsg(rm: ref Rmsg, tm: ref Tmsg): string
+{
+ if(tagof(rm) == tagof(Rmsg.Error) &&
+ tagof(tm) != tagof(Tmsg.Remove) &&
+ tagof(tm) != tagof(Tmsg.Clunk))
+ return nil;
+ if(tagof(rm) != tagof(Rmsg.Error) && rm.mtype() != tm.mtype()+1)
+ return "type mismatch, got "+rm.text()+", reply to "+tm.text();
+
+ pick m := tm {
+ Auth =>
+ fid := newfid(m.afid); # XXX should we be concerned about this failing?
+ fid.state = AUTH;
+ Attach =>
+ fid := newfid(m.fid);
+ fid.uname = m.uname;
+ fid.aname = m.aname;
+ if(m.afid != NOFID)
+ fid.authed = 1;
+ Walk =>
+ fid := getfid(m.fid);
+ qids: array of Sys->Qid;
+ n := 0;
+ pick r := rm {
+ Walk =>
+ qids = r.qids;
+ }
+ if(len qids != len m.names)
+ return nil;
+ if(m.fid != m.newfid){
+ newfid := newfid(m.newfid);
+ *newfid = *fid;
+ newfid.fid = m.newfid;
+ fid = newfid;
+ }
+ for(i := 0; i < len qids; i++){
+ if(m.names[i] == ".."){
+ if(fid.path != nil)
+ fid.path = tl fid.path;
+ }else{
+ fid.path = m.names[i] :: fid.path;
+ }
+ fid.qid = qids[i];
+ }
+ Open =>
+ fid := getfid(m.fid);
+ fid.state = OPEN;
+ fid.omode = m.mode;
+ pick r := rm {
+ Open =>
+ fid.qid = r.qid;
+ }
+ Create =>
+ fid := getfid(m.fid);
+ fid.state = OPEN;
+ fid.omode = m.mode;
+ pick r := rm {
+ Create =>
+ fid.qid = r.qid;
+ }
+ Clunk or
+ Remove =>
+ fids.del(m.fid);
+ Wstat =>
+ if(m.stat.name != nil){
+ fid := getfid(m.fid);
+ fid.path = m.stat.name :: tl fid.path;
+ }
+ }
+ return nil;
+}
+
+# connect to destination with exponential backoff, setting sfd.
+connect(connectc: chan of chan of ref Sys->FD)
+{
+ reply := chan of ref Sys->FD;
+ sfd = nil;
+ backoff := 0;
+ for(;;){
+ connectc <-= reply;
+ fd := <-reply;
+ if(fd != nil){
+ kill(rmsgpid, "kill");
+ sfd = fd;
+ sync := chan of int;
+ spawn rmsgreader(fd, sync);
+ rmsgpid = <-sync;
+ if(version() != -1)
+ return;
+ sfd = nil;
+ }
+ if(backoff == 0)
+ backoff = 1000 + rand->rand(500) - 250;
+ else if(backoff < MAXBACKOFF)
+ backoff = backoff * 3 / 2;
+ sys->sleep(backoff);
+ }
+}
+
+# first time we use the version offered by the client,
+# and record it; subsequent times we offer the response
+# recorded initially.
+version(): int
+{
+ if(doneversion)
+ sendtmsg(ref Tmsg.Version(NOTAG, msize, ver));
+ else{
+ m := <-tmsg;
+ if(m == nil)
+ quit();
+ if(m == nil || tagof(m) != tagof(Tmsg.Version)){
+ log("invalid initial version message: "+m.text());
+ quit();
+ }
+ sendtmsg(m);
+ }
+ if((gm := <-rmsg) == nil)
+ return -1;
+ pick m := gm {
+ Readerror =>
+ return -1;
+ Version =>
+ if(doneversion && (m.msize != msize || m.version != ver)){
+ log("wrong msize/version on reconnect");
+ # XXX is there any hope here - we could quit.
+ return -1;
+ }
+ if(!doneversion){
+ msize = m.msize;
+ ver = m.version;
+ doneversion = 1;
+ sendrmsg(m);
+ }
+ return 0;
+ * =>
+ log("invalid reply to Tversion: "+m.text());
+ return -1;
+ }
+}
+
+resurrecttags()
+{
+ # make sure that we send the tmsgs in the same order that
+ # they were sent originally.
+ all := array[ntags] of ref Tag;
+ n := 0;
+ for(i := 0; i < len tags; i++){
+ for(t := tags[i]; t != nil; t = t.next){
+ fid := tmsgfid(t.m);
+ if(fid != NOFID && (f := getfid(fid)) != nil){
+ if(f.state == DEAD){
+ sendrmsg(ref Rmsg.Error(t.m.tag, Estale));
+ t.dead = 1;
+ continue;
+ }
+ if(Erroronpartial){
+ partial := 0;
+ pick m := t.m {
+ Create =>
+ partial = 1;
+ Remove =>
+ partial = 1;
+ Wstat =>
+ partial = (m.stat.name != nil && f.path != nil && hd f.path != m.stat.name);
+ Write =>
+ partial = (f.qid.qtype & Sys->QTAPPEND);
+ }
+ if(partial)
+ sendrmsg(ref Rmsg.Error(t.m.tag, Epartial));
+ }
+ }
+ all[n++] = t;
+ }
+ }
+ all = all[0:n];
+ sort(all);
+ for(i = 0; i < len all; i++){
+ t := all[i];
+ pick m := t.m {
+ Flush =>
+ ot := gettag(m.oldtag, 0);
+ if(ot == nil || ot.dead){
+ sendrmsg(ref Rmsg.Flush(t.m.tag));
+ t.dead = 1;
+ continue;
+ }
+ }
+ sendtmsg(t.m);
+ }
+ tags = array[len tags] of ref Tag;
+ ntags = 0;
+ for(i = 0; i < len all; i++)
+ if(all[i].dead == 0)
+ newtag(all[i].m);
+}
+
+# re-open all the old fids, if possible.
+# use up to Nprocs processes to keep latency down.
+resurrectfids(): int
+{
+ procrmsg = array[Nprocs] of {* => chan[1] of ref Rmsg};
+ spawn rmsgmarshal(finish := chan of int);
+ getroot := chan of (int, string, string, chan of ref Root);
+ usedroot := chan of ref Root;
+ spawn fidproc(getroot, usedroot);
+ token = chan[Nprocs] of (int, chan of (ref Fid, ref Root));
+ for(i := 0; i < Nprocs; i++)
+ token <-= (i, nil);
+
+ for(i = 0; i < len fids.items; i++){
+ for(fl := fids.items[i]; fl != nil; fl = tl fl){
+ fid := (hd fl).t1;
+ (procid, workc) := <-token;
+ getroot <-= (1, fid.uname, fid.aname, reply := chan of ref Root);
+ root := <-reply;
+ if(workc == nil){
+ workc = chan of (ref Fid, ref Root);
+ spawn workproc(procid, workc, usedroot);
+ }
+ workc <-= (fid, root);
+ }
+ }
+
+ for(i = 0; i < Nprocs; i++){
+ (nil, workc) := <-token;
+ if(workc != nil)
+ workc <-= (nil, nil);
+ }
+ for(i = 0; i < Nprocs; i++){
+ getroot <-= (0, nil, nil, reply := chan of ref Root);
+ root := <-reply;
+ if(<-root.attached > 0)
+ clunk(0, root.fid);
+ }
+ usedroot <-= nil;
+ return <-finish;
+}
+
+workproc(procid: int, workc: chan of (ref Fid, ref Root), usedroot: chan of ref Root)
+{
+ while(((fid, root) := <-workc).t0 != nil){
+ # mark fid as dead only if it's a genuine server error, not if
+ # the server has just hung up.
+ if((err := resurrectfid(procid, fid, root)) != nil && sfd != nil){
+ log(err);
+ fid.state = DEAD;
+ }
+ usedroot <-= root;
+ token <-= (procid, workc);
+ }
+}
+
+resurrectfid(procid: int, fid: ref Fid, root: ref Root): string
+{
+ if(fid.state == AUTH)
+ return "auth fid discarded";
+ attached := <-root.attached;
+ if(attached == -1){
+ root.attached <-= -1;
+ return "root attach failed";
+ }
+ if(!attached || root.uname != fid.uname || root.aname != fid.aname){
+ if(attached)
+ clunk(procid, root.fid);
+ afid := NOFID;
+ if(fid.authed){
+ afid = fid.fid - 1; # see unusedfid()
+ if((err := auth(procid, afid, root.uname, root.aname)) != nil){
+ log(err);
+ afid = -1;
+ }
+ }
+ (err, qid) := attach(procid, root.fid, afid, fid.uname, fid.aname);
+ if(afid != NOFID)
+ clunk(procid, afid);
+ if(err != nil){
+ root.attached <-= -1;
+ return "attach failed: "+err;
+ }
+ root.uname = fid.uname;
+ root.aname = fid.aname;
+ root.qid = qid;
+ }
+ root.attached <-= 1;
+ (err, qid) := walk(procid, root.fid, fid.fid, fid.path, root.qid);
+ if(err != nil)
+ return err;
+ if(fid.state == OPEN && (err = openfid(procid, fid)) != nil){
+ clunk(procid, fid.fid);
+ return err;
+ }
+ return nil;
+}
+
+openfid(procid: int, fid: ref Fid): string
+{
+ (err, qid) := open(procid, fid.fid, fid.omode);
+ if(err != nil)
+ return err;
+ if(qid.path != fid.qid.path || qid.qtype != fid.qid.qtype)
+ return "qid mismatch on reopen";
+ return nil;
+}
+
+# store up to Nprocs separate root fids and dole them out to those that want them.
+fidproc(getroot: chan of (int, string, string, chan of ref Root), usedroot: chan of ref Root)
+{
+ roots := array[Nprocs] of ref Root;
+ n := 0;
+ maxfid := -1;
+ for(;;)alt{
+ (match, uname, aname, reply) := <-getroot =>
+ for(i := 0; i < n; i++)
+ if(match && roots[i].uname == uname && roots[i].aname == aname)
+ break;
+ if(i == n)
+ for(i = 0; i < n; i++)
+ if(roots[i].refcount == 0)
+ break;
+ if(i == n){
+ maxfid = unusedfid(maxfid);
+ roots[n] = ref Root(0, chan[1] of int, maxfid, Noqid, uname, aname);
+ roots[n++].attached <-= 0;
+ }
+ roots[i].refcount++;
+ reply <-= roots[i];
+ r := <-usedroot =>
+ if(r == nil)
+ exit;
+ r.refcount--;
+ }
+}
+
+clunk(procid: int, fid: int)
+{
+ pick m := fcall(ref Tmsg.Clunk(procid, fid)) {
+ Error =>
+ if(sfd != nil)
+ log("error on clunk: " + m.ename);
+ }
+}
+
+attach(procid, fid, afid: int, uname, aname: string): (string, Sys->Qid)
+{
+ pick m := fcall(ref Tmsg.Attach(procid, fid, afid, uname, aname)) {
+ Attach =>
+ return (nil, m.qid);
+ Error =>
+ return (m.ename, Noqid);
+ }
+ return (nil, Noqid); # not reached
+}
+
+read(procid, fid: int, buf: array of byte): (int, string)
+{
+ # XXX assume that offsets are ignored of auth fid reads/writes
+ pick m := fcall(ref Tmsg.Read(procid, fid, big 0, len buf)) {
+ Error =>
+ return (-1, m.ename);
+ Read =>
+ buf[0:] = m.data;
+ return (len m.data, nil);
+ }
+ return (-1, nil); # not reached
+}
+
+write(procid, fid: int, buf: array of byte): (int, string)
+{
+ # XXX assume that offsets are ignored of auth fid reads/writes
+ pick m := fcall(ref Tmsg.Write(procid, fid, big 0, buf)) {
+ Error =>
+ sys->werrstr(m.ename);
+ return (-1, sys->sprint("%r"));
+ Write =>
+ return (m.count, nil);
+ }
+ return (-1, nil); # not reached
+}
+
+auth(procid, fid: int, uname, aname: string): string
+{
+ if(factotum == nil)
+ return "no factotum available";
+
+ pick m := fcall(ref Tmsg.Auth(procid, fid, uname, aname)) {
+ Error =>
+ return m.ename;
+ }
+
+ readc := chan of (array of byte, chan of (int, string));
+ writec := chan of (array of byte, chan of (int, string));
+ done := chan of (ref Factotum->Authinfo, string);
+ spawn factotum->genproxy(readc, writec, done,
+ sys->open("/mnt/factotum/rpc", Sys->ORDWR),
+ "proto=p9any role=client "+keyspec);
+ for(;;)alt{
+ (buf, reply) := <-readc =>
+ reply <-= read(procid, fid, buf);
+ (buf, reply) := <-writec =>
+ reply <-= write(procid, fid, buf);
+ (authinfo, err) := <-done =>
+ if(authinfo == nil){
+ clunk(procid, fid);
+ return err;
+ }
+ # XXX check that authinfo.cuid == uname?
+ return nil;
+ }
+}
+
+# path is in reverse order; assume fid != newfid on entry.
+walk(procid: int, fid, newfid: int, path: list of string, qid: Sys->Qid): (string, Sys->Qid)
+{
+ names := array[len path] of string;
+ for(i := len names - 1; i >= 0; i--)
+ (names[i], path) = (hd path, tl path);
+ do{
+ w := names;
+ if(len w > Styx->MAXWELEM)
+ w = w[0:Styx->MAXWELEM];
+ names = names[len w:];
+ pick m := fcall(ref Tmsg.Walk(procid, fid, newfid, w)) {
+ Error =>
+ if(newfid == fid)
+ clunk(procid, newfid);
+ return ("walk error: "+m.ename, Noqid);
+ Walk =>
+ if(len m.qids != len w){
+ if(newfid == fid)
+ clunk(procid, newfid);
+ return ("walk: file not found", Noqid);
+ }
+ if(len m.qids > 0)
+ qid = m.qids[len m.qids - 1];
+ fid = newfid;
+ }
+ }while(len names > 0);
+ return (nil, qid);
+}
+
+open(procid: int, fid: int, mode: int): (string, Sys->Qid)
+{
+ pick m := fcall(ref Tmsg.Open(procid, fid, mode)) {
+ Error =>
+ return ("open: "+m.ename, Noqid);
+ Open =>
+ return (nil, m.qid); # XXX what if iounit doesn't match the original?
+ }
+ return (nil, Noqid); # not reached
+}
+
+fcall(m: ref Tmsg): ref Rmsg
+{
+ sendtmsg(m);
+ pick rm := <-procrmsg[m.tag] {
+ Readerror =>
+ procrmsg[m.tag] <-= rm;
+ return ref Rmsg.Error(rm.tag, rm.error);
+ Error =>
+ return rm;
+ * =>
+ if(rm.mtype() != m.mtype()+1)
+ return ref Rmsg.Error(m.tag, Etypemismatch);
+ return rm;
+ }
+}
+
+# find an unused fid (and make sure that the one before it is unused
+# too, in case we want to use it for an auth fid);
+unusedfid(maxfid: int): int
+{
+ for(f := maxfid + 1; ; f++)
+ if(fids.find(f) == nil && fids.find(f+1) == nil)
+ return f + 1;
+ abort("no unused fids - i don't believe it");
+ return 0;
+}
+
+# XXX what about message length limitations?
+sendtmsg(m: ref Tmsg)
+{
+ if(Debug) sys->print("%s\n", m.text());
+ d := m.pack();
+ if(sys->write(sfd, d, len d) != len d)
+ log(sys->sprint("tmsg write failed: %r")); # XXX could signal to redial
+}
+
+sendrmsg(m: ref Rmsg)
+{
+ d := m.pack();
+ if(sys->write(cfd, d, len d) != len d){
+ log(sys->sprint("rmsg write failed: %r"));
+ quit();
+ }
+}
+
+rmsgmarshal(finish: chan of int)
+{
+ for(;;)alt{
+ finish <-= 1 =>
+ exit;
+ m := <-rmsg =>
+ if(m == nil || tagof(m) == tagof(Rmsg.Readerror)){
+ sfd = nil;
+ for(i := 0; i < Nprocs; i++)
+ procrmsg[i] <-= ref Rmsg.Readerror(NOTAG, "hung up");
+ finish <-= 0;
+ exit;
+ }
+ if(m.tag >= Nprocs){
+ log("invalid reply message");
+ break;
+ }
+ # XXX if the server replies with a tag that no-one's waiting for. we'll lock up.
+ # (but is it much of a concern, given no flushes, etc?)
+ procrmsg[m.tag] <-= m;
+ }
+}
+
+rmsgreader(fd: ref Sys->FD, sync: chan of int)
+{
+ sync <-= sys->pctl(0, nil);
+ m: ref Rmsg;
+ do {
+ m = Rmsg.read(fd, msize);
+ if(Debug) sys->print("%s\n", m.text());
+ rmsg <-= m;
+ } while(m != nil && tagof(m) != tagof(Tmsg.Readerror));
+}
+
+tmsgreader()
+{
+ m: ref Tmsg;
+ do{
+ m = Tmsg.read(cfd, msize);
+ tmsg <-= m;
+ } while(m != nil && tagof(m) != tagof(Tmsg.Readerror));
+}
+
+abort(s: string)
+{
+ log(s);
+ raise "abort";
+}
+
+tmsgfid(t: ref Tmsg): int
+{
+ fid := NOFID;
+ pick m := t {
+ Attach =>
+ fid = m.afid;
+ Walk =>
+ fid = m.fid;
+ Open =>
+ fid = m.fid;
+ Create =>
+ fid = m.fid;
+ Read =>
+ fid = m.fid;
+ Write =>
+ fid = m.fid;
+ Clunk or
+ Stat or
+ Remove =>
+ fid = m.fid;
+ Wstat =>
+ fid = m.fid;
+ }
+ return fid;
+}
+
+blankfid: Fid;
+newfid(fid: int): ref Fid
+{
+ f := ref blankfid;
+ f.fid = fid;
+ if(fids.add(fid, f) == 0){
+ abort("duplicate fid "+string fid);
+ }
+ return f;
+}
+
+getfid(fid: int): ref Fid
+{
+ return fids.find(fid);
+}
+
+newtag(m: ref Tmsg): ref Tag
+{
+ # XXX what happens if the client sends a duplicate tag?
+ t := ref Tag(m, seqno++, 0, nil);
+ slot := t.m.tag & (NTAGHASH - 1);
+ t.next = tags[slot];
+ tags[slot] = t;
+ ntags++;
+ return t;
+}
+
+gettag(tag: int, destroy: int): ref Tag
+{
+ slot := tag & (NTAGHASH - 1);
+ prev: ref Tag;
+ for(t := tags[slot]; t != nil; t = t.next){
+ if(t.m.tag == tag)
+ break;
+ prev = t;
+ }
+ if(t == nil || !destroy)
+ return t;
+ if(prev == nil)
+ tags[slot] = t.next;
+ else
+ prev.next = t.next;
+ ntags--;
+ return t;
+}
+
+Table[T].new(nslots: int, nilval: T): ref Table[T]
+{
+ if(nslots == 0)
+ nslots = 13;
+ return ref Table[T](array[nslots] of list of (int, T), nilval);
+}
+
+Table[T].add(t: self ref Table[T], id: int, x: T): int
+{
+ slot := id % len t.items;
+ for(q := t.items[slot]; q != nil; q = tl q)
+ if((hd q).t0 == id)
+ return 0;
+ t.items[slot] = (id, x) :: t.items[slot];
+ return 1;
+}
+
+Table[T].del(t: self ref Table[T], id: int): int
+{
+ slot := id % len t.items;
+
+ p: list of (int, T);
+ r := 0;
+ for(q := t.items[slot]; q != nil; q = tl q){
+ if((hd q).t0 == id){
+ p = joinip(p, tl q);
+ r = 1;
+ break;
+ }
+ p = hd q :: p;
+ }
+ t.items[slot] = p;
+ return r;
+}
+
+Table[T].find(t: self ref Table[T], id: int): T
+{
+ for(p := t.items[id % len t.items]; p != nil; p = tl p)
+ if((hd p).t0 == id)
+ return (hd p).t1;
+ return t.nilval;
+}
+
+sort(a: array of ref Tag)
+{
+ mergesort(a, array[len a] of ref Tag);
+}
+
+mergesort(a, b: array of ref Tag)
+{
+ r := len a;
+ if (r > 1) {
+ m := (r-1)/2 + 1;
+ mergesort(a[0:m], b[0:m]);
+ mergesort(a[m:], b[m:]);
+ b[0:] = a;
+ for ((i, j, k) := (0, m, 0); i < m && j < r; k++) {
+ if (b[i].seq > b[j].seq)
+ a[k] = b[j++];
+ else
+ a[k] = b[i++];
+ }
+ if (i < m)
+ a[k:] = b[i:m];
+ else if (j < r)
+ a[k:] = b[j:r];
+ }
+}
+
+kill(pid: int, note: string): int
+{
+ fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);
+ if(fd == nil || sys->fprint(fd, "%s", note) < 0)
+ return -1;
+ return 0;
+}
+
+# join x to y, leaving result in arbitrary order.
+joinip[T](x, y: list of (int, T)): list of (int, T)
+{
+ if(len x > len y)
+ (x, y) = (y, x);
+ for(; x != nil; x = tl x)
+ y = hd x :: y;
+ return y;
+}
diff --git a/appl/lib/styxservers.b b/appl/lib/styxservers.b
new file mode 100644
index 00000000..034cd4b7
--- /dev/null
+++ b/appl/lib/styxservers.b
@@ -0,0 +1,605 @@
+implement Styxservers;
+
+#
+# Copyright © 1999 Vita Nuova Limited. All rights reserved.
+# Revisions copyright © 2000-2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+# Derived from Roger Peppe's Styxlib by Martin C. Atkins, 2001/2002 by
+# adding new helper functions, and then removing Dirgenmod and its helpers
+#
+# Further modified by Roger Peppe to simplify the interface by
+# adding the Navigator/Navop channel interface and making other changes,
+# including using the Styx module
+#
+# converted to revised Styx at Vita Nuova
+# further revised August/September 2002
+#
+# TO DO:
+# - directory reading interface revision?
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "styx.m";
+ styx: Styx;
+ Tmsg, Rmsg: import styx;
+
+include "styxservers.m";
+
+CHANHASHSIZE: con 32;
+DIRREADSIZE: con Styx->STATFIXLEN+4*20; # ``reasonable'' chunk for reading directories
+
+debug := 0;
+
+init(styxmod: Styx)
+{
+ sys = load Sys Sys->PATH;
+ styx = styxmod;
+}
+
+traceset(d: int)
+{
+ debug = d;
+}
+
+Styxserver.new(fd: ref Sys->FD, t: ref Navigator, rootpath: big): (chan of ref Tmsg, ref Styxserver)
+{
+ tchan := chan of ref Tmsg;
+ srv := ref Styxserver(fd, array[CHANHASHSIZE] of list of ref Fid, chan[1] of int, t, rootpath, 0, nil);
+
+ sync := chan of int;
+ spawn tmsgreader(fd, srv, tchan, sync);
+ <-sync;
+ return (tchan, srv);
+}
+
+tmsgreader(fd: ref Sys->FD, srv: ref Styxserver, tchan: chan of ref Tmsg, sync: chan of int)
+{
+ if(debug)
+ sys->pctl(Sys->NEWFD|Sys->NEWNS, fd.fd :: 2 :: nil);
+ else
+ sys->pctl(Sys->NEWFD|Sys->NEWNS, fd.fd :: nil);
+ sync <-= 1;
+ fd = sys->fildes(fd.fd);
+ m: ref Tmsg;
+ do {
+ m = Tmsg.read(fd, srv.msize);
+ if(debug && m != nil)
+ sys->fprint(sys->fildes(2), "<- %s\n", m.text());
+ tchan <-= m;
+ } while(m != nil && tagof(m) != tagof(Tmsg.Readerror));
+}
+
+Fid.clone(oc: self ref Fid, c: ref Fid): ref Fid
+{
+ # c.fid not touched, other values copied from c
+ c.path = oc.path;
+ c.qtype = oc.qtype;
+ c.isopen = oc.isopen;
+ c.mode = oc.mode;
+ c.doffset = oc.doffset;
+ c.uname = oc.uname;
+ c.param = oc.param;
+ c.data = oc.data;
+ return c;
+}
+
+Fid.walk(c: self ref Fid, qid: Sys->Qid)
+{
+ c.path = qid.path;
+ c.qtype = qid.qtype;
+}
+
+Fid.open(c: self ref Fid, mode: int, qid: Sys->Qid)
+{
+ c.isopen = 1;
+ c.mode = mode;
+ c.doffset = (0, 0);
+ c.path = qid.path;
+ c.qtype = qid.qtype;
+}
+
+Styxserver.reply(srv: self ref Styxserver, m: ref Rmsg): int
+{
+ if(debug)
+ sys->fprint(sys->fildes(2), "-> %s\n", m.text());
+ if(srv.replychan != nil){
+ srv.replychan <-= m;
+ return 0;
+ }
+ return srv.replydirect(m);
+}
+
+Styxserver.replydirect(srv: self ref Styxserver, m: ref Rmsg): int
+{
+ if(srv.msize == 0)
+ m = ref Rmsg.Error(m.tag, "Tversion not seen");
+ d := m.pack();
+ if(srv.msize != 0 && len d > srv.msize){
+ m = ref Rmsg.Error(m.tag, "Styx reply didn't fit");
+ d = m.pack();
+ }
+ return sys->write(srv.fd, d, len d);
+}
+
+Styxserver.attach(srv: self ref Styxserver, m: ref Tmsg.Attach): ref Fid
+{
+ (d, err) := srv.t.stat(srv.rootpath);
+ if(d == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, err));
+ return nil;
+ }
+ if((d.qid.qtype & Sys->QTDIR) == 0) {
+ srv.reply(ref Rmsg.Error(m.tag, Enotdir));
+ return nil;
+ }
+ c := srv.newfid(m.fid);
+ if(c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Einuse));
+ return nil;
+ }
+ c.uname = m.uname;
+ c.param = m.aname;
+ c.path = d.qid.path;
+ c.qtype = d.qid.qtype;
+ srv.reply(ref Rmsg.Attach(m.tag, d.qid));
+ return c;
+}
+
+walk1(n: ref Navigator, c: ref Fid, name: string): (ref Sys->Dir, string)
+{
+ (d, err) := n.stat(c.path);
+ if(d == nil)
+ return (nil, err);
+ if((d.qid.qtype & Sys->QTDIR) == 0)
+ return (nil, Enotdir);
+ if(!openok(c.uname, Styx->OEXEC, d.mode, d.uid, d.gid))
+ return (nil, Eperm);
+ (d, err) = n.walk(d.qid.path, name);
+ if(d == nil)
+ return (nil, err);
+ return (d, nil);
+}
+
+Styxserver.walk(srv: self ref Styxserver, m: ref Tmsg.Walk): ref Fid
+{
+ c := srv.getfid(m.fid);
+ if(c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
+ return nil;
+ }
+ if(c.isopen) {
+ srv.reply(ref Rmsg.Error(m.tag, Eopen));
+ return nil;
+ }
+ if(m.newfid != m.fid){
+ nc := srv.newfid(m.newfid);
+ if(nc == nil){
+ srv.reply(ref Rmsg.Error(m.tag, Einuse));
+ return nil;
+ }
+ c = c.clone(nc);
+ }
+ qids := array[len m.names] of Sys->Qid;
+ oldpath := c.path;
+ oldqtype := c.qtype;
+ for(i := 0; i < len m.names; i++){
+ (d, err) := walk1(srv.t, c, m.names[i]);
+ if(d == nil){
+ c.path = oldpath; # restore c
+ c.qtype = oldqtype;
+ if(m.newfid != m.fid)
+ srv.delfid(c);
+ if(i == 0)
+ srv.reply(ref Rmsg.Error(m.tag, err));
+ else
+ srv.reply(ref Rmsg.Walk(m.tag, qids[0:i]));
+ return nil;
+ }
+ c.walk(d.qid);
+ qids[i] = d.qid;
+ }
+ srv.reply(ref Rmsg.Walk(m.tag, qids));
+ return c;
+}
+
+Styxserver.canopen(srv: self ref Styxserver, m: ref Tmsg.Open): (ref Fid, int, ref Sys->Dir, string)
+{
+ c := srv.getfid(m.fid);
+ if(c == nil)
+ return (nil, 0, nil, Ebadfid);
+ if(c.isopen)
+ return (nil, 0, nil, Eopen);
+ (f, err) := srv.t.stat(c.path);
+ if(f == nil)
+ return (nil, 0, nil, err);
+ mode := openmode(m.mode);
+ if(mode == -1)
+ return (nil, 0, nil, Ebadarg);
+ if(mode != Sys->OREAD && f.qid.qtype & Sys->QTDIR)
+ return (nil, 0, nil, Eperm);
+ if(!openok(c.uname, m.mode, f.mode, f.uid, f.gid))
+ return (nil, 0, nil, Eperm);
+ if(m.mode & Sys->ORCLOSE) {
+ (dir, nil) := srv.t.walk(c.path, "..");
+ if(dir == nil || dir.qid.path == f.qid.path && dir.qid.qtype == f.qid.qtype || # can't remove root directory
+ !openok(c.uname, Sys->OWRITE, dir.mode, dir.uid, dir.gid))
+ return (nil, 0, nil, Eperm);
+ mode |= Sys->ORCLOSE;
+ }
+ return (c, mode, f, err);
+}
+
+Styxserver.open(srv: self ref Styxserver, m: ref Tmsg.Open): ref Fid
+{
+ (c, mode, f, err) := srv.canopen(m);
+ if(c == nil){
+ srv.reply(ref Rmsg.Error(m.tag, err));
+ return nil;
+ }
+ c.open(mode, f.qid);
+ srv.reply(ref Rmsg.Open(m.tag, f.qid, srv.iounit()));
+ return c;
+}
+
+Styxserver.cancreate(srv: self ref Styxserver, m: ref Tmsg.Create): (ref Fid, int, ref Sys->Dir, string)
+{
+ c := srv.getfid(m.fid);
+ if(c == nil)
+ return (nil, 0, nil, Ebadfid);
+ if(c.isopen)
+ return (nil, 0, nil, Eopen);
+ (d, err) := srv.t.stat(c.path);
+ if(d == nil)
+ return (nil, 0, nil, err);
+ if((d.mode & Sys->DMDIR) == 0)
+ return (nil, 0, nil, Enotdir);
+ if(m.name == "")
+ return (nil, 0, nil, Ename);
+ if(m.name == "." || m.name == "..")
+ return (nil, 0, nil, Edot);
+ if(!openok(c.uname, Sys->OWRITE, d.mode, d.uid, d.gid))
+ return (nil, 0, nil, Eperm);
+ if(srv.t.walk(d.qid.path, m.name).t0 != nil)
+ return (nil, 0, nil, Eexists);
+ if((mode := openmode(m.mode)) == -1)
+ return (nil, 0, nil, Ebadarg);
+ mode |= m.mode & Sys->ORCLOSE; # can create, so directory known to be writable
+ f := ref Sys->zerodir;
+ if(m.perm & Sys->DMDIR){
+ f.mode = m.perm & (~8r777 | (d.mode & 8r777));
+ f.qid.qtype = Sys->QTDIR;
+ }else{
+ f.mode = m.perm & (~8r666 | (d.mode & 8r666));
+ f.qid.qtype = Sys->QTFILE;
+ }
+ f.name = m.name;
+ f.uid = c.uname;
+ f.muid = c.uname;
+ f.gid = d.gid;
+ f.dtype = d.dtype;
+ f.dev = d.dev;
+ # caller must supply atime, mtime, qid.path
+ return (c, mode, f, nil);
+}
+
+Styxserver.canread(srv: self ref Styxserver, m: ref Tmsg.Read): (ref Fid, string)
+{
+ c := srv.getfid(m.fid);
+ if(c == nil)
+ return (nil, Ebadfid);
+ if(!c.isopen)
+ return (nil, Enotopen);
+ mode := c.mode & 3;
+ if(mode != Sys->OREAD && mode != Sys->ORDWR) # readable modes
+ return (nil, Eaccess);
+ if(m.count < 0 || m.count > srv.msize-Styx->IOHDRSZ)
+ return (nil, Ecount);
+ if(m.offset < big 0)
+ return (nil, Eoffset);
+ return (c, nil);
+}
+
+Styxserver.read(srv: self ref Styxserver, m: ref Tmsg.Read): ref Fid
+{
+ (c, err) := srv.canread(m);
+ if(c == nil){
+ srv.reply(ref Rmsg.Error(m.tag, err));
+ return nil;
+ }
+ if((c.qtype & Sys->QTDIR) == 0) {
+ srv.reply(ref Rmsg.Error(m.tag, Eperm));
+ return nil;
+ }
+ if(m.count <= 0){
+ srv.reply(ref Rmsg.Read(m.tag, nil));
+ return c;
+ }
+ a := array[m.count] of byte;
+ (offset, index) := c.doffset;
+ if(int m.offset != offset){ # rescan from the beginning
+ offset = 0;
+ index = 0;
+ }
+ p := 0;
+Dread:
+ while((d := srv.t.readdir(c.path, index, (m.count+DIRREADSIZE-1)/DIRREADSIZE)) != nil && (nd := len d) > 0){
+ for(i := 0; i < nd; i++) {
+ size := styx->packdirsize(*d[i]);
+ offset += size;
+ index++;
+ if(offset < int m.offset)
+ continue;
+ if((m.count -= size) < 0){ # won't fit, save state for next time
+ offset -= size;
+ index--;
+ break Dread;
+ }
+ de := styx->packdir(*d[i]);
+ a[p:] = de;
+ p += size;
+ }
+ }
+ c.doffset = (offset, index);
+ srv.reply(ref Rmsg.Read(m.tag, a[0:p]));
+ return c;
+}
+
+Styxserver.canwrite(srv: self ref Styxserver, m: ref Tmsg.Write): (ref Fid, string)
+{
+ c := srv.getfid(m.fid);
+ if(c == nil)
+ return (nil, Ebadfid);
+ if(!c.isopen)
+ return (nil, Enotopen);
+ if(c.qtype & Sys->QTDIR)
+ return (nil, Eperm);
+ mode := c.mode & 3;
+ if(mode != Sys->OWRITE && mode != Sys->ORDWR) # writable modes
+ return (nil, Eaccess);
+ if(m.offset < big 0)
+ return (nil, Eoffset);
+ # could check len m.data > iounit, but since we've got it now, it doesn't matter
+ return (c, nil);
+}
+
+Styxserver.stat(srv: self ref Styxserver, m: ref Tmsg.Stat)
+{
+ c := srv.getfid(m.fid);
+ if(c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
+ return;
+ }
+ (d, err) := srv.t.stat(c.path);
+ if(d == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, err));
+ return;
+ }
+ srv.reply(ref Rmsg.Stat(m.tag, *d));
+}
+
+Styxserver.canremove(srv: self ref Styxserver, m: ref Tmsg.Remove): (ref Fid, big, string)
+{
+ c := srv.getfid(m.fid);
+ if(c == nil)
+ return (nil, big 0, Ebadfid);
+ (dir, nil) := srv.t.walk(c.path, ".."); # this relies on .. working for non-directories
+ if(dir == nil)
+ return (nil, big 0, "can't find parent directory");
+ if(dir.qid.path == c.path && dir.qid.qtype == c.qtype || # can't remove root directory
+ !openok(c.uname, Sys->OWRITE, dir.mode, dir.uid, dir.gid))
+ return (nil, big 0, Eperm);
+ return (c, dir.qid.path, nil);
+}
+
+Styxserver.remove(srv: self ref Styxserver, m: ref Tmsg.Remove): ref Fid
+{
+ c := srv.getfid(m.fid);
+ if(c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
+ return nil;
+ }
+ srv.delfid(c); # Remove always clunks the fid
+ srv.reply(ref Rmsg.Error(m.tag, Eperm));
+ return c;
+}
+
+Styxserver.clunk(srv: self ref Styxserver, m: ref Tmsg.Clunk): ref Fid
+{
+ c := srv.getfid(m.fid);
+ if(c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
+ return nil;
+ }
+ srv.delfid(c);
+ srv.reply(ref Rmsg.Clunk(m.tag));
+ return c;
+}
+
+Styxserver.default(srv: self ref Styxserver, gm: ref Tmsg)
+{
+ if(gm == nil) {
+ srv.t.c <-= nil;
+ exit;
+ }
+ pick m := gm {
+ Readerror =>
+ srv.t.c <-= nil;
+ exit;
+ Version =>
+ if(srv.msize <= 0)
+ srv.msize = Styx->MAXRPC;
+ (msize, version) := styx->compatible(m, srv.msize, Styx->VERSION);
+ if(msize < 256){
+ srv.reply(ref Rmsg.Error(m.tag, "message size too small"));
+ break;
+ }
+ srv.msize = msize;
+ srv.reply(ref Rmsg.Version(m.tag, msize, version));
+ Auth =>
+ srv.reply(ref Rmsg.Error(m.tag, "authentication not required"));
+ Flush =>
+ srv.reply(ref Rmsg.Flush(m.tag));
+ Walk =>
+ srv.walk(m);
+ Open =>
+ srv.open(m);
+ Create =>
+ srv.reply(ref Rmsg.Error(m.tag, Eperm));
+ Read =>
+ srv.read(m);
+ Write =>
+ srv.reply(ref Rmsg.Error(m.tag, Eperm));
+ Clunk =>
+ srv.clunk(m);
+ # to delete on ORCLOSE:
+ # c := srv.clunk(m);
+ # if(c != nil && c.mode & Sys->ORCLOSE)
+ # srv.doremove(c);
+ Stat =>
+ srv.stat(m);
+ Remove =>
+ srv.remove(m);
+ Wstat =>
+ srv.reply(ref Rmsg.Error(m.tag, Eperm));
+ Attach =>
+ srv.attach(m);
+ * =>
+ sys->fprint(sys->fildes(2), "styxservers: unhandled Tmsg tag %d - should not happen\n", tagof gm);
+ raise "fail: unhandled case";
+ }
+}
+
+Styxserver.iounit(srv: self ref Styxserver): int
+{
+ n := srv.msize - Styx->IOHDRSZ;
+ if(n <= 0)
+ return 0; # unknown
+ return n;
+}
+
+Styxserver.getfid(srv: self ref Styxserver, fid: int): ref Fid
+{
+ # the list is safe to use without locking
+ for(l := srv.fids[fid & (CHANHASHSIZE-1)]; l != nil; l = tl l)
+ if((hd l).fid == fid)
+ return hd l;
+ return nil;
+}
+
+Styxserver.delfid(srv: self ref Styxserver, c: ref Fid)
+{
+ slot := c.fid & (CHANHASHSIZE-1);
+ nl: list of ref Fid;
+ srv.fidlock <-= 1;
+ for(l := srv.fids[slot]; l != nil; l = tl l)
+ if((hd l).fid != c.fid)
+ nl = (hd l) :: nl;
+ srv.fids[slot] = nl;
+ <-srv.fidlock;
+}
+
+Styxserver.allfids(srv: self ref Styxserver): list of ref Fid
+{
+ cl: list of ref Fid;
+ srv.fidlock <-= 1;
+ for(i := 0; i < len srv.fids; i++)
+ for(l := srv.fids[i]; l != nil; l = tl l)
+ cl = hd l :: cl;
+ <-srv.fidlock;
+ return cl;
+}
+
+Styxserver.newfid(srv: self ref Styxserver, fid: int): ref Fid
+{
+ srv.fidlock <-= 1;
+ if((c := srv.getfid(fid)) != nil){
+ <-srv.fidlock;
+ return nil; # illegal: fid in use
+ }
+ c = ref Fid;
+ c.path = big -1;
+ c.qtype = 0;
+ c.isopen = 0;
+ c.mode = 0;
+ c.fid = fid;
+ c.doffset = (0, 0);
+ slot := fid & (CHANHASHSIZE-1);
+ srv.fids[slot] = c :: srv.fids[slot];
+ <-srv.fidlock;
+ return c;
+}
+
+readstr(m: ref Tmsg.Read, d: string): ref Rmsg.Read
+{
+ return readbytes(m, array of byte d);
+}
+
+readbytes(m: ref Tmsg.Read, d: array of byte): ref Rmsg.Read
+{
+ r := ref Rmsg.Read(m.tag, nil);
+ if(m.offset >= big len d || m.offset < big 0)
+ return r;
+ offset := int m.offset;
+ e := offset + m.count;
+ if(e > len d)
+ e = len d;
+ r.data = d[offset:e];
+ return r;
+}
+
+Navigator.new(c: chan of ref Navop): ref Navigator
+{
+ return ref Navigator(c, chan of (ref Sys->Dir, string));
+}
+
+Navigator.stat(t: self ref Navigator, q: big): (ref Sys->Dir, string)
+{
+ t.c <-= ref Navop.Stat(t.reply, q);
+ return <-t.reply;
+}
+
+Navigator.walk(t: self ref Navigator, q: big, name: string): (ref Sys->Dir, string)
+{
+ t.c <-= ref Navop.Walk(t.reply, q, name);
+ return <-t.reply;
+}
+
+Navigator.readdir(t: self ref Navigator, q: big, offset, count: int): array of ref Sys->Dir
+{
+ a := array[count] of ref Sys->Dir;
+ t.c <-= ref Navop.Readdir(t.reply, q, offset, count);
+ i := 0;
+ while((d := (<-t.reply).t0) != nil)
+ if(i < count)
+ a[i++] = d;
+ if(i == 0)
+ return nil;
+ return a[0:i];
+}
+
+openmode(o: int): int
+{
+ OTRUNC, ORCLOSE, OREAD, ORDWR: import Sys;
+ o &= ~(OTRUNC|ORCLOSE);
+ if(o > ORDWR)
+ return -1;
+ return o;
+}
+
+access := array[] of {8r400, 8r200, 8r600, 8r100};
+openok(uname: string, omode: int, perm: int, fuid: string, fgid: string): int
+{
+ t := access[omode & 3];
+ if(omode & Sys->OTRUNC){
+ if(perm & Sys->DMDIR)
+ return 0;
+ t |= 8r200;
+ }
+ if(uname == fuid && (t&perm) == t)
+ return 1;
+ if(uname == fgid && (t&(perm<<3)) == t)
+ return 1;
+ return (t&(perm<<6)) == t;
+}
diff --git a/appl/lib/tables.b b/appl/lib/tables.b
new file mode 100644
index 00000000..a3ea38d2
--- /dev/null
+++ b/appl/lib/tables.b
@@ -0,0 +1,105 @@
+implement Tables;
+include "tables.m";
+
+Table[T].new(nslots: int, nilval: T): ref Table[T]
+{
+ if(nslots == 0)
+ nslots = 13;
+ return ref Table[T](array[nslots] of list of (int, T), nilval);
+}
+
+Table[T].add(t: self ref Table[T], id: int, x: T): int
+{
+ slot := id % len t.items;
+ for(q := t.items[slot]; q != nil; q = tl q)
+ if((hd q).t0 == id)
+ return 0;
+ t.items[slot] = (id, x) :: t.items[slot];
+ return 1;
+}
+
+Table[T].del(t: self ref Table[T], id: int): int
+{
+ slot := id % len t.items;
+
+ p: list of (int, T);
+ r := 0;
+ for(q := t.items[slot]; q != nil; q = tl q){
+ if((hd q).t0 == id){
+ p = joinip(p, tl q);
+ r = 1;
+ break;
+ }
+ p = hd q :: p;
+ }
+ t.items[slot] = p;
+ return r;
+}
+
+Table[T].find(t: self ref Table[T], id: int): T
+{
+ for(p := t.items[id % len t.items]; p != nil; p = tl p)
+ if((hd p).t0 == id)
+ return (hd p).t1;
+ return t.nilval;
+}
+
+Strhash[T].new(nslots: int, nilval: T): ref Strhash[T]
+{
+ if(nslots == 0)
+ nslots = 13;
+ return ref Strhash[T](array[nslots] of list of (string, T), nilval);
+}
+
+Strhash[T].add(t: self ref Strhash, id: string, x: T)
+{
+ slot := hash(id, len t.items);
+ t.items[slot] = (id, x) :: t.items[slot];
+}
+
+Strhash[T].del(t: self ref Strhash, id: string)
+{
+ slot := hash(id, len t.items);
+
+ p: list of (string, T);
+ for(q := t.items[slot]; q != nil; q = tl q)
+ if((hd q).t0 != id)
+ p = hd q :: p;
+ t.items[slot] = p;
+}
+
+Strhash[T].find(t: self ref Strhash, id: string): T
+{
+ for(p := t.items[hash(id, len t.items)]; p != nil; p = tl p)
+ if((hd p).t0 == id)
+ return (hd p).t1;
+ return t.nilval;
+}
+
+hash(s: string, n: int): int
+{
+ h := 0;
+ m := len s;
+ for(i:=0; i<m; i++){
+ h = 65599*h+s[i];
+ }
+ return (h & 16r7fffffff) % n;
+}
+
+rev[T](x: list of T): list of T
+{
+ l: list of T;
+ for(; x != nil; x = tl x)
+ l = hd x :: l;
+ return l;
+}
+
+# join x to y, leaving result in arbitrary order.
+joinip[T](x, y: list of (int, T)): list of (int, T)
+{
+ if(len x > len y)
+ (x, y) = (y, x);
+ for(; x != nil; x = tl x)
+ y = hd x :: y;
+ return y;
+}
diff --git a/appl/lib/tabs.b b/appl/lib/tabs.b
new file mode 100644
index 00000000..d4314272
--- /dev/null
+++ b/appl/lib/tabs.b
@@ -0,0 +1,191 @@
+implement Tabs;
+
+# pseudo-widget for folder tab selections
+
+#
+# Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved.
+# Revisions Copyright © 2000-2002 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "tk.m";
+ tk: Tk;
+
+include "string.m";
+ str: String; # could load on demand
+
+include "tabs.m";
+
+TABSXdelta : con 2;
+TABSXslant : con 5;
+TABSXoff : con 5;
+TABSYheight : con 35;
+TABSYtop : con 10;
+TABSBord : con 3;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ tk = load Tk Tk->PATH;
+ str = load String String->PATH;
+}
+
+mktabs(t: ref Tk->Toplevel, dot: string, tabs: array of (string, string), dflt: int): chan of string
+{
+ lab, widg: string;
+ cmd(t, "canvas "+dot+" -height "+string TABSYheight);
+ cmd(t, "pack propagate "+dot+" 0");
+ c := chan of string;
+ tk->namechan(t, c, dot[1:]);
+ xpos := 2*TABSXdelta;
+ top := 10;
+ ypos := TABSYheight - 3;
+ back := cmd(t, dot+" cget -background");
+ dark := "#999999";
+ light := "#ffffff";
+ w := 20;
+ h := 30;
+ last := "";
+ for(i := 0; i < len tabs; i++){
+ (lab, widg) = tabs[i];
+ tag := "tag" + string i;
+ sel := "sel" + string i;
+ xs := xpos;
+ xpos += TABSXslant + TABSXoff;
+ v := cmd(t, dot+" create text "+string xpos+" "+string ypos+" -text "+tk->quote(lab)+" -anchor sw -tags "+tag);
+ bbox := tk->cmd(t, dot+" bbox "+tag);
+ if(bbox[0] == '!')
+ break;
+ (r, nil) := parserect(bbox);
+ r.max.x += TABSXoff;
+ x1 := " "+string xs;
+ x2 := " "+string(xs + TABSXslant);
+ x3 := " "+string r.max.x;
+ x4 := " "+string(r.max.x + TABSXslant);
+ y1 := " "+string(TABSYheight - 2);
+ y2 := " "+string TABSYtop;
+ cmd(t, dot+" create polygon " + x1+y1 + x2+y2 + x3+y2 + x4+y1 +
+ " -fill "+back+" -tags "+tag);
+ cmd(t, dot+" create line " + x3+y2 + x4+y1 +
+ " -fill "+dark+" -width 3 -tags "+tag);
+ cmd(t, dot+" create line " + x1+y1 + x2+y2 + x3+y2 +
+ " -fill "+light+" -width 3 -tags "+tag);
+
+ x1 = " "+string(xs+2);
+ x4 = " "+string(r.max.x + TABSXslant - 2);
+ y1 = " "+string(TABSYheight);
+ cmd(t, dot+" create line " + x1+y1 + x4+y1 +
+ " -fill "+back+" -width 5 -tags "+sel);
+
+ cmd(t, dot+" raise "+v);
+ cmd(t, dot+" bind "+tag+" <ButtonRelease-1> 'send "+
+ dot[1:]+" "+string i);
+
+ cmd(t, dot+" lower "+tag+" "+last);
+ last = tag;
+
+ xpos = r.max.x;
+ ww := int cmd(t, widg+" cget -width");
+ wh := int cmd(t, widg+" cget -height");
+ if(wh > h)
+ h = wh;
+ if(ww > w)
+ w = ww;
+ }
+ xpos += 4*TABSXslant;
+ if(w < xpos)
+ w = xpos;
+
+ for(i = 0; i < len tabs; i++){
+ (nil, widg) = tabs[i];
+ cmd(t, "pack propagate "+widg+" 0");
+ cmd(t, widg+" configure -width "+string w+" -height "+string h);
+ }
+
+ w += 2*TABSBord;
+ h += 2*TABSBord + TABSYheight;
+
+ cmd(t, dot+" create line 0 "+string TABSYheight+
+ " "+string w+" "+string TABSYheight+" -width 3 -fill "+light);
+ cmd(t, dot+" create line 1 "+string TABSYheight+
+ " 1 "+string(h-1)+" -width 3 -fill "+light);
+ cmd(t, dot+" create line 0 "+string(h-1)+
+ " "+string w+" "+string(h-1)+" -width 3 -fill "+dark);
+ cmd(t, dot+" create line "+string(w-1)+" "+string TABSYheight+
+ " "+string(w-1)+" "+string(h-1)+" -width 3 -fill "+dark);
+
+ cmd(t, dot+" configure -width "+string w+" -height "+string h);
+ cmd(t, dot+" configure -scrollregion {0 0 "+string w+" "+string h+"}");
+ tabsctl(t, dot, tabs, -1, string dflt);
+ return c;
+}
+
+tabsctl(t: ref Tk->Toplevel,
+ dot: string,
+ tabs: array of (string, string),
+ id: int,
+ s: string): int
+{
+ lab, widg: string;
+
+ nid := int s;
+ if(id == nid)
+ return id;
+ if(id >= 0){
+ (lab, widg) = tabs[id];
+ tag := "tag" + string id;
+ cmd(t, dot+" lower sel" + string id);
+ pos := cmd(t, dot+" coords " + tag);
+ if(len pos >= 1 && pos[0] != '!'){
+ (p, nil) := parsept(pos);
+ cmd(t, dot+" coords "+tag+" "+string(p.x+1)+
+ " "+string(p.y+1));
+ }
+ if(id > 0)
+ cmd(t, dot+" lower "+ tag + " tag"+string (id - 1));
+ cmd(t, dot+" delete win" + string id);
+ }
+ id = nid;
+ (lab, widg) = tabs[id];
+ pos := tk->cmd(t, dot+" coords tag" + string id);
+ if(len pos >= 1 && pos[0] != '!'){
+ (p, nli) := parsept(pos);
+ cmd(t, dot+" coords tag"+string id+" "+string(p.x-1)+" "+string(p.y-1));
+ }
+ cmd(t, dot+" raise tag"+string id);
+ cmd(t, dot+" raise sel"+string id);
+ cmd(t, dot+" create window "+string TABSBord+" "+
+ string(TABSYheight+TABSBord)+" -window "+widg+" -anchor nw -tags win"+string id);
+ cmd(t, "update");
+ return id;
+}
+
+parsept(s: string): (Draw->Point, string)
+{
+ p: Draw->Point;
+
+ (p.x, s) = str->toint(s, 10);
+ (p.y, s) = str->toint(s, 10);
+ return (p, s);
+}
+
+parserect(s: string): (Draw->Rect, string)
+{
+ r: Draw->Rect;
+
+ (r.min, s) = parsept(s);
+ (r.max, s) = parsept(s);
+ return (r, s);
+}
+
+cmd(top: ref Tk->Toplevel, s: string): string
+{
+ e := tk->cmd(top, s);
+ if (e != nil && e[0] == '!')
+ sys->fprint(sys->fildes(2), "%s: tk error %s on [%s]\n", PATH, e, s);
+ return e;
+}
diff --git a/appl/lib/tcl.m b/appl/lib/tcl.m
new file mode 100644
index 00000000..4062e541
--- /dev/null
+++ b/appl/lib/tcl.m
@@ -0,0 +1,19 @@
+Tcl_Core: module {
+
+ PATH : con "/dis/lib/tcl_core.dis";
+ TclData : adt {
+ context : ref Draw->Context;
+ top : ref Tk->Toplevel;
+ lines : chan of string;
+ debug : int;
+ };
+
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+ grab_lines : fn(new_inp,unfin : string, lines: chan of string);
+ prepass : fn(line : string) : string;
+ evalcmd : fn(line : string,termchar : int) : string;
+ clear_error : fn();
+ set_top : fn(win:ref Tk->Toplevel);
+ finished : fn(s : string,termchar : int) : int;
+ notify : fn(num : int, s: string) : string;
+};
diff --git a/appl/lib/tcl_calc.b b/appl/lib/tcl_calc.b
new file mode 100644
index 00000000..6844dbef
--- /dev/null
+++ b/appl/lib/tcl_calc.b
@@ -0,0 +1,909 @@
+implement TclLib;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "tk.m";
+
+include "string.m";
+ str : String;
+
+include "tcl.m";
+
+include "tcllib.m";
+
+include "math.m";
+ math : Math;
+
+include "regex.m";
+ regex : Regex;
+
+include "utils.m";
+ htab: Int_Hashtab;
+
+IHash: import htab;
+
+leaf : adt {
+ which : int;
+ s_val : string;
+ i_val : int;
+ r_val : real;
+};
+
+where : int;
+text:string;
+EOS,MALFORMED,UNKNOWN,REAL,INT,STRING,FUNC,ADD,SUB,MUL,MOD,DIV,LAND,
+LOR,BAND,BOR,BEOR,EXCL,TILDE,QUEST,COLON,F_ABS,F_ACOS,F_ASIN,F_ATAN,
+F_ATAN2,F_CEIL,F_COS,F_COSH,F_EXP,F_FLOOR,F_FMOD,F_HYPOT,F_LOG,F_LOG10,
+F_POW,F_SIN,F_SINH,F_SQRT,F_TAN,F_TANH,L_BRACE,R_BRACE,COMMA,LSHIF,RSHIF,
+LT,GT,LEQ,GEQ,EQ,NEQ : con iota;
+i_val : int;
+r_val : real;
+s_val : string;
+numbers : con "-?(([0-9]+)|([0-9]*\\.[0-9]+)([eE][-+]?[0-9]+)?)";
+re : Regex->Re;
+f_table : ref IHash;
+started : int;
+
+# does an eval on a string. The string is assumed to be
+# mathematically correct. No Tcl parsing is done.
+
+commands := array[] of {"calc"};
+
+about() : array of string {
+ return commands;
+}
+
+init() : string {
+ sys = load Sys Sys->PATH;
+ str = load String String->PATH;
+ math = load Math Math->PATH;
+ regex = load Regex Regex->PATH;
+ htab = load Int_Hashtab Int_Hashtab->PATH;
+ started=1;
+ if (regex==nil || math==nil || str==nil || htab==nil)
+ return "Cannot initialise calc module.";
+ f_table=htab->alloc(101);
+ f_table.insert("abs",F_ABS);
+ f_table.insert("acos",F_ACOS);
+ f_table.insert("asin",F_ASIN);
+ f_table.insert("atan",F_ATAN);
+ f_table.insert("atan2",F_ATAN2);
+ f_table.insert("ceil",F_CEIL);
+ f_table.insert("cos",F_COS);
+ f_table.insert("cosh",F_COSH);
+ f_table.insert("exp",F_EXP);
+ f_table.insert("floor",F_FLOOR);
+ f_table.insert("fmod",F_FMOD);
+ f_table.insert("hypot",F_HYPOT);
+ f_table.insert("log",F_LOG);
+ f_table.insert("log10",F_LOG10);
+ f_table.insert("pow",F_POW);
+ f_table.insert("sin",F_SIN);
+ f_table.insert("sinh",F_SINH);
+ f_table.insert("sqrt",F_SQRT);
+ f_table.insert("tan",F_TAN);
+ f_table.insert("tanh",F_TANH);
+ (re,nil)=regex->compile(numbers, 0);
+ return nil;
+}
+
+uarray:= array[] of { EXCL, 0, 0, 0, MOD, BAND, 0, L_BRACE, R_BRACE, MUL,
+ ADD, COMMA, SUB, 0, DIV, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, COLON,
+ 0, LT, EQ, GT, QUEST};
+
+getTok(eat : int) : int {
+ val, s : string;
+ dec:=0;
+ s=text;
+ i:=0;
+ if (s==nil)
+ return EOS;
+ while(i<len s && (s[i]==' '||s[i]=='\t')) i++;
+ if (i==len s)
+ return EOS;
+ case s[i]{
+ '+' or '-' or '*' or '?' or '%' or '/' or '('
+ or ')' or ',' or ':' =>
+ if (eat)
+ text=s[i+1:];
+ return uarray[s[i]-'!'];
+ '~' =>
+ if (eat)
+ text=s[i+1:];
+ return TILDE;
+ '^' =>
+ if (eat)
+ text=s[i+1:];
+ return BEOR;
+ '&' =>
+ if (s[i+1]=='&'){
+ if (eat)
+ text=s[i+2:];
+ return LAND;
+ }
+ if (eat)
+ text=s[i+1:];
+ return BAND;
+
+ '|' =>
+ if (s[i+1]=='|'){
+ if (eat)
+ text=s[i+2:];
+ return LOR;
+ }
+ if (eat)
+ text=s[i+1:];
+ return BOR;
+
+ '!' =>
+ if (s[i+1]=='='){
+ if (eat)
+ text=s[i+2:];
+ return NEQ;
+ }
+ if (eat)
+ text=s[i+1:];
+ return EXCL;
+ '=' =>
+ if (s[i+1]!='=')
+ return UNKNOWN;
+ if (eat)
+ text=s[i+2:];
+ return EQ;
+ '>' =>
+ case s[i+1]{
+ '>' =>
+ if (eat)
+ text=s[i+2:];
+ return RSHIF;
+ '=' =>
+ if (eat)
+ text=s[i+2:];
+ return GEQ;
+ * =>
+ if (eat)
+ text=s[i+1:];
+ return GT;
+ }
+ '<' =>
+ case s[i+1]{
+ '<' =>
+ if (eat)
+ text=s[i+2:];
+ return LSHIF;
+ '=' =>
+ if (eat)
+ text=s[i+2:];
+ return LEQ;
+ * =>
+ if (eat)
+ text=s[i+1:];
+ return LT;
+ }
+ '0' =>
+ return oct_hex(eat);
+ '1' to '9'
+ or '.'=>
+
+ match:=regex->execute(re,s[i:]);
+ if (match != nil)
+ (i1, i2) := match[0];
+ if (match==nil || i1!=0)
+ sys->print("ARRG! non-number where number should be!");
+ if (eat)
+ text=s[i+i2:];
+ val=s[i:i+i2];
+ if (str->in('.',val) || str->in('e',val)
+ || str->in('E',val)) {
+ r_val=real val;
+ return REAL;
+ }
+ i_val=int val;
+ return INT;
+ * =>
+ return get_func(eat);
+ }
+ return UNKNOWN;
+}
+
+oct_hex(eat : int) : int {
+ s:=text;
+ rest : string;
+ if (len s == 1){
+ i_val=0;
+ if (eat)
+ text=nil;
+ return INT;
+ }
+ if(s[1]=='x' || s[1]=='X'){
+ (i_val,rest)=str->toint(s[2:],16);
+ if (eat)
+ text = rest;
+ return INT;
+ }
+ if (s[1]=='.'){
+ match:=regex->execute(re,s);
+ if (match != nil)
+ (i1, i2) := match[0];
+ if (match==nil || i1!=0)
+ sys->print("ARRG!");
+ if (eat)
+ text=s[i2:];
+ val:=s[0:i2];
+ r_val=real val;
+ return REAL;
+ }
+ (i_val,rest)=str->toint(s[1:],8);
+ if (eat)
+ text = rest;
+ return INT;
+}
+
+get_func(eat : int) : int{
+ s:=text;
+ i:=0;
+ tok:=STRING;
+ while(i<len s && ((s[i]>='a' && s[i]<='z') ||
+ (s[i]>='A' && s[i]<='Z') ||
+ (s[i]>='0' && s[i]<='9') || (s[i]=='_'))) i++;
+ (found,val):=f_table.find(s[0:i]);
+ if (found)
+ tok=val;
+ else
+ s_val = s[0:i];
+ if (eat)
+ text = s[i:];
+ return tok;
+}
+
+
+exec(tcl: ref Tcl_Core->TclData,argv : array of string) : (int,string){
+ if (tcl==nil);
+ if (!started)
+ if ((msg:=init())!=nil)
+ return (1,msg);
+ retval : leaf;
+ expr:="";
+ for (i:=0;i<len argv;i++){
+ expr+=argv[i];
+ expr[len expr]=' ';
+ }
+ if (expr=="")
+ return (1,"Error!");
+ text=expr[0:len expr-1];
+ #sys->print("Text is %s\n",text);
+ retval = expr_9();
+ if (retval.which == UNKNOWN)
+ return (1,"Error!");
+ if (retval.which == INT)
+ return (0,string retval.i_val);
+ if (retval.which == STRING)
+ return (0,retval.s_val);
+ return (0,string retval.r_val);
+}
+
+expr_9() : leaf {
+ retval : leaf;
+ r1:=expr_8();
+ tok := getTok(0);
+ if(tok==QUEST){
+ getTok(1);
+ r2:=expr_8();
+ if (getTok(1)!=COLON)
+ r1.which=UNKNOWN;
+ r3:=expr_8();
+ if (r1.which == INT && r1.i_val==0)
+ return r3;
+ if (r1.which == INT && r1.i_val!=0)
+ return r2;
+ if (r1.which == REAL && r1.r_val==0.0)
+ return r3;
+ if (r1.which == REAL && r1.r_val!=0.0)
+ return r2;
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ return r1;
+}
+
+
+expr_8() : leaf {
+ retval : leaf;
+ r1:=expr_7();
+ retval=r1;
+ tok := getTok(0);
+ if (tok == LOR){
+ getTok(1);
+ r2:=expr_7(); # start again?
+ if (r1.which!=INT || r2.which!=INT){
+ retval.which = UNKNOWN;
+ return retval;
+ }
+ retval.i_val=r1.i_val || r2.i_val;
+ return retval;
+ }
+ return retval;
+}
+
+expr_7() : leaf {
+ retval : leaf;
+ r1:=expr_6();
+ retval=r1;
+ tok := getTok(0);
+ if (tok == LAND){
+ getTok(1);
+ r2:=expr_6();
+ if (r1.which!=INT || r2.which!=INT){
+ retval.which = UNKNOWN;
+ return retval;
+ }
+ retval.i_val=r1.i_val && r2.i_val;
+ return retval;
+ }
+ return retval;
+}
+
+expr_6() : leaf {
+ retval : leaf;
+ r1:=expr_5();
+ retval=r1;
+ tok := getTok(0);
+ if (tok == BOR){
+ getTok(1);
+ r2:=expr_5();
+ if (r1.which!=INT || r2.which!=INT){
+ retval.which = UNKNOWN;
+ return retval;
+ }
+ retval.i_val=r1.i_val | r2.i_val;
+ return retval;
+ }
+ return retval;
+}
+
+expr_5() : leaf {
+ retval : leaf;
+ r1:=expr_4();
+ retval=r1;
+ tok := getTok(0);
+ if (tok == BEOR){
+ getTok(1);
+ r2:=expr_4();
+ if (r1.which!=INT || r2.which!=INT){
+ retval.which = UNKNOWN;
+ return retval;
+ }
+ retval.i_val=r1.i_val ^ r2.i_val;
+ return retval;
+ }
+ return retval;
+}
+
+expr_4() : leaf {
+ retval : leaf;
+ r1:=expr_3();
+ retval=r1;
+ tok := getTok(0);
+ if (tok == BAND){
+ getTok(1);
+ r2:=expr_3();
+ if (r1.which!=INT || r2.which!=INT){
+ retval.which = UNKNOWN;
+ return retval;
+ }
+ retval.i_val=r1.i_val & r2.i_val;
+ return retval;
+ }
+ return retval;
+}
+
+expr_3() : leaf {
+ retval : leaf;
+ r1:=expr_2();
+ retval=r1;
+ tok:=getTok(0);
+ if (tok==EQ || tok==NEQ){
+ retval.which=INT;
+ getTok(1);
+ r2:=expr_2();
+ if (r1.which==UNKNOWN || r2.which==UNKNOWN){
+ r1.which=UNKNOWN;
+ return r1;
+ }
+ if (tok==EQ){
+ case r1.which {
+ STRING =>
+ if (r2.which == INT)
+ retval.i_val =
+ (r1.s_val == string r2.i_val);
+ else if (r2.which == REAL)
+ retval.i_val =
+ (r1.s_val == string r2.r_val);
+ else retval.i_val =
+ (r1.s_val == r2.s_val);
+ INT =>
+ if (r2.which == INT)
+ retval.i_val =
+ (r1.i_val == r2.i_val);
+ else if (r2.which == REAL)
+ retval.i_val =
+ (real r1.i_val == r2.r_val);
+ else retval.i_val =
+ (string r1.i_val == r2.s_val);
+ REAL =>
+ if (r2.which == INT)
+ retval.i_val =
+ (r1.r_val == real r2.i_val);
+ else if (r2.which == REAL)
+ retval.i_val =
+ (r1.r_val == r2.r_val);
+ else retval.i_val =
+ (string r1.r_val == r2.s_val);
+ }
+ }
+ else {
+ case r1.which {
+ STRING =>
+ if (r2.which == INT)
+ retval.i_val =
+ (r1.s_val != string r2.i_val);
+ else if (r2.which == REAL)
+ retval.i_val =
+ (r1.s_val != string r2.r_val);
+ else retval.i_val =
+ (r1.s_val != r2.s_val);
+ INT =>
+ if (r2.which == INT)
+ retval.i_val =
+ (r1.i_val != r2.i_val);
+ else if (r2.which == REAL)
+ retval.i_val =
+ (real r1.i_val != r2.r_val);
+ else retval.i_val =
+ (string r1.i_val != r2.s_val);
+ REAL =>
+ if (r2.which == INT)
+ retval.i_val =
+ (r1.r_val != real r2.i_val);
+ else if (r2.which == REAL)
+ retval.i_val =
+ (r1.r_val != r2.r_val);
+ else retval.i_val =
+ (string r1.r_val != r2.s_val);
+ }
+ }
+ return retval;
+ }
+ return retval;
+}
+
+
+expr_2() : leaf {
+ retval : leaf;
+ ar1,ar2 : real;
+ s1,s2 : string;
+ r1:=expr_1();
+ retval=r1;
+ tok:=getTok(0);
+ if (tok==LT || tok==GT || tok ==LEQ || tok==GEQ){
+ retval.which=INT;
+ getTok(1);
+ r2:=expr_1();
+ if (r1.which == STRING || r2.which == STRING){
+ if (r1.which==STRING)
+ s1=r1.s_val;
+ else if (r1.which==INT)
+ s1=string r1.i_val;
+ else s1= string r1.r_val;
+ if (r2.which==STRING)
+ s2=r2.s_val;
+ else if (r2.which==INT)
+ s2=string r2.i_val;
+ else s2= string r2.r_val;
+ case tok{
+ LT =>
+ retval.i_val = (s1<s2);
+ GT =>
+ retval.i_val = (s1>s2);
+ LEQ =>
+ retval.i_val = (s1<=s2);
+ GEQ =>
+ retval.i_val = (s1>=s2);
+ }
+ return retval;
+ }
+ if (r1.which==UNKNOWN || r2.which==UNKNOWN){
+ r1.which=UNKNOWN;
+ return r1;
+ }
+ if (r1.which == INT)
+ ar1 = real r1.i_val;
+ else
+ ar1 = r1.r_val;
+ if (r2.which == INT)
+ ar2 = real r2.i_val;
+ else
+ ar2 = r2.r_val;
+ case tok{
+ LT =>
+ retval.i_val = (ar1<ar2);
+ GT =>
+ retval.i_val = (ar1>ar2);
+ LEQ =>
+ retval.i_val = (ar1<=ar2);
+ GEQ =>
+ retval.i_val = (ar1>=ar2);
+ }
+ return retval;
+ }
+ return retval;
+}
+expr_1() : leaf {
+ retval : leaf;
+ r1:=expr0();
+ retval=r1;
+ tok := getTok(0);
+ if (tok == LSHIF || tok==RSHIF){
+ getTok(1);
+ r2:=expr0();
+ if (r1.which!=INT || r2.which!=INT){
+ retval.which = UNKNOWN;
+ return retval;
+ }
+ if (tok == LSHIF)
+ retval.i_val=r1.i_val << r2.i_val;
+ if (tok == RSHIF)
+ retval.i_val=r1.i_val >> r2.i_val;
+ return retval;
+ }
+ return retval;
+}
+
+expr0() : leaf {
+ retval : leaf;
+ r1:=expr1();
+ retval=r1;
+ tok := getTok(0);
+ while(tok==ADD || tok==SUB){
+ getTok(1);
+ r2:=expr1();
+ if (r1.which==UNKNOWN || r2.which==UNKNOWN){
+ r1.which=UNKNOWN;
+ return r1;
+ }
+ if (r2.which==r1.which){
+ case tok{
+ ADD =>
+ if (r1.which==INT)
+ r1.i_val+=r2.i_val;
+ else if (r1.which==REAL)
+ r1.r_val+=r2.r_val;
+ SUB =>
+ if (r1.which==INT)
+ r1.i_val-=r2.i_val;
+ else if (r1.which==REAL)
+ r1.r_val-=r2.r_val;
+ }
+ retval = r1;
+ }else{
+ retval.which = REAL;
+ ar1,ar2 : real;
+ if (r1.which==INT)
+ ar1= real r1.i_val;
+ else
+ ar1 = r1.r_val;
+ if (r2.which==INT)
+ ar2= real r2.i_val;
+ else
+ ar2 = r2.r_val;
+ if (tok==ADD)
+ retval.r_val = ar1+ar2;
+ if (tok==SUB)
+ retval.r_val = ar1-ar2;
+ }
+ tok=getTok(0);
+ }
+ return retval;
+}
+
+expr1() : leaf {
+ retval : leaf;
+ r1:=expr2();
+ retval=r1;
+ tok := getTok(0);
+ while(tok==MUL || tok==DIV || tok==MOD){
+ getTok(1);
+ r2:=expr2();
+ if (tok==MOD){
+ if (r1.which!=INT && r2.which!=INT){
+ r1.which=UNKNOWN;
+ return r1;
+ }
+ r1.i_val %= r2.i_val;
+ return r1;
+ }
+ if (r1.which==UNKNOWN || r2.which==UNKNOWN){
+ r1.which=UNKNOWN;
+ return r1;
+ }
+ if (r2.which==r1.which){
+ case tok{
+ MUL =>
+ if (r1.which==INT)
+ r1.i_val*=r2.i_val;
+ else if (r1.which==REAL)
+ r1.r_val*=r2.r_val;
+ DIV =>
+ if (r1.which==INT)
+ r1.i_val/=r2.i_val;
+ else if (r1.which==REAL)
+ r1.r_val/=r2.r_val;
+ }
+ retval = r1;
+ }else{
+ retval.which = REAL;
+ ar1,ar2 : real;
+ if (r1.which==INT)
+ ar1= real r1.i_val;
+ else
+ ar1 = r1.r_val;
+ if (r2.which==INT)
+ ar2= real r2.i_val;
+ else
+ ar2 = r2.r_val;
+ if (tok==MUL)
+ retval.r_val = ar1*ar2;
+ if (tok==DIV)
+ retval.r_val = ar1/ar2;
+ }
+ tok=getTok(0);
+ }
+ return retval;
+}
+
+expr2() : leaf {
+ tok := getTok(0);
+ if(tok==ADD || tok==SUB || tok==EXCL || tok==TILDE){
+ getTok(1);
+ r1:=expr2();
+ if (r1.which!=UNKNOWN)
+ case tok{
+ ADD =>
+ ;
+ SUB =>
+ if (r1.which==INT)
+ r1.i_val=-r1.i_val;
+ else if (r1.which==REAL)
+ r1.r_val=-r1.r_val;
+ EXCL =>
+ if (r1.which != INT)
+ r1.which=UNKNOWN;
+ else
+ r1.i_val = !r1.i_val;
+ TILDE =>
+ if (r1.which != INT)
+ r1.which=UNKNOWN;
+ else
+ r1.i_val = ~r1.i_val;
+ }
+ else
+ r1.which = UNKNOWN;
+ return r1;
+ }
+ return expr5();
+}
+
+do_func(tok : int) : leaf {
+ retval : leaf;
+ r1,r2 : real;
+ ok : int;
+ retval.which=REAL;
+ case tok{
+ F_ACOS =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->acos(r1);
+ F_ASIN =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->asin(r1);
+ F_ATAN =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->atan(r1);
+ F_ATAN2 =>
+ (ok,r1,r2)=pars_rfunc(2);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->atan2(r1,r2);
+ F_CEIL =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->ceil(r1);
+ F_COS =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->cos(r1);
+ F_COSH =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->cosh(r1);
+ F_EXP =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->exp(r1);
+ F_FLOOR =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->floor(r1);
+ F_FMOD =>
+ (ok,r1,r2)=pars_rfunc(2);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->fmod(r1,r2);
+ F_HYPOT =>
+ (ok,r1,r2)=pars_rfunc(2);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->hypot(r1,r2);
+ F_LOG =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->log(r1);
+ F_LOG10 =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->log10(r1);
+ F_POW =>
+ (ok,r1,r2)=pars_rfunc(2);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->pow(r1,r2);
+ F_SIN =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->sin(r1);
+ F_SINH =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->sinh(r1);
+ F_SQRT =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->sqrt(r1);
+ F_TAN =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->tan(r1);
+ F_TANH =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->tanh(r1);
+ F_ABS =>
+ (ok,r1,r2)=pars_rfunc(1);
+ if (!ok){
+ retval.which=UNKNOWN;
+ return retval;
+ }
+ retval.r_val=math->fabs(r1);
+ * =>
+ sys->print("unexpected op %d\n", tok);
+ retval.which=UNKNOWN;
+ }
+ return retval;
+}
+
+pars_rfunc(args : int) : (int,real,real){
+ a1,a2 : real;
+ ok := 1;
+ if (getTok(0)!=L_BRACE)
+ ok=0;
+ getTok(1);
+ r1:=expr_9();
+ if (r1.which == INT)
+ a1 = real r1.i_val;
+ else if (r1.which == REAL)
+ a1 = r1.r_val;
+ else ok=0;
+ if(args==2){
+ if (getTok(0)!=COMMA)
+ ok=0;
+ getTok(1);
+ r2:=expr_9();
+ if (r2.which == INT)
+ a2 = real r2.i_val;
+ else if (r2.which == REAL)
+ a2 = r2.r_val;
+ else ok=0;
+ }
+ if (getTok(0)!=R_BRACE)
+ ok=0;
+ getTok(1);
+ return (ok,a1,a2);
+}
+
+
+expr5() : leaf {
+ retval : leaf;
+ tok:=getTok(1);
+ if (tok>=F_ABS && tok<=F_TANH)
+ return do_func(tok);
+ case tok{
+ STRING =>
+ retval.which = STRING;
+ retval.s_val = s_val;
+ INT =>
+ retval.which = INT;
+ retval.i_val = i_val;
+ REAL =>
+ retval.which = REAL;
+ retval.r_val = r_val;
+ R_BRACE or COMMA =>
+ return retval;
+ L_BRACE =>
+ r1:=expr_9();
+ if (getTok(1)!=R_BRACE)
+ r1.which=UNKNOWN;
+ return r1;
+ * =>
+ retval.which = UNKNOWN;
+ }
+ return retval;
+}
+
diff --git a/appl/lib/tcl_core.b b/appl/lib/tcl_core.b
new file mode 100644
index 00000000..ef05ef97
--- /dev/null
+++ b/appl/lib/tcl_core.b
@@ -0,0 +1,1397 @@
+implement Tcl_Core;
+
+# these are the outside modules, self explanatory..
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+
+include "bufio.m";
+ bufmod : Bufio;
+Iobuf : import bufmod;
+
+include "string.m";
+ str : String;
+
+include "tk.m";
+ tk: Tk;
+
+include "wmlib.m";
+ wmlib: Wmlib;
+
+# these are stand alone Tcl libraries, for Tcl pieces that
+# are "big" enough to be called their own.
+
+include "tcl.m";
+
+include "tcllib.m";
+
+include "utils.m";
+ htab: Str_Hashtab;
+ mhtab : Mod_Hashtab;
+ shtab : Sym_Hashtab;
+ stack : Tcl_Stack;
+ utils : Tcl_Utils;
+
+Hash: import htab;
+MHash : import mhtab;
+SHash : import shtab;
+
+
+
+
+# global error flag and message. One day, this will be stack based..
+errmsg : string;
+error, mypid : int;
+
+sproc : adt {
+ name : string;
+ args : string;
+ script : string;
+};
+
+TCL_UNKNOWN, TCL_SIMPLE, TCL_ARRAY : con iota;
+
+# Global vars. Simple variables, and associative arrays.
+libmods : ref MHash;
+proctab := array[100] of sproc;
+retfl : int;
+symtab : ref SHash;
+nvtab : ref Hash;
+avtab : array of (ref Hash,string);
+tclmod : TclData;
+
+core_commands:=array[] of {
+ "append" , "array", "break" , "continue" , "catch", "dumpstack",
+ "exit" , "expr" , "eval" ,
+ "for" , "foreach" ,
+ "global" , "if" , "incr" , "info",
+ "lappend" , "level" , "load" ,
+ "proc" , "return" , "set" ,
+ "source" ,"switch" , "time" ,
+ "unset" , "uplevel", "upvar", "while" , "#"
+};
+
+
+about() : array of string {
+ return core_commands;
+}
+
+init(ctxt: ref Draw->Context, argv: list of string) {
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+ bufmod = load Bufio Bufio->PATH;
+ htab = load Str_Hashtab Str_Hashtab->PATH;
+ mhtab = load Mod_Hashtab Mod_Hashtab->PATH;
+ shtab = load Sym_Hashtab Sym_Hashtab->PATH;
+ stack = load Tcl_Stack Tcl_Stack->PATH;
+ str = load String String->PATH;
+ utils = load Tcl_Utils Tcl_Utils->PATH;
+ tk = load Tk Tk->PATH;
+ wmlib= load Wmlib Wmlib->PATH;
+ if (bufmod == nil || htab == nil || stack == nil ||
+ str == nil || utils == nil || tk == nil ||
+ wmlib==nil || mhtab == nil || shtab == nil){
+ sys->print("can't load initial modules %r\n");
+ exit;
+ }
+
+ # get a new stack frame.
+ stack->init();
+ (nvtab,avtab,symtab)=stack->newframe();
+
+ libmods=mhtab->alloc(101);
+
+ # grab my pid, and set a new group to make me easy to kill.
+ mypid=sys->pctl(sys->NEWPGRP, nil);
+
+ # no default top window.
+ tclmod.top=nil;
+ tclmod.context=ctxt;
+ tclmod.debug=0;
+
+ # set up library modules.
+ args:=array[] of {"do_load","io"};
+ do_load(args);
+ args=array[] of {"do_load","string"};
+ do_load(args);
+ args=array[] of {"do_load","calc"};
+ do_load(args);
+ args=array[] of {"do_load","list"};
+ do_load(args);
+ args=array[] of {"do_load","tk"};
+ do_load(args);
+ arr:=about();
+ for(i:=0;i<len arr;i++)
+ libmods.insert(arr[i],nil);
+
+ # cmd line args...
+ if (argv != nil)
+ argv = tl argv;
+ while (argv != nil) {
+ loadfile(hd argv);
+ argv = tl argv;
+ }
+
+}
+
+set_top(win:ref Tk->Toplevel){
+ tclmod.top=win;
+}
+
+clear_error(){
+ error=0;
+ errmsg="";
+}
+
+notify(num : int,s : string) : string {
+ error=1;
+ case num{
+ 1 =>
+ errmsg=sys->sprint(
+ "wrong # args: should be \"%s\"",s);
+ * =>
+ errmsg= s;
+ }
+ return errmsg;
+}
+
+grab_lines(new_inp,unfin: string ,lines : chan of string){
+ error=0;
+ tclmod.lines=lines;
+ input,line : string;
+ if (new_inp==nil)
+ new_inp = "tcl%";
+ if (unfin==nil)
+ unfin = "tcl>";
+ sys->print("%s ", new_inp);
+ iob := bufmod->fopen(sys->fildes(0),bufmod->OREAD);
+ if (iob==nil){
+ sys->print("cannot open stdin for reading.\n");
+ return;
+ }
+ while((input=iob.gets('\n'))!=nil){
+ line+=input;
+ if (!finished(line,0))
+ sys->print("%s ", unfin);
+ else{
+ lines <- = line;
+ line=nil;
+ }
+ }
+}
+
+# this is the main function. Its input is a complete (i.e. matching
+# brackets etc) tcl script, and its output is a message - if there
+# is one.
+evalcmd(s: string, termchar: int) : string {
+ msg : string;
+ i:=0;
+ retfl=0;
+ if (tclmod.debug==2)
+ sys->print("Entered evalcmd, s=%s, termchar=%c\n",s,termchar);
+ # strip null statements..
+ while((i<len s) && (s[i]=='\n' || s[i]==';')) i++;
+ if (i==len s) return nil;
+
+ # parse the script statement by statement
+ for(;s!=nil;i++){
+ # wait till we have a complete statement
+ if (i==len s || ((s[i]==termchar || s[i]==';' || s[i]=='\n')
+ && finished(s[0:i],termchar))){
+ # throw it away if its a comment...
+ if (s[0]!='#')
+ argv := parsecmd(s[0:i],termchar,0);
+ msg = nil;
+ if (tclmod.debug==2)
+ for(k:=0;k<len argv;k++)
+ sys->print("argv[%d]: (%s)\n",k,argv[k]);
+
+ # argv is now a completely parsed array of arguments
+ # for the Tcl command..
+
+ # find the module that the command is in and
+ # execute it.
+ if (len argv != 0){
+ mod:=lookup(argv[0]);
+ if (mod!=nil){
+ (error,msg)=
+ mod->exec(ref tclmod,argv);
+ if (error)
+ errmsg=msg;
+ } else {
+ if (argv[0]!=nil &&
+ argv[0][0]=='.')
+ msg=do_tk(argv);
+ else
+ msg=exec(argv);
+ }
+ }
+
+ # was there an error?
+ if (error) {
+ if (len argv > 0 && argv[0]!=""){
+ stat : string;
+ stat = "In function "+argv[0];
+ if (len argv >1 && argv[1]!=""){
+ stat[len stat]=' ';
+ stat+=argv[1];
+ }
+ stat+=".....\n\t";
+ errmsg=stat+errmsg;
+ }
+ msg=errmsg;
+ }
+
+ # we stop parsing if we hit a break, continue, return,
+ # error, termchar or end of string.
+ if (msg=="break" || msg=="continue" || error || retfl==1
+ || len s <= i || (len s > i && s[i]==termchar))
+ return msg;
+
+ # otherwise eat up the parsed statement and continue
+ s=s[i+1:];
+ i=-1;
+ }
+ }
+ return msg;
+}
+
+
+# returns 1 if the line has matching braces, brackets and
+# double-quotes and does not end in "\\\n"
+finished(s : string, termchar : int) : int {
+ cb:=0;
+ dq:=0;
+ sb:=0;
+ if (s==nil) return 1;
+ if (termchar=='}') cb++;
+ if (termchar==']') sb++;
+ if (len s > 1 && s[len s -2]=='\\')
+ return 0;
+ if (s[0]=='{') cb++;
+ if (s[0]=='}' && cb>0) cb--;
+ if (s[0]=='[') sb++;
+ if (s[0]==']' && sb>0) sb--;
+ if (s[0]=='"') dq=1-dq;
+ for(i:=1;i<len s;i++){
+ if (s[i]=='{' && s[i-1]!='\\') cb++;
+ if (s[i]=='}' && s[i-1]!='\\' && cb>0) cb--;
+ if (s[i]=='[' && s[i-1]!='\\') sb++;
+ if (s[i]==']' && s[i-1]!='\\' && sb>0) sb--;
+ if (s[i]=='"' && s[i-1]!='\\') dq=1-dq;
+ }
+ return (cb==0 && sb==0 && dq==0);
+}
+
+# counts the offset till the next matching ']'
+strip_to_match(s : string, ptr: int) : int {
+ j :=0;
+ nb:=0;
+ while(j<len s){
+ if (s[j]=='{')
+ while (j < len s && s[j]!='}') j++;
+ if (s[j]=='[') nb++;
+ if (s[j]==']'){
+ nb--;
+ if (nb==-1) return ptr+j;
+ }
+ j++;
+ }
+ return ptr+j;
+}
+
+# returns the type of variable represented by the string s, which is
+# a name.
+isa(s: string) : (int,int,string) {
+ found,val : int;
+ name,al : string;
+ curlev:=stack->level();
+ if (tclmod.debug==2)
+ sys->print("Called isa with %s, current stack level is %d\n",s,curlev);
+ (found,nil)=nvtab.find(s);
+ if (found) return (TCL_SIMPLE,curlev,s);
+ for (i:=0;i<len avtab;i++){
+ (nil,name)=avtab[i];
+ if (name==s) return (TCL_ARRAY,curlev,s);
+ }
+ if (symtab==nil)
+ return (TCL_UNKNOWN,curlev,s);
+ (found,val,al)=symtab.find(s);
+ if (!found)
+ return (TCL_UNKNOWN,curlev,s);
+ (tnv,tav,nil):=stack->examine(val);
+ if (tclmod.debug==2)
+ sys->print("have a level %d for %s\n",val,al);
+ if (tnv!=nil){
+ (found,nil)=tnv.find(al);
+ if (found) return (TCL_SIMPLE,val,al);
+ }
+ if (tav!=nil){
+ for (i=0;i<len tav;i++){
+ (nil,name)=tav[i];
+ if (name==al) return (TCL_ARRAY,val,al);
+ }
+ }
+ if (tclmod.debug==2)
+ sys->print("%s not found, creating at stack level %d\n",al,val);
+ return (TCL_UNKNOWN,val,al);
+}
+
+# This function only works if the string is already parsed!
+# takes a var_name and returns the hash table for it and the
+# name to look up. This is one of two things:
+# for simple variables:
+# findvar(foo) ---> (nvtab,foo)
+# for associative arrays:
+# findvar(foo(bar)) -----> (avtab[i],bar)
+# where avtab[i].name==foo
+# if create is 1, then an associative array is created upon first
+# reference.
+# returns (nil,error message) if there is a problem.
+
+find_var(s : string,create : int) : (ref Hash,string) {
+ rest,name,index : string;
+ retval,tnv : ref Hash;
+ tav : array of (ref Hash,string);
+ i,tag,lev: int;
+ (name,index)=str->splitl(s,"(");
+ if (index!=nil){
+ (index,rest)=str->splitl(index[1:],")");
+ if (rest!=")")
+ return (nil,"bad variable name");
+ }
+ (tag,lev,name) = isa(name);
+ case tag {
+ TCL_SIMPLE =>
+ if (index!=nil)
+ return (nil,"variable isn't array");
+ (tnv,nil,nil)=stack->examine(lev);
+ return (tnv,name);
+ TCL_ARRAY =>
+ if (index==nil)
+ return (nil,"variable is array");
+ (nil,tav,nil)=stack->examine(lev);
+ for(i=0;i<len tav;i++){
+ (retval,rest)=tav[i];
+ if (rest==name)
+ return (retval,index);
+ }
+ return (nil,"find_var: impossible!!");
+ # if we get here, the variable needs to be
+ # created.
+ TCL_UNKNOWN =>
+ if (!create)
+ return (nil,"no such variable");
+ (tnv,tav,nil)=stack->examine(lev);
+ if (index==nil)
+ return (tnv,name);
+
+ }
+ # if we get here, we are creating an associative variable in the
+ # tav array.
+ for(i=0;i<len tav;i++){
+ (retval,rest)=tav[i];
+ if (rest==nil){
+ retval=htab->alloc(101);
+ tav[i]=(retval,name);
+ return (retval,index);
+ }
+ }
+ return (nil,"associative array table full!");
+}
+
+# the main parsing function, a la ousterhouts man pages. Takes a
+# string that is meant to be a tcl statement and parses it,
+# reevaluating and quoting upto the termchar character. If disable
+# is true, then whitespace is not ignored.
+parsecmd(s: string, termchar,disable: int) : array of string {
+ argv:= array[200] of string;
+ buf,nm,id: string;
+ argc := 0;
+ nc := 0;
+ c :=0;
+ tab : ref Hash;
+
+ if (disable && (termchar=='\n' || termchar==';')) termchar=0;
+ outer:
+ for (i := 0; i<len s ;) {
+ if ((i>0 &&s[i-1]!='\\' &&s[i]==termchar)||(s[0]==termchar))
+ break;
+ case int s[i] {
+ ' ' or '\t' or '\n' =>
+ if (!disable){
+ if (nc > 0) { # end of a word?
+ argv[argc++] = buf;
+ buf = nil;
+ nc = 0;
+ }
+ i++;
+ }
+ else
+ buf[nc++]=s[i++];
+ '$' =>
+ if (i>0 && s[i-1]=='\\')
+ buf[nc++]=s[i++];
+ else {
+ (nm,id) = parsename(s[i+1:], termchar);
+ if (id!=nil)
+ nm=nm+"("+id+")";
+ (tab,nm)=find_var(nm,0); #don't create var!
+ if (len nm > 0 && tab!=nil) {
+ (found, val) := tab.find(nm);
+ buf += val;
+ nc += len val;
+ #sys->print("Here s[i:] is (%s)\n",s[i:]);
+ if(nm==id)
+ while(s[i]!=')') i++;
+ else
+ if (s[i+1]=='{')
+ while(s[i]!='}') i++;
+ else
+ i += len nm;
+ if (nc==0 && (i==len s-1 ||
+ s[i+1]==' ' ||
+ s[i+1]=='\t'||
+ s[i+1]==termchar))
+ argv[argc++]=buf;
+ } else {
+ buf[nc++] = '$';
+ }
+ i++;
+ }
+ '{' =>
+ if (i>0 && s[i-1]=='\\')
+ buf[nc++]=s[i++];
+ else if (s[i+1]=='}'){
+ argv[argc++] = nil;
+ buf = nil;
+ nc = 0;
+ i+=2;
+ } else {
+ nbra := 1;
+ for (i++; i < len s; i++) {
+ if (s[i] == '{')
+ nbra++;
+ else if (s[i] == '}') {
+ nbra--;
+ if (nbra == 0) {
+ i++;
+ continue outer;
+ }
+ }
+ buf[nc++] = s[i];
+ }
+ }
+ '[' =>
+ if (i>0 && s[i-1]=='\\')
+ buf[nc++]=s[i++];
+ else{
+ a:=evalcmd(s[i+1:],']');
+ if (error)
+ return nil;
+ if (nc>0){
+ buf+=a;
+ nc += len a;
+ } else {
+ argv[argc++] = a;
+ buf = nil;
+ nc = 0;
+ }
+ i++;
+ i=strip_to_match(s[i:],i);
+ i++;
+ }
+ '"' =>
+ if (i>0 && s[i-1]!='\\' && nc==0){
+ ans:=parsecmd(s[i+1:],'"',1);
+ #sys->print("len ans is %d\n",len ans);
+ if (len ans!=0){
+ for(;;){
+ i++;
+ if(s[i]=='"' &&
+ s[i-1]!='\\')
+ break;
+ }
+ i++;
+ argv[argc++] = ans[0];
+ } else {
+ argv[argc++] = nil;
+ i+=2;
+ }
+ buf = nil;
+ nc = 0;
+ }
+ else buf[nc++] = s[i++];
+ * =>
+ if (s[i]=='\\'){
+ c=unesc(s[i:]);
+ if (c!=0){
+ buf[nc++] = c;
+ i+=2;
+ } else {
+ if (i+1 < len s && !(s[i+1]=='"'
+ || s[i+1]=='$' || s[i+1]=='{'
+ || s[i+1]=='['))
+ buf[nc++]=s[i];
+ i++;
+ }
+ c=0;
+ } else
+ buf[nc++]=s[i++];
+ }
+ }
+ if (nc > 0) # fix up last word if present
+ argv[argc++] = buf;
+ ret := array[argc] of string;
+ ret[0:] = argv[0:argc];
+ return ret;
+}
+
+# parses a name by Tcl rules, a valid name is either $foo, $foo(bar)
+# or ${foo}.
+parsename(s: string, termchar: int) : (string,string) {
+ ret,arr,rest: string;
+ rets : array of string;
+ if (len s == 0)
+ return (nil,nil);
+ if (s[0]=='{'){
+ (ret,nil)=str->splitl(s,"}");
+ #sys->print("returning [%s]\n",ret[1:]);
+ return (ret[1:],nil);
+ }
+ loop: for (i := 0; i < len s && s[i] != termchar; i++) {
+ case (s[i]) {
+ 'a' to 'z' or 'A' to 'Z' or '0' to '9' or '_' =>
+ ret[i] = s[i];
+ * =>
+ break loop;
+ '(' =>
+ arr=ret[0:i];
+ rest=s[i+1:];
+ rets=parsecmd(rest,')',0);
+ # should always be len 1?
+ if (len rets >1)
+ sys->print("len rets>1 in parsename!\n");
+ return (arr,rets[0]);
+ }
+ }
+ return (ret,nil);
+}
+
+loadfile(file :string) : string {
+ iob : ref Iobuf;
+ msg,input,line : string;
+ if (file==nil)
+ return nil;
+ iob = bufmod->open(file,bufmod->OREAD);
+ if (iob==nil)
+ return notify(0,sys->sprint(
+ "couldn't read file \"%s\":%r",file));
+ while((input=iob.gets('\n'))!=nil){
+ line+=input;
+ if (finished(line,0)){
+ # put in a return catch here...
+ line = prepass(line);
+ msg=evalcmd(line,0);
+ if (error) return errmsg;
+ line=nil;
+ }
+ }
+ return msg;
+}
+
+
+#unescapes a string. Can do better.....
+unesc(s: string) : int {
+ c: int;
+ if (len s == 1) return 0;
+ case s[1] {
+ 'a'=> c = '\a';
+ 'n'=> c = '\n';
+ 't'=> c = '\t';
+ 'r'=> c = '\r';
+ 'b'=> c = '\b';
+ '\\'=> c = '\\';
+ '}' => c = '}';
+ ']' => c=']';
+ # do hex and octal.
+ * => c = 0;
+ }
+ return c;
+}
+
+# prepass a string and replace "\\n[ \t]*" with ' '
+prepass(s : string) : string {
+ for(i := 0; i < len s; i++) {
+ if(s[i] != '\\')
+ continue;
+ j:=i;
+ if (s[i+1] == '\n') {
+ s[j]=' ';
+ i++;
+ while(i<len s && (s[i]==' ' || s[i]=='\t'))
+ i++;
+ if (i==len s)
+ s = s[0:j];
+ else
+ s=s[0:j]+s[i+1:];
+ i=j;
+ }
+ }
+ return s;
+}
+
+exec(argv : array of string) : string {
+ msg : string;
+ if (argv[0]=="")
+ return nil;
+ case (argv[0]) {
+ "append" =>
+ msg= do_append(argv);
+ "array" =>
+ msg= do_array(argv);
+ "break" or "continue" =>
+ return argv[0];
+ "catch" =>
+ msg=do_catch(argv);
+ "debug" =>
+ msg=do_debug(argv);
+ "dumpstack" =>
+ msg=do_dumpstack(argv);
+ "exit" =>
+ do_exit();
+ "expr" =>
+ msg = do_expr(argv);
+ "eval" =>
+ msg = do_eval(argv);
+ "for" =>
+ msg = do_for(argv);
+ "foreach" =>
+ msg = do_foreach(argv);
+ "format" =>
+ msg = do_string(argv);
+ "global" =>
+ msg = do_global(argv);
+ "if" =>
+ msg = do_if(argv);
+ "incr" =>
+ msg = do_incr(argv);
+ "info" =>
+ msg = do_info(argv);
+ "lappend" =>
+ msg = do_lappend(argv);
+ "level" =>
+ msg=sys->sprint("Current Stack "+
+ "level is %d",
+ stack->level());
+ "load" =>
+ msg=do_load(argv);
+ "proc" =>
+ msg=do_proc(argv);
+ "return" =>
+ msg=do_return(argv);
+ retfl =1;
+ "set" =>
+ msg = do_set(argv);
+ "source" =>
+ msg = do_source(argv);
+ "string" =>
+ msg = do_string(argv);
+ "switch" =>
+ msg = do_switch(argv);
+ "time" =>
+ msg=do_time(argv);
+ "unset" =>
+ msg = do_unset(argv);
+ "uplevel" =>
+ msg=do_uplevel(argv);
+ "upvar" =>
+ msg=do_upvar(argv);
+ "while" =>
+ msg = do_while(argv);
+ "#" =>
+ msg=nil;
+ * =>
+ msg = uproc(argv);
+ }
+ return msg;
+}
+
+# from here on is the list of commands, alpahabetised, we hope.
+
+do_append(argv :array of string) : string {
+ tab : ref Hash;
+ if (len argv==1 || len argv==2)
+ return notify(1,
+ "append varName value ?value ...?");
+ name := argv[1];
+ (tab,name)=find_var(name,1);
+ if (tab==nil)
+ return notify(0,name);
+ (found, val) := tab.find(name);
+ for (i:=2;i<len argv;i++)
+ val+=argv[i];
+ tab.insert(name,val);
+ return val;
+}
+
+do_array(argv : array of string) : string {
+ tab : ref Hash;
+ name : string;
+ flag : int;
+ if (len argv!=3)
+ return notify(1,"array [names, size] name");
+ case argv[1] {
+ "names" =>
+ flag=1;
+ "size" =>
+ flag=0;
+ * =>
+ return notify(0,"expexted names or size, got "+argv[1]);
+
+ }
+ (tag,lev,al) := isa(argv[2]);
+ if (tag!=TCL_ARRAY)
+ return notify(0,argv[2]+" isn't an array");
+ (nil,tav,nil):=stack->examine(lev);
+ for (i:=0;i<len tav;i++){
+ (tab,name)=tav[i];
+ if (name==al) break;
+ }
+ if (flag==0)
+ return string tab.lsize;
+ return tab.dump();
+}
+
+do_catch(argv : array of string) : string {
+ if (len argv==1 || len argv > 3)
+ return notify(1,"catch command ?varName?");
+ msg:=evalcmd(argv[1],0);
+ if (len argv==3 && error){
+ (tab,name):=find_var(argv[2],1);
+ if (tab==nil)
+ return notify(0,name);
+ tab.insert(name, msg);
+ }
+ ret:=string error;
+ error=0;
+ return ret;
+}
+
+do_debug(argv : array of string) : string {
+ add : string;
+ if (len argv!=2)
+ return notify(1,"debug");
+ (i,rest):=str->toint(argv[1],10);
+ if (rest!=nil)
+ return notify(0,"Expected integer and got "+argv[1]);
+ tclmod.debug=i;
+ if (tclmod.debug==0)
+ add="off";
+ else
+ add="on";
+ return "debugging is now "+add+" at level"+ string i;
+}
+
+do_dumpstack(argv : array of string) : string {
+ if (len argv!=1)
+ return notify(1,"dumpstack");
+ stack->dump();
+ return nil;
+}
+
+do_eval(argv : array of string) : string {
+ eval_str : string;
+ for(i:=1;i<len argv;i++){
+ eval_str += argv[i];
+ eval_str[len eval_str]=' ';
+ }
+ return evalcmd(eval_str[0:len eval_str -1],0);
+}
+
+do_exit(){
+ kfd := sys->open("#p/"+string mypid+"/ctl", sys->OWRITE);
+ if(kfd == nil)
+ sys->print("error opening pid %d (%r)\n",mypid);
+ sys->fprint(kfd, "killgrp");
+ exit;
+}
+
+
+
+do_expr(argv : array of string) : string {
+ retval : string;
+ for (i:=1;i<len argv;i++){
+ retval+=argv[i];
+ retval[len retval]=' ';
+ }
+ retval=retval[0: len retval -1];
+ argv=parsecmd(retval,0,0);
+ cal:=lookup("calc");
+ (err,ret):= cal->exec(ref tclmod,argv);
+ if (err) return notify(0,ret);
+ return ret;
+}
+
+
+do_for(argv : array of string) : string {
+ if (len argv!=5)
+ return notify(1,"for start test next command");
+ test := array[] of {"expr",argv[2]};
+ evalcmd(argv[1],0);
+ for(;;){
+ msg:=do_expr(test);
+ if (msg=="Error!")
+ return notify(0,sys->sprint(
+ "syntax error in expression \"%s\"",
+ argv[2]));
+ if (msg=="0")
+ return nil;
+ msg=evalcmd(argv[4],0);
+ if (msg=="break")
+ return nil;
+ if (msg=="continue"); #do nothing!
+ evalcmd(argv[3],0);
+ if (error)
+ return errmsg;
+ }
+}
+
+
+
+do_foreach(argv: array of string) : string{
+ tab : ref Hash;
+ if (len argv!=4)
+ return notify(1,"foreach varName list command");
+ name := argv[1];
+ (tab,name)=find_var(name,1);
+ if (tab==nil)
+ return notify(0,name);
+ arr:=utils->break_it(argv[2]);
+ for(i:=0;i<len arr;i++){
+ tab.insert(name,arr[i]);
+ evalcmd(argv[3],0);
+ }
+ return nil;
+}
+
+
+
+do_global(argv : array of string) : string {
+ if (len argv==1)
+ return notify(1,"global varName ?varName ...?");
+ if (symtab==nil)
+ return nil;
+ for (i:=1 ; i < len argv;i++)
+ symtab.insert(argv[i],argv[i],0);
+ return nil;
+}
+
+
+
+do_if(argv : array of string) : string {
+ if (len argv==1)
+ return notify(1,"no expression after \"if\" argument");
+ expr1 := array[] of {"expr",argv[1]};
+ msg:=do_expr(expr1);
+ if (msg=="Error!")
+ return notify(0,sys->sprint(
+ "syntax error in expression \"%s\"",
+ argv[1]));
+ if (len argv==2)
+ return notify(1,sys->sprint(
+ "no script following \""+
+ "%s\" argument",msg));
+ if (msg=="0"){
+ if (len argv>3){
+ if (argv[3]=="else"){
+ if (len argv==4)
+ return notify(1,
+ "no script"+
+ " following \"else\" argument");
+ return evalcmd(argv[4],0);
+ }
+ if (argv[3]=="elseif"){
+ argv[3]="if";
+ return do_if(argv[3:]);
+ }
+ }
+ return nil;
+ }
+ return evalcmd(argv[2],0);
+}
+
+do_incr(argv :array of string) : string {
+ num,xtra : int;
+ rest :string;
+ tab : ref Hash;
+ if (len argv==1)
+ return notify(1,"incr varName ?increment?");
+ name := argv[1];
+ (tab,name)=find_var(name,0); #doesn't create!!
+ if (tab==nil)
+ return notify(0,name);
+ (found, val) := tab.find(name);
+ if (!found)
+ return notify(0,sys->sprint("can't read \"%s\": "
+ +"no such variable",name));
+ (num,rest)=str->toint(val,10);
+ if (rest!=nil)
+ return notify(0,sys->sprint(
+ "expected integer but got \"%s\"",val));
+ if (len argv == 2){
+ num+=1;
+ tab.insert(name,string num);
+ }
+ if (len argv == 3) {
+ val = argv[2];
+ (xtra,rest)=str->toint(val,10);
+ if (rest!=nil)
+ return notify(0,sys->sprint(
+ "expected integer but got \"%s\""
+ ,val));
+ num+=xtra;
+ tab.insert(name, string num);
+ }
+ return string num;
+}
+
+do_info(argv : array of string) : string {
+ if (len argv==1)
+ return notify(1,"info option ?arg arg ...?");
+ case argv[1] {
+ "args" =>
+ return do_info_args(argv,0);
+ "body" =>
+ return do_info_args(argv,1);
+ "commands" =>
+ return do_info_commands(argv);
+ "exists" =>
+ return do_info_exists(argv);
+ "procs" =>
+ return do_info_procs(argv);
+
+ }
+ return sys->sprint(
+ "bad option \"%s\": should be args, body, commands, exists, procs",
+ argv[1]);
+}
+
+do_info_args(argv : array of string,body :int) : string {
+ name: string;
+ s : sproc;
+ if (body)
+ name="body";
+ else
+ name="args";
+ if (len argv!=3)
+ return notify(1,"info "+name+" procname");
+ for(i:=0;i<len proctab;i++){
+ s=proctab[i];
+ if (s.name==argv[2])
+ break;
+ }
+ if (i==len proctab)
+ return notify(0,argv[2]+" isn't a procedure.");
+ if (body)
+ return s.script;
+ return s.args;
+}
+
+do_info_commands(argv : array of string) : string {
+ if (len argv==1 || len argv>3)
+ return notify(1,"info commands [pattern]");
+ return libmods.dump();
+}
+
+do_info_exists(argv : array of string) : string {
+ name, index : string;
+ tab : ref Hash;
+ if (len argv!=3)
+ return notify(1,"info exists varName");
+ (name,index)=parsename(argv[2],0);
+ (i,nil,nil):=isa(name);
+ if (i==TCL_UNKNOWN)
+ return "0";
+ if (index==nil)
+ return "1";
+ (tab,name)=find_var(argv[2],0);
+ if (tab==nil)
+ return "0";
+ (found, val) := tab.find(name);
+ if (!found)
+ return "0";
+ return "1";
+
+}
+
+do_info_procs(argv : array of string) : string {
+ if (len argv==1 || len argv>3)
+ return notify(1,"info procs [pattern]");
+ retval : string;
+ for(i:=0;i<len proctab;i++){
+ s:=proctab[i];
+ if (s.name!=nil){
+ retval+=s.name;
+ retval[len retval]=' ';
+ }
+ }
+ return retval;
+}
+
+do_lappend(argv : array of string) : string{
+ tab : ref Hash;
+ retval :string;
+ retval=nil;
+ if (len argv==1 || len argv==2)
+ return notify(1,
+ "lappend varName value ?value ...?");
+ name := argv[1];
+ (tab,name)=find_var(name,1);
+ if (tab==nil)
+ return notify(0,name);
+ (found, val) := tab.find(name);
+ for(i:=2;i<len argv;i++){
+ flag:=0;
+ if (spaces(argv[i])) flag=1;
+ if (flag) retval[len retval]='{';
+ retval += argv[i];
+ if (flag) retval[len retval]='}';
+ retval[len retval]=' ';
+ }
+ if (retval!=nil)
+ retval=retval[0:len retval-1];
+ if (val!=nil)
+ retval=val+" "+retval;
+ tab.insert(name,retval);
+ return retval;
+}
+
+spaces(s : string) : int{
+ if (s==nil) return 1;
+ for(i:=0;i<len s;i++)
+ if (s[i]==' ' || s[i]=='\t') return 1;
+ return 0;
+}
+
+do_load(argv : array of string) : string {
+ # look for a dis library to load up, then
+ # add to library array.
+ if (len argv!=2)
+ return notify(1,"load libname");
+ fname:="/dis/lib/tcl_"+argv[1]+".dis";
+ mod:= load TclLib fname;
+ if (mod==nil)
+ return notify(0,
+ sys->sprint("Cannot load %s",fname));
+ arr:=mod->about();
+ for(i:=0;i<len arr;i++)
+ libmods.insert(arr[i],mod);
+ return nil;
+}
+
+
+do_proc(argv : array of string) : string {
+ if (len argv != 4)
+ return notify(1,"proc name args body");
+ for(i:=0;i<len proctab;i++)
+ if (proctab[i].name==nil ||
+ proctab[i].name==argv[1]) break;
+ if (i==len proctab)
+ return notify(0,"procedure table full!");
+ proctab[i].name=argv[1];
+ proctab[i].args=argv[2];
+ proctab[i].script=argv[3];
+ return nil;
+}
+
+do_return(argv : array of string) : string {
+ if (len argv==1)
+ return nil;
+ # put in options here.....
+ return argv[1];
+}
+
+do_set(argv : array of string) : string {
+ tab : ref Hash;
+ if (len argv == 1 || len argv > 3)
+ return notify(1,"set varName ?newValue?");
+ name := argv[1];
+ (tab,name)=find_var(name,1);
+ if (tab==nil)
+ return notify(0,name);
+ (found, val) := tab.find(name);
+ if (len argv == 2)
+ if (!found)
+ val = notify(0,sys->sprint(
+ "can't read \"%s\": "
+ +"no such variable",name));
+ if (len argv == 3) {
+ val = argv[2];
+ tab.insert(name, val);
+ }
+ return val;
+}
+
+do_source(argv : array of string) : string {
+ if (len argv !=2)
+ return notify(1,"source fileName");
+ return loadfile(argv[1]);
+}
+
+do_string(argv : array of string) : string {
+ stringmod := lookup("string");
+ if (stringmod==nil)
+ return notify(0,sys->sprint(
+ "String Package not loaded (%r)"));
+ (err,retval):= stringmod->exec(ref tclmod,argv);
+ if (err) return notify(0,retval);
+ return retval;
+}
+
+do_switch(argv : array of string) : string {
+ i:=0;
+ arr : array of string;
+ if (len argv < 3)
+ return notify(1,"switch "
+ +"?switches? string pattern body ... "+
+ "?default body?\"");
+ if (len argv == 3)
+ arr=utils->break_it(argv[2]);
+ else
+ arr=argv[2:];
+ if (len arr % 2 !=0)
+ return notify(0,
+ "extra switch pattern with no body");
+ for (i=0;i<len arr;i+=2)
+ if (argv[1]==arr[i])
+ break;
+ if (i==len arr){
+ if (arr[i-2]=="default")
+ return evalcmd(arr[i-1],0);
+ else return nil;
+ }
+ while (i<len arr && arr[i+1]=="-") i+=2;
+ return evalcmd(arr[i+1],0);
+}
+
+do_time(argv : array of string) : string {
+ rest : string;
+ end,start,times : int;
+ if (len argv==1 || len argv>3)
+ return notify(1,"time command ?count?");
+ if (len argv==2)
+ times=1;
+ else{
+ (times,rest)=str->toint(argv[2],10);
+ if (rest!=nil)
+ return notify(0,sys->sprint(
+ "expected integer but got \"%s\"",argv[2]));
+ }
+ start=sys->millisec();
+ for(i:=0;i<times;i++)
+ evalcmd(argv[1],0);
+ end=sys->millisec();
+ r:= (real end - real start) / real times;
+ return sys->sprint("%g milliseconds per iteration", r);
+}
+
+do_unset(argv : array of string) : string {
+ tab : ref Hash;
+ name: string;
+ if (len argv == 1)
+ return notify(1,"unset "+
+ "varName ?varName ...?");
+ for(i:=1;i<len argv;i++){
+ name = argv[i];
+ (tab,name)=find_var(name,0);
+ if (tab==nil)
+ return notify(0,sys->sprint("can't unset \"%s\": no such" +
+ " variable",name));
+ tab.delete(name);
+
+ }
+ return nil;
+}
+
+do_uplevel(argv : array of string) : string {
+ level: int;
+ rest,scr : string;
+ scr=nil;
+ exact:=0;
+ i:=1;
+ if (len argv==1)
+ return notify(1,"uplevel ?level? command ?arg ...?");
+ if (len argv==2)
+ level=-1;
+ else {
+ lev:=argv[1];
+ if (lev[0]=='#'){
+ exact=1;
+ lev=lev[1:];
+ }
+ (level,rest)=str->toint(lev,10);
+ if (rest!=nil){
+ i=2;
+ level =-1;
+ }
+ }
+ oldlev:=stack->level();
+ if (!exact)
+ level+=oldlev;
+ (tnv,tav,sym):=stack->examine(level);
+ if (tnv==nil && tav==nil)
+ return notify(0,"bad level "+argv[1]);
+ if (tclmod.debug==2)
+ sys->print("In uplevel, current level is %d, moving to level %d\n",
+ oldlev,level);
+ stack->move(level);
+ oldav:=avtab;
+ oldnv:=nvtab;
+ oldsym:=symtab;
+ avtab=tav;
+ nvtab=tnv;
+ symtab=sym;
+ for(;i<len argv;i++)
+ scr=scr+argv[i]+" ";
+ msg:=evalcmd(scr[0:len scr-1],0);
+ avtab=oldav;
+ nvtab=oldnv;
+ symtab=oldsym;
+ ok:=stack->move(oldlev);
+ if (tclmod.debug==2)
+ sys->print("Leaving uplevel, current level is %d, moving back to"+
+ " level %d,move was %d\n",
+ level,oldlev,ok);
+ return msg;
+}
+
+do_upvar(argv : array of string) : string {
+ level:int;
+ rest:string;
+ i:=1;
+ exact:=0;
+ if (len argv<3 || len argv>4)
+ return notify(1,"upvar ?level? ThisVar OtherVar");
+ if (len argv==3)
+ level=-1;
+ else {
+ lev:=argv[1];
+ if (lev[0]=='#'){
+ exact=1;
+ lev=lev[1:];
+ }
+ (level,rest)=str->toint(lev,10);
+ if (rest!=nil){
+ i=2;
+ level =-1;
+ }
+ }
+ if (!exact)
+ level+=stack->level();
+ symtab.insert(argv[i],argv[i+1],level);
+ return nil;
+}
+
+do_while(argv : array of string) : string {
+ if (len argv!=3)
+ return notify(1,"while test command");
+ for(;;){
+ expr1 := array[] of {"expr",argv[1]};
+ msg:=do_expr(expr1);
+ if (msg=="Error!")
+ return notify(0,sys->sprint(
+ "syntax error in expression \"%s\"",
+ argv[1]));
+ if (msg=="0")
+ return nil;
+ evalcmd(argv[2],0);
+ if (error)
+ return errmsg;
+ }
+}
+
+uproc(argv : array of string) : string {
+ cmd,add : string;
+ for(i:=0;i< len proctab;i++)
+ if (proctab[i].name==argv[0])
+ break;
+ if (i==len proctab)
+ return notify(0,sys->sprint("invalid command name \"%s\"",
+ argv[0]));
+ # save tables
+ # push a newframe
+ # bind args to arguments
+ # do cmd
+ # pop frame
+ # return msg
+
+ # globals are supported, but upvar and uplevel are not!
+
+ arg_arr:=utils->break_it(proctab[i].args);
+ j:=len arg_arr;
+ if (len argv < j+1 && arg_arr[j-1]!="args"){
+ j=len argv-1;
+ return notify(0,sys->sprint(
+ "no value given for"+
+ " parameter \"%s\" to \"%s\"",
+ arg_arr[j],proctab[i].name));
+ }
+ if ((len argv > j+1) && arg_arr[j-1]!="args")
+ return notify(0,"called "+proctab[i].name+
+ " with too many arguments");
+ oldavtab:=avtab;
+ oldnvtab:=nvtab;
+ oldsymtab:=symtab;
+ (nvtab,avtab,symtab)=stack->newframe();
+ for (j=0;j< len arg_arr-1;j++){
+ cmd="set "+arg_arr[j]+" {"+argv[j+1]+"}";
+ evalcmd(cmd,0);
+ }
+ if (len arg_arr>j && arg_arr[j] != "args") {
+ cmd="set "+arg_arr[j]+" {"+argv[j+1]+"}";
+ evalcmd(cmd,0);
+ }
+ else {
+ if (len arg_arr > j) {
+ if (j+1==len argv)
+ add="";
+ else
+ add=argv[j+1];
+ cmd="set "+arg_arr[j]+" ";
+ arglist:="{"+add+" ";
+ j++;
+ while(j<len argv-1) {
+ arglist+=argv[j+1];
+ arglist[len arglist]=' ';
+ j++;
+ }
+ arglist[len arglist]='}';
+ cmd+=arglist;
+ evalcmd(cmd,0);
+ }
+ }
+ msg:=evalcmd(proctab[i].script,0);
+ stack->pop();
+ avtab=oldavtab;
+ nvtab=oldnvtab;
+ symtab=oldsymtab;
+ #sys->print("Error is %d, msg is %s\n",error,msg);
+ return msg;
+}
+
+do_tk(argv : array of string) : string {
+ tkpack:=lookup("button");
+ (err,retval):= tkpack->exec(ref tclmod,argv);
+ if (err) return notify(0,retval);
+ return retval;
+}
+
+
+lookup(s : string) : TclLib {
+ (found,mod):=libmods.find(s);
+ if (!found)
+ return nil;
+ return mod;
+}
diff --git a/appl/lib/tcl_inthash.b b/appl/lib/tcl_inthash.b
new file mode 100644
index 00000000..e0c6c6e8
--- /dev/null
+++ b/appl/lib/tcl_inthash.b
@@ -0,0 +1,95 @@
+implement Int_Hashtab;
+
+include "sys.m";
+include "draw.m";
+include "tk.m";
+include "tcl.m";
+include "tcllib.m";
+include "utils.m";
+
+
+hashasu(key : string,n : int): int{
+ i, h : int;
+ h=0;
+ i=0;
+ while(i<len key){
+ h = 10*h + key[i];
+ h = h%n;
+ i++;
+ }
+ return h%n;
+}
+
+alloc(size : int) : ref IHash {
+ h : IHash;
+ t : list of H_link;
+ t=nil;
+ h.size= size;
+ h.tab = array[size] of {* => t};
+ return ref h;
+}
+
+
+IHash.insert(h : self ref IHash,name: string,val:int) : int {
+ link : H_link;
+ hash,found : int;
+ nlist : list of H_link;
+ nlist=nil;
+ found=0;
+ hash = hashasu(name,h.size);
+ tmp:=(h.tab)[hash];
+ for(;tmp!=nil;tmp = tl tmp){
+ link=hd tmp;
+ if (link.name==name){
+ found=1;
+ link.val = val;
+ }
+ nlist = link :: nlist;
+ }
+ if (!found){
+ link.name=name;
+ link.val=val;
+ (h.tab)[hash]= link :: (h.tab)[hash];
+ }else
+ (h.tab)[hash]=nlist;
+ return 1;
+}
+
+IHash.find(h : self ref IHash,name : string) : (int, int){
+ hash,flag : int;
+ nlist : list of H_link;
+ retval:=0;
+ flag=0;
+ nlist=nil;
+ hash = hashasu(name,h.size);
+ tmp:=(h.tab)[hash];
+ for(;tmp!=nil;tmp = tl tmp){
+ link:=hd tmp;
+ if ((hd tmp).name==name){
+ flag = 1;
+ retval = (hd tmp).val;
+ }
+ nlist = link :: nlist;
+ }
+ (h.tab)[hash]=nlist;
+ return (flag,retval);
+}
+
+IHash.delete(h : self ref IHash,name : string) : int {
+ hash,flag : int;
+ nlist : list of H_link;
+ flag=0;
+ nlist=nil;
+ hash = hashasu(name,h.size);
+ tmp:=(h.tab)[hash];
+ for(;tmp!=nil;tmp = tl tmp){
+ link:=hd tmp;
+ if (link.name==name)
+ flag = 1;
+ else
+ nlist = link :: nlist;
+ }
+ (h.tab)[hash]=nlist;
+ return flag;
+}
+
diff --git a/appl/lib/tcl_io.b b/appl/lib/tcl_io.b
new file mode 100644
index 00000000..33d30b3e
--- /dev/null
+++ b/appl/lib/tcl_io.b
@@ -0,0 +1,350 @@
+implement TclLib;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "bufio.m";
+ bufmod : Bufio;
+Iobuf : import bufmod;
+
+include "string.m";
+ str : String;
+
+include "tk.m";
+
+include "tcl.m";
+
+include "tcllib.m";
+
+error : int;
+started : int;
+tclmod : ref Tcl_Core->TclData;
+
+name2fid : array of (ref Iobuf,string,int);
+
+valid_commands := array[] of {
+ "close",
+ "eof" ,
+ "file",
+ "flush",
+ "gets" ,
+ "open",
+ "puts",
+ "read" ,
+ "seek" ,
+ "tell"
+};
+
+init() : string {
+ started=1;
+ str = load String String->PATH;
+ sys = load Sys Sys->PATH;
+ bufmod = load Bufio Bufio->PATH;
+ if (str==nil || bufmod==nil)
+ return "Can't initialise IO package.";
+ name2fid = array[100] of (ref Iobuf,string,int);
+ stdout := bufmod->fopen(sys->fildes(1),bufmod->OWRITE);
+ if (stdout==nil)
+ return "cannot open stdout for writing.\n";
+ name2fid[0]=(nil,"stdin",0);
+ name2fid[1]=(stdout,"stdout",0);
+ return nil;
+}
+
+about() : array of string{
+ return valid_commands;
+}
+
+exec(tcl : ref Tcl_Core->TclData,argv : array of string) : (int,string) {
+ tclmod=tcl;
+ msg :string;
+ if (!started) init();
+ error=0;
+ case argv[0] {
+ "close" =>
+ msg = do_close(argv);
+ return (error,msg);
+ "eof" =>
+ msg = do_eof(argv);
+ return (error,msg);
+ "file" =>
+ msg = do_nothing(argv);
+ return (error,msg);
+ "flush" =>
+ msg = do_nothing(argv);
+ return (error,msg);
+ "gets" =>
+ msg = do_gets(argv);
+ return (error,msg);
+ "open" =>
+ msg = do_open(argv);
+ return (error,msg);
+ "puts" =>
+ msg = do_puts(argv);
+ return (error,msg);
+ "read" =>
+ msg = do_read(argv);
+ return (error,msg);
+ "seek" =>
+ msg = do_seek(argv);
+ return (error,msg);
+ "tell" =>
+ msg = do_nothing(argv);
+ return (error,msg);
+ }
+ return (1,nil);
+}
+
+do_nothing(argv : array of string) : string {
+ if (len argv==0);
+ return nil;
+}
+
+do_close(argv : array of string) : string {
+ iob : ref Iobuf;
+ name : string;
+ j : int;
+ iob=nil;
+ if (len argv!=2)
+ return notify(1,"close fileId");
+ for(i:=0;i<len name2fid;i++){
+ (iob,name,j)=name2fid[i];
+ if (name==argv[1])
+ break;
+ }
+ if (iob==nil)
+ return notify(0,sys->sprint("bad file identifier \"%s\"",
+ argv[1]));
+ iob.flush();
+ iob.close();
+ iob=nil;
+ name2fid[i]=(nil,"",0);
+ return nil;
+}
+
+do_eof(argv : array of string) : string {
+ name : string;
+ j : int;
+ iob : ref Iobuf;
+ if (len argv!=2)
+ return notify(1,"eof fileId");
+ for(i:=0;i<len name2fid;i++){
+ (iob,name,j)=name2fid[i];
+ if (name==argv[1])
+ return string j;
+ }
+ return notify(0,sys->sprint("bad file identifier \"%s\"",argv[1]));
+}
+
+
+do_gets(argv : array of string) : string {
+ iob : ref Iobuf;
+ line : string;
+ if (len argv==1 || len argv > 3)
+ return notify(1,"gets fileId ?varName?");
+ if (argv[1]=="stdin")
+ line = <- tclmod.lines;
+ else{
+ iob=lookup_iob(argv[1]);
+ if (iob==nil)
+ return notify(0,sys->sprint(
+ "bad file identifier \"%s\"",argv[1]));
+ line=iob.gets('\n');
+ }
+ if (line==nil){
+ set_eof(iob);
+ return nil;
+ }
+ return line[0:len line -1];
+}
+
+do_seek(argv : array of string) : string {
+ iob : ref Iobuf;
+ if (len argv < 3 || len argv > 4)
+ return notify(1,"seek fileId offset ?origin?");
+ iob=lookup_iob(argv[1]);
+ if (iob==nil)
+ return notify(0,sys->sprint(
+ "bad file identifier \"%s\"",argv[1]));
+ flag := Sys->SEEKSTART;
+ if (len argv == 4) {
+ case argv[3] {
+ "SEEKSTART" =>
+ flag = Sys->SEEKSTART;
+ "SEEKRELA" =>
+ flag = Sys->SEEKRELA;
+ "SEEKEND" =>
+ flag = Sys->SEEKEND;
+ * =>
+ return notify(0,sys->sprint(
+ "illegal access mode \"%s\"",
+ argv[3]));
+ }
+ }
+ iob.seek(big argv[2],flag);
+ return nil;
+}
+
+do_open(argv : array of string) : string {
+ flag : int;
+ if (len argv==1 || len argv > 3)
+ return notify(1,
+ "open filename ?access? ?permissions?");
+ name:=argv[1];
+ if (len argv == 2)
+ flag = bufmod->OREAD;
+ else {
+ case argv[2] {
+ "OREAD" =>
+ flag = bufmod->OREAD;
+ "OWRITE" =>
+ flag = bufmod->OWRITE;
+ "ORDWR" =>
+ flag = bufmod->ORDWR;
+ * =>
+ return notify(0,sys->sprint(
+ "illegal access mode \"%s\"",
+ argv[2]));
+ }
+ }
+ iob := bufmod->open(name,flag);
+ if (iob==nil)
+ return notify(0,
+ sys->sprint("couldn't open \"%s\": No" +
+ " such file or directory.",name));
+ for (i:=0;i<len name2fid;i++){
+ (iob2,name2,j):=name2fid[i];
+ if (iob2==nil){
+ name2fid[i]=(iob,"file"+string i,0);
+ return "file"+string i;
+ }
+ }
+ return notify(0,"File table full!");
+}
+
+do_puts(argv : array of string) : string {
+ iob : ref Iobuf;
+ if (len argv==1 || len argv >4)
+ return notify(1,
+ "puts ?-nonewline? ?fileId? string");
+ if (argv[1]=="-nonewline"){
+ if (len argv==2)
+ return notify(1,
+ "puts ?-nonewline? ?fileId? string");
+ if (len argv==3)
+ sys->print("%s",argv[2]);
+ else{
+ iob=lookup_iob(argv[2]);
+ if (iob==nil)
+ return notify(0,sys->sprint(
+ "bad file identifier \"%s\"",
+ argv[2]));
+ iob.puts(argv[3]);
+ iob.flush();
+ }
+ } else {
+ if (len argv==2)
+ sys->print("%s\n",argv[1]);
+ if (len argv==3){
+ iob=lookup_iob(argv[1]);
+ if (iob==nil)
+ return notify(0,sys->sprint(
+ "bad file identifier \"%s\"",
+ argv[1]));
+ iob.puts(argv[2]+"\n");
+ iob.flush();
+
+ }
+ if (len argv==4)
+ return notify(0,sys->sprint(
+ "bad argument \"%s\": should be"+
+ " \"nonewline\"",argv[3]));
+ }
+ return nil;
+}
+
+do_read(argv : array of string) : string {
+ iob : ref Iobuf;
+ line :string;
+ if (len argv<2 || len argv>3)
+ return notify(1,
+ "read fileId ?numBytes?\" or \"read ?-nonewline? fileId");
+ if (argv[1]!="-nonewline"){
+ iob=lookup_iob(argv[1]);
+ if (iob==nil)
+ return notify(0,sys->sprint(
+ "bad file identifier \"%s\"", argv[1]));
+ if (len argv == 3){
+ buf := array[int argv[2]] of byte;
+ n:=iob.read(buf,len buf);
+ if (n==0){
+ set_eof(iob);
+ return nil;
+ }
+ return string buf[0:n];
+ }
+ line=iob.gets('\n');
+ if (line==nil)
+ set_eof(iob);
+ else
+ line[len line]='\n';
+ return line;
+ }else{
+ iob=lookup_iob(argv[2]);
+ if (iob==nil)
+ return notify(0,sys->sprint(
+ "bad file identifier \"%s\"", argv[2]));
+ line=iob.gets('\n');
+ if (line==nil)
+ set_eof(iob);
+ return line;
+ }
+}
+
+
+
+
+
+
+
+
+notify(num : int,s : string) : string {
+ error=1;
+ case num{
+ 1 =>
+ return sys->sprint(
+ "wrong # args: should be \"%s\"",s);
+ * =>
+ return s;
+ }
+}
+
+
+lookup_iob(s:string) : ref Iobuf{
+ iob : ref Iobuf;
+ name : string;
+ j : int;
+ for(i:=0;i<len name2fid;i++){
+ (iob,name,j)=name2fid[i];
+ if (name==s)
+ break;
+ }
+ if (i==len name2fid)
+ return nil;
+ return iob;
+}
+
+set_eof(iob : ref Iobuf) {
+ iob2 : ref Iobuf;
+ name : string;
+ j : int;
+ for(i:=0;i<len name2fid;i++){
+ (iob2,name,j)=name2fid[i];
+ if (iob==iob2)
+ break;
+ }
+ if (i!=len name2fid)
+ name2fid[i]=(iob,name,1);
+ return;
+}
+
diff --git a/appl/lib/tcl_list.b b/appl/lib/tcl_list.b
new file mode 100644
index 00000000..9ef281d1
--- /dev/null
+++ b/appl/lib/tcl_list.b
@@ -0,0 +1,335 @@
+implement TclLib;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "tk.m";
+include "bufio.m";
+ bufmod : Bufio;
+Iobuf : import bufmod;
+
+include "string.m";
+ str : String;
+
+include "tcl.m";
+include "tcllib.m";
+
+include "utils.m";
+ utils : Tcl_Utils;
+
+
+error : int;
+
+DEF,DEC,INT : con iota;
+valid_commands:= array[] of {
+ "concat" ,
+ "join" ,
+ "lindex" ,
+ "linsert" ,
+ "list" ,
+ "llength" ,
+ "lrange" ,
+ "lreplace" ,
+ "lsearch" ,
+ "lsort" ,
+ "split"
+};
+
+about() : array of string {
+ return valid_commands;
+}
+
+exec(tcl : ref Tcl_Core->TclData,argv : array of string) : (int,string) {
+ if (tcl.context==nil);
+ str = load String String->PATH;
+ sys = load Sys Sys->PATH;
+ utils = load Tcl_Utils Tcl_Utils->PATH;
+ if (str==nil || utils==nil)
+ return (1,"Can't load modules\n");
+ case argv[0] {
+ "concat" =>
+ return (error,do_concat(argv,0));
+ "join" =>
+ return (error,do_join(argv));
+ "lindex" =>
+ return (error,do_lindex(argv));
+ "linsert" =>
+ return (error,do_linsert(argv));
+ "list" =>
+ return (error,do_concat(argv,1));
+ "llength" =>
+ return (error,do_llength(argv));
+ "lrange" =>
+ return (error,do_lrange(argv));
+ "lreplace" =>
+ return (error,do_lreplace(argv));
+ "lsearch" =>
+ return (error,do_lsearch(argv));
+ "lsort" =>
+ return (error,do_lsort(argv));
+ "split" =>
+ return (error,do_split(argv));
+ }
+ return (1,nil);
+}
+
+spaces(s : string) : int{
+ if (s==nil) return 1;
+ for(i:=0;i<len s;i++)
+ if (s[i]==' ' || s[i]=='\t') return 1;
+ return 0;
+}
+
+
+sort(a: array of string, key: int): array of string {
+ m: int;
+ n := len a;
+ for(m = n; m > 1; ) {
+ if(m < 5)
+ m = 1;
+ else
+ m = (5*m-1)/11;
+ for(i := n-m-1; i >= 0; i--) {
+ tmp := a[i];
+ for(j := i+m; j <= n-1 && greater(tmp, a[j], key); j += m)
+ a[j-m] = a[j];
+ a[j-m] = tmp;
+ }
+ }
+ return a;
+}
+
+greater(x, y: string, sortkey: int): int {
+ case (sortkey) {
+ DEF => return(x > y);
+ DEC => return(x < y);
+ INT => return(int x > int y);
+ }
+ return 0;
+}
+
+# from here on are the commands in alphabetical order...
+
+# turns an array into a string with spaces between the elements.
+# in braces is non-zero, the elements will be enclosed in braces.
+do_concat(argv : array of string, braces : int) : string {
+ retval :string;
+ retval=nil;
+ for(i:=1;i<len argv;i++){
+ flag:=0;
+ if (spaces(argv[i])) flag=1;
+ if (braces && flag) retval[len retval]='{';
+ retval += argv[i];
+ if (braces && flag) retval[len retval]='}';
+ retval[len retval]=' ';
+ }
+ if (retval!=nil)
+ retval=retval[0:len retval-1];
+ return retval;
+}
+
+do_join(argv : array of string) : string {
+ retval : string;
+ if (len argv ==1 || len argv >3)
+ return notify(1,"join list ?joinString?");
+ if (len argv == 2)
+ return argv[1];
+ if (argv[1]==nil) return nil;
+ arr := utils->break_it(argv[1]);
+ for (i:=0;i<len arr;i++){
+ retval+=arr[i];
+ if (i!=len arr -1)
+ retval+=argv[2];
+ }
+ return retval;
+}
+
+do_lindex(argv : array of string) : string {
+ if (len argv != 3)
+ return notify(1,"lindex list index");
+ (num,rest):=str->toint(argv[2],10);
+ if (rest!=nil)
+ return notify(2,argv[2]);
+ arr:=utils->break_it(argv[1]);
+ if (num>=len arr)
+ return nil;
+ return arr[num];
+}
+
+do_linsert(argv : array of string) : string {
+ if (len argv < 4){
+ return notify(1,
+ "linsert list index element ?element ...?");
+ }
+ (num,rest):=str->toint(argv[2],10);
+ if (rest!=nil)
+ return notify(2,argv[2]);
+ arr:=utils->break_it(argv[1]);
+ narr := array[len arr + len argv - 2] of string;
+ narr[0]="do_concat";
+ if (num==0){
+ narr[1:]=argv[3:];
+ narr[len argv -2:]=arr[0:];
+ }else if (num>= len arr){
+ narr[1:]=arr[0:];
+ narr[len arr+1:]=argv[3:];
+ }else{
+ narr[1:]=arr[0:num];
+ narr[num+1:]=argv[3:];
+ narr[num+len argv -2:]=arr[num:];
+ }
+ return do_concat(narr,1);
+}
+
+do_llength(argv : array of string) : string {
+ if (len argv !=2){
+ return notify(1,"llength list");
+ }
+ arr:=utils->break_it(argv[1]);
+ return string len arr;
+}
+
+do_lrange(argv :array of string) : string {
+ beg,end : int;
+ rest : string;
+ if (len argv != 4)
+ return notify(1,"lrange list first last");
+ (beg,rest)=str->toint(argv[2],10);
+ if (rest!=nil)
+ return notify(2,argv[2]);
+ (end,rest)=str->toint(argv[3],10);
+ if (rest!=nil)
+ return notify(2,argv[3]);
+ if (beg <0) beg=0;
+ if (end < 0) return nil;
+ if (beg > end) return nil;
+ arr:=utils->break_it(argv[1]);
+ if (beg>len arr) return nil;
+ narr:=array[end-beg+2] of string;
+ narr[0]="do_concat";
+ narr[1:]=arr[beg:end+1];
+ return do_concat(narr,1);
+}
+
+do_lreplace(argv : array of string) : string {
+ beg,end : int;
+ rest : string;
+ if (len argv < 3)
+ return notify(1,"lreplace list "+
+ "first last ?element element ...?");
+ arr:=utils->break_it(argv[1]);
+ (beg,rest)=str->toint(argv[2],10);
+ if (rest!=nil)
+ return notify(2,argv[2]);
+ (end,rest)=str->toint(argv[3],10);
+ if (rest!=nil)
+ return notify(2,argv[3]);
+ if (beg <0) beg=0;
+ if (end < 0) return nil;
+ if (beg > end)
+ return notify(0,
+ "first index must not be greater than second");
+ if (beg>len arr)
+ return notify(1,
+ "list doesn't contain element "+string beg);
+ narr:=array[len arr-(end-beg+1)+len argv - 3] of string;
+ narr[1:]=arr[0:beg];
+ narr[beg+1:]=argv[4:];
+ narr[beg+1+len argv-4:]=arr[end+1:];
+ narr[0]="do_concat";
+ return do_concat(narr,1);
+}
+
+do_lsearch(argv : array of string) : string {
+ if (len argv!=3)
+ return notify(1,"lsearch ?mode? list pattern");
+ arr:=utils->break_it(argv[1]);
+ for(i:=0;i<len arr;i++)
+ if (arr[i]==argv[2])
+ return string i;
+ return "-1";
+}
+
+do_lsort(argv : array of string) : string {
+ lis : array of string;
+ key : int;
+ key=DEF;
+ if (len argv == 1)
+ return notify(1,"lsort ?-ascii? ?-integer? ?-real?"+
+ " ?-increasing? ?-decreasing?"+
+ " ?-command string? list");
+ for(i:=1;i<len argv;i++)
+ if (argv[i][0]=='-')
+ case argv[i]{
+ "-decreasing" =>
+ key = DEC;
+ * =>
+ if (len argv != i+1)
+ return notify(0,sys->sprint(
+ "bad switch \"%s\": must be"+
+ " -ascii, -integer, -real, "+
+ "-increasing -decreasing, or"+
+ " -command" ,argv[i]));
+ }
+ lis=utils->break_it(argv[len argv-1]);
+ arr:=sort(lis,key);
+ narr:= array[len arr+1] of string;
+ narr[0]="list";
+ narr[1:]=arr[0:];
+ return do_concat(narr,1);
+}
+
+
+
+do_split(argv : array of string) : string {
+ arr := array[20] of string;
+ narr : array of string;
+ if (len argv ==1 || len argv>3)
+ return notify(1,"split string ?splitChars?");
+ if (len argv == 2)
+ return argv[1];
+ s:=argv[1];
+ if (s==nil) return nil;
+ if (argv[2]==nil){
+ arr=array[len s+1] of string;
+ for(i:=0;i<len s;i++)
+ arr[i+1][len arr[i+1]]=s[i];
+ arr[0]="do_concat";
+ return do_concat(arr,1);
+ }
+ i:=1;
+ while(s!=nil){
+ (piece,rest):=str->splitl(s,argv[2]);
+ arr[i]=piece;
+ if (len rest>1)
+ s=rest[1:];
+ if (len rest==1)
+ s=nil;
+ i++;
+ if (i==len arr){
+ narr=array[i+10] of string;
+ narr[0:]=arr[0:];
+ arr=array[i+10] of string;
+ arr=narr;
+ }
+ }
+ narr = array[i] of string;
+ arr[0]="do_concat";
+ narr = arr[0:i+1];
+ return do_concat(narr,1);
+}
+
+notify(num : int,s : string) : string {
+ error=1;
+ case num{
+ 1 =>
+ return sys->sprint(
+ "wrong # args: should be \"%s\"",s);
+ 2 =>
+ return sys->sprint(
+ "expected integer but got \"%s\"",s);
+ * =>
+ return s;
+ }
+}
+
diff --git a/appl/lib/tcl_modhash.b b/appl/lib/tcl_modhash.b
new file mode 100644
index 00000000..ebb85146
--- /dev/null
+++ b/appl/lib/tcl_modhash.b
@@ -0,0 +1,114 @@
+implement Mod_Hashtab;
+
+include "sys.m";
+include "draw.m";
+include "tk.m";
+include "tcl.m";
+include "tcllib.m";
+
+include "utils.m";
+
+
+hashasu(key : string,n : int): int{
+ i, h : int;
+ h=0;
+ i=0;
+ while(i<len key){
+ h = 10*h + key[i];
+ h = h%n;
+ i++;
+ }
+ return h%n;
+}
+
+alloc(size : int) : ref MHash {
+ h : MHash;
+ t : list of H_link;
+ t=nil;
+ h.size= size;
+ h.tab = array[size] of {* => t};
+ return ref h;
+}
+
+MHash.dump(h : self ref MHash) : string {
+ retval :string;
+ for (i:=0;i<h.size;i++){
+ tmp:=(h.tab)[i];
+ for(;tmp!=nil;tmp = tl tmp){
+ if ((hd tmp).name!=nil){
+ retval+=(hd tmp).name;
+ retval[len retval]=' ';
+ }
+ }
+ }
+ if (retval!=nil)
+ retval=retval[0:len retval-1];
+ return retval;
+}
+
+
+
+MHash.insert(h : self ref MHash,name: string, val:TclLib) : int {
+ link : H_link;
+ hash,found : int;
+ nlist : list of H_link;
+ nlist=nil;
+ found=0;
+ hash = hashasu(name,h.size);
+ tmp:=(h.tab)[hash];
+ for(;tmp!=nil;tmp = tl tmp){
+ link=hd tmp;
+ if (link.name==name){
+ found=1;
+ link.val = val;
+ }
+ nlist = link :: nlist;
+ }
+ if (!found){
+ link.name=name;
+ link.val=val;
+ (h.tab)[hash]= link :: (h.tab)[hash];
+ }else
+ (h.tab)[hash]=nlist;
+ return 1;
+}
+
+MHash.find(h : self ref MHash,name : string) : (int, TclLib){
+ hash,flag : int;
+ nlist : list of H_link;
+ retval : TclLib;
+ retval=nil;
+ flag=0;
+ nlist=nil;
+ hash = hashasu(name,h.size);
+ tmp:=(h.tab)[hash];
+ for(;tmp!=nil;tmp = tl tmp){
+ link:=hd tmp;
+ if ((hd tmp).name==name){
+ flag = 1;
+ retval = (hd tmp).val;
+ }
+ nlist = link :: nlist;
+ }
+ (h.tab)[hash]=nlist;
+ return (flag,retval);
+}
+
+MHash.delete(h : self ref MHash,name : string) : int {
+ hash,flag : int;
+ nlist : list of H_link;
+ flag=0;
+ nlist=nil;
+ hash = hashasu(name,h.size);
+ tmp:=(h.tab)[hash];
+ for(;tmp!=nil;tmp = tl tmp){
+ link:=hd tmp;
+ if (link.name==name)
+ flag = 1;
+ else
+ nlist = link :: nlist;
+ }
+ (h.tab)[hash]=nlist;
+ return flag;
+}
+
diff --git a/appl/lib/tcl_stack.b b/appl/lib/tcl_stack.b
new file mode 100644
index 00000000..8246ddf2
--- /dev/null
+++ b/appl/lib/tcl_stack.b
@@ -0,0 +1,142 @@
+implement Tcl_Stack;
+
+include "sys.m";
+ sys : Sys;
+include "draw.m";
+include "tk.m";
+include "tcl.m";
+include "tcllib.m";
+include "utils.m";
+ htab: Str_Hashtab;
+ shtab: Sym_Hashtab;
+Hash: import htab;
+SHash: import shtab;
+
+sframe : adt {
+ simple : ref Hash;
+ assoc : array of (ref Hash,string);
+ symtab : ref SHash;
+};
+
+stack := array[100] of sframe;
+curlevel : int;
+nlev : int;
+
+init() {
+ curlevel=-1;
+ nlev=-1;
+ htab = load Str_Hashtab Str_Hashtab->PATH;
+ shtab = load Sym_Hashtab Sym_Hashtab->PATH;
+ sys = load Sys Sys->PATH;
+ if (htab == nil){
+ sys->print("can't load Hashtab %r\n");
+ exit;
+ }
+ if (shtab == nil){
+ sys->print("can't load Sym_Hashtab %r\n");
+ exit;
+ }
+}
+
+newframe() : (ref Hash,array of (ref Hash,string),ref SHash) {
+ nv := htab->alloc(101);
+ av := array[100] of (ref Hash,string);
+ st := shtab->alloc(101);
+ #sys->print("New frame, curlevel is %d\n",curlevel);
+ push (nv,av,st);
+ return (nv,av,st);
+}
+
+level() : int {
+ return curlevel;
+}
+
+move(lev :int) : int {
+ if (lev <0 || lev>nlev)
+ return 0;
+ curlevel=lev;
+ return 1;
+}
+
+push(sv : ref Hash, av : array of (ref Hash,string), st :ref SHash){
+ curlevel++;
+ nlev++;
+ stack[curlevel].simple=sv;
+ stack[curlevel].assoc=av;
+ stack[curlevel].symtab=st;
+}
+
+pop() : (ref Hash,array of (ref Hash,string),ref SHash) {
+ s:=stack[curlevel].simple;
+ a:=stack[curlevel].assoc;
+ t:=stack[curlevel].symtab;
+ stack[curlevel].simple=nil;
+ stack[curlevel].assoc=nil;
+ stack[curlevel].symtab=nil;
+ curlevel--;
+ nlev--;
+ return (s,a,t);
+}
+
+examine(lev : int) : (ref Hash,array of (ref Hash,string),ref SHash) {
+ if (lev <0 || lev > nlev)
+ return (nil,nil,nil);
+ return (stack[lev].simple,stack[lev].assoc,stack[lev].symtab);
+}
+
+dump() {
+ for (i:=0;i<100;i++){
+ if (stack[i].simple!=nil){
+ sys->print("simple table at %d\n",i);
+ for (j:=0;j<101;j++)
+ if (stack[i].simple.tab[j]!=nil){
+ sys->print("\tH_link at %d\n",j);
+ l:=stack[i].simple.tab[j];
+ while(l!=nil){
+ sys->print("\tname [%s], value [%s]\n",
+ (hd l).name,(hd l).val);
+ l=tl l;
+ }
+ }
+ }
+ if (stack[i].assoc!=nil){
+ sys->print("assoc table at %d\n",i);
+ for(j:=0;j<100;j++){
+ (rh,s):=stack[i].assoc[j];
+ if (rh!=nil){
+ sys->print(
+ "\tassoc array at %d, name %s\n",
+ j,s);
+ for (k:=0;k<101;k++)
+ if (rh.tab[k]!=nil){
+ sys->print(
+ "\t\tH_link at %d\n",k);
+ l:=rh.tab[k];
+ while(l!=nil){
+ sys->print(
+ "\t\tname [%s], value [%s]\n",
+ (hd l).name,(hd l).val);
+ l=tl l;
+ }
+ }
+
+ }
+ }
+ }
+ if (stack[i].symtab!=nil){
+ sys->print("Symbol table at %d\n",i);
+ for (j:=0;j<101;j++)
+ if (stack[i].symtab.tab[j]!=nil){
+ sys->print("\tH_link at %d\n",j);
+ l:=stack[i].symtab.tab[j];
+ while(l!=nil){
+ sys->print("\tname [%s], alias [%s], "+
+ "value [%d]\n",(hd l).name,
+ (hd l).alias,(hd l).val);
+ l=tl l;
+ }
+ }
+ }
+ }
+}
+
diff --git a/appl/lib/tcl_strhash.b b/appl/lib/tcl_strhash.b
new file mode 100644
index 00000000..c273ef61
--- /dev/null
+++ b/appl/lib/tcl_strhash.b
@@ -0,0 +1,116 @@
+implement Str_Hashtab;
+
+include "sys.m";
+include "draw.m";
+include "tk.m";
+include "tcl.m";
+include "tcllib.m";
+include "utils.m";
+
+
+hashasu(key : string,n : int): int{
+ i, h : int;
+ h=0;
+ i=0;
+ while(i<len key){
+ h = 10*h + key[i];
+ h = h%n;
+ i++;
+ }
+ return h%n;
+}
+
+alloc(size : int) : ref Hash {
+ h : Hash;
+ t : list of H_link;
+ t=nil;
+ h.size= size;
+ h.lsize=0;
+ h.tab = array[size] of {* => t};
+ return ref h;
+}
+
+Hash.dump(h : self ref Hash) : string {
+ retval :string;
+ for (i:=0;i<h.size;i++){
+ tmp:=(h.tab)[i];
+ for(;tmp!=nil;tmp = tl tmp){
+ if ((hd tmp).name!=nil){
+ retval+=(hd tmp).name;
+ retval[len retval]=' ';
+ }
+ }
+ }
+ if (retval!=nil)
+ retval=retval[0:len retval-1];
+ return retval;
+}
+
+Hash.insert(h : self ref Hash,name,val: string) : int {
+ link : H_link;
+ hash,found : int;
+ nlist : list of H_link;
+ nlist=nil;
+ found=0;
+ hash = hashasu(name,h.size);
+ tmp:=(h.tab)[hash];
+ for(;tmp!=nil;tmp = tl tmp){
+ link=hd tmp;
+ if (link.name==name){
+ found=1;
+ link.val = val;
+ }
+ nlist = link :: nlist;
+ }
+ if (!found){
+ h.lsize++;
+ link.name=name;
+ link.val=val;
+ (h.tab)[hash]= link :: (h.tab)[hash];
+ }else
+ (h.tab)[hash]=nlist;
+ return 1;
+}
+
+Hash.find(h : self ref Hash,name : string) : (int, string){
+ hash,flag : int;
+ nlist : list of H_link;
+ retval : string;
+ flag=0;
+ nlist=nil;
+ retval=nil;
+ hash = hashasu(name,h.size);
+ tmp:=(h.tab)[hash];
+ for(;tmp!=nil;tmp = tl tmp){
+ link:=hd tmp;
+ if ((hd tmp).name==name){
+ flag = 1;
+ retval = (hd tmp).val;
+ }
+ nlist = link :: nlist;
+ }
+ (h.tab)[hash]=nlist;
+ return (flag,retval);
+}
+
+Hash.delete(h : self ref Hash,name : string) : int {
+ hash,flag : int;
+ nlist : list of H_link;
+ retval : string;
+ flag=0;
+ nlist=nil;
+ retval=nil;
+ hash = hashasu(name,h.size);
+ tmp:=(h.tab)[hash];
+ for(;tmp!=nil;tmp = tl tmp){
+ link:=hd tmp;
+ if (link.name==name){
+ flag = 1;
+ h.lsize--;
+ }else
+ nlist = link :: nlist;
+ }
+ (h.tab)[hash]=nlist;
+ return flag;
+}
+
diff --git a/appl/lib/tcl_string.b b/appl/lib/tcl_string.b
new file mode 100644
index 00000000..088390df
--- /dev/null
+++ b/appl/lib/tcl_string.b
@@ -0,0 +1,246 @@
+implement TclLib;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "tk.m";
+include "bufio.m";
+ bufmod : Bufio;
+Iobuf : import bufmod;
+
+include "string.m";
+ str : String;
+include "tcl.m";
+include "tcllib.m";
+
+error : int;
+started : int;
+valid_commands:=array[] of {"format","string"};
+
+about() : array of string{
+ return valid_commands;
+}
+
+init(){
+ started=1;
+ sys=load Sys Sys->PATH;
+}
+
+exec(tcl : ref Tcl_Core->TclData,argv : array of string) : (int,string) {
+ if (tcl.context==nil);
+ if (!started) init();
+ error=0;
+ str=load String String->PATH;
+ if (str==nil)
+ return(1,"String module not loaded.");
+ if (len argv==1 && argv[0]=="string")
+ return (error,
+ notify(1,"string option arg ?arg ...?"));
+ case argv[0]{
+ "format" =>
+ return (error,do_format(argv));
+ "string" =>
+ return (error,do_string(argv));
+ }
+ return (1,nil);
+}
+
+
+do_string(argv : array of string) : string{
+ case argv[1]{
+ "compare" =>
+ if (len argv == 4){
+ i:= - (argv[2]<argv[3])+ (argv[2]>argv[3]);
+ return string i;
+ }
+ return notify(1,
+ "string compare string1 string2");
+ "first" =>
+ return nil;
+ "last" =>
+ return nil;
+ "index" =>
+ if (len argv == 4){
+ if (len argv[2] > int argv[3])
+ return argv[2][int argv[3]:int argv[3]+1];
+ return nil;
+ }
+ return notify(1,
+ "string index string charIndex");
+ "length" =>
+ if (len argv==3)
+ return string len argv[2];
+ return notify(1,"string length string");
+ "match" =>
+ return nil;
+ "range" =>
+ if (len argv==5){
+ end :int;
+ if (argv[4]=="end")
+ end=len argv[2];
+ else
+ end=int argv[4];
+ if (end>len argv[2]) end=len argv[2];
+ beg:=int argv[3];
+ if (beg<0) beg=0;
+ if (beg>end)
+ return nil;
+ return argv[2][int argv[3]:end];
+ }
+ return notify(1,
+ "string range string first last");
+ "tolower" =>
+ if (len argv==3)
+ return str->tolower(argv[2]);
+ return notify(1,"string tolower string");
+ "toupper" =>
+ if (len argv==3)
+ return str->tolower(argv[2]);
+ return notify(1,"string tolower string");
+ "trim" =>
+ return nil;
+ "trimleft" =>
+ return nil;
+ "trimright" =>
+ return nil;
+ "wordend" =>
+ return nil;
+ "wordstart" =>
+ return nil;
+ }
+ return nil;
+}
+
+do_format(argv : array of string) : string {
+ retval,num1,num2,rest,curfm : string;
+ i,j : int;
+ if (len argv==1)
+ return notify(1,
+ "format formatString ?arg arg ...?");
+ j=2;
+ i1:=-1;
+ i2:=-1;
+ (retval,rest)=str->splitl(argv[1],"%");
+ do {
+ (curfm,rest)=str->splitl(rest[1:],"%");
+ i=0;
+ num1="";
+ num2="";
+ if (curfm[i]=='-'){
+ num1[len num1]=curfm[i];
+ i++;
+ }
+ while(curfm[i]>='0' && curfm[i]<='9'){
+ num1[len num1]=curfm[i];
+ i++;
+ }
+ if (num1!="")
+ (i1,nil) = str->toint(num1,10);
+ if (curfm[i]=='.'){
+ i++;
+ while(curfm[i]>='0' && curfm[i]<='9'){
+ num2[len num2]=curfm[i];
+ i++;
+ }
+ (i2,nil) = str->toint(num2,10);
+ } else {
+ i2=i1;
+ i1=-1;
+ }
+ case curfm[i] {
+ 's' =>
+ retval+=print_string(i1,i2,argv[j]);
+ 'd' =>
+ retval+=print_int(i1,i2,argv[j]);
+ 'f' =>
+ retval+=print_float(i1,i2,argv[j]);
+ 'x' =>
+ retval+=print_hex(i1,i2,argv[j]);
+ }
+ j++;
+ } while (rest!=nil && j<len argv);
+ return retval;
+}
+
+notify(num : int,s : string) : string {
+ error=1;
+ case num{
+ 1 =>
+ return sys->sprint(
+ "wrong # args: should be \"%s\"",s);
+ * =>
+ return s;
+ }
+}
+
+print_string(i1,i2 : int, s : string) : string {
+ retval : string;
+ if (i1==-1 && i2==-1)
+ retval=sys->sprint("%s",s);
+ if (i1==-1 && i2!=-1)
+ retval=sys->sprint("%*s",i1,s);
+ if (i1!=-1 && i2!=-1)
+ retval=sys->sprint("%*.*s",i1,i2,s);
+ if (i1!=-1 && i2==-1)
+ retval=sys->sprint("%.*s",i2,s);
+ return retval;
+}
+
+print_int(i1,i2 : int, s : string) : string {
+ retval,ret2 : string;
+ n : int;
+ (num,nil):=str->toint(s,10);
+ width:=1;
+ i:=num;
+ while((i/=10)!= 0) width++;
+ if (i2 !=-1 && width<i2) width=i2;
+ for(i=0;i<width;i++)
+ retval[len retval]='0';
+ while(width!=0){
+ retval[width-1]=num%10+'0';
+ num/=10;
+ width--;
+ }
+ if (i1 !=-1 && i1>i){
+ for(n=0;n<i1-i;n++)
+ ret2[len ret2]=' ';
+ ret2+=retval;
+ retval=ret2;
+ }
+ return retval;
+}
+
+
+print_float(i1,i2 : int, s : string) : string {
+ r:= real s;
+ retval:=sys->sprint("%*.*f",i1,i2,r);
+ return retval;
+}
+
+print_hex(i1,i2 : int, s : string) : string {
+ retval,ret2 : string;
+ n : int;
+ (num,nil):=str->toint(s,10);
+ width:=1;
+ i:=num;
+ while((i/=16)!= 0) width++;
+ if (i2 !=-1 && width<i2) width=i2;
+ for(i=0;i<width;i++)
+ retval[len retval]='0';
+ while(width!=0){
+ n=num%16;
+ if (n>=0 && n<=9)
+ retval[width-1]=n+'0';
+ else
+ retval[width-1]=n+'a'-10;
+ num/=16;
+ width--;
+ }
+ if (i1 !=-1 && i1>i){
+ for(n=0;n<i1-i;n++)
+ ret2[len ret2]=' ';
+ ret2+=retval;
+ retval=ret2;
+ }
+ return retval;
+}
diff --git a/appl/lib/tcl_symhash.b b/appl/lib/tcl_symhash.b
new file mode 100644
index 00000000..f5783eaa
--- /dev/null
+++ b/appl/lib/tcl_symhash.b
@@ -0,0 +1,99 @@
+implement Sym_Hashtab;
+
+include "sys.m";
+include "draw.m";
+include "tk.m";
+include "tcl.m";
+include "tcllib.m";
+include "utils.m";
+
+
+hashasu(key : string,n : int): int{
+ i, h : int;
+ h=0;
+ i=0;
+ while(i<len key){
+ h = 10*h + key[i];
+ h = h%n;
+ i++;
+ }
+ return h%n;
+}
+
+alloc(size : int) : ref SHash {
+ h : SHash;
+ t : list of H_link;
+ t=nil;
+ h.size= size;
+ h.tab = array[size] of {* => t};
+ return ref h;
+}
+
+
+SHash.insert(h : self ref SHash,name,alias: string,val:int) : int {
+ link : H_link;
+ hash,found : int;
+ nlist : list of H_link;
+ nlist=nil;
+ found=0;
+ hash = hashasu(name,h.size);
+ tmp:=(h.tab)[hash];
+ for(;tmp!=nil;tmp = tl tmp){
+ link=hd tmp;
+ if (link.name==name){
+ found=1;
+ link.val = val;
+ link.alias = alias;
+ }
+ nlist = link :: nlist;
+ }
+ if (!found){
+ link.name=name;
+ link.val=val;
+ link.alias = alias;
+ (h.tab)[hash]= link :: (h.tab)[hash];
+ }else
+ (h.tab)[hash]=nlist;
+ return 1;
+}
+
+SHash.find(h : self ref SHash,name : string) : (int, int,string){
+ hash,flag : int;
+ nlist : list of H_link;
+ al : string;
+ retval:=0;
+ flag=0;
+ nlist=nil;
+ hash = hashasu(name,h.size);
+ tmp:=(h.tab)[hash];
+ for(;tmp!=nil;tmp = tl tmp){
+ link:=hd tmp;
+ if ((hd tmp).name==name){
+ flag = 1;
+ retval = (hd tmp).val;
+ al = (hd tmp).alias;
+ }
+ nlist = link :: nlist;
+ }
+ (h.tab)[hash]=nlist;
+ return (flag,retval,al);
+}
+
+SHash.delete(h : self ref SHash,name : string) : int {
+ hash,flag : int;
+ nlist : list of H_link;
+ flag=0;
+ nlist=nil;
+ hash = hashasu(name,h.size);
+ tmp:=(h.tab)[hash];
+ for(;tmp!=nil;tmp = tl tmp){
+ link:=hd tmp;
+ if (link.name==name)
+ flag = 1;
+ else
+ nlist = link :: nlist;
+ }
+ (h.tab)[hash]=nlist;
+ return flag;
+}
+
diff --git a/appl/lib/tcl_tk.b b/appl/lib/tcl_tk.b
new file mode 100644
index 00000000..12cd611d
--- /dev/null
+++ b/appl/lib/tcl_tk.b
@@ -0,0 +1,223 @@
+implement TclLib;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "string.m";
+ str : String;
+
+include "tk.m";
+ tk: Tk;
+
+include "tkclient.m";
+ tkclient: Tkclient;
+
+include "tcl.m";
+
+include "tcllib.m";
+
+error,started : int;
+w_cfg := array[] of {
+ "pack .Wm_t -side top -fill x",
+ "update",
+};
+
+tclmod : ref Tcl_Core->TclData;
+
+windows := array[100] of (string, ref Tk->Toplevel, chan of string);
+
+valid_commands:= array[] of {
+ "bind" , "bitmap" , "button" ,
+ "canvas" , "checkbutton" , "destroy" ,
+ "entry" , "focus", "frame" , "grab", "image" , "label" ,
+ "listbox" ,"lower", "menu" , "menubutton" ,
+ "pack" , "radiobutton" , "raise", "scale" ,
+ "scrollbar" , "text" , "update" ,
+ "toplevel" , "variable"
+};
+
+about() : array of string {
+ return valid_commands;
+}
+
+init() : string {
+ sys = load Sys Sys->PATH;
+ str = load String String->PATH;
+ tk = load Tk Tk->PATH;
+ tkclient = load Tkclient Tkclient->PATH;
+ if (tkclient==nil || str==nil || tk==nil)
+ return "Not Initialised";
+ # set up Draw context
+ tkclient->init();
+ started=1;
+ return nil;
+}
+
+exec(tcl : ref Tcl_Core->TclData,argv : array of string) : (int,string) {
+ retval : string;
+ retval="";
+ han,whan : ref Tk->Toplevel;
+ whan=nil;
+ msg : string;
+ c : chan of string;
+ msg=nil;
+ error=0;
+ tclmod=tcl;
+ if (!started)
+ if (init()!=nil)
+ return (1,"Can't Initialise TK");
+ if (argv[0][0]!='.')
+ case argv[0] {
+ "destroy" =>
+ for (j:=1;j<len argv;j++){
+ (msg,han)=sweepthru(argv[j]);
+ if (msg==nil){
+ if (argv[j][0]=='.')
+ argv[j]=argv[j][1:];
+ for(i:=0;i<100;i++){
+ (retval,nil,c)=windows[i];
+ if (retval==argv[1]){
+ c <-= "exit";
+ break;
+ }
+ }
+ }
+ else
+ msg=tkcmd(whan,"destroy "+msg);
+ }
+ return (error,msg);
+ "bind" or "bitmap" or "button" or
+ "canvas" or "checkbutton" or "entry" or
+ "focus" or "frame" or "grab" or
+ "image" or "label" or "listbox" or "lower" or
+ "menu" or "menubutton" or "pack" or
+ "radiobutton" or "raise" or "scale" or
+ "scrollbar" or "text" or "update" or
+ "variable" =>
+ ; # do nothing
+ "toplevel" =>
+ msg=do_toplevel(argv);
+ return (error,msg);
+ * =>
+ return (0,"Unknown");
+ }
+ # so it's a tk-command ... replace any -command with
+ # a send on the tcl channel.
+ if (argv[0]=="bind")
+ argv[3]="{send Tcl_Chan "+argv[3]+"}";
+ for (i:=0;i<len argv;i++){
+ (argv[i],han)=sweepthru(argv[i]);
+ if (han!=nil) whan=han;
+ if (argv[i]!="-tcl")
+ retval+=argv[i];
+ if (i+1<len argv &&
+ (argv[i]=="-command" || argv[i]=="-yscrollcommand"
+ || argv[i]=="-tcl" || argv[i]=="-xscrollcommand"))
+ argv[i+1]="{send Tcl_Chan "+argv[i+1]+"}";
+ if (argv[i]!="-tcl")
+ retval[len retval]=' ';
+ }
+ retval=retval[0:len retval -1];
+ if (tclmod.debug==1)
+ sys->print("Sending [%s] to tkcmd.\n",retval);
+ msg=tkcmd(whan,retval);
+ if (msg!="" && msg[0]=='!')
+ error=1;
+ return (error,msg);
+}
+
+
+sweepthru(s: string) : (string,ref Tk->Toplevel) {
+ han : ref Tk->Toplevel;
+ ret : string;
+ if (s=="" || s=="." || s[0]!='.')
+ return (s,nil);
+ (wname,rest):=str->splitl(s[1:],".");
+ for (i:=0;i<len windows;i++){
+ (ret,han,nil)=windows[i];
+ if (ret==wname)
+ break;
+ }
+ if (i==len windows)
+ return (s,nil);
+ return (rest,han);
+}
+
+do_toplevel(argv : array of string): string
+{
+ name : string;
+ whan : ref Tk->Toplevel;
+ if (len argv!=2)
+ return notify(1,"toplevel name");
+ if (argv[1][0]=='.')
+ argv[1]=argv[1][1:];
+ for(i:=0;i<len windows;i++){
+ (name,whan,nil)=windows[i];
+ if(whan==nil || name==argv[1])
+ break;
+ }
+ if (i==len windows)
+ return notify(0,"Too many top level windows");
+ if (name==argv[1])
+ return notify(0,argv[1]+" is already a window name in use.");
+
+ (top, menubut) := tkclient->toplevel(tclmod.context, "", argv[1], Tkclient->Appl);
+ whan = top;
+
+ windows[i]=(argv[1],whan,menubut);
+ if (tclmod.debug==1)
+ sys->print("creating window %d, name %s, handle %ux\n",i,argv[1],whan);
+ cmd := chan of string;
+ tk->namechan(whan, cmd, argv[1]);
+ for(i=0; i<len w_cfg; i++)
+ tk->cmd(whan, w_cfg[i]);
+ tkclient->onscreen(whan, nil);
+ tkclient->startinput(whan, "kbd"::"ptr"::nil);
+ stop := chan of int;
+ spawn tkclient->handler(whan, stop);
+ spawn menulisten(whan,menubut, stop);
+ return nil;
+}
+
+
+menulisten(t : ref Tk->Toplevel, menubut : chan of string, stop: chan of int) {
+ for(;;) alt {
+ menu := <-menubut =>
+ if(menu == "exit"){
+ for(i:=0;i<len windows;i++){
+ (name,whan,nil):=windows[i];
+ if(whan==t)
+ break;
+ }
+ if (i!=len windows)
+ windows[i]=("",nil,nil);
+ stop <-= 1;
+ exit;
+ }
+ tkclient->wmctl(t, menu);
+ }
+}
+
+tkcmd(t : ref Tk->Toplevel, cmd: string): string {
+ if (len cmd ==0 || tclmod.top==nil) return nil;
+ if (t==nil){
+ t=tclmod.top;
+ #sys->print("Sending to WishPad\n");
+ }
+ s := tk->cmd(t, cmd);
+ tk->cmd(t,"update");
+ return s;
+}
+
+notify(num : int,s : string) : string {
+ error=1;
+ case num{
+ 1 =>
+ return sys->sprint(
+ "wrong # args: should be \"%s\"",s);
+ * =>
+ return s;
+ }
+}
diff --git a/appl/lib/tcl_utils.b b/appl/lib/tcl_utils.b
new file mode 100644
index 00000000..8dcf13bf
--- /dev/null
+++ b/appl/lib/tcl_utils.b
@@ -0,0 +1,61 @@
+implement Tcl_Utils;
+include "sys.m";
+include "draw.m";
+include "tk.m";
+include "tcl.m";
+include "tcllib.m";
+include "utils.m";
+
+break_it(s : string) : array of string {
+ argv:= array[200] of string;
+ buf : string;
+ argc := 0;
+ nc := 0;
+ outer:
+ for (i := 0; i < len s ; ) {
+ case int s[i] {
+ ' ' or '\t' or '\n' =>
+ if (nc > 0) { # end of a word?
+ argv[argc++] = buf;
+ buf = nil;
+ nc = 0;
+ }
+ i++;
+ '{' =>
+ if (s[i+1]=='}'){
+ argv[argc++] = nil;
+ buf = nil;
+ nc = 0;
+ i+=2;
+ }else{
+ nbra := 1;
+ for (i++; i < len s; i++) {
+ if (s[i] == '{')
+ nbra++;
+ else if (s[i] == '}') {
+ nbra--;
+ if (nbra == 0) {
+ i++;
+ continue outer;
+ }
+ }
+ buf[nc++] = s[i];
+ }
+ }
+ * =>
+ buf[nc++] = s[i++];
+ }
+ }
+ if (nc > 0) # fix up last word if present
+ argv[argc++] = buf;
+ ret := array[argc] of string;
+ ret[0:] = argv[0:argc];
+ return ret;
+}
+
+arr_resize(argv : array of string) : array of string {
+ ret := array[len argv + 25] of string;
+ ret[0:]=argv;
+ return ret;
+}
+
diff --git a/appl/lib/tftp.b b/appl/lib/tftp.b
new file mode 100644
index 00000000..f112b892
--- /dev/null
+++ b/appl/lib/tftp.b
@@ -0,0 +1,170 @@
+implement Tftp;
+
+include "sys.m";
+ sys: Sys;
+
+include "tftp.m";
+
+Maxretry: con 5; # retries per block
+Maxblock: con 512; # protocol's usual maximum data block size
+Tftphdrlen: con 4;
+Read, Write, Data, Ack, Error: con 1+iota; # tftp opcode
+
+progress: int;
+
+put2(buf: array of byte, o: int, val: int)
+{
+ buf[o] = byte (val >> 8);
+ buf[o+1] = byte val;
+}
+
+get2(buf: array of byte, o: int): int
+{
+ return (int buf[o] << 8) | int buf[o+1];
+}
+
+kill(pid: int)
+{
+ fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE);
+ if(fd == nil)
+ return;
+
+ msg := array of byte "kill";
+ sys->write(fd, msg, len msg);
+}
+
+timeoutproc(c: chan of int, howlong: int)
+{
+ c <-= sys->pctl(0, nil);
+ sys->sleep(howlong);
+ c <-= 1;
+}
+
+tpid := -1;
+
+timeoutcancel()
+{
+ if(tpid >= 0) {
+ kill(tpid);
+ tpid = -1;
+ }
+}
+
+timeoutstart(howlong: int): chan of int
+{
+ timeoutcancel();
+ tc := chan of int;
+ spawn timeoutproc(tc, howlong);
+ tpid = <-tc;
+ return tc;
+}
+
+init(p: int)
+{
+ sys = load Sys Sys->PATH;
+ progress = p;
+}
+
+reader(pidc: chan of int, fd: ref Sys->FD, bc: chan of array of byte)
+{
+ pid := sys->pctl(0, nil);
+ pidc <-= pid;
+ buf := array[Tftphdrlen + Maxblock] of byte;
+ for(;;){
+ n := sys->read(fd, buf, len buf);
+ bc <-= buf[0 : n];
+ }
+}
+
+receive(host: string, filename: string, fd: ref Sys->FD): string
+{
+ rbuf: array of byte;
+
+ (ok, conn) := sys->dial("udp!" + host + "!69", nil);
+ if(ok < 0)
+ return sys->sprint("can't dial %s: %r", host);
+ buf := array[Tftphdrlen + Maxblock] of byte;
+ i := 0;
+ put2(buf, i, Read);
+ i += 2;
+ a := array of byte filename;
+ buf[i:] = a;
+ i += len a;
+ buf[i++] = byte 0;
+ mode := array of byte "binary";
+ buf[i:] = mode;
+ i += len mode;
+ buf[i++] = byte 0;
+ pidc := chan of int;
+ bc := chan of array of byte;
+ spawn reader(pidc, conn.dfd, bc);
+ tftppid := <-pidc;
+ lastblock := 0;
+ for(;;) {
+ Retry:
+ for(count := 0;; count++) {
+ if(count >= Maxretry){
+ kill(tftppid);
+ return sys->sprint("tftp timeout");
+ }
+
+ # (re)send request/ack
+ if(sys->write(conn.dfd, buf, i) < 0) {
+ kill(tftppid);
+ return sys->sprint( "error writing %s/data: %r", conn.dir);
+ }
+
+ # wait for next block
+ mtc := timeoutstart(3000);
+ for(;;){
+ alt {
+ <-mtc =>
+ if(progress)
+ sys->print("T");
+ continue Retry;
+ rbuf = <-bc =>
+ if(len rbuf < Tftphdrlen)
+ break;
+ op := get2(rbuf, 0);
+ case op {
+ Data =>
+ block := get2(rbuf, 2);
+ if(block == lastblock + 1) {
+ timeoutcancel();
+ break Retry;
+ }else if(progress)
+ sys->print("S");
+ Error =>
+ timeoutcancel();
+ kill(tftppid);
+ return sys->sprint("server error %d: %s", get2(rbuf, 2), string rbuf[4:]);
+ * =>
+ timeoutcancel();
+ kill(tftppid);
+ return sys->sprint("phase error op=%d", op);
+ }
+ }
+ }
+ }
+ n := len rbuf;
+ # copy the data somewhere
+ if(sys->write(fd, rbuf[Tftphdrlen:], n - Tftphdrlen) < 0) {
+ kill(tftppid);
+ return sys->sprint("writing destination: %r");
+ }
+ lastblock++;
+ if(progress && lastblock % 25 == 0)
+ sys->print(".");
+ if(n < Maxblock + Tftphdrlen) {
+ if(progress)
+ sys->print("\n");
+ break;
+ }
+
+ # send an ack
+ put2(buf, 0, Ack);
+ put2(buf, 2, lastblock);
+ }
+ kill(tftppid);
+ return nil;
+}
diff --git a/appl/lib/timers.b b/appl/lib/timers.b
new file mode 100644
index 00000000..7811ed5f
--- /dev/null
+++ b/appl/lib/timers.b
@@ -0,0 +1,99 @@
+implement Timers;
+
+include "sys.m";
+ sys: Sys;
+
+include "timers.m";
+
+timerin: chan of ref Timer;
+
+init(minms: int): int
+{
+ sys = load Sys Sys->PATH;
+ timerin = chan[20] of ref Timer;
+ if(minms <= 0)
+ minms = 1;
+ pid := chan of int;
+ spawn timeproc(timerin, minms, pid);
+ return <-pid;
+}
+
+shutdown()
+{
+ if(timerin != nil)
+ timerin <-= nil;
+}
+
+Timer.start(dt: int): ref Timer
+{
+ t := ref Timer(dt, chan[1] of int);
+ timerin <-= t;
+ return t;
+}
+
+Timer.stop(t: self ref Timer)
+{
+ # this is safe, because only Timer.stop sets t.timeout and timeproc only fetches it
+ t.timeout = nil;
+}
+
+timeproc(req: chan of ref Timer, msec: int, pid: chan of int)
+{
+ pending: list of ref Timer;
+
+ pid <-= sys->pctl(Sys->NEWFD|Sys->NEWNS|Sys->NEWENV, nil); # same pgrp
+ old := sys->millisec();
+Work:
+ for(;;){
+ if(pending == nil){
+ if((t := <-req) == nil)
+ break Work;
+ pending = t :: pending;
+ old = sys->millisec();
+ }else{
+ # check quickly for new requests
+ Check:
+ for(;;) alt{
+ t := <-req =>
+ if(t == nil)
+ break Work;
+ pending = t :: pending;
+ * =>
+ break Check;
+ }
+ }
+ sys->sleep(msec);
+ new := sys->millisec();
+ dt := new-old;
+ old = new;
+ if(dt < 0)
+ continue; # millisec counter wrapped
+ ticked := 0;
+ for(l := pending; l != nil; l = tl l)
+ if(((hd l).dt -= dt) <= 0)
+ ticked = 1;
+ if(ticked){
+ l = pending;
+ pending = nil;
+ for(; l != nil; l = tl l){
+ t := hd l;
+ if(t.dt > 0 || !notify(t))
+ pending = t :: pending;
+ }
+ }
+ }
+ # shut down: attempt to clear pending requests
+ for(; pending != nil; pending = tl pending)
+ notify(hd pending);
+}
+
+notify(t: ref Timer): int
+{
+ # copy to c to avoid race with Timer.stop
+ if((c := t.timeout) == nil)
+ return 1; # cancelled; consider it done
+ alt{
+ c <-= 1 => return 1;
+ * => return 0;
+ }
+}
diff --git a/appl/lib/titlebar.b b/appl/lib/titlebar.b
new file mode 100644
index 00000000..08c5b927
--- /dev/null
+++ b/appl/lib/titlebar.b
@@ -0,0 +1,111 @@
+implement Titlebar;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Point, Rect: import draw;
+include "tk.m";
+ tk: Tk;
+include "titlebar.m";
+
+title_cfg := array[] of {
+ "frame .Wm_t -bg #aaaaaa -borderwidth 1",
+ "label .Wm_t.title -anchor w -bg #aaaaaa -fg white",
+ "button .Wm_t.e -bitmap exit.bit -command {send wm_title exit} -takefocus 0",
+ "pack .Wm_t.e -side right",
+ "bind .Wm_t <Button-1> {send wm_title move %X %Y}",
+ "bind .Wm_t <Double-Button-1> {send wm_title lower .}",
+ "bind .Wm_t <Motion-Button-1> {}",
+ "bind .Wm_t <Motion> {}",
+ "bind .Wm_t.title <Button-1> {send wm_title move %X %Y}",
+ "bind .Wm_t.title <Double-Button-1> {send wm_title lower .}",
+ "bind .Wm_t.title <Motion-Button-1> {}",
+ "bind .Wm_t.title <Motion> {}",
+ "bind . <FocusIn> {.Wm_t configure -bg blue;"+
+ ".Wm_t.title configure -bg blue;update}",
+ "bind . <FocusOut> {.Wm_t configure -bg #aaaaaa;"+
+ ".Wm_t.title configure -bg #aaaaaa;update}",
+};
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+ tk = load Tk Tk->PATH;
+}
+
+new(top: ref Tk->Toplevel, buts: int): chan of string
+{
+ ctl := chan of string;
+ tk->namechan(top, ctl, "wm_title");
+
+ if(buts & Plain)
+ return ctl;
+
+ for(i := 0; i < len title_cfg; i++)
+ cmd(top, title_cfg[i]);
+
+ if(buts & OK)
+ cmd(top, "button .Wm_t.ok -bitmap ok.bit"+
+ " -command {send wm_title ok} -takefocus 0; pack .Wm_t.ok -side right");
+
+ if(buts & Hide)
+ cmd(top, "button .Wm_t.top -bitmap task.bit"+
+ " -command {send wm_title task} -takefocus 0; pack .Wm_t.top -side right");
+
+ if(buts & Resize)
+ cmd(top, "button .Wm_t.m -bitmap maxf.bit"+
+ " -command {send wm_title size} -takefocus 0; pack .Wm_t.m -side right");
+
+ if(buts & Help)
+ cmd(top, "button .Wm_t.h -bitmap help.bit"+
+ " -command {send wm_title help} -takefocus 0; pack .Wm_t.h -side right");
+
+ # pack the title last so it gets clipped first
+ cmd(top, "pack .Wm_t.title -side left");
+ cmd(top, "pack .Wm_t -fill x");
+
+ return ctl;
+}
+
+title(top: ref Tk->Toplevel): string
+{
+ if(tk->cmd(top, "winfo class .Wm_t.title")[0] != '!')
+ return cmd(top, ".Wm_t.title cget -text");
+ return nil;
+}
+
+settitle(top: ref Tk->Toplevel, t: string): string
+{
+ s := title(top);
+ tk->cmd(top, ".Wm_t.title configure -text '" + t);
+ return s;
+}
+
+sendctl(top: ref Tk->Toplevel, c: string)
+{
+ cmd(top, "send wm_title " + c);
+}
+
+minsize(top: ref Tk->Toplevel): Point
+{
+ buts := array[] of {"e", "ok", "top", "m", "h"};
+ r := tk->rect(top, ".", Tk->Border);
+ r.min.x = r.max.x;
+ r.max.y = r.min.y;
+ for(i := 0; i < len buts; i++){
+ br := tk->rect(top, ".Wm_t." + buts[i], Tk->Border);
+ if(br.dx() > 0)
+ r = r.combine(br);
+ }
+ r.max.x += tk->rect(top, ".Wm_t." + buts[0], Tk->Border).dx();
+ return r.size();
+}
+
+cmd(top: ref Tk->Toplevel, s: string): string
+{
+ e := tk->cmd(top, s);
+ if (e != nil && e[0] == '!')
+ sys->fprint(sys->fildes(2), "wmclient: tk error %s on '%s'\n", e, s);
+ return e;
+}
diff --git a/appl/lib/tkclient.b b/appl/lib/tkclient.b
new file mode 100644
index 00000000..c2eebe07
--- /dev/null
+++ b/appl/lib/tkclient.b
@@ -0,0 +1,249 @@
+implement Tkclient;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited
+#
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Display, Image, Screen, Rect, Point, Pointer, Wmcontext, Context: import draw;
+include "tk.m";
+ tk: Tk;
+ Toplevel: import tk;
+include "wmlib.m";
+ wmlib: Wmlib;
+ qword, splitqword, s2r: import wmlib;
+include "titlebar.m";
+ titlebar: Titlebar;
+include "tkclient.m";
+
+Background: con int 16r777777FF; # should be drawn over immediately, but just in case...
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+ tk = load Tk Tk->PATH;
+ wmlib = load Wmlib Wmlib->PATH;
+ if(wmlib == nil){
+ sys->fprint(sys->fildes(2), "tkclient: cannot load %s: %r\n", Wmlib->PATH);
+ raise "fail:bad module";
+ }
+ wmlib->init();
+ titlebar = load Titlebar Titlebar->PATH;
+ if(titlebar == nil){
+ sys->fprint(sys->fildes(2), "tkclient: cannot load %s: %r\n", Titlebar->PATH);
+ raise "fail:bad module";
+ }
+ titlebar->init();
+}
+
+makedrawcontext(): ref Draw->Context
+{
+ return wmlib->makedrawcontext();
+}
+
+toplevel(ctxt: ref Draw->Context, topconfig: string, title: string, buts: int): (ref Tk->Toplevel, chan of string)
+{
+ wm := wmlib->connect(ctxt);
+ opts := "";
+ if((buts & Plain) == 0)
+ opts = "-borderwidth 1 -relief raised ";
+ top := tk->toplevel(wm.ctxt.display, opts+topconfig);
+ if (top == nil) {
+ sys->fprint(sys->fildes(2), "wmlib: window creation failed (top %ux, i %ux)\n", top, top.image);
+ raise "fail:window creation failed";
+ }
+ top.ctxt = wm;
+ readscreenrect(top);
+ c := titlebar->new(top, buts);
+ titlebar->settitle(top, title);
+ return (top, c);
+}
+
+readscreenrect(top: ref Tk->Toplevel)
+{
+ if((fd := sys->open("/chan/wmrect", Sys->OREAD)) != nil){
+ buf := array[12*4] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n > 0)
+ (top.screenr, nil) = s2r(string buf[0:n], 0);
+ }
+}
+
+onscreen(top: ref Tk->Toplevel, how: string)
+{
+ if(how == nil)
+ how = "place";
+ wmctl(top, sys->sprint("!reshape . -1 %s %q",
+ r2s(tk->rect(top, ".", Tk->Border|Tk->Required)), how));
+}
+
+startinput(top: ref Tk->Toplevel, devs: list of string)
+{
+ for(; devs != nil; devs = tl devs)
+ wmctl(top, sys->sprint("start %q", hd devs));
+}
+
+r2s(r: Rect): string
+{
+ return sys->sprint("%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y);
+}
+
+# commands originating both from tkclient and wm (via ctl)
+wmctl(top: ref Tk->Toplevel, req: string): string
+{
+#sys->print("wmctl %s\n", req);
+ (c, next) := qword(req, 0);
+ case c {
+ "exit" =>
+ sys->fprint(sys->open("/prog/" + string sys->pctl(0, nil) + "/ctl", Sys->OWRITE), "killgrp");
+ exit;
+ # old-style requests: pass them back around in proper form.
+ "move" =>
+ # move x y
+ titlebar->sendctl(top, "!move . -1 " + req[next:]);
+ "size" =>
+ minsz := titlebar->minsize(top);
+ titlebar->sendctl(top, "!size . -1 " + string minsz.x + " " + string minsz.y);
+ "ok" or
+ "help" =>
+ ;
+ "rect" =>
+ r: Rect;
+ (c, next) = qword(req, next);
+ r.min.x = int c;
+ (c, next) = qword(req, next);
+ r.min.y = int c;
+ (c, next) = qword(req, next);
+ r.max.x = int c;
+ (c, next) = qword(req, next);
+ r.max.y = int c;
+ top.screenr = r;
+ "haskbdfocus" =>
+ in := int qword(req, next).t0 != 0;
+ cmd(top, "focus -global " + string in);
+ cmd(top, "update");
+ "task" =>
+ (r, nil) := splitqword(req, next);
+ if(r.t0 == r.t1)
+ req = sys->sprint("task %q", cmd(top, ".Wm_t.title cget -text"));
+ if(wmreq(top, c, req, next) == nil)
+ cmd(top, ". unmap; update");
+ "untask" =>
+ cmd(top, ". map; update");
+ return wmreq(top, c, req, next);
+ * =>
+ return wmreq(top, c, req, next);
+ }
+ return nil;
+}
+
+wmreq(top: ref Tk->Toplevel, c, req: string, e: int): string
+{
+ err := wmreq1(top, c, req, e);
+# if(err != nil)
+# sys->fprint(sys->fildes(2), "tkclient: request %#q failed: %s\n", req, err);
+ return err;
+}
+
+wmreq1(top: ref Tk->Toplevel, c, req: string, e: int): string
+{
+ name, reqid: string;
+ if(req != nil && req[0] == '!'){
+ (name, e) = qword(req, e);
+ (reqid, e) = qword(req, e);
+ if(name == nil || reqid == nil)
+ return "bad arg count";
+ }
+ if(top.ctxt.connfd != nil){
+ if(sys->fprint(top.ctxt.connfd, "%s", req) == -1)
+ return sys->sprint("%r");
+ if(req[0] == '!')
+ recvimage(top, name, reqid);
+ return nil;
+ }
+ if(req[0] != '!'){
+ (nil, nil, err) := wmlib->wmctl(top.ctxt, req);
+ return err;
+ }
+ # if there's no window manager, then we create a screen on the
+ # display image. there's nowhere to find the screen again except
+ # through the toplevel's image. that means that you can't create a
+ # menu without mapping a toplevel, and if you manage to unmap
+ # the toplevel without unmapping the menu, you'll have two
+ # screens on the same display image
+ # in the image, so
+ if(c != "!reshape")
+ return "unknown request";
+ i: ref Image;
+ if(top.image == nil){
+ if(name != ".")
+ return "screen not available";
+ di := top.display.image;
+ screen := Screen.allocate(di, top.display.color(Background), 0);
+ di.draw(di.r, screen.fill, nil, screen.fill.r.min);
+ i = screen.newwindow(di.r, Draw->Refbackup, Draw->Nofill);
+ }else{
+ if(name == ".")
+ i = top.image;
+ else
+ i = top.image.screen.newwindow(s2r(req, e).t0, Draw->Refbackup, Draw->Red);
+ }
+ tk->putimage(top, name+" "+reqid, i, nil);
+ return nil;
+}
+
+recvimage(top: ref Tk->Toplevel, name, reqid: string)
+{
+ i := <-top.ctxt.images;
+ if(i == nil){
+ cmd(top, name + " suspend");
+ i = <-top.ctxt.images;
+ }
+ tk->putimage(top, name+" "+reqid, i, nil);
+}
+
+settitle(top: ref Tk->Toplevel, name: string): string
+{
+ return titlebar->settitle(top, name);
+}
+
+handler(top: ref Tk->Toplevel, stop: chan of int)
+{
+ ctxt := top.ctxt;
+ if(stop == nil)
+ stop = chan of int;
+ for(;;)alt{
+ c := <-ctxt.kbd =>
+ tk->keyboard(top, c);
+ p := <-ctxt.ptr =>
+ tk->pointer(top, *p);
+ c := <-ctxt.ctl or
+ c = <-top.wreq =>
+ wmctl(top, c);
+ <-stop =>
+ exit;
+ }
+}
+
+snarfget(): string
+{
+ return wmlib->snarfget();
+}
+
+snarfput(buf: string)
+{
+ return wmlib->snarfput(buf);
+}
+
+cmd(top: ref Tk->Toplevel, s: string): string
+{
+ e := tk->cmd(top, s);
+ if (e != nil && e[0] == '!')
+ sys->fprint(sys->fildes(2), "tkclient: tk error %s on '%s'\n", e, s);
+ return e;
+}
+
diff --git a/appl/lib/translate.b b/appl/lib/translate.b
new file mode 100644
index 00000000..e21a79cf
--- /dev/null
+++ b/appl/lib/translate.b
@@ -0,0 +1,248 @@
+implement Translate;
+
+#
+# prototype string translation for natural language substitutions
+#
+# Copyright © 2000 Vita Nuova Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "bufio.m";
+
+include "translate.m";
+
+NTEXT: con 131; # prime
+NNOTE: con 37;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+opendict(file: string): (ref Dict, string)
+{
+ d := Dict.new();
+ return (d, d.add(file));
+}
+
+
+opendicts(files: list of string): (ref Dict, string)
+{
+ d := Dict.new();
+ err: string;
+ for(; files != nil; files = tl files){
+ e := d.add(hd files);
+ if(e != nil){
+ if(err != nil)
+ err += "; ";
+ err += (hd files)+":"+e;
+ }
+ }
+ return (d, err);
+}
+
+Dict.new(): ref Dict
+{
+ d := ref Dict;
+ d.texts = array[NTEXT] of list of ref Phrase;
+ d.notes = array[NNOTE] of list of ref Phrase;
+ return d;
+}
+
+Dict.xlate(d: self ref Dict, text: string): string
+{
+ return d.xlaten(text, nil);
+}
+
+Dict.xlaten(d: self ref Dict, text: string, note: string): string
+{
+ nnote := 0;
+ if(note != nil){
+ pnote := look(d.notes, note);
+ if(pnote != nil)
+ nnote = pnote.n + 1;
+ }
+ (h, code) := hash(text, len d.texts);
+ for(l := d.texts[h]; l != nil; l = tl l){
+ p := hd l;
+ if(p.hash == code && p.key == text && p.note == nnote)
+ return p.text;
+ }
+ return text;
+}
+
+mkdictname(locale, app: string): string
+{
+ if(locale == nil || locale == "default")
+ return "/locale/dict/"+app; # looks better
+ return "/locale/"+locale+"/dict/"+app;
+}
+
+#
+# eventually could load a compiled version of the tables
+# (allows some consistency checking, etc)
+#
+Dict.add(d: self ref Dict, file: string): string
+{
+ bufio := load Bufio Bufio->PATH;
+ if(bufio == nil)
+ return "can't load Bufio";
+ fd := bufio->open(file, Sys->OREAD);
+ if(fd == nil)
+ return sys->sprint("%r");
+ ntext := 0;
+ nnote := 0;
+ errs: string;
+ for(lineno := 1; (line := bufio->fd.gets('\n')) != nil; lineno++){
+ if(line[0] == '#' || line[0] == '\n')
+ continue;
+ (key, note, text, err) := parseline(line);
+ if(err != nil){
+ if(errs != nil)
+ errs += ",";
+ errs += string lineno+":"+err;
+ }
+ pkey := look(d.texts, key);
+ if(pkey != nil)
+ key = pkey.key; # share key strings (useful with notes)
+ pkey = insert(d.texts, key);
+ if(note != nil){
+ pnote := look(d.notes, note);
+ if(pnote == nil){
+ pnote = insert(d.notes, note);
+ pnote.n = nnote++;
+ }
+ pkey.note = pnote.n+1;
+ }
+ pkey.text = text;
+ pkey.n = ntext++;
+ }
+ return errs;
+}
+
+parseline(line: string): (string, string, string, string)
+{
+ note, text: string;
+
+ (key, i) := quoted(line, 0);
+ if(i < 0)
+ return (nil, nil, nil, "bad key field");
+ i = skipwhite(line, i);
+ if(i < len line && line[i] == '('){
+ (note, i) = delimited(line, i+1, ')');
+ if(note == nil)
+ return (nil, nil, nil, "bad note syntax");
+ }
+ i = skipwhite(line, i);
+ if(i >= len line)
+ return (key, note, key, nil); # identity
+ if(line[i] != '=')
+ return (nil, nil, nil, "missing/misplaced '='");
+ (text, i) = quoted(line, i+1);
+ if(i < 0)
+ return (nil, nil, nil, "missing translation");
+ return (key, note, text, nil);
+}
+
+quoted(s: string, i: int): (string, int)
+{
+ i = skipwhite(s, i);
+ if(i >= len s || (qc := s[i]) != '"' && qc != '\'')
+ return (nil, -1);
+ return delimited(s, i+1, qc);
+}
+
+delimited(s: string, i: int, qc: int): (string, int)
+{
+ o := "";
+ b := i;
+ for(; i < len s; i++){
+ c := s[i];
+ if(c == qc)
+ return (o, i+1);
+ if(c == '\\' && i+1 < len s){
+ i++;
+ c = s[i];
+ case c {
+ 'n' => c = '\n';
+ 'r' => c = '\r';
+ 't' => c = '\t';
+ 'b' => c = '\b';
+ 'a' => c = '\a';
+ 'v' => c = '\v';
+ 'u' =>
+ (c, i) = hex2c(s, i + 1);
+ i--;
+ '0' => c = '\0';
+ * => ;
+ }
+ }
+ o[len o] = c;
+ }
+ return (nil, -1);
+}
+
+hex2c(s: string, i: int): (int, int)
+{
+ x := 0;
+ for (j := i; j < i + 4; j++) {
+ if (j >= len s)
+ return (Sys->UTFerror, j);
+ c := s[j];
+ if (c >= '0' && c <= '9')
+ c = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ c = c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ c = c - 'A' + 10;
+ else
+ return (Sys->UTFerror, j);
+ x = (x * 16) + c;
+ }
+ return (x, j);
+}
+
+skipwhite(s: string, i: int): int
+{
+ for(; i<len s && (s[i] == ' ' || s[i] == '\t' || s[i] == '\n'); i++)
+ ;
+ return i;
+}
+
+look(tab: array of list of ref Phrase, key: string): ref Phrase
+{
+ (h, code) := hash(key, len tab);
+ for(l := tab[h]; l != nil; l = tl l){
+ p := hd l;
+ if(p.hash == code && p.key == key)
+ return p;
+ }
+ return nil;
+}
+
+insert(tab: array of list of ref Phrase, key: string): ref Phrase
+{
+ (h, code) := hash(key, len tab);
+ p := ref Phrase;
+ p.n = 0;
+ p.note = 0;
+ p.key = key;
+ p.hash = code;
+ #sys->print("%s = %ux [%d]\n", key, code, h);
+ tab[h] = p :: tab[h];
+ return p;
+}
+
+# hashpjw from aho & ullman
+hash(s: string, n: int): (int, int)
+{
+ h := 0;
+ for(i:=0; i<len s; i++){
+ h = (h<<4) + s[i];
+ if((g := h & int 16rF0000000) != 0)
+ h ^= ((g>>24) & 16rFF) | g;
+ }
+ return ((h&~(1<<31))%n, h);
+}
diff --git a/appl/lib/ubfa.b b/appl/lib/ubfa.b
new file mode 100644
index 00000000..bfbf21ca
--- /dev/null
+++ b/appl/lib/ubfa.b
@@ -0,0 +1,623 @@
+implement UBFa;
+
+#
+# UBF(A) data encoding interpreter
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "ubfa.m";
+
+Syntax: exception(string);
+Badwrite: exception;
+
+dict: array of list of string;
+dictlock: chan of int;
+
+init(m: Bufio)
+{
+ sys = load Sys Sys->PATH;
+ bufio = m;
+
+ dict = array[74] of list of string;
+ dictlock = chan[1] of int;
+}
+
+uvatom(s: string): ref UValue.Atom
+{
+ return ref UValue.Atom(uniq(s));
+}
+
+uvint(i: int): ref UValue.Int
+{
+ return ref UValue.Int(i);
+}
+
+uvbig(i: big): ref UValue.Int
+{
+ return ref UValue.Int(int i);
+}
+
+uvbinary(a: array of byte): ref UValue.Binary
+{
+ return ref UValue.Binary(a);
+}
+
+uvstring(s: string): ref UValue.String
+{
+ return ref UValue.String(s);
+}
+
+uvtuple(a: array of ref UValue): ref UValue.Tuple
+{
+ return ref UValue.Tuple(a);
+}
+
+uvlist(l: list of ref UValue): ref UValue.List
+{
+ return ref UValue.List(l);
+}
+
+uvtag(s: string, o: ref UValue): ref UValue.Tag
+{
+ return ref UValue.Tag(uniq(s), o);
+}
+
+# needed only to avoid O(n) len s.s
+Stack: adt {
+ s: list of ref UValue;
+ n: int;
+
+ new: fn(): ref Stack;
+ pop: fn(s: self ref Stack): ref UValue raises(Syntax);
+ push: fn(s: self ref Stack, o: ref UValue);
+};
+
+Stack.new(): ref Stack
+{
+ return ref Stack(nil, 0);
+}
+
+Stack.pop(s: self ref Stack): ref UValue raises(Syntax)
+{
+ if(--s.n < 0 || s.s == nil)
+ raise Syntax("parse stack underflow");
+ v := hd s.s;
+ s.s = tl s.s;
+ return v;
+}
+
+Stack.push(s: self ref Stack, o: ref UValue)
+{
+ s.s = o :: s.s;
+ s.n++;
+}
+
+Parse: adt {
+ input: ref Iobuf;
+ stack: ref Stack;
+ reg: array of ref UValue;
+
+ getb: fn(nil: self ref Parse): int raises(Syntax);
+ unget: fn(nil: self ref Parse);
+};
+
+Parse.getb(p: self ref Parse): int raises(Syntax)
+{
+ c := p.input.getb();
+ if(c < 0){
+ if(c == Bufio->EOF)
+ raise Syntax("unexpected end-of-file");
+ raise Syntax(sys->sprint("read error: %r"));
+ }
+ return c;
+}
+
+Parse.unget(p: self ref Parse)
+{
+ p.input.ungetb();
+}
+
+uniq(s: string): string
+{
+ if(s == nil)
+ return "";
+ dictlock <-= 1;
+ h := 0;
+ for(i:=0; i<len s; i++){
+ h = (h<<4) + s[i];
+ if((g := h & int 16rF0000000) != 0)
+ h ^= ((g>>24) & 16rFF) | g;
+ }
+ h = (h & Sys->Maxint)%len dict;
+ for(l := dict[h]; l != nil; l = tl l)
+ if(hd l == s){
+ s = hd l; # share space
+ break;
+ }
+ if(l == nil)
+ dict[h] = s :: dict[h];
+ <-dictlock;
+ return s;
+}
+
+writeubf(out: ref Iobuf, obj: ref UValue): int
+{
+ {
+ # write it out, put final '$'
+ if(out != nil)
+ writeobj(out, obj);
+ putc(out, '$');
+ return 0;
+ }exception{
+ Badwrite =>
+ return -1;
+ }
+}
+
+readubf(input: ref Iobuf): (ref UValue, string)
+{
+ {
+ return (getobj(ref Parse(input, Stack.new(), array[256] of ref UValue)), nil);
+ }exception e{
+ Syntax =>
+ return (nil, sys->sprint("ubf error: offset %bd: %s", input.offset(), e));
+ }
+}
+
+UValue.isatom(o: self ref UValue): int
+{
+ return tagof o == tagof UValue.Atom;
+}
+
+UValue.isstring(o: self ref UValue): int
+{
+ return tagof o == tagof UValue.String;
+}
+
+UValue.isint(o: self ref UValue): int
+{
+ return tagof o == tagof UValue.Int;
+}
+
+UValue.islist(o: self ref UValue): int
+{
+ return tagof o == tagof UValue.List;
+}
+
+UValue.istuple(o: self ref UValue): int
+{
+ return tagof o == tagof UValue.Tuple;
+}
+
+UValue.isbinary(o: self ref UValue): int
+{
+ return tagof o == tagof UValue.Binary;
+}
+
+UValue.istag(o: self ref UValue): int
+{
+ return tagof o == tagof UValue.Tag;
+}
+
+UValue.isop(o: self ref UValue, op: string, arity: int): int
+{
+ pick r := o {
+ Tuple =>
+ if(len r.a > 0 && (arity <= 0 || len r.a == arity))
+ pick s := r.a[0] {
+ Atom =>
+ return s.name == op;
+ String =>
+ return s.s == op;
+ }
+ }
+ return 0;
+}
+
+UValue.op(o: self ref UValue, arity: int): string
+{
+ pick r := o {
+ Tuple =>
+ if(len r.a > 0 && (arity <= 0 || len r.a == arity))
+ pick s := r.a[0] {
+ Atom =>
+ return s.name;
+ String =>
+ return s.s;
+ }
+ }
+ return nil;
+}
+
+UValue.args(o: self ref UValue, arity: int): array of ref UValue
+{
+ pick r := o {
+ Tuple =>
+ if(len r.a > 0 && (arity <= 0 || len r.a == arity))
+ return r.a[1:];
+ }
+ return nil;
+}
+
+UValue.els(o: self ref UValue): list of ref UValue
+{
+ pick r := o {
+ List =>
+ return r.l;
+ }
+ return nil;
+}
+
+UValue.val(o: self ref UValue): int
+{
+ pick r := o {
+ Int =>
+ return r.value;
+ }
+ return 0;
+}
+
+UValue.objtag(o: self ref UValue): string
+{
+ pick r := o {
+ Tag =>
+ return r.name;
+ }
+ return nil;
+}
+
+UValue.obj(o: self ref UValue): ref UValue
+{
+ pick r := o {
+ Tag =>
+ return r.o;
+ }
+ return o;
+}
+
+UValue.binary(o: self ref UValue): array of byte
+{
+ pick r := o {
+ Atom =>
+ return array of byte r.name;
+ String =>
+ return array of byte r.s;
+ Binary =>
+ return r.a;
+ }
+ return nil;
+}
+
+UValue.text(o: self ref UValue): string
+{
+ pick r := o {
+ Atom =>
+ return r.name;
+ String =>
+ return r.s;
+ Int =>
+ return string r.value;
+ Tuple =>
+ s := "{";
+ for(i := 0; i < len r.a; i++)
+ s += " "+r.a[i].text();
+ return s+"}";
+ List =>
+ s := "[";
+ for(l := r.l; l != nil; l = tl l)
+ s += " "+(hd l).text();
+ return s+"]";
+ Binary =>
+ s := "<<";
+ for(i := 0; i < len r.a; i++)
+ s += sys->sprint(" %.2ux", int r.a[i]);
+ return s+">>";
+ Tag =>
+ return "{'$TYPE', "+r.name+", "+r.o.text()+"}";
+ * =>
+ return "unknown";
+ }
+}
+
+UValue.eq(o: self ref UValue, v: ref UValue): int
+{
+ if(v == nil)
+ return 0;
+ if(o == v)
+ return 1;
+ pick r := o {
+ Atom =>
+ pick s := v {
+ Atom =>
+ return r.name == s.name;
+ }
+ return 0;
+ String =>
+ pick s := v {
+ String =>
+ return r.s == s.s;
+ }
+ return 0;
+ Int =>
+ pick s := v {
+ Int =>
+ return r.value == s.value;
+ }
+ Tuple =>
+ pick s := v {
+ Tuple =>
+ if(len r.a != len s.a)
+ return 0;
+ for(i := 0; i < len r.a; i++)
+ if(!r.a[i].eq(s.a[i]))
+ return 0;
+ return 1;
+ }
+ return 0;
+ List =>
+ pick s := v {
+ List =>
+ l1 := r.l;
+ l2 := s.l;
+ while(l1 != nil && l2 != nil){
+ if(!(hd l1).eq(hd l2))
+ return 0;
+ l1 = tl l1;
+ l2 = tl l2;
+ }
+ return l1 == l2;
+ }
+ return 0;
+ Binary =>
+ pick s := v {
+ Binary =>
+ if(len r.a != len s.a)
+ return 0;
+ for(i := 0; i < len r.a; i++)
+ if(r.a[i] != s.a[i])
+ return 0;
+ return 1;
+ }
+ return 0;
+ Tag =>
+ pick s := v {
+ Tag =>
+ return r.name == s.name && r.o.eq(s.o);
+ }
+ return 0;
+ * =>
+ raise "ubf: bad object"; # can't happen
+ }
+}
+
+S: con byte 1;
+
+special := array[256] of {
+ '\n' or '\r' or '\t' or ' ' or ',' => S,
+ '}' => S, '$' => S, '>' => S, '#' => S, '&' => S,
+ '"' => S, '\'' => S, '{' => S, '~' => S, '-' => S,
+ '0' to '9' => S, '%' => S, '`' => S, * => byte 0
+};
+
+getobj(p: ref Parse): ref UValue raises(Syntax)
+{
+ {
+ for(;;){
+ case p.getb() {
+ '\n' or '\r' or '\t' or ' ' or ',' =>
+ ; # white space
+ '%' =>
+ while((c := p.getb()) != '%'){
+ if(c == '\\'){ # do comments really use \?
+ c = p.getb();
+ if(c != '\\' && c != '%')
+ raise Syntax("invalid escape in comment");
+ }
+ }
+ '}' =>
+ a := array[p.stack.n] of ref UValue;
+ for(i := len a; --i >= 0;)
+ a[i] = p.stack.pop();
+ return ref UValue.Tuple(a);
+ '$' =>
+ if(p.stack.n != 1)
+ raise Syntax("unbalanced stack: size "+string p.stack.n);
+ return p.stack.pop();
+ '>' =>
+ r := p.getb();
+ if(special[r] == S)
+ raise Syntax("invalid register name");
+ p.reg[r] = p.stack.pop();
+ '`' =>
+ t := uniq(readdelimitedstring(p, '`'));
+ p.stack.push(ref UValue.Tag(t, p.stack.pop()));
+ * =>
+ p.unget();
+ p.stack.push(readobj(p));
+ }
+ }
+ }exception{
+ Syntax =>
+ raise;
+ }
+}
+
+readobj(p: ref Parse): ref UValue raises(Syntax)
+{
+ {
+ case c := p.getb() {
+ '#' =>
+ return ref UValue.List(nil);
+ '&' =>
+ a := p.stack.pop();
+ b := p.stack.pop();
+ pick r := b {
+ List =>
+ return ref UValue.List(a :: r.l); # not changed in place: might be shared register value
+ * =>
+ raise Syntax("can't make cons with cdr "+b.text());
+ }
+ '"' =>
+ return ref UValue.String(readdelimitedstring(p, c));
+ '\'' =>
+ return ref UValue.Atom(uniq(readdelimitedstring(p, c)));
+ '{' =>
+ obj := getobj(ref Parse(p.input, Stack.new(), p.reg));
+ if(!obj.istuple())
+ raise Syntax("expected tuple: obj");
+ return obj;
+ '~' =>
+ o := p.stack.pop();
+ if(!o.isint())
+ raise Syntax("expected Int before ~");
+ n := o.val();
+ if(n < 0)
+ raise Syntax("negative length for binary");
+ a := array[n] of byte;
+ n = p.input.read(a, len a);
+ if(n != len a){
+ if(n != Bufio->ERROR)
+ sys->werrstr("short read");
+ raise Syntax(sys->sprint("cannot read binary data: %r"));
+ }
+ if(p.getb() != '~')
+ raise Syntax("missing closing ~");
+ return ref UValue.Binary(a);
+ '-' or '0' to '9' =>
+ p.unget();
+ return ref UValue.Int(int readinteger(p));
+ * =>
+ if(p.reg[c] != nil)
+ return p.reg[c];
+ p.unget(); # point to error
+ raise Syntax(sys->sprint("invalid start character/undefined register #%.2ux",c));
+ }
+ }exception{
+ Syntax =>
+ raise;
+ }
+}
+
+readdelimitedstring(p: ref Parse, delim: int): string raises(Syntax)
+{
+ {
+ s := "";
+ while((c := p.input.getc()) != delim){ # note: we'll use UTF-8
+ if(c < 0){
+ if(c == Bufio->ERROR)
+ raise Syntax(sys->sprint("read error: %r"));
+ raise Syntax("unexpected end of file");
+ }
+ if(c == '\\'){
+ c = p.getb();
+ if(c != '\\' && c != delim)
+ raise Syntax("invalid escape");
+ }
+ s[len s] = c;
+ }
+ return s;
+ }exception{
+ Syntax =>
+ raise;
+ }
+}
+
+readinteger(p: ref Parse): big raises(Syntax)
+{
+ sign := 1;
+ c := p.getb();
+ if(c == '-'){
+ sign = -1;
+ c = p.getb();
+ if(!(c >= '0' && c <= '9'))
+ raise Syntax("expected integer literal");
+ }
+ for(n := big 0; c >= '0' && c <= '9'; c = p.getb()){
+ n = n*big 10 + big((c-'0')*sign);
+ if(n > big Sys->Maxint || n < big(-Sys->Maxint-1))
+ raise Syntax("integer overflow");
+ }
+ p.unget();
+ return n;
+}
+
+writeobj(out: ref Iobuf, o: ref UValue) raises(Badwrite)
+{
+ {
+ pick r := o {
+ Atom =>
+ writedelimitedstring(out, r.name, '\'');
+ String =>
+ writedelimitedstring(out, r.s, '"');
+ Int =>
+ puts(out, string r.value);
+ Tuple => # { el * }
+ putc(out, '{');
+ for(i := 0; i < len r.a; i++){
+ if(i != 0)
+ putc(out, ' ');
+ writeobj(out, r.a[i]);
+ }
+ putc(out, '}');
+ List => # # eN & eN-1 & ... & e0 &
+ putc(out, '#');
+ # put them out in reverse order, each followed by '&'
+ rl: list of ref UValue;
+ for(l := r.l; l != nil; l = tl l)
+ rl = hd l :: rl;
+ for(; rl != nil; rl = tl rl){
+ writeobj(out, hd rl);
+ putc(out, '&');
+ }
+ Binary => # Int ~data~
+ puts(out, string len r.a);
+ putc(out, '~');
+ if(out.write(r.a, len r.a) != len r.a)
+ raise Badwrite;
+ putc(out, '~');
+ Tag => # obj `tag`
+ writeobj(out, r.o);
+ writedelimitedstring(out, r.name, '`');
+ * =>
+ raise "ubf: unknown object"; # can't happen
+ }
+ }exception{
+ Badwrite =>
+ raise;
+ }
+}
+
+writedelimitedstring(out: ref Iobuf, s: string, d: int) raises(Badwrite)
+{
+ {
+ putc(out, d);
+ for(i := 0; i < len s; i++){
+ c := s[i];
+ if(c == d || c == '\\')
+ putc(out, '\\');
+ putc(out, c);
+ }
+ putc(out, d);
+ }exception{
+ Badwrite =>
+ raise;
+ }
+}
+
+puts(out: ref Iobuf, s: string) raises(Badwrite)
+{
+ if(out.puts(s) == Bufio->ERROR)
+ raise Badwrite;
+}
+
+putc(out: ref Iobuf, c: int) raises(Badwrite)
+{
+ if(out.putc(c) == Bufio->ERROR)
+ raise Badwrite;
+}
diff --git a/appl/lib/url.b b/appl/lib/url.b
new file mode 100644
index 00000000..415018c2
--- /dev/null
+++ b/appl/lib/url.b
@@ -0,0 +1,224 @@
+implement Url;
+
+include "sys.m";
+ sys: Sys;
+
+include "string.m";
+ S: String;
+
+include "url.m";
+
+schemes = array[] of {
+ NOSCHEME => "",
+ HTTP => "http",
+ HTTPS => "https",
+ FTP => "ftp",
+ FILE => "file",
+ GOPHER => "gopher",
+ MAILTO => "mailto",
+ NEWS => "news",
+ NNTP => "nntp",
+ TELNET => "telnet",
+ WAIS => "wais",
+ PROSPERO => "prospero",
+ JAVASCRIPT => "javascript",
+ UNKNOWN => "unknown"
+};
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ S = load String String->PATH;
+}
+
+# To allow relative urls, only fill in specified pieces (don't apply defaults)
+# general syntax: <scheme>:<scheme-specific>
+# for IP schemes, <scheme-specific> is
+# //<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
+makeurl(surl: string): ref ParsedUrl
+{
+ scheme := NOSCHEME;
+ user := "";
+ passwd := "";
+ host := "";
+ port := "";
+ pstart := "";
+ path := "";
+ query := "";
+ frag := "";
+
+ (sch, url) := split(surl, ":");
+ if(url == "") {
+ url = sch;
+ sch = "";
+ }
+ else {
+ (nil, x) := S->splitl(sch, "^-a-zA-Z0-9.+");
+ if(x != nil) {
+ url = surl;
+ sch = "";
+ }
+ else {
+ scheme = UNKNOWN;
+ sch = S->tolower(sch);
+ for(i := 0; i < len schemes; i++)
+ if(schemes[i] == sch) {
+ scheme = i;
+ break;
+ }
+ }
+ }
+ if(scheme == MAILTO)
+ path = url;
+ else if (scheme == JAVASCRIPT)
+ path = url;
+ else {
+ if(S->prefix("//", url)) {
+ netloc: string;
+ (netloc, path) = S->splitl(url[2:], "/");
+ if(path != "")
+ path = path[1:];
+ pstart = "/";
+ if(scheme == FILE)
+ host = netloc;
+ else {
+ (up,hp) := split(netloc, "@");
+ if(hp == "")
+ hp = up;
+ else
+ (user, passwd) = split(up, ":");
+ (host, port) = split(hp, ":");
+ }
+ }
+ else {
+ if(S->prefix("/", url)) {
+ pstart = "/";
+ path = url[1:];
+ }
+ else
+ path = url;
+ }
+ if(scheme == FILE) {
+ if(host == "")
+ host = "localhost";
+ }
+ else {
+ (path, frag) = split(path, "#");
+ (path, query) = split(path, "?");
+ }
+ }
+
+ return ref ParsedUrl(scheme, 1, user, passwd, host, port, pstart, path, query, frag);
+}
+
+ParsedUrl.tostring(u: self ref ParsedUrl) : string
+{
+ if (u == nil)
+ return nil;
+
+ ans := "";
+ if(u.scheme > 0 && u.scheme < len schemes)
+ ans = schemes[u.scheme] + ":";
+ if(u.host != "") {
+ ans = ans + "//";
+ if(u.user != "") {
+ ans = ans + u.user;
+ if(u.passwd != "")
+ ans = ans + ":" + u.passwd;
+ ans = ans + "@";
+ }
+ ans = ans + u.host;
+ if(u.port != "")
+ ans = ans + ":" + u.port;
+ }
+ ans = ans + u.pstart + u.path;
+ if(u.query != "")
+ ans = ans + "?" + u.query;
+ if(u.frag != "")
+ ans = ans + "#" + u.frag;
+ return ans;
+}
+
+ParsedUrl.makeabsolute(u: self ref ParsedUrl, b: ref ParsedUrl)
+{
+# The following is correct according to RFC 1808, but is violated
+# by various extant web pages.
+
+ if(u.scheme != NOSCHEME && u.scheme != HTTP)
+ return;
+
+ if(u.host == "" && u.path == "" && u.pstart == "" && u.query == "" && u.frag == "") {
+ u.scheme = b.scheme;
+ u.user = b.user;
+ u.passwd = b.passwd;
+ u.host = b.host;
+ u.port = b.port;
+ u.path = b.path;
+ u.pstart = b.pstart;
+ u.query = b.query;
+ u.frag = b.frag;
+ return;
+ }
+ if(u.scheme == NOSCHEME)
+ u.scheme = b.scheme;
+ if(u.host != "")
+ return;
+ u.user = b.user;
+ u.passwd = b.passwd;
+ u.host = b.host;
+ u.port = b.port;
+ if(u.pstart == "/")
+ return;
+ u.pstart = "/";
+ if(u.path == "") {
+ u.path = b.path;
+ if(u.query == "")
+ u.query = b.query;
+ }
+ else {
+ (p1,nil) := S->splitr(b.path, "/");
+ u.path = canonize(p1 + u.path);
+ }
+}
+
+# Like splitl, but assume one char match, and omit that from second part.
+# If c doesn't appear in s, the return is (s, "").
+split(s, c: string) : (string, string)
+{
+ (a,b) := S->splitl(s, c);
+ if(b != "")
+ b = b[1:];
+ return (a,b);
+}
+
+# remove ./ and ../ from s
+canonize(s: string): string
+{
+ (base, file) := S->splitr(s, "/");
+ (n,path) := sys->tokenize(base, "/");
+ revpath : list of string = nil;
+ for(p := path; p != nil; p = tl p) {
+ if(hd p == "..") {
+ if(revpath != nil)
+ revpath = tl revpath;
+ }
+ else if(hd p != ".")
+ revpath = (hd p) :: revpath;
+ }
+ while(revpath != nil && hd revpath == "..")
+ revpath = tl revpath;
+ ans := "";
+ if(revpath != nil) {
+ ans = hd revpath;
+ revpath = tl revpath;
+ while(revpath != nil) {
+ ans = (hd revpath) + "/" + ans;
+ revpath = tl revpath;
+ }
+ }
+ if (ans != nil)
+ ans += "/";
+ ans += file;
+ return ans;
+}
+
diff --git a/appl/lib/usb/mkfile b/appl/lib/usb/mkfile
new file mode 100644
index 00000000..3fe78cca
--- /dev/null
+++ b/appl/lib/usb/mkfile
@@ -0,0 +1,16 @@
+<../../../mkconfig
+
+TARG=\
+ usb.dis\
+ usbmouse.dis\
+ usbmct.dis\
+ usbmass.dis
+
+MODULES=
+
+SYSMODULES= \
+ usb.m\
+
+DISBIN=$ROOT/dis/lib/usb
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/lib/usb/usb.b b/appl/lib/usb/usb.b
new file mode 100644
index 00000000..8c8fa139
--- /dev/null
+++ b/appl/lib/usb/usb.b
@@ -0,0 +1,453 @@
+#
+# Copyright © 2002 Vita Nuova Holdings Limited
+#
+implement Usb;
+
+include "sys.m";
+ sys: Sys;
+
+include "usb.m";
+
+include "string.m";
+ str: String;
+
+Proto: adt {
+ proto: int;
+ name: string;
+};
+
+SubClass: adt {
+ subclass: int;
+ name: string;
+ proto: array of Proto;
+};
+
+Class: adt {
+ class: int;
+ name: string;
+ subclass: array of SubClass;
+};
+
+classes := array [] of {
+ Class(Usb->CL_AUDIO, "audio",
+ array [] of {
+ SubClass(1, "control", nil),
+ SubClass(2, "stream", nil),
+ SubClass(3, "midi", nil),
+ }
+ ),
+ Class(Usb->CL_COMMS, "comms",
+ array [] of {
+ SubClass(1, "abstract",
+ array [] of {
+ Proto(1, "AT"),
+ }
+ )
+ }
+ ),
+ Class(Usb->CL_HID, "hid",
+ array [] of {
+ SubClass(1, "boot",
+ array [] of {
+ Proto(1, "kbd"),
+ Proto(2, "mouse"),
+ }
+ )
+ }
+ ),
+ Class(Usb->CL_PRINTER, "printer",
+ array [] of {
+ SubClass(1, "printer",
+ array [] of {
+ Proto(1, "uni"),
+ Proto(2, "bi"),
+ }
+ )
+ }
+ ),
+ Class(Usb->CL_HUB, "hub",
+ array [] of {
+ SubClass(1, "hub", nil),
+ }
+ ),
+ Class(Usb->CL_DATA, "data", nil),
+ Class(Usb->CL_MASS, "mass",
+ array [] of {
+ SubClass(1, "rbc",
+ array [] of {
+ Proto(0, "cbi-cc"),
+ Proto(1, "cbi-nocc"),
+ Proto(16r50, "bulkonly"),
+ }
+ ),
+ SubClass(2, "sff-8020i/mmc-2",
+ array [] of {
+ Proto(0, "cbi-cc"),
+ Proto(1, "cbi-nocc"),
+ Proto(16r50, "bulkonly"),
+ }
+ ),
+ SubClass(3, "qic-157",
+ array [] of {
+ Proto(0, "cbi-cc"),
+ Proto(1, "cbi-nocc"),
+ Proto(16r50, "bulkonly"),
+ }
+ ),
+ SubClass(4, "ufi",
+ array [] of {
+ Proto(0, "cbi-cc"),
+ Proto(1, "cbi-nocc"),
+ Proto(16r50, "bulkonly"),
+ }
+ ),
+ SubClass(5, "sff-8070i",
+ array [] of {
+ Proto(0, "cbi-cc"),
+ Proto(1, "cbi-nocc"),
+ Proto(16r50, "bulkonly"),
+ }
+ ),
+ SubClass(6, "scsi",
+ array [] of {
+ Proto(0, "cbi-cc"),
+ Proto(1, "cbi-nocc"),
+ Proto(16r50, "bulkonly"),
+ }
+ ),
+ }
+ ),
+};
+
+get2(b: array of byte): int
+{
+ return int b[0] | (int b[1] << 8);
+}
+
+put2(buf: array of byte, v: int)
+{
+ buf[0] = byte v;
+ buf[1] = byte (v >> 8);
+}
+
+get4(b: array of byte): int
+{
+ return int b[0] | (int b[1] << 8) | (int b[2] << 16) | (int b[3] << 24);
+}
+
+put4(buf: array of byte, v: int)
+{
+ buf[0] = byte v;
+ buf[1] = byte (v >> 8);
+ buf[2] = byte (v >> 16);
+ buf[3] = byte (v >> 24);
+}
+
+bigget2(b: array of byte): int
+{
+ return int b[1] | (int b[0] << 8);
+}
+
+bigput2(buf: array of byte, v: int)
+{
+ buf[1] = byte v;
+ buf[0] = byte (v >> 8);
+}
+
+bigget4(b: array of byte): int
+{
+ return int b[3] | (int b[2] << 8) | (int b[1] << 16) | (int b[0] << 24);
+}
+
+bigput4(buf: array of byte, v: int)
+{
+ buf[3] = byte v;
+ buf[2] = byte (v >> 8);
+ buf[1] = byte (v >> 16);
+ buf[0] = byte (v >> 24);
+}
+
+strtol(s: string, base: int): (int, string)
+{
+ if (str == nil)
+ str = load String String->PATH;
+ if (base != 0)
+ return str->toint(s, base);
+ if (len s >= 2 && (s[0:2] == "0X" || s[0:2] == "0x"))
+ return str->toint(s[2:], 16);
+ if (len s > 0 && s[0:1] == "0")
+ return str->toint(s[1:], 8);
+ return str->toint(s, 10);
+}
+
+memset(buf: array of byte, v: int)
+{
+ for (x := 0; x < len buf; x++)
+ buf[x] = byte v;
+}
+
+setupreq(setupfd: ref Sys->FD, typ, req, value, index: int, outbuf: array of byte, count: int): int
+{
+ additional: int;
+ if (outbuf != nil) {
+ additional = len outbuf;
+ # if there is an outbuf, then the count sent must be length of the payload
+ # this assumes that RH2D is set
+ count = additional;
+ }
+ else
+ additional = 0;
+ buf := array[8 + additional] of byte;
+ buf[0] = byte typ;
+ buf[1] = byte req;
+ put2(buf[2:], value);
+ put2(buf[4:], index);
+ put2(buf[6:], count);
+ if (additional)
+ buf[8:] = outbuf;
+ rv := sys->write(setupfd, buf, len buf);
+ if (rv < 0)
+ return -1;
+ if (rv != len buf)
+ return -1;
+ return rv;
+}
+
+setupreply(setupfd: ref Sys->FD, buf: array of byte): int
+{
+ nb := sys->read(setupfd, buf, len buf);
+ return nb;
+}
+
+setup(setupfd: ref Sys->FD, typ, req, value, index: int, outbuf: array of byte, inbuf: array of byte): int
+{
+ count: int;
+ if (inbuf != nil)
+ count = len inbuf;
+ else
+ count = 0;
+ if (setupreq(setupfd, typ, req, value, index, outbuf, count) < 0)
+ return -1;
+ if (count == 0)
+ return 0;
+ return setupreply(setupfd, inbuf);
+}
+
+get_descriptor(fd: ref Sys->FD, rtyp: int, dtyp: int, dindex: int, langid: int, buf: array of byte): int
+{
+ nr := -1;
+ if (setupreq(fd, RD2H | rtyp | Rdevice, GET_DESCRIPTOR, (dtyp << 8) | dindex, langid, nil, len buf) < 0
+ || (nr = setupreply(fd, buf)) < 1)
+ return -1;
+ return nr;
+}
+
+get_standard_descriptor(fd: ref Sys->FD, dtyp: int, index: int, buf: array of byte): int
+{
+ return get_descriptor(fd, Rstandard, dtyp, index, 0, buf);
+}
+
+get_class_descriptor(fd: ref Sys->FD, dtyp: int, index: int, buf: array of byte): int
+{
+ return get_descriptor(fd, Rclass, dtyp, index, 0, buf);
+}
+
+get_vendor_descriptor(fd: ref Sys->FD, dtyp: int, index: int, buf: array of byte): int
+{
+ return get_descriptor(fd, Rvendor, dtyp, index, 0, buf);
+}
+
+get_status(fd: ref Sys->FD, port: int): int
+{
+ buf := array [4] of byte;
+ if (setupreq(fd, RD2H | Rclass | Rother, GET_STATUS, 0, port, nil, len buf) < 0
+ || setupreply(fd, buf) < len buf)
+ return -1;
+ return get2(buf);
+}
+
+set_address(fd: ref Sys->FD, address: int): int
+{
+ return setupreq(fd, RH2D | Rstandard | Rdevice, SET_ADDRESS, address, 0, nil, 0);
+}
+
+set_configuration(fd: ref Sys->FD, n: int): int
+{
+ return setupreq(fd, RH2D | Rstandard | Rdevice, SET_CONFIGURATION, n, 0, nil, 0);
+}
+
+setclear_feature(fd: ref Sys->FD, rtyp: int, value: int, index: int, on: int): int
+{
+ req: int;
+ if (on)
+ req = SET_FEATURE;
+ else
+ req = CLEAR_FEATURE;
+ return setupreq(fd, RH2D | rtyp, req, value, index, nil, 0);
+}
+
+parse_conf(b: array of byte): ref Configuration
+{
+ if (len b < DCONFLEN)
+ return nil;
+ conf := ref Configuration;
+ conf.id = int b[5];
+ conf.iface = array[int b[4]] of Interface;
+ conf.attr = int b[7];
+ conf.powerma = int b[8] * 2;
+ return conf;
+}
+
+parse_iface(conf: ref Configuration, b: array of byte): ref AltInterface
+{
+ if (len b < DINTERLEN || conf == nil)
+ return nil;
+ id := int b[2];
+ if (id >= len conf.iface)
+ return nil;
+ ai := ref AltInterface;
+ ai.id = int b[3];
+ if (int b[4] != 0)
+ ai.ep = array [int b[4]] of ref Endpt;
+ ai.class = int b[5];
+ ai.subclass = int b[6];
+ ai.proto = int b[7];
+ conf.iface[id].altiface = ai :: conf.iface[id].altiface;
+ return ai;
+}
+
+parse_endpt(conf: ref Configuration, ai: ref AltInterface, b: array of byte): ref Endpt
+{
+ if (len b < DENDPLEN || conf == nil || ai == nil || ai.ep == nil)
+ return nil;
+ for (i := 0; i < len ai.ep; i++)
+ if (ai.ep[i] == nil)
+ break;
+ if (i >= len ai.ep)
+ return nil;
+ ep := ref Endpt;
+ ai.ep[i] = ep;
+ ep.addr = int b[2];
+ ep.attr = int b[3];
+ ep.d2h = ep.addr & 16r80;
+ ep.etype = int b[3] & 3;
+ ep.isotype = (int b[3] >> 2) & 3;
+ ep.maxpkt = get2(b[4:]);
+ ep.interval = int b[6];
+ return ep;
+}
+
+get_parsed_configuration_descriptor(fd: ref Sys->FD, n: int): ref Configuration
+{
+ conf: ref Configuration;
+ altiface: ref AltInterface;
+
+ b := array [256] of byte;
+ nr := get_standard_descriptor(fd, CONFIGURATION, n, b);
+ if (nr < 0)
+ return nil;
+ conf = nil;
+ altiface = nil;
+ for (i := 0; nr - i > 2 && b[i] > byte 0 && int b[i] <= nr - i; i += int b[i]) {
+ ni := i + int b[i];
+ case int b[i + 1] {
+ Usb->CONFIGURATION =>
+ conf = parse_conf(b[i: ni]);
+ if (conf == nil)
+ return nil;
+ Usb->INTERFACE =>
+ altiface = parse_iface(conf, b[i: ni]);
+ if (altiface == nil)
+ return nil;
+ Usb->ENDPOINT =>
+ if (parse_endpt(conf, altiface, b[i: ni]) == nil)
+ return nil;
+ }
+ }
+ if (i < nr)
+ sys->print("usb: residue at end of descriptors\n");
+ return conf;
+}
+
+get_parsed_device_descriptor(fd: ref Sys->FD): ref Device
+{
+ b := array [256] of byte;
+ nr := get_standard_descriptor(fd, DEVICE, 0, b);
+ if (nr < DDEVLEN) {
+ if (nr == 8 || nr == 16) {
+ memset(b[nr: DDEVLEN - 1], 0);
+ b[DDEVLEN - 1] = byte 1;
+ nr = DDEVLEN;
+ }
+ else
+ return nil;
+ }
+ dev := ref Device;
+ dev.usbmajor = int b[3];
+ dev.usbminor = int b[2];
+ dev.class = int b[4];
+ dev.subclass = int b[5];
+ dev.proto = int b[6];
+ dev.maxpkt0 = int b[7];
+ dev.vid = get2(b[8:]);
+ dev.did = get2(b[10:]);
+ dev.relmajor = int b[13];
+ dev.relminor = int b[12];
+ dev.nconf = int b[17];
+ return dev;
+}
+
+dump_configuration(fd: ref Sys->FD, conf: ref Configuration)
+{
+ sys->fprint(fd, "configuration %d attr 0x%.x powerma %d\n", conf.id, conf.attr, conf.powerma);
+ for (i := 0; i < len conf.iface; i++) {
+ sys->fprint(fd, "\tinterface %d\n", i);
+ ail := conf.iface[i].altiface;
+ while (ail != nil) {
+ ai := hd ail;
+ sys->fprint(fd, "\t\t%d class %d subclass %d proto %d [%s]\n",
+ ai.id, ai.class, ai.subclass, ai.proto,
+ sclass(ai.class, ai.subclass, ai.proto));
+ for (e := 0; e < len ai.ep; e++) {
+ if (ai.ep[e] == nil) {
+ sys->fprint(fd, "\t\t\t missing descriptor\n");
+ continue;
+ }
+ sys->fprint(fd, "\t\t\t0x%.2ux attr 0x%.x maxpkt %d interval %d\n",
+ ai.ep[e].addr, ai.ep[e].attr, ai.ep[e].maxpkt, ai.ep[e].interval);
+ }
+ ail = tl ail;
+ }
+ }
+sys->fprint(fd, "done dumping\n");
+}
+
+sclass(class, subclass, proto: int): string
+{
+ for (c := 0; c < len classes; c++)
+ if (classes[c].class == class)
+ break;
+ if (c >= len classes)
+ return sys->sprint("%d.%d.%d", class, subclass, proto);
+ if (classes[c].subclass == nil)
+ return sys->sprint("%s.%d.%d", classes[c].name, subclass, proto);
+ for (sc := 0; sc < len classes[c].subclass; sc++)
+ if (classes[c].subclass[sc].subclass == subclass)
+ break;
+ if (sc >= len classes[c].subclass)
+ return sys->sprint("%s.%d.%d", classes[c].name, subclass, proto);
+ if (classes[c].subclass[sc].proto == nil)
+ return sys->sprint("%s.%s.%d", classes[c].name, classes[c].subclass[sc].name, proto);
+ for (p := 0; p < len classes[c].subclass[sc].proto; p++)
+ if (classes[c].subclass[sc].proto[p].proto == proto)
+ break;
+ if (p >= len classes[c].subclass[sc].proto)
+ return sys->sprint("%s.%s.%d", classes[c].name, classes[c].subclass[sc].name, proto);
+ return sys->sprint("%s.%s.%s", classes[c].name, classes[c].subclass[sc].name,
+ classes[c].subclass[sc].proto[p].name);
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
diff --git a/appl/lib/usb/usbmass.b b/appl/lib/usb/usbmass.b
new file mode 100644
index 00000000..f5140cc3
--- /dev/null
+++ b/appl/lib/usb/usbmass.b
@@ -0,0 +1,465 @@
+#
+# Copyright © 2001 Vita Nuova Holdings Limited.
+#
+implement UsbDriver;
+
+include "sys.m";
+ sys: Sys;
+include "usb.m";
+ usb: Usb;
+ Endpt, RD2H, RH2D: import Usb;
+
+ENDPOINT_STALL: con 0; # TO DO: should be in usb.m
+
+readerpid: int;
+setupfd, ctlfd: ref Sys->FD;
+infd, outfd: ref Sys->FD;
+inep, outep: ref Endpt;
+cbwseq := 0;
+capacity: big;
+debug := 0;
+
+lun: int;
+blocksize: int;
+
+kill(pid: int)
+{
+ fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);
+ if (fd != nil)
+ sys->fprint(fd, "kill");
+}
+
+reader(pidc: chan of int, fileio: ref Sys->FileIO)
+{
+ pid := sys->pctl(0, nil);
+ pidc <-= pid;
+ for(;;) alt{
+ (offset, count, nil, rc) := <-fileio.read =>
+ if (rc != nil) {
+ if (offset%blocksize || count%blocksize) {
+ rc <- = (nil, "unaligned read");
+ continue;
+ }
+ offset /= blocksize;
+ count /= blocksize;
+ buf := array [count * blocksize] of byte;
+ if (scsiread10(lun, offset, count, buf) < 0) {
+ scsirequestsense(lun);
+ rc <- = (nil, "read error");
+ continue;
+ }
+ rc <- = (buf, nil);
+ }
+ (offset, data, nil, wc) := <-fileio.write =>
+ if(wc != nil){
+ count := len data;
+ if(offset%blocksize || count%blocksize){
+ wc <-= (0, "unaligned write");
+ continue;
+ }
+ offset /= blocksize;
+ count /= blocksize;
+ if(scsiwrite10(lun, offset, count, data) < 0){
+ scsirequestsense(lun);
+ wc <-= (0, "write error");
+ continue;
+ }
+ wc <-= (len data, nil);
+ }
+ }
+ readerpid = -1;
+}
+
+massstoragereset(): int
+{
+ if (usb->setup(setupfd, Usb->RH2D | Usb->Rclass | Usb->Rinterface, 255, 0, 0, nil, nil) < 0) {
+ sys->print("usbmass: storagereset failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+getmaxlun(): int
+{
+ buf := array[1] of byte;
+ if (usb->setup(setupfd, Usb->RD2H | Usb->Rclass | Usb->Rinterface, 254, 0, 0, nil, buf) < 0) {
+ sys->print("usbmass: getmaxlun failed\n");
+ return -1;
+ }
+ return int buf[0];
+}
+
+#
+# CBW:
+# sig[4]="USBC" tag[4] datalen[4] flags[1] lun[1] len[1] cmd[len]
+#
+sendcbw(dtl: int, outdir: int, lun: int, cmd: array of byte): int
+{
+ cbw := array [31] of byte;
+ cbw[0] = byte 'U';
+ cbw[1] = byte 'S';
+ cbw[2] = byte 'B';
+ cbw[3] = byte 'C';
+ usb->put4(cbw[4:], ++cbwseq);
+ usb->put4(cbw[8:], dtl);
+ if (outdir)
+ cbw[12] = byte RH2D;
+ else
+ cbw[12] = byte RD2H;
+ cbw[13] = byte lun;
+ cbw[14] = byte len cmd;
+ cbw[15:] = cmd;
+ rv := sys->write(outfd, cbw, len cbw);
+ if (rv < 0) {
+ sys->print("sendcbw: failed: %r\n");
+ return -1;
+ }
+ if (rv != len cbw) {
+ sys->print("sendcbw: truncated send\n");
+ return -1;
+ }
+ return 0;
+}
+
+#
+# CSW:
+# sig[4]="USBS" tag[4] residue[4] status[1]
+#
+
+recvcsw(tag: int): (int, int)
+{
+ if(debug)
+ sys->print("recvcsw\n");
+ buf := array [13] of byte;
+ if (sys->read(infd, buf, len buf) != len buf) {
+ sys->print("recvcsw: read failed: %r\n");
+ return (-1, -1);
+ }
+ if (usb->get4(buf) != (('S'<<24)|('B'<<16)|('S'<<8)|'U')) {
+ sys->print("recvcsw: signature wrong\n");
+ return (-1, -1);
+ }
+ recvtag := usb->get4(buf[4:]);
+ if (recvtag != tag) {
+ sys->print("recvcsw: tag does not match: sent %d recved %d\n", tag, recvtag);
+ return (-1, -1);
+ }
+ residue := usb->get4(buf[8:]);
+ status := int buf[12];
+ if(debug)
+ sys->print("recvcsw: residue %d status %d\n", residue, status);
+ return (residue, status);
+}
+
+unstall(ep: ref Endpt)
+{
+ if(debug)
+ sys->print("unstalling bulk %x\n", ep.addr);
+ x := ep.addr & 16rF;
+ sys->fprint(ctlfd, "unstall %d", x);
+ sys->fprint(ctlfd, "data %d 0", x);
+ if (usb->setclear_feature(setupfd, Usb->Rendpt, ENDPOINT_STALL, ep.addr, 0) < 0) {
+ sys->print("unstall: clear_feature() failed: %r\n");
+ return;
+ }
+}
+
+warnfprint(fd: ref Sys->FD, s: string)
+{
+ if (sys->fprint(fd, "%s", s) != len s)
+ sys->print("warning: writing %s failed: %r\n", s);
+}
+
+bulkread(lun: int, cmd: array of byte, buf: array of byte, dump: int): int
+{
+ if (sendcbw(len buf, 0, lun, cmd) < 0)
+ return -1;
+ got := 0;
+ if (buf != nil) {
+ while (got < len buf) {
+ rv := sys->read(infd, buf[got:], len buf - got);
+ if (rv < 0) {
+ sys->print("bulkread: read failed: %r\n");
+ break;
+ }
+ if(debug)
+ sys->print("read %d\n", rv);
+ got += rv;
+ break;
+ }
+ if (dump) {
+ for (i := 0; i < got; i++)
+ sys->print("%.2ux", int buf[i]);
+ sys->print("\n");
+ }
+ if (got == 0)
+ unstall(inep);
+ }
+ (residue, status) := recvcsw(cbwseq);
+ if (residue < 0) {
+ unstall(inep);
+ (residue, status) = recvcsw(cbwseq);
+ if (residue < 0)
+ return -1;
+ }
+ if (status != 0)
+ return -1;
+ return got;
+}
+
+bulkwrite(lun: int, cmd: array of byte, buf: array of byte): int
+{
+ if (sendcbw(len buf, 1, lun, cmd) < 0)
+ return -1;
+ got := 0;
+ if (buf != nil) {
+ while (got < len buf) {
+ rv := sys->write(outfd, buf[got:], len buf - got);
+ if (rv < 0) {
+ sys->print("bulkwrite: write failed: %r\n");
+ break;
+ }
+ if(debug)
+ sys->print("write %d\n", rv);
+ got += rv;
+ break;
+ }
+ if (got == 0)
+ unstall(outep);
+ }
+ (residue, status) := recvcsw(cbwseq);
+ if (residue < 0) {
+ unstall(inep);
+ (residue, status) = recvcsw(cbwseq);
+ if (residue < 0)
+ return -1;
+ }
+ if (status != 0)
+ return -1;
+ return got;
+}
+
+scsiinquiry(lun: int): int
+{
+ buf := array [36] of byte; # don't use 255, many devices can't cope
+ cmd := array [6] of byte;
+ cmd[0] = byte 16r12;
+ cmd[1] = byte (lun << 5);
+ cmd[2] = byte 0;
+ cmd[3] = byte 0;
+ cmd[4] = byte len buf;
+ cmd[5] = byte 0;
+ got := bulkread(lun, cmd, buf, 0);
+ if (got < 0)
+ return -1;
+ if (got < 36) {
+ sys->print("scsiinquiry: too little data\n");
+ return -1;
+ }
+ t := int buf[0] & 16r1f;
+ if(debug)
+ sys->print("scsiinquiry: type %d/%s\n", t, string buf[8:35]);
+ if (t != 0)
+ return -1;
+ return 0;
+}
+
+scsireadcapacity(lun: int): int
+{
+# warnfprint(ctlfd, "debug 0 1");
+# warnfprint(ctlfd, "debug 1 1");
+# warnfprint(ctlfd, "debug 2 1");
+ buf := array [8] of byte;
+ cmd := array [10] of byte;
+ cmd[0] = byte 16r25;
+ cmd[1] = byte (lun << 5);
+ cmd[2] = byte 0;
+ cmd[3] = byte 0;
+ cmd[4] = byte 0;
+ cmd[5] = byte 0;
+ cmd[6] = byte 0;
+ cmd[7] = byte 0;
+ cmd[8] = byte 0;
+ cmd[9] = byte 0;
+ got := bulkread(lun, cmd, buf, 0);
+ if (got < 0)
+ return -1;
+ if (got != len buf) {
+ sys->print("scsireadcapacity: returned data not right size\n");
+ return -1;
+ }
+ blocksize = usb->bigget4(buf[4:]);
+ lba := big usb->bigget4(buf[0:]) & 16rFFFFFFFF;
+ capacity = big blocksize * (lba+big 1);
+ if(debug)
+ sys->print("block size %d lba %bd cap %bd\n", blocksize, lba, capacity);
+ return 0;
+}
+
+scsirequestsense(lun: int): int
+{
+# warnfprint(ctlfd, "debug 0 1");
+# warnfprint(ctlfd, "debug 1 1");
+# warnfprint(ctlfd, "debug 2 1");
+ buf := array [18] of byte;
+ cmd := array [6] of byte;
+ cmd[0] = byte 16r03;
+ cmd[1] = byte (lun << 5);
+ cmd[2] = byte 0;
+ cmd[3] = byte 0;
+ cmd[4] = byte len buf;
+ cmd[5] = byte 0;
+ got := bulkread(lun, cmd, buf, 1);
+ if (got < 0)
+ return -1;
+ return 0;
+}
+
+scsiread10(lun: int, offset, count: int, buf: array of byte): int
+{
+ cmd := array [10] of byte;
+ cmd[0] = byte 16r28;
+ cmd[1] = byte (lun << 5);
+ usb->bigput4(cmd[2:], offset);
+ cmd[6] = byte 0;
+ usb->bigput2(cmd[7:], count);
+ cmd[9] = byte 0;
+ got := bulkread(lun, cmd, buf, 0);
+ if (got < 0)
+ return -1;
+ return 0;
+}
+
+scsiwrite10(lun: int, offset, count: int, buf: array of byte): int
+{
+ cmd := array [10] of byte;
+ cmd[0] = byte 16r2A;
+ cmd[1] = byte (lun << 5);
+ usb->bigput4(cmd[2:], offset);
+ cmd[6] = byte 0;
+ usb->bigput2(cmd[7:], count);
+ cmd[9] = byte 0;
+ got := bulkwrite(lun, cmd, buf);
+ if (got < 0)
+ return -1;
+ return 0;
+}
+
+scsistartunit(lun: int, start: int): int
+{
+# warnfprint(ctlfd, "debug 0 1");
+# warnfprint(ctlfd, "debug 1 1");
+# warnfprint(ctlfd, "debug 2 1");
+ cmd := array [6] of byte;
+ cmd[0] = byte 16r1b;
+ cmd[1] = byte (lun << 5);
+ cmd[2] = byte 0;
+ cmd[3] = byte 0;
+ cmd[4] = byte (start & 1);
+ cmd[5] = byte 0;
+ got := bulkread(lun, cmd, nil, 0);
+ if (got < 0)
+ return -1;
+ return 0;
+}
+
+init(usbmod: Usb, psetupfd, pctlfd: ref Sys->FD,
+ nil: ref Usb->Device,
+ conf: array of ref Usb->Configuration, path: string): int
+{
+ usb = usbmod;
+ setupfd = psetupfd;
+ ctlfd = pctlfd;
+
+ sys = load Sys Sys->PATH;
+ rv := usb->set_configuration(setupfd, conf[0].id);
+ if (rv < 0)
+ return rv;
+ rv = massstoragereset();
+ if (rv < 0)
+ return rv;
+ maxlun := getmaxlun();
+ if (maxlun < 0)
+ return maxlun;
+ lun = 0;
+ if(debug)
+ sys->print("maxlun %d\n", maxlun);
+ inep = outep = nil;
+ epts := (hd conf[0].iface[0].altiface).ep;
+ for(i := 0; i < len epts; i++)
+ if(epts[i].etype == Usb->Ebulk){
+ if(epts[i].d2h){
+ if(inep == nil)
+ inep = epts[i];
+ }else{
+ if(outep == nil)
+ outep = epts[i];
+ }
+ }
+ if(inep == nil || outep == nil){
+ sys->print("can't find endpoints\n");
+ return -1;
+ }
+ isrw := (inep.addr & 16rF) == (outep.addr & 16rF);
+ if(!isrw){
+ infd = openep(path, inep, Sys->OREAD);
+ if(infd == nil)
+ return -1;
+ outfd = openep(path, outep, Sys->OWRITE);
+ if(outfd == nil)
+ return -1;
+ }else{
+ infd = outfd = openep(path, inep, Sys->ORDWR);
+ if(infd == nil)
+ return -1;
+ }
+ if (scsiinquiry(0) < 0)
+ return -1;
+ scsistartunit(lun, 1);
+ if (scsireadcapacity(0) < 0) {
+ scsirequestsense(0);
+ if (scsireadcapacity(0) < 0)
+ return -1;
+ }
+ fileio := sys->file2chan("/chan", "usbdisk");
+ if (fileio == nil) {
+ sys->print("file2chan failed: %r\n");
+ return -1;
+ }
+ setlength("/chan/usbdisk", capacity);
+# warnfprint(ctlfd, "debug 0 1");
+# warnfprint(ctlfd, "debug 1 1");
+# warnfprint(ctlfd, "debug 2 1");
+ pidc := chan of int;
+ spawn reader(pidc, fileio);
+ readerpid = <- pidc;
+ return 0;
+}
+
+shutdown()
+{
+ if (readerpid >= 0)
+ kill(readerpid);
+}
+
+openep(path: string, ep: ref Endpt, mode: int): ref Sys->FD
+{
+ if(debug)
+ sys->print("ep %x maxpkt %d interval %d\n", ep.addr, ep.maxpkt, ep.interval);
+ ms: string;
+ case mode {
+ Sys->OREAD => ms = "r";
+ Sys->OWRITE => ms = "w";
+ * => ms = "rw";
+ }
+ if(sys->fprint(ctlfd, "ep %d bulk %s %d 16", ep.addr&16rF, ms, ep.maxpkt) < 0)
+ return nil;
+ return sys->open(sys->sprint("%s/ep%ddata", path, ep.addr&16rF), mode);
+}
+
+setlength(f: string, size: big)
+{
+ d := sys->nulldir;
+ d.length = size;
+ sys->wstat(f, d); # ignore errors since it fails on older kernels
+}
diff --git a/appl/lib/usb/usbmct.b b/appl/lib/usb/usbmct.b
new file mode 100644
index 00000000..7384cb23
--- /dev/null
+++ b/appl/lib/usb/usbmct.b
@@ -0,0 +1,204 @@
+#
+# Copyright © 2002 Vita Nuova Holdings Limited
+#
+implement UsbDriver;
+
+# MCT RS232 USB driver
+# 'Documentation' mined from NetBSD
+
+include "sys.m";
+ sys: Sys;
+include "usb.m";
+ usb: Usb;
+
+UMCT_SET_REQUEST: con 1640;
+
+REQ_SET_BAUD_RATE: con 5;
+REQ_SET_LCR: con 7;
+
+LCR_SET_BREAK: con 16r40;
+LCR_PARITY_EVEN: con 16r18;
+LCR_PARITY_ODD: con 16r08;
+LCR_PARITY_NONE: con 16r00;
+LCR_DATA_BITS_5, LCR_DATA_BITS_6, LCR_DATA_BITS_7, LCR_DATA_BITS_8: con iota;
+LCR_STOP_BITS_2: con 16r04;
+LCR_STOP_BITS_1: con 16r00;
+
+setupfd: ref Sys->FD;
+debug: con 1;
+
+ioreaderpid, statusreaderpid: int;
+
+kill(pid: int): int
+{
+ fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE);
+ if (fd == nil)
+ return -1;
+ if (sys->write(fd, array of byte "kill", 4) != 4)
+ return -1;
+ return 0;
+}
+
+ioreader(pidc: chan of int, fd: ref Sys->FD)
+{
+ pid := sys->pctl(0, nil);
+ pidc <-= pid;
+ buf := array [256] of byte;
+ while ((n := sys->read(fd, buf, len buf)) >= 0)
+ {
+ sys->print("[%d]\n", n);
+ sys->write(sys->fildes(1), buf, n);
+ }
+ ioreaderpid = -1;
+}
+
+statusreader(pidc: chan of int, fd: ref Sys->FD)
+{
+ pid := sys->pctl(0, nil);
+ pidc <-= pid;
+ buf := array [2] of byte;
+ while ((n := sys->read(fd, buf, len buf)) >= 0)
+ {
+ sys->print("S(%d)%.2ux%.2ux\n", n, int buf[0], int buf[1]);
+ }
+ statusreaderpid = -1;
+}
+
+set_baud_rate(baud: int)
+{
+ buf := array [1] of byte;
+ val := 12;
+ case baud {
+ 300 => val = 1;
+ 1200 => val = 3;
+ 2400 => val = 4;
+ 4800 => val = 6;
+ 9600 => val = 8;
+ 19200 => val = 9;
+ 38400 => val = 10;
+ 57600 => val = 11;
+ 115200 => val = 12;
+ }
+ buf[0] = byte val;
+ if (usb->setup(setupfd, UMCT_SET_REQUEST, REQ_SET_BAUD_RATE, 0, 0, buf, nil) < 0) {
+ if (debug)
+ sys->print("usbmct: set_baud_rate failed\n");
+ }
+}
+
+set_lcr(val: int)
+{
+ buf := array [1] of byte;
+ buf[0] = byte val;
+ if (usb->setup(setupfd, UMCT_SET_REQUEST, REQ_SET_LCR, 0, 0, buf, nil) < 0) {
+ if (debug)
+ sys->print("usbmct: set_lcr failed\n");
+ }
+}
+
+init(usbmod: Usb, psetupfd, pctlfd: ref Sys->FD,
+ dev: ref Usb->Device,
+ conf: array of ref Usb->Configuration, path: string): int
+{
+ statusep, inep, outep: ref Usb->Endpt;
+ usb = usbmod;
+ sys = load Sys Sys->PATH;
+ setupfd = psetupfd;
+ # check the device descriptor to see if it really is an MCT doofer
+ if (dev.vid != 16r0711 || dev.did != 16r0230) {
+ if (debug)
+ sys->print("usbmct: wrong device!\n");
+ return -1;
+ }
+ usb->set_configuration(setupfd, conf[0].id);
+ ai := hd conf[0].iface[0].altiface;
+ statusep = nil;
+ inep = nil;
+ outep = nil;
+ for (e := 0; e < len ai.ep; e++) {
+ ep := ai.ep[e];
+ if ((ep.addr & 16r80) != 0 && (ep.attr & 3) == 3 && ep.maxpkt == 2)
+ statusep = ep;
+ else if ((ep.addr & 16r80) != 0 && (ep.attr & 3) == 3)
+ inep = ep;
+ else if ((ep.addr & 16r80) == 0 && (ep.attr & 3) == 2)
+ outep = ep;
+ }
+ if (statusep == nil || outep == nil || inep == nil) {
+ if (debug)
+ sys->print("usbmct: can't find sensible endpoints\n");
+ return -1;
+ }
+ if ((inep.addr & 15) != (outep.addr & 15)) {
+ if (debug)
+ sys->print("usbmct: in and out endpoints not same number\n");
+ return -1;
+ }
+ ioid := inep.addr & 15;
+ statusid := statusep.addr & 15;
+ if (debug)
+ sys->print("ep %d %d r %d 32\n", ioid, inep.maxpkt, inep.interval);
+ if (sys->fprint(pctlfd, "ep %d %d r %d 32", ioid, inep.maxpkt, inep.interval) < 0) {
+ if (debug)
+ sys->print("usbmct: can't create i/o endpoint (i)\n");
+ return -1;
+ }
+# if (debug)
+# sys->print("ep %d %d r bulk 32\n", ioid, inep.maxpkt);
+# if (sys->fprint(pctlfd, "ep %d %d r bulk 32", ioid, inep.maxpkt) < 0) {
+# if (debug)
+# sys->print("usbmct: can't create i/o endpoint (i)\n");
+# return -1;
+# }
+ if (debug)
+ sys->print("ep %d %d w bulk 8\n", ioid, outep.maxpkt);
+ if (sys->fprint(pctlfd, "ep %d %d w bulk 8", ioid, outep.maxpkt) < 0) {
+ if (debug)
+ sys->print("usbmct: can't create i/o endpoint (o)\n");
+ return -1;
+ }
+ iofd := sys->open(path + "ep" + string ioid + "data", Sys->ORDWR);
+ if (iofd == nil) {
+ if (debug)
+ sys->print("usbmct: can't open i/o endpoint\n");
+ return -1;
+ }
+ if (debug)
+ sys->print("ep %d %d r %d 8\n", statusid, statusep.maxpkt, statusep.interval);
+ if (sys->fprint(pctlfd, "ep %d %d r %d 8", statusid, statusep.maxpkt, statusep.interval) < 0) {
+ if (debug)
+ sys->print("usbmct: can't create status endpoint\n");
+ return -1;
+ }
+ statusfd := sys->open(path + "ep" + string statusid + "data", Sys->ORDWR);
+ if (statusfd == nil) {
+ if (debug)
+ sys->print("usbmct: can't open status endpoint\n");
+ return -1;
+ }
+sys->print("setting baud rate\n");
+ set_baud_rate(9600);
+sys->print("setting lcr\n");
+ set_lcr(LCR_PARITY_NONE | LCR_DATA_BITS_8 | LCR_STOP_BITS_1);
+sys->print("launching reader\n");
+ pidc := chan of int;
+ spawn ioreader(pidc, iofd);
+ ioreaderpid = <- pidc;
+ spawn statusreader(pidc, statusfd);
+ statusreaderpid = <- pidc;
+ buf := array[512] of byte;
+ for (x := 0; x < 512; x += 16) {
+ buf[x:] = array of byte sys->sprint("%.2ux", x / 16);
+ buf[x + 2:] = array of byte "-0123456789-\r\n";
+ }
+ sys->write(iofd, buf, 512);
+ return 0;
+}
+
+shutdown()
+{
+ if (ioreaderpid >= 0)
+ kill(ioreaderpid);
+ if (statusreaderpid >= 0)
+ kill(statusreaderpid);
+}
diff --git a/appl/lib/usb/usbmouse.b b/appl/lib/usb/usbmouse.b
new file mode 100644
index 00000000..138f31c1
--- /dev/null
+++ b/appl/lib/usb/usbmouse.b
@@ -0,0 +1,60 @@
+#
+# Copyright © 2002 Vita Nuova Holdings Limited.
+#
+implement UsbDriver;
+
+include "sys.m";
+ sys: Sys;
+include "usb.m";
+ usb: Usb;
+
+readerpid: int;
+
+kill(pid: int): int
+{
+ fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE);
+ if (fd == nil)
+ return -1;
+ if (sys->write(fd, array of byte "kill", 4) != 4)
+ return -1;
+ return 0;
+}
+
+reader(pidc: chan of int, fd: ref Sys->FD)
+{
+ pid := sys->pctl(0, nil);
+ pidc <-= pid;
+ buf := array [4] of byte;
+ while ((n := sys->read(fd, buf, len buf)) >= 0)
+ sys->print("%d: %d\n", sys->millisec(), n);
+ readerpid = -1;
+}
+
+init(usbmod: Usb, setupfd, ctlfd: ref Sys->FD,
+ nil: ref Usb->Device,
+ conf: array of ref Usb->Configuration, path: string): int
+{
+ usb = usbmod;
+ sys = load Sys Sys->PATH;
+ rv := usb->set_configuration(setupfd, conf[0].id);
+ if (rv < 0)
+ return rv;
+ ep := (hd conf[0].iface[0].altiface).ep[0];
+ sys->print("maxpkt %d interval %d\n", ep.maxpkt, ep.interval);
+ rv = sys->fprint(ctlfd, "ep 1 %d r %d 32", ep.maxpkt, ep.interval);
+ if (rv < 0)
+ return rv;
+ datafd := sys->open(path + "ep1data", Sys->OREAD);
+ if (datafd == nil)
+ return -1;
+ pidc := chan of int;
+ spawn reader(pidc, datafd);
+ readerpid = <- pidc;
+ return 0;
+}
+
+shutdown()
+{
+ if (readerpid >= 0)
+ kill(readerpid);
+}
diff --git a/appl/lib/utils.m b/appl/lib/utils.m
new file mode 100644
index 00000000..4e9a176a
--- /dev/null
+++ b/appl/lib/utils.m
@@ -0,0 +1,112 @@
+Str_Hashtab : module
+{
+ PATH: con "/dis/lib/tcl_strhash.dis";
+
+ H_link : adt{
+ name : string;
+ val : string;
+ };
+
+ Hash : adt {
+ size : int;
+ lsize : int;
+ tab : array of list of H_link;
+ insert : fn(h:self ref Hash,name,val: string) : int;
+ dump: fn(h:self ref Hash) : string;
+ find: fn(h:self ref Hash,name : string) : (int,string);
+ delete: fn(h:self ref Hash,name : string) : int;
+ };
+
+ alloc : fn(size : int) : ref Hash;
+};
+
+Int_Hashtab : module
+{
+ PATH: con "/dis/lib/tcl_inthash.dis";
+
+ H_link : adt{
+ name : string;
+ val : int;
+ };
+
+ IHash : adt {
+ size : int;
+ tab : array of list of H_link;
+ insert : fn(h:self ref IHash,name: string,val : int) : int;
+ find: fn(h:self ref IHash,name : string) : (int,int);
+ delete: fn(h:self ref IHash,name : string) : int;
+ };
+
+ alloc : fn(size : int) : ref IHash;
+};
+
+Sym_Hashtab : module
+{
+ PATH: con "/dis/lib/tcl_symhash.dis";
+
+ H_link : adt{
+ name : string;
+ alias : string;
+ val : int;
+ };
+
+ SHash : adt {
+ size : int;
+ tab : array of list of H_link;
+ insert : fn(h:self ref SHash,name,alias: string,val : int) : int;
+ find: fn(h:self ref SHash,name : string) : (int,int,string);
+ delete: fn(h:self ref SHash,name : string) : int;
+ };
+
+ alloc : fn(size : int) : ref SHash;
+};
+
+Mod_Hashtab : module
+{
+ PATH: con "/dis/lib/tcl_modhash.dis";
+
+ H_link : adt{
+ name : string;
+ val : TclLib;
+ };
+
+ MHash : adt {
+ size : int;
+ tab : array of list of H_link;
+ insert : fn(h:self ref MHash,name: string,val : TclLib)
+ : int;
+ dump: fn(h:self ref MHash) : string;
+ find: fn(h:self ref MHash,name : string) : (int,TclLib);
+ delete: fn(h:self ref MHash,name : string) : int;
+ };
+
+ alloc : fn(size : int) : ref MHash;
+};
+
+Tcl_Stack : module
+{
+ PATH: con "/dis/lib/tcl_stack.dis";
+
+ level : fn() : int;
+ examine : fn(lev : int) :
+ (ref Str_Hashtab->Hash,array of (ref Str_Hashtab->Hash,string),ref Sym_Hashtab->SHash);
+ push : fn(s:ref Str_Hashtab->Hash,
+ a:array of (ref Str_Hashtab->Hash,string),t: ref Sym_Hashtab->SHash);
+ init : fn();
+ move : fn(lev :int) : int;
+ newframe : fn() :
+ (ref Str_Hashtab->Hash,array of (ref Str_Hashtab->Hash,string),ref Sym_Hashtab->SHash);
+ pop : fn() : (ref Str_Hashtab->Hash,
+ array of (ref Str_Hashtab->Hash,string),ref Sym_Hashtab->SHash);
+ dump : fn();
+};
+
+
+
+Tcl_Utils : module
+{
+ PATH: con "/dis/lib/tcl_utils.dis";
+ break_it : fn(s : string) : array of string;
+ arr_resize : fn(argv : array of string) : array of string;
+};
+
diff --git a/appl/lib/venti.b b/appl/lib/venti.b
new file mode 100644
index 00000000..ec9ec24e
--- /dev/null
+++ b/appl/lib/venti.b
@@ -0,0 +1,660 @@
+implement Venti;
+
+include "sys.m";
+ sys: Sys;
+include "venti.m";
+
+BIT8SZ: con 1;
+BIT16SZ: con 2;
+BIT32SZ: con 4;
+BIT48SZ: con 6;
+SCORE: con 20;
+STR: con BIT16SZ;
+H: con BIT16SZ+BIT8SZ+BIT8SZ; # minimum header length: size[2] op[1] tid[1]
+Rootnamelen: con 128;
+
+versions := array[] of {"02"};
+
+blankroot: Root;
+blankentry: Entry;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+hdrlen := array[Tmax] of {
+Rerror => H+STR, # size[2] Rerror tid[1] error[s]
+Tping => H, # size[2] Tping tid[1]
+Rping => H, # size[2] Rping tid[1]
+Thello => H+STR+STR+BIT8SZ+BIT8SZ+BIT8SZ, # size[2] Thello tid[1] version[s] uid[s] crypto[1] cryptos[n] codecs[n]
+Rhello => H+STR+BIT8SZ+BIT8SZ, # size[2] Rhello tid[1] sid[s] crypto[1] codec[1]
+Tgoodbye => H, # size[2] Tgoodbye tid[1]
+Tread => H+SCORE+BIT8SZ+BIT8SZ+BIT16SZ, # size[2] Tread tid[1] score[20] type[1] pad[1] n[2]
+Rread => H, # size[2] Rread tid[1] data
+Twrite => H+BIT8SZ+3, # size[2] Twrite tid[1] type[1] pad[3]
+Rwrite => H+SCORE, # size[2] Rwrite tid[1] score[20
+Tsync => H, # size[2] Tsync tid[1]
+Rsync => H, # size[2] Rsync tid[1]
+};
+
+tag2type := array[] of {
+tagof Vmsg.Rerror => Rerror,
+tagof Vmsg.Tping => Tping,
+tagof Vmsg.Rping => Rping,
+tagof Vmsg.Thello => Thello,
+tagof Vmsg.Rhello => Rhello,
+tagof Vmsg.Tgoodbye => Tgoodbye,
+tagof Vmsg.Tread => Tread,
+tagof Vmsg.Rread => Rread,
+tagof Vmsg.Twrite => Twrite,
+tagof Vmsg.Rwrite => Rwrite,
+tagof Vmsg.Tsync => Tsync,
+tagof Vmsg.Rsync => Rsync,
+};
+
+msgname := array[] of {
+tagof Vmsg.Rerror => "Rerror",
+tagof Vmsg.Tping => "Tping",
+tagof Vmsg.Rping => "Rping",
+tagof Vmsg.Thello => "Thello",
+tagof Vmsg.Rhello => "Rhello",
+tagof Vmsg.Tgoodbye => "Tgoodbye",
+tagof Vmsg.Tread => "Tread",
+tagof Vmsg.Rread => "Rread",
+tagof Vmsg.Twrite => "Twrite",
+tagof Vmsg.Rwrite => "Rwrite",
+tagof Vmsg.Tsync => "Tsync",
+tagof Vmsg.Rsync => "Rsync",
+};
+
+zero := array[] of {
+ byte 16rda, byte 16r39, byte 16ra3, byte 16ree, byte 16r5e,
+ byte 16r6b, byte 16r4b, byte 16r0d, byte 16r32, byte 16r55,
+ byte 16rbf, byte 16ref, byte 16r95, byte 16r60, byte 16r18,
+ byte 16r90, byte 16raf, byte 16rd8, byte 16r07, byte 16r09
+};
+
+
+Vmsg.read(fd: ref Sys->FD): (ref Vmsg, string)
+{
+ (msg, err) := readmsg(fd);
+ if(err != nil)
+ return (nil, err);
+ if(msg == nil)
+ return (nil, nil);
+ (nil, m) := Vmsg.unpack(msg);
+ if(m == nil)
+ return (nil, sys->sprint("bad venti message format: %r"));
+ return (m, nil);
+}
+
+Vmsg.unpack(f: array of byte): (int, ref Vmsg)
+{
+ if(len f < H)
+ return (0, nil);
+ size := (int f[0] << 8) | int f[1]; # size does not include self
+ size += BIT16SZ;
+ if(len f != size){
+ if(len f < size){
+ sys->werrstr("need more data");
+ return (0, nil); # need more data
+ }
+ f = f[0:size]; # trim to exact length
+ }
+ mtype := int f[2];
+ if(mtype >= len hdrlen || size < hdrlen[mtype]){
+ sys->werrstr("mtype out of range");
+ return (-1, nil);
+ }
+ tid := int f[3];
+ m: ref Vmsg;
+ case mtype {
+ Thello =>
+ (version, o) := gstring(f, H);
+ if(o < 0)
+ break;
+ uid: string;
+ (uid, o) = gstring(f, o);
+ if(o < 0)
+ break;
+ if(o+2 >= len f)
+ break;
+ cryptostrength := int f[o++];
+ ncryptos := int f[o++];
+ if(o+ncryptos >= len f)
+ break;
+ cryptos := f[o:o+ncryptos];
+ o += ncryptos;
+ if(o+1 >= len f)
+ break;
+ ncodecs := int f[o++];
+ if(o+ncodecs >= len f)
+ break;
+ codecs := f[o:o+ncodecs];
+ m = ref Vmsg.Thello(1, tid, version, uid, cryptostrength, cryptos, codecs);
+ Tping =>
+ m = ref Vmsg.Tping(1, tid);
+ Tgoodbye =>
+ m = ref Vmsg.Tgoodbye(1, tid);
+ Tread =>
+ score := Score(f[H:H+SCORE]);
+ etype := int f[H+SCORE];
+ n := (int f[H+SCORE+2] << 8) | int f[H+SCORE+3];
+ m = ref Vmsg.Tread(1, tid, score, etype, n);
+ Twrite =>
+ etype := int f[H];
+ m = ref Vmsg.Twrite(1, tid, etype, f[H+4:]);
+ Tsync =>
+ m = ref Vmsg.Tsync(1, tid);
+ Rhello =>
+ (sid, o) := gstring(f, H);
+ if(o+2 != len f){
+ sys->werrstr("Rhello message size incorrect");
+ break;
+ }
+ crypto := int f[o++];
+ codec := int f[o++];
+ m = ref Vmsg.Rhello(0, tid, sid, crypto, codec);
+ Rping =>
+ m = ref Vmsg.Rping(0, tid);
+ Rread =>
+ m = ref Vmsg.Rread(0, tid, f[H:]);
+ Rwrite =>
+ m = ref Vmsg.Rwrite(0, tid, Score(f[H:H+SCORE]));
+ Rsync =>
+ m = ref Vmsg.Rsync(0, tid);
+ Rerror =>
+ (err, o) := gstring(f, H);
+ if(o < 0)
+ break;
+ m = ref Vmsg.Rerror(0, tid, err);
+ * =>
+ sys->werrstr("unrecognised mtype " + string mtype);
+ }
+ if(m == nil)
+ return (-1, nil);
+ return (size, m);
+}
+
+Vmsg.pack(gm: self ref Vmsg): array of byte
+{
+ if(gm == nil)
+ return nil;
+ ds := gm.packedsize();
+ if(ds <= 0)
+ return nil;
+ d := array[ds] of byte;
+ d[0] = byte ((ds - 2) >> 8);
+ d[1] = byte (ds - 2);
+ d[2] = byte tag2type[tagof gm];
+ d[3] = byte gm.tid;
+ pick m := gm {
+ Thello =>
+ o := pstring(d, H, m.version);
+ o = pstring(d, o, m.uid);
+ d[o++] = byte m.cryptostrength;
+ d[o++] = byte len m.cryptos;
+ d[o:] = m.cryptos;
+ o += len m.cryptos;
+ d[o++] = byte len m.codecs;
+ d[o:] = m.codecs;
+ o += len m.codecs;
+ Tping =>
+ ;
+ Tgoodbye =>
+ ;
+ Tread =>
+ d[H:] = m.score.a;
+ d[H+SCORE] = byte m.etype;
+ d[H+SCORE+2] = byte (m.n >> 8);
+ d[H+SCORE+3] = byte m.n;
+ Twrite =>
+ d[H] = byte m.etype;
+ d[H+4:] = m.data;
+ Tsync =>
+ ;
+ Rhello =>
+ o := pstring(d, H, m.sid);
+ d[o++] = byte m.crypto;
+ d[o++] = byte m.codec;
+ Rping =>
+ ;
+ Rread =>
+ d[H:] = m.data;
+ Rwrite =>
+ d[H:] = m.score.a;
+ Rsync =>
+ ;
+ Rerror =>
+ pstring(d, H, m.e);
+ * =>
+ return nil;
+ }
+ return d;
+}
+
+Vmsg.packedsize(gm: self ref Vmsg): int
+{
+ mtype := tag2type[tagof gm];
+ if(mtype <= 0)
+ return 0;
+ ml := hdrlen[mtype];
+ pick m := gm {
+ Thello =>
+ ml += utflen(m.version) + utflen(m.uid) + len m.cryptos + len m.codecs;
+ Rhello =>
+ ml += utflen(m.sid);
+ Rread =>
+ ml += len m.data;
+ Twrite =>
+ ml += len m.data;
+ Rerror =>
+ ml += utflen(m.e);
+ }
+ return ml;
+}
+
+Vmsg.text(gm: self ref Vmsg): string
+{
+ if(gm == nil)
+ return "(nil)";
+ s := sys->sprint("%s(%d", msgname[tagof gm], gm.tid);
+ pick m := gm {
+ * =>
+ s += ",ILLEGAL";
+ Thello =>
+ s += sys->sprint(", %#q, %#q, %d, [", m.version, m.uid, m.cryptostrength);
+ if(len m.cryptos > 0){
+ s += string int m.cryptos[0];
+ for(i := 1; i < len m.cryptos; i++)
+ s += "," + string int m.cryptos[i];
+ }
+ s += "], [";
+ if(len m.codecs > 0){
+ s += string int m.codecs[0];
+ for(i := 1; i < len m.codecs; i++)
+ s += "," + string int m.codecs[i];
+ }
+ s += "]";
+ Tping =>
+ ;
+ Tgoodbye =>
+ ;
+ Tread =>
+ s += sys->sprint(", %s, %d, %d", m.score.text(), m.etype, m.n);
+ Twrite =>
+ s += sys->sprint(", %d, data[%d]", m.etype, len m.data);
+ Tsync =>
+ ;
+ Rhello =>
+ s += sys->sprint(", %#q, %d, %d", m.sid, m.crypto, m.codec);
+ Rping =>
+ Rread =>
+ s += sys->sprint(", data[%d]", len m.data);
+ Rwrite =>
+ s += ", " + m.score.text();
+ Rsync =>
+ ;
+ Rerror =>
+ s += sys->sprint(", %#q", m.e);
+ }
+ return s + ")";
+}
+
+Session.new(fd: ref Sys->FD): ref Session
+{
+ s := "venti-";
+ for(i := 0; i < len versions; i++){
+ if(i != 0)
+ s[len s] = ':';
+ s += versions[i];
+ }
+ s += "-libventi\n";
+ d := array of byte s;
+ if(sys->write(fd, d, len d) != len d)
+ return nil;
+ version := readversion(fd, "venti-", versions);
+ if(version == nil)
+ return nil;
+ session := ref Session(fd, version);
+ (r, e) := session.rpc(ref Vmsg.Thello(1, 0, version, nil, 0, nil, nil));
+ if(r == nil){
+ sys->werrstr("hello failed: " + e);
+ return nil;
+ }
+ return ref Session(fd, version);
+}
+
+Session.read(s: self ref Session, score: Score, etype: int, maxn: int): array of byte
+{
+ (gm, err) := s.rpc(ref Vmsg.Tread(1, 0, score, etype, maxn));
+ if(gm == nil){
+ sys->werrstr(err);
+ return nil;
+ }
+ pick m := gm {
+ Rread =>
+ return m.data;
+ }
+ return nil;
+}
+
+Session.write(s: self ref Session, etype: int, data: array of byte): (int, Score)
+{
+ (gm, err) := s.rpc(ref Vmsg.Twrite(1, 0, etype, data));
+ if(gm == nil){
+ sys->werrstr(err);
+ return (-1, Score(nil));
+ }
+ pick m := gm {
+ Rwrite =>
+ return (0, m.score);
+ }
+ return (-1, Score(nil));
+}
+
+Session.sync(s: self ref Session): int
+{
+ (gm, err) := s.rpc(ref Vmsg.Tsync(1, 0));
+ if(gm == nil){
+ sys->werrstr(err);
+ return -1;
+ }
+ return 0;
+}
+
+Session.rpc(s: self ref Session, m: ref Vmsg): (ref Vmsg, string)
+{
+ d := m.pack();
+ if(sys->write(s.fd, d, len d) != len d)
+ return (nil, "write failed");
+ (grm, err) := Vmsg.read(s.fd);
+ if(grm == nil)
+ return (nil, err);
+ if(grm.tid != m.tid)
+ return (nil, "message tags don't match");
+ if(grm.istmsg)
+ return (nil, "reply message is a t-message");
+ pick rm := grm {
+ Rerror =>
+ return (nil, rm.e);
+ }
+ if(tagof(grm) != tagof(m) + 1)
+ return (nil, "reply message is of wrong type");
+ return (grm, nil);
+}
+
+readversion(fd: ref Sys->FD, prefix: string, versions: array of string): string
+{
+ buf := array[Maxstringsize] of byte;
+ i := 0;
+ for(;;){
+ if(i >= len buf){
+ sys->werrstr("initial version string too long");
+ return nil;
+ }
+ if(readn(fd, buf[i:], 1) != 1){
+ sys->werrstr("eof on version string");
+ return nil;
+ }
+ c := int buf[i];
+ if(c == '\n')
+ break;
+ if(c < ' ' || c > 16r7f || i < len prefix && prefix[i] != c){
+ sys->werrstr("bad version string");
+ return nil;
+ }
+ i++;
+ }
+ if(i < len prefix){
+ sys->werrstr("bad version string");
+ return nil;
+ }
+sys->fprint(sys->fildes(2), "read version %#q\n", string buf[0:i]);
+ v := string buf[len prefix:i];
+ i = 0;
+ for(;;){
+ for(j := i; j < len v && v[j] != ':' && v[j] != '-'; j++)
+ ;
+ vv := v[i:j];
+sys->fprint(sys->fildes(2), "checking %#q\n", vv);
+ for(k := 0; k < len versions; k++)
+ if(versions[k] == vv)
+ return vv;
+ i = j;
+ if(i >= len v || v[i] != ':'){
+ sys->werrstr("unknown version");
+ return nil;
+ }
+ i++;
+ }
+ sys->werrstr("unknown version");
+ return nil;
+}
+
+
+Score.eq(a: self Score, b: Score): int
+{
+ for(i := 0; i < SCORE; i++)
+ if(a.a[i] != b.a[i])
+ return 0;
+ return 1;
+}
+
+Score.zero(): Score
+{
+ return Score(zero);
+}
+
+Score.parse(s: string): (int, Score)
+{
+ if(len s != Scoresize * 2)
+ return (-1, Score(nil));
+ score := array[Scoresize] of {* => byte 0};
+ for(i := 0; i < len s; i++){
+ c := s[i];
+ case s[i] {
+ '0' to '9' =>
+ c -= '0';
+ 'a' to 'f' =>
+ c -= 'a' - 10;
+ 'A' to 'F' =>
+ c -= 'A' - 10;
+ * =>
+ return (-1, Score(nil));
+ }
+ if((i & 1) == 0)
+ c <<= 4;
+ score[i>>1] |= byte c;
+ }
+ return (0, Score(score));
+}
+
+Score.text(a: self Score): string
+{
+ s := "";
+ for(i := 0; i < SCORE; i++)
+ s += sys->sprint("%.2ux", int a.a[i]);
+ return s;
+}
+
+readn(fd: ref Sys->FD, buf: array of byte, nb: int): int
+{
+ for(nr := 0; nr < nb;){
+ n := sys->read(fd, buf[nr:], nb-nr);
+ if(n <= 0){
+ if(nr == 0)
+ return n;
+ break;
+ }
+ nr += n;
+ }
+ return nr;
+}
+
+readmsg(fd: ref Sys->FD): (array of byte, string)
+{
+ sbuf := array[BIT16SZ] of byte;
+ if((n := readn(fd, sbuf, BIT16SZ)) != BIT16SZ){
+ if(n == 0)
+ return (nil, nil);
+ return (nil, sys->sprint("%r"));
+ }
+ ml := (int sbuf[0] << 8) | int sbuf[1];
+ if(ml <= BIT16SZ)
+ return (nil, "invalid venti message size");
+ buf := array[ml + BIT16SZ] of byte;
+ buf[0:] = sbuf;
+ if((n = readn(fd, buf[BIT16SZ:], ml)) != ml){
+ if(n == 0)
+ return (nil, "venti message truncated");
+ return (nil, sys->sprint("%r"));
+ }
+ return (buf, nil);
+}
+
+pstring(a: array of byte, o: int, s: string): int
+{
+ sa := array of byte s; # could do conversion ourselves
+ n := len sa;
+ a[o] = byte (n >> 8);
+ a[o+1] = byte n;
+ a[o+2:] = sa;
+ return o+STR+n;
+}
+
+gstring(a: array of byte, o: int): (string, int)
+{
+ if(o < 0 || o+STR > len a)
+ return (nil, -1);
+ l := (int a[o] << 8) | int a[o+1];
+ if(l > Maxstringsize)
+ return (nil, -1);
+ o += STR;
+ e := o+l;
+ if(e > len a)
+ return (nil, -1);
+ return (string a[o:e], e);
+}
+
+utflen(s: string): int
+{
+ # the domain is 16-bit unicode only, which is all that Inferno now implements
+ n := l := len s;
+ for(i:=0; i<l; i++)
+ if((c := s[i]) > 16r7F){
+ n++;
+ if(c > 16r7FF)
+ n++;
+ }
+ return n;
+}
+
+gtstring(a: array of byte, o: int, n: int): string
+{
+ e := o + n;
+ if(e > len a)
+ return nil;
+ for(i := o; i < e; i++)
+ if(a[i] == byte 0)
+ break;
+ return string a[o:i];
+}
+unpackroot(d: array of byte): ref Root
+{
+ if(len d != Rootsize){
+ sys->werrstr("root entry is wrong length");
+ return nil;
+ }
+ r := ref blankroot;
+ r.version = g16(d, 0);
+ if(r.version != Rootversion){
+ sys->werrstr("unknown root version");
+ return nil;
+ }
+ o := BIT16SZ;
+ r.name = gtstring(d, o, Rootnamelen);
+ o += Rootnamelen;
+ r.rtype = gtstring(d, o, Rootnamelen);
+ o += Rootnamelen;
+ r.score = gscore(d, o);
+ o += Scoresize;
+ r.blocksize = g16(d, o);
+ o += BIT16SZ;
+ r.prev = gscore(d, o);
+ return r;
+}
+
+unpackentry(d: array of byte): ref Entry
+{
+ if(len d != Entrysize){
+ sys->werrstr("entry is wrong length");
+ return nil;
+ }
+ e := ref blankentry;
+ i := 0;
+ e.gen = g32(d, i);
+ i += BIT32SZ;
+ e.psize = g16(d, i);
+ i += BIT16SZ;
+ e.dsize = g16(d, i);
+ i += BIT16SZ;
+ e.flags = int d[i];
+ e.depth= (e.flags & Entrydepthmask) >> Entrydepthshift;
+ e.flags &= ~Entrydepthmask;
+ i += BIT8SZ;
+ i += 5; # skip something...
+ e.size = g48(d, i);
+ i += BIT48SZ;
+ e.score = gscore(d, i);
+ i += Scoresize;
+ if((e.flags & Entryactive) == 0)
+ return e;
+ if(!checksize(e.psize) || !checksize(e.dsize)){
+ sys->werrstr(sys->sprint("bad blocksize (%d or %d)", e.psize, e.dsize));
+ return nil;
+ }
+ return e;
+}
+
+checksize(n: int): int
+{
+ if(n < 256 || n > Maxlumpsize) {
+ sys->werrstr("bad block size");
+ return 0;
+ }
+ return 1;
+}
+
+gscore(f: array of byte, i: int): Score
+{
+ s := Score(array[Scoresize] of byte);
+ s.a[0:] = f[i:i+Scoresize];
+ return s;
+}
+
+g16(f: array of byte, i: int): int
+{
+ return (int f[i] << 8) | int f[i+1];
+}
+
+g32(f: array of byte, i: int): int
+{
+ return (((((int f[i+0] << 8) | int f[i+1]) << 8) | int f[i+2]) << 8) | int f[i+3];
+}
+
+g48(f: array of byte, i: int): big
+{
+ b1 := (((((int f[i+0] << 8) | int f[i+1]) << 8) | int f[i+2]) << 8) | int f[i+3];
+ b0 := (int f[i+4] << 8) | int f[i+5];
+ return (big b1 << 16) | big b0;
+}
+
+g64(f: array of byte, i: int): big
+{
+ b0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
+ b1 := (((((int f[i+7] << 8) | int f[i+6]) << 8) | int f[i+5]) << 8) | int f[i+4];
+ return (big b1 << 32) | (big b0 & 16rFFFFFFFF);
+}
+
diff --git a/appl/lib/virgil.b b/appl/lib/virgil.b
new file mode 100644
index 00000000..eb68671b
--- /dev/null
+++ b/appl/lib/virgil.b
@@ -0,0 +1,177 @@
+implement Virgil;
+
+include "sys.m";
+ sys: Sys;
+include "string.m";
+include "keyring.m";
+include "draw.m";
+include "security.m";
+include "ip.m";
+ ip: IP;
+ IPaddr, Udphdr: import ip;
+
+stderr: ref Sys->FD;
+done: int;
+Udphdrsize: con IP->OUdphdrlen; # use oldheaders format for compatibility
+Virgilport: con 2202;
+
+#
+# this module is very udp dependent. it shouldn't be. -- presotto
+# Call with first element of argv an arbitrary string, which is
+# discarded here. argv must also contain at least a question.
+#
+virgil(argv: list of string): string
+{
+ s,question,reply,r : string;
+ timerpid, readerpid: int;
+
+ if (argv == nil || tl argv == nil || hd (tl argv) == nil)
+ return nil;
+ done = 0;
+ sys = load Sys Sys->PATH;
+ str := load String String->PATH;
+ if(str == nil){
+ cantload(String->PATH);
+ return nil;
+ }
+ ip = load IP IP->PATH;
+ if(ip == nil){
+ cantload(IP->PATH);
+ return nil;
+ }
+ ip->init();
+ stderr = sys->fildes(2);
+
+ # We preserve the convention that the first arg is not an option.
+ # Undocumented '-v address' option allows passing in address
+ # of virgild, circumventing broadcast. Used for development,
+ # to avoid pestering servers on network.
+ dest := ip->v4bcast;
+ argv = tl argv;
+ s = hd argv;
+ if(s[0] == '-') {
+ if(s[1] != 'v')
+ return nil;
+ argv = tl argv;
+ if (argv == nil)
+ return nil;
+ s = hd argv;
+ ok: int;
+ (ok, dest) = IPaddr.parse(s);
+ if(ok < 0){
+ sys->fprint(stderr, "virgil: invalid IP address %s\n", s);
+ return nil;
+ }
+ argv = tl argv;
+ }
+
+ # Is there a question?
+ if (argv == nil)
+ return nil;
+ question = hd argv;
+
+ (ok, c) := sys->announce("udp!*!0");
+ if(ok < 0)
+ return nil;
+ if(sys->fprint(c.cfd, "headers") < 0)
+ return nil;
+ sys->fprint(c.cfd, "oldheaders");
+ c.dfd = sys->open(c.dir+"/data", sys->ORDWR);
+ if(c.dfd == nil)
+ return nil;
+
+ readerchan := chan of string;
+ timerchan := chan of int;
+ readerpidchan := chan of int;
+
+ spawn timer(timerchan);
+ timerpid = <-timerchan;
+ spawn reader(c.dfd, readerchan, readerpidchan);
+ readerpid = <-readerpidchan;
+
+ question = getid() + "?" + question;
+ qbuf := array of byte question;
+ hdr := Udphdr.new();
+ hdr.raddr = dest;
+ hdr.rport = Virgilport;
+ buf := array[Udphdrsize + len qbuf] of byte;
+ buf[Udphdrsize:] = qbuf;
+ hdr.pack(buf, Udphdrsize);
+ for(tries := 0; tries < 5; ){
+ if(sys->write(c.dfd, buf, len buf) < 0)
+ break;
+
+ alt {
+ r = <-readerchan =>
+ ;
+ <-timerchan =>
+ tries++;
+ continue;
+ };
+
+ if(str->prefix(question + "=", r)){
+ reply = r[len question + 1:];
+ break;
+ }
+ }
+
+ done = 1;
+ killpid(readerpid);
+ killpid(timerpid);
+ return reply;
+}
+
+cantload(s: string)
+{
+ sys->fprint(stderr, "virgil: can't load %s: %r\n", s);
+}
+
+getid(): string
+{
+ fd := sys->open("/dev/sysname", sys->OREAD);
+ if(fd == nil)
+ return "unknown";
+ buf := array[256] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 1)
+ return "unknown";
+ return string buf[0:n];
+}
+
+reader(fd: ref sys->FD, cstring: chan of string, cpid: chan of int)
+{
+ pid := sys->pctl(0, nil);
+ cpid <-= pid;
+
+ buf := array[2048] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n <= Udphdrsize)
+ return;
+
+ # dump cruft
+ for(i := Udphdrsize; i < n; i++)
+ if((int buf[i]) == 0)
+ break;
+
+ if(!done)
+ cstring <-= string buf[Udphdrsize:i];
+}
+
+timer(c: chan of int)
+{
+ pid := sys->pctl(0, nil);
+ c <-= pid;
+ while(!done){
+ sys->sleep(1000);
+ if(done)
+ break;
+ c <-= 1;
+ }
+}
+
+killpid(pid: int)
+{
+ fd := sys->open("#p/"+(string pid)+"/ctl", sys->OWRITE);
+ if(fd != nil)
+ sys->fprint(fd, "kill");
+}
diff --git a/appl/lib/volume.b b/appl/lib/volume.b
new file mode 100644
index 00000000..8615d1e3
--- /dev/null
+++ b/appl/lib/volume.b
@@ -0,0 +1,172 @@
+implement Volumectl;
+
+include "sys.m";
+sys: Sys;
+sprint: import sys;
+
+include "draw.m";
+draw: Draw;
+Context, Display, Font, Rect, Point, Image, Screen, Pointer: import draw;
+
+include "prefab.m";
+prefab: Prefab;
+Style, Element, Compound, Environ: import prefab;
+
+include "muxclient.m";
+include "volume.m";
+
+include "bufio.m";
+bufio: Bufio;
+Iobuf: import bufio;
+
+include "ir.m";
+
+screen: ref Screen;
+display: ref Display;
+windows: array of ref Image;
+env: ref Environ;
+zr := ((0,0),(0,0));
+
+el, et: ref Element;
+
+style: ref Style;
+
+c: ref Compound;
+
+tics: int;
+INTERVAL: con 500;
+
+value: int;
+
+ones, white, red: ref Image;
+
+volumectl(ctxt: ref Context, ch: chan of int, var: string)
+{
+ key: int;
+
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+ prefab = load Prefab Prefab->PATH;
+ if ((bufio = load Bufio Bufio->PATH) == nil) {
+ sys->print("Audioctl: Can't load bufio\n");
+ exit;
+ }
+
+ if ((ac := bufio->open("/dev/volume", bufio->ORDWR)) == nil) {
+ sys->print("Audioctl: Can't open /dev/volume: %r\n");
+ exit;
+ }
+
+ screen = ctxt.screen;
+ display = ctxt.display;
+ windows = array[1] of ref Image;
+
+ ones = display.opaque;
+ white = display.color(draw->White);
+ red = display.color(draw->Red);
+
+ textfont := Font.open(display, "*default*");
+
+ style = ref Style(
+ textfont, # titlefont
+ textfont, # textfont
+ display.color(draw->White), # elemcolor
+ display.color(draw->Black), # edgecolor
+ display.color(draw->Yellow), # titlecolor
+ display.color(draw->Black), # textcolor
+ display.color(130)); # highlightcolor
+
+ env = ref Environ (ctxt.screen, style);
+
+ slavectl := chan of int;
+ spawn timerslave(slavectl);
+
+ while ((s := ac.gets('\n')) != nil) {
+ sp := -1;
+ for (i := 0; i < len s; i++) if (s[i] == ' ') sp = i;
+ if (sp <= 1) {
+ sys->print("Volume: /dev/volume bad:\n%s\n", s);
+ exit;
+ }
+ if (var == s[0:sp]) {
+ value = int s[sp+1:];
+ }
+ }
+
+ n := 0;
+ for(;;) {
+ key = <- ch;
+ case key {
+ Ir->Enter =>
+ slavectl <-= Muxclient->AMexit;
+ return;
+ Ir->VolUP =>
+ if (value++ >= 100) value = 100;
+ ac.puts(sprint("%s %d\n", var, value));
+ displayslider();
+ Ir->VolDN =>
+ if (value-- <= 0) value = 0;
+ ac.puts(sprint("%s %d\n", var, value));
+ displayslider();
+ }
+ }
+}
+
+slider(): ref Element
+{
+ r: Rect;
+
+ r = ((0,0),(200,20));
+ chans := display.image.chans;
+ icon := display.newimage(r.inset(-2), chans, 0, draw->Black);
+ icon.draw(r, white, ones, (0,0));
+ rr := r;
+ rr.max.x = 2*value;
+ icon.draw(rr, red, ones, (0,0));
+ return Element.icon(env, zr, icon, ones);
+}
+
+displayslider()
+{
+ if (et == nil) {
+ et = Element.text(env, "Volume", zr, Prefab->EText);
+ el = slider();
+ }
+
+ img := el.image;
+ r: Rect = ((0,0),(200,20));
+ img.draw(r, white, nil, (0,0));
+ r.max.x = 2*value;
+ img.draw(r, red, nil, (0,0));
+
+ if (c == nil) {
+ c = Compound.box(env, Point(100, 100), et, el);
+ windows[0] = c.image;
+ }
+ c.draw();
+ screen.top(windows);
+ tics = 5;
+}
+
+timerslave(ctl: chan of int)
+{
+ m: int;
+
+ for(;;) {
+ sys->sleep(INTERVAL);
+ if (tics-- <= 0) {
+ tics = 0;
+ c = nil;
+ el = nil;
+ et = nil;
+ windows[0] = nil;
+ }
+
+ alt{
+ m = <-ctl =>
+ return;
+ * =>
+ continue;
+ }
+ }
+}
diff --git a/appl/lib/w3c/css.b b/appl/lib/w3c/css.b
new file mode 100644
index 00000000..9b1475b5
--- /dev/null
+++ b/appl/lib/w3c/css.b
@@ -0,0 +1,1019 @@
+implement CSS;
+
+#
+# CSS2 parsing module
+#
+# CSS2.1 style sheets
+#
+# Copyright © 2001, 2005 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "css.m";
+
+B, NUMBER, IDENT, STRING, URL, PERCENTAGE, UNIT,
+ HASH, ATKEYWORD, IMPORTANT, IMPORT, PSEUDO, CLASS, INCLUDES,
+ DASHMATCH, FUNCTION: con 16rE000+iota;
+
+toknames := array[] of{
+ B-B => "Zero",
+ NUMBER-B => "NUMBER",
+ IDENT-B => "IDENT",
+ STRING-B => "STRING",
+ URL-B => "URL",
+ PERCENTAGE-B => "PERCENTAGE",
+ UNIT-B => "UNIT",
+ HASH-B => "HASH",
+ ATKEYWORD-B => "ATKEYWORD",
+ IMPORTANT-B => "IMPORTANT",
+ CLASS-B => "CLASS",
+ INCLUDES-B => "INCLUDES",
+ DASHMATCH-B => "DASHMATCH",
+ PSEUDO-B => "PSEUDO",
+ FUNCTION-B => "FUNCTION",
+};
+
+printdiag := 0;
+
+init(d: int)
+{
+ sys = load Sys Sys->PATH;
+ printdiag = d;
+}
+
+parse(s: string): (ref Stylesheet, string)
+{
+ return stylesheet(ref Cparse(-1, 0, nil, nil, Clex.new(s,1)));
+}
+
+parsedecl(s: string): (list of ref Decl, string)
+{
+ return (declarations(ref Cparse(-1, 0, nil, nil, Clex.new(s,0))), nil);
+}
+
+ptok(c: int): string
+{
+ if(c < 0)
+ return "eof";
+ if(c == 0)
+ return "zero?";
+ if(c >= B)
+ return sys->sprint("%s", toknames[c-B]);
+ return sys->sprint("%c", c);
+}
+
+Cparse: adt {
+ lookahead: int;
+ eof: int;
+ value: string;
+ suffix: string;
+ cs: ref Clex;
+
+ get: fn(nil: self ref Cparse): int;
+ look: fn(nil: self ref Cparse): int;
+ unget: fn(nil: self ref Cparse, tok: int);
+ skipto: fn(nil: self ref Cparse, followset: string): int;
+ synerr: fn(nil: self ref Cparse, s: string);
+};
+
+Cparse.get(p: self ref Cparse): int
+{
+ if((c := p.lookahead) >= 0){
+ p.lookahead = -1;
+ return c;
+ }
+ if(p.eof)
+ return -1;
+ (c, p.value, p.suffix) = csslex(p.cs);
+ if(c < 0)
+ p.eof = 1;
+ if(printdiag > 1)
+ sys->print("lex: %s v=%s s=%s\n", ptok(c), p.value, p.suffix);
+ return c;
+}
+
+Cparse.look(p: self ref Cparse): int
+{
+ c := p.get();
+ p.unget(c);
+ return c;
+}
+
+Cparse.unget(p: self ref Cparse, c: int)
+{
+ if(p.lookahead >= 0)
+ raise "css: internal error: Cparse.unget";
+ p.lookahead = c; # note that p.value and p.suffix are assumed to be those of c
+}
+
+Cparse.skipto(p: self ref Cparse, followset: string): int
+{
+ while((c := p.get()) >= 0)
+ for(i := 0; i < len followset; i++)
+ if(followset[i] == c){
+ p.unget(c);
+ return c;
+ }
+ return -1;
+}
+
+Cparse.synerr(p: self ref Cparse, s: string)
+{
+ p.cs.synerr(s);
+}
+
+#
+# stylesheet:
+# ["@charset" STRING ';']?
+# [CDO|CDC]* [import [CDO|CDC]*]*
+# [[ruleset | media | page ] [CDO|CDC]*]*
+# import:
+# "@import" [STRING|URL] [ medium [',' medium]*]? ';'
+# media:
+# "@media" medium [',' medium]* '{' ruleset* '}'
+# medium:
+# IDENT
+# page:
+# "@page" pseudo_page? '{' declaration [';' declaration]* '}'
+# pseudo_page:
+# ':' IDENT
+#
+
+stylesheet(p: ref Cparse): (ref Stylesheet, string)
+{
+ charset: string;
+ if(atkeywd(p, "@charset")){
+ if(itisa(p, STRING)){
+ charset = p.value;
+ itisa(p, ';');
+ }else
+ p.synerr("bad @charset declaration");
+ }
+ imports: list of ref Import;
+ while(atkeywd(p, "@import")){
+ c := p.get();
+ if(c == STRING || c == URL){
+ name := p.value;
+ media: list of string;
+ c = p.get();
+ if(c == IDENT){ # optional medium [, ...]
+ p.unget(c);
+ media = medialist(p);
+ }
+ imports = ref Import(name, media) :: imports;
+ }else
+ p.synerr("bad @import");
+ if(c != ';'){
+ p.synerr("missing ; in @import");
+ p.unget(c);
+ if(p.skipto(";}") < 0)
+ break;
+ }
+ }
+ imports = rev(imports);
+
+ stmts: list of ref Statement;
+ do{
+ while((c := p.get()) == ATKEYWORD)
+ case p.value {
+ "@media" => # medium[,medium]* { ruleset*}
+ media := medialist(p);
+ if(!itisa(p, '{')){
+ p.synerr("bad @media");
+ skipatrule("@media", p);
+ continue;
+ }
+ rules: list of ref Statement.Ruleset;
+ do{
+ rule := checkrule(p);
+ if(rule != nil)
+ rules = rule :: rules;
+ }while(!itisa(p, '}') && !p.eof);
+ stmts = ref Statement.Media(media, rev(rules)) :: stmts;
+ "@page" => # [:ident]? { declaration [; declaration]* }
+ pseudo: string;
+ if(itisa(p, PSEUDO))
+ pseudo = p.value;
+ if(!itisa(p, '{')){
+ p.synerr("bad @page");
+ skipatrule("@page", p);
+ continue;
+ }
+ decls := declarations(p);
+ if(!itisa(p, '}')){
+ p.synerr("unclosed @page declaration block");
+ skipatrule("@page", p);
+ continue;
+ }
+ stmts = ref Statement.Page(pseudo, decls) :: stmts;
+ * =>
+ skipatrule(p.value, p); # skip unknown or misplaced at-rule
+ }
+ p.unget(c);
+ rule := checkrule(p);
+ if(rule != nil)
+ stmts = rule :: stmts;
+ }while(!p.eof);
+ rl := stmts;
+ stmts = nil;
+ for(; rl != nil; rl = tl rl)
+ stmts = hd rl :: stmts;
+ return (ref Stylesheet(charset, imports, stmts), nil);
+}
+
+checkrule(p: ref Cparse): ref Statement.Ruleset
+{
+ (rule, err) := ruleset(p);
+ if(rule == nil){
+ if(err != nil){
+ p.synerr(sys->sprint("bad ruleset: %s", err));
+ p.get(); # make some progress
+ }
+ }
+ return rule;
+}
+
+medialist(p: ref Cparse): list of string
+{
+ media: list of string;
+ do{
+ c := p.get();
+ if(c != IDENT){
+ p.unget(c);
+ p.synerr("missing medium identifier");
+ break;
+ }
+ media = p.value :: media;
+ }while(itisa(p, ','));
+ return rev(media);
+}
+
+itisa(p: ref Cparse, expect: int): int
+{
+ if((c := p.get()) == expect)
+ return 1;
+ p.unget(c);
+ return 0;
+}
+
+atkeywd(p: ref Cparse, expect: string): int
+{
+ if((c := p.get()) == ATKEYWORD && p.value == expect)
+ return 1;
+ p.unget(c);
+ return 0;
+}
+
+skipatrule(name: string, p: ref Cparse)
+{
+ if(printdiag)
+ sys->print("skip unimplemented or misplaced %s\n", name);
+ if((c := p.get()) == '{'){ # block
+ for(nesting := '}' :: nil; nesting != nil && c >= 0; nesting = tl nesting){
+ while((c = p.cs.getc()) >= 0 && c != hd nesting)
+ case c {
+ '{' =>
+ nesting = '}' :: nesting;
+ '(' =>
+ nesting = ')' :: nesting;
+ '[' =>
+ nesting = ']' :: nesting;
+ '"' or '\'' =>
+ quotedstring(p.cs, c);
+ }
+ }
+ }else{
+ while(c >= 0 && c != ';')
+ c = p.get();
+ }
+}
+
+# ruleset:
+# selector [',' S* selector]* '{' S* declaration [';' S* declaration]* '}' S*
+
+ruleset(p: ref Cparse): (ref Statement.Ruleset, string)
+{
+ selectors: list of list of (int, list of ref Select);
+ c := -1;
+ do{
+ s := selector(p);
+ if(s == nil){
+ if(p.eof)
+ return (nil, nil);
+ p.synerr("expected selector");
+ if(p.skipto(",{}") < 0)
+ return (nil, nil);
+ c = p.look();
+ }else
+ selectors = s :: selectors;
+ }while((c = p.get()) == ',');
+ if(c != '{')
+ return (nil, "expected declaration block");
+ sl := selectors;
+ selectors = nil;
+ for(; sl != nil; sl = tl sl)
+ selectors = hd sl :: selectors;
+ decls := declarations(p);
+ if(!itisa(p, '}')){
+ p.synerr("unclosed declaration block");
+ }
+ return (ref Statement.Ruleset(selectors, decls), nil);
+}
+
+declarations(p: ref Cparse): list of ref Decl
+{
+ decls: list of ref Decl;
+ c: int;
+ do{
+ (d, e) := declaration(p);
+ if(d != nil)
+ decls = d :: decls;
+ else if(e != nil){
+ p.synerr("ruleset declaration: "+e);
+ if((c = p.skipto(";}")) < 0)
+ break;
+ }
+ }while((c = p.get()) == ';');
+ p.unget(c);
+ l := decls;
+ for(decls = nil; l != nil; l = tl l)
+ decls = hd l :: decls;
+ return decls;
+}
+
+# selector:
+# simple_selector [combinator simple_selector]*
+# combinator:
+# '+' S* | '>' S* | /* empty */
+#
+
+selector(p: ref Cparse): list of (int, list of ref Select)
+{
+ sel: list of (int, list of ref Select);
+ op := ' ';
+ while((s := selector1(p)) != nil){
+ sel = (op, s) :: sel;
+ if((c := p.look()) == '+' || c == '>')
+ op = p.get();
+ else
+ op = ' ';
+ }
+ l: list of (int, list of ref Select);
+ for(; sel != nil; sel = tl sel)
+ l = hd sel :: l;
+ return l;
+}
+
+#
+# simple_selector:
+# element_name? [HASH | class | attrib | pseudo]* S*
+# element_name:
+# IDENT | '*'
+# class:
+# '.' IDENT
+# attrib:
+# '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S* [IDENT | STRING] S* ]? ']'
+# pseudo
+# ':' [ IDENT | FUNCTION S* IDENT? S* ')' ]
+
+selector1(p: ref Cparse): list of ref Select
+{
+ sel: list of ref Select;
+ c := p.get();
+ if(c == IDENT)
+ sel = ref Select.Element(p.value) :: sel;
+ else if(c== '*')
+ sel = ref Select.Any("*") :: sel;
+ else
+ p.unget(c);
+Sel:
+ for(;;){
+ c = p.get();
+ case c {
+ HASH =>
+ sel = ref Select.ID(p.value) :: sel;
+ CLASS =>
+ sel = ref Select.Class(p.value) :: sel;
+ '[' =>
+ if(!itisa(p, IDENT))
+ break;
+ name := p.value;
+ case c = p.get() {
+ '=' =>
+ sel = ref Select.Attrib(name, "=", optaval(p)) :: sel;
+ INCLUDES =>
+ sel = ref Select.Attrib(name, "~=", optaval(p)) :: sel;
+ DASHMATCH =>
+ sel = ref Select.Attrib(name, "|=", optaval(p)) :: sel;
+ * =>
+ sel = ref Select.Attrib(name, nil, nil) :: sel;
+ p.unget(c);
+ }
+ if((c = p.get()) != ']'){
+ p.synerr("bad attribute syntax");
+ p.unget(c);
+ break Sel;
+ }
+ PSEUDO =>
+ case c = p.get() {
+ IDENT =>
+ sel = ref Select.Pseudo(p.value) :: sel;
+ FUNCTION =>
+ name := p.value;
+ case c = p.get() {
+ IDENT =>
+ sel = ref Select.Pseudofn(name, lowercase(p.value)) :: sel;
+ ')' =>
+ p.unget(c);
+ sel = ref Select.Pseudofn(name, nil) :: sel;
+ * =>
+ p.synerr("bad pseudo-function syntax");
+ p.unget(c);
+ break Sel;
+ }
+ if((c = p.get()) != ')'){
+ p.synerr("missing ')' for pseudo-function");
+ p.unget(c);
+ break Sel;
+ }
+ * =>
+ p.synerr(sys->sprint("unexpected :pseudo: %s:%s", ptok(c), p.value));
+ p.unget(c);
+ break Sel;
+ }
+ * =>
+ p.unget(c);
+ break Sel;
+ }
+ # qualifiers must be adjacent to the first item, and each other
+ c = p.cs.getc();
+ p.cs.ungetc(c);
+ if(isspace(c))
+ break;
+ }
+ sl := sel;
+ for(sel = nil; sl != nil; sl = tl sl)
+ sel = hd sl :: sel;
+ return sel;
+}
+
+optaval(p: ref Cparse): ref Value
+{
+ case c := p.get() {
+ IDENT =>
+ return ref Value.Ident(' ', p.value);
+ STRING =>
+ return ref Value.String(' ', p.value);
+ * =>
+ p.unget(c);
+ return nil;
+ }
+}
+
+# declaration:
+# property ':' S* expr prio?
+# | /* empty */
+# property:
+# IDENT
+# prio:
+# IMPORTANT S* /* ! important */
+
+declaration(p: ref Cparse): (ref Decl, string)
+{
+ c := p.get();
+ if(c != IDENT){
+ p.unget(c);
+ return (nil, nil);
+ }
+ prop := lowercase(p.value);
+ c = p.get();
+ if(c != ':'){
+ p.unget(c);
+ return (nil, "missing :");
+ }
+ values := expr(p);
+ if(values == nil)
+ return (nil, "missing expression(s)");
+ prio := 0;
+ if(p.look() == IMPORTANT){
+ p.get();
+ prio = 1;
+ }
+ return (ref Decl(prop, values, prio), nil);
+}
+
+# expr:
+# term [operator term]*
+# operator:
+# '/' | ',' | /* empty */
+
+expr(p: ref Cparse): list of ref Value
+{
+ values: list of ref Value;
+ sep := ' ';
+ while((t := term(p, sep)) != nil){
+ values = t :: values;
+ if((c := p.look()) == '/' || c == ',')
+ sep = p.get(); # need something fancier here?
+ else
+ sep = ' ';
+ }
+ vl := values;
+ for(values = nil; vl != nil; vl = tl vl)
+ values = hd vl :: values;
+ return values;
+}
+
+#
+# term:
+# unary_operator? [NUMBER | PERCENTAGE | LENGTH | EMS | EXS | ANGLE | TIME | FREQ | function]
+# | STRING | IDENT | URI | RGB | UNICODERANGE | hexcolour
+# function:
+# FUNCTION expr ')'
+# unary_operator:
+# '-' | '+'
+# hexcolour:
+# HASH S*
+#
+# LENGTH, EMS, ... FREQ have been combined into UNIT here
+#
+# TO DO: UNICODERANGE
+
+term(p: ref Cparse, sep: int): ref Value
+{
+ prefix: string;
+ case p.look(){
+ '+' or '-' =>
+ prefix[0] = p.get();
+ }
+ c := p.get();
+ case c {
+ NUMBER =>
+ return ref Value.Number(sep, prefix+p.value);
+ PERCENTAGE =>
+ return ref Value.Percentage(sep, prefix+p.value);
+ UNIT =>
+ return ref Value.Unit(sep, prefix+p.value, p.suffix);
+ }
+ if(prefix != nil)
+ p.synerr("+/- before non-numeric");
+ case c {
+ STRING =>
+ return ref Value.String(sep, p.value);
+ IDENT =>
+ return ref Value.Ident(sep, lowercase(p.value));
+ URL =>
+ return ref Value.Url(sep, p.value);
+ HASH =>
+ # could check value: 3 or 6 hex digits
+ (r, g, b) := torgb(p.value);
+ if(r < 0)
+ return nil;
+ return ref Value.Hexcolour(sep, p.value, (r,g,b));
+ FUNCTION =>
+ name := p.value;
+ args := expr(p);
+ c = p.get();
+ if(c != ')'){
+ p.synerr(sys->sprint("missing ')' for function %s", name));
+ return nil;
+ }
+ if(name == "rgb"){
+ if(len args != 3){
+ p.synerr("wrong number of arguments to rgb()");
+ return nil;
+ }
+ r := colourof(hd args);
+ g := colourof(hd tl args);
+ b := colourof(hd tl tl args);
+ if(r < 0 || g < 0 || b < 0){
+ p.synerr("invalid rgb() parameters");
+ return nil;
+ }
+ return ref Value.RGB(sep, args, (r,g,b));
+ }
+ return ref Value.Function(sep, name, args);
+ * =>
+ p.unget(c);
+ return nil;
+ }
+}
+
+torgb(s: string): (int, int, int)
+{
+ case len s {
+ 3 =>
+ r := hex(s[0]);
+ g := hex(s[1]);
+ b := hex(s[2]);
+ if(r >= 0 && g >= 0 && b >= 0)
+ return ((r<<4)|r, (g<<4)|g, (b<<4)|b);
+ 6 =>
+ v := 0;
+ for(i := 0; i < 6; i++){
+ n := hex(s[i]);
+ if(n < 0)
+ return (-1, 0, 0);
+ v = (v<<4) | n;
+ }
+ return (v>>16, (v>>8)&16rFF, v&16rFF);
+ }
+ return (-1, 0, 0);
+}
+
+colourof(v: ref Value): int
+{
+ pick r := v {
+ Number =>
+ return clip(int r.value, 0, 255);
+ Percentage =>
+ # just the integer part
+ return clip((int r.value*255 + 50)/100, 0, 255);
+ * =>
+ return -1;
+ }
+}
+
+clip(v: int, l: int, u: int): int
+{
+ if(v < l)
+ return l;
+ if(v > u)
+ return u;
+ return v;
+}
+
+rev[T](l: list of T): list of T
+{
+ t: list of T;
+ for(; l != nil; l = tl l)
+ t = hd l :: t;
+ return t;
+}
+
+Clex: adt {
+ context: list of int; # characters
+ input: string;
+ lim: int;
+ n: int;
+ lineno: int;
+
+ new: fn(s: string, lno: int): ref Clex;
+ getc: fn(cs: self ref Clex): int;
+ ungetc: fn(cs: self ref Clex, c: int);
+ synerr: fn(nil: self ref Clex, s: string);
+};
+
+Clex.new(s: string, lno: int): ref Clex
+{
+ return ref Clex(nil, s, len s, 0, lno);
+}
+
+Clex.getc(cs: self ref Clex): int
+{
+ if(cs.context != nil){
+ c := hd cs.context;
+ cs.context = tl cs.context;
+ return c;
+ }
+ if(cs.n >= cs.lim)
+ return -1;
+ c := cs.input[cs.n++];
+ if(c == '\n')
+ cs.lineno++;
+ return c;
+}
+
+Clex.ungetc(cs: self ref Clex, c: int)
+{
+ cs.context = c :: cs.context;
+}
+
+Clex.synerr(cs: self ref Clex, s: string)
+{
+ if(printdiag)
+ sys->fprint(sys->fildes(2), "%d: err: %s\n", cs.lineno, s);
+}
+
+csslex(cs: ref Clex): (int, string, string)
+{
+ for(;;){
+ c := skipws(cs);
+ if(c < 0)
+ return (-1, nil, nil);
+ case c {
+ '<' =>
+ if(seq(cs, "!--"))
+ break; # <!-- ignore HTML comment start (CDO)
+ return (c, nil, nil);
+ '-' =>
+ if(seq(cs, "->"))
+ break; # --> ignore HTML comment end (CDC)
+ return (c, nil, nil);
+ ':' =>
+ c = cs.getc();
+ cs.ungetc(c);
+ if(isnamec(c, 0))
+ return (PSEUDO, nil, nil);
+ return (':', nil, nil);
+ '#' =>
+ c = cs.getc();
+ if(isnamec(c, 1))
+ return (HASH, name(cs, c), nil);
+ cs.ungetc(c);
+ return ('#', nil, nil);
+ '/' =>
+ if(subseq(cs, '*', 1, 0)){
+ comment(cs);
+ break;
+ }
+ return (c, nil, nil);
+ '\'' or '"' =>
+ return (STRING, quotedstring(cs, c), nil);
+ '0' to '9' or '.' =>
+ if(c == '.'){
+ d := cs.getc();
+ cs.ungetc(d);
+ if(!isdigit(d)){
+ if(isnamec(d, 1))
+ return (CLASS, name(cs, cs.getc()), nil);
+ return ('.', nil, nil);
+ }
+ # apply CSS2 treatment: .55 is a number not a class
+ }
+ val := number(cs, c);
+ c = cs.getc();
+ if(c == '%')
+ return (PERCENTAGE, val, "%");
+ if(isnamec(c, 0)) # use CSS2 interpetation
+ return (UNIT, val, lowercase(name(cs, c)));
+ cs.ungetc(c);
+ return (NUMBER, val, nil);
+ '\\' =>
+ d := cs.getc();
+ if(d >= ' ' && d <= '~' || islatin1(d)){ # probably should handle it in name
+ wd := name(cs, d);
+ return (IDENT, "\\"+wd, nil);
+ }
+ cs.ungetc(d);
+ return ('\\', nil, nil);
+ '@' =>
+ c = cs.getc();
+ if(isnamec(c, 0)) # @something
+ return (ATKEYWORD, "@"+lowercase(name(cs,c)), nil);
+ cs.ungetc(c);
+ return ('@', nil, nil);
+ '!' =>
+ c = skipws(cs);
+ if(isnamec(c, 0)){ # !something
+ wd := name(cs, c);
+ if(lowercase(wd) == "important")
+ return (IMPORTANT, nil, nil);
+ pushback(cs, wd);
+ }else
+ cs.ungetc(c);
+ return ('!', nil, nil);
+ '~' =>
+ if(subseq(cs, '=', 1, 0))
+ return (INCLUDES, "~=", nil);
+ return ('~', nil, nil);
+ '|' =>
+ if(subseq(cs, '=', 1, 0))
+ return (DASHMATCH, "|=", nil);
+ return ('|', nil, nil);
+ * =>
+ if(isnamec(c, 0)){
+ wd := name(cs, c);
+ d := cs.getc();
+ if(d != '('){
+ cs.ungetc(d);
+ return (IDENT, wd, nil);
+ }
+ val := lowercase(wd);
+ if(val == "url")
+ return (URL, url(cs), nil); # bizarre special case
+ return (FUNCTION, val, nil);
+ }
+ return (c, nil, nil);
+ }
+
+ }
+}
+
+skipws(cs: ref Clex): int
+{
+ for(;;){
+ while((c := cs.getc()) == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f')
+ ;
+ if(c != '/')
+ return c;
+ c = cs.getc();
+ if(c != '*'){
+ cs.ungetc(c);
+ return '/';
+ }
+ comment(cs);
+ }
+}
+
+seq(cs: ref Clex, s: string): int
+{
+ for(i := 0; i < len s; i++)
+ if((c := cs.getc()) != s[i])
+ break;
+ if(i == len s)
+ return 1;
+ cs.ungetc(c);
+ while(i > 0)
+ cs.ungetc(s[--i]);
+ if(c < 0)
+ return -1;
+ return 0;
+}
+
+subseq(cs: ref Clex, a: int, t: int, e: int): int
+{
+ if((c := cs.getc()) != a){
+ cs.ungetc(c);
+ return e;
+ }
+ return t;
+}
+
+pushback(cs: ref Clex, wd: string)
+{
+ for(i := len wd; --i >= 0;)
+ cs.ungetc(wd[i]);
+}
+
+comment(cs: ref Clex)
+{
+ while((c := cs.getc()) != '*' || (c = cs.getc()) != '/')
+ if(c < 0) {
+ # end of file in comment
+ break;
+ }
+}
+
+number(cs: ref Clex, c: int): string
+{
+ s: string;
+ for(; isdigit(c); c = cs.getc())
+ s[len s] = c;
+ if(c != '.'){
+ cs.ungetc(c);
+ return s;
+ }
+ if(!isdigit(c = cs.getc())){
+ cs.ungetc(c);
+ cs.ungetc('.');
+ return s;
+ }
+ s[len s] = '.';
+ do{
+ s[len s] = c;
+ }while(isdigit(c = cs.getc()));
+ cs.ungetc(c);
+ return s;
+}
+
+name(cs: ref Clex, c: int): string
+{
+ s: string;
+ for(; isnamec(c, 1); c = cs.getc()){
+ s[len s] = c;
+ if(c == '\\'){
+ c = cs.getc();
+ if(isescapable(c))
+ s[len s] = c;
+ }
+ }
+ cs.ungetc(c);
+ return s;
+}
+
+isescapable(c: int): int
+{
+ return c >= ' ' && c <= '~' || isnamec(c, 1);
+}
+
+islatin1(c: int): int
+{
+ return c >= 16rA1 && c <= 16rFF; # printable latin-1
+}
+
+isnamec(c: int, notfirst: int): int
+{
+ return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '\\' ||
+ notfirst && (c >= '0' && c <= '9' || c == '-') ||
+ c >= 16rA1 && c <= 16rFF; # printable latin-1
+}
+
+isxdigit(c: int): int
+{
+ return c>='0' && c<='9' || c>='a'&&c<='f' || c>='A'&&c<='F';
+}
+
+isdigit(c: int): int
+{
+ return c >= '0' && c <= '9';
+}
+
+isspace(c: int): int
+{
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f';
+}
+
+hex(c: int): int
+{
+ if(c >= '0' && c <= '9')
+ return c-'0';
+ if(c >= 'A' && c <= 'F')
+ return c-'A' + 10;
+ if(c >= 'a' && c <= 'f')
+ return c-'a' + 10;
+ return -1;
+}
+
+quotedstring(cs: ref Clex, delim: int): string
+{
+ s: string;
+ while((c := cs.getc()) != delim){
+ if(c < 0){
+ cs.synerr("end-of-file in string");
+ return s;
+ }
+ if(c == '\\'){
+ c = cs.getc();
+ if(c < 0){
+ cs.synerr("end-of-file in string");
+ return s;
+ }
+ if(isxdigit(c)){
+ # unicode escape
+ n := 0;
+ for(i := 0;;){
+ n = (n<<4) | hex(c);
+ c = cs.getc();
+ if(!isxdigit(c) || ++i >= 6){
+ if(!isspace(c))
+ cs.ungetc(c); # CSS2 ignores the first white space following
+ break;
+ }
+ }
+ s[len s] = n;
+ }else if(c == '\n'){
+ ; # escaped newline
+ }else if(isescapable(c))
+ s[len s] = c;
+ }else if(c)
+ s[len s] = c;
+ }
+ return s;
+}
+
+url(cs: ref Clex): string
+{
+ s: string;
+ c := skipws(cs);
+ if(c != '"' && c != '\''){ # not a quoted string
+ while(c != ' ' && c != '\n' && c != '\'' && c != '"' && c != ')'){
+ s[len s] = c;
+ c = cs.getc();
+ if(c == '\\'){
+ c = cs.getc();
+ if(c < 0){
+ cs.synerr("end of file in url parameter");
+ break;
+ }
+ if(c == ' ' || c == '\'' || c == '"' || c == ')')
+ s[len s] = c;
+ else{
+ cs.synerr("invalid escape sequence in url");
+ s[len s] = '\\';
+ s[len s] = c;
+ }
+ c = cs.getc();
+ }
+ }
+ cs.ungetc(c);
+# if(s == nil)
+# p.synerr("empty parameter to url");
+ }else
+ s = quotedstring(cs, c);
+ if((c = skipws(cs)) != ')'){
+ cs.synerr("unclosed parameter to url");
+ cs.ungetc(c);
+ }
+ return s;
+}
+
+lowercase(s: string): string
+{
+ for(i := 0; i < len s; i++)
+ if((c := s[i]) >= 'A' && c <= 'Z')
+ s[i] = c-'A' + 'a';
+ return s;
+}
diff --git a/appl/lib/w3c/mkfile b/appl/lib/w3c/mkfile
new file mode 100644
index 00000000..90f4d043
--- /dev/null
+++ b/appl/lib/w3c/mkfile
@@ -0,0 +1,17 @@
+<../../../mkconfig
+
+TARG=\
+ css.dis\
+ xpointers.dis\
+
+MODULES=
+
+SYSMODULES= \
+ sys.m\
+ bufio.m\
+ css.m\
+ xpointers.m\
+
+DISBIN=$ROOT/dis/lib/w3c
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/lib/w3c/xpointers.b b/appl/lib/w3c/xpointers.b
new file mode 100644
index 00000000..0d7c231a
--- /dev/null
+++ b/appl/lib/w3c/xpointers.b
@@ -0,0 +1,858 @@
+implement Xpointers;
+
+#
+# Copyright © 2005 Vita Nuova Holdings Oimited
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "xpointers.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+#
+# XPointer framework syntax
+#
+# Pointer ::= Shorthand | SchemeBased
+# Shorthand ::= NCName # from [XML-Names]
+# SchemeBased ::= PointerPart (S? PointerPart)*
+# PointerPart ::= SchemeName '(' SchemeData ')'
+# SchemeName ::= QName # from [XML-Names]
+# SchemeData ::= EscapedData*
+# EscapedData ::= NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')'
+# NormalChar ::= UnicodeChar - [()^]
+# UnicodeChar ::= [#x0 - #x10FFFF]
+
+framework(s: string): (string, list of (string, string, string), string)
+{
+ (q, nm, i) := name(s, 0);
+ if(i >= len s){ # Shorthand
+ if(q != nil)
+ return (nil, nil, "shorthand pointer must be unqualified name");
+ if(nm == nil)
+ return (nil, nil, "missing pointer name");
+ return (nm, nil, nil);
+ }
+ # must be SchemeBased
+ l: list of (string, string, string);
+ for(;;){
+ if(nm == nil){
+ if(q != nil)
+ return (nil, nil, sys->sprint("prefix but no local part in name at %d", i));
+ return (nil, nil, sys->sprint("expected name at %d", i));
+ }
+ if(i >= len s || s[i] != '(')
+ return (nil, nil, sys->sprint("expected '(' at %d", i));
+ o := i++;
+ a := "";
+ nesting := 0;
+ for(; i < len s && ((c := s[i]) != ')' || nesting); i++){
+ case c {
+ '^' =>
+ if(i+1 >= len s)
+ return (nil, nil, "unexpected eof after ^");
+ c = s[++i];
+ if(c != '(' && c != ')' && c != '^')
+ return (nil, nil, sys->sprint("invalid escape ^%c at %d", c, i));
+ '(' =>
+ nesting++;
+ ')' =>
+ if(--nesting < 0)
+ return (nil, nil, sys->sprint("unbalanced ) at %d", i));
+ }
+ a[len a] = c;
+ }
+ if(i >= len s)
+ return (nil, nil, sys->sprint("unbalanced ( at %d", o));
+ l = (q, nm, a) :: l;
+ if(++i == len s)
+ break;
+ while(i < len s && isspace(s[i]))
+ i++;
+ (q, nm, i) = name(s, i);
+ }
+ rl: list of (string, string, string);
+ for(; l != nil; l = tl l)
+ rl = hd l :: rl;
+ return (nil, rl, nil);
+}
+
+isspace(c: int): int
+{
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
+}
+
+#
+# QName ::= (Prefix ':')? LocalPart
+# Prefix ::= NCName
+# LocalPart ::= NCName
+#
+#NCName :: (Oetter | '_') NCNameChar*
+#NCNameChar :: Oetter | Digit | '.' | '-' | '_' | CombiningChar | Extender
+
+name(s: string, o: int): (string, string, int)
+{
+ (ns, i) := ncname(s, o);
+ if(i >= len s || s[i] != ':')
+ return (nil, ns, i);
+ (nm, j) := ncname(s, i+1);
+ if(j == i+1)
+ return (nil, ns, i); # assume it's a LocalPart followed by ':'
+ return (ns, nm, j);
+}
+
+ncname(s: string, o: int): (string, int)
+{
+ if(o >= len s || !isalnum(c := s[o]) && c != '_' || c >= '0' && c <= '9')
+ return (nil, o); # missing or invalid start character
+ for(i := o; i < len s && isnamec(s[i]); i++)
+ ;
+ return (s[o:i], i);
+}
+
+isnamec(c: int): int
+{
+ return isalnum(c) || c == '_' || c == '-' || c == '.';
+}
+
+isalnum(c: int): int
+{
+ #
+ # Hard to get absolutely right without silly amount of character data.
+ # Use what we know about ASCII
+ # and assume anything above the Oatin control characters is
+ # potentially an alphanumeric.
+ #
+ if(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9')
+ return 1; # usual case
+ if(c <= ' ')
+ return 0;
+ if(c > 16rA0)
+ return 1; # non-ASCII
+ return 0;
+}
+
+# schemes: xpointer(), xmlns(), element()
+
+# xmlns()
+# XmlnsSchemeData ::= NCName S? '=' S? EscapedNamespaceName
+# EscapedNamespaceName ::= EscapedData*
+
+xmlns(s: string): (string, string, string)
+{
+ (nm, i) := ncname(s, 0);
+ if(nm == nil)
+ return (nil, nil, "illegal namespace name");
+ while(i < len s && isspace(s[i]))
+ i++;
+ if(i >= len s || s[i++] != '=')
+ return (nil, nil, "illegal xmlns declaration");
+ while(i < len s && isspace(s[i]))
+ i++;
+ return (nm, s[i:], nil);
+}
+
+# element()
+# ElementSchemeData ::= (NCName ChildSequence?) | ChildSequence
+# ChildSequence ::= ('/' [1-9] [0-9]*)+
+
+element(s: string): (string, list of int, string)
+{
+ nm: string;
+ i := 0;
+ if(s != nil && s[0] != '/'){
+ (nm, i) = ncname(s, 0);
+ if(nm == nil)
+ return (nil, nil, "illegal element name");
+ }
+ l: list of int;
+ do{
+ if(i >= len s || s[i++] != '/')
+ return (nil, nil, "illegal child sequence (expected '/')");
+ v := 0;
+ do{
+ if(i >= len s || !isdigit(s[i]))
+ return (nil, nil, "illegal child sequence (expected integer)");
+ v = v*10 + s[i]-'0';
+ }while(++i < len s && s[i] != '/');
+ l = v :: l;
+ }while(i < len s);
+ rl: list of int;
+ for(; l != nil; l = tl l)
+ rl = hd l :: rl;
+ return (nm, rl, nil);
+}
+
+# xpointer()
+# XpointerSchemeData ::= Expr # from Xpath, with new functions and data types
+
+xpointer(s: string): (ref Xpath, string)
+{
+ p := ref Parse(ref Rd(s, 0, 0), nil);
+ {
+ e := expr(p, 0);
+ if(p.r.i < len s)
+ synerr("missing operator");
+ return (e, nil);
+ }exception e{
+ "syntax error*" =>
+ return (nil, e);
+ * =>
+ raise;
+ }
+}
+
+Lerror, Ldslash, Lint, Lreal, Llit, Lvar, Ldotdot, Lop, Laxis, Lfn: con 'a'+iota; # internal lexical items
+
+Keywd: adt {
+ name: string;
+ val: int;
+};
+
+axes: array of Keywd = array[] of {
+ ("ancestor", Aancestor),
+ ("ancestor-or-self", Aancestor_or_self),
+ ("attribute", Aattribute),
+ ("child", Achild),
+ ("descendant", Adescendant),
+ ("descendant-or-self", Adescendant_or_self),
+ ("following", Afollowing),
+ ("following-sibling", Afollowing_sibling),
+ ("namespace", Anamespace),
+ ("parent", Aparent),
+ ("preceding", Apreceding),
+ ("preceding-sibling", Apreceding_sibling),
+ ("self", Aself),
+};
+
+keywds: array of Keywd = array[] of {
+ ("and", Oand),
+ ("comment", Onodetype),
+ ("div", Odiv),
+ ("mod", Omod),
+ ("node", Onodetype),
+ ("or", Oor),
+ ("processing-instruction", Onodetype),
+ ("text", Onodetype),
+};
+
+iskeywd(s: string): int
+{
+ return look(keywds, s);
+}
+
+look(k: array of Keywd, s: string): int
+{
+ for(i := 0; i < len k; i++)
+ if(k[i].name == s)
+ return k[i].val;
+ return 0;
+}
+
+lookname(k: array of Keywd, v: int): string
+{
+ for(i := 0; i < len k; i++)
+ if(k[i].val == v)
+ return k[i].name;
+ return nil;
+}
+
+prectab := array[] of {
+ array[] of {Oor},
+ array[] of {Oand},
+ array[] of {'=', One},
+ array[] of {'<', Ole, '>', Oge},
+ array[] of {'+', '-'},
+ array[] of {Omul, Odiv, Omod},
+ array[] of {Oneg}, # unary '-'
+ array[] of {'|'}, # UnionExpr
+};
+
+isop(t: int, p: array of int): int
+{
+ if(t >= 0)
+ for(j := 0; j < len p; j++)
+ if(t == p[j])
+ return 1;
+ return 0;
+}
+
+# Expr ::= OrExpr
+# UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
+# PathExpr ::= LocationPath | FilterExpr | FilterExpr '/' RelativeLocationPath |
+# FilterExpr '//' RelativeLocationPath
+# OrExpr ::= AndExpr | OrExpr 'or' AndExpr
+# AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
+# EqualityExpr ::= RelationalExpr | EqualityExpr '=' RelationalExpr | EqualityExpr '!=' RelationalExpr
+# RelationalExpr ::= AdditiveExpr | RelationalExpr '<' AdditiveExpr | RelationalExpr '>' AdditiveExpr |
+# RelationalExpr '<=' AdditiveExpr | RelationalExpr '>=' AdditiveExpr
+# AdditiveExpr ::= MultiplicativeExpr | AdditiveExpr '+' MultiplicativeExpr | AdditiveExpr '-' MultiplicativeExpr
+# MultiplicativeExpr ::= UnaryExpr | MultiplicativeExpr MultiplyOperator UnaryExpr |
+# MultiplicativeExpr 'div' UnaryExpr | MultiplicativeExpr 'mod' UnaryExpr
+# UnaryExpr ::= UnionExpr | '-' UnaryExpr
+
+expr(p: ref Parse, k: int): ref Xpath
+{
+ if(k >= len prectab)
+ return pathexpr(p);
+ if(prectab[k][0] == Oneg){ # unary '-'
+ if(p.look() == '-'){
+ p.get();
+ return ref Xpath.E(Oneg, expr(p,k+1), nil);
+ }
+ # must be UnionExpr
+ k++;
+ }
+ e := expr(p, k+1);
+ while(isop(p.look(), prectab[k])){
+ o := p.get().t0;
+ e = ref Xpath.E(o, e, expr(p, k+1)); # +assoc[k]
+ }
+ return e;
+}
+
+# PathExpr ::= LocationPath | FilterExpr ( ('/' | '//') RelativeLocationPath )
+# FilterExpr ::= PrimaryExpr | FilterExpr Predicate => PrimaryExpr Predicate*
+
+pathexpr(p: ref Parse): ref Xpath
+{
+ # LocationPath?
+ case p.look() {
+ '.' or Ldotdot or Laxis or '@' or Onametest or Onodetype or '*' =>
+ return locationpath(p, 0);
+ '/' or Ldslash =>
+ return locationpath(p, 1);
+ }
+ # FilterExpr
+ e := primary(p);
+ while(p.look() == '[')
+ e = ref Xpath.E(Ofilter, e, predicate(p));
+ if((o := p.look()) == '/' || o == Ldslash)
+ e = ref Xpath.E(Opath, e, locationpath(p, 0));
+ return e;
+}
+
+# LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
+# AbsoluteLocationPath ::= '/' RelativeLocationPath? | AbbreviatedAbsoluteLocationPath
+# RelativeLocationPath ::= Step | RelativeLocationPath '/' Step
+# AbbreviatedAbsoluteLocationPath ::= '//' RelativeLocationPath
+# AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
+
+locationpath(p: ref Parse, abs: int): ref Xpath
+{
+ # // => /descendent-or-self::node()/
+ pl: list of ref Xstep;
+ o := p.look();
+ if(o != '/' && o != Ldslash){
+ s := step(p);
+ if(s == nil)
+ synerr("expected Step in LocationPath");
+ pl = s :: pl;
+ }
+ while((o = p.look()) == '/' || o == Ldslash){
+ p.get();
+ if(o == Ldslash)
+ pl = ref Xstep(Adescendant_or_self, Onodetype, nil, "node", nil, nil) :: pl;
+ s := step(p);
+ if(s == nil){
+ if(abs && pl == nil)
+ break; # it's just an initial '/'
+ synerr("expected Step in LocationPath");
+ }
+ pl = s :: pl;
+ }
+ return ref Xpath.Path(abs, rev(pl));
+}
+
+# Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep
+# AxisSpecifier ::= AxisName '::' | AbbreviatedAxisSpecifier
+# AxisName := ... # long list
+# NodeTest ::= NameTest | NodeType '(' ')'
+# Predicate ::= '[' PredicateExpr ']'
+# PredicateExpr ::= Expr
+# AbbreviatedStep ::= '.' | '..'
+# AbbreviatedAxisSpecifier ::= '@'?
+
+step(p: ref Parse): ref Xstep
+{
+ # AxisSpecifier ... | AbbreviatedStep
+ (o, ns, nm) := p.get();
+ axis := Achild;
+ case o {
+ '.' =>
+ return ref Xstep(Aself, Onodetype, nil, "node", nil, nil); # self::node()
+ Ldotdot =>
+ return ref Xstep(Aparent, Onodetype, nil, "node", nil, nil); # parent::node()
+ Laxis =>
+ axis = look(axes, ns);
+ (o, ns, nm) = p.get();
+ '@' =>
+ axis = Aattribute;
+ (o, ns, nm) = p.get();
+ * =>
+ ;
+ }
+
+ if(o == '*'){
+ o = Onametest;
+ nm = "*";
+ ns = nil;
+ }
+
+ # NodeTest ::= NameTest | NodeType '(' ')'
+ if(o != Onametest && o != Onodetype){
+ p.unget((o, ns, nm));
+ return nil;
+ }
+
+ arg: string;
+ if(o == Onodetype){ # '(' ... ')'
+ expect(p, '(');
+ # grammar is wrong: processing-instruction can have optional literal
+ if(nm == "processing-instruction" && p.look() == Llit)
+ arg = p.get().t1;
+ expect(p, ')');
+ }
+
+ # Predicate*
+ pl: list of ref Xpath;
+ while((pe := predicate(p)) != nil)
+ pl = pe :: pl;
+ return ref Xstep(axis, o, ns, nm, arg, rev(pl));
+}
+
+# PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall
+# FunctionCall ::= FunctionName '(' (Argument ( ',' Argument)*)? ')'
+# Argument ::= Expr
+
+primary(p: ref Parse): ref Xpath
+{
+ (o, ns, nm) := p.get();
+ case o {
+ Lvar =>
+ return ref Xpath.Var(ns, nm);
+ '(' =>
+ e := expr(p, 0);
+ expect(p, ')');
+ return e;
+ Llit =>
+ return ref Xpath.Str(ns);
+ Lint =>
+ return ref Xpath.Int(big ns);
+ Lreal =>
+ return ref Xpath.Real(real ns);
+ Lfn =>
+ expect(p, '(');
+ al: list of ref Xpath;
+ if(p.look() != ')'){
+ for(;;){
+ al = expr(p, 0) :: al;
+ if(p.look() != ',')
+ break;
+ p.get();
+ }
+ al = rev(al);
+ }
+ expect(p, ')');
+ return ref Xpath.Fn(ns, nm, al);
+ * =>
+ synerr("invalid PrimaryExpr");
+ return nil;
+ }
+}
+
+# Predicate ::= '[' PredicateExpr ']'
+# PredicateExpr ::= Expr
+
+predicate(p: ref Parse): ref Xpath
+{
+ l := p.get();
+ if(l.t0 != '['){
+ p.unget(l);
+ return nil;
+ }
+ e := expr(p, 0);
+ expect(p, ']');
+ return e;
+}
+
+expect(p: ref Parse, t: int)
+{
+ l := p.get();
+ if(l.t0 != t)
+ synerr(sys->sprint("expected '%c'", t));
+}
+
+Xpath.text(e: self ref Xpath): string
+{
+ if(e == nil)
+ return "nil";
+ pick r := e {
+ E =>
+ if(r.r == nil)
+ return sys->sprint("(%s%s)", opname(r.op), r.l.text());
+ if(r.op == Ofilter)
+ return sys->sprint("%s[%s]", r.l.text(), r.r.text());
+ return sys->sprint("(%s%s%s)", r.l.text(), opname(r.op), r.r.text());
+ Fn =>
+ a := "";
+ for(l := r.args; l != nil; l = tl l)
+ a += sys->sprint(",%s", (hd l).text());
+ if(a != "")
+ a = a[1:];
+ return sys->sprint("%s(%s)", qual(r.ns, r.name), a);
+ Var =>
+ return sys->sprint("$%s", qual(r.ns, r.name));
+ Path =>
+ if(r.abs)
+ t := "/";
+ else
+ t = "";
+ for(l := r.steps; l != nil; l = tl l){
+ if(t != nil && t != "/")
+ t += "/";
+ t += (hd l).text();
+ }
+ return t;
+ Int =>
+ return sys->sprint("%bd", r.val);
+ Real =>
+ return sys->sprint("%g", r.val);
+ Str =>
+ return sys->sprint("%s", str(r.s));
+ }
+}
+
+qual(ns: string, nm: string): string
+{
+ if(ns != nil)
+ return ns+":"+nm;
+ return nm;
+}
+
+str(s: string): string
+{
+ for(i := 0; i < len s; i++)
+ if(s[i] == '\'')
+ return sys->sprint("\"%s\"", s);
+ return sys->sprint("'%s'", s);
+}
+
+opname(o: int): string
+{
+ case o {
+ One => return "!=";
+ Ole => return "<=";
+ Oge => return ">=";
+ Omul => return "*";
+ Odiv => return " div ";
+ Omod => return " mod ";
+ Oand => return " and ";
+ Oor => return " or ";
+ Oneg => return "-";
+ Ofilter => return " op_filter ";
+ Opath => return "/";
+ * => return sys->sprint(" %c ", o);
+ }
+}
+
+Xstep.text(s: self ref Xstep): string
+{
+ t := sys->sprint("%s::", Xstep.axisname(s.axis));
+ case s.op {
+ Onametest =>
+ if(s.ns == "*" && s.name == "*")
+ t += "*";
+ else
+ t += qual(s.ns, s.name);
+ Onodetype =>
+ if(s.arg != nil)
+ t += sys->sprint("%s(%s)", s.name, str(s.arg));
+ else
+ t += sys->sprint("%s()", s.name);
+ }
+ for(l := s.preds; l != nil; l = tl l)
+ t += sys->sprint("[%s]", (hd l).text());
+ return t;
+}
+
+Xstep.axisname(n: int): string
+{
+ return lookname(axes, n);
+}
+
+# ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::' |
+# NameTest | NodeType | Operator | FunctionName | AxisName |
+# Literal | Number | VariableReference
+# Operator ::= OperatorName | MultiplyOperator | '/' | '//' | '|' | '+' | '' | '=' | '!=' | '<' | '<=' | '>' | '>='
+# MultiplyOperator ::= '*'
+# FunctionName ::= QName - NodeType
+# VariableReference ::= '$' QName
+# NameTest ::= '*' | NCName ':' '*' | QName
+# NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node'
+#
+
+Lex: type (int, string, string);
+
+Parse: adt {
+ r: ref Rd;
+ pb: list of Lex; # push back
+
+ look: fn(p: self ref Parse): int;
+ get: fn(p: self ref Parse): Lex;
+ unget: fn(p: self ref Parse, t: Lex);
+};
+
+Parse.get(p: self ref Parse): Lex
+{
+ if(p.pb != nil){
+ h := hd p.pb;
+ p.pb = tl p.pb;
+ return h;
+ }
+ return lex(p.r);
+}
+
+Parse.look(p: self ref Parse): int
+{
+ t := p.get();
+ p.unget(t);
+ return t.t0;
+}
+
+Parse.unget(p: self ref Parse, t: Lex)
+{
+ p.pb = t :: p.pb;
+}
+
+lex(r: ref Rd): Lex
+{
+ l := lex0(r);
+ r.prev = l.t0;
+ return l;
+}
+
+# disambiguating rules are D1 to D3
+
+# D1. preceding token p && p not in {'@', '::', '(', '[', ',', Operator} then '*' is MultiplyOperator
+# and NCName must be OperatorName
+
+xop(t: int): int
+{
+ case t {
+ -1 or 0 or '@' or '(' or '[' or ',' or Lop or Omul or
+ '/' or Ldslash or '|' or '+' or '-' or '=' or One or '<' or Ole or '>' or Oge or
+ Oand or Oor or Omod or Odiv or Laxis =>
+ return 0;
+ }
+ return 1;
+}
+
+# UnaryExpr ::= UnionExpr | '-' UnaryExpr
+# ExprToken ::= ... |
+# NameTest | NodeType | Operator | FunctionName | AxisName |
+# Literal | Number | VariableReference
+# Operator ::= OperatorName | MultiplyOperator | '/' | '//' | '|' | '+' | '' | '=' | '!=' | '<' | '<=' | '>' | '>='
+# MultiplyOperator ::= '*'
+
+lex0(r: ref Rd): Lex
+{
+ while(isspace(r.look()))
+ r.get();
+ case c := r.get() {
+ -1 or
+ '(' or ')' or '[' or ']' or '@' or ',' or '+' or '-' or '|' or '=' or ':' =>
+ # singletons ('::' only valid after name, see below)
+ return (c, nil, nil);
+ '/' =>
+ return subseq(r, '/', Ldslash, '/');
+ '!' =>
+ return subseq(r, '=', One, '!');
+ '<' =>
+ return subseq(r, '=', Ole, '<');
+ '>' =>
+ return subseq(r, '=', Oge, '>');
+ '*' =>
+ if(xop(r.prev))
+ return (Omul, nil, nil);
+ return (c, nil, nil);
+ '.' =>
+ case r.look() {
+ '0' to '9' =>
+ (v, nil) := number(r, r.get());
+ return (Lreal, v, nil);
+ '.' =>
+ r.get();
+ return (Ldotdot, nil, nil);
+ * =>
+ return ('.', nil, nil);
+ }
+ '$' =>
+ # variable reference
+ (ns, nm, i) := name(r.s, r.i);
+ if(ns == nil && nm == nil)
+ return (Lerror, nil, nil);
+ r.i = i;
+ return (Lvar, ns, nm);
+ '0' to '9' =>
+ (v, f) := number(r, c);
+ if(f)
+ return (Lreal, v, nil);
+ return (Lint, v, nil);
+ '"' or '\'' =>
+ return (Llit, literal(r, c), nil);
+ * =>
+ if(isalnum(c) || c == '_'){
+ # QName/NCName
+ r.unget();
+ (ns, nm, i) := name(r.s, r.i);
+ if(ns == nil && nm == nil)
+ return (Lerror, nil, nil);
+ r.i = i;
+ if(xop(r.prev)){
+ if(ns == nil){
+ o := iskeywd(nm);
+ if(o != Laxis && o != Onodetype)
+ return (o, nil, nil);
+ }
+ return (Lop, ns, nm);
+ }
+ while(isspace(r.look()))
+ r.get();
+ case r.look() {
+ '(' => # D2: NCName '(' =>NodeType or FunctionName
+ if(ns == nil && iskeywd(nm) == Onodetype)
+ return (Onodetype, nil, nm);
+ return (Lfn, ns, nm); # possibly NodeTest
+ ':' => # D3: NCName '::' => AxisName
+ r.get();
+ case r.look() {
+ ':' =>
+ if(ns == nil && look(axes, nm) != 0){
+ r.get();
+ return (Laxis, nm, nil);
+ }
+ '*' =>
+ # NameTest ::= ... | NCName ':' '*'
+ if(ns == nil){
+ r.get();
+ return (Onametest, nm, "*");
+ }
+ }
+ r.unget(); # put back the ':'
+ # NameTest ::= '*' | NCName ':' '*' | QName
+ }
+ return (Onametest, ns, nm); # actually NameTest
+ }
+ # unexpected character
+ }
+ return (Lerror, nil, nil);
+}
+
+subseq(r: ref Rd, a: int, t: int, e: int): Lex
+{
+ if(r.look() != a)
+ return (e, nil, nil);
+ r.get();
+ return (t, nil, nil);
+}
+
+# Literal ::= '"'[^"]*'"' | "'"[^']* "'"
+
+literal(r: ref Rd, delim: int): string
+{
+ s: string;
+ while((c := r.get()) != delim){
+ if(c < 0){
+ synerr("missing string terminator");
+ return s;
+ }
+ if(c)
+ s[len s] = c; # could slice r.s
+ }
+ return s;
+}
+
+#
+# Number ::= Digits('.' Digits?)? | '.' Digits
+# Digits ::= [0-9]+
+#
+number(r: ref Rd, c: int): (string, int)
+{
+ s: string;
+ for(; isdigit(c); c = r.get())
+ s[len s] = c;
+ if(c != '.'){
+ if(c >= 0)
+ r.unget();
+ return (s, 0);
+ }
+ if(!isdigit(c = r.get())){
+ if(c >= 0)
+ r.unget();
+ r.unget(); # the '.'
+ return (s, 0);
+ }
+ s[len s] = '.';
+ do{
+ s[len s] = c;
+ }while(isdigit(c = r.get()));
+ if(c >= 0)
+ r.unget();
+ return (s, 1);
+}
+
+isdigit(c: int): int
+{
+ return c>='0' && c<='9';
+}
+
+Rd: adt{
+ s: string;
+ i: int;
+ prev: int; # previous token
+
+ get: fn(r: self ref Rd): int;
+ look: fn(r: self ref Rd): int;
+ unget: fn(r: self ref Rd);
+};
+
+Rd.get(r: self ref Rd): int
+{
+ if(r.i >= len r.s)
+ return -1;
+ return r.s[r.i++];
+}
+
+Rd.look(r: self ref Rd): int
+{
+ if(r.i >= len r.s)
+ return -1;
+ return r.s[r.i];
+}
+
+Rd.unget(r: self ref Rd)
+{
+ if(r.i > 0)
+ r.i--;
+}
+
+rev[T](l: list of T): list of T
+{
+ rl: list of T;
+ for(; l != nil; l = tl l)
+ rl = hd l :: rl;
+ return rl;
+}
+
+synerr(s: string)
+{
+ raise "syntax error: "+s;
+}
+
+# to do:
+# dictionary?
diff --git a/appl/lib/wait.b b/appl/lib/wait.b
new file mode 100644
index 00000000..f24be0f2
--- /dev/null
+++ b/appl/lib/wait.b
@@ -0,0 +1,55 @@
+implement Wait;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "wait.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+read(fd: ref Sys->FD): (int, string, string)
+{
+ buf := array[2*Sys->WAITLEN] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n <= 0)
+ return (n, nil, sys->sprint("%r"));
+ return parse(string buf[0:n]);
+}
+
+monitor(fd: ref Sys->FD): (int, chan of (int, string, string))
+{
+ pid := chan of int;
+ out := chan of (int, string, string);
+ spawn waitreader(fd, pid, out);
+ return (<-pid, out);
+}
+
+waitreader(fd: ref Sys->FD, pid: chan of int, out: chan of (int, string, string))
+{
+ pid <-= sys->pctl(0, nil);
+ for(;;){
+ (child, modname, status) := read(fd);
+ out <-= (child, modname, status);
+ if(child <= 0)
+ break; # exit on error
+ }
+}
+
+parse(status: string): (int, string, string)
+{
+ for (i := 0; i < len status; i++)
+ if (status[i] == ' ')
+ break;
+ j := i+2; # skip space and "
+ for (i = j; i < len status; i++)
+ if (status[i] == '"')
+ break;
+ return (int status, status[j:i], status[i+2:]);
+}
diff --git a/appl/lib/watchvars.b b/appl/lib/watchvars.b
new file mode 100644
index 00000000..ad6c6947
--- /dev/null
+++ b/appl/lib/watchvars.b
@@ -0,0 +1,44 @@
+implement Watchvars;
+include "watchvars.m";
+
+Watchvar[T].new(v: T): Watchvar
+{
+ e := Watchvar[T](chan[1] of (T, chan of T));
+ e.c <-= (v, chan[1] of T);
+ return e;
+}
+
+Watchvar[T].get(e: self Watchvar): T
+{
+ (v, ic) := <-e.c;
+ e.c <-= (v, ic);
+ return v;
+}
+
+Watchvar[T].set(e: self Watchvar, v: T)
+{
+ (ov, ic) := <-e.c;
+ ic <-= v;
+ e.c <-= (v, chan[1] of T);
+}
+
+Watchvar[T].wait(e: self Watchvar): T
+{
+ (v, ic) := <-e.c;
+ e.c <-= (v, ic);
+ v = <-ic;
+ ic <-= v;
+ return v;
+}
+
+Watchvar[T].waitc(e: self Watchvar): (T, chan of T)
+{
+ vic := <-e.c;
+ e.c <-= vic;
+ return vic;
+}
+
+Watchvar[T].waited(nil: self Watchvar, ic: chan of T, v: T)
+{
+ ic <-= v;
+}
diff --git a/appl/lib/winplace.b b/appl/lib/winplace.b
new file mode 100644
index 00000000..ed454737
--- /dev/null
+++ b/appl/lib/winplace.b
@@ -0,0 +1,359 @@
+implement Winplace;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited
+#
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Rect, Point: import draw;
+include "winplace.m";
+
+Delta: adt {
+ d: int; # +1 or -1
+ wid: int; # index into wr
+ coord: int; # x/y coord
+};
+
+EW, NS: con iota;
+Lay: adt {
+ d: int;
+ x: fn(l: self Lay, p: Point): int;
+ y: fn(l: self Lay, p: Point): int;
+ mkr: fn(l: self Lay, r: Rect): Rect;
+};
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+}
+
+place(wins: list of Rect, scr, lastrect: Rect, minsize: Point): Rect
+{
+ found := find(wins, scr);
+ if(found != nil){
+ # first look for any spaces big enough to hold minsize;
+ # choose top-left of those available.
+ (ok, best) := findfit(found, minsize);
+ if (ok){
+ if(minsize.x == 0)
+ return best;
+ return (best.min, best.min.add(minsize));
+ }
+ if(minsize.x == 0)
+ minsize = scr.size().div(2);
+ }
+ # no big enough space; try to avoid covering titlebars
+ tfound := find(titlebarrects(wins), scr);
+ (ok, best) := findfit(tfound, minsize);
+ if (ok)
+ return (best.min, best.min.add(minsize));
+ tfound = nil;
+
+ # no areas available - just find somewhere.
+ if(found == nil)
+ return somespace(scr, lastrect, minsize);
+
+ # no big enough space found; find the largest area available
+ # that will fit within minsize
+ best = clipsize(hd found, minsize);
+ area := best.dx() * best.dy();
+ for (fl := tl found; fl != nil; fl = tl fl) {
+ r := clipsize(hd fl, minsize);
+ rarea := r.dx() * r.dy();
+ if (rarea > area || (rarea == area && better(r, best)))
+ (area, best) = (rarea, r);
+ }
+ best.max = best.min.add(minsize);
+ return checkrect(best, scr);
+}
+
+findfit(found: list of Rect, minsize: Point): (int, Rect)
+{
+ best: Rect;
+ ok := 0;
+ for (fl := found; fl != nil; fl = tl fl) {
+ r := hd fl;
+ if (r.dx() < minsize.x || r.dy() < minsize.y)
+ continue;
+ if (!ok || better(r, best)) {
+ best = r;
+ ok++;
+ }
+ }
+ return (ok, best);
+}
+
+TBARWIDTH: con 100;
+TBARHEIGHT: con 20;
+titlebarrects(rl: list of Rect): list of Rect
+{
+ nl: list of Rect;
+ for (; rl != nil; rl = tl rl) {
+ r := hd rl;
+ tr := Rect((r.max.x - TBARWIDTH, r.min.y),
+ (r.max.x, r.min.y + TBARHEIGHT));
+ if (tr.min.x < r.min.x)
+ tr.min.x = r.min.x;
+ if (tr.max.y > r.max.y)
+ tr.max.y = r.max.y;
+ nl = tr :: nl;
+ }
+ return nl;
+}
+
+somespace(scr, lastrect: Rect, minsize: Point): Rect
+{
+ r := Rect(lastrect.min, lastrect.min.add(minsize)).addpt((20, 20));
+ if (r.max.x > scr.max.x || r.max.y > scr.max.y)
+ r = Rect(scr.min, scr.min.add(minsize));
+ return r;
+}
+
+checkrect(r, scr: Rect): Rect
+{
+ # make sure it's all on screen
+ if (r.max.x > scr.max.x) {
+ dx := r.max.x - scr.max.x;
+ r.max.x -= dx;
+ r.min.x -= dx;
+ }
+ if (r.max.y > scr.max.y) {
+ dy := r.max.y - scr.max.y;
+ r.max.y -= dy;
+ r.min.y -= dy;
+ }
+
+ # make sure origin is on screen.
+ off := r.min.sub(scr.min);
+ if (off.x > 0)
+ off.x = 0;
+ if (off.y > 0)
+ off.y = 0;
+ r = r.subpt(off);
+ return r;
+}
+
+# return true if r1 is ``better'' placed than r2, all other things
+# being equal.
+# currently we choose top-most, left-most, in that order.
+better(r1, r2: Rect): int
+{
+ return r1.min.y < r2.min.y ||
+ (r1.min.y == r2.min.y && r1.min.x < r2.min.x);
+}
+
+clipsize(r: Rect, size: Point): Rect
+{
+ if (r.dx() > size.x)
+ r.max.x = r.min.x + size.x;
+ if (r.dy() > size.y)
+ r.max.y = r.min.y + size.y;
+ return r;
+}
+
+find(wins: list of Rect, scr: Rect): list of Rect
+{
+
+ n := len wins + 4;
+ wr := array[n] of Rect;
+ for (; wins != nil; wins = tl wins)
+ wr[--n] = hd wins;
+ scr2 := scr.inset(-1);
+ # border sentinels
+ wr[3] = Rect((scr.min.x,scr2.min.y), (scr.max.x, scr.min.y)); # top
+ wr[2] = Rect((scr2.min.x, scr2.min.y), (scr.min.x, scr2.max.y)); # left
+ wr[1] = Rect((scr.min.x, scr.max.y), (scr.max.x, scr2.max.y)); # bottom
+ wr[0] = Rect((scr.max.x, scr2.min.y), (scr2.max.x, scr2.max.y)); # right
+ found := sweep(wr, Lay(EW), nil);
+ return sweep(wr, Lay(NS), found);
+}
+
+sweep(wr: array of Rect, lay: Lay, found: list of Rect): list of Rect
+{
+ # sweep through in the direction of lay,
+ # adding and removing end points of rectangles
+ # as we pass them, and maintaining list of current viable rectangles.
+ maj := sortcoords(wr, lay);
+ (cr, ncr) := (array[len wr * 2] of Delta, 0);
+ rl: list of Rect; # ordered by lay.y(min)
+ for (i := 0; i < len maj; i++) {
+ wid := maj[i].wid;
+ if (maj[i].d > 0)
+ ncr = addwin(cr, ncr, wid, lay.y(wr[wid].min), lay.y(wr[wid].max));
+ else
+ ncr = removewin(cr, ncr, wid, lay.y(wr[wid].min), lay.y(wr[wid].max));
+ nrl: list of Rect = nil;
+ count := 0;
+ for (j := 0; j < ncr - 1; j++) {
+ count += cr[j].d;
+ (start, end) := (cr[j].coord, cr[j+1].coord);
+ if (count == 0 && end > start) {
+ nf: list of Rect;
+ (rl, nrl, nf) = select(rl, nrl, maj[i].coord, start, end);
+ for (; nf != nil; nf = tl nf)
+ found = addfound(found, lay.mkr(hd nf));
+ }
+ }
+ for (; rl != nil; rl = tl rl) {
+ r := hd rl;
+ r.max.x = maj[i].coord;
+ found = addfound(found, lay.mkr(r));
+ }
+ for (; nrl != nil; nrl = tl nrl)
+ rl = hd nrl :: rl;
+ nrl = nil;
+ }
+ return found;
+}
+
+addfound(found: list of Rect, r: Rect): list of Rect
+{
+ if (r.max.x - r.min.x < 1 ||
+ r.max.y - r.min.y < 1)
+ return found;
+ return r :: found;
+}
+
+select(rl, nrl: list of Rect, xcoord, start, end: int): (list of Rect, list of Rect, list of Rect)
+{
+ found: list of Rect;
+ made := 0;
+ while (rl != nil) {
+ r := hd rl;
+ r.max.x = xcoord;
+ (rstart, rend) := (r.min.y, r.max.y);
+ if (rstart >= end)
+ break;
+ addit := 1;
+ if (rstart == start && rend == end) {
+ made = 1;
+ } else {
+ if (!made && rstart > start) {
+ nrl = ((xcoord, start), (xcoord, end)) :: nrl;
+ made = 1;
+ }
+ if (rend > end || rstart < start) {
+ found = r :: found;
+ if (rend > end)
+ rend = end;
+ if (rstart < start)
+ rstart = start;
+ if (rstart >= rend)
+ addit = 0;
+ (r.min.y, r.max.y) = (rstart, rend);
+ }
+ }
+ if (addit)
+ nrl = r :: nrl;
+ rl = tl rl;
+ }
+ if (!made)
+ nrl = ((xcoord, start), (xcoord, end)) :: nrl;
+ return (rl, nrl, found);
+}
+
+removewin(d: array of Delta, nd: int, wid: int, min, max: int): int
+{
+ minidx := finddelta(d, nd, Delta(+1, wid, min));
+ maxidx := finddelta(d, nd, Delta(-1, wid, max));
+ if (minidx == -1 || maxidx == -1 || minidx == maxidx) {
+ sys->fprint(sys->fildes(2),
+ "bad delta find; minidx: %d; maxidx: %d; wid: %d; min: %d; max: %d\n",
+ minidx, maxidx, wid, min, max);
+ raise "panic";
+ }
+ d[minidx:] = d[minidx + 1:maxidx];
+ d[maxidx - 1:] = d[maxidx + 1:nd];
+ return nd - 2;
+}
+
+addwin(d: array of Delta, nd: int, wid: int, min, max: int): int
+{
+ (minidx, maxidx) := (findcoord(d, nd, min), findcoord(d, nd, max));
+ d[maxidx + 2:] = d[maxidx:nd];
+ d[maxidx + 1] = Delta(-1, wid, max);
+ d[minidx + 1:] = d[minidx:maxidx];
+ d[minidx] = Delta(+1, wid, min);
+ return nd + 2;
+}
+
+finddelta(d: array of Delta, nd: int, df: Delta): int
+{
+ idx := findcoord(d, nd, df.coord);
+ for (i := idx; i < nd && d[i].coord == df.coord; i++)
+ if (d[i].wid == df.wid && d[i].d == df.d)
+ return i;
+ for (i = idx - 1; i >= 0 && d[i].coord == df.coord; i--)
+ if (d[i].wid == df.wid && d[i].d == df.d)
+ return i;
+ return -1;
+}
+
+findcoord(d: array of Delta, nd: int, coord: int): int
+{
+ (lo, hi) := (0, nd - 1);
+ while (lo <= hi) {
+ mid := (lo + hi) / 2;
+ if (coord < d[mid].coord)
+ hi = mid - 1;
+ else if (coord > d[mid].coord)
+ lo = mid + 1;
+ else
+ return mid;
+ }
+ return lo;
+}
+
+sortcoords(wr: array of Rect, lay: Lay): array of Delta
+{
+ a := array[len wr * 2] of Delta;
+ j := 0;
+ for (i := 0; i < len wr; i++) {
+ a[j++] = (+1, i, lay.x(wr[i].min));
+ a[j++] = (-1, i, lay.x(wr[i].max));
+ }
+ sortdelta(a);
+ return a;
+}
+
+sortdelta(a: array of Delta)
+{
+ n := len a;
+ for(m := n; m > 1; ) {
+ if(m < 5)
+ m = 1;
+ else
+ m = (5*m-1)/11;
+ for(i := n-m-1; i >= 0; i--) {
+ tmp := a[i];
+ for(j := i+m; j <= n-1 && tmp.coord > a[j].coord; j += m)
+ a[j-m] = a[j];
+ a[j-m] = tmp;
+ }
+ }
+}
+
+Lay.x(l: self Lay, p: Point): int
+{
+ if (l.d == EW)
+ return p.x;
+ return p.y;
+}
+
+Lay.y(l: self Lay, p: Point): int
+{
+ if (l.d == EW)
+ return p.y;
+ return p.x;
+}
+
+Lay.mkr(l: self Lay, r: Rect): Rect
+{
+ if (l.d == EW)
+ return r;
+ return ((r.min.y, r.min.x), (r.max.y, r.max.x));
+}
diff --git a/appl/lib/wmclient.b b/appl/lib/wmclient.b
new file mode 100644
index 00000000..d205f17c
--- /dev/null
+++ b/appl/lib/wmclient.b
@@ -0,0 +1,346 @@
+implement Wmclient;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited
+#
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Display, Image, Screen, Rect, Point, Pointer, Wmcontext, Context: import draw;
+include "tk.m";
+ tk: Tk;
+ Toplevel: import tk;
+include "wmlib.m";
+ wmlib: Wmlib;
+ qword, splitqword, s2r: import wmlib;
+include "titlebar.m";
+ titlebar: Titlebar;
+include "wmclient.m";
+
+Focusnone, Focusimage, Focustitle: con iota;
+
+Bdup: con int 16rffffffff;
+Bddown: con int 16radadadff;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+ tk = load Tk Tk->PATH;
+ wmlib = load Wmlib Wmlib->PATH;
+ if(wmlib == nil){
+ sys->fprint(sys->fildes(2), "wmclient: cannot load %s: %r\n", Wmlib->PATH);
+ raise "fail:bad module";
+ }
+ wmlib->init();
+ titlebar = load Titlebar Titlebar->PATH;
+ if(titlebar == nil){
+ sys->fprint(sys->fildes(2), "wmclient: cannot load %s: %r\n", Titlebar->PATH);
+ raise "fail:bad module";
+ }
+ titlebar->init();
+}
+
+makedrawcontext(): ref Draw->Context
+{
+ return wmlib->makedrawcontext();
+}
+
+cursorspec(img: ref Draw->Image): string
+{
+ Hex: con "0123456789abcdef";
+ if(img == nil || img.depth != 1)
+ return "cursor";
+ display := img.display;
+ hot := img.r.min;
+ if(img.r.min.x != 0 || img.r.min.y != 0){
+ n := display.newimage(((0, 0), img.r.size()), Draw->GREY1, 0, Draw->Nofill);
+ n.draw(n.r, img, nil, img.r.min);
+ img = n;
+ }
+ s := sys->sprint("cursor %d %d %d %d ", hot.x, hot.y, img.r.dx(), img.r.dy());
+ nb := img.r.dy() * draw->bytesperline(img.r, img.depth);
+ buf := array[nb] of byte;
+ if(img.readpixels(img.r, buf) == -1)
+ return "cursor";
+
+ for(i := 0; i < nb; i++){
+ c := int buf[i];
+ s[len s] = Hex[c >> 4];
+ s[len s] = Hex[c & 16rf];
+ }
+ return s;
+}
+
+blankwin: Window;
+window(ctxt: ref Draw->Context, title: string, buts: int): ref Window
+{
+ w := ref blankwin;
+ w.ctxt = wmlib->connect(ctxt);
+ w.display = ctxt.display;
+ w.ctl = chan of string;
+ readscreenrect(w);
+
+ if(buts & Plain)
+ return w;
+
+ if(ctxt.wm == nil)
+ buts &= ~(Resize|Hide);
+
+ w.bd = 1;
+ w.titlebar = tk->toplevel(ctxt.display, nil);
+ top := w.titlebar;
+ top.wreq = nil;
+
+ w.ctl = titlebar->new(top, buts);
+ titlebar->settitle(top, title);
+ sizetb(w);
+ w.wmctl("fixedorigin");
+ return w;
+}
+
+Window.pointer(w: self ref Window, p: Draw->Pointer): int
+{
+ if(w.screen == nil || w.titlebar == nil)
+ return 0;
+
+ if(p.buttons && (w.ptrfocus == Focusnone || w.buttons == 0)){
+ if(p.xy.in(w.tbrect))
+ w.ptrfocus = Focustitle;
+ else
+ w.ptrfocus = Focusimage;
+ }
+ w.buttons = p.buttons;
+ if(w.ptrfocus == Focustitle){
+ tk->pointer(w.titlebar, p);
+ return 1;
+ }
+ return 0;
+}
+
+# titlebar requested size might have changed:
+# find out what size it's requesting.
+sizetb(w: ref Window)
+{
+ if(w.titlebar == nil)
+ return;
+ w.tbsize = tk->rect(w.titlebar, ".", Tk->Border|Tk->Required).size();
+}
+
+# reshape the image; the space needed for the
+# titlebar is added to r.
+Window.reshape(w: self ref Window, r: Rect)
+{
+ w.r = w.screenr(r);
+ if(w.screen == nil)
+ return;
+ w.wmctl(sys->sprint("!reshape . -1 %s", r2s(w.r)));
+}
+
+putimage(w: ref Window, i: ref Image)
+{
+ if(w.screen != nil && i == w.screen.image)
+ return;
+ w.screen = Screen.allocate(i, w.display.color(Draw->White), 0);
+ ir := i.r.inset(w.bd);
+ if(ir.dx() < 0)
+ ir.max.x = ir.min.x;
+ if(ir.dy() < 0)
+ ir.max.y = ir.min.y;
+ if(w.titlebar != nil){
+ w.tbrect = Rect(ir.min, (ir.max.x, ir.min.y + w.tbsize.y));
+ tbimage := w.screen.newwindow(w.tbrect, Draw->Refnone, Draw->Nofill);
+ tk->putimage(w.titlebar, ".", tbimage, nil);
+ ir.min.y = w.tbrect.max.y;
+ }
+ if(ir.dy() < 0)
+ ir.max.y = ir.min.y;
+ w.image = w.screen.newwindow(ir, Draw->Refnone, Draw->Nofill);
+ drawborder(w);
+ w.r = i.r;
+}
+
+# return a rectangle suitable to hold image r when the
+# titlebar and border are included.
+Window.screenr(w: self ref Window, r: Rect): Rect
+{
+ if(w.titlebar != nil){
+ if(r.dx() < w.tbsize.x)
+ r.max.x = r.min.x + w.tbsize.x;
+ r.min.y -= w.tbsize.y;
+ }
+ return r.inset(-w.bd);
+}
+
+# return the available space inside r when space for
+# border and titlebar is taken away.
+Window.imager(w: self ref Window, r: Rect): Rect
+{
+ r = r.inset(w.bd);
+ if(r.dx() < 0)
+ r.max.x = r.min.x;
+ if(r.dy() < 0)
+ r.max.y = r.min.y;
+ if(w.titlebar != nil){
+ r.min.y += w.tbsize.y;
+ if(r.dy() < 0)
+ r.max.y = r.min.y;
+ }
+ return r;
+}
+
+# draw an imitation tk border.
+drawborder(w: ref Window)
+{
+ if(w.screen == nil)
+ return;
+ col := w.display.color(Bdup);
+ i := w.screen.image;
+ r := w.screen.image.r;
+ i.draw((r.min, (r.min.x+w.bd, r.max.y)), col, nil, (0, 0));
+ i.draw(((r.min.x+w.bd, r.min.y), (r.max.x, r.min.y+w.bd)), col, nil, (0, 0));
+ col = w.display.color(Bddown);
+ i.draw(((r.max.x-w.bd, r.min.y+w.bd), r.max), col, nil, (0, 0));
+ i.draw(((r.min.x+w.bd, r.max.y-w.bd), (r.max.x-w.bd, r.max.y)), col, nil, (0, 0));
+}
+
+readscreenrect(w: ref Window)
+{
+ if((fd := sys->open("/chan/wmrect", Sys->OREAD)) != nil){
+ buf := array[12*4] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n > 0){
+ (w.displayr, nil) = s2r(string buf[0:n], 0);
+ return;
+ }
+ }
+ w.displayr = w.display.image.r;
+}
+
+Window.onscreen(w: self ref Window, how: string)
+{
+ if(how == nil)
+ how = "place";
+ w.wmctl(sys->sprint("!reshape . -1 %s %q", r2s(w.r), how));
+}
+
+Window.startinput(w: self ref Window, devs: list of string)
+{
+ for(; devs != nil; devs = tl devs)
+ w.wmctl(sys->sprint("start %q", hd devs));
+}
+
+# commands originating both from tkclient and wm (via ctl)
+Window.wmctl(w: self ref Window, req: string): string
+{
+ (c, next) := qword(req, 0);
+ case c {
+ "exit" =>
+ sys->fprint(sys->open("/prog/" + string sys->pctl(0, nil) + "/ctl", Sys->OWRITE), "killgrp");
+ exit;
+ # old-style requests: pass them back around in proper form.
+ "move" =>
+ # move x y
+ if(w.titlebar != nil)
+ titlebar->sendctl(w.titlebar, "!move . -1 " + req[next:]);
+ "size" =>
+ if(w.titlebar != nil){
+ minsz := titlebar->minsize(w.titlebar);
+ titlebar->sendctl(w.titlebar, "!size . -1 " + string minsz.x + " " + string minsz.y);
+ }
+ "ok" or
+ "help" =>
+ ;
+ "rect" =>
+ (w.displayr, nil) = s2r(req, next);
+ "haskbdfocus" =>
+ w.focused = int qword(req, next).t0;
+ if(w.titlebar != nil){
+ tk->cmd(w.titlebar, "focus -global " + string w.focused);
+ tk->cmd(w.titlebar, "update");
+ }
+ drawborder(w);
+ "task" =>
+ title := "";
+ if(w.titlebar != nil)
+ title = titlebar->title(w.titlebar);
+ wmreq(w, sys->sprint("task %q", title), next);
+ w.saved = w.r.min;
+ # send window out of the way
+ # XXX oops, can't do this for plain windows...
+ titlebar->sendctl(w.titlebar, "!reshape . -1 " + r2s((w.displayr.max, w.displayr.max.add(w.r.size()))));
+ "untask" =>
+ wmreq(w, req, next);
+ # put window back where it was before.
+ # XXX what do we we do if the window manager window has been reshape in the meantime...?
+ titlebar->sendctl(w.titlebar, "!reshape . -1 " + r2s((w.saved, w.saved.add(w.r.size()))));
+ * =>
+ return wmreq(w, req, next);
+ }
+ return nil;
+}
+
+wmreq(w: ref Window, req: string, e: int): string
+{
+ name: string;
+ if(req != nil && req[0] == '!'){
+ (name, e) = qword(req, e);
+ if(name != ".")
+ return "invalid window name";
+ }
+ if(w.ctxt.connfd != nil){
+ if(sys->fprint(w.ctxt.connfd, "%s", req) == -1)
+ return sys->sprint("%r");
+ if(req[0] == '!')
+ recvimage(w);
+ return nil;
+ }
+ # if we're getting an image and there's no window manager,
+ # then there's only one image to get...
+ if(req[0] == '!')
+ putimage(w, w.ctxt.ctxt.display.image);
+ else{
+ (nil, nil, err) := wmlib->wmctl(w.ctxt, req);
+ return err;
+ }
+ return nil;
+}
+
+recvimage(w: ref Window)
+{
+ i := <-w.ctxt.images;
+ if(i == nil)
+ i = <-w.ctxt.images;
+ putimage(w, i);
+}
+
+Window.settitle(w: self ref Window, title: string): string
+{
+ if(w.titlebar == nil)
+ return nil;
+ oldr := w.imager(w.r);
+ old := titlebar->settitle(w.titlebar, title);
+ sizetb(w);
+ if(w.tbsize.x < w.r.dx())
+ tk->putimage(w.titlebar, ".", w.titlebar.image, nil); # unsuspend the window
+ else
+ w.wmctl("!reshape . -1 " + r2s(w.screenr(oldr)));
+ return old;
+}
+
+snarfget(): string
+{
+ return wmlib->snarfget();
+}
+
+snarfput(buf: string)
+{
+ return wmlib->snarfput(buf);
+}
+
+r2s(r: Rect): string
+{
+ return sys->sprint("%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y);
+}
diff --git a/appl/lib/wmlib.b b/appl/lib/wmlib.b
new file mode 100644
index 00000000..804d532e
--- /dev/null
+++ b/appl/lib/wmlib.b
@@ -0,0 +1,590 @@
+implement Wmlib;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited
+#
+
+# basic window manager functionality, used by
+# tkclient and wmclient to create more usable functionality.
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Display, Image, Screen, Rect, Point, Pointer, Wmcontext, Context: import draw;
+include "wmsrv.m";
+include "wmlib.m";
+
+Client: adt{
+ ptrpid: int;
+ kbdpid: int;
+ ctlpid: int;
+ req: chan of (array of byte, Sys->Rwrite);
+ dir: string;
+ ctlfd: ref Sys->FD;
+ winfd: ref Sys->FD;
+};
+
+DEVWM: con "/mnt/wm";
+Ptrsize: con 1+4*12; # 'm' plus 4 12-byte decimal integers
+
+kbdstarted: int;
+ptrstarted: int;
+wptr: chan of Point; # set mouse position (only if we've opened /dev/pointer directly)
+cswitch: chan of (string, int, chan of string); # switch cursor images (as for wptr)
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+}
+
+# (_screen, dispi) := ctxt.display.getwindow("/dev/winname", nil, nil, 1); XXX corrupts heap... fix it!
+
+makedrawcontext(): ref Draw->Context
+{
+ display := Display.allocate(nil);
+ if(display == nil){
+ sys->fprint(sys->fildes(2), "wmlib: can't allocate Display: %r\n");
+ raise "fail:no display";
+ }
+ return ref Draw->Context(display, nil, nil);
+}
+
+importdrawcontext(devdraw, mntwm: string): (ref Draw->Context, string)
+{
+ if(mntwm == nil)
+ mntwm = "/mnt/wm";
+
+ display := Display.allocate(devdraw);
+ if(display == nil)
+ return (nil, sys->sprint("cannot allocate display: %r"));
+ (ok, nil) := sys->stat(mntwm + "/clone");
+ if(ok == -1)
+ return (nil, "cannot find wm namespace");
+ wc := chan of (ref Draw->Context, string);
+ spawn wmproxy(display, mntwm, wc);
+ return <-wc;
+}
+
+# XXX we have no way of knowing when this process should go away...
+# perhaps a Draw->Context should hold a file descriptor
+# so that we do.
+wmproxy(display: ref Display, dir: string, wc: chan of (ref Draw->Context, string))
+{
+ wmsrv := load Wmsrv Wmsrv->PATH;
+ if(wmsrv == nil){
+ wc <-= (nil, sys->sprint("cannot load %s: %r", Wmsrv->PATH));
+ return;
+ }
+ sys->pctl(Sys->NEWFD, 1 :: 2 :: nil);
+
+ (wm, join, req) := wmsrv->init();
+ if(wm == nil){
+ wc <-= (nil, sys->sprint("%r"));
+ return;
+ }
+ wc <-= (ref Draw->Context(display, nil, wm), nil);
+
+ clients: array of ref Client;
+ for(;;) alt{
+ (sc, rc) := <-join =>
+ sync := chan of (ref Client, string);
+ spawn clientproc(display, sc, dir, sync);
+ (c, err) := <-sync;
+ rc <-= err;
+ if(c != nil){
+ if(sc.id >= len clients)
+ clients = (array[sc.id + 1] of ref Client)[0:] = clients;
+ clients[sc.id] = c;
+ }
+ (sc, data, rc) := <-req =>
+ clients[sc.id].req <-= (data, rc);
+ if(rc == nil)
+ clients[sc.id] = nil;
+ }
+}
+
+zclient: Client;
+clientproc(display: ref Display, sc: ref Wmsrv->Client, dir: string, rc: chan of (ref Client, string))
+{
+ ctlfd := sys->open(dir + "/clone", Sys->ORDWR);
+ if(ctlfd == nil){
+ rc <-= (nil, sys->sprint("cannot open %s/clone: %r", dir));
+ return;
+ }
+ buf := array[20] of byte;
+ n := sys->read(ctlfd, buf, len buf);
+ if(n <= 0){
+ rc <-= (nil, "cannot read ctl id");
+ return;
+ }
+ sys->fprint(ctlfd, "fixedorigin");
+ dir += "/" + string buf[0:n];
+ c := ref zclient;
+ c.req = chan of (array of byte, Sys->Rwrite);
+ c.dir = dir;
+ c.ctlfd = ctlfd;
+ if ((c.winfd = sys->open(dir + "/winname", Sys->OREAD)) == nil){
+ rc <-= (nil, sys->sprint("cannot open %s/winname: %r", dir));
+ return;
+ }
+ rc <-= (c, nil);
+
+ pidc := chan of int;
+ spawn ctlproc(pidc, ctlfd, sc.ctl);
+ c.ctlpid = <-pidc;
+ for(;;) {
+ (data, drc) := <-c.req;
+ if(drc == nil)
+ break;
+ err := handlerequest(display, c, sc, data);
+ n = len data;
+ if(err != nil)
+ n = -1;
+ alt{
+ drc <-= (n, err) =>;
+ * =>;
+ }
+ }
+ sc.stop <-= 1;
+ kill(c.kbdpid, "kill");
+ kill(c.ptrpid, "kill");
+ kill(c.ctlpid, "kill");
+ c.ctlfd = nil;
+ c.winfd = nil;
+}
+
+handlerequest(display: ref Display, c: ref Client, sc: ref Wmsrv->Client, data: array of byte): string
+{
+ req := string data;
+ if(req == nil)
+ return nil;
+ (w, e) := qword(req, 0);
+ case w {
+ "start" =>
+ (w, e) = qword(req, e);
+ case w {
+ "ptr" or
+ "mouse" =>
+ if(c.ptrpid == -1)
+ return "already started";
+ fd := sys->open(c.dir + "/pointer", Sys->OREAD);
+ if(fd == nil)
+ return sys->sprint("cannot open %s: %r", c.dir + "/pointer");
+ sync := chan of int;
+ spawn ptrproc(sync, fd, sc.ptr);
+ c.ptrpid = <-sync;
+ return nil;
+ "kbd" =>
+ if(c.kbdpid == -1)
+ return "already started";
+ sync := chan of (int, string);
+ spawn kbdproc(sync, c.dir + "/keyboard", sc.kbd);
+ (pid, err) := <-sync;
+ c.kbdpid = pid;
+ return err;
+ }
+ }
+
+ if(sys->write(c.ctlfd, data, len data) == -1)
+ return sys->sprint("%r");
+ if(req[0] == '!'){
+ buf := array[100] of byte;
+ n := sys->read(c.winfd, buf, len buf);
+ if(n <= 0)
+ return sys->sprint("read winname: %r");
+ name := string buf[0:n];
+ # XXX this is the dodgy bit...
+ i := display.namedimage(name);
+ if(i == nil)
+ return sys->sprint("cannot get image %#q: %r", name);
+ s := Screen.allocate(i, display.white, 0);
+ i = s.newwindow(i.r, Draw->Refnone, Draw->Nofill);
+ rc := chan of int;
+ sc.images <-= (nil, i, rc);
+ if(<-rc == -1)
+ return "image request already in progress";
+ }
+ return nil;
+}
+
+connect(ctxt: ref Context): ref Wmcontext
+{
+ # don't automatically make a new Draw->Context, 'cos the
+ # client should be aware that there's no wm so multiple
+ # windows won't work correctly.
+ # ... unless there's an exported wm available, of course!
+ if(ctxt == nil){
+ sys->fprint(sys->fildes(2), "wmlib: no draw context\n");
+ raise "fail:error";
+ }
+ if(ctxt.wm == nil){
+ wm := ref Wmcontext(
+ chan of int,
+ chan of ref Draw->Pointer,
+ chan of string,
+ nil, # unused
+ chan of ref Image,
+ nil,
+ ctxt
+ );
+ return wm;
+ }
+ fd := sys->open("/chan/wmctl", Sys->ORDWR);
+ if(fd == nil){
+ sys->fprint(sys->fildes(2), "wmlib: cannot open /chan/wmctl: %r\n");
+ raise "fail:error";
+ }
+ buf := array[32] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0){
+ sys->fprint(sys->fildes(2), "wmlib: cannot get window token: %r\n");
+ raise "fail:error";
+ }
+ reply := chan of (string, ref Wmcontext);
+ ctxt.wm <-= (string buf[0:n], reply);
+ (err, wm) := <-reply;
+ if(err != nil){
+ sys->fprint(sys->fildes(2), "wmlib: cannot connect: %s\n", err);
+ raise "fail:" + err;
+ }
+ wm.connfd = fd;
+ wm.ctxt = ctxt;
+ return wm;
+}
+
+startinput(wm: ref Wmcontext, devs: list of string): string
+{
+ for(; devs != nil; devs = tl devs)
+ wmctl(wm, "start " + hd devs);
+ return nil;
+}
+
+reshape(wm: ref Wmcontext, name: string, r: Draw->Rect, i: ref Draw->Image, how: string): ref Draw->Image
+{
+ if(name == nil)
+ return nil;
+ (nil, ni, err) := wmctl(wm, sys->sprint("!reshape %s -1 %d %d %d %d %s", name, r.min.x, r.min.y, r.max.x, r.max.y, how));
+ if(err == nil)
+ return ni;
+ return i;
+}
+
+#
+# wmctl implements the default window behaviour
+#
+wmctl(wm: ref Wmcontext, request: string): (string, ref Image, string)
+{
+ (w, e) := qword(request, 0);
+ case w {
+ "exit" =>
+ kill(sys->pctl(0, nil), "killgrp");
+ exit;
+ * =>
+ if(wm.connfd != nil){
+ # standard form for requests: if request starts with '!',
+ # then the next word gives the tag of the window that the
+ # request applies to, and a new image is provided.
+ if(sys->fprint(wm.connfd, "%s", request) == -1){
+ sys->fprint(sys->fildes(2), "wmlib: wm request '%s' failed\n", request);
+ return (nil, nil, sys->sprint("%r"));
+ }
+ if(request[0] == '!'){
+ i := <-wm.images;
+ if(i == nil)
+ i = <-wm.images;
+ return (qword(request, e).t0, i, nil);
+ }
+ return (nil, nil, nil);
+ }
+ # requests we can handle ourselves, if we have to.
+ case w{
+ "start" =>
+ (w, e) = qword(request, e);
+ case w{
+ "ptr" or
+ "mouse" =>
+ if(!ptrstarted){
+ fd := sys->open("/dev/pointer", Sys->ORDWR);
+ if(fd != nil)
+ wptr = chan of Point;
+ else
+ fd = sys->open("/dev/pointer", Sys->OREAD);
+ if(fd == nil)
+ return (nil, nil, sys->sprint("cannot open /dev/pointer: %r"));
+ cfd := sys->open("/dev/cursor", Sys->OWRITE);
+ if(cfd != nil)
+ cswitch = chan of (string, int, chan of string);
+ spawn wptrproc(fd, cfd);
+ sync := chan of int;
+ spawn ptrproc(sync, fd, wm.ptr);
+ <-sync;
+ ptrstarted = 1;
+ }
+ "kbd" =>
+ if(!kbdstarted){
+ sync := chan of (int, string);
+ spawn kbdproc(sync, "/dev/keyboard", wm.kbd);
+ (nil, err) := <-sync;
+ if(err != nil)
+ return (nil, nil, err);
+ spawn sendreq(wm.ctl, "haskbdfocus 1");
+ kbdstarted = 1;
+ }
+ * =>
+ return (nil, nil, "unknown input source");
+ }
+ return (nil, nil, nil);
+ "ptr" =>
+ if(wptr == nil)
+ return (nil, nil, "cannot change mouse position");
+ p: Point;
+ (w, e) = qword(request, e);
+ p.x = int w;
+ (w, e) = qword(request, e);
+ p.y = int w;
+ wptr <-= p;
+ return (nil, nil, nil);
+ "cursor" =>
+ if(cswitch == nil)
+ return (nil, nil, "cannot switch cursor");
+ cswitch <-= (request, e, reply := chan of string);
+ return (nil, nil, <-reply);
+ * =>
+ return (nil, nil, "unknown wmctl request");
+ }
+ }
+}
+
+sendreq(c: chan of string, s: string)
+{
+ c <-= s;
+}
+
+ctlproc(sync: chan of int, fd: ref Sys->FD, ctl: chan of string)
+{
+ sync <-= sys->pctl(0, nil);
+ buf := array[4096] of byte;
+ while((n := sys->read(fd, buf, len buf)) > 0)
+ ctl <-= string buf[0:n];
+}
+
+kbdproc(sync: chan of (int, string), f: string, keys: chan of int)
+{
+ sys->pctl(Sys->NEWFD, nil);
+ fd := sys->open(f, Sys->OREAD);
+ if(fd == nil){
+ sync <-= (-1, sys->sprint("cannot open /dev/keyboard: %r"));
+ return;
+ }
+ sync <-= (sys->pctl(0, nil), nil);
+ buf := array[12] of byte;
+ while((n := sys->read(fd, buf, len buf)) > 0){
+ s := string buf[0:n];
+ for(j := 0; j < len s; j++)
+ keys <-= int s[j];
+ }
+}
+
+wptrproc(pfd, cfd: ref Sys->FD)
+{
+ if(wptr == nil && cswitch == nil)
+ return;
+ if(wptr == nil)
+ wptr = chan of Point;
+ if(cswitch == nil)
+ cswitch = chan of (string, int, chan of string);
+ for(;;)alt{
+ p := <-wptr =>
+ sys->fprint(pfd, "m%11d %11d", p.x, p.y);
+ (c, start, reply) := <-cswitch =>
+ buf: array of byte;
+ if(start == len c){
+ buf = array[0] of byte;
+ }else{
+ hot, size: Point;
+ (w, e) := qword(c, start);
+ hot.x = int w;
+ (w, e) = qword(c, e);
+ hot.y = int w;
+ (w, e) = qword(c, e);
+ size.x = int w;
+ (w, e) = qword(c, e);
+ size.y = int w;
+ ((d0, d1), nil) := splitqword(c, e);
+ nb := size.x/8*size.y;
+ if(d1 - d0 != nb * 2){
+ reply <-= "inconsistent cursor image data";
+ break;
+ }
+ buf = array[4*4 + nb] of byte;
+ bplong(buf, 0*4, hot.x);
+ bplong(buf, 1*4, hot.y);
+ bplong(buf, 2*4, size.x);
+ bplong(buf, 3*4, size.y);
+ j := 4*4;
+ for(i := d0; i < d1; i += 2)
+ buf[j++] = byte ((hexc(c[i]) << 4) | hexc(c[i+1]));
+ }
+ if(sys->write(cfd, buf, len buf) != len buf)
+ reply <-= sys->sprint("%r");
+ else
+ reply <-= nil;
+ }
+}
+
+hexc(c: int): int
+{
+ if(c >= '0' && c <= '9')
+ return c - '0';
+ if(c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if(c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return 0;
+}
+
+bplong(d: array of byte, o: int, x: int)
+{
+ d[o] = byte x;
+ d[o+1] = byte (x >> 8);
+ d[o+2] = byte (x >> 16);
+ d[o+3] = byte (x >> 24);
+}
+
+ptrproc(sync: chan of int, fd: ref Sys->FD, ptr: chan of ref Draw->Pointer)
+{
+ sync <-= sys->pctl(0, nil);
+
+ b:= array[Ptrsize] of byte;
+ while(sys->read(fd, b, len b) > 0){
+ p := bytes2ptr(b);
+ if(p != nil)
+ ptr <-= p;
+ }
+}
+
+bytes2ptr(b: array of byte): ref Pointer
+{
+ if(len b < Ptrsize || int b[0] != 'm')
+ return nil;
+ x := int string b[1:13];
+ y := int string b[13:25];
+ but := int string b[25:37];
+ msec := int string b[37:49];
+ return ref Pointer (but, (x, y), msec);
+}
+
+snarfbuf: string; # at least we get *something* when there's no wm.
+
+snarfget(): string
+{
+ fd := sys->open("/chan/snarf", sys->OREAD);
+ if(fd == nil)
+ return snarfbuf;
+
+ buf := array[8192] of byte;
+ nr := 0;
+ while ((n := sys->read(fd, buf[nr:], len buf - nr)) > 0) {
+ nr += n;
+ if (nr == len buf) {
+ nbuf := array[len buf * 2] of byte;
+ nbuf[0:] = buf;
+ buf = nbuf;
+ }
+ }
+ return string buf[0:nr];
+}
+
+snarfput(buf: string)
+{
+ fd := sys->open("/chan/snarf", sys->OWRITE);
+ if(fd != nil)
+ sys->fprint(fd, "%s", buf);
+ else
+ snarfbuf = buf;
+}
+
+# return (qslice, end).
+# the slice has a leading quote if the word is quoted; it does not include the terminating quote.
+splitqword(s: string, start: int): ((int, int), int)
+{
+ for(; start < len s; start++)
+ if(s[start] != ' ')
+ break;
+ if(start >= len s)
+ return ((start, start), start);
+ i := start;
+ end := -1;
+ if(s[i] == '\''){
+ gotq := 0;
+ for(i++; i < len s; i++){
+ if(s[i] == '\''){
+ if(i + 1 >= len s || s[i + 1] != '\''){
+ end = i+1;
+ break;
+ }
+ i++;
+ gotq = 1;
+ }
+ }
+ if(!gotq && i > start+1)
+ start++;
+ if(end == -1)
+ end = i;
+ } else {
+ for(; i < len s; i++)
+ if(s[i] == ' ')
+ break;
+ end = i;
+ }
+ return ((start, i), end);
+}
+
+# unquote a string slice as returned by sliceqword.
+qslice(s: string, r: (int, int)): string
+{
+ if(r.t0 == r.t1)
+ return nil;
+ if(s[r.t0] != '\'')
+ return s[r.t0:r.t1];
+ t := "";
+ for(i := r.t0 + 1; i < r.t1; i++){
+ t[len t] = s[i];
+ if(s[i] == '\'')
+ i++;
+ }
+ return t;
+}
+
+qword(s: string, start: int): (string, int)
+{
+ (w, next) := splitqword(s, start);
+ return (qslice(s, w), next);
+}
+
+s2r(s: string, e: int): (Rect, int)
+{
+ r: Rect;
+ w: string;
+ (w, e) = qword(s, e);
+ r.min.x = int w;
+ (w, e) = qword(s, e);
+ r.min.y = int w;
+ (w, e) = qword(s, e);
+ r.max.x = int w;
+ (w, e) = qword(s, e);
+ r.max.y = int w;
+ return (r, e);
+}
+
+kill(pid: int, note: string): int
+{
+ fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);
+ if(fd == nil) # dodgy failover
+ fd = sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE);
+ if(fd == nil || sys->fprint(fd, "%s", note) < 0)
+ return -1;
+ return 0;
+}
diff --git a/appl/lib/wmsrv.b b/appl/lib/wmsrv.b
new file mode 100644
index 00000000..1f2c4e79
--- /dev/null
+++ b/appl/lib/wmsrv.b
@@ -0,0 +1,610 @@
+implement Wmsrv;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Display, Image, Point, Rect, Screen, Pointer, Context, Wmcontext: import draw;
+include "wmsrv.m";
+
+zorder: ref Client; # top of z-order list, linked by znext.
+
+ZR: con Rect((0, 0), (0, 0));
+Iqueue: adt {
+ h, t: list of int;
+ n: int;
+ put: fn(q: self ref Iqueue, s: int);
+ get: fn(q: self ref Iqueue): int;
+ peek: fn(q: self ref Iqueue): int;
+ nonempty: fn(q: self ref Iqueue): int;
+};
+Squeue: adt {
+ h, t: list of string;
+ n: int;
+ put: fn(q: self ref Squeue, s: string);
+ get: fn(q: self ref Squeue): string;
+ peek: fn(q: self ref Squeue): string;
+ nonempty: fn(q: self ref Squeue): int;
+};
+# Ptrqueue is the same as the other queues except it merges events
+# that have the same button state.
+Ptrqueue: adt {
+ last: ref Pointer;
+ h, t: list of ref Pointer;
+ put: fn(q: self ref Ptrqueue, s: ref Pointer);
+ get: fn(q: self ref Ptrqueue): ref Pointer;
+ peek: fn(q: self ref Ptrqueue): ref Pointer;
+ nonempty: fn(q: self ref Ptrqueue): int;
+ flush: fn(q: self ref Ptrqueue);
+};
+
+init(): (chan of (string, chan of (string, ref Wmcontext)),
+ chan of (ref Client, chan of string),
+ chan of (ref Client, array of byte, Sys->Rwrite))
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+
+ sys->bind("#s", "/chan", Sys->MBEFORE);
+
+ ctlio := sys->file2chan("/chan", "wmctl");
+ if(ctlio == nil){
+ sys->werrstr(sys->sprint("can't create /chan/wmctl: %r"));
+ return (nil, nil, nil);
+ }
+
+ wmreq := chan of (string, chan of (string, ref Wmcontext));
+ join := chan of (ref Client, chan of string);
+ req := chan of (ref Client, array of byte, Sys->Rwrite);
+ spawn wm(ctlio, wmreq, join, req);
+ return (wmreq, join, req);
+}
+
+wm(ctlio: ref Sys->FileIO,
+ wmreq: chan of (string, chan of (string, ref Wmcontext)),
+ join: chan of (ref Client, chan of string),
+ req: chan of (ref Client, array of byte, Sys->Rwrite))
+{
+ clients: array of ref Client;
+
+ for(;;)alt{
+ (cmd, rc) := <-wmreq =>
+ token := int cmd;
+ for(i := 0; i < len clients; i++)
+ if(clients[i] != nil && clients[i].token == token)
+ break;
+
+ if(i == len clients){
+ spawn senderror(rc, "not found");
+ break;
+ }
+ c := clients[i];
+ if(c.stop != nil){
+ spawn senderror(rc, "already started");
+ break;
+ }
+ ok := chan of string;
+ join <-= (c, ok);
+ if((e := <-ok) != nil){
+ spawn senderror(rc, e);
+ break;
+ }
+ c.stop = chan of int;
+ spawn childminder(c, rc);
+
+ (nil, nbytes, fid, rc) := <-ctlio.read =>
+ if(rc == nil)
+ break;
+ c := findfid(clients, fid);
+ if(c == nil){
+ c = ref Client(
+ chan of int,
+ chan of ref Draw->Pointer,
+ chan of string,
+ nil,
+ 0,
+ nil,
+ nil,
+ nil,
+
+ chan of (ref Point, ref Image, chan of int),
+ -1,
+ fid,
+ fid, # token; XXX could be random integer + fid
+ newwmcontext()
+ );
+ clients = addclient(clients, c);
+ }
+ alt{
+ rc <-= (sys->aprint("%d", c.token), nil) => ;
+ * => ;
+ }
+ (nil, data, fid, wc) := <-ctlio.write =>
+ c := findfid(clients, fid);
+ if(wc != nil){
+ if(c == nil){
+ alt{
+ wc <-= (0, "must read first") => ;
+ * => ;
+ }
+ break;
+ }
+ req <-= (c, data, wc);
+ }else if(c != nil){
+ req <-= (c, nil, nil);
+ delclient(clients, c);
+ }
+ }
+}
+
+# buffer all events between a window manager and
+# a client, so that one recalcitrant child can't
+# clog the whole system.
+childminder(c: ref Client, rc: chan of (string, ref Wmcontext))
+{
+ wmctxt := c.wmctxt;
+
+ dummykbd := chan of int;
+ dummyptr := chan of ref Pointer;
+ dummyimg := chan of ref Image;
+ dummyctl := chan of string;
+
+ kbdq := ref Iqueue;
+ ptrq := ref Ptrqueue;
+ ctlq := ref Squeue;
+
+ Imgnone, Imgsend, Imgsendnil1, Imgsendnil2, Imgorigin: con iota;
+ img, sendimg: ref Image;
+ imgorigin: Point;
+ imgstate := Imgnone;
+
+ # send reply to client, but make sure we don't block.
+Reply:
+ for(;;) alt{
+ rc <-= (nil, ref *wmctxt) =>
+ break Reply;
+ <-c.stop =>
+ exit;
+ key := <-c.kbd =>
+ kbdq.put(key);
+ ptr := <-c.ptr =>
+ ptrq.put(ptr);
+ ctl := <-c.ctl =>
+ ctlq.put(ctl);
+ }
+
+ for(;;){
+ outkbd := dummykbd;
+ key := -1;
+ if(kbdq.nonempty()){
+ key = kbdq.peek();
+ outkbd = wmctxt.kbd;
+ }
+
+ outptr := dummyptr;
+ ptr: ref Pointer;
+ if(ptrq.nonempty()){
+ ptr = ptrq.peek();
+ outptr = wmctxt.ptr;
+ }
+
+ outctl := dummyctl;
+ ctl: string;
+ if(ctlq.nonempty()){
+ ctl = ctlq.peek();
+ outctl = wmctxt.ctl;
+ }
+
+ outimg := dummyimg;
+ case imgstate{
+ Imgsend =>
+ outimg = wmctxt.images;
+ sendimg = img;
+ Imgsendnil1 or
+ Imgsendnil2 or
+ Imgorigin =>
+ outimg = wmctxt.images;
+ sendimg = nil;
+ }
+
+ alt{
+ outkbd <-= key =>
+ kbdq.get();
+ outptr <-= ptr =>
+ ptrq.get();
+ outctl <-= ctl =>
+ ctlq.get();
+ outimg <-= sendimg =>
+ case imgstate{
+ Imgsend =>
+ imgstate = Imgnone;
+ img = sendimg = nil;
+ Imgsendnil1 =>
+ imgstate = Imgsendnil2;
+ Imgsendnil2 =>
+ imgstate = Imgnone;
+ Imgorigin =>
+ if(img.origin(imgorigin, imgorigin) == -1){
+ # XXX what can we do about this? there's no way at the moment
+ # of getting the information about the origin failure back to the wm,
+ # so we end up with an inconsistent window position.
+ # if the window manager blocks while we got the sync from
+ # the client, then a client could block the whole window manager
+ # which is what we're trying to avoid.
+ # but there's no other time we could set the origin of the window,
+ # and not risk mucking up the window contents.
+ # the short answer is that running out of image space is Bad News.
+ }
+ imgstate = Imgsend;
+ }
+
+ # XXX could mark the application as unresponding if any of these queues
+ # start growing too much.
+ ch := <-c.kbd =>
+ kbdq.put(ch);
+ p := <-c.ptr =>
+ if(p == nil)
+ ptrq.flush();
+ else
+ ptrq.put(p);
+ e := <-c.ctl =>
+ ctlq.put(e);
+ (o, i, reply) := <-c.images =>
+ # can't queue multiple image requests.
+ if(imgstate != Imgnone)
+ reply <-= -1;
+ else {
+ # if the origin is being set, then we first send a nil image
+ # to indicate that this is happening, and then the
+ # image itself (reorigined).
+ # if a nil image is being set, then we
+ # send nil twice.
+ if(o != nil){
+ imgorigin = *o;
+ imgstate = Imgorigin;
+ img = i;
+ }else if(i != nil){
+ img = i;
+ imgstate = Imgsend;
+ }else
+ imgstate = Imgsendnil1;
+ reply <-= 0;
+ }
+ <-c.stop =>
+ # XXX do we need to unblock channels, kill, etc.?
+ # we should perhaps drain the ctl output channel here
+ # if possible, exiting if it times out.
+ exit;
+ }
+ }
+}
+
+findfid(clients: array of ref Client, fid: int): ref Client
+{
+ for(i := 0; i < len clients; i++)
+ if(clients[i] != nil && clients[i].fid == fid)
+ return clients[i];
+ return nil;
+}
+
+addclient(clients: array of ref Client, c: ref Client): array of ref Client
+{
+ for(i := 0; i < len clients; i++)
+ if(clients[i] == nil){
+ clients[i] = c;
+ c.id = i;
+ return clients;
+ }
+ nc := array[len clients + 4] of ref Client;
+ nc[0:] = clients;
+ nc[len clients] = c;
+ c.id = len clients;
+ return nc;
+}
+
+delclient(clients: array of ref Client, c: ref Client)
+{
+ clients[c.id] = nil;
+}
+
+senderror(rc: chan of (string, ref Wmcontext), e: string)
+{
+ rc <-= (e, nil);
+}
+
+Client.window(c: self ref Client, tag: string): ref Window
+{
+ for (w := c.wins; w != nil; w = tl w)
+ if((hd w).tag == tag)
+ return hd w;
+ return nil;
+}
+
+Client.image(c: self ref Client, tag: string): ref Draw->Image
+{
+ w := c.window(tag);
+ if(w != nil)
+ return w.img;
+ return nil;
+}
+
+Client.setimage(c: self ref Client, tag: string, img: ref Draw->Image): int
+{
+ # if img is nil, remove window from list.
+ if(img == nil){
+ # usual case:
+ if(c.wins != nil && (hd c.wins).tag == tag){
+ c.wins = tl c.wins;
+ return -1;
+ }
+ nw: list of ref Window;
+ for (w := c.wins; w != nil; w = tl w)
+ if((hd w).tag != tag)
+ nw = hd w :: nw;
+ c.wins = nil;
+ for(; nw != nil; nw = tl nw)
+ c.wins = hd nw :: c.wins;
+ return -1;
+ }
+ for(w := c.wins; w != nil; w = tl w)
+ if((hd w).tag == tag)
+ break;
+ win: ref Window;
+ if(w != nil)
+ win = hd w;
+ else{
+ win = ref Window(tag, ZR, nil);
+ c.wins = win :: c.wins;
+ }
+ win.img = img;
+ win.r = img.r; # save so clients can set logical origin
+ rc := chan of int;
+ c.images <-= (nil, img, rc);
+ return <-rc;
+}
+
+# tell a client about a window that's moved to screen coord o.
+Client.setorigin(c: self ref Client, tag: string, o: Draw->Point): int
+{
+ w := c.window(tag);
+ if(w == nil)
+ return -1;
+ img := w.img;
+ if(img == nil)
+ return -1;
+ rc := chan of int;
+ c.images <-= (ref o, w.img, rc);
+ if(<-rc != -1){
+ w.r = (o, o.add(img.r.size()));
+ return 0;
+ }
+ return -1;
+}
+
+clientimages(c: ref Client): array of ref Image
+{
+ a := array[len c.wins] of ref Draw->Image;
+ i := 0;
+ for(w := c.wins; w != nil; w = tl w)
+ if((hd w).img != nil)
+ a[i++] = (hd w).img;
+ return a[0:i];
+}
+
+Client.top(c: self ref Client)
+{
+ imgs := clientimages(c);
+ if(len imgs > 0)
+ imgs[0].screen.top(imgs);
+
+ if(zorder == c)
+ return;
+
+ prev: ref Client;
+ for(z := zorder; z != nil; (prev, z) = (z, z.znext))
+ if(z == c)
+ break;
+ if(prev != nil)
+ prev.znext = c.znext;
+ c.znext = zorder;
+ zorder = c;
+}
+
+Client.bottom(c: self ref Client)
+{
+ if(c.znext == nil)
+ return;
+ imgs := clientimages(c);
+ if(len imgs > 0)
+ imgs[0].screen.bottom(imgs);
+ prev: ref Client;
+ for(z := zorder; z != nil; (prev, z) = (z, z.znext))
+ if(z == c)
+ break;
+ if(prev != nil)
+ prev.znext = c.znext;
+ else
+ zorder = c.znext;
+ z = c.znext;
+ c.znext = nil;
+ for(; z != nil; (prev, z) = (z, z.znext))
+ ;
+ if(prev != nil)
+ prev.znext = c;
+ else
+ zorder = c;
+}
+
+Client.hide(nil: self ref Client)
+{
+}
+
+Client.unhide(nil: self ref Client)
+{
+}
+
+Client.remove(c: self ref Client)
+{
+ prev: ref Client;
+ for(z := zorder; z != nil; (prev, z) = (z, z.znext))
+ if(z == c)
+ break;
+ if(z == nil)
+ return;
+ if(prev != nil)
+ prev.znext = z.znext;
+ else if(z != nil)
+ zorder = zorder.znext;
+}
+
+find(p: Draw->Point): ref Client
+{
+ for(z := zorder; z != nil; z = z.znext)
+ if(z.contains(p))
+ return z;
+ return nil;
+}
+
+top(): ref Client
+{
+ return zorder;
+}
+
+Client.contains(c: self ref Client, p: Point): int
+{
+ for(w := c.wins; w != nil; w = tl w)
+ if((hd w).r.contains(p))
+ return 1;
+ return 0;
+}
+
+r2s(r: Rect): string
+{
+ return string r.min.x + " " + string r.min.y + " " +
+ string r.max.x + " " + string r.max.y;
+}
+
+newwmcontext(): ref Wmcontext
+{
+ return ref Wmcontext(
+ chan of int,
+ chan of ref Pointer,
+ chan of string,
+ nil,
+ chan of ref Image,
+ nil,
+ nil
+ );
+}
+
+Iqueue.put(q: self ref Iqueue, s: int)
+{
+ q.t = s :: q.t;
+}
+Iqueue.get(q: self ref Iqueue): int
+{
+ s := -1;
+ if(q.h == nil){
+ for(t := q.t; t != nil; t = tl t)
+ q.h = hd t :: q.h;
+ q.t = nil;
+ }
+ if(q.h != nil){
+ s = hd q.h;
+ q.h = tl q.h;
+ }
+ return s;
+}
+Iqueue.peek(q: self ref Iqueue): int
+{
+ s := -1;
+ if (q.h == nil && q.t == nil)
+ return s;
+ s = q.get();
+ q.h = s :: q.h;
+ return s;
+}
+Iqueue.nonempty(q: self ref Iqueue): int
+{
+ return q.h != nil || q.t != nil;
+}
+
+
+Squeue.put(q: self ref Squeue, s: string)
+{
+ q.t = s :: q.t;
+}
+Squeue.get(q: self ref Squeue): string
+{
+ s: string;
+ if(q.h == nil){
+ for(t := q.t; t != nil; t = tl t)
+ q.h = hd t :: q.h;
+ q.t = nil;
+ }
+ if(q.h != nil){
+ s = hd q.h;
+ q.h = tl q.h;
+ }
+ return s;
+}
+Squeue.peek(q: self ref Squeue): string
+{
+ s: string;
+ if (q.h == nil && q.t == nil)
+ return s;
+ s = q.get();
+ q.h = s :: q.h;
+ return s;
+}
+Squeue.nonempty(q: self ref Squeue): int
+{
+ return q.h != nil || q.t != nil;
+}
+
+Ptrqueue.put(q: self ref Ptrqueue, s: ref Pointer)
+{
+ if(q.last != nil && s.buttons == q.last.buttons)
+ *q.last = *s;
+ else{
+ q.t = s :: q.t;
+ q.last = s;
+ }
+}
+Ptrqueue.get(q: self ref Ptrqueue): ref Pointer
+{
+ s: ref Pointer;
+ h := q.h;
+ if(h == nil){
+ for(t := q.t; t != nil; t = tl t)
+ h = hd t :: h;
+ q.t = nil;
+ }
+ if(h != nil){
+ s = hd h;
+ h = tl h;
+ if(h == nil)
+ q.last = nil;
+ }
+ q.h = h;
+ return s;
+}
+Ptrqueue.peek(q: self ref Ptrqueue): ref Pointer
+{
+ s: ref Pointer;
+ if (q.h == nil && q.t == nil)
+ return s;
+ t := q.last;
+ s = q.get();
+ q.h = s :: q.h;
+ q.last = t;
+ return s;
+}
+Ptrqueue.nonempty(q: self ref Ptrqueue): int
+{
+ return q.h != nil || q.t != nil;
+}
+Ptrqueue.flush(q: self ref Ptrqueue)
+{
+ q.h = q.t = nil;
+}
diff --git a/appl/lib/workdir.b b/appl/lib/workdir.b
new file mode 100644
index 00000000..aa571b9d
--- /dev/null
+++ b/appl/lib/workdir.b
@@ -0,0 +1,14 @@
+implement Workdir;
+
+include "sys.m";
+
+include "workdir.m";
+
+init(): string
+{
+ sys := load Sys Sys->PATH;
+ fd := sys->open(".", Sys->OREAD);
+ if(fd == nil)
+ return nil;
+ return sys->fd2path(fd);
+}
diff --git a/appl/lib/writegif.b b/appl/lib/writegif.b
new file mode 100644
index 00000000..3990ed63
--- /dev/null
+++ b/appl/lib/writegif.b
@@ -0,0 +1,362 @@
+implement WImagefile;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+ draw: Draw;
+ Chans, Display, Image, Rect: import draw;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "imagefile.m";
+
+Nhash: con 4001;
+
+Entry: adt
+{
+ index: int;
+ prefix: int;
+ exten: int;
+ next: cyclic ref Entry;
+};
+
+IO: adt
+{
+ fd: ref Iobuf;
+ buf: array of byte;
+ i: int;
+ nbits: int; # bits in right side of shift register
+ sreg: int; # shift register
+};
+
+tbl: array of ref Entry;
+
+colormap: array of array of byte;
+log2 := array[] of {1 => 0, 2 => 1, 4 => 2, 8 => 3, * => -1};
+
+init(iomod: Bufio)
+{
+ if(sys == nil){
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+ }
+ bufio = iomod;
+}
+
+writeimage(fd: ref Iobuf, image: ref Image): string
+{
+ case image.chans.desc {
+ (Draw->GREY1).desc or (Draw->GREY2).desc or
+ (Draw->GREY4).desc or (Draw->GREY8).desc or
+ (Draw->CMAP8).desc =>
+ if(image.depth > 8 || (image.depth&(image.depth-1)) != 0)
+ return "inconsistent depth";
+ * =>
+ return "unsupported channel type";
+ }
+
+ inittbl();
+
+ writeheader(fd, image);
+ writedescriptor(fd, image);
+
+ err := writedata(fd, image);
+ if(err != nil)
+ return err;
+
+ writetrailer(fd);
+ fd.flush();
+ return err;
+}
+
+inittbl()
+{
+ tbl = array[4096] of ref Entry;
+ for(i:=0; i<len tbl; i++)
+ tbl[i] = ref Entry(i, -1, i, nil);
+}
+
+# Write header, logical screen descriptor, and color map
+writeheader(fd: ref Iobuf, image: ref Image): string
+{
+ # Header
+ fd.puts("GIF89a");
+
+ # Logical Screen Descriptor
+ put2(fd, image.r.dx());
+ put2(fd, image.r.dy());
+ # color table present, 4 bits per color (for RGBV best case), size of color map
+ fd.putb(byte ((1<<7)|(3<<4)|(image.depth-1)));
+ fd.putb(byte 0); # white background (doesn't matter anyway)
+ fd.putb(byte 0); # pixel aspect ratio - unused
+
+ # Global Color Table
+ getcolormap(image);
+ ldepth := log2[image.depth];
+ if(image.chans.eq(Draw->GREY8))
+ ldepth = 4;
+ fd.write(colormap[ldepth], len colormap[ldepth]);
+ return nil;
+}
+
+# Write image descriptor
+writedescriptor(fd: ref Iobuf, image: ref Image)
+{
+ # Image Separator
+ fd.putb(byte 16r2C);
+
+ # Left, top, width, height
+ put2(fd, 0);
+ put2(fd, 0);
+ put2(fd, image.r.dx());
+ put2(fd, image.r.dy());
+ # no special processing
+ fd.putb(byte 0);
+}
+
+# Write data
+writedata(fd: ref Iobuf, image: ref Image): string
+{
+ # LZW Minimum code size
+ if(image.depth == 1)
+ fd.putb(byte 2);
+
+ else
+ fd.putb(byte image.depth);
+
+ # Encode and emit the data
+ err := encode(fd, image);
+ if(err != nil)
+ return err;
+
+ # Block Terminator
+ fd.putb(byte 0);
+ return nil;
+}
+
+# Write data
+writetrailer(fd: ref Iobuf)
+{
+ fd.putb(byte 16r3B);
+}
+
+# Write little-endian 16-bit integer
+put2(fd: ref Iobuf, i: int)
+{
+ fd.putb(byte i);
+ fd.putb(byte (i>>8));
+}
+
+# Get color map for all ldepths, in format suitable for writing out
+getcolormap(image: ref Draw->Image)
+{
+ if(colormap != nil)
+ return;
+ colormap = array[5] of array of byte;
+ display := image.display;
+ colormap[4] = array[3*256] of byte;
+ colormap[3] = array[3*256] of byte;
+ colormap[2] = array[3*16] of byte;
+ colormap[1] = array[3*4] of byte;
+ colormap[0] = array[3*2] of byte;
+ c := colormap[4];
+ for(i:=0; i<256; i++){
+ c[3*i+0] = byte i;
+ c[3*i+1] = byte i;
+ c[3*i+2] = byte i;
+ }
+ c = colormap[3];
+ for(i=0; i<256; i++){
+ (r, g, b) := display.cmap2rgb(i);
+ c[3*i+0] = byte r;
+ c[3*i+1] = byte g;
+ c[3*i+2] = byte b;
+ }
+ c = colormap[2];
+ for(i=0; i<16; i++){
+ col := (i<<4)|i;
+ (r, g, b) := display.cmap2rgb(col);
+ c[3*i+0] = byte r;
+ c[3*i+1] = byte g;
+ c[3*i+2] = byte b;
+ }
+ c = colormap[1];
+ for(i=0; i<4; i++){
+ col := (i<<6)|(i<<4)|(i<<2)|i;
+ (r, g, b) := display.cmap2rgb(col);
+ c[3*i+0] = byte r;
+ c[3*i+1] = byte g;
+ c[3*i+2] = byte b;
+ }
+ c = colormap[0];
+ for(i=0; i<2; i++){
+ if(i == 0)
+ col := 0;
+ else
+ col = 16rFF;
+ (r, g, b) := display.cmap2rgb(col);
+ c[3*i+0] = byte r;
+ c[3*i+1] = byte g;
+ c[3*i+2] = byte b;
+ }
+}
+
+# Put n bits of c into output at io.buf[i];
+output(io: ref IO, c, n: int)
+{
+ if(c < 0){
+ if(io.nbits != 0)
+ io.buf[io.i++] = byte io.sreg;
+ io.fd.putb(byte io.i);
+ io.fd.write(io.buf, io.i);
+ io.nbits = 0;
+ return;
+ }
+
+ if(io.nbits+n >= 31){
+ sys->print("panic: WriteGIF sr overflow\n");
+ exit;
+ }
+ io.sreg |= c<<io.nbits;
+ io.nbits += n;
+
+ while(io.nbits >= 8){
+ io.buf[io.i++] = byte io.sreg;
+ io.sreg >>= 8;
+ io.nbits -= 8;
+ }
+
+ if(io.i >= 255){
+ io.fd.putb(byte 255);
+ io.fd.write(io.buf, 255);
+ io.buf[0:] = io.buf[255:io.i];
+ io.i -= 255;
+ }
+}
+
+# LZW encoder
+encode(fd: ref Iobuf, image: ref Image): string
+{
+ c, h, csize, prefix: int;
+ e, oe: ref Entry;
+
+ first := 1;
+ ld := log2[image.depth];
+ # ldepth 0 must generate codesize 2 with values 0 and 1 (see the spec.)
+ ld0 := ld;
+ if(ld0 == 0)
+ ld0 = 1;
+ codesize := (1<<ld0);
+ CTM := 1<<codesize;
+ EOD := CTM+1;
+
+ io := ref IO (fd, array[300] of byte, 0, 0, 0);
+ sreg := 0;
+ nbits := 0;
+ bitsperpixel := 1<<ld;
+ pm := (1<<bitsperpixel)-1;
+
+ # Read image data into memory
+ # potentially one extra byte on each end of each scan line
+ data := array[image.r.dy()*(2+(image.r.dx()>>(3-log2[image.depth])))] of byte;
+ ndata := image.readpixels(image.r, data);
+ if(ndata < 0)
+ return sys->sprint("WriteGIF: readpixels: %r");
+ datai := 0;
+ x := image.r.min.x;
+
+Init:
+ for(;;){
+ csize = codesize+1;
+ nentry := EOD+1;
+ maxentry := (1<<csize);
+ hash := array[Nhash] of ref Entry;
+ for(i := 0; i<nentry; i++){
+ e = tbl[i];
+ h = (e.prefix<<24) | (e.exten<<8);
+ h %= Nhash;
+ if(h < 0)
+ h += Nhash;
+ e.next = hash[h];
+ hash[h] = e;
+ }
+ prefix = -1;
+ if(first)
+ output(io, CTM, csize);
+ first = 0;
+
+ # Scan over pixels. Because of partially filled bytes on ends of scan lines,
+ # which must be ignored in the data stream passed to GIF, this is more
+ # complex than we'd like
+ Next:
+ for(;;){
+ if(ld != 3){
+ # beginning of scan line is difficult; prime the shift register
+ if(x == image.r.min.x){
+ if(datai == ndata)
+ break;
+ sreg = int data[datai++];
+ nbits = 8-((x&(7>>ld))<<ld);
+ }
+ x++;
+ if(x == image.r.max.x)
+ x = image.r.min.x;
+ }
+ if(nbits == 0){
+ if(datai == ndata)
+ break;
+ sreg = int data[datai++];
+ nbits = 8;
+ }
+ nbits -= bitsperpixel;
+ c = sreg>>nbits & pm;
+ h = prefix<<24 | c<<8;
+ h %= Nhash;
+ if(h < 0)
+ h += Nhash;
+ oe = nil;
+ for(e = hash[h]; e!=nil; e=e.next){
+ if(e.prefix == prefix && e.exten == c){
+ if(oe != nil){
+ oe.next = e.next;
+ e.next = hash[h];
+ hash[h] = e;
+ }
+ prefix = e.index;
+ continue Next;
+ }
+ oe = e;
+ }
+
+ output(io, prefix, csize);
+ early:=0; # peculiar tiff feature here for reference
+ if(nentry == maxentry-early){
+ if(csize == 12){
+ nbits += codesize; # unget pixel
+ x--;
+ output(io, CTM, csize);
+ continue Init;
+ }
+ csize++;
+ maxentry = (1<<csize);
+ }
+
+ e = tbl[nentry];
+ e.prefix = prefix;
+ e.exten = c;
+ e.next = hash[h];
+ hash[h] = e;
+
+ prefix = c;
+ nentry++;
+ }
+ break Init;
+ }
+ output(io, prefix, csize);
+ output(io, EOD, csize);
+ output(io, -1, csize);
+ return nil;
+}
diff --git a/appl/lib/xml.b b/appl/lib/xml.b
new file mode 100644
index 00000000..5e10608d
--- /dev/null
+++ b/appl/lib/xml.b
@@ -0,0 +1,712 @@
+implement Xml;
+
+#
+# Portions copyright © 2002 Vita Nuova Holdings Limited
+#
+#
+# Derived from saxparser.b Copyright © 2001-2002 by John Powers or his employer
+#
+
+# TO DO:
+# - provide a way of getting attributes out of <?...?> (process) requests,
+# so that we can process stylesheet requests given in that way.
+
+include "sys.m";
+ sys: Sys;
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "string.m";
+ str: String;
+include "hash.m";
+ hash: Hash;
+ HashTable: import hash;
+include "xml.m";
+
+Parcel: adt {
+ pick {
+ Start or
+ Empty =>
+ name: string;
+ attrs: Attributes;
+ End =>
+ name: string;
+ Text =>
+ ch: string;
+ ws1, ws2: int;
+ Process =>
+ target: string;
+ data: string;
+ Error =>
+ loc: Locator;
+ msg: string;
+ Doctype =>
+ name: string;
+ public: int;
+ params: list of string;
+ Stylesheet =>
+ attrs: Attributes;
+ EOF =>
+ }
+};
+
+entinit := array[] of {
+ ("AElig", "Æ"),
+ ("OElig", "Œ"),
+ ("aelig", "æ"),
+ ("amp", "&"),
+ ("apos", "\'"),
+ ("copy", "©"),
+ ("gt", ">"),
+ ("ldquo", "``"),
+ ("lt", "<"),
+ ("mdash", "-"), # XXX ??
+ ("oelig", "œ"),
+ ("quot", "\""),
+ ("rdquo", "''"),
+ ("rsquo", "'"),
+ ("trade", "™"),
+ ("nbsp", "\u00a0"),
+};
+entdict: ref HashTable;
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ if (bufio == nil)
+ return sys->sprint("cannot load %s: %r", Bufio->PATH);
+ str = load String String->PATH;
+ if (str == nil)
+ return sys->sprint("cannot load %s: %r", String->PATH);
+ hash = load Hash Hash->PATH;
+ if (hash == nil)
+ return sys->sprint("cannot load %s: %r", Hash->PATH);
+ entdict = hash->new(23);
+ for (i := 0; i < len entinit; i += 1) {
+ (key, value) := entinit[i];
+ entdict.insert(key, (0, 0.0, value));
+ }
+ return nil;
+}
+
+blankparser: Parser;
+
+open(srcfile: string, warning: chan of (Locator, string), preelem: string): (ref Parser, string)
+{
+ x := ref blankparser;
+ x.in = bufio->open(srcfile, Bufio->OREAD);
+ if (x.in == nil)
+ return (nil, sys->sprint("cannot open %s: %r", srcfile));
+ # ignore utf16 intialisation character (yuck)
+ c := x.in.getc();
+ if (c != 16rfffe && c != 16rfeff)
+ x.in.ungetc();
+ x.estack = nil;
+ x.loc = Locator(1, srcfile, "");
+ x.warning = warning;
+ x.preelem = preelem;
+ return (x, "");
+}
+
+Parser.next(x: self ref Parser): ref Item
+{
+ curroffset := x.fileoffset;
+ currloc := x.loc;
+ # read up until end of current item
+ while (x.actdepth > x.readdepth) {
+ pick p := getparcel(x) {
+ Start =>
+ x.actdepth++;
+ End =>
+ x.actdepth--;
+ EOF =>
+ x.actdepth = 0; # premature EOF closes all tags
+ Error =>
+ return ref Item.Error(curroffset, x.loc, x.errormsg);
+ }
+ }
+ if (x.actdepth < x.readdepth) {
+ x.fileoffset = int x.in.offset();
+ return nil;
+ }
+ gp := getparcel(x);
+ item: ref Item;
+ pick p := gp {
+ Start =>
+ x.actdepth++;
+ item = ref Item.Tag(curroffset, p.name, p.attrs);
+ End =>
+ x.actdepth--;
+ item = nil;
+ EOF =>
+ x.actdepth = 0;
+ item = nil;
+ Error =>
+ x.actdepth = 0; # XXX is this the right thing to do?
+ item = ref Item.Error(curroffset, currloc, x.errormsg);
+ Text =>
+ item = ref Item.Text(curroffset, p.ch, p.ws1, p.ws2);
+ Process =>
+ item = ref Item.Process(curroffset, p.target, p.data);
+ Empty =>
+ item = ref Item.Tag(curroffset, p.name, p.attrs);
+ Doctype =>
+ item = ref Item.Doctype(curroffset, p.name, p.public, p.params);
+ Stylesheet =>
+ item = ref Item.Stylesheet(curroffset, p.attrs);
+ }
+ x.fileoffset = int x.in.offset();
+ return item;
+}
+
+Parser.atmark(x: self ref Parser, m: ref Mark): int
+{
+ return int x.in.offset() == m.offset;
+}
+
+Parser.down(x: self ref Parser)
+{
+ x.readdepth++;
+}
+
+Parser.up(x: self ref Parser)
+{
+ x.readdepth--;
+}
+
+# mark is only defined after a next(), not after up() or down().
+# this means that we don't have to record lots of state when going up or down levels.
+Parser.mark(x: self ref Parser): ref Mark
+{
+ return ref Mark(x.estack, x.loc.line, int x.in.offset(), x.readdepth);
+}
+
+Parser.goto(x: self ref Parser, m: ref Mark)
+{
+ x.in.seek(big m.offset, Sys->SEEKSTART);
+ x.fileoffset = m.offset;
+ x.eof = 0;
+ x.estack = m.estack;
+ x.loc.line = m.line;
+ x.readdepth = m.readdepth;
+ x.actdepth = len x.estack;
+}
+
+Mark.str(m: self ref Mark): string
+{
+ # assume that neither the filename nor any of the tags contain spaces.
+ # format:
+ # offset readdepth linenum [tag...]
+ # XXX would be nice if the produced string did not contain
+ # any spaces so it could be treated as a word in other contexts.
+ s := sys->sprint("%d %d %d", m.offset, m.readdepth, m.line);
+ for (t := m.estack; t != nil; t = tl t)
+ s += " " + hd t;
+ return s;
+}
+
+Parser.str2mark(p: self ref Parser, s: string): ref Mark
+{
+ (n, toks) := sys->tokenize(s, " ");
+ if (n < 3)
+ return nil;
+ m := ref Mark(nil, p.loc.line, 0, 0);
+ (m.offset, toks) = (int hd toks, tl toks);
+ (m.readdepth, toks) = (int hd toks, tl toks);
+ (m.line, toks) = (int hd toks, tl toks);
+ m.estack = toks;
+ return m;
+}
+
+getparcel(x: ref Parser): ref Parcel
+{
+ {
+ p: ref Parcel;
+ while (!x.eof && p == nil) {
+ c := getc(x);
+ if (c == '<')
+ p = element(x);
+ else {
+ ungetc(x);
+ p = characters(x);
+ }
+ }
+ if (p == nil)
+ p = ref Parcel.EOF;
+ return p;
+ }
+ exception e{
+ "sax:*" =>
+ return ref Parcel.Error(x.loc, x.errormsg);
+ }
+}
+
+parcelstr(gi: ref Parcel): string
+{
+ if (gi == nil)
+ return "nil";
+ pick i := gi {
+ Start =>
+ return sys->sprint("Start: %s", i.name);
+ Empty =>
+ return sys->sprint("Empty: %s", i.name);
+ End =>
+ return "End";
+ Text =>
+ return "Text";
+ Doctype =>
+ return sys->sprint("Doctype: %s", i.name);
+ Stylesheet =>
+ return "Stylesheet";
+ Error =>
+ return "Error: " + i.msg;
+ EOF =>
+ return "EOF";
+ * =>
+ return "Unknown";
+ }
+}
+
+element(x: ref Parser): ref Parcel
+{
+ # <tag ...>
+ elemname := xmlname(x);
+ c: int;
+ if (elemname != "") {
+ attrs := buildattrs(x);
+ skipwhite(x);
+ c = getc(x);
+ isend := 0;
+ if (c == '/')
+ isend = 1;
+ else
+ ungetc(x);
+ expect(x, '>');
+
+ if (isend)
+ return ref Parcel.Empty(elemname, attrs);
+ else {
+ startelement(x, elemname);
+ return ref Parcel.Start(elemname, attrs);
+ }
+ # </tag>
+ } else if ((c = getc(x)) == '/') {
+ elemname = xmlname(x);
+ if (elemname != "") {
+ expect(x, '>');
+ endelement(x, elemname);
+ return ref Parcel.End(elemname);
+ }
+ else
+ error(x, sys->sprint("illegal beginning of tag: '%c'", c));
+ # <?tag ... ?>
+ } else if (c == '?') {
+ elemname = xmlname(x);
+ if (elemname != "") {
+ # this special case could be generalised if there were many
+ # processing instructions that took attributes like this.
+ if (elemname == "xml-stylesheet") {
+ attrs := buildattrs(x);
+ balancedstring(x, "?>");
+ return ref Parcel.Stylesheet(attrs);
+ } else {
+ data := balancedstring(x, "?>");
+ return ref Parcel.Process(elemname, data);
+ }
+ }
+ } else if (c == '!') {
+ c = getc(x);
+ case c {
+ '-' =>
+ # <!-- comment -->
+ if(getc(x) == '-'){
+ balancedstring(x, "-->");
+ return nil;
+ }
+ '[' =>
+ # <![CDATA[...]]
+ s := xmlname(x);
+ if(s == "CDATA" && getc(x) == '['){
+ data := balancedstring(x, "]]>");
+ return ref Parcel.Text(data, 0, 0);
+ }
+ * =>
+ # <!declaration
+ ungetc(x);
+ s := xmlname(x);
+ case s {
+ "DOCTYPE" =>
+ # <!DOCTYPE name (SYSTEM "filename" | PUBLIC "pubid" "uri"?)? ("[" decls "]")?>
+ skipwhite(x);
+ name := xmlname(x);
+ if(name == nil)
+ break;
+ id := "";
+ uri := "";
+ public := 0;
+ skipwhite(x);
+ case sort := xmlname(x) {
+ "SYSTEM" =>
+ id = xmlstring(x, 1);
+ "PUBLIC" =>
+ public = 1;
+ id = xmlstring(x, 1);
+ skipwhite(x);
+ c = getc(x);
+ ungetc(x);
+ if(c == '"' || c == '\'')
+ uri = xmlstring(x, 1);
+ * =>
+ error(x, sys->sprint("unknown DOCTYPE: %s", sort));
+ return nil;
+ }
+ skipwhite(x);
+ if(getc(x) == '['){
+ error(x, "cannot handle DOCTYPE with declarations");
+ return nil;
+ }
+ ungetc(x);
+ skipwhite(x);
+ if(getc(x) == '>')
+ return ref Parcel.Doctype(name, public, id :: uri :: nil);
+ "ELEMENT" or "ATTRLIST" or "NOTATION" or "ENTITY" =>
+ # don't interpret internal DTDs
+ # <!ENTITY name ("value" | SYSTEM "filename")>
+ s = gets(x, '>');
+ if(s == nil || s[len s-1] != '>')
+ error(x, "end of file in declaration");
+ return nil;
+ * =>
+ error(x, sys->sprint("unknown declaration: %s", s));
+ }
+ }
+ error(x, "invalid XML declaration");
+ } else
+ error(x, sys->sprint("illegal beginning of tag: %c", c));
+ return nil;
+}
+
+characters(x: ref Parser): ref Parcel
+{
+ p: ref Parcel;
+ content := gets(x, '<');
+ if (len content > 0) {
+ if (content[len content - 1] == '<') {
+ ungetc(x);
+ content = content[0:len content - 1];
+ }
+ ws1, ws2: int;
+ if (x.ispre) {
+ content = substituteentities(x, content);
+ ws1 = ws2 = 0;
+ } else
+ (content, ws1, ws2) = substituteentities_sp(x, content);
+ if (content != nil || ws1)
+ p = ref Parcel.Text(content, ws1, ws2);
+ }
+ return p;
+}
+
+startelement(x: ref Parser, name: string)
+{
+ x.estack = name :: x.estack;
+ if (name == x.preelem)
+ x.ispre++;
+}
+
+endelement(x: ref Parser, name: string)
+{
+ if (x.estack != nil && name == hd x.estack) {
+ x.estack = tl x.estack;
+ if (name == x.preelem)
+ x.ispre--;
+ } else {
+ starttag := "";
+ if (x.estack != nil)
+ starttag = hd x.estack;
+ warning(x, sys->sprint("<%s></%s> mismatch", starttag, name));
+
+ # invalid XML but try to recover anyway to reduce turnaround time on fixing errors.
+ # loop back up through the tag stack to see if there's a matching tag, in which case
+ # jump up in the stack to that, making some rude assumptions about the
+ # way Parcels are handled at the top level.
+ n := 0;
+ for (t := x.estack; t != nil; (t, n) = (tl t, n + 1))
+ if (hd t == name)
+ break;
+ if (t != nil) {
+ x.estack = tl t;
+ x.actdepth -= n;
+ }
+ }
+}
+
+buildattrs(x: ref Parser): Attributes
+{
+ attrs: list of Attribute;
+
+ attr: Attribute;
+ for (;;) {
+ skipwhite(x);
+ attr.name = xmlname(x);
+ if (attr.name == nil)
+ break;
+ skipwhite(x);
+ c := getc(x);
+ if(c != '='){
+ ungetc(x);
+ attr.value = nil;
+ }else
+ attr.value = xmlstring(x, 1);
+ attrs = attr :: attrs;
+ }
+ return Attributes(attrs);
+}
+
+xmlstring(x: ref Parser, dosub: int): string
+{
+ skipwhite(x);
+ s := "";
+ delim := getc(x);
+ if (delim == '\"' || delim == '\'') {
+ s = gets(x, delim);
+ n := len s;
+ if (n == 0 || s[n-1] != delim)
+ error(x, "unclosed string at end of file");
+ s = s[0:n-1]; # TO DO: avoid copy
+ if(dosub)
+ s = substituteentities(x, s);
+ } else
+ error(x, sys->sprint("illegal string delimiter: %c", delim));
+ return s;
+}
+
+xmlname(x: ref Parser): string
+{
+ name := "";
+ ch := getc(x);
+ case ch {
+ '_' or ':' or
+ 'a' to 'z' or
+ 'A' to 'Z' or
+ 16r100 to 16rd7ff or
+ 16re000 or 16rfffd =>
+ name[0] = ch;
+loop:
+ for (;;) {
+ case ch = getc(x) {
+ '_' or '-' or ':' or '.' or
+ 'a' to 'z' or
+ '0' to '9' or
+ 'A' to 'Z' or
+ 16r100 to 16rd7ff or
+ 16re000 to 16rfffd =>
+ name[len name] = ch;
+ * =>
+ break loop;
+ }
+ }
+ }
+ ungetc(x);
+ return name;
+}
+
+substituteentities(x: ref Parser, buff: string): string
+{
+ i := 0;
+ while (i < len buff) {
+ if (buff[i] == '&') {
+ (t, j) := translateentity(x, buff, i);
+ # XXX could be quicker
+ buff = buff[0:i] + t + buff[j:];
+ i += len t;
+ } else
+ i++;
+ }
+ return buff;
+}
+
+# subsitute entities, squashing whitespace along the way.
+substituteentities_sp(x: ref Parser, buf: string): (string, int, int)
+{
+ firstwhite := 0;
+ # skip initial white space
+ for (i := 0; i < len buf; i++) {
+ c := buf[i];
+ if (c != ' ' && c != '\t' && c != '\n' && c != '\r')
+ break;
+ firstwhite = 1;
+ }
+
+ lastwhite := 0;
+ s := "";
+ for (; i < len buf; i++) {
+ c := buf[i];
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
+ lastwhite = 1;
+ else {
+ if (lastwhite) {
+ s[len s] = ' ';
+ lastwhite = 0;
+ }
+ if (c == '&') {
+ # should &x20; count as whitespace?
+ (ent, j) := translateentity(x, buf, i);
+ i = j - 1;
+ s += ent;
+ } else
+ s[len s] = c;
+ }
+ }
+ return (s, firstwhite, lastwhite);
+}
+
+translateentity(x: ref Parser, s: string, i: int): (string, int)
+{
+ i++;
+ for (j := i; j < len s; j++)
+ if (s[j] == ';')
+ break;
+ ent := s[i:j];
+ if (j == len s) {
+ if (len ent > 10)
+ ent = ent[0:11] + "...";
+ warning(x, sys->sprint("missing ; at end of entity (&%s)", ent));
+ return (nil, i);
+ }
+ j++;
+ if (ent == nil) {
+ warning(x, "empty entity");
+ return ("", j);
+ }
+ if (ent[0] == '#') {
+ n: int;
+ rem := ent;
+ if (len ent >= 3 && ent[1] == 'x')
+ (n, rem) = str->toint(ent[2:], 16);
+ else if (len ent >= 2)
+ (n, rem) = str->toint(ent[1:], 10);
+ if (rem != nil) {
+ warning(x, sys->sprint("unrecognized entity (&%s)", ent));
+ return (nil, j);
+ }
+ ch: string = nil;
+ ch[0] = n;
+ return (ch, j);
+ }
+ hv := entdict.find(ent);
+ if (hv == nil) {
+ warning(x, sys->sprint("unrecognized entity (&%s)", ent));
+ return (nil, j);
+ }
+ return (hv.s, j);
+}
+
+balancedstring(x: ref Parser, eos: string): string
+{
+ s := "";
+ instring := 0;
+ quote: int;
+
+ for (i := 0; i < len eos; i++)
+ s[len s] = ' ';
+
+ skipwhite(x);
+ while ((c := getc(x)) != Bufio->EOF) {
+ s[len s] = c;
+ if (instring) {
+ if (c == quote)
+ instring = 0;
+ } else if (c == '\"' || c == '\'') {
+ quote = c;
+ instring = 1;
+ } else if (s[len s - len eos : len s] == eos)
+ return s[len eos : len s - len eos];
+ }
+ error(x, sys->sprint("unexpected end of file while looking for \"%s\"", eos));
+ return "";
+}
+
+skipwhite(x: ref Parser)
+{
+ while ((c := getc(x)) == ' ' || c == '\t' || c == '\n' || c == '\r')
+ ;
+ ungetc(x);
+}
+
+expectwhite(x: ref Parser)
+{
+ if ((c := getc(x)) != ' ' && c != '\t' && c != '\n' && c != '\r')
+ error(x, "expecting white space");
+ skipwhite(x);
+}
+
+expect(x: ref Parser, ch: int)
+{
+ skipwhite(x);
+ c := getc(x);
+ if (c != ch)
+ error(x, sys->sprint("expecting %c", ch));
+}
+
+getc(x: ref Parser): int
+{
+ if (x.eof)
+ return Bufio->EOF;
+ ch := x.in.getc();
+ if (ch == Bufio->EOF)
+ x.eof = 1;
+ else if (ch == '\n')
+ x.loc.line++;
+ x.lastnl = ch == '\n';
+ return ch;
+}
+
+gets(x: ref Parser, delim: int): string
+{
+ if (x.eof)
+ return "";
+ s := x.in.gets(delim);
+ for (i := 0; i < len s; i++)
+ if (s[i] == '\n')
+ x.loc.line++;
+ if (s == "")
+ x.eof = 1;
+ else
+ x.lastnl = s[len s - 1] == '\n';
+ return s;
+}
+
+ungetc(x: ref Parser)
+{
+ if (x.eof)
+ return;
+ x.in.ungetc();
+ x.loc.line -= x.lastnl;
+}
+
+Attributes.all(al: self Attributes): list of Attribute
+{
+ return al.attrs;
+}
+
+Attributes.get(attrs: self Attributes, name: string): string
+{
+ for (a := attrs.attrs; a != nil; a = tl a)
+ if ((hd a).name == name)
+ return (hd a).value;
+ return nil;
+}
+
+warning(x: ref Parser, msg: string)
+{
+ if (x.warning != nil)
+ x.warning <-= (x.loc, msg);
+}
+
+error(x: ref Parser, msg: string)
+{
+ x.errormsg = msg;
+ raise "sax:error";
+}