diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/lib')
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"; +} |
